P1: All Kotlin VMs align with DataManager single-source-of-truth

Four broken VMs refactored to derive read-state from IDataManager, three
gaps closed:

1. TaskViewModel: tasksState / tasksByResidenceState / taskCompletionsState
   now derived via .map + .stateIn / combine. isLoading / loadError separated.
2. ResidenceViewModel: residencesState / myResidencesState / summaryState /
   residenceTasksState / residenceContractorsState all derived. 8 mutation
   states retained as independent (legit one-shot feedback).
3. ContractorViewModel: contractorsState / contractorDetailState derived.
   4 mutation states retained.
4. DocumentViewModel: documentsState / documentDetailState derived. 6
   mutation states retained.
5. AuthViewModel: currentUserState now derived from dataManager.currentUser.
   10 other states stay independent (one-shot mutation feedback by design).
6. LookupsViewModel: accepts IDataManager ctor param for test injection
   consistency. Direct-exposure pattern preserved. Legacy ApiResult-wrapped
   states now derived from DataManager instead of manual _xxxState.value =.
7. NotificationPreferencesViewModel: preferencesState derived from new
   IDataManager.notificationPreferences. APILayer writes through on both
   getNotificationPreferences and updateNotificationPreferences.

IDataManager also grew notificationPreferences: StateFlow<NotificationPreference?>.
DataManager, InMemoryDataManager updated. No screen edits needed — screens
consume viewModel.xxxState the same way; the source just switched.

Architecture enforcement test comes in P3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Trey T
2026-04-19 18:42:40 -05:00
parent 2230cde071
commit f0f8dfb68b
11 changed files with 489 additions and 330 deletions
@@ -207,6 +207,13 @@ object DataManager : IDataManager {
private val _taskTemplatesGrouped = MutableStateFlow<TaskTemplatesGroupedResponse?>(null)
override val taskTemplatesGrouped: StateFlow<TaskTemplatesGroupedResponse?> = _taskTemplatesGrouped.asStateFlow()
private val _notificationPreferences = MutableStateFlow<com.tt.honeyDue.models.NotificationPreference?>(null)
override val notificationPreferences: StateFlow<com.tt.honeyDue.models.NotificationPreference?> = _notificationPreferences.asStateFlow()
fun setNotificationPreferences(prefs: com.tt.honeyDue.models.NotificationPreference?) {
_notificationPreferences.value = prefs
}
// Map-based for O(1) ID resolution
private val _residenceTypesMap = MutableStateFlow<Map<Int, ResidenceType>>(emptyMap())
val residenceTypesMap: StateFlow<Map<Int, ResidenceType>> = _residenceTypesMap.asStateFlow()