From 78ae27d104bb1d7a79ebe05e8152cee256f0e03a Mon Sep 17 00:00:00 2001 From: t-regbs Date: Fri, 6 Nov 2026 20:08:19 +0000 Subject: [PATCH 1/6] refactor(storage): remove unused alarm persistence APIs --- .../mathalarm/data/AlarmDataSource.kt | 2 -- .../mathalarm/data/AlarmRepository.kt | 2 -- .../mathalarm/fake/AlarmRepositoryFake.kt | 3 --- .../com/timilehinaregbesola/mathalarm/di/CommonModule.kt | 1 - .../mathalarm/framework/RoomAlarmDataSource.kt | 3 --- .../timilehinaregbesola/mathalarm/framework/Usecases.kt | 2 -- .../mathalarm/framework/database/AlarmDao.kt | 9 --------- .../mathalarm/framework/database/AlarmMapper.kt | 3 --- .../mathalarm/fake/AlarmRepositoryFake.kt | 4 ---- .../presentation/alarmlist/AlarmListViewModelTest.kt | 1 - .../presentation/alarmmath/AlarmMathViewModelTest.kt | 1 - .../alarmsettings/AlarmSettingsViewModelTest.kt | 1 - 12 files changed, 32 deletions(-) diff --git a/core/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/data/AlarmDataSource.kt b/core/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/data/AlarmDataSource.kt index a22d031..a9f6314 100644 --- a/core/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/data/AlarmDataSource.kt +++ b/core/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/data/AlarmDataSource.kt @@ -8,8 +8,6 @@ import kotlinx.coroutines.flow.Flow interface AlarmDataSource { suspend fun addAlarm(alarm: Alarm): Long - suspend fun deleteAlarm(alarm: Alarm) - suspend fun deleteAlarmFromId(id: Long) suspend fun updateAlarm(alarm: Alarm) diff --git a/core/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/data/AlarmRepository.kt b/core/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/data/AlarmRepository.kt index 5d0b199..4464586 100644 --- a/core/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/data/AlarmRepository.kt +++ b/core/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/data/AlarmRepository.kt @@ -7,8 +7,6 @@ import com.timilehinaregbesola.mathalarm.domain.model.Alarm class AlarmRepository(private val dataSource: AlarmDataSource) { suspend fun addAlarm(alarm: Alarm) = dataSource.addAlarm(alarm) - suspend fun deleteAlarm(alarm: Alarm) = dataSource.deleteAlarm(alarm) - suspend fun deleteAlarmWithId(id: Long) = dataSource.deleteAlarmFromId(id) suspend fun updateAlarm(alarm: Alarm) = dataSource.updateAlarm(alarm) diff --git a/core/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/fake/AlarmRepositoryFake.kt b/core/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/fake/AlarmRepositoryFake.kt index c200f55..670322d 100644 --- a/core/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/fake/AlarmRepositoryFake.kt +++ b/core/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/fake/AlarmRepositoryFake.kt @@ -22,9 +22,6 @@ class AlarmRepositoryFake : AlarmDataSource { return id } - override suspend fun deleteAlarm(alarm: Alarm) { - alarmMap.remove(alarm.alarmId) - } override suspend fun deleteAlarmFromId(id: Long) { alarmMap.remove(id) diff --git a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/di/CommonModule.kt b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/di/CommonModule.kt index a18f34a..2e23cb5 100644 --- a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/di/CommonModule.kt +++ b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/di/CommonModule.kt @@ -105,7 +105,6 @@ val commonModule = module { }, onCleanupFailure = cleanupFailure, onCleanupSuccess = { com.timilehinaregbesola.mathalarm.application.AlarmApplicationStatus.clearRecoveryFailure(it) }), rescheduleFutureAlarms = rescheduleFutureAlarms, - scheduleNextAlarm = get(), showAlarm = ShowAlarm(get(), get(), get()), snoozeAlarm = SnoozeAlarm(get(), get(), get(), get(), onSnoozed = { runCatching { analytics.trackSafely(AnalyticsEvents.alarmSnoozed) } diff --git a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/framework/RoomAlarmDataSource.kt b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/framework/RoomAlarmDataSource.kt index 3ee3635..8db1b55 100644 --- a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/framework/RoomAlarmDataSource.kt +++ b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/framework/RoomAlarmDataSource.kt @@ -18,9 +18,6 @@ class RoomAlarmDataSource( return alarmDao.addAlarm(mapper.mapFromDomainModel(alarm)) } - override suspend fun deleteAlarm(alarm: Alarm) = - alarmDao.deleteAlarm(mapper.mapFromDomainModel(alarm)) - override suspend fun deleteAlarmFromId(id: Long) { alarmDao.deleteAlarmWithId(id) } diff --git a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/framework/Usecases.kt b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/framework/Usecases.kt index 7b5a53f..737d3f7 100644 --- a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/framework/Usecases.kt +++ b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/framework/Usecases.kt @@ -9,7 +9,6 @@ import com.timilehinaregbesola.mathalarm.usecases.FindAlarm import com.timilehinaregbesola.mathalarm.usecases.GetSavedAlarms import com.timilehinaregbesola.mathalarm.usecases.RescheduleFutureAlarms import com.timilehinaregbesola.mathalarm.usecases.ScheduleAlarm -import com.timilehinaregbesola.mathalarm.usecases.ScheduleNextAlarm import com.timilehinaregbesola.mathalarm.usecases.ShowAlarm import com.timilehinaregbesola.mathalarm.usecases.SkipNextAlarm import com.timilehinaregbesola.mathalarm.usecases.SnoozeAlarm @@ -34,7 +33,6 @@ data class Usecases( val scheduleAlarm: ScheduleAlarm, val completeAlarm: CompleteAlarm, val rescheduleFutureAlarms: RescheduleFutureAlarms, - val scheduleNextAlarm: ScheduleNextAlarm, val showAlarm: ShowAlarm, val snoozeAlarm: SnoozeAlarm, val cancelAlarm: CancelAlarm, diff --git a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/framework/database/AlarmDao.kt b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/framework/database/AlarmDao.kt index 80026ba..dd08d8c 100644 --- a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/framework/database/AlarmDao.kt +++ b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/framework/database/AlarmDao.kt @@ -13,9 +13,6 @@ interface AlarmDao { @Update suspend fun updateAlarm(alarm: AlarmEntity) - @Delete - suspend fun deleteAlarm(alarm: AlarmEntity) - @Query("DELETE FROM alarms WHERE alarmid = :id") suspend fun deleteAlarmWithId(id: Long?) @@ -25,18 +22,12 @@ interface AlarmDao { @Query("SELECT * FROM alarms WHERE alarmid = :alarmUid LIMIT 1") suspend fun getAlarm(alarmUid: Long?): AlarmEntity? - @Query("SELECT * FROM alarms WHERE alarmid = :alarmUid LIMIT 1") - suspend fun search(alarmUid: Long?): AlarmEntity? - @Query("SELECT * FROM alarms ORDER BY alarmid DESC LIMIT 1") suspend fun getLastAlarm(): AlarmEntity? @Query("SELECT * FROM alarms ORDER BY alarmid DESC") fun getAlarms(): Flow> - @Query("SELECT * FROM alarms WHERE ison = :state") - suspend fun getActiveAlarms(state: Boolean = true): List - @Query("SELECT * FROM alarms WHERE isSaved = :state ORDER BY alarmid DESC") fun getSavedAlarms(state: Boolean = true): Flow> diff --git a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/framework/database/AlarmMapper.kt b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/framework/database/AlarmMapper.kt index 412668e..117163c 100644 --- a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/framework/database/AlarmMapper.kt +++ b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/framework/database/AlarmMapper.kt @@ -83,7 +83,4 @@ class AlarmMapper : DomainMapper { return initial.map { mapToDomainModel(it) } } - fun fromDomainList(initial: List): List { - return initial.map { mapFromDomainModel(it) } - } } diff --git a/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/fake/AlarmRepositoryFake.kt b/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/fake/AlarmRepositoryFake.kt index 5e0891c..eca6f0b 100644 --- a/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/fake/AlarmRepositoryFake.kt +++ b/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/fake/AlarmRepositoryFake.kt @@ -25,10 +25,6 @@ class AlarmRepositoryFake : AlarmDataSource { return id } - override suspend fun deleteAlarm(alarm: Alarm) { - alarmMap.remove(alarm.alarmId) - changed() - } override suspend fun deleteAlarmFromId(id: Long) { alarmMap.remove(id) diff --git a/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmlist/AlarmListViewModelTest.kt b/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmlist/AlarmListViewModelTest.kt index 9f7011c..b06e337 100644 --- a/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmlist/AlarmListViewModelTest.kt +++ b/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmlist/AlarmListViewModelTest.kt @@ -154,7 +154,6 @@ class AlarmListViewModelTest { updateAlarm = UpdateAlarm(repository, alarmInteractor), cancelAlarm = CancelAlarm(alarmInteractor), clearAlarms = ClearAlarms(repository, DeleteAlarm(repository, alarmInteractor, notificationInteractor)), - scheduleNextAlarm = scheduleNextAlarm, rescheduleFutureAlarms = rescheduleFutureAlarms, snoozeAlarm = SnoozeAlarm(dateTimeProvider, notificationInteractor, alarmInteractor, repository), skipNextAlarm = SkipNextAlarm(repository, alarmTimeCalculator, rescheduleFutureAlarms), diff --git a/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmmath/AlarmMathViewModelTest.kt b/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmmath/AlarmMathViewModelTest.kt index c7ba5b4..12d1929 100644 --- a/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmmath/AlarmMathViewModelTest.kt +++ b/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmmath/AlarmMathViewModelTest.kt @@ -96,7 +96,6 @@ class AlarmMathViewModelTest { updateAlarm = UpdateAlarm(repository, alarmInteractor), cancelAlarm = CancelAlarm(alarmInteractor), clearAlarms = ClearAlarms(repository, DeleteAlarm(repository, alarmInteractor, notificationInteractor)), - scheduleNextAlarm = scheduleNextAlarm, rescheduleFutureAlarms = rescheduleFutureAlarms, snoozeAlarm = SnoozeAlarm(dateTimeProvider, notificationInteractor, alarmInteractor, repository), skipNextAlarm = SkipNextAlarm(repository, alarmTimeCalculator, rescheduleFutureAlarms), diff --git a/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmsettings/AlarmSettingsViewModelTest.kt b/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmsettings/AlarmSettingsViewModelTest.kt index 44e85e1..888c4ad 100644 --- a/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmsettings/AlarmSettingsViewModelTest.kt +++ b/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmsettings/AlarmSettingsViewModelTest.kt @@ -512,7 +512,6 @@ class AlarmSettingsViewModelTest { updateAlarm = UpdateAlarm(repository, alarmInteractor), cancelAlarm = CancelAlarm(alarmInteractor), clearAlarms = ClearAlarms(repository, DeleteAlarm(repository, alarmInteractor, notificationInteractor)), - scheduleNextAlarm = scheduleNextAlarm, rescheduleFutureAlarms = rescheduleFutureAlarms, snoozeAlarm = SnoozeAlarm(dateTimeProvider, notificationInteractor, alarmInteractor, repository), skipNextAlarm = SkipNextAlarm(repository, alarmTimeCalculator, rescheduleFutureAlarms), From 7b51cde4c607f36ca2c3d62fb52c52a623c49112 Mon Sep 17 00:00:00 2001 From: t-regbs Date: Fri, 6 Nov 2026 20:15:25 +0000 Subject: [PATCH 2/6] refactor(settings): share stable theme storage conversion --- .../framework/app/AlarmApplication.kt | 9 +-- .../AlarmPreferencesMigrationTest.kt | 7 +- .../whatsnew/AnnouncementMigrationTest.kt | 4 +- .../mathalarm/di/CommonModule.kt | 4 -- .../appsettings/AlarmPreferences.kt | 12 ++-- .../appsettings/AlarmPreferencesImpl.kt | 10 +-- .../appsettings/AppThemeOptionsMapper.kt | 37 ----------- .../appsettings/DataStoreTheme.kt | 26 -------- .../alarmlist/AlarmListViewModelTest.kt | 7 +- .../appsettings/AlarmPreferencesImplTest.kt | 64 ++++++++++++------- .../appsettings/AppSettingsViewModelTest.kt | 13 ++-- .../whatsnew/AnnouncementFeatureTest.kt | 4 +- 12 files changed, 65 insertions(+), 132 deletions(-) delete mode 100644 shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AppThemeOptionsMapper.kt delete mode 100644 shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/DataStoreTheme.kt diff --git a/androidApp/src/main/kotlin/com/timilehinaregbesola/mathalarm/framework/app/AlarmApplication.kt b/androidApp/src/main/kotlin/com/timilehinaregbesola/mathalarm/framework/app/AlarmApplication.kt index c2fe243..83f8394 100644 --- a/androidApp/src/main/kotlin/com/timilehinaregbesola/mathalarm/framework/app/AlarmApplication.kt +++ b/androidApp/src/main/kotlin/com/timilehinaregbesola/mathalarm/framework/app/AlarmApplication.kt @@ -11,7 +11,6 @@ import com.timilehinaregbesola.mathalarm.framework.app.di.appModule import com.timilehinaregbesola.mathalarm.platform.applyPlatformNightMode import com.timilehinaregbesola.mathalarm.presentation.appsettings.AlarmPreferences import com.timilehinaregbesola.mathalarm.presentation.appsettings.AlarmPreferencesImpl -import com.timilehinaregbesola.mathalarm.presentation.appsettings.DataStoreTheme import kotlinx.coroutines.InternalCoroutinesApi import org.koin.android.ext.koin.androidContext import org.koin.core.context.startKoin @@ -34,13 +33,9 @@ class AlarmApplication : Application() { private fun applySavedNightMode() { val themeId = Settings().getInt( AlarmPreferencesImpl.APP_THEME_OPTION, - DataStoreTheme.SYSTEM.id + AlarmPreferences.Theme.SYSTEM.storageId ) - val theme = when (themeId) { - DataStoreTheme.LIGHT.id -> AlarmPreferences.Theme.LIGHT - DataStoreTheme.DARK.id -> AlarmPreferences.Theme.DARK - else -> AlarmPreferences.Theme.SYSTEM - } + val theme = AlarmPreferences.Theme.fromStorageId(themeId) applyPlatformNightMode(this, theme) } } diff --git a/shared/src/androidHostTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AlarmPreferencesMigrationTest.kt b/shared/src/androidHostTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AlarmPreferencesMigrationTest.kt index e9dc1a2..75bb662 100644 --- a/shared/src/androidHostTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AlarmPreferencesMigrationTest.kt +++ b/shared/src/androidHostTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AlarmPreferencesMigrationTest.kt @@ -1,6 +1,5 @@ package com.timilehinaregbesola.mathalarm.presentation.appsettings -import co.touchlab.kermit.Logger import com.russhwolf.settings.MapSettings import io.kotest.matchers.shouldBe import kotlin.test.Test @@ -10,13 +9,13 @@ class AlarmPreferencesMigrationTest { fun `legacy migration marks only the recorded feature and persists the result`() { for (legacyId in listOf("math-challenges-v1", "skip-next-alarm-v1")) { val settings = MapSettings().apply { putString("mathalarm_last_announcement", legacyId) } - val preferences = AlarmPreferencesImpl(AppThemeOptionsMapper(), Logger.withTag("test"), settings) + val preferences = AlarmPreferencesImpl(settings) preferences.hasSeenAnnouncement(legacyId) shouldBe true val other = if (legacyId == "math-challenges-v1") "skip-next-alarm-v1" else "math-challenges-v1" preferences.hasSeenAnnouncement(other) shouldBe false settings.hasKey("mathalarm_last_announcement") shouldBe false - val recreated = AlarmPreferencesImpl(AppThemeOptionsMapper(), Logger.withTag("test"), settings) + val recreated = AlarmPreferencesImpl(settings) recreated.hasSeenAnnouncement(legacyId) shouldBe true recreated.hasSeenAnnouncement(other) shouldBe false } @@ -28,7 +27,7 @@ class AlarmPreferencesMigrationTest { putBoolean("mathalarm_seen_announcement_math-challenges-v1", true) putString("mathalarm_last_announcement", "skip-next-alarm-v1") } - val preferences = AlarmPreferencesImpl(AppThemeOptionsMapper(), Logger.withTag("test"), settings) + val preferences = AlarmPreferencesImpl(settings) preferences.hasSeenAnnouncement("math-challenges-v1") shouldBe true preferences.hasSeenAnnouncement("skip-next-alarm-v1") shouldBe true } diff --git a/shared/src/androidHostTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/whatsnew/AnnouncementMigrationTest.kt b/shared/src/androidHostTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/whatsnew/AnnouncementMigrationTest.kt index 463defa..9ed9e89 100644 --- a/shared/src/androidHostTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/whatsnew/AnnouncementMigrationTest.kt +++ b/shared/src/androidHostTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/whatsnew/AnnouncementMigrationTest.kt @@ -1,15 +1,13 @@ package com.timilehinaregbesola.mathalarm.presentation.whatsnew -import co.touchlab.kermit.Logger import com.russhwolf.settings.MapSettings import com.timilehinaregbesola.mathalarm.presentation.appsettings.AlarmPreferencesImpl -import com.timilehinaregbesola.mathalarm.presentation.appsettings.AppThemeOptionsMapper import kotlin.test.Test import kotlin.test.assertEquals class AnnouncementMigrationTest { private fun preferences(settings: MapSettings) = - AlarmPreferencesImpl(AppThemeOptionsMapper(), Logger.withTag("test"), settings) + AlarmPreferencesImpl(settings) @Test fun oldMathAcknowledgementShowsLaterFeatures() { diff --git a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/di/CommonModule.kt b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/di/CommonModule.kt index 2e23cb5..465f999 100644 --- a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/di/CommonModule.kt +++ b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/di/CommonModule.kt @@ -18,7 +18,6 @@ import com.timilehinaregbesola.mathalarm.presentation.alarmlist.AlarmListViewMod import com.timilehinaregbesola.mathalarm.presentation.alarmmath.AlarmMathViewModel import com.timilehinaregbesola.mathalarm.presentation.alarmsettings.AlarmSettingsViewModel import com.timilehinaregbesola.mathalarm.presentation.appsettings.AlarmPreferencesImpl -import com.timilehinaregbesola.mathalarm.presentation.appsettings.AppThemeOptionsMapper import com.timilehinaregbesola.mathalarm.provider.AlarmTimeCalculator import com.timilehinaregbesola.mathalarm.provider.AlarmTimeCalculatorImpl import com.timilehinaregbesola.mathalarm.provider.DateTimeProvider @@ -47,7 +46,6 @@ import org.koin.dsl.module val commonModule = module { // Mappers single { AlarmMapper() } - single { AppThemeOptionsMapper() } // Coroutine Scope - replaces GlobalScope usage single { AppCoroutineScope(kotlinx.coroutines.Dispatchers.Main.immediate) } @@ -70,8 +68,6 @@ val commonModule = module { single { ReviewEligibilityStore(get()) } single { AlarmPreferencesImpl( - get(), - getWith("AlarmPreferencesImpl"), get() ) } diff --git a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AlarmPreferences.kt b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AlarmPreferences.kt index 7d6a2d1..ba475ba 100644 --- a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AlarmPreferences.kt +++ b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AlarmPreferences.kt @@ -39,10 +39,14 @@ interface AlarmPreferences { */ fun loadAlarmSortOrder(): AlarmSortOrder - enum class Theme { - LIGHT, - DARK, - SYSTEM + enum class Theme(val storageId: Int) { + LIGHT(0), + DARK(1), + SYSTEM(2); + + companion object { + fun fromStorageId(id: Int): Theme = entries.firstOrNull { it.storageId == id } ?: SYSTEM + } } enum class AlarmSortOrder { diff --git a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AlarmPreferencesImpl.kt b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AlarmPreferencesImpl.kt index 15671f5..13a8b87 100644 --- a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AlarmPreferencesImpl.kt +++ b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AlarmPreferencesImpl.kt @@ -2,7 +2,6 @@ package com.timilehinaregbesola.mathalarm.presentation.appsettings import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow -import co.touchlab.kermit.Logger import com.russhwolf.settings.Settings import com.timilehinaregbesola.mathalarm.presentation.appsettings.AlarmPreferences.AlarmSortOrder import com.timilehinaregbesola.mathalarm.presentation.appsettings.AlarmPreferences.Theme @@ -10,8 +9,6 @@ import com.timilehinaregbesola.mathalarm.presentation.appsettings.AlarmPreferenc @OptIn(kotlin.experimental.ExperimentalObjCRefinement::class) @kotlin.native.HiddenFromObjC class AlarmPreferencesImpl( - private val mapper: AppThemeOptionsMapper, - private val logger: Logger, private val settings: Settings ) : AlarmPreferences { companion object { @@ -61,7 +58,7 @@ class AlarmPreferencesImpl( override val alarmSortOrderState = sortOrder.asStateFlow() override fun updateAppTheme(theme: Theme) { - settings.putInt(APP_THEME_OPTION, mapper.toDataStore(theme).id) + settings.putInt(APP_THEME_OPTION, theme.storageId) this.theme.value = theme } @@ -75,9 +72,8 @@ class AlarmPreferencesImpl( override fun loadAlarmSortOrder(): AlarmSortOrder = alarmSortOrderState.value private fun loadAppThemeFromStorage(): Theme { - val id = settings.getInt(APP_THEME_OPTION, DataStoreTheme.SYSTEM.id) - val result = DataStoreTheme.entries.find { it.id == id } ?: DataStoreTheme.SYSTEM - return mapper.toRepo(result) + val id = settings.getInt(APP_THEME_OPTION, Theme.SYSTEM.storageId) + return Theme.fromStorageId(id) } private fun loadAlarmSortOrderFromStorage(): AlarmSortOrder { diff --git a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AppThemeOptionsMapper.kt b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AppThemeOptionsMapper.kt deleted file mode 100644 index 5facab9..0000000 --- a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AppThemeOptionsMapper.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.timilehinaregbesola.mathalarm.presentation.appsettings - -/** - * Maps AppThemeOptions between Repository and DataStore. - */ -@OptIn(kotlin.experimental.ExperimentalObjCRefinement::class) -@kotlin.native.HiddenFromObjC -class AppThemeOptionsMapper { - - /** - * Maps AppThemeOptions from DataStore to Domain. - * - * @param appThemeOptions the object to be converted - * - * @return the converted object - */ - fun toDataStore(appThemeOptions: AlarmPreferences.Theme): DataStoreTheme = - when (appThemeOptions) { - AlarmPreferences.Theme.LIGHT -> DataStoreTheme.LIGHT - AlarmPreferences.Theme.DARK -> DataStoreTheme.DARK - AlarmPreferences.Theme.SYSTEM -> DataStoreTheme.SYSTEM - } - - /** - * Maps AppThemeOptions from DataStore to Repo. - * - * @param appThemeOptions the object to be converted - * - * @return the converted object - */ - fun toRepo(appThemeOptions: DataStoreTheme): AlarmPreferences.Theme = - when (appThemeOptions) { - DataStoreTheme.LIGHT -> AlarmPreferences.Theme.LIGHT - DataStoreTheme.DARK -> AlarmPreferences.Theme.DARK - DataStoreTheme.SYSTEM -> AlarmPreferences.Theme.SYSTEM - } -} diff --git a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/DataStoreTheme.kt b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/DataStoreTheme.kt deleted file mode 100644 index 98d827a..0000000 --- a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/DataStoreTheme.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.timilehinaregbesola.mathalarm.presentation.appsettings - -/** - * Enum to represent the app theme selected by the user. - * - * @property id the theme id - */ -@OptIn(kotlin.experimental.ExperimentalObjCRefinement::class) -@kotlin.native.HiddenFromObjC -enum class DataStoreTheme(val id: Int) { - - /** - * Light app theme. - */ - LIGHT(0), - - /** - * Dark app theme. - */ - DARK(1), - - /** - * System-based app theme. - */ - SYSTEM(2) -} diff --git a/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmlist/AlarmListViewModelTest.kt b/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmlist/AlarmListViewModelTest.kt index b06e337..37549ba 100644 --- a/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmlist/AlarmListViewModelTest.kt +++ b/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmlist/AlarmListViewModelTest.kt @@ -10,7 +10,6 @@ import com.timilehinaregbesola.mathalarm.framework.Usecases import com.timilehinaregbesola.mathalarm.interactors.AlarmInteractor import com.timilehinaregbesola.mathalarm.presentation.appsettings.AlarmPreferences import com.timilehinaregbesola.mathalarm.presentation.appsettings.AlarmPreferencesImpl -import com.timilehinaregbesola.mathalarm.presentation.appsettings.AppThemeOptionsMapper import com.timilehinaregbesola.mathalarm.provider.AlarmTimeCalculatorImpl import com.timilehinaregbesola.mathalarm.usecases.* import com.timilehinaregbesola.mathalarm.utils.AlarmErrorMessage @@ -133,11 +132,7 @@ class AlarmListViewModelTest { notificationInteractor = NotificationInteractorFake() dateTimeProvider = DateTimeProviderFake() permission = AlarmPermissionFake() - preferences = AlarmPreferencesImpl( - mapper = AppThemeOptionsMapper(), - logger = Logger.withTag("AlarmPreferencesImplTest"), - settings = MapSettings() - ) + preferences = AlarmPreferencesImpl(MapSettings()) val alarmTimeCalculator = AlarmTimeCalculatorFake() val scheduleNextAlarm = ScheduleNextAlarm(alarmInteractor, alarmTimeCalculator) diff --git a/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AlarmPreferencesImplTest.kt b/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AlarmPreferencesImplTest.kt index a10057d..7de9dc1 100644 --- a/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AlarmPreferencesImplTest.kt +++ b/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AlarmPreferencesImplTest.kt @@ -1,28 +1,44 @@ package com.timilehinaregbesola.mathalarm.presentation.appsettings -import co.touchlab.kermit.Logger import com.russhwolf.settings.MapSettings import io.kotest.matchers.shouldBe import kotlin.test.Test class AlarmPreferencesImplTest { + @Test + fun `theme encoding keeps released storage IDs and invalid values use system`() { + val released = listOf(0 to AlarmPreferences.Theme.LIGHT, + 1 to AlarmPreferences.Theme.DARK, 2 to AlarmPreferences.Theme.SYSTEM) + for ((id, expected) in released) { + val settings = MapSettings() + settings.putInt(AlarmPreferencesImpl.APP_THEME_OPTION, id) + val preferences = AlarmPreferencesImpl(settings) + preferences.loadAppTheme() shouldBe expected + for ((writtenId, theme) in released) { + preferences.updateAppTheme(theme) + settings.getInt(AlarmPreferencesImpl.APP_THEME_OPTION, -1) shouldBe writtenId + AlarmPreferencesImpl(settings) + .loadAppTheme() shouldBe theme + } + } + for (invalid in listOf(-1, 3, Int.MAX_VALUE)) { + val settings = MapSettings() + settings.putInt(AlarmPreferencesImpl.APP_THEME_OPTION, invalid) + AlarmPreferencesImpl(settings) + .loadAppTheme() shouldBe AlarmPreferences.Theme.SYSTEM + } + AlarmPreferencesImpl(MapSettings()) + .loadAppTheme() shouldBe AlarmPreferences.Theme.SYSTEM + } + @Test fun `alarm sort order persists across preference recreation`() { val settings = MapSettings() - val logger = Logger.withTag("AlarmPreferencesImplTest") - AlarmPreferencesImpl( - mapper = AppThemeOptionsMapper(), - logger = logger, - settings = settings - ).updateAlarmSortOrder(AlarmPreferences.AlarmSortOrder.TIME) + AlarmPreferencesImpl(settings).updateAlarmSortOrder(AlarmPreferences.AlarmSortOrder.TIME) - val recreatedPreferences = AlarmPreferencesImpl( - mapper = AppThemeOptionsMapper(), - logger = logger, - settings = settings - ) + val recreatedPreferences = AlarmPreferencesImpl(settings) recreatedPreferences.loadAlarmSortOrder() shouldBe AlarmPreferences.AlarmSortOrder.TIME } @@ -30,18 +46,18 @@ class AlarmPreferencesImplTest { @Test fun `acknowledged announcement stays dismissed after restart`() { val settings = MapSettings() - val preferences = AlarmPreferencesImpl(AppThemeOptionsMapper(), Logger.withTag("test"), settings) + val preferences = AlarmPreferencesImpl(settings) preferences.hasSeenAnnouncement("math-challenges-v1") shouldBe false preferences.markAnnouncementSeen("math-challenges-v1") preferences.hasSeenAnnouncement("math-challenges-v1") shouldBe true - val recreated = AlarmPreferencesImpl(AppThemeOptionsMapper(), Logger.withTag("test"), settings) + val recreated = AlarmPreferencesImpl(settings) recreated.hasSeenAnnouncement("math-challenges-v1") shouldBe true } @Test fun `new feature release is eligible after previous announcement was acknowledged`() { - val preferences = AlarmPreferencesImpl(AppThemeOptionsMapper(), Logger.withTag("test"), MapSettings()) + val preferences = AlarmPreferencesImpl(MapSettings()) preferences.markAnnouncementSeen("math-challenges-v1") preferences.hasSeenAnnouncement("next-feature-v1") shouldBe false preferences.markAnnouncementSeen("next-feature-v1") @@ -50,12 +66,12 @@ class AlarmPreferencesImplTest { @Test fun `seen features accumulate and survive recreation`() { val settings = MapSettings() - val preferences = AlarmPreferencesImpl(AppThemeOptionsMapper(), Logger.withTag("test"), settings) + val preferences = AlarmPreferencesImpl(settings) preferences.markAnnouncementSeen("math-challenges-v1") preferences.markAnnouncementSeen("skip-next-alarm-v1") preferences.markAnnouncementSeen("math-challenges-v1") - val recreated = AlarmPreferencesImpl(AppThemeOptionsMapper(), Logger.withTag("test"), settings) + val recreated = AlarmPreferencesImpl(settings) recreated.hasSeenAnnouncement("math-challenges-v1") shouldBe true recreated.hasSeenAnnouncement("skip-next-alarm-v1") shouldBe true recreated.hasSeenAnnouncement("future-feature") shouldBe false @@ -64,7 +80,7 @@ class AlarmPreferencesImplTest { @Test fun `sequential updates replace replay batch with only new features`() { val settings = MapSettings() - val prefs = AlarmPreferencesImpl(AppThemeOptionsMapper(), Logger.withTag("test"), settings) + val prefs = AlarmPreferencesImpl(settings) prefs.latestAnnouncementBatch(listOf("math")) shouldBe listOf("math") prefs.markAnnouncementSeen("math") prefs.latestAnnouncementBatch(listOf("math", "skip")) shouldBe listOf("skip") @@ -74,7 +90,7 @@ class AlarmPreferencesImplTest { @Test fun `skipped releases include all missed features but exclude older acknowledged ones`() { - val prefs = AlarmPreferencesImpl(AppThemeOptionsMapper(), Logger.withTag("test"), MapSettings()) + val prefs = AlarmPreferencesImpl(MapSettings()) prefs.markAnnouncementSeen("old") prefs.latestAnnouncementBatch(listOf("old", "math", "skip")) shouldBe listOf("math", "skip") } @@ -83,20 +99,20 @@ class AlarmPreferencesImplTest { fun `update replay survives partial acknowledgement completion and restart`() { val settings = MapSettings() val catalog = listOf("math", "skip") - val prefs = AlarmPreferencesImpl(AppThemeOptionsMapper(), Logger.withTag("test"), settings) + val prefs = AlarmPreferencesImpl(settings) prefs.latestAnnouncementBatch(catalog) shouldBe catalog prefs.markAnnouncementSeen("math") - val recreated = AlarmPreferencesImpl(AppThemeOptionsMapper(), Logger.withTag("test"), settings) + val recreated = AlarmPreferencesImpl(settings) recreated.latestAnnouncementBatch(catalog) shouldBe catalog recreated.markAnnouncementSeen("skip") recreated.latestAnnouncementBatch(catalog) shouldBe catalog - val restarted = AlarmPreferencesImpl(AppThemeOptionsMapper(), Logger.withTag("test"), settings) + val restarted = AlarmPreferencesImpl(settings) restarted.latestAnnouncementBatch(catalog) shouldBe catalog } @Test fun `catalog without batch history and all features acknowledged replays only the latest feature`() { - val prefs = AlarmPreferencesImpl(AppThemeOptionsMapper(), Logger.withTag("test"), MapSettings()) + val prefs = AlarmPreferencesImpl(MapSettings()) prefs.markAnnouncementSeen("math") prefs.markAnnouncementSeen("skip") prefs.latestAnnouncementBatch(listOf("math", "skip")) shouldBe listOf("skip") @@ -104,7 +120,7 @@ class AlarmPreferencesImplTest { @Test fun `later update keeps still unread older features in its batch`() { - val prefs = AlarmPreferencesImpl(AppThemeOptionsMapper(), Logger.withTag("test"), MapSettings()) + val prefs = AlarmPreferencesImpl(MapSettings()) prefs.latestAnnouncementBatch(listOf("math", "skip")) shouldBe listOf("math", "skip") prefs.markAnnouncementSeen("math") prefs.latestAnnouncementBatch(listOf("math", "skip", "future")) shouldBe listOf("skip", "future") diff --git a/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AppSettingsViewModelTest.kt b/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AppSettingsViewModelTest.kt index 1c0385f..55e2ea1 100644 --- a/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AppSettingsViewModelTest.kt +++ b/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/appsettings/AppSettingsViewModelTest.kt @@ -1,6 +1,5 @@ package com.timilehinaregbesola.mathalarm.presentation.appsettings -import co.touchlab.kermit.Logger import com.russhwolf.settings.MapSettings import com.russhwolf.settings.Settings import kotlinx.coroutines.Dispatchers @@ -27,7 +26,7 @@ class AppSettingsViewModelTest { persisted.putString(key, value) } } - val preferences = AlarmPreferencesImpl(AppThemeOptionsMapper(), Logger.withTag("batchRetry"), storage) + val preferences = AlarmPreferencesImpl(storage) val model = AppSettingsViewModel(preferences, listOf("math-challenges-v1", "snooze-settings-v1")) try { val failure = model.state.value.failures.single() @@ -60,7 +59,7 @@ class AppSettingsViewModelTest { // A frozen manual batch and per-feature seen records are separate persisted formats. persisted.putString("mathalarm_announcement_catalog", "math-challenges-v1\nsnooze-settings-v1") persisted.putString("mathalarm_announcement_batch", "snooze-settings-v1") - val preferences = AlarmPreferencesImpl(AppThemeOptionsMapper(), Logger.withTag("announcement"), storage) + val preferences = AlarmPreferencesImpl(storage) val model = AppSettingsViewModel(preferences, listOf("math-challenges-v1", "snooze-settings-v1")) try { assertEquals(listOf("snooze-settings-v1"), model.state.value.announcementIds) @@ -94,7 +93,7 @@ class AppSettingsViewModelTest { persisted.putInt(key, value) } } - val preferences = AlarmPreferencesImpl(AppThemeOptionsMapper(), Logger.withTag("test"), storage) + val preferences = AlarmPreferencesImpl(storage) val model = AppSettingsViewModel(preferences) try { model.selectTheme(AlarmPreferences.Theme.DARK) @@ -105,7 +104,7 @@ class AppSettingsViewModelTest { model.selectTheme(AlarmPreferences.Theme.DARK) advanceUntilIdle() assertEquals(AlarmPreferences.Theme.DARK, model.state.value.theme) - assertEquals(AlarmPreferences.Theme.DARK, AlarmPreferencesImpl(AppThemeOptionsMapper(), Logger.withTag("restored"), persisted).loadAppTheme()) + assertEquals(AlarmPreferences.Theme.DARK, AlarmPreferencesImpl(persisted).loadAppTheme()) assertEquals(listOf(failure), model.state.value.failures) model.acknowledgeFailure(failure.id) assertEquals(emptyList(), model.state.value.failures) @@ -119,7 +118,7 @@ class AppSettingsViewModelTest { fun `settings observe preference changes and acknowledgement survives recreation`() = runTest { Dispatchers.setMain(StandardTestDispatcher(testScheduler)) val storage = MapSettings() - val preferences = AlarmPreferencesImpl(AppThemeOptionsMapper(), Logger.withTag("test"), storage) + val preferences = AlarmPreferencesImpl(storage) val model = AppSettingsViewModel(preferences, listOf("math-challenges-v1", "snooze-settings-v1")) try { preferences.updateAlarmSortOrder(AlarmPreferences.AlarmSortOrder.TIME) @@ -129,7 +128,7 @@ class AppSettingsViewModelTest { assertEquals(AlarmPreferences.Theme.DARK, model.state.value.theme) model.acknowledgeAnnouncement("math-challenges-v1") assertFalse("math-challenges-v1" in model.state.value.unseenAnnouncementIds) - val restored = AlarmPreferencesImpl(AppThemeOptionsMapper(), Logger.withTag("test"), storage) + val restored = AlarmPreferencesImpl(storage) assertEquals(true, restored.hasSeenAnnouncement("math-challenges-v1")) assertEquals(AlarmPreferences.Theme.DARK, restored.themeState.value) assertEquals(AlarmPreferences.AlarmSortOrder.TIME, restored.alarmSortOrderState.value) diff --git a/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/whatsnew/AnnouncementFeatureTest.kt b/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/whatsnew/AnnouncementFeatureTest.kt index b2e1409..eeb8e58 100644 --- a/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/whatsnew/AnnouncementFeatureTest.kt +++ b/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/whatsnew/AnnouncementFeatureTest.kt @@ -1,15 +1,13 @@ package com.timilehinaregbesola.mathalarm.presentation.whatsnew -import co.touchlab.kermit.Logger import com.russhwolf.settings.MapSettings import com.timilehinaregbesola.mathalarm.presentation.appsettings.AlarmPreferencesImpl -import com.timilehinaregbesola.mathalarm.presentation.appsettings.AppThemeOptionsMapper import kotlin.test.Test import kotlin.test.assertEquals class AnnouncementFeatureTest { private fun preferences(settings: MapSettings = MapSettings()) = - AlarmPreferencesImpl(AppThemeOptionsMapper(), Logger.withTag("test"), settings) + AlarmPreferencesImpl(settings) @Test fun skippingReleasesIncludesAllFeaturesInReleaseOrder() { From aed6de6609398e532c0b45763769d5bc90a15f2a Mon Sep 17 00:00:00 2001 From: t-regbs Date: Fri, 6 Nov 2026 20:16:08 +0000 Subject: [PATCH 3/6] test(scheduling): reproduce unsaved alarm identity fallback --- .../mathalarm/usecases/ScheduleAlarmTest.kt | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/core/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/usecases/ScheduleAlarmTest.kt b/core/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/usecases/ScheduleAlarmTest.kt index ef7ebc3..97628da 100644 --- a/core/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/usecases/ScheduleAlarmTest.kt +++ b/core/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/usecases/ScheduleAlarmTest.kt @@ -33,6 +33,32 @@ class ScheduleAlarmTest { alarmInteractor.clear() } + @Test + fun `unsaved identity cannot schedule or change another stored alarm`() = runTest { + val first = Alarm(alarmId = 11, hour = 6, minute = 30, isSaved = true) + val latest = Alarm(alarmId = 12, hour = 8, minute = 45, isSaved = true) + addAlarmUseCase(first) + addAlarmUseCase(latest) + kotlin.test.assertFailsWith { + scheduleAlarmUseCase(Alarm(hour = 9, minute = 15), reschedule = true) + } + assertEquals(first, findAlarmUseCase(first.alarmId)) + assertEquals(latest, findAlarmUseCase(latest.alarmId)) + assertEquals(emptyMap(), alarmInteractor.getScheduledAlarms()) + } + + @Test + fun `negative fixture identity schedules only its own persisted row`() = runTest { + val fixture = Alarm(alarmId = -42, hour = 6, minute = 30, isSaved = true) + val unrelated = Alarm(alarmId = 12, hour = 8, minute = 45, isSaved = true) + addAlarmUseCase(fixture) + addAlarmUseCase(unrelated) + scheduleAlarmUseCase(fixture, reschedule = false) + assertEquals(setOf(fixture.alarmId), alarmInteractor.getScheduledAlarms().keys) + assertEquals(alarmTimeCalculator.calculateAlarmTimes(fixture).sorted(), findAlarmUseCase(-42)?.pendingTimes) + assertEquals(unrelated, findAlarmUseCase(unrelated.alarmId)) + } + @Test fun `test if alarm is scheduled`() = runTest { val newAlarm = Alarm(alarmId = 2, vibrate = true) From 9776e5b614069c645f113c5c0d2849c324fa010d Mon Sep 17 00:00:00 2001 From: t-regbs Date: Fri, 6 Nov 2026 20:17:42 +0000 Subject: [PATCH 4/6] fix(scheduling): reject unsaved alarm identity --- .../mathalarm/usecases/ScheduleAlarm.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/core/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/usecases/ScheduleAlarm.kt b/core/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/usecases/ScheduleAlarm.kt index 608f515..72ffba9 100644 --- a/core/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/usecases/ScheduleAlarm.kt +++ b/core/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/usecases/ScheduleAlarm.kt @@ -20,11 +20,8 @@ class ScheduleAlarm( private val onCleanupSuccess: (Long) -> Unit = {}, ) { suspend operator fun invoke(alarm: Alarm, reschedule: Boolean) { - val saved = if (alarm.alarmId == 0L) { - alarmRepository.getLatestAlarm() - } else { - alarmRepository.findAlarm(alarm.alarmId) - } ?: return + require(alarm.alarmId != 0L) { "Save an alarm before scheduling it" } + val saved = alarmRepository.findAlarm(alarm.alarmId) ?: return val times = alarmTimeCalculator.calculateAlarmTimes(saved.copy(skippedDate = null)).sorted() val planned = saved.copy( isOn = times.isNotEmpty(), From b530e31b98ed2764f5165159363f927ebc36cbed Mon Sep 17 00:00:00 2001 From: t-regbs Date: Fri, 6 Nov 2026 20:22:50 +0000 Subject: [PATCH 5/6] refactor(domain): separate alarm values from draft creation time --- .../mathalarm/navigation/NavGraph.kt | 2 +- .../mathalarm/domain/model/Alarm.kt | 10 +++---- .../mathalarm/domain/model/AlarmValueTest.kt | 27 +++++++++++++++++++ .../iosApp/NativeSettingsVerification.swift | 3 +-- .../mathalarm/presentation/SharedFeatures.kt | 2 +- .../alarmlist/AlarmListViewModel.kt | 2 +- .../alarmsettings/AlarmSettingsViewModel.kt | 3 +-- .../framework/database/AlarmMapperTest.kt | 22 +++++++++++++++ 8 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 core/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/domain/model/AlarmValueTest.kt create mode 100644 shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/framework/database/AlarmMapperTest.kt diff --git a/androidApp/src/main/kotlin/com/timilehinaregbesola/mathalarm/navigation/NavGraph.kt b/androidApp/src/main/kotlin/com/timilehinaregbesola/mathalarm/navigation/NavGraph.kt index c09e442..ff4f43c 100644 --- a/androidApp/src/main/kotlin/com/timilehinaregbesola/mathalarm/navigation/NavGraph.kt +++ b/androidApp/src/main/kotlin/com/timilehinaregbesola/mathalarm/navigation/NavGraph.kt @@ -343,7 +343,7 @@ fun NavGraph( announcementIds = null when (feature) { AnnouncementFeature.MATH_CHALLENGES, AnnouncementFeature.SNOOZE_SETTINGS -> { - val alarmJson = Json.encodeToString(AlarmMapper().mapFromDomainModel(Alarm())) + val alarmJson = Json.encodeToString(AlarmMapper().mapFromDomainModel(Alarm.newAlarm())) backStack.add(SettingsSheet(alarmJson)) } AnnouncementFeature.SKIP_NEXT -> { diff --git a/core/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/domain/model/Alarm.kt b/core/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/domain/model/Alarm.kt index 9973a19..f9d970d 100644 --- a/core/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/domain/model/Alarm.kt +++ b/core/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/domain/model/Alarm.kt @@ -7,11 +7,8 @@ import kotlin.time.Clock data class Alarm( val alarmId: Long = 0L, - val newDateTime: LocalDateTime = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()), - val newHour: Int = newDateTime.hour, - val newMinute: Int = newDateTime.minute, - val hour: Int = newHour, - val minute: Int = newMinute, + val hour: Int = 0, + val minute: Int = 0, val repeat: Boolean = false, val repeatDays: String = "FFFFFFF", val isOn: Boolean = false, @@ -53,6 +50,9 @@ data class Alarm( get() = isOn && skippedDate == null && (repeat || repeatDays.count { it == 'T' } > 1) companion object { + fun newAlarm(now: LocalDateTime = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault())): Alarm = + Alarm(hour = now.hour, minute = now.minute) + // Retain the persisted value for alarms saved by schema version 5. const val SCHEDULING_IN_PROGRESS = "Scheduling has not completed" } diff --git a/core/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/domain/model/AlarmValueTest.kt b/core/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/domain/model/AlarmValueTest.kt new file mode 100644 index 0000000..ea4626f --- /dev/null +++ b/core/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/domain/model/AlarmValueTest.kt @@ -0,0 +1,27 @@ +package com.timilehinaregbesola.mathalarm.domain.model + +import kotlinx.datetime.LocalDateTime +import kotlin.test.Test +import kotlin.test.assertEquals + +class AlarmValueTest { + @Test + fun `creation date does not participate in alarm equality`() { + val first = Alarm.newAlarm(LocalDateTime(2030, 1, 6, 7, 30)) + val later = Alarm.newAlarm(LocalDateTime(2040, 6, 1, 7, 30)) + assertEquals(first, later) + assertEquals(first.hashCode(), later.hashCode()) + assertEquals(Alarm(hour = 7, minute = 30), first) + } + + @Test + fun `new draft takes hour and minute from one supplied instant`() { + for (now in listOf(LocalDateTime(2030, 12, 31, 23, 59), LocalDateTime(2031, 1, 1, 0, 0))) { + val draft = Alarm.newAlarm(now) + assertEquals(now.hour, draft.hour) + assertEquals(now.minute, draft.minute) + assertEquals(0L, draft.alarmId) + assertEquals(false, draft.isSaved) + } + } +} diff --git a/iosApp/iosApp/NativeSettingsVerification.swift b/iosApp/iosApp/NativeSettingsVerification.swift index a6ad428..9ad84b6 100644 --- a/iosApp/iosApp/NativeSettingsVerification.swift +++ b/iosApp/iosApp/NativeSettingsVerification.swift @@ -56,8 +56,7 @@ enum NativeSettingsVerification { let seedResult = first.model.state.results.first { $0.event is AlarmSettingsViewModel.UiEventTestAlarm }! let seed = (seedResult.event as! AlarmSettingsViewModel.UiEventTestAlarm).alarm first.model.acknowledgeResult(id: seedResult.id) - let secondAlarm = seed.doCopy(alarmId: -6001, newDateTime: seed.newDateTime, - newHour: seed.newHour, newMinute: seed.newMinute, hour: seed.hour, minute: seed.minute, + let secondAlarm = seed.doCopy(alarmId: -6001, hour: seed.hour, minute: seed.minute, repeat: seed.repeat, repeatDays: seed.repeatDays, isOn: seed.isOn, difficulty: seed.difficulty, questionCount: seed.questionCount, challengeOperations: seed.challengeOperations, additionRange: seed.additionRange, factorRange: seed.factorRange, difficultyMix: seed.difficultyMix, diff --git a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/SharedFeatures.kt b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/SharedFeatures.kt index 22099e3..49e53a7 100644 --- a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/SharedFeatures.kt +++ b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/SharedFeatures.kt @@ -13,7 +13,7 @@ object SharedFeatures { private val challenges = mutableMapOf() fun list(): AlarmListViewModel = KoinPlatform.getKoin().get() fun settings(): AppSettingsViewModel = KoinPlatform.getKoin().get() - fun newEditor(sessionId: String): AlarmSettingsViewModel = editor(sessionId, Alarm()) + fun newEditor(sessionId: String): AlarmSettingsViewModel = editor(sessionId, Alarm.newAlarm()) fun editor(sessionId: String, alarm: Alarm): AlarmSettingsViewModel { require(sessionId.isNotBlank()) return editors[sessionId]?.takeUnless { it.isClosed } ?: KoinPlatform.getKoin().get().also { diff --git a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmlist/AlarmListViewModel.kt b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmlist/AlarmListViewModel.kt index 6d2f5d3..927ed4b 100644 --- a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmlist/AlarmListViewModel.kt +++ b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmlist/AlarmListViewModel.kt @@ -88,7 +88,7 @@ class AlarmListViewModel internal constructor( if (isClosed) return when (event) { is AlarmListEvent.OnEditAlarmClick -> sendUiEvent(Navigate(event.alarm)) - is AlarmListEvent.OnAddAlarmClick -> sendUiEvent(Navigate(Alarm())) + is AlarmListEvent.OnAddAlarmClick -> sendUiEvent(Navigate(Alarm.newAlarm())) is AlarmListEvent.OnAlarmOnChange -> setEnabled(event.alarm, event.isOn) is AlarmListEvent.OnSkipNextClick -> launchCommand { if (!skipNextSupported) return@launchCommand diff --git a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmsettings/AlarmSettingsViewModel.kt b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmsettings/AlarmSettingsViewModel.kt index f77f573..7d4d2a0 100644 --- a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmsettings/AlarmSettingsViewModel.kt +++ b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmsettings/AlarmSettingsViewModel.kt @@ -159,8 +159,7 @@ class AlarmSettingsViewModel internal constructor( else -> AlarmEditorValidation.NONE } val dirty = initialDraft?.let { initial -> - createAlarm().copy(newDateTime = initial.newDateTime, - newHour = initial.newHour, newMinute = initial.newMinute) != initial + createAlarm() != initial } ?: false mutableState.update { it.copy(validation = validation, hasUnsavedChanges = dirty) } } diff --git a/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/framework/database/AlarmMapperTest.kt b/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/framework/database/AlarmMapperTest.kt new file mode 100644 index 0000000..f2f071f --- /dev/null +++ b/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/framework/database/AlarmMapperTest.kt @@ -0,0 +1,22 @@ +package com.timilehinaregbesola.mathalarm.framework.database + +import com.timilehinaregbesola.mathalarm.domain.model.Alarm +import kotlin.test.Test +import kotlin.test.assertEquals + +class AlarmMapperTest { + @Test + fun `stored alarm equality is stable across reconstruction`() { + val mapper = AlarmMapper() + val saved = Alarm(alarmId = 42, hour = 6, minute = 30, isSaved = true, + title = "Morning", isOn = true, pendingTimes = listOf(1000L, 2000L), + scheduleInitialized = true, activeAt = 1000L) + val entity = mapper.mapFromDomainModel(saved) + val first = mapper.mapToDomainModel(entity) + val reconstructed = mapper.mapToDomainModel(entity) + assertEquals(saved, first) + assertEquals(first, reconstructed) + assertEquals(first.hashCode(), reconstructed.hashCode()) + assertEquals(entity, mapper.mapFromDomainModel(reconstructed)) + } +} From a11554a943aa455968ab7b446633a41cb0e89268 Mon Sep 17 00:00:00 2001 From: t-regbs Date: Fri, 6 Nov 2026 20:27:32 +0000 Subject: [PATCH 6/6] refactor(challenge): inject the shared coordinator directly --- .../mathalarm/di/CommonModule.kt | 2 +- .../alarmmath/AlarmMathViewModel.kt | 12 +----- .../alarmmath/AlarmMathViewModelTest.kt | 40 ++++++++++++------- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/di/CommonModule.kt b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/di/CommonModule.kt index 465f999..d7097e9 100644 --- a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/di/CommonModule.kt +++ b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/di/CommonModule.kt @@ -117,7 +117,7 @@ val commonModule = module { single { ChallengeProgressStore(get()) } viewModel { AlarmSettingsViewModel(get(), get(), get()) } single { com.timilehinaregbesola.mathalarm.application.ChallengeCoordinator(get(), get(), getWith("ChallengeCoordinator"), get(), get(), { com.timilehinaregbesola.mathalarm.platform.getDefaultAlarmTone() }, { com.timilehinaregbesola.mathalarm.platform.PlatformPreviewVibration() }) } - viewModel { AlarmMathViewModel(get(), get(), getWith("AlarmMathViewModel"), get(), get(), get()) } + viewModel { AlarmMathViewModel(get()) } viewModel { com.timilehinaregbesola.mathalarm.presentation.appsettings.AppSettingsViewModel(get(), listOf("math-challenges-v1") + (if (com.timilehinaregbesola.mathalarm.platform.supportsSkipNext()) listOf("skip-next-alarm-v1") else emptyList()) + listOf("snooze-settings-v1")) } } diff --git a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmmath/AlarmMathViewModel.kt b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmmath/AlarmMathViewModel.kt index 26ffa0a..75ec08c 100644 --- a/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmmath/AlarmMathViewModel.kt +++ b/shared/src/commonMain/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmmath/AlarmMathViewModel.kt @@ -1,13 +1,8 @@ package com.timilehinaregbesola.mathalarm.presentation.alarmmath -import co.touchlab.kermit.Logger -import com.timilehinaregbesola.mathalarm.analytics.AnalyticsTracker -import com.timilehinaregbesola.mathalarm.analytics.NoopAnalyticsTracker import com.timilehinaregbesola.mathalarm.application.ChallengeCoordinator import com.timilehinaregbesola.mathalarm.application.ChallengeSession import com.timilehinaregbesola.mathalarm.domain.model.Alarm -import com.timilehinaregbesola.mathalarm.framework.Usecases -import com.timilehinaregbesola.mathalarm.interactors.AudioPlayer import com.timilehinaregbesola.mathalarm.presentation.SharedFeatureViewModel import com.rickclephas.kmp.observableviewmodel.MutableStateFlow import com.rickclephas.kmp.observableviewmodel.coroutineScope @@ -19,12 +14,7 @@ import kotlinx.coroutines.flow.asStateFlow /** Observation adapter over the application session; no durable work belongs to its scope. */ class AlarmMathViewModel internal constructor( - usecases: Usecases, - audioPlayer: AudioPlayer, - logger: Logger, - progressStore: ChallengeProgressStore, - analytics: AnalyticsTracker = NoopAnalyticsTracker, - private val coordinator: ChallengeCoordinator = ChallengeCoordinator(usecases, audioPlayer, logger, progressStore, analytics), + private val coordinator: ChallengeCoordinator, ) : SharedFeatureViewModel() { private val mutable = MutableStateFlow(viewModelScope, ChallengeState()) @NativeCoroutinesState diff --git a/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmmath/AlarmMathViewModelTest.kt b/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmmath/AlarmMathViewModelTest.kt index 12d1929..d09f926 100644 --- a/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmmath/AlarmMathViewModelTest.kt +++ b/shared/src/commonTest/kotlin/com/timilehinaregbesola/mathalarm/presentation/alarmmath/AlarmMathViewModelTest.kt @@ -6,6 +6,9 @@ import com.timilehinaregbesola.mathalarm.data.AlarmRepository import com.timilehinaregbesola.mathalarm.domain.model.Alarm import com.timilehinaregbesola.mathalarm.analytics.AnalyticsEvent import com.timilehinaregbesola.mathalarm.analytics.AnalyticsTracker +import com.timilehinaregbesola.mathalarm.analytics.NoopAnalyticsTracker +import com.timilehinaregbesola.mathalarm.application.ChallengeCoordinator +import com.timilehinaregbesola.mathalarm.interactors.AudioPlayer import com.timilehinaregbesola.mathalarm.fake.AlarmInteractorFake import com.timilehinaregbesola.mathalarm.fake.AlarmRepositoryFake import com.timilehinaregbesola.mathalarm.fake.AlarmTimeCalculatorFake @@ -53,6 +56,15 @@ import kotlinx.coroutines.test.setMain @OptIn(ExperimentalCoroutinesApi::class) class AlarmMathViewModelTest { + private fun createViewModel( + usecases: Usecases, + audioPlayer: AudioPlayer, + logger: Logger, + progressStore: ChallengeProgressStore, + analytics: AnalyticsTracker = NoopAnalyticsTracker, + ) = AlarmMathViewModel( + ChallengeCoordinator(usecases, audioPlayer, logger, progressStore, analytics), + ) private lateinit var progressStore: ChallengeProgressStore private lateinit var progressSettings: com.russhwolf.settings.MapSettings @@ -101,7 +113,7 @@ class AlarmMathViewModelTest { skipNextAlarm = SkipNextAlarm(repository, alarmTimeCalculator, rescheduleFutureAlarms), ) - viewModel = AlarmMathViewModel( + viewModel = createViewModel( usecases = usecases, audioPlayer = audioPlayer, logger = Logger.withTag("AlarmMathViewModelTest"), @@ -116,7 +128,7 @@ class AlarmMathViewModelTest { val audio = object : com.timilehinaregbesola.mathalarm.interactors.AudioPlayer by audioPlayer { override fun startAlarmAudio() { starts++; audioPlayer.startAlarmAudio() } } - val vm = AlarmMathViewModel(usecases, audio, Logger.withTag("instantPreview"), progressStore) + val vm = createViewModel(usecases, audio, Logger.withTag("instantPreview"), progressStore) val blocked = kotlinx.coroutines.CompletableDeferred() val command = launch { usecases.command { blocked.await() } } runCurrent() @@ -184,7 +196,7 @@ class AlarmMathViewModelTest { viewModel.questionCount shouldBe 2 } - private fun recreatedViewModel() = AlarmMathViewModel( + private fun recreatedViewModel() = createViewModel( usecases, AudioPlayerFake(), Logger.withTag("restored"), ChallengeProgressStore(progressSettings), analytics ) @@ -306,7 +318,7 @@ class AlarmMathViewModelTest { val alarm = Alarm(alarmId = 991, isOn = true, isSaved = true, activeAt = 1000) usecases.addAlarm(alarm) val commands = usecases.copy(findAlarm = FindAlarm(AlarmRepository(source))) - val vm = AlarmMathViewModel(commands, audioPlayer, Logger.withTag("retry"), progressStore) + val vm = createViewModel(commands, audioPlayer, Logger.withTag("retry"), progressStore) vm.initializeOccurrence(alarm.alarmId, alarm.activeAt) shouldBe false vm.state.value.readiness shouldBe ChallengeReadiness.ERROR vm.initializeOccurrence(alarm.alarmId, alarm.activeAt) shouldBe false @@ -355,7 +367,7 @@ class AlarmMathViewModelTest { usecases.addAlarm(first); usecases.addAlarm(later) val coordinator = com.timilehinaregbesola.mathalarm.application.ChallengeCoordinator(usecases, audioPlayer, Logger.withTag("retained"), progressStore, analytics) - fun owner() = AlarmMathViewModel(usecases, audioPlayer, Logger.withTag("owner"), progressStore, analytics, coordinator) + fun owner() = AlarmMathViewModel(coordinator) val a = owner(); val b = owner(); val queued = owner() a.initializeChallenge(first, false) shouldBe true val problem = a.currentProblem!! @@ -425,7 +437,7 @@ class AlarmMathViewModelTest { } val alarm = Alarm(alarmId = 998, isOn = true, isSaved = true, activeAt = 1000) usecases.addAlarm(alarm) - val vm = AlarmMathViewModel(usecases, audioPlayer, Logger.withTag("progressRetry"), ChallengeProgressStore(settings)) + val vm = createViewModel(usecases, audioPlayer, Logger.withTag("progressRetry"), ChallengeProgressStore(settings)) vm.initializeChallenge(alarm, false) shouldBe false val firstFailure = vm.state.value firstFailure.problems.isNotEmpty() shouldBe true @@ -457,7 +469,7 @@ class AlarmMathViewModelTest { } val alarm = Alarm(alarmId = 1001, isOn = true, isSaved = true, activeAt = 1000, questionCount = 2) usecases.addAlarm(alarm) - val vm = AlarmMathViewModel(usecases, audioPlayer, Logger.withTag("progressUpdate"), ChallengeProgressStore(settings)) + val vm = createViewModel(usecases, audioPlayer, Logger.withTag("progressUpdate"), ChallengeProgressStore(settings)) vm.initializeChallenge(alarm, false) shouldBe true val problem = vm.currentProblem!! vm.onEvent(MathScreenEvent.EnteredAnswer(problem.answer.toString())) @@ -487,7 +499,7 @@ class AlarmMathViewModelTest { } val coordinator = com.timilehinaregbesola.mathalarm.application.ChallengeCoordinator(usecases, audio, Logger.withTag("partialAudio"), progressStore, analytics, previewVibrationFactory = { vibration }) - val vm = AlarmMathViewModel(usecases, audio, Logger.withTag("previewFailure"), progressStore, analytics, coordinator) + val vm = AlarmMathViewModel(coordinator) vm.initializeChallenge(Alarm(vibrate = true), true) shouldBe true vm.state.value.results.last().outcome shouldBe ChallengeOutcome.Failure(AlarmErrorMessage.TONE) audioPlayer.isStopped shouldBe true @@ -506,8 +518,8 @@ class AlarmMathViewModelTest { } val coordinator = com.timilehinaregbesola.mathalarm.application.ChallengeCoordinator(usecases, audio, Logger.withTag("audioOwner"), progressStore, analytics) - val preview = AlarmMathViewModel(usecases, audio, Logger.withTag("preview"), progressStore, analytics, coordinator) - val real = AlarmMathViewModel(usecases, audio, Logger.withTag("real"), progressStore, analytics, coordinator) + val preview = AlarmMathViewModel(coordinator) + val real = AlarmMathViewModel(coordinator) preview.initializeChallenge(Alarm(), true) shouldBe true audioPlayer.isPlaying shouldBe true val alarm = Alarm(alarmId = 999, isOn = true, isSaved = true, activeAt = 1000) @@ -538,7 +550,7 @@ class AlarmMathViewModelTest { } val commands = usecases.copy(completeAlarm = CompleteAlarm(faultRepository, backend, notificationInteractor, dateTimeProvider)) - val vm = AlarmMathViewModel(commands, audioPlayer, Logger.withTag("finalWriteRetry"), progressStore) + val vm = createViewModel(commands, audioPlayer, Logger.withTag("finalWriteRetry"), progressStore) val alarm = Alarm(alarmId = 1010, activeAt = 1000, isOn = true, isSaved = true) commands.addAlarm(alarm) vm.initializeChallenge(alarm, false) shouldBe true @@ -621,7 +633,7 @@ class AlarmMathViewModelTest { val commands = usecases.copy(completeAlarm = CompleteAlarm(repository, backend, notificationInteractor, dateTimeProvider)) val coordinator = com.timilehinaregbesola.mathalarm.application.ChallengeCoordinator(commands, audio, Logger.withTag("orderedIsolation"), progressStore, analytics) - fun owner() = AlarmMathViewModel(commands, audio, Logger.withTag("orderedOwner"), progressStore, analytics, coordinator) + fun owner() = AlarmMathViewModel(coordinator) val first = Alarm(alarmId = 1012, activeAt = 1000, isOn = true, isSaved = true, questionCount = 2) val second = first.copy(alarmId = 1013, activeAt = 2000) commands.addAlarm(first); commands.addAlarm(second) @@ -694,7 +706,7 @@ class AlarmMathViewModelTest { val commands = usecases.copy( snoozeAlarm = SnoozeAlarm(dateTimeProvider, notificationInteractor, backend, repository) ) - viewModel = AlarmMathViewModel(commands, audioPlayer, Logger.withTag("ErrorTest"), progressStore) + viewModel = createViewModel(commands, audioPlayer, Logger.withTag("ErrorTest"), progressStore) val alarm = Alarm(alarmId = 804, isOn = true, isSaved = true, snooze = 5, activeAt = 1000, questionCount = 3) commands.addAlarm(alarm) viewModel.initializeChallenge(alarm, preview = false) @@ -1075,7 +1087,7 @@ class AlarmMathViewModelTest { val audio = object : com.timilehinaregbesola.mathalarm.interactors.AudioPlayer by audioPlayer { override fun stop() { stops++; audioPlayer.stop() } } - val vm = AlarmMathViewModel(commands, audio, Logger.withTag("postAcceptanceRead"), progressStore) + val vm = createViewModel(commands, audio, Logger.withTag("postAcceptanceRead"), progressStore) val alarm = Alarm(alarmId = if (snooze) 4007 else 4006, isOn = true, isSaved = true, activeAt = 1000) commands.addAlarm(alarm) vm.initializeChallenge(alarm, false) shouldBe true