From 598c5f9ef31c347141e550c68839643870837a61 Mon Sep 17 00:00:00 2001 From: PJ Date: Thu, 10 Sep 2026 03:44:59 +0530 Subject: [PATCH] refine home options and trivia pacing --- .../main/res/drawable/app_icon_foreground.xml | 22 +-- .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 3163 -> 3432 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 3163 -> 3432 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 1930 -> 2055 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 1930 -> 2055 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 4368 -> 4833 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 4368 -> 4833 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 6799 -> 7566 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 6799 -> 7566 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 9107 -> 10286 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 9107 -> 10286 bytes .../drawable/bert_atlas_mark.svg | 26 +-- .../composeResources/drawable/logo_bert.svg | 11 ++ .../commonMain/kotlin/app/bertsatlas/App.kt | 34 +++- .../kotlin/app/bertsatlas/AppController.kt | 58 +++++- .../kotlin/app/bertsatlas/data/Database.kt | 19 +- .../app/bertsatlas/ui/AtlasComponents.kt | 178 +++++++++++++++--- .../bertsatlas/ui/HomeAndOptionsScreens.kt | 122 ++++++++---- .../kotlin/app/bertsatlas/ui/QuizScreens.kt | 44 +++-- .../ui/ResultsAndProgressScreens.kt | 24 ++- .../app/bertsatlas/ui/SettingsScreens.kt | 16 +- content/brand/berts-atlas-mark.svg | 21 +-- content/brand/berts-atlas-splash.svg | 14 +- content/brand/logo-bert.svg | 11 ++ .../kotlin/app/bertsatlas/domain/Contracts.kt | 3 +- .../app/bertsatlas/domain/ProgressRules.kt | 13 +- .../app/bertsatlas/domain/QuizEngine.kt | 17 +- .../bertsatlas/domain/model/Configuration.kt | 13 +- .../app/bertsatlas/domain/model/Progress.kt | 5 +- spec.md | 29 +-- 30 files changed, 493 insertions(+), 187 deletions(-) create mode 100644 composeApp/src/commonMain/composeResources/drawable/logo_bert.svg create mode 100644 content/brand/logo-bert.svg diff --git a/androidApp/src/main/res/drawable/app_icon_foreground.xml b/androidApp/src/main/res/drawable/app_icon_foreground.xml index 3ff08b7..a9c4a02 100644 --- a/androidApp/src/main/res/drawable/app_icon_foreground.xml +++ b/androidApp/src/main/res/drawable/app_icon_foreground.xml @@ -1,17 +1,13 @@ - - - - - - - - - - - - - + + + + + + + + + diff --git a/androidApp/src/main/res/mipmap-hdpi/ic_launcher.png b/androidApp/src/main/res/mipmap-hdpi/ic_launcher.png index 37d7d186b40d607e2018da39b02f26356a28670e..c008161fc0240c61780c35deb7006fa928a85d80 100644 GIT binary patch delta 3412 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 flha#fWR@fSw+Dz7$^uK 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 flha#+bT`<;8QV1EF#H&#s+h{ZyRhX4=Y z1k{22hZq{r2-Hd;i>1`J*<7Pf_va|SY4A7Ijnj4QRp7}xOfpJ{or(}Inw_5VKGxoi z_Qu*rRjI!(!EtxV4H`Zv#nMD~#_pSjuz-%H>c@mGKH~0elUSiiA%C3c%-Rb*0{l&N z<1}46As{Kf+<;Z*?9(O5l2Nb%ozlZpXSN1LJm zi{i~BMhJo7CrVG8zb7GJp#o&an@H@C848dZZyL7&m%{&l-2XPHhM}0{QbPkwqKX!}{N{+DN z{z=SD_h3Xv7Y0r>dD(dWG-J}!Su{P5xAuR=ncB_ifl#<1-ujjQ{bIG)KCvjipA#7PQ5sESFr%6eB+f~%-W!Of|ikPRq z-N(v<#r$Q(GkAs#kD7-P3weO5rbg1t<}i5xq3eCf1E@maaHJtaejxH%jkMYM-GkHE zu&*d49#IRp>hrN~_ikSJ=X;E{I|ww_^y(EzJ%7~yLJ3x6=tj7$bv1{5XQyXyzP1j3 zFc>YJ=mZ=qFK7A2jf};>8}lCKw;l&F6bSbxq-N9AaK7KMdY}`bngNhfr=$89LUb>$ z3PF03gNByYJ15{ub2ES1xr>c+7E+jt3oSY6e?s zF@L3IgSfSPXW$0q4PW0m0b9N(;pg9*$Ov1nkS8LCTig>OY(gj`j+z;6hdQsL+WHm% zt8!=X?q>(scJ%0ADFHC(L{?H-%5x7t9&X>zH2zWF!o~VxG`;V`nP@cdL{>I)(lffp zUa4nST<1-Ko3kJiw=$ z)!GuV)xHgb-a-kgfla?$!LQ%kOoE~ygdh;qNKg%^ipqP>t&A9xX0c*58WFv&Q(!Y4sh`O)ZXmW+P@MF`4Un_0B}b#84=%*;_ z3I%!jcnRLlPV5OL>=|FS7zK+%Z z+QJJ{e?Y;A^gewZ!5|&MAVoD5oW0tF1SSqoCD?4}5;(u!=&*er4!Ih47PI|w_V@u-C`STcVu?#r={ql=^P*Ki?<pS0-@br>(C+uM_tYsqzi^TImKK7+5Kc!D4}W+(Jn{Wp#yFirvd$e7 zaJQh){Xy+sg{IK`HSk_Vg96mXn?>vpwTcwt(|FT}6%tvbNVE^*O(aH0iLyhnIY*rW zu`}LmV!(Er%X3BnP=t6q0K!-Uz9hE|jOuuFOj4RgES( zv-aqS$AFf&QDFOS_@t!J=Jf2lY3Ne|+FTh$3L+Q84*GkQ96v~4y8@FF-I*Wv?ilbJ zPrJABK_Lu_gybQR8Gwts`@4-7#AOM^60zOp^7Q+%=zo5v(jP6k1wQ})002ovPDHLk FV1f(8_~`%u delta 1897 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 FV1f(8_~`%u delta 1897 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 diff --git a/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_round.png index 084f1489f707fa514af4089db2108c74ce10e608..3c8fc0f2b4cd2a93484b12a3f2f2babac901537b 100644 GIT binary patch literal 7566 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 diff --git a/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 5c77a07ef78e73a4af349e8967dd7bc03023cb24..061f06d82e07c258c4bb5d65f3f83125b96f5a02 100644 GIT binary patch literal 10286 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 - - - - - - - - - - - - - - - + + + + + + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/logo_bert.svg b/composeApp/src/commonMain/composeResources/drawable/logo_bert.svg new file mode 100644 index 0000000..eafccf4 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/logo_bert.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/composeApp/src/commonMain/kotlin/app/bertsatlas/App.kt b/composeApp/src/commonMain/kotlin/app/bertsatlas/App.kt index c712ba3..1e99b23 100644 --- a/composeApp/src/commonMain/kotlin/app/bertsatlas/App.kt +++ b/composeApp/src/commonMain/kotlin/app/bertsatlas/App.kt @@ -80,7 +80,7 @@ private fun AtlasDestination(controller: AppController, state: AtlasAppState) { Destination.Home -> HomeScreen( isFirstLaunch = state.firstLaunch, streakDays = state.streak.currentDays, - progressSummary = "${state.attempts.size} questions explored", + progressSummary = if (state.attempts.size == 1) "1 question explored" else "${state.attempts.size} questions explored", totalPoints = state.attempts.sumOf { it.pointsAwarded } - state.attempts.sumOf { it.hintPointsDeducted }, hasStartedTrivia = !state.firstLaunch, onStart = controller::start, @@ -90,8 +90,15 @@ private fun AtlasDestination(controller: AppController, state: AtlasAppState) { ) is Destination.Options -> OptionsScreen( step = destination.step, - choices = if (destination.step == 1) durationChoices(state.selectedDuration) else topicChoices(), + choices = when (destination.step) { + 1 -> durationChoices(state.selectedDuration) + 2 -> topicChoices(state.selectedTopic) + else -> emptyList() + }, + durationSummary = state.selectedDuration.displayName(), + topicSummary = state.selectedTopic.displayName(), onChoose = controller::chooseOption, + onStart = controller::startSavedOptions, onBack = controller::back, ) Destination.Recovery -> if (run == null) BootstrapScreen() else RecoveryScreen( @@ -262,15 +269,28 @@ private fun RunDestination(controller: AppController, state: AtlasAppState) { } private fun durationChoices(selected: QuizDuration) = QuizDuration.entries.map { duration -> - ChoiceUi(duration.name, when (duration) { QuizDuration.FiveMinutes -> "5 minutes"; QuizDuration.TenMinutes -> "10 minutes"; QuizDuration.TwentyMinutes -> "20 minutes"; QuizDuration.Unlimited -> "Unlimited" }, state = if (duration == selected) AnswerState.Selected else AnswerState.Available) + ChoiceUi(duration.name, duration.displayName(), state = if (duration == selected) AnswerState.Selected else AnswerState.Available) } -private fun topicChoices() = listOf( - ChoiceUi("MostlyFlags", "Mostly flags", "Recognize flags and spell country names"), - ChoiceUi("Capitals", "Capitals", "Match countries and capitals"), - ChoiceUi("MixedGeography", "Mixed geography", "A broader grab bag"), +private fun topicChoices(selected: app.bertsatlas.domain.model.QuizTopic) = listOf( + ChoiceUi("MostlyFlags", "Mostly flags", "Recognize flags and spell country names", state = if (selected == app.bertsatlas.domain.model.QuizTopic.MostlyFlags) AnswerState.Selected else AnswerState.Available), + ChoiceUi("Capitals", "Capitals", "Match countries and capitals", state = if (selected == app.bertsatlas.domain.model.QuizTopic.Capitals) AnswerState.Selected else AnswerState.Available), + ChoiceUi("MixedGeography", "Mixed geography", "A broader grab bag", state = if (selected == app.bertsatlas.domain.model.QuizTopic.MixedGeography) AnswerState.Selected else AnswerState.Available), ) +private fun QuizDuration.displayName() = when (this) { + QuizDuration.FiveMinutes -> "5 minutes" + QuizDuration.TenMinutes -> "10 minutes" + QuizDuration.TwentyMinutes -> "20 minutes" + QuizDuration.Unlimited -> "Unlimited" +} + +private fun app.bertsatlas.domain.model.QuizTopic.displayName() = when (this) { + app.bertsatlas.domain.model.QuizTopic.MostlyFlags -> "Mostly flags" + app.bertsatlas.domain.model.QuizTopic.Capitals -> "Capitals" + app.bertsatlas.domain.model.QuizTopic.MixedGeography -> "Mixed geography" +} + private fun letterSlots( question: Question, placements: List, diff --git a/composeApp/src/commonMain/kotlin/app/bertsatlas/AppController.kt b/composeApp/src/commonMain/kotlin/app/bertsatlas/AppController.kt index 856340f..32496f1 100644 --- a/composeApp/src/commonMain/kotlin/app/bertsatlas/AppController.kt +++ b/composeApp/src/commonMain/kotlin/app/bertsatlas/AppController.kt @@ -78,6 +78,7 @@ data class AtlasAppState( val firstLaunch: Boolean = true, val preferences: SavedPreferences = SavedPreferences(), val selectedDuration: QuizDuration = QuizDuration.FiveMinutes, + val selectedTopic: QuizTopic = QuizTopic.MostlyFlags, val run: QuizRunState? = null, val result: RunResult? = null, val newPersonalBest: Boolean = false, @@ -131,7 +132,16 @@ class AppController( val mastery = readMastery() val attempts = database.progressReader().attemptsNow() val runs = database.progressReader().completedRunsNow() - val streak = database.progressReader().streakNow().toDomain() + val storedStreak = database.progressReader().streakNow().toDomain() + val streak = if (attempts.isNotEmpty() && storedStreak.currentDays == 0) { + attempts.map { it.localEpochDay } + .distinct() + .sorted() + .fold(Streak(), StreakRules::recordActivity) + .also { database.runWriter().saveStreak(it.toEntity()) } + } else { + storedStreak + } val stamps = database.progressReader().stampsNow().mapNotNull { it.toDomain() } val snapshot = database.progressReader().activeRun() val restored = snapshot?.let { @@ -145,6 +155,7 @@ class AppController( firstLaunch = !hasStartedTrivia, preferences = preferences, selectedDuration = enumValueOrDefault(preferences.duration, QuizDuration.FiveMinutes), + selectedTopic = enumValueOrDefault(preferences.topic, QuizTopic.MostlyFlags), run = restored, mastery = mastery, attempts = attempts, @@ -168,7 +179,14 @@ class AppController( } } - fun openOptions() { _state.value = _state.value.copy(destination = Destination.Options(1)) } + fun openOptions() { + val current = _state.value + _state.value = current.copy( + destination = Destination.Options(1), + selectedDuration = enumValueOrDefault(current.preferences.duration, QuizDuration.FiveMinutes), + selectedTopic = enumValueOrDefault(current.preferences.topic, QuizTopic.MostlyFlags), + ) + } fun chooseOption(id: String) { when (val destination = _state.value.destination) { @@ -180,17 +198,31 @@ class AppController( } else { val topic = enumValueOrDefault(id, QuizTopic.MostlyFlags) val duration = _state.value.selectedDuration + _state.value = _state.value.copy(selectedTopic = topic) scope.launch { preferencesStore.saveConfiguration(duration.name, topic.name) val saved = preferencesStore.values.first() - _state.value = _state.value.copy(preferences = saved) - beginRun(QuizConfiguration(duration, topic)) + val current = _state.value + val showSavedStep = current.destination == Destination.Options(2) && + current.selectedDuration == duration && + current.selectedTopic == topic + _state.value = if (showSavedStep) { + current.copy(preferences = saved, destination = Destination.Options(3)) + } else { + current.copy(preferences = saved) + } } } else -> Unit } } + fun startSavedOptions() { + val current = _state.value + if (current.destination != Destination.Options(3)) return + beginRun(QuizConfiguration(current.selectedDuration, current.selectedTopic)) + } + fun back() { handleBack() } fun handleBack(): Boolean { @@ -203,7 +235,11 @@ class AppController( Destination.Bootstrap, Destination.Home -> false is Destination.Options -> { _state.value = current.copy( - destination = if (destination.step == 2) Destination.Options(1) else Destination.Home, + destination = when (destination.step) { + 3 -> Destination.Options(2) + 2 -> Destination.Options(1) + else -> Destination.Home + }, ) true } @@ -333,16 +369,22 @@ class AppController( .distinct() .size, ) + val activityDays = abandoned.attempts.map { it.localEpochDay }.distinct().sorted() + val activityStreak = activityDays.fold(_state.value.streak, StreakRules::recordActivity) _state.value = _state.value.copy( destination = Destination.Results, run = abandoned, result = summary, newPersonalBest = false, + streak = activityStreak, leaveConfirmationRequested = false, resumeRunAfterLeaveCancellation = false, ) enqueueRunPersistence { - database.runWriter().abandonRun(abandoned.toRunEntity(summary)) + database.runWriter().abandonRun( + abandoned.toRunEntity(summary), + activityStreak.takeIf { activityDays.isNotEmpty() }?.toEntity(), + ) refreshProgress() } } @@ -385,13 +427,15 @@ class AppController( enqueueRunPersistence { val before = _state.value.mastery val after = MasteryRules.applyAttemptToCountries(before, attempt) + val activityStreak = StreakRules.recordActivity(_state.value.streak, attempt.localEpochDay) val changed = after.filter { updated -> before.none { it.countryId == updated.countryId && it.skill == updated.skill && it == updated } } database.runWriter().recordAcceptedAnswer( attempt.toEntity(json), changed.map { it.toEntity() }, run.toSnapshotEntity(engine, json), + activityStreak.toEntity(), ) - _state.value = _state.value.copy(mastery = after) + _state.value = _state.value.copy(mastery = after, streak = activityStreak) } } diff --git a/composeApp/src/commonMain/kotlin/app/bertsatlas/data/Database.kt b/composeApp/src/commonMain/kotlin/app/bertsatlas/data/Database.kt index fa20db3..0371410 100644 --- a/composeApp/src/commonMain/kotlin/app/bertsatlas/data/Database.kt +++ b/composeApp/src/commonMain/kotlin/app/bertsatlas/data/Database.kt @@ -306,7 +306,7 @@ abstract class RunWriteDao { protected abstract suspend fun upsertPersonalBest(personalBest: PersonalBestEntity) @Upsert - protected abstract suspend fun upsertStreak(streak: StreakEntity) + abstract suspend fun saveStreak(streak: StreakEntity) @Upsert protected abstract suspend fun upsertStamps(stamps: List) @@ -319,6 +319,7 @@ abstract class RunWriteDao { attempt: AttemptEntity, updatedMastery: List, snapshot: ActiveRunSnapshotEntity, + streak: StreakEntity, ) { require(updatedMastery.isNotEmpty()) { "An accepted answer must update at least one mastery item" } val countryIds = updatedMastery.mapTo(linkedSetOf()) { it.countryId } @@ -329,6 +330,7 @@ abstract class RunWriteDao { require(snapshot.runId == attempt.runId) { "Attempt and snapshot must belong to the same run" } insertAttempt(attempt.copy(countryIdsJson = countryIds.encodeStringSet())) upsertMastery(updatedMastery) + saveStreak(streak) saveActiveRun(snapshot) } @@ -346,7 +348,7 @@ abstract class RunWriteDao { if (existingBest == null || run.score > existingBest.score) { upsertPersonalBest(PersonalBestEntity(duration, topic, run.score, run.id)) } - upsertStreak(streak) + saveStreak(streak) if (stamps.isNotEmpty()) upsertStamps(stamps) clearActiveRun(run.id) } @@ -364,7 +366,7 @@ abstract class RunWriteDao { val existingBest = personalBest(it.duration, it.topic) if (existingBest == null || it.score > existingBest.score) upsertPersonalBest(it) } - upsertStreak(streak) + saveStreak(streak) if (stamps.isNotEmpty()) upsertStamps(stamps) clearActiveRun(run.id) } @@ -387,7 +389,7 @@ abstract class RunWriteDao { } @Transaction - open suspend fun abandonRun(runId: String) { + open suspend fun abandonRun(runId: String, streak: StreakEntity? = null) { upsertRun( RunEntity( id = runId, @@ -404,12 +406,14 @@ abstract class RunWriteDao { completedAtEpochMillis = null, ), ) + if (streak != null) saveStreak(streak) clearActiveRun(runId) } @Transaction - open suspend fun abandonRun(run: RunEntity) { + open suspend fun abandonRun(run: RunEntity, streak: StreakEntity? = null) { upsertRun(run.copy(status = "Abandoned", completedAtEpochMillis = null)) + if (streak != null) saveStreak(streak) clearActiveRun(run.id) } @@ -520,6 +524,7 @@ class RoomProgressRepository( attempt: Attempt, updatedMastery: List, activeRunSnapshot: ActiveRunSnapshot, + streak: Streak, ) { require(attempt.countryIds.isNotEmpty()) writer.recordAcceptedAnswer( @@ -528,6 +533,7 @@ class RoomProgressRepository( .filter { it.countryId in attempt.countryIds && it.skill == attempt.skill } .map { it.toEntity(json) }, snapshot = activeRunSnapshot.toEntity(json), + streak = streak.toEntity(), ) } @@ -539,7 +545,8 @@ class RoomProgressRepository( writer.completeRun(result.toEntity(json), streak.toEntity(), newlyEarnedStamps.map(PassportStamp::toEntity)) } - override suspend fun recordAbandonedRun(runId: String) = writer.abandonRun(runId) + override suspend fun recordAbandonedRun(runId: String, streak: Streak?) = + writer.abandonRun(runId, streak?.toEntity()) override suspend fun resetProgress() = writer.resetProgress() diff --git a/composeApp/src/commonMain/kotlin/app/bertsatlas/ui/AtlasComponents.kt b/composeApp/src/commonMain/kotlin/app/bertsatlas/ui/AtlasComponents.kt index 639763c..d66f288 100644 --- a/composeApp/src/commonMain/kotlin/app/bertsatlas/ui/AtlasComponents.kt +++ b/composeApp/src/commonMain/kotlin/app/bertsatlas/ui/AtlasComponents.kt @@ -48,6 +48,7 @@ import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Path import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.draw.clip @@ -63,6 +64,7 @@ import androidx.compose.ui.state.ToggleableState import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.compose.ui.platform.LocalDensity import kotlin.math.min import org.jetbrains.compose.resources.DrawableResource import org.jetbrains.compose.resources.InternalResourceApi @@ -77,6 +79,8 @@ fun AtlasScreen( modifier: Modifier = Modifier, scrollable: Boolean = true, contentPadding: PaddingValues = PaddingValues(horizontal = 20.dp, vertical = 24.dp), + headerPadding: PaddingValues = PaddingValues(0.dp), + header: (@Composable () -> Unit)? = null, content: @Composable BoxScope.() -> Unit, ) { Box( @@ -86,16 +90,41 @@ fun AtlasScreen( .windowInsetsPadding(WindowInsets.safeDrawing), contentAlignment = Alignment.TopCenter, ) { - val contentModifier = Modifier - .fillMaxWidth() - .fillMaxHeight() - .widthIn(max = 720.dp) - .padding(contentPadding) - .let { if (scrollable) it.verticalScroll(rememberScrollState()) else it } - Box(modifier = contentModifier, content = content) + Column(Modifier.fillMaxSize().widthIn(max = 720.dp)) { + header?.let { + Box(Modifier.fillMaxWidth().padding(headerPadding), contentAlignment = Alignment.TopCenter) { + it() + } + } + val contentModifier = Modifier + .fillMaxWidth() + .weight(1f) + .let { base -> if (scrollable) base.verticalScroll(rememberScrollState()) else base } + .padding(contentPadding) + Box(modifier = contentModifier, content = content) + } } } +@Composable +fun AtlasScreenScaffold( + title: String, + onBack: (() -> Unit)?, + modifier: Modifier = Modifier, + scrollable: Boolean = true, + trailing: (@Composable () -> Unit)? = null, + content: @Composable BoxScope.() -> Unit, +) { + AtlasScreen( + modifier = modifier, + scrollable = scrollable, + contentPadding = PaddingValues(start = 20.dp, top = 12.dp, end = 20.dp, bottom = 24.dp), + headerPadding = PaddingValues(start = 20.dp, top = 12.dp, end = 20.dp), + header = { ScreenTopBar(title, onBack, trailing) }, + content = content, + ) +} + @Composable fun AtlasText( text: String, @@ -204,7 +233,7 @@ fun AtlasChoice( } val outline = when (choice.state) { AnswerState.Available -> colors.outline - AnswerState.Selected -> colors.mustard + AnswerState.Selected -> colors.success AnswerState.Correct -> colors.success AnswerState.Incorrect -> colors.orangeAccent AnswerState.Eliminated -> colors.outline.copy(alpha = .5f) @@ -334,6 +363,11 @@ fun AtlasToggle(setting: SettingToggleUi, onToggle: (String, Boolean) -> Unit) { Box( Modifier.size(width = 54.dp, height = 32.dp) .background(if (setting.checked) AtlasTheme.colors.success else AtlasTheme.colors.seaGlass, CircleShape) + .border( + 2.dp, + if (setting.checked) AtlasTheme.colors.success else AtlasTheme.colors.outline, + CircleShape, + ) .padding(4.dp), contentAlignment = if (setting.checked) Alignment.CenterEnd else Alignment.CenterStart, ) { @@ -366,18 +400,121 @@ fun BertIllustration( contentDescription: String = "Bert, the curious atlas brain", reducedMotion: Boolean = AtlasTheme.reducedMotion, ) { - if (reducedMotion) { - StaticBertIllustration(pose, modifier, contentDescription) - return - } val transition = rememberInfiniteTransition(label = "Bert bulb") - val rayLength by transition.animateFloat( - initialValue = .82f, - targetValue = 1.08f, - animationSpec = infiniteRepeatable(tween(800), RepeatMode.Reverse), - label = "Bulb ray length", + val animatedProgress by transition.animateFloat( + initialValue = 0f, + targetValue = 1f, + animationSpec = infiniteRepeatable( + animation = tween( + when (pose) { + BertPose.Correct, BertPose.Overtime -> 520 + BertPose.Incorrect -> 260 + BertPose.Hint -> 1_100 + BertPose.Fact -> 800 + BertPose.Results, BertPose.Stamp -> 900 + BertPose.Streak -> 700 + BertPose.Idle -> 1_400 + }, + ), + repeatMode = RepeatMode.Reverse, + ), + label = "Bert ${pose.name.lowercase()} motion", ) - StaticBertIllustration(pose, modifier, contentDescription, rayLength) + val progress = if (reducedMotion) .5f else animatedProgress + val wobble = with(LocalDensity.current) { ((progress - .5f) * 6f).dp.toPx() } + Box( + modifier.size(180.dp).semantics { this.contentDescription = contentDescription }, + contentAlignment = Alignment.Center, + ) { + Canvas(Modifier.matchParentSize()) { drawBertDecoration(pose, progress) } + StaticBertIllustration( + pose = pose, + modifier = Modifier.matchParentSize().graphicsLayer { + when (pose) { + BertPose.Incorrect -> translationX = wobble + BertPose.Idle -> { + scaleX = .99f + progress * .01f + scaleY = .99f + progress * .01f + } + else -> Unit + } + }, + contentDescription = null, + ) + } +} + +private fun androidx.compose.ui.graphics.drawscope.DrawScope.drawBertDecoration(pose: BertPose, progress: Float) { + val green = Color(0xFF27635B) + val blue = Color(0xFF778BA5) + val seaGlass = Color(0xFFB7CFC8) + val coral = Color(0xFFF7BFBE) + val peach = Color(0xFFE9AF8B) + val orange = Color(0xFFE1814E) + val unit = size.minDimension / 180f + fun p(x: Float, y: Float) = Offset(x * unit, y * unit) + fun star(center: Offset, radius: Float, color: Color) { + val path = Path() + repeat(10) { index -> + val angle = -kotlin.math.PI.toFloat() / 2f + index * kotlin.math.PI.toFloat() / 5f + val length = if (index % 2 == 0) radius else radius * .42f + val point = Offset(center.x + kotlin.math.cos(angle) * length, center.y + kotlin.math.sin(angle) * length) + if (index == 0) path.moveTo(point.x, point.y) else path.lineTo(point.x, point.y) + } + path.close() + drawPath(path, color) + } + fun successBurst(includeClock: Boolean) { + val alpha = .35f + progress * .65f + val radius = (5f + progress * 4f) * unit + star(p(22f, 38f), radius, seaGlass.copy(alpha = alpha)) + star(p(158f, 42f), radius * .82f, coral.copy(alpha = alpha)) + star(p(24f, 142f), radius * .72f, peach.copy(alpha = alpha)) + star(p(155f, 136f), radius, seaGlass.copy(alpha = alpha)) + if (includeClock) { + val center = p(155f, 151f) + drawCircle(Color(0xFFF6E9E1), 15f * unit, center) + drawCircle(green, 15f * unit, center, style = Stroke(3f * unit)) + drawLine(green, center, p(155f, 140f), 2.5f * unit, StrokeCap.Round) + drawLine(green, center, p(163f, 155f), 2.5f * unit, StrokeCap.Round) + } + } + when (pose) { + BertPose.Idle, BertPose.Incorrect -> Unit + BertPose.Correct -> successBurst(includeClock = false) + BertPose.Overtime -> successBurst(includeClock = true) + BertPose.Hint -> { + val lift = progress * 3f + drawCircle(seaGlass.copy(alpha = .7f), 4f * unit, p(139f, 38f + lift)) + drawCircle(seaGlass.copy(alpha = .8f), 7f * unit, p(151f, 27f + lift)) + drawCircle(seaGlass, 10f * unit, p(165f, 17f + lift)) + } + BertPose.Fact -> { + val center = p(151f, 32f) + drawCircle(peach, 10f * unit, center) + val rayLength = 7f + 4f * progress + drawLine(orange, p(151f, 14f), p(151f, 14f - rayLength), 3.5f * unit, StrokeCap.Round) + drawLine(orange, p(165f, 19f), p(165f + rayLength * .7f, 19f - rayLength * .7f), 3.5f * unit, StrokeCap.Round) + } + BertPose.Results -> { + drawCircle(blue.copy(alpha = .18f + progress * .12f), (72f + progress * 5f) * unit, p(90f, 88f), style = Stroke(4f * unit)) + } + BertPose.Streak -> { + val flame = Path().apply { + moveTo(158f * unit, 161f * unit) + cubicTo(139f * unit, 151f * unit, 151f * unit, (127f - progress * 5f) * unit, 159f * unit, 121f * unit) + cubicTo(177f * unit, 141f * unit, 174f * unit, 157f * unit, 158f * unit, 161f * unit) + close() + } + drawPath(flame, orange.copy(alpha = .8f + progress * .2f)) + } + BertPose.Stamp -> { + val center = p(155f, 150f) + drawCircle(blue.copy(alpha = .7f), (13f + progress * 2f) * unit, center, style = Stroke(3f * unit)) + drawLine(green, p(148f, 150f), p(153f, 155f), 3f * unit, StrokeCap.Round) + drawLine(green, p(153f, 155f), p(163f, 143f), 3f * unit, StrokeCap.Round) + } + } } @Composable @@ -385,7 +522,6 @@ private fun StaticBertIllustration( pose: BertPose, modifier: Modifier, contentDescription: String?, - rayLength: Float = 1f, ) { val colors = AtlasTheme.colors val accessibleModifier = if (contentDescription == null) Modifier else Modifier.semantics { this.contentDescription = contentDescription } @@ -428,10 +564,6 @@ private fun StaticBertIllustration( quadraticTo(90f * scale, (if (pose == BertPose.Incorrect) 106f else 130f) * scale, 109f * scale, mouthY * scale) } drawPath(mouth, colors.plum, style = Stroke(4f * scale, cap = StrokeCap.Round)) - val accent = colors.orangeAccent - drawCircle(accent, 10f * scale, p(145f, 39f)) - drawLine(accent, p(145f, 17f), p(145f, 17f - 10f * rayLength), 4f * scale, StrokeCap.Round) - drawLine(accent, p(161f, 25f), p(161f + 8f * rayLength, 25f - 7f * rayLength), 4f * scale, StrokeCap.Round) } } diff --git a/composeApp/src/commonMain/kotlin/app/bertsatlas/ui/HomeAndOptionsScreens.kt b/composeApp/src/commonMain/kotlin/app/bertsatlas/ui/HomeAndOptionsScreens.kt index 86970a0..6e8edac 100644 --- a/composeApp/src/commonMain/kotlin/app/bertsatlas/ui/HomeAndOptionsScreens.kt +++ b/composeApp/src/commonMain/kotlin/app/bertsatlas/ui/HomeAndOptionsScreens.kt @@ -4,10 +4,12 @@ import androidx.compose.foundation.Canvas import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.size @@ -39,18 +41,23 @@ fun HomeScreen( onSettings: () -> Unit = {}, modifier: Modifier = Modifier, ) { - AtlasScreen(modifier) { + AtlasScreen( + modifier = modifier, + headerPadding = PaddingValues(start = 20.dp, top = 12.dp, end = 20.dp), + header = if (hasStartedTrivia) { + { + Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { + PointsBadge(totalPoints, onProgress, Modifier.weight(1f)) + SettingsIconButton(onSettings) + } + } + } else null, + ) { Column( Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(12.dp), ) { - if (hasStartedTrivia) { - Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { - PointsBadge(totalPoints, Modifier.weight(1f)) - SettingsIconButton(onSettings) - } - } Spacer(Modifier.height(if (hasStartedTrivia) 8.dp else 24.dp)) HomeBertIllustration(large = !hasStartedTrivia) AtlasText( @@ -65,11 +72,16 @@ fun HomeScreen( if (hasStartedTrivia) { AtlasButton("Options", onOptions, Modifier.fillMaxWidth().widthIn(max = 420.dp), AtlasButtonStyle.Secondary) SectionGap() - AtlasCard(Modifier.widthIn(max = 420.dp), "Current streak: $streakDays days. $progressSummary") { + val streakSummary = when (streakDays) { + 0 -> "Your curiosity streak starts with your first answer" + 1 -> "1 curious day" + else -> "$streakDays curious days" + } + AtlasCard(Modifier.widthIn(max = 420.dp), "$streakSummary. $progressSummary") { Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { AtlasText("✦", style = AtlasTheme.typography.title, color = AtlasTheme.colors.coral) Column(Modifier.weight(1f).padding(horizontal = 12.dp)) { - AtlasText(if (streakDays == 1) "1 curious day" else "$streakDays curious days", style = AtlasTheme.typography.label) + AtlasText(streakSummary, style = AtlasTheme.typography.label) AtlasText(progressSummary, style = AtlasTheme.typography.detail, color = AtlasTheme.colors.mutedPlum) } } @@ -85,35 +97,45 @@ private fun HomeBertIllustration(large: Boolean) { val size = if (large) 270.dp else 200.dp val colors = AtlasTheme.colors androidx.compose.foundation.layout.Box(Modifier.size(size), contentAlignment = Alignment.Center) { - BertIllustration(BertPose.Idle, Modifier.fillMaxWidth(), contentDescription = "Bert smiles with a bright idea and a folded map") + BertIllustration(BertPose.Idle, Modifier.size(size), contentDescription = "Bert smiles with a bright idea and a folded map") Canvas(Modifier.fillMaxWidth().size(size)) { val w = this.size.width val h = this.size.height val outline = 4.dp.toPx() val map = Path().apply { - moveTo(w * .04f, h * .68f) - lineTo(w * .14f, h * .63f) - lineTo(w * .25f, h * .68f) - lineTo(w * .36f, h * .63f) - lineTo(w * .36f, h * .88f) - lineTo(w * .25f, h * .93f) - lineTo(w * .14f, h * .88f) - lineTo(w * .04f, h * .93f) + moveTo(w * .01f, h * .68f) + lineTo(w * .11f, h * .63f) + lineTo(w * .22f, h * .68f) + lineTo(w * .33f, h * .63f) + lineTo(w * .33f, h * .88f) + lineTo(w * .22f, h * .93f) + lineTo(w * .11f, h * .88f) + lineTo(w * .01f, h * .93f) close() } drawPath(map, colors.paper) drawPath(map, colors.plum, style = Stroke(outline, join = androidx.compose.ui.graphics.StrokeJoin.Round)) - drawLine(colors.plum, Offset(w * .14f, h * .63f), Offset(w * .14f, h * .88f), outline) - drawLine(colors.plum, Offset(w * .25f, h * .68f), Offset(w * .25f, h * .93f), outline) - drawLine(colors.orangeAccent, Offset(w * .06f, h * .83f), Offset(w * .34f, h * .76f), outline * .7f, StrokeCap.Round) + drawLine(colors.plum, Offset(w * .11f, h * .63f), Offset(w * .11f, h * .88f), outline) + drawLine(colors.plum, Offset(w * .22f, h * .68f), Offset(w * .22f, h * .93f), outline) + drawLine(colors.orangeAccent, Offset(w * .03f, h * .83f), Offset(w * .31f, h * .76f), outline * .7f, StrokeCap.Round) } } } @Composable -private fun PointsBadge(points: Int, modifier: Modifier = Modifier) { +private fun PointsBadge(points: Int, onClick: () -> Unit, modifier: Modifier = Modifier) { val colors = AtlasTheme.colors - Row(modifier, verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) { + Row( + modifier + .heightIn(min = 48.dp) + .semantics(mergeDescendants = true) { + role = Role.Button + contentDescription = "View progress, $points total points" + } + .clickable(role = Role.Button, onClick = onClick), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { Canvas(Modifier.size(24.dp)) { drawCircle(colors.mustard, size.minDimension * .40f) drawCircle(colors.plum, size.minDimension * .40f, style = Stroke(2.4.dp.toPx())) @@ -148,28 +170,50 @@ private fun SettingsIconButton(onClick: () -> Unit) { fun OptionsScreen( step: Int, choices: List, + durationSummary: String, + topicSummary: String, onChoose: (String) -> Unit, + onStart: () -> Unit, onBack: () -> Unit, modifier: Modifier = Modifier, ) { val isTimeStep = step <= 1 - AtlasScreen(modifier) { + val title = when (step) { + 1 -> "Options · 1 of 2" + 2 -> "Options · 2 of 2" + else -> "Options saved" + } + AtlasScreenScaffold(title, onBack, modifier) { Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(16.dp)) { - ScreenTopBar(if (isTimeStep) "Options · 1 of 2" else "Options · 2 of 2", onBack) - SectionGap() - BertIllustration(if (isTimeStep) BertPose.Idle else BertPose.Fact, Modifier.align(Alignment.CenterHorizontally)) - AtlasHeading( - if (isTimeStep) "How much time have you got?" else "What are we nerding out about?", - Modifier.fillMaxWidth(), - centered = true, - ) - AtlasText( - if (isTimeStep) "The clock only runs while you're answering." else "Difficulty adapts quietly as you play.", - Modifier.fillMaxWidth(), - color = AtlasTheme.colors.mutedPlum, - textAlign = TextAlign.Center, - ) - choices.forEach { AtlasChoice(it, onChoose) } + if (step <= 2) { + BertIllustration(if (isTimeStep) BertPose.Idle else BertPose.Fact, Modifier.align(Alignment.CenterHorizontally)) + AtlasHeading( + if (isTimeStep) "How much time have you got?" else "What are we nerding out about?", + Modifier.fillMaxWidth(), + centered = true, + ) + AtlasText( + if (isTimeStep) "The clock only runs while you're answering." else "Difficulty adapts quietly as you play.", + Modifier.fillMaxWidth(), + color = AtlasTheme.colors.mutedPlum, + textAlign = TextAlign.Center, + ) + choices.forEach { AtlasChoice(it, onChoose) } + } else { + SectionGap() + BertIllustration(BertPose.Correct, Modifier.align(Alignment.CenterHorizontally)) + AtlasHeading("Saved and ready", Modifier.fillMaxWidth(), centered = true) + AtlasText( + "Your next trivia run is set up.", + Modifier.fillMaxWidth(), + color = AtlasTheme.colors.mutedPlum, + textAlign = TextAlign.Center, + ) + AtlasCard(contentDescription = "Saved options. Duration: $durationSummary. Topic: $topicSummary") { + StatRow(listOf(StatUi("Duration", durationSummary), StatUi("Topic", topicSummary))) + } + AtlasButton("Start", onStart, Modifier.fillMaxWidth()) + } } } } diff --git a/composeApp/src/commonMain/kotlin/app/bertsatlas/ui/QuizScreens.kt b/composeApp/src/commonMain/kotlin/app/bertsatlas/ui/QuizScreens.kt index 9fa6cc0..8161f54 100644 --- a/composeApp/src/commonMain/kotlin/app/bertsatlas/ui/QuizScreens.kt +++ b/composeApp/src/commonMain/kotlin/app/bertsatlas/ui/QuizScreens.kt @@ -56,21 +56,33 @@ fun QuizScreen( runProgressDescription: String = "Trivia progress", overtimeMessage: String = "Time's up. Keep going for half points", ) { - AtlasScreen(modifier, contentPadding = PaddingValues(0.dp)) { - Column(Modifier.fillMaxWidth()) { - runProgress?.let { SubtleRunProgress(it, runProgressDescription) } - Column( - Modifier.fillMaxWidth().padding(horizontal = 20.dp, vertical = 24.dp), - verticalArrangement = Arrangement.spacedBy(16.dp), - ) { - Row(Modifier.fillMaxWidth().heightIn(min = 56.dp), verticalAlignment = Alignment.CenterVertically) { - QuizMetric("Score", score.toString(), Modifier.weight(1f)) - QuizMetric("Question", questionTime, Modifier.weight(1f), warning = timerExpired) - AtlasExitButton(onLeave) - } - if (timerExpired) { - AtlasText(overtimeMessage, Modifier.fillMaxWidth(), color = AtlasTheme.colors.error, style = AtlasTheme.typography.label, textAlign = TextAlign.Center) + AtlasScreen( + modifier = modifier, + contentPadding = PaddingValues(horizontal = 20.dp, vertical = 16.dp), + header = { + Column(Modifier.fillMaxWidth().padding(vertical = 8.dp)) { + runProgress?.let { SubtleRunProgress(it, runProgressDescription) } + Row( + Modifier.fillMaxWidth().heightIn(min = 56.dp).padding(horizontal = 20.dp, vertical = 4.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + QuizMetric("Score", score.toString(), Modifier.weight(1f)) + QuizMetric("Question", questionTime, Modifier.weight(1f), warning = timerExpired) + AtlasExitButton(onLeave) + } + if (timerExpired) { + AtlasText( + overtimeMessage, + Modifier.fillMaxWidth().padding(horizontal = 20.dp, vertical = 4.dp), + color = AtlasTheme.colors.error, + style = AtlasTheme.typography.label, + textAlign = TextAlign.Center, + ) + } } + }, + ) { + Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(16.dp)) { if (promptFlag != null || imageDescription != null) { Box( Modifier.fillMaxWidth().heightIn(min = 148.dp) @@ -104,7 +116,6 @@ fun QuizScreen( AtlasButton("Get a hint", onHint, Modifier.weight(1f), AtlasButtonStyle.Secondary, enabled = hintAvailable) if (unlimited) AtlasButton("Finish", onFinish, Modifier.weight(1f), AtlasButtonStyle.Quiet) } - } } } } @@ -240,9 +251,8 @@ fun HintChooserScreen( introText: String = "Choose the clue that sounds most useful.", chargedHintLabel: String = "−25 points", ) { - AtlasScreen(modifier) { + AtlasScreenScaffold("Choose a hint", onCancel, modifier) { Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(16.dp), horizontalAlignment = Alignment.CenterHorizontally) { - ScreenTopBar("Choose a hint", onCancel) BertIllustration(BertPose.Hint, Modifier.size(150.dp)) AtlasHeading("A little nudge?", Modifier.fillMaxWidth(), centered = true) AtlasText(introText, textAlign = TextAlign.Center, color = AtlasTheme.colors.mutedPlum) diff --git a/composeApp/src/commonMain/kotlin/app/bertsatlas/ui/ResultsAndProgressScreens.kt b/composeApp/src/commonMain/kotlin/app/bertsatlas/ui/ResultsAndProgressScreens.kt index 13818b6..2403e79 100644 --- a/composeApp/src/commonMain/kotlin/app/bertsatlas/ui/ResultsAndProgressScreens.kt +++ b/composeApp/src/commonMain/kotlin/app/bertsatlas/ui/ResultsAndProgressScreens.kt @@ -47,7 +47,7 @@ fun ResultsScreen( ) if (abandoned) { AtlasText( - "Your answers still shaped what you'll practise next. This trivia run didn't count toward your personal best or daily streak.", + "Your answers still shaped what you'll practise next and counted toward today's activity. This trivia run didn't count toward your personal best.", Modifier.fillMaxWidth(), color = AtlasTheme.colors.mutedPlum, textAlign = TextAlign.Center, @@ -92,14 +92,28 @@ fun ProgressScreen( onBack: () -> Unit, modifier: Modifier = Modifier, ) { - AtlasScreen(modifier) { + AtlasScreenScaffold("Your atlas", onBack, modifier) { Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(16.dp)) { - ScreenTopBar("Your atlas", onBack) Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { BertIllustration(BertPose.Streak, Modifier.weight(.8f), "Bert celebrates your curiosity streak") Column(Modifier.weight(1.2f), verticalArrangement = Arrangement.spacedBy(8.dp)) { - AtlasText("$currentStreak day streak", style = AtlasTheme.typography.heading, color = AtlasTheme.colors.coral) - AtlasText("Best · $bestStreak days", color = AtlasTheme.colors.mutedPlum) + AtlasText( + when (currentStreak) { + 0 -> "Start your streak" + 1 -> "1 day streak" + else -> "$currentStreak day streak" + }, + style = AtlasTheme.typography.heading, + color = AtlasTheme.colors.coral, + ) + AtlasText( + when (bestStreak) { + 0 -> "Answer a question to begin" + 1 -> "Best · 1 day" + else -> "Best · $bestStreak days" + }, + color = AtlasTheme.colors.mutedPlum, + ) } } StatRow(listOf(StatUi("Accuracy", overallAccuracy), StatUi("Questions", totalAnswered.toString()))) diff --git a/composeApp/src/commonMain/kotlin/app/bertsatlas/ui/SettingsScreens.kt b/composeApp/src/commonMain/kotlin/app/bertsatlas/ui/SettingsScreens.kt index 844c6dd..6f08a9d 100644 --- a/composeApp/src/commonMain/kotlin/app/bertsatlas/ui/SettingsScreens.kt +++ b/composeApp/src/commonMain/kotlin/app/bertsatlas/ui/SettingsScreens.kt @@ -30,10 +30,8 @@ fun SettingsScreen( onBack: () -> Unit, modifier: Modifier = Modifier, ) { - AtlasScreen(modifier) { + AtlasScreenScaffold("Settings", onBack, modifier) { Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(12.dp)) { - ScreenTopBar("Settings", onBack) - SectionGap() AtlasCard { settings.forEach { AtlasToggle(it, onToggle) } } @@ -69,9 +67,8 @@ fun ResetConfirmationScreen( onCancel: () -> Unit, modifier: Modifier = Modifier, ) { - AtlasScreen(modifier) { + AtlasScreenScaffold("Reset local data", onCancel, modifier) { Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(16.dp)) { - ScreenTopBar("Reset local data", onCancel) BertIllustration(BertPose.Incorrect, Modifier.fillMaxWidth()) AtlasHeading("Clear this atlas?") AtlasText("This removes your progress, streaks, stamps, settings, and any saved run from this device. It cannot be undone.") @@ -103,9 +100,8 @@ fun CreditsScreen( onBack: () -> Unit, modifier: Modifier = Modifier, ) { - AtlasScreen(modifier) { + AtlasScreenScaffold("Credits and licenses", onBack, modifier) { Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(16.dp)) { - ScreenTopBar("Credits and licenses", onBack) AtlasText("Bert, the writing, and the interface artwork are original to Bert's Atlas.") AtlasCard { AtlasText("Content", style = AtlasTheme.typography.heading) @@ -123,9 +119,8 @@ fun CreditsScreen( @Composable private fun InformationScreen(title: String, paragraphs: List, onBack: () -> Unit, modifier: Modifier = Modifier) { - AtlasScreen(modifier) { + AtlasScreenScaffold(title, onBack, modifier) { Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(16.dp)) { - ScreenTopBar(title, onBack) paragraphs.forEach { AtlasText(it) } } } @@ -141,9 +136,8 @@ fun DebugPanelScreen( onClose: () -> Unit, modifier: Modifier = Modifier, ) { - AtlasScreen(modifier) { + AtlasScreenScaffold("Debug QA", onClose, modifier) { Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(12.dp)) { - ScreenTopBar("Debug QA", onClose) AtlasCard { AtlasText("Build $buildVersion", style = AtlasTheme.typography.label) AtlasText("Catalog $catalogVersion") diff --git a/content/brand/berts-atlas-mark.svg b/content/brand/berts-atlas-mark.svg index 08af621..f28b460 100644 --- a/content/brand/berts-atlas-mark.svg +++ b/content/brand/berts-atlas-mark.svg @@ -1,14 +1,13 @@ - - Bert's Atlas mark - Smiling Bert in green glasses with a small folded paper map at his top right. + - - - - - - - - + + + + + + + + + diff --git a/content/brand/berts-atlas-splash.svg b/content/brand/berts-atlas-splash.svg index e31cb2b..eaba6e1 100644 --- a/content/brand/berts-atlas-splash.svg +++ b/content/brand/berts-atlas-splash.svg @@ -1,7 +1,13 @@ - + - - + + + + + + + + + - diff --git a/content/brand/logo-bert.svg b/content/brand/logo-bert.svg new file mode 100644 index 0000000..eafccf4 --- /dev/null +++ b/content/brand/logo-bert.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/domain/src/commonMain/kotlin/app/bertsatlas/domain/Contracts.kt b/domain/src/commonMain/kotlin/app/bertsatlas/domain/Contracts.kt index 1e5e662..565b593 100644 --- a/domain/src/commonMain/kotlin/app/bertsatlas/domain/Contracts.kt +++ b/domain/src/commonMain/kotlin/app/bertsatlas/domain/Contracts.kt @@ -28,6 +28,7 @@ interface ProgressWriter { attempt: Attempt, updatedMastery: List, activeRunSnapshot: ActiveRunSnapshot, + streak: Streak, ) suspend fun recordCompletedRun( @@ -36,7 +37,7 @@ interface ProgressWriter { newlyEarnedStamps: List, ) - suspend fun recordAbandonedRun(runId: String) + suspend fun recordAbandonedRun(runId: String, streak: Streak? = null) suspend fun resetProgress() } diff --git a/domain/src/commonMain/kotlin/app/bertsatlas/domain/ProgressRules.kt b/domain/src/commonMain/kotlin/app/bertsatlas/domain/ProgressRules.kt index bf13d9c..16476f4 100644 --- a/domain/src/commonMain/kotlin/app/bertsatlas/domain/ProgressRules.kt +++ b/domain/src/commonMain/kotlin/app/bertsatlas/domain/ProgressRules.kt @@ -76,11 +76,11 @@ object MasteryRules { } object StreakRules { - fun recordCompletedRun(current: Streak, completedEpochDay: Int): Streak { - val lastDay = current.lastCompletedEpochDay - if (lastDay != null && completedEpochDay <= lastDay) return current + fun recordActivity(current: Streak, activityEpochDay: Int): Streak { + val lastDay = current.lastActivityEpochDay + if (lastDay != null && activityEpochDay <= lastDay) return current - val currentDays = if (lastDay != null && completedEpochDay == lastDay + 1) { + val currentDays = if (lastDay != null && activityEpochDay == lastDay + 1) { current.currentDays + 1 } else { 1 @@ -88,9 +88,12 @@ object StreakRules { return Streak( currentDays = currentDays, bestDays = maxOf(current.bestDays, currentDays), - lastCompletedEpochDay = completedEpochDay, + lastCompletedEpochDay = activityEpochDay, ) } + + fun recordCompletedRun(current: Streak, completedEpochDay: Int): Streak = + recordActivity(current, completedEpochDay) } data class PassportEvidence( diff --git a/domain/src/commonMain/kotlin/app/bertsatlas/domain/QuizEngine.kt b/domain/src/commonMain/kotlin/app/bertsatlas/domain/QuizEngine.kt index 68f1764..2c342c5 100644 --- a/domain/src/commonMain/kotlin/app/bertsatlas/domain/QuizEngine.kt +++ b/domain/src/commonMain/kotlin/app/bertsatlas/domain/QuizEngine.kt @@ -109,7 +109,8 @@ class QuizEngine( } val completionPending = state.runCompletionPending || state.sessionExpired || - (state.configuration.duration != QuizDuration.Unlimited && state.sessionRemainingMillis == 0L) + (state.configuration.duration != QuizDuration.Unlimited && state.sessionRemainingMillis == 0L) || + state.hasReachedAnsweredQuestionLimit() if (state.phase == RunPhase.Paused) { require(state.pausedFromPhase in PAUSABLE_PHASES) { "A paused snapshot has no resumable phase" } return state.copy( @@ -161,13 +162,17 @@ class QuizEngine( hintTypes = timed.activeQuestion.usedHintTypes, ) val correctStreak = if (evaluation.correct) timed.currentCorrectStreak + 1 else 0 + val attempts = timed.attempts + attempt + val answeredQuestionLimitReached = timed.configuration.duration.answeredQuestionLimit + ?.let { attempts.size >= it } + ?: false return timed.copy( phase = RunPhase.Feedback, score = timed.score + points, answeredQuestionIds = timed.answeredQuestionIds + timed.activeQuestion.question.id, recentQuestionIds = (timed.recentQuestionIds + timed.activeQuestion.question.id).takeLast(10), answeredCountryIds = (timed.answeredCountryIds + timed.activeQuestion.question.countryIds).takeLast(10), - attempts = timed.attempts + attempt, + attempts = attempts, feedback = AnswerFeedback( kind = when { !evaluation.correct -> FeedbackKind.Incorrect @@ -182,6 +187,7 @@ class QuizEngine( adaptive = AdaptiveDifficulty.afterAnswer(timed.adaptive, evaluation.correct, hinted), currentCorrectStreak = correctStreak, bestCorrectStreak = maxOf(timed.bestCorrectStreak, correctStreak), + runCompletionPending = timed.runCompletionPending || answeredQuestionLimitReached, tickingSinceMillis = null, ) } @@ -592,6 +598,9 @@ class QuizEngine( private fun ActiveQuestion.revealedTileIds(): Set = appliedHints.mapNotNullTo(linkedSetOf()) { it.revealedTileId } + private fun QuizRunState.hasReachedAnsweredQuestionLimit(): Boolean = + configuration.duration.answeredQuestionLimit?.let { attempts.size >= it } == true + private fun correctAnswer(question: Question): String = when (question) { is Question.MultipleChoice -> question.choices.first { it.id == question.correctChoiceId }.label is Question.LetterBank -> question.displayAnswer @@ -667,8 +676,8 @@ class QuizEngine( private data class AnswerEvaluation(val correct: Boolean, val submittedText: String) companion object { - const val MULTIPLE_CHOICE_WINDOW_MILLIS = 10_000L - const val LETTER_BANK_WINDOW_MILLIS = 20_000L + const val MULTIPLE_CHOICE_WINDOW_MILLIS = 30_000L + const val LETTER_BANK_WINDOW_MILLIS = 30_000L const val LETTER_BANK_DECOY_COUNT = 3 const val MINIMUM_UNLIMITED_ANSWERS = 5 const val ACTIVE_RUN_SNAPSHOT_SCHEMA_VERSION = 1 diff --git a/domain/src/commonMain/kotlin/app/bertsatlas/domain/model/Configuration.kt b/domain/src/commonMain/kotlin/app/bertsatlas/domain/model/Configuration.kt index 57d8732..fc38e17 100644 --- a/domain/src/commonMain/kotlin/app/bertsatlas/domain/model/Configuration.kt +++ b/domain/src/commonMain/kotlin/app/bertsatlas/domain/model/Configuration.kt @@ -9,11 +9,14 @@ data class QuizConfiguration( ) @Serializable -enum class QuizDuration(val activeDurationMillis: Long?) { - FiveMinutes(5 * 60 * 1_000L), - TenMinutes(10 * 60 * 1_000L), - TwentyMinutes(20 * 60 * 1_000L), - Unlimited(null), +enum class QuizDuration( + val activeDurationMillis: Long?, + val answeredQuestionLimit: Int?, +) { + FiveMinutes(5 * 60 * 1_000L, 10), + TenMinutes(10 * 60 * 1_000L, 20), + TwentyMinutes(20 * 60 * 1_000L, 40), + Unlimited(null, null), } @Serializable diff --git a/domain/src/commonMain/kotlin/app/bertsatlas/domain/model/Progress.kt b/domain/src/commonMain/kotlin/app/bertsatlas/domain/model/Progress.kt index aabc059..5dc5fda 100644 --- a/domain/src/commonMain/kotlin/app/bertsatlas/domain/model/Progress.kt +++ b/domain/src/commonMain/kotlin/app/bertsatlas/domain/model/Progress.kt @@ -60,7 +60,10 @@ data class Streak( val currentDays: Int = 0, val bestDays: Int = 0, val lastCompletedEpochDay: Int? = null, -) +) { + val lastActivityEpochDay: Int? + get() = lastCompletedEpochDay +} @Serializable enum class PassportStampKind { diff --git a/spec.md b/spec.md index 04cd1df..c3674f1 100644 --- a/spec.md +++ b/spec.md @@ -111,14 +111,15 @@ Tapping `Start` immediately begins a trivia run with these defaults: No configuration questionnaire or modal tutorial appears before the first trivia run. The interface teaches mechanics contextually without delaying the first question. -### 6.2 Returning launch +### 6.2 Returning home -From the second app launch onward, the home screen contains: +After the player starts their first trivia run, the home screen contains: - `Start`, which immediately uses the most recently saved trivia configuration - `Options`, a secondary action +- A tappable lifetime-points summary that opens Progress - Current streak and a compact recent-progress summary -- Settings +- A Settings icon The minimal first-use Home remains until the player starts their first trivia run. Merely opening and closing the app does not reveal returning-player controls. Completing the run is not required. @@ -138,7 +139,7 @@ The initial V1 sequence is: - Capitals - Mixed geography -The final answer is saved and immediately starts the trivia. All answers are saved as one configuration. Abandoning the flow leaves the previously saved configuration unchanged. +The final answer is saved atomically and opens a full-screen success step that summarizes the chosen duration and topic. The player starts the trivia from that screen. Abandoning the flow before saving leaves the previously saved configuration unchanged. Difficulty is always adaptive in V1 and has no player-facing configuration. @@ -156,11 +157,11 @@ A run repeatedly moves through: 6. Occasionally present a full-screen fun fact. 7. End with a result screen when the selected duration expires. -Timed runs finish the question currently on screen when the session clock expires. They do not begin another question. Unlimited runs continue until the player chooses to finish; elapsed time and score remain visible. +Timed runs also have fixed question caps: 10 questions in 5 minutes, 20 in 10 minutes, and 40 in 20 minutes. Reaching the cap completes the run after feedback. The session clock remains a fallback and moves directly to Results when it expires. Unlimited runs continue until the player chooses to finish; elapsed time and score remain visible. The selected duration measures active answering time. The session and question clocks run only while an unanswered question is interactive. Both clocks pause during answer feedback, the hint chooser, fun-fact cards, lifecycle interruptions, and backgrounding. This gives the player the full chosen duration for play rather than charging them for reading. -An interrupted active run is saved locally and can be resumed after process death or relaunch. A `Finish` action is always available in unlimited mode. Finishing after at least five answered questions produces results and maintains the daily streak. Leaving any mode early, or finishing unlimited mode before five answers, is an abandoned run: answered questions still update mastery, but it produces no result, personal best, or daily-streak credit. +An interrupted active run is saved locally and can be resumed after process death or relaunch. A `Finish` action is always available in unlimited mode. Finishing after at least five answered questions produces results. Leaving any mode early, or finishing unlimited mode before five answers, is an abandoned run: answered questions still update mastery and daily activity, but it produces no personal best or completed-run credit. ## 7. Question system @@ -239,8 +240,8 @@ Time pressure is central to scoring, but never prevents an answer. ### 8.1 Question timer -- Multiple choice starts with a 10-second response window. -- Letter-bank questions start with a 20-second response window. +- Multiple choice starts with a 30-second response window. +- Letter-bank questions start with a 30-second response window. - The interface warns visually and haptically near expiration without relying on color alone. - When the timer reaches zero, the question stays active and the player may still answer. @@ -316,7 +317,7 @@ Personal bests are recorded separately for each duration and topic configuration ### 11.1 Gentle daily streak -- Finishing any quiz run counts toward the current local calendar day. +- Answering at least one question counts toward the current local calendar day, including in a run the player later leaves. - Multiple runs in one day do not inflate the streak. - Missing a day resets the number without guilt-oriented language or loss animation. - There are no streak freezes, repairs, purchases, or notifications in V1. @@ -359,7 +360,7 @@ Core traits: - Supportive after mistakes without excessive praise - Never smug about a player's lack of knowledge -The provided brain-with-glasses reference establishes only the broad character archetype. Final artwork must use an original silhouette, facial design, proportions, clothing/accessories, poses, and rendering style. Its licensing status is unknown and it must not be shipped or traced. +The user-supplied `logo-bert.svg` is the canonical launcher and splash artwork. In-app poses preserve Bert's recognizable brain, glasses, and palette while adding state-specific supporting motion that never covers the face. ### 12.2 Art direction @@ -371,9 +372,13 @@ The provided brain-with-glasses reference establishes only the broad character a ### 12.3 Motion -Compottie animations support state and personality: +Animations support state and personality: -- Bert reacts to correct, incorrect, overtime, hint, streak, and milestone events. +- Idle uses restrained breathing motion. +- Correct answers use stars behind Bert; overtime adds a clock at bottom right. +- Incorrect answers use a restrained wobble. +- Hints use a thought cloud, while fun facts animate only the bulb rays. +- Results, streaks, and stamps use distinct non-overlapping motifs. - Answer cards respond immediately to selection. - Passport stamps land with a short satisfying animation. - Fun facts can use a restrained entrance and supporting loop.