Coverage Summary for Class: LocalDataResetRepositoryImpl (com.georgeci.moneysurfer.data.repository)
| Class |
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| LocalDataResetRepositoryImpl |
100%
(4/4)
|
87.5%
(7/8)
|
100%
(25/25)
|
100%
(236/236)
|
| LocalDataResetRepositoryImpl$clearAll$1 |
|
| Total |
100%
(4/4)
|
87.5%
(7/8)
|
100%
(25/25)
|
100%
(236/236)
|
package com.georgeci.moneysurfer.data.repository
import com.georgeci.moneysurfer.appconfig.ConfigKey
import com.georgeci.moneysurfer.appconfig.ConfigKeyGroup
import com.georgeci.moneysurfer.appconfig.LayerValue
import com.georgeci.moneysurfer.appconfig.LocalConfigSource
import com.georgeci.moneysurfer.appconfig.SessionConfigOverlay
import com.georgeci.moneysurfer.appconfig.SettingKey
import com.georgeci.moneysurfer.data.db.MoneySurferDatabase
import com.georgeci.moneysurfer.domain.repositories.LocalDataResetRepository
import org.koin.core.annotation.Single
/**
* Takes the database rather than one DAO per table — the parameter list grew
* with every new table and bought nothing, since this class only ever fans out
* `deleteAll()`.
*
* The configuration arguments are the exception: synced settings are account data and are wiped
* here with everything else, but the *running UI* must not notice, so their current values are
* lifted into the in-memory overlay on the way out.
*/
@Single(binds = [LocalDataResetRepository::class])
class LocalDataResetRepositoryImpl(
private val database: MoneySurferDatabase,
private val localConfig: LocalConfigSource,
private val overlay: SessionConfigOverlay,
private val keyGroups: List<ConfigKeyGroup>,
) : LocalDataResetRepository {
// FK-safe order: leaf tables first, then parents.
override suspend fun clearAll() {
database.goalContributionDao().deleteAll()
database.goalDao().deleteAll()
database.recurringRuleDao().deleteAll()
database.budgetDao().deleteAll()
database.transactionDao().deleteAll()
database.accountDao().deleteAll()
database.categoryDao().deleteAll()
database.workspaceInviteDao().deleteAll()
database.workspaceMemberDao().deleteAll()
database.workspaceDao().deleteAll()
database.userDao().deleteAll()
// Not user data — public FX quotes — but "reset local data" should leave no rows behind,
// and the cost of forgetting them is a single refetch on the next dashboard open.
database.exchangeRateDao().deleteAll()
// Synced settings are account data. Without this the next user on the device inherits the
// previous one's theme *and* wins every LWW comparison against their own remote documents
// (the local row is newer), so their real settings would never be pulled and never pushed.
//
// Through the config layer rather than `database.configEntryDao()`, unlike every line
// above: the layer keeps an in-memory snapshot behind its synchronous `peek`, refreshed
// only by Room's invalidation through a cold flow with no collector of its own. Deleting
// the rows underneath it would leave `peek` serving this account's values until something
// else happened to observe a setting — and the session overlay is cleared at the next
// sign-in, so the new user would then see them.
holdSyncedSettingsInMemory()
localConfig.clearSynced()
}
/**
* Copies the values about to be deleted into the overlay, so the screen the user is looking at
* keeps its theme, palette and container style instead of flashing back to defaults the moment
* they tap "log out". Nothing persists them, and `SyncedSettingsSession.onSessionStart()` drops
* them before the next session's pull.
*
* Reads through `peek`, so it holds exactly what the Local layer holds: a key the user never set
* is absent and stays absent, which keeps the overlay from materialising defaults that would
* then outrank the next user's pulled values.
*/
private fun holdSyncedSettingsInMemory() {
val held = keyGroups
.flatMap { it.keys }
.filter { it is SettingKey && it.sync }
.mapNotNull { key -> key.encodedLocalValue()?.let { key.name to it } }
overlay.hold(held.toMap())
}
private fun <T : Any> ConfigKey<T>.encodedLocalValue(): String? =
(localConfig.peek(this) as? LayerValue.Present)?.let { codec.encode(it.value) }
}