From 0ec2ac774459393d35603c871637cd52b10d69b2 Mon Sep 17 00:00:00 2001 From: Trey T Date: Sat, 18 Apr 2026 14:15:03 -0500 Subject: [PATCH] UI fix 2/5: lifecycle-aware StateFlow collection in screens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace collectAsState() with collectAsStateWithLifecycle() so StateFlows stop collecting when the host is in background — prevents memory/CPU leaks on lifecycle transitions. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../tt/honeyDue/ui/screens/AllTasksScreen.kt | 9 +++--- .../honeyDue/ui/screens/CompleteTaskScreen.kt | 3 +- .../ui/screens/ContractorDetailScreen.kt | 7 +++-- .../honeyDue/ui/screens/ContractorsScreen.kt | 9 +++--- .../ui/screens/DocumentDetailScreen.kt | 5 ++-- .../honeyDue/ui/screens/DocumentFormScreen.kt | 9 +++--- .../tt/honeyDue/ui/screens/DocumentsScreen.kt | 3 +- .../tt/honeyDue/ui/screens/EditTaskScreen.kt | 9 +++--- .../ui/screens/ForgotPasswordScreen.kt | 5 ++-- .../com/tt/honeyDue/ui/screens/HomeScreen.kt | 5 ++-- .../com/tt/honeyDue/ui/screens/LoginScreen.kt | 5 ++-- .../screens/NotificationPreferencesScreen.kt | 5 ++-- .../tt/honeyDue/ui/screens/ProfileScreen.kt | 9 +++--- .../tt/honeyDue/ui/screens/RegisterScreen.kt | 3 +- .../ui/screens/ResetPasswordScreen.kt | 7 +++-- .../ui/screens/ResidenceDetailScreen.kt | 19 ++++++------ .../ui/screens/ResidenceFormScreen.kt | 9 +++--- .../honeyDue/ui/screens/ResidencesScreen.kt | 5 ++-- .../com/tt/honeyDue/ui/screens/TasksScreen.kt | 5 ++-- .../honeyDue/ui/screens/VerifyEmailScreen.kt | 3 +- .../ui/screens/VerifyResetCodeScreen.kt | 7 +++-- .../OnboardingCreateAccountContent.kt | 5 ++-- .../onboarding/OnboardingFirstTaskContent.kt | 7 +++-- .../OnboardingHomeProfileContent.kt | 29 ++++++++++--------- .../OnboardingJoinResidenceContent.kt | 3 +- .../OnboardingNameResidenceContent.kt | 3 +- .../ui/screens/onboarding/OnboardingScreen.kt | 7 +++-- .../OnboardingVerifyEmailContent.kt | 3 +- .../onboarding/OnboardingWelcomeContent.kt | 3 +- .../screens/residence/JoinResidenceScreen.kt | 8 ++--- .../subscription/FeatureComparisonScreen.kt | 4 +-- .../task/AddTaskWithResidenceScreen.kt | 28 +++++++++--------- .../ui/screens/task/TaskSuggestionsScreen.kt | 6 ++-- .../task/TaskTemplatesBrowserScreen.kt | 9 +++--- 34 files changed, 143 insertions(+), 113 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/AllTasksScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/AllTasksScreen.kt index b3d99e7..8f1db00 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/AllTasksScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/AllTasksScreen.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.ui.components.AddNewTaskWithResidenceDialog import com.tt.honeyDue.ui.components.ApiResultHandler import com.tt.honeyDue.ui.components.CompleteTaskDialog @@ -38,10 +39,10 @@ fun AllTasksScreen( onClearNavigateToTask: () -> Unit = {}, onNavigateToCompleteTask: ((TaskDetail, String) -> Unit)? = null ) { - val tasksState by viewModel.tasksState.collectAsState() - val completionState by taskCompletionViewModel.createCompletionState.collectAsState() - val myResidencesState by residenceViewModel.myResidencesState.collectAsState() - val createTaskState by viewModel.taskAddNewCustomTaskState.collectAsState() + val tasksState by viewModel.tasksState.collectAsStateWithLifecycle() + val completionState by taskCompletionViewModel.createCompletionState.collectAsStateWithLifecycle() + val myResidencesState by residenceViewModel.myResidencesState.collectAsStateWithLifecycle() + val createTaskState by viewModel.taskAddNewCustomTaskState.collectAsStateWithLifecycle() var showCompleteDialog by remember { mutableStateOf(false) } var showNewTaskDialog by remember { mutableStateOf(false) } diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/CompleteTaskScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/CompleteTaskScreen.kt index 970a5f3..cb40164 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/CompleteTaskScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/CompleteTaskScreen.kt @@ -24,6 +24,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import honeydue.composeapp.generated.resources.* import com.tt.honeyDue.models.TaskCompletionCreateRequest import com.tt.honeyDue.models.ContractorSummary @@ -55,7 +56,7 @@ fun CompleteTaskScreen( var showContractorPicker by remember { mutableStateOf(false) } var isSubmitting by remember { mutableStateOf(false) } - val contractorsState by contractorViewModel.contractorsState.collectAsState() + val contractorsState by contractorViewModel.contractorsState.collectAsStateWithLifecycle() val hapticFeedback = rememberHapticFeedback() LaunchedEffect(Unit) { diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ContractorDetailScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ContractorDetailScreen.kt index fcfe306..7b52aa6 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ContractorDetailScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ContractorDetailScreen.kt @@ -18,6 +18,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.data.DataManager import com.tt.honeyDue.ui.components.AddContractorDialog import com.tt.honeyDue.ui.components.ApiResultHandler @@ -39,9 +40,9 @@ fun ContractorDetailScreen( onNavigateBack: () -> Unit, viewModel: ContractorViewModel = viewModel { ContractorViewModel() } ) { - val contractorState by viewModel.contractorDetailState.collectAsState() - val deleteState by viewModel.deleteState.collectAsState() - val toggleFavoriteState by viewModel.toggleFavoriteState.collectAsState() + val contractorState by viewModel.contractorDetailState.collectAsStateWithLifecycle() + val deleteState by viewModel.deleteState.collectAsStateWithLifecycle() + val toggleFavoriteState by viewModel.toggleFavoriteState.collectAsStateWithLifecycle() var showEditDialog by remember { mutableStateOf(false) } var showDeleteConfirmation by remember { mutableStateOf(false) } diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ContractorsScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ContractorsScreen.kt index 76ad259..183c6cd 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ContractorsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ContractorsScreen.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.ui.components.AddContractorDialog import com.tt.honeyDue.ui.components.ApiResultHandler import com.tt.honeyDue.ui.components.HandleErrors @@ -37,10 +38,10 @@ fun ContractorsScreen( onNavigateToContractorDetail: (Int) -> Unit, viewModel: ContractorViewModel = viewModel { ContractorViewModel() } ) { - val contractorsState by viewModel.contractorsState.collectAsState() - val deleteState by viewModel.deleteState.collectAsState() - val toggleFavoriteState by viewModel.toggleFavoriteState.collectAsState() - val contractorSpecialties by LookupsRepository.contractorSpecialties.collectAsState() + val contractorsState by viewModel.contractorsState.collectAsStateWithLifecycle() + val deleteState by viewModel.deleteState.collectAsStateWithLifecycle() + val toggleFavoriteState by viewModel.toggleFavoriteState.collectAsStateWithLifecycle() + val contractorSpecialties by LookupsRepository.contractorSpecialties.collectAsStateWithLifecycle() // Check if screen should be blocked (limit=0) val isBlocked = SubscriptionHelper.isContractorsBlocked() diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/DocumentDetailScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/DocumentDetailScreen.kt index 44d490a..3367cd9 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/DocumentDetailScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/DocumentDetailScreen.kt @@ -33,6 +33,7 @@ import androidx.compose.ui.window.DialogProperties import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items +import androidx.lifecycle.compose.collectAsStateWithLifecycle import coil3.compose.SubcomposeAsyncImage import coil3.compose.SubcomposeAsyncImageContent import coil3.compose.AsyncImagePainter @@ -50,8 +51,8 @@ fun DocumentDetailScreen( onNavigateToEdit: (Int) -> Unit, documentViewModel: DocumentViewModel = viewModel { DocumentViewModel() } ) { - val documentState by documentViewModel.documentDetailState.collectAsState() - val deleteState by documentViewModel.deleteState.collectAsState() + val documentState by documentViewModel.documentDetailState.collectAsStateWithLifecycle() + val deleteState by documentViewModel.deleteState.collectAsStateWithLifecycle() var showDeleteDialog by remember { mutableStateOf(false) } var showPhotoViewer by remember { mutableStateOf(false) } var selectedPhotoIndex by remember { mutableStateOf(0) } diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/DocumentFormScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/DocumentFormScreen.kt index b2f126c..bf71dd9 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/DocumentFormScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/DocumentFormScreen.kt @@ -18,6 +18,7 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import coil3.compose.AsyncImage import com.tt.honeyDue.ui.components.AuthenticatedImage import com.tt.honeyDue.viewmodel.DocumentViewModel @@ -83,12 +84,12 @@ fun DocumentFormScreen( var providerError by remember { mutableStateOf("") } var residenceError by remember { mutableStateOf("") } - val residencesState by residenceViewModel.residencesState.collectAsState() - val documentDetailState by documentViewModel.documentDetailState.collectAsState() + val residencesState by residenceViewModel.residencesState.collectAsStateWithLifecycle() + val documentDetailState by documentViewModel.documentDetailState.collectAsStateWithLifecycle() val operationState by if (isEditMode) { - documentViewModel.updateState.collectAsState() + documentViewModel.updateState.collectAsStateWithLifecycle() } else { - documentViewModel.createState.collectAsState() + documentViewModel.createState.collectAsStateWithLifecycle() } val isWarranty = selectedDocumentType == "warranty" diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/DocumentsScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/DocumentsScreen.kt index ce608da..cb547b9 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/DocumentsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/DocumentsScreen.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.ui.components.documents.DocumentsTabContent import com.tt.honeyDue.ui.subscription.UpgradeFeatureScreen import com.tt.honeyDue.utils.SubscriptionHelper @@ -37,7 +38,7 @@ fun DocumentsScreen( documentViewModel: DocumentViewModel = viewModel { DocumentViewModel() } ) { var selectedTab by remember { mutableStateOf(DocumentTab.WARRANTIES) } - val documentsState by documentViewModel.documentsState.collectAsState() + val documentsState by documentViewModel.documentsState.collectAsStateWithLifecycle() // Check if screen should be blocked (limit=0) val isBlocked = SubscriptionHelper.isDocumentsBlocked() diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/EditTaskScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/EditTaskScreen.kt index e795a1c..68883b9 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/EditTaskScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/EditTaskScreen.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.ui.components.HandleErrors import com.tt.honeyDue.viewmodel.ResidenceViewModel import com.tt.honeyDue.repository.LookupsRepository @@ -43,10 +44,10 @@ fun EditTaskScreen( var frequencyExpanded by remember { mutableStateOf(false) } var priorityExpanded by remember { mutableStateOf(false) } - val updateTaskState by viewModel.updateTaskState.collectAsState() - val categories by LookupsRepository.taskCategories.collectAsState() - val frequencies by LookupsRepository.taskFrequencies.collectAsState() - val priorities by LookupsRepository.taskPriorities.collectAsState() + val updateTaskState by viewModel.updateTaskState.collectAsStateWithLifecycle() + val categories by LookupsRepository.taskCategories.collectAsStateWithLifecycle() + val frequencies by LookupsRepository.taskFrequencies.collectAsStateWithLifecycle() + val priorities by LookupsRepository.taskPriorities.collectAsStateWithLifecycle() // Validation errors var titleError by remember { mutableStateOf("") } diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ForgotPasswordScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ForgotPasswordScreen.kt index d7534e9..b6d9d17 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ForgotPasswordScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ForgotPasswordScreen.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.ui.components.HandleErrors import com.tt.honeyDue.ui.components.auth.AuthHeader import com.tt.honeyDue.ui.components.common.ErrorCard @@ -30,8 +31,8 @@ fun ForgotPasswordScreen( viewModel: PasswordResetViewModel ) { var email by remember { mutableStateOf("") } - val forgotPasswordState by viewModel.forgotPasswordState.collectAsState() - val currentStep by viewModel.currentStep.collectAsState() + val forgotPasswordState by viewModel.forgotPasswordState.collectAsStateWithLifecycle() + val currentStep by viewModel.currentStep.collectAsStateWithLifecycle() // Handle errors for forgot password forgotPasswordState.HandleErrors( diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/HomeScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/HomeScreen.kt index 545000e..832dd25 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/HomeScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/HomeScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.ui.components.HandleErrors import com.tt.honeyDue.ui.theme.* import com.tt.honeyDue.viewmodel.ResidenceViewModel @@ -29,8 +30,8 @@ fun HomeScreen( viewModel: ResidenceViewModel = viewModel { ResidenceViewModel() }, taskViewModel: TaskViewModel = viewModel { TaskViewModel() } ) { - val summaryState by viewModel.myResidencesState.collectAsState() - val totalSummary by DataManager.totalSummary.collectAsState() + val summaryState by viewModel.myResidencesState.collectAsStateWithLifecycle() + val totalSummary by DataManager.totalSummary.collectAsStateWithLifecycle() LaunchedEffect(Unit) { viewModel.loadMyResidences() diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/LoginScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/LoginScreen.kt index 97f9156..142c28a 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/LoginScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/LoginScreen.kt @@ -21,6 +21,7 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.ui.components.HandleErrors import com.tt.honeyDue.ui.components.auth.AuthHeader import com.tt.honeyDue.ui.components.auth.GoogleSignInButton @@ -44,8 +45,8 @@ fun LoginScreen( var password by remember { mutableStateOf("") } var passwordVisible by remember { mutableStateOf(false) } var googleSignInError by remember { mutableStateOf(null) } - val loginState by viewModel.loginState.collectAsState() - val googleSignInState by viewModel.googleSignInState.collectAsState() + val loginState by viewModel.loginState.collectAsStateWithLifecycle() + val googleSignInState by viewModel.googleSignInState.collectAsStateWithLifecycle() // Handle errors for login loginState.HandleErrors( diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/NotificationPreferencesScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/NotificationPreferencesScreen.kt index 8ce21b3..742fc8c 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/NotificationPreferencesScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/NotificationPreferencesScreen.kt @@ -16,6 +16,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.analytics.AnalyticsEvents import com.tt.honeyDue.analytics.PostHogAnalytics import com.tt.honeyDue.network.ApiResult @@ -57,8 +58,8 @@ fun NotificationPreferencesScreen( onNavigateBack: () -> Unit, viewModel: NotificationPreferencesViewModel = viewModel { NotificationPreferencesViewModel() }, ) { - val preferencesState by viewModel.preferencesState.collectAsState() - val categoryState by viewModel.categoryState.collectAsState() + val preferencesState by viewModel.preferencesState.collectAsStateWithLifecycle() + val categoryState by viewModel.categoryState.collectAsStateWithLifecycle() // Platform-specific wiring: Android provides real controller + settings // launcher; every other target returns null and the matching section diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ProfileScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ProfileScreen.kt index b848e64..5d3fede 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ProfileScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ProfileScreen.kt @@ -31,6 +31,7 @@ import com.tt.honeyDue.network.APILayer import com.tt.honeyDue.data.DataManager import com.tt.honeyDue.ui.subscription.UpgradePromptDialog import androidx.compose.runtime.getValue +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.analytics.PostHogAnalytics import com.tt.honeyDue.analytics.AnalyticsEvents import com.tt.honeyDue.platform.BiometricResult @@ -64,11 +65,11 @@ fun ProfileScreen( val isBiometricAvailable = remember { biometricAuth.isBiometricAvailable() } var isBiometricEnabled by remember { mutableStateOf(BiometricPreference.isBiometricEnabled()) } - val updateState by viewModel.updateProfileState.collectAsState() - val deleteAccountState by viewModel.deleteAccountState.collectAsState() + val updateState by viewModel.updateProfileState.collectAsStateWithLifecycle() + val deleteAccountState by viewModel.deleteAccountState.collectAsStateWithLifecycle() val currentTheme by remember { derivedStateOf { ThemeManager.currentTheme } } - val currentSubscription by DataManager.subscription.collectAsState() - val currentUser by DataManager.currentUser.collectAsState() + val currentSubscription by DataManager.subscription.collectAsStateWithLifecycle() + val currentUser by DataManager.currentUser.collectAsStateWithLifecycle() // Handle errors for profile update updateState.HandleErrors( diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/RegisterScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/RegisterScreen.kt index 77ee212..b1fdcd9 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/RegisterScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/RegisterScreen.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.ui.components.HandleErrors import com.tt.honeyDue.ui.components.auth.AuthHeader import com.tt.honeyDue.ui.components.auth.RequirementItem @@ -41,7 +42,7 @@ fun RegisterScreen( var errorMessage by remember { mutableStateOf("") } var isLoading by remember { mutableStateOf(false) } - val createState by viewModel.registerState.collectAsState() + val createState by viewModel.registerState.collectAsStateWithLifecycle() // Handle errors for registration createState.HandleErrors( diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ResetPasswordScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ResetPasswordScreen.kt index 6cc33bc..6e8cb3d 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ResetPasswordScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ResetPasswordScreen.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.ui.components.HandleErrors import com.tt.honeyDue.ui.components.auth.AuthHeader import com.tt.honeyDue.ui.components.auth.RequirementItem @@ -35,9 +36,9 @@ fun ResetPasswordScreen( var newPasswordVisible by remember { mutableStateOf(false) } var confirmPasswordVisible by remember { mutableStateOf(false) } - val resetPasswordState by viewModel.resetPasswordState.collectAsState() - val loginState by viewModel.loginState.collectAsState() - val currentStep by viewModel.currentStep.collectAsState() + val resetPasswordState by viewModel.resetPasswordState.collectAsStateWithLifecycle() + val loginState by viewModel.loginState.collectAsStateWithLifecycle() + val currentStep by viewModel.currentStep.collectAsStateWithLifecycle() // Handle errors for password reset resetPasswordState.HandleErrors( diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ResidenceDetailScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ResidenceDetailScreen.kt index 1ceb285..71eb5da 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ResidenceDetailScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ResidenceDetailScreen.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.ui.components.AddNewTaskDialog import com.tt.honeyDue.ui.components.ApiResultHandler import com.tt.honeyDue.ui.components.CompleteTaskDialog @@ -57,13 +58,13 @@ fun ResidenceDetailScreen( taskViewModel: TaskViewModel = viewModel { TaskViewModel() } ) { var residenceState by remember { mutableStateOf>(ApiResult.Loading) } - val tasksState by residenceViewModel.residenceTasksState.collectAsState() - val contractorsState by residenceViewModel.residenceContractorsState.collectAsState() - val completionState by taskCompletionViewModel.createCompletionState.collectAsState() - val taskAddNewTaskState by taskViewModel.taskAddNewCustomTaskState.collectAsState() - val cancelTaskState by residenceViewModel.cancelTaskState.collectAsState() - val uncancelTaskState by residenceViewModel.uncancelTaskState.collectAsState() - val generateReportState by residenceViewModel.generateReportState.collectAsState() + val tasksState by residenceViewModel.residenceTasksState.collectAsStateWithLifecycle() + val contractorsState by residenceViewModel.residenceContractorsState.collectAsStateWithLifecycle() + val completionState by taskCompletionViewModel.createCompletionState.collectAsStateWithLifecycle() + val taskAddNewTaskState by taskViewModel.taskAddNewCustomTaskState.collectAsStateWithLifecycle() + val cancelTaskState by residenceViewModel.cancelTaskState.collectAsStateWithLifecycle() + val uncancelTaskState by residenceViewModel.uncancelTaskState.collectAsStateWithLifecycle() + val generateReportState by residenceViewModel.generateReportState.collectAsStateWithLifecycle() var showCompleteDialog by remember { mutableStateOf(false) } var selectedTask by remember { mutableStateOf(null) } @@ -77,12 +78,12 @@ fun ResidenceDetailScreen( var showArchiveTaskConfirmation by remember { mutableStateOf(false) } var taskToCancel by remember { mutableStateOf(null) } var taskToArchive by remember { mutableStateOf(null) } - val deleteState by residenceViewModel.deleteResidenceState.collectAsState() + val deleteState by residenceViewModel.deleteResidenceState.collectAsStateWithLifecycle() var showUpgradePrompt by remember { mutableStateOf(false) } var upgradeTriggerKey by remember { mutableStateOf(null) } // Get current user for ownership checks - val currentUser by DataManager.currentUser.collectAsState() + val currentUser by DataManager.currentUser.collectAsStateWithLifecycle() // Residence sharing state and function val (shareState, shareResidence) = rememberShareResidence() diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ResidenceFormScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ResidenceFormScreen.kt index 3ab12b0..114c986 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ResidenceFormScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ResidenceFormScreen.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.viewmodel.ResidenceViewModel import com.tt.honeyDue.repository.LookupsRepository import com.tt.honeyDue.data.DataManager @@ -59,12 +60,12 @@ fun ResidenceFormScreen( var expanded by remember { mutableStateOf(false) } val operationState by if (isEditMode) { - viewModel.updateResidenceState.collectAsState() + viewModel.updateResidenceState.collectAsStateWithLifecycle() } else { - viewModel.createResidenceState.collectAsState() + viewModel.createResidenceState.collectAsStateWithLifecycle() } - val propertyTypes by LookupsRepository.residenceTypes.collectAsState() - val currentUser by DataManager.currentUser.collectAsState() + val propertyTypes by LookupsRepository.residenceTypes.collectAsStateWithLifecycle() + val currentUser by DataManager.currentUser.collectAsStateWithLifecycle() // Check if current user is the owner val isCurrentUserOwner = remember(existingResidence, currentUser) { diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ResidencesScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ResidencesScreen.kt index 4ce635d..6275d49 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ResidencesScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/ResidencesScreen.kt @@ -24,6 +24,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.ui.components.ApiResultHandler import com.tt.honeyDue.ui.components.common.StatItem import com.tt.honeyDue.ui.components.residence.TaskStatChip @@ -51,8 +52,8 @@ fun ResidencesScreen( viewModel: ResidenceViewModel = viewModel { ResidenceViewModel() }, taskViewModel: TaskViewModel = viewModel { TaskViewModel() } ) { - val myResidencesState by viewModel.myResidencesState.collectAsState() - val totalSummary by DataManager.totalSummary.collectAsState() + val myResidencesState by viewModel.myResidencesState.collectAsStateWithLifecycle() + val totalSummary by DataManager.totalSummary.collectAsStateWithLifecycle() var isRefreshing by remember { mutableStateOf(false) } var showUpgradePrompt by remember { mutableStateOf(false) } var upgradeTriggerKey by remember { mutableStateOf(null) } diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/TasksScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/TasksScreen.kt index 6e3764f..4a0983a 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/TasksScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/TasksScreen.kt @@ -12,6 +12,7 @@ import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.ui.components.CompleteTaskDialog import com.tt.honeyDue.ui.components.ErrorDialog import com.tt.honeyDue.ui.components.task.TaskCard @@ -35,8 +36,8 @@ fun TasksScreen( viewModel: TaskViewModel = viewModel { TaskViewModel() }, taskCompletionViewModel: TaskCompletionViewModel = viewModel { TaskCompletionViewModel() } ) { - val tasksState by viewModel.tasksState.collectAsState() - val completionState by taskCompletionViewModel.createCompletionState.collectAsState() + val tasksState by viewModel.tasksState.collectAsStateWithLifecycle() + val completionState by taskCompletionViewModel.createCompletionState.collectAsStateWithLifecycle() var expandedColumns by remember { mutableStateOf(setOf()) } var showCompleteDialog by remember { mutableStateOf(false) } var selectedTask by remember { mutableStateOf(null) } diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/VerifyEmailScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/VerifyEmailScreen.kt index e3d0b45..606c9a0 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/VerifyEmailScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/VerifyEmailScreen.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.ui.components.HandleErrors import com.tt.honeyDue.ui.components.auth.AuthHeader import com.tt.honeyDue.ui.components.common.ErrorCard @@ -35,7 +36,7 @@ fun VerifyEmailScreen( var errorMessage by remember { mutableStateOf("") } var isLoading by remember { mutableStateOf(false) } - val verifyState by viewModel.verifyEmailState.collectAsState() + val verifyState by viewModel.verifyEmailState.collectAsStateWithLifecycle() // Handle errors for email verification verifyState.HandleErrors( diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/VerifyResetCodeScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/VerifyResetCodeScreen.kt index b8be631..da035e2 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/VerifyResetCodeScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/VerifyResetCodeScreen.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.ui.components.HandleErrors import com.tt.honeyDue.ui.components.auth.AuthHeader import com.tt.honeyDue.ui.components.common.ErrorCard @@ -30,9 +31,9 @@ fun VerifyResetCodeScreen( viewModel: PasswordResetViewModel ) { var code by remember { mutableStateOf("") } - val email by viewModel.email.collectAsState() - val verifyCodeState by viewModel.verifyCodeState.collectAsState() - val currentStep by viewModel.currentStep.collectAsState() + val email by viewModel.email.collectAsStateWithLifecycle() + val verifyCodeState by viewModel.verifyCodeState.collectAsStateWithLifecycle() + val currentStep by viewModel.currentStep.collectAsStateWithLifecycle() // Handle errors for code verification verifyCodeState.HandleErrors( diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingCreateAccountContent.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingCreateAccountContent.kt index 75dfb94..860686b 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingCreateAccountContent.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingCreateAccountContent.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.network.ApiResult import com.tt.honeyDue.ui.components.auth.RequirementItem import com.tt.honeyDue.ui.theme.* @@ -39,7 +40,7 @@ fun OnboardingCreateAccountContent( var showLoginDialog by remember { mutableStateOf(false) } var localErrorMessage by remember { mutableStateOf(null) } - val registerState by viewModel.registerState.collectAsState() + val registerState by viewModel.registerState.collectAsStateWithLifecycle() LaunchedEffect(registerState) { when (registerState) { @@ -327,7 +328,7 @@ private fun OnboardingLoginDialog( ) { var username by remember { mutableStateOf("") } var password by remember { mutableStateOf("") } - val loginState by viewModel.loginState.collectAsState() + val loginState by viewModel.loginState.collectAsStateWithLifecycle() LaunchedEffect(loginState) { when (loginState) { diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingFirstTaskContent.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingFirstTaskContent.kt index 3e28177..2302aa5 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingFirstTaskContent.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingFirstTaskContent.kt @@ -21,6 +21,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.analytics.AnalyticsEvents import com.tt.honeyDue.analytics.PostHogAnalytics import com.tt.honeyDue.data.DataManager @@ -71,9 +72,9 @@ fun OnboardingFirstTaskContent( var isCreatingTasks by remember { mutableStateOf(false) } var selectedTabIndex by remember { mutableStateOf(0) } - val createTasksState by viewModel.createTasksState.collectAsState() - val suggestionsState by viewModel.suggestionsState.collectAsState() - val templatesGroupedState by viewModel.templatesGroupedState.collectAsState() + val createTasksState by viewModel.createTasksState.collectAsStateWithLifecycle() + val suggestionsState by viewModel.suggestionsState.collectAsStateWithLifecycle() + val templatesGroupedState by viewModel.templatesGroupedState.collectAsStateWithLifecycle() // Kick off both network calls on mount. Suggestions needs a residence; // the grouped catalog is user-independent and safe to load immediately. diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingHomeProfileContent.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingHomeProfileContent.kt index 314dbbf..70b0ae0 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingHomeProfileContent.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingHomeProfileContent.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.models.HomeProfileOptions import com.tt.honeyDue.ui.theme.* import com.tt.honeyDue.viewmodel.OnboardingViewModel @@ -27,20 +28,20 @@ fun OnboardingHomeProfileContent( onContinue: () -> Unit, onSkip: () -> Unit ) { - val heatingType by viewModel.heatingType.collectAsState() - val coolingType by viewModel.coolingType.collectAsState() - val waterHeaterType by viewModel.waterHeaterType.collectAsState() - val roofType by viewModel.roofType.collectAsState() - val hasPool by viewModel.hasPool.collectAsState() - val hasSprinklerSystem by viewModel.hasSprinklerSystem.collectAsState() - val hasSeptic by viewModel.hasSeptic.collectAsState() - val hasFireplace by viewModel.hasFireplace.collectAsState() - val hasGarage by viewModel.hasGarage.collectAsState() - val hasBasement by viewModel.hasBasement.collectAsState() - val hasAttic by viewModel.hasAttic.collectAsState() - val exteriorType by viewModel.exteriorType.collectAsState() - val flooringPrimary by viewModel.flooringPrimary.collectAsState() - val landscapingType by viewModel.landscapingType.collectAsState() + val heatingType by viewModel.heatingType.collectAsStateWithLifecycle() + val coolingType by viewModel.coolingType.collectAsStateWithLifecycle() + val waterHeaterType by viewModel.waterHeaterType.collectAsStateWithLifecycle() + val roofType by viewModel.roofType.collectAsStateWithLifecycle() + val hasPool by viewModel.hasPool.collectAsStateWithLifecycle() + val hasSprinklerSystem by viewModel.hasSprinklerSystem.collectAsStateWithLifecycle() + val hasSeptic by viewModel.hasSeptic.collectAsStateWithLifecycle() + val hasFireplace by viewModel.hasFireplace.collectAsStateWithLifecycle() + val hasGarage by viewModel.hasGarage.collectAsStateWithLifecycle() + val hasBasement by viewModel.hasBasement.collectAsStateWithLifecycle() + val hasAttic by viewModel.hasAttic.collectAsStateWithLifecycle() + val exteriorType by viewModel.exteriorType.collectAsStateWithLifecycle() + val flooringPrimary by viewModel.flooringPrimary.collectAsStateWithLifecycle() + val landscapingType by viewModel.landscapingType.collectAsStateWithLifecycle() Column(modifier = Modifier.fillMaxSize()) { LazyColumn( diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingJoinResidenceContent.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingJoinResidenceContent.kt index 433a5fb..fc1df4f 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingJoinResidenceContent.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingJoinResidenceContent.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.network.ApiResult import com.tt.honeyDue.ui.theme.* import com.tt.honeyDue.viewmodel.OnboardingViewModel @@ -27,7 +28,7 @@ fun OnboardingJoinResidenceContent( var shareCode by remember { mutableStateOf("") } var localErrorMessage by remember { mutableStateOf(null) } - val joinState by viewModel.joinResidenceState.collectAsState() + val joinState by viewModel.joinResidenceState.collectAsStateWithLifecycle() LaunchedEffect(joinState) { when (joinState) { diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingNameResidenceContent.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingNameResidenceContent.kt index 2821ebb..48ed346 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingNameResidenceContent.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingNameResidenceContent.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.ui.theme.* import com.tt.honeyDue.viewmodel.OnboardingViewModel import honeydue.composeapp.generated.resources.* @@ -22,7 +23,7 @@ fun OnboardingNameResidenceContent( viewModel: OnboardingViewModel, onContinue: () -> Unit ) { - val residenceName by viewModel.residenceName.collectAsState() + val residenceName by viewModel.residenceName.collectAsStateWithLifecycle() var localName by remember { mutableStateOf(residenceName) } WarmGradientBackground( diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingScreen.kt index 6257471..3660fb8 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingScreen.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.ui.theme.* import com.tt.honeyDue.viewmodel.OnboardingStep import com.tt.honeyDue.viewmodel.OnboardingViewModel @@ -26,9 +27,9 @@ fun OnboardingScreen( onLoginSuccess: (Boolean) -> Unit, // Boolean = isVerified viewModel: OnboardingViewModel = viewModel { OnboardingViewModel() } ) { - val currentStep by viewModel.currentStep.collectAsState() - val userIntent by viewModel.userIntent.collectAsState() - val isComplete by viewModel.isComplete.collectAsState() + val currentStep by viewModel.currentStep.collectAsStateWithLifecycle() + val userIntent by viewModel.userIntent.collectAsStateWithLifecycle() + val isComplete by viewModel.isComplete.collectAsStateWithLifecycle() // Handle onboarding completion LaunchedEffect(isComplete) { diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingVerifyEmailContent.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingVerifyEmailContent.kt index 08febc6..d79c074 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingVerifyEmailContent.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingVerifyEmailContent.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.network.ApiResult import com.tt.honeyDue.ui.theme.* import com.tt.honeyDue.viewmodel.OnboardingViewModel @@ -27,7 +28,7 @@ fun OnboardingVerifyEmailContent( var code by remember { mutableStateOf("") } var localErrorMessage by remember { mutableStateOf(null) } - val verifyState by viewModel.verifyEmailState.collectAsState() + val verifyState by viewModel.verifyEmailState.collectAsStateWithLifecycle() LaunchedEffect(verifyState) { when (verifyState) { diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingWelcomeContent.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingWelcomeContent.kt index 236a20c..6246b10 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingWelcomeContent.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/onboarding/OnboardingWelcomeContent.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.ui.theme.* import com.tt.honeyDue.viewmodel.AuthViewModel import com.tt.honeyDue.network.ApiResult @@ -152,7 +153,7 @@ private fun LoginDialog( val authViewModel: AuthViewModel = viewModel { AuthViewModel() } var username by remember { mutableStateOf("") } var password by remember { mutableStateOf("") } - val loginState by authViewModel.loginState.collectAsState() + val loginState by authViewModel.loginState.collectAsStateWithLifecycle() LaunchedEffect(loginState) { when (loginState) { diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/residence/JoinResidenceScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/residence/JoinResidenceScreen.kt index 313337b..df33d86 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/residence/JoinResidenceScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/residence/JoinResidenceScreen.kt @@ -30,7 +30,6 @@ import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -39,6 +38,7 @@ import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.network.ApiResult import com.tt.honeyDue.ui.components.common.StandardCard import com.tt.honeyDue.ui.components.forms.FormTextField @@ -62,9 +62,9 @@ fun JoinResidenceScreen( onJoined: (Int) -> Unit, viewModel: JoinResidenceViewModel = viewModel { JoinResidenceViewModel() }, ) { - val code by viewModel.code.collectAsState() - val error by viewModel.errorMessage.collectAsState() - val submitState by viewModel.submitState.collectAsState() + val code by viewModel.code.collectAsStateWithLifecycle() + val error by viewModel.errorMessage.collectAsStateWithLifecycle() + val submitState by viewModel.submitState.collectAsStateWithLifecycle() val isLoading = submitState is ApiResult.Loading diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/subscription/FeatureComparisonScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/subscription/FeatureComparisonScreen.kt index 9c163fc..b49cb93 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/subscription/FeatureComparisonScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/subscription/FeatureComparisonScreen.kt @@ -32,12 +32,12 @@ import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.runtime.collectAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.analytics.AnalyticsEvents import com.tt.honeyDue.analytics.PostHogAnalytics import com.tt.honeyDue.data.DataManager @@ -71,7 +71,7 @@ fun FeatureComparisonScreen( onNavigateBack: () -> Unit, onNavigateToUpgrade: () -> Unit, ) { - val benefits by DataManager.featureBenefits.collectAsState() + val benefits by DataManager.featureBenefits.collectAsStateWithLifecycle() val rows = FeatureComparisonScreenState.resolveFeatureRows(benefits) Scaffold( diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/task/AddTaskWithResidenceScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/task/AddTaskWithResidenceScreen.kt index 504c015..aa51e96 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/task/AddTaskWithResidenceScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/task/AddTaskWithResidenceScreen.kt @@ -28,13 +28,13 @@ import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.network.ApiResult import com.tt.honeyDue.repository.LookupsRepository import com.tt.honeyDue.ui.components.common.StandardCard @@ -65,20 +65,20 @@ fun AddTaskWithResidenceScreen( AddTaskWithResidenceViewModel(residenceId = residenceId) } ) { - val title by viewModel.title.collectAsState() - val description by viewModel.description.collectAsState() - val priorityId by viewModel.priorityId.collectAsState() - val categoryId by viewModel.categoryId.collectAsState() - val frequencyId by viewModel.frequencyId.collectAsState() - val dueDate by viewModel.dueDate.collectAsState() - val estimatedCost by viewModel.estimatedCost.collectAsState() - val titleError by viewModel.titleError.collectAsState() - val canSubmit by viewModel.canSubmit.collectAsState() - val submitState by viewModel.submitState.collectAsState() + val title by viewModel.title.collectAsStateWithLifecycle() + val description by viewModel.description.collectAsStateWithLifecycle() + val priorityId by viewModel.priorityId.collectAsStateWithLifecycle() + val categoryId by viewModel.categoryId.collectAsStateWithLifecycle() + val frequencyId by viewModel.frequencyId.collectAsStateWithLifecycle() + val dueDate by viewModel.dueDate.collectAsStateWithLifecycle() + val estimatedCost by viewModel.estimatedCost.collectAsStateWithLifecycle() + val titleError by viewModel.titleError.collectAsStateWithLifecycle() + val canSubmit by viewModel.canSubmit.collectAsStateWithLifecycle() + val submitState by viewModel.submitState.collectAsStateWithLifecycle() - val priorities by LookupsRepository.taskPriorities.collectAsState() - val categories by LookupsRepository.taskCategories.collectAsState() - val frequencies by LookupsRepository.taskFrequencies.collectAsState() + val priorities by LookupsRepository.taskPriorities.collectAsStateWithLifecycle() + val categories by LookupsRepository.taskCategories.collectAsStateWithLifecycle() + val frequencies by LookupsRepository.taskFrequencies.collectAsStateWithLifecycle() val isSubmitting = submitState is ApiResult.Loading diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/task/TaskSuggestionsScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/task/TaskSuggestionsScreen.kt index 5dacdf8..a887edd 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/task/TaskSuggestionsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/task/TaskSuggestionsScreen.kt @@ -35,7 +35,6 @@ import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.pulltorefresh.PullToRefreshBox import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -45,6 +44,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.models.TaskSuggestionResponse import com.tt.honeyDue.network.ApiResult import com.tt.honeyDue.ui.components.common.StandardCard @@ -76,8 +76,8 @@ fun TaskSuggestionsScreen( TaskSuggestionsViewModel(residenceId = residenceId) } ) { - val suggestionsState by viewModel.suggestionsState.collectAsState() - val acceptState by viewModel.acceptState.collectAsState() + val suggestionsState by viewModel.suggestionsState.collectAsStateWithLifecycle() + val acceptState by viewModel.acceptState.collectAsStateWithLifecycle() var isRefreshing by remember { mutableStateOf(false) } LaunchedEffect(Unit) { diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/task/TaskTemplatesBrowserScreen.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/task/TaskTemplatesBrowserScreen.kt index 775ba19..cbd8fd1 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/task/TaskTemplatesBrowserScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/ui/screens/task/TaskTemplatesBrowserScreen.kt @@ -38,6 +38,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tt.honeyDue.analytics.AnalyticsEvents import com.tt.honeyDue.analytics.PostHogAnalytics import com.tt.honeyDue.models.TaskTemplate @@ -90,10 +91,10 @@ fun TaskTemplatesBrowserScreen( ) } ) { - val templatesState by viewModel.templatesState.collectAsState() - val selectedIds by viewModel.selectedTemplateIds.collectAsState() - val selectedCategory by viewModel.selectedCategory.collectAsState() - val applyState by viewModel.applyState.collectAsState() + val templatesState by viewModel.templatesState.collectAsStateWithLifecycle() + val selectedIds by viewModel.selectedTemplateIds.collectAsStateWithLifecycle() + val selectedCategory by viewModel.selectedCategory.collectAsStateWithLifecycle() + val applyState by viewModel.applyState.collectAsStateWithLifecycle() var isRefreshing by remember { mutableStateOf(false) } LaunchedEffect(Unit) {