P0.2: migrate screens to LocalDataManager.current

Swaps direct `DataManager.xxx` access for `LocalDataManager.current.xxx`
across every Compose screen under ui/screens/** that references the
singleton. Each composable resolves the ambient once at the top of its
body and reuses the local val for subsequent reads — keeping rewrites
minimal and predictable.

Screens touched:
  - HomeScreen                          (totalSummary)
  - ResidencesScreen                    (totalSummary)
  - ResidenceDetailScreen               (currentUser)
  - ResidenceFormScreen                 (currentUser)
  - ProfileScreen                       (currentUser + subscription)
  - ContractorDetailScreen              (residences)
  - subscription/FeatureComparisonScreen (featureBenefits)
  - onboarding/OnboardingFirstTaskContent (residences × 3 sites)

No behavior change — in production the ambient default resolves to the
same DataManager singleton. The change is purely so tests, previews, and
the parity-gallery can `CompositionLocalProvider(LocalDataManager provides fake)`
to substitute a fake without tearing screens apart.

Files under ui/subscription/** and ui/components/AddTaskDialog.kt also
reference DataManager but live outside ui/screens/** (plan's scope) —
flagged for a follow-up pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Trey T
2026-04-18 19:08:58 -05:00
parent 98b775d335
commit 00e215920a
8 changed files with 30 additions and 20 deletions

View File

@@ -21,7 +21,7 @@ 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.data.LocalDataManager
import com.tt.honeyDue.ui.components.AddContractorDialog
import com.tt.honeyDue.ui.components.ApiResultHandler
import com.tt.honeyDue.ui.components.HandleErrors
@@ -42,6 +42,7 @@ fun ContractorDetailScreen(
onNavigateBack: () -> Unit,
viewModel: ContractorViewModel = viewModel { ContractorViewModel() }
) {
val dataManager = LocalDataManager.current
val contractorState by viewModel.contractorDetailState.collectAsStateWithLifecycle()
val deleteState by viewModel.deleteState.collectAsStateWithLifecycle()
val toggleFavoriteState by viewModel.toggleFavoriteState.collectAsStateWithLifecycle()
@@ -146,7 +147,7 @@ fun ContractorDetailScreen(
.testTag(AccessibilityIds.Contractor.detailView)
) {
val uriHandler = LocalUriHandler.current
val residences = DataManager.residences.value
val residences = dataManager.residences.value
ApiResultHandler(
state = contractorState,

View File

@@ -20,7 +20,7 @@ import com.tt.honeyDue.ui.theme.*
import com.tt.honeyDue.viewmodel.ResidenceViewModel
import com.tt.honeyDue.viewmodel.TaskViewModel
import com.tt.honeyDue.network.ApiResult
import com.tt.honeyDue.data.DataManager
import com.tt.honeyDue.data.LocalDataManager
import honeydue.composeapp.generated.resources.*
import org.jetbrains.compose.resources.stringResource
@@ -33,8 +33,9 @@ fun HomeScreen(
viewModel: ResidenceViewModel = viewModel { ResidenceViewModel() },
taskViewModel: TaskViewModel = viewModel { TaskViewModel() }
) {
val dataManager = LocalDataManager.current
val summaryState by viewModel.myResidencesState.collectAsStateWithLifecycle()
val totalSummary by DataManager.totalSummary.collectAsStateWithLifecycle()
val totalSummary by dataManager.totalSummary.collectAsStateWithLifecycle()
var isRefreshing by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {

View File

@@ -28,7 +28,7 @@ import com.tt.honeyDue.ui.theme.*
import com.tt.honeyDue.viewmodel.AuthViewModel
import com.tt.honeyDue.network.ApiResult
import com.tt.honeyDue.network.APILayer
import com.tt.honeyDue.data.DataManager
import com.tt.honeyDue.data.LocalDataManager
import com.tt.honeyDue.ui.subscription.UpgradePromptDialog
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -68,8 +68,9 @@ fun ProfileScreen(
val updateState by viewModel.updateProfileState.collectAsStateWithLifecycle()
val deleteAccountState by viewModel.deleteAccountState.collectAsStateWithLifecycle()
val currentTheme by remember { derivedStateOf { ThemeManager.currentTheme } }
val currentSubscription by DataManager.subscription.collectAsStateWithLifecycle()
val currentUser by DataManager.currentUser.collectAsStateWithLifecycle()
val dataManager = LocalDataManager.current
val currentSubscription by dataManager.subscription.collectAsStateWithLifecycle()
val currentUser by dataManager.currentUser.collectAsStateWithLifecycle()
// Handle errors for profile update
updateState.HandleErrors(

View File

@@ -40,7 +40,7 @@ import com.tt.honeyDue.models.ContractorSummary
import com.tt.honeyDue.network.ApiResult
import com.tt.honeyDue.utils.SubscriptionHelper
import com.tt.honeyDue.ui.subscription.UpgradePromptDialog
import com.tt.honeyDue.data.DataManager
import com.tt.honeyDue.data.LocalDataManager
import com.tt.honeyDue.util.DateUtils
import com.tt.honeyDue.platform.rememberShareResidence
import com.tt.honeyDue.analytics.PostHogAnalytics
@@ -88,7 +88,8 @@ fun ResidenceDetailScreen(
var upgradeTriggerKey by remember { mutableStateOf<String?>(null) }
// Get current user for ownership checks
val currentUser by DataManager.currentUser.collectAsStateWithLifecycle()
val dataManager = LocalDataManager.current
val currentUser by dataManager.currentUser.collectAsStateWithLifecycle()
// Residence sharing state and function
val (shareState, shareResidence) = rememberShareResidence()

View File

@@ -21,7 +21,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tt.honeyDue.testing.AccessibilityIds
import com.tt.honeyDue.viewmodel.ResidenceViewModel
import com.tt.honeyDue.repository.LookupsRepository
import com.tt.honeyDue.data.DataManager
import com.tt.honeyDue.data.LocalDataManager
import com.tt.honeyDue.models.Residence
import com.tt.honeyDue.models.ResidenceCreateRequest
import com.tt.honeyDue.models.ResidenceType
@@ -68,8 +68,9 @@ fun ResidenceFormScreen(
} else {
viewModel.createResidenceState.collectAsStateWithLifecycle()
}
val dataManager = LocalDataManager.current
val propertyTypes by LookupsRepository.residenceTypes.collectAsStateWithLifecycle()
val currentUser by DataManager.currentUser.collectAsStateWithLifecycle()
val currentUser by dataManager.currentUser.collectAsStateWithLifecycle()
// Check if current user is the owner
val isCurrentUserOwner = remember(existingResidence, currentUser) {

View File

@@ -41,7 +41,7 @@ import com.tt.honeyDue.utils.SubscriptionHelper
import com.tt.honeyDue.ui.subscription.UpgradePromptDialog
import com.tt.honeyDue.analytics.PostHogAnalytics
import com.tt.honeyDue.analytics.AnalyticsEvents
import com.tt.honeyDue.data.DataManager
import com.tt.honeyDue.data.LocalDataManager
import com.tt.honeyDue.ui.theme.*
import honeydue.composeapp.generated.resources.*
import org.jetbrains.compose.resources.stringResource
@@ -61,8 +61,9 @@ fun ResidencesScreen(
viewModel: ResidenceViewModel = viewModel { ResidenceViewModel() },
taskViewModel: TaskViewModel = viewModel { TaskViewModel() }
) {
val dataManager = LocalDataManager.current
val myResidencesState by viewModel.myResidencesState.collectAsStateWithLifecycle()
val totalSummary by DataManager.totalSummary.collectAsStateWithLifecycle()
val totalSummary by dataManager.totalSummary.collectAsStateWithLifecycle()
var isRefreshing by remember { mutableStateOf(false) }
var showUpgradePrompt by remember { mutableStateOf(false) }
var upgradeTriggerKey by remember { mutableStateOf<String?>(null) }

View File

@@ -24,7 +24,7 @@ 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
import com.tt.honeyDue.data.LocalDataManager
import com.tt.honeyDue.models.TaskCreateRequest
import com.tt.honeyDue.models.TaskSuggestionResponse
import com.tt.honeyDue.models.TaskSuggestionsResponse
@@ -66,6 +66,7 @@ fun OnboardingFirstTaskContent(
viewModel: OnboardingViewModel,
onTasksAdded: () -> Unit
) {
val dataManager = LocalDataManager.current
var selectedBrowseIds by remember { mutableStateOf(setOf<Int>()) }
var selectedSuggestionIds by remember { mutableStateOf(setOf<Int>()) }
var expandedCategoryId by remember { mutableStateOf<Int?>(null) }
@@ -79,7 +80,7 @@ fun OnboardingFirstTaskContent(
// Kick off both network calls on mount. Suggestions needs a residence;
// the grouped catalog is user-independent and safe to load immediately.
LaunchedEffect(Unit) {
val residence = DataManager.residences.value.firstOrNull()
val residence = dataManager.residences.value.firstOrNull()
if (residence != null) {
viewModel.loadSuggestions(residence.id)
}
@@ -256,7 +257,7 @@ fun OnboardingFirstTaskContent(
}
// Tab content
val residenceForRetry = DataManager.residences.value.firstOrNull()
val residenceForRetry = dataManager.residences.value.firstOrNull()
if (selectedTabIndex == 0 && showTabs) {
ForYouTabContent(
suggestionsState = suggestionsState,
@@ -328,7 +329,7 @@ fun OnboardingFirstTaskContent(
if (selectedBrowseIds.isEmpty() && selectedSuggestionIds.isEmpty()) {
skipOnboarding("user_skip")
} else {
val residence = DataManager.residences.value.firstOrNull()
val residence = dataManager.residences.value.firstOrNull()
if (residence != null) {
val today = DateUtils.getTodayString()
val taskRequests = mutableListOf<TaskCreateRequest>()

View File

@@ -40,7 +40,7 @@ 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
import com.tt.honeyDue.data.LocalDataManager
import com.tt.honeyDue.models.FeatureBenefit
import com.tt.honeyDue.ui.components.common.StandardCard
import com.tt.honeyDue.ui.theme.AppSpacing
@@ -56,7 +56,9 @@ import com.tt.honeyDue.ui.theme.AppSpacing
* - Header ("Choose Your Plan" + subtitle)
* - Two-column comparison table rendered inside a [StandardCard]
* (Feature | Free | Pro) with N rows sourced from
* `DataManager.featureBenefits`, falling back to a 4-row default list.
* `LocalDataManager.current.featureBenefits` (resolves to the
* `DataManager` singleton in production), falling back to a 4-row
* default list.
* - CTA button at the bottom ("Upgrade to Pro") which fires analytics
* event [AnalyticsEvents.PAYWALL_COMPARE_CTA] and invokes
* [onNavigateToUpgrade].
@@ -71,7 +73,8 @@ fun FeatureComparisonScreen(
onNavigateBack: () -> Unit,
onNavigateToUpgrade: () -> Unit,
) {
val benefits by DataManager.featureBenefits.collectAsStateWithLifecycle()
val dataManager = LocalDataManager.current
val benefits by dataManager.featureBenefits.collectAsStateWithLifecycle()
val rows = FeatureComparisonScreenState.resolveFeatureRows(benefits)
Scaffold(