P0: IDataManager coverage gaps — contractorDetail/documentDetail/taskCompletions/contractorsByResidence

Adds 4 new StateFlow members to IDataManager + DataManager + InMemoryDataManager + FixtureDataManager:
- contractorDetail: Map<Int, Contractor> — cached detail fetches
- documentDetail: Map<Int, Document>
- taskCompletions: Map<Int, List<TaskCompletionResponse>>
- contractorsByResidence: Map<Int, List<ContractorSummary>>

APILayer now writes to these on successful detail/per-residence fetches:
- getTaskCompletions -> setTaskCompletions
- getDocument -> setDocumentDetail
- getContractor -> setContractorDetail
- getContractorsByResidence -> setContractorsForResidence

Fixture populated() seeds contractorDetail + contractorsByResidence.
Populated taskCompletions is empty (Fixtures doesn't define any completions yet).

Foundation for P1 — VMs can now derive every read-state from DataManager
reactively instead of owning independent MutableStateFlow fields.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Trey T
2026-04-19 18:31:06 -05:00
parent f83e89bee3
commit 2230cde071
55 changed files with 69 additions and 1 deletions

View File

@@ -141,6 +141,9 @@ object DataManager : IDataManager {
private val _tasksByResidence = MutableStateFlow<Map<Int, TaskColumnsResponse>>(emptyMap())
override val tasksByResidence: StateFlow<Map<Int, TaskColumnsResponse>> = _tasksByResidence.asStateFlow()
private val _taskCompletions = MutableStateFlow<Map<Int, List<com.tt.honeyDue.models.TaskCompletionResponse>>>(emptyMap())
override val taskCompletions: StateFlow<Map<Int, List<com.tt.honeyDue.models.TaskCompletionResponse>>> = _taskCompletions.asStateFlow()
// ==================== DOCUMENTS ====================
private val _documents = MutableStateFlow<List<Document>>(emptyList())
@@ -149,12 +152,21 @@ object DataManager : IDataManager {
private val _documentsByResidence = MutableStateFlow<Map<Int, List<Document>>>(emptyMap())
override val documentsByResidence: StateFlow<Map<Int, List<Document>>> = _documentsByResidence.asStateFlow()
private val _documentDetail = MutableStateFlow<Map<Int, Document>>(emptyMap())
override val documentDetail: StateFlow<Map<Int, Document>> = _documentDetail.asStateFlow()
// ==================== CONTRACTORS ====================
// Stores ContractorSummary for list views (lighter weight than full Contractor)
private val _contractors = MutableStateFlow<List<ContractorSummary>>(emptyList())
override val contractors: StateFlow<List<ContractorSummary>> = _contractors.asStateFlow()
private val _contractorsByResidence = MutableStateFlow<Map<Int, List<ContractorSummary>>>(emptyMap())
override val contractorsByResidence: StateFlow<Map<Int, List<ContractorSummary>>> = _contractorsByResidence.asStateFlow()
private val _contractorDetail = MutableStateFlow<Map<Int, com.tt.honeyDue.models.Contractor>>(emptyMap())
override val contractorDetail: StateFlow<Map<Int, com.tt.honeyDue.models.Contractor>> = _contractorDetail.asStateFlow()
// ==================== SUBSCRIPTION ====================
private val _subscription = MutableStateFlow<SubscriptionStatus?>(null)
@@ -451,6 +463,11 @@ object DataManager : IDataManager {
persistToDisk()
}
/** Populate the per-task completion cache (used by TaskViewModel's derived flow). */
fun setTaskCompletions(taskId: Int, completions: List<com.tt.honeyDue.models.TaskCompletionResponse>) {
_taskCompletions.value = _taskCompletions.value + (taskId to completions)
}
/**
* Filter cached allTasks by residence ID to avoid separate API call.
* Returns null if allTasks not cached.
@@ -557,6 +574,12 @@ object DataManager : IDataManager {
persistToDisk()
}
/** Populate the per-document detail cache (used by DocumentViewModel's derived flow). */
fun setDocumentDetail(document: Document) {
val id = document.id ?: return
_documentDetail.value = _documentDetail.value + (id to document)
}
/**
* Add a new document to the cache.
* Caches affected: _documents, _documentsByResidence[residenceId]
@@ -606,6 +629,16 @@ object DataManager : IDataManager {
persistToDisk()
}
/** Populate the per-residence contractor cache. */
fun setContractorsForResidence(residenceId: Int, contractors: List<ContractorSummary>) {
_contractorsByResidence.value = _contractorsByResidence.value + (residenceId to contractors)
}
/** Populate the per-contractor detail cache (used by ContractorViewModel's derived flow). */
fun setContractorDetail(contractor: com.tt.honeyDue.models.Contractor) {
_contractorDetail.value = _contractorDetail.value + (contractor.id to contractor)
}
fun addContractor(contractor: ContractorSummary) {
_contractors.value = _contractors.value + contractor
persistToDisk()