From ffa92ea10a42950136609a5c0209a1e0c6dc2cde Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 12 Sep 2026 01:06:13 +0000 Subject: [PATCH 1/3] Explain memo privacy and chore/task sync in Help The Help cards defined what a chore, task and memo are but never said where each one's data lives. Add a clause to each of the three type cards: chores and tasks sync across the household (Supabase), memos stay on the device and never sync. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01GZHz422dXfSfQtRadVhSuf --- .../java/com/mapgie/dash/ui/components/HelpContent.kt | 9 ++++++--- changelog/unreleased/help-privacy-clarification.json | 6 ++++++ 2 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 changelog/unreleased/help-privacy-clarification.json diff --git a/app/src/main/java/com/mapgie/dash/ui/components/HelpContent.kt b/app/src/main/java/com/mapgie/dash/ui/components/HelpContent.kt index c419d33..a34e817 100644 --- a/app/src/main/java/com/mapgie/dash/ui/components/HelpContent.kt +++ b/app/src/main/java/com/mapgie/dash/ui/components/HelpContent.kt @@ -51,7 +51,8 @@ fun HelpContent( title = "Chores repeat", body = "A chore comes round again on its own cadence: every 3 days, every " + "month. Log it with a tap, a swipe, or by holding the phone to its NFC " + - "sticker. The colour spine and badge show how overdue it is.", + "sticker. The colour spine and badge show how overdue it is. Chores sync " + + "across your household, so everyone sees the same list.", ) SheetRowDivider() HelpRow( @@ -60,7 +61,8 @@ fun HelpContent( content = accents.onTaskContainer, title = "Tasks happen once", body = "A task is a one-off with an optional due date and priority. Tick it " + - "when it is done and it drops into the Done section.", + "when it is done and it drops into the Done section. Tasks sync across " + + "your household too.", ) SheetRowDivider() HelpRow( @@ -69,7 +71,8 @@ fun HelpContent( content = accents.onReminderContainer, title = "$reminderLabel nudge you", body = "A ${reminderLabel.lowercase().trimEnd('s')} is a nudge at a set time, on its " + - "own or linked to a chore or task. Snooze it or mark it done from the alert.", + "own or linked to a chore or task. Snooze it or mark it done from the alert. " + + "It stays on this phone, private to you, and never syncs.", ) } if (showTips) { diff --git a/changelog/unreleased/help-privacy-clarification.json b/changelog/unreleased/help-privacy-clarification.json new file mode 100644 index 0000000..3cb2f7e --- /dev/null +++ b/changelog/unreleased/help-privacy-clarification.json @@ -0,0 +1,6 @@ +{ + "bump": "patch", + "changed": [ + "Help now explains that chores and tasks sync across your household while memos stay private on your phone." + ] +} From 54ddf0ce1d3a2511d95dfb1c6a64cb5d6496cf16 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 12 Sep 2026 01:41:38 +0000 Subject: [PATCH 2/3] Add a 'Getting around' page to Help Help had one page defining chores, tasks and memos, but nothing explaining the controls. Add a second page behind a segmented header that tours them, grouped by where they live: the actions on an open card (log/finish, Calendar, Pin, Remind, Tag, history and undo), the icons above a list (search, mine/all, sort, group or flat, zen), and the Settings that thin a list out. The terse control hints that used to sit under the type cards move here, expanded and organised. The first-run welcome sheet shows the same tour under the three type cards, so it is taught once and repeated under Settings > Help. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01GZHz422dXfSfQtRadVhSuf --- .../mapgie/dash/ui/components/HelpContent.kt | 186 +++++++++++++----- .../mapgie/dash/ui/components/WelcomeSheet.kt | 4 +- .../dash/ui/screens/settings/HelpSubScreen.kt | 114 ++++++++++- .../unreleased/help-getting-around-page.json | 9 + 4 files changed, 257 insertions(+), 56 deletions(-) create mode 100644 changelog/unreleased/help-getting-around-page.json diff --git a/app/src/main/java/com/mapgie/dash/ui/components/HelpContent.kt b/app/src/main/java/com/mapgie/dash/ui/components/HelpContent.kt index a34e817..05e6aa0 100644 --- a/app/src/main/java/com/mapgie/dash/ui/components/HelpContent.kt +++ b/app/src/main/java/com/mapgie/dash/ui/components/HelpContent.kt @@ -21,16 +21,17 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import com.mapgie.dash.ui.components.core.SectionLabel import com.mapgie.dash.ui.components.sheet.SheetBlock import com.mapgie.dash.ui.components.sheet.SheetRowDivider -import com.mapgie.dash.ui.theme.LocalDashTokens import com.mapgie.dash.ui.theme.LocalTypeAccents import com.mapgie.dash.ui.theme.LucideIcons /** - * The one explanation of what a chore, a task and a memo are. The speed dial - * carries no hint text (handoff 7a): this is taught once in the first-run - * welcome sheet and repeated under Settings › Help, so both render this. + * Page one of Help: the one explanation of what a chore, a task and a memo + * are. The speed dial carries no hint text (handoff 7a): this is taught once + * in the first-run welcome sheet and repeated under Settings › Help, so both + * render this. The controls tour lives in [HelpGettingAround]. * * [reminderLabel] is the user's chosen name for the reminders feature * ("Memos", "Alarms" or "Reminders"). @@ -39,54 +40,151 @@ import com.mapgie.dash.ui.theme.LucideIcons fun HelpContent( reminderLabel: String, modifier: Modifier = Modifier, - showTips: Boolean = true, ) { val accents = LocalTypeAccents.current + val one = reminderLabel.lowercase().trimEnd('s') + SheetBlock(modifier = modifier) { + HelpRow( + icon = LucideIcons.HouseCheck, + container = accents.choreContainer, + content = accents.onChoreContainer, + title = "Chores repeat", + body = "A chore comes round again on its own cadence: every 3 days, every " + + "month. Log it with a tap, a swipe, or by holding the phone to its NFC " + + "sticker. The colour spine and badge show how overdue it is. Chores sync " + + "across your household, so everyone sees the same list.", + ) + SheetRowDivider() + HelpRow( + icon = LucideIcons.CircleCheck, + container = accents.taskContainer, + content = accents.onTaskContainer, + title = "Tasks happen once", + body = "A task is a one-off with an optional due date and priority. Tick it " + + "when it is done and it drops into the Done section. Tasks sync across " + + "your household too.", + ) + SheetRowDivider() + HelpRow( + icon = LucideIcons.Bell, + container = accents.reminderContainer, + content = accents.onReminderContainer, + title = "$reminderLabel nudge you", + body = "A $one is a nudge at a set time, on its own or linked to a chore or " + + "task. Snooze it or mark it done from the alert. It stays on this phone, " + + "private to you, and never syncs.", + ) + } +} + +/** + * Page two of Help: a grouped tour of the controls, so the buttons on a card, + * the icons above a list, and the Settings that thin a list out are each + * explained once, in the place they belong. Shares [HelpRow] with the type + * cards above; the control rows tint neutrally so they read as actions rather + * than as a fourth kind of thing. + */ +@Composable +fun HelpGettingAround( + reminderLabel: String, + modifier: Modifier = Modifier, +) { + val one = reminderLabel.lowercase().trimEnd('s') + val container = MaterialTheme.colorScheme.surfaceContainerHigh + val content = MaterialTheme.colorScheme.onSurfaceVariant Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(14.dp)) { + SectionLabel(text = "On a card") SheetBlock { HelpRow( - icon = LucideIcons.HouseCheck, - container = accents.choreContainer, - content = accents.onChoreContainer, - title = "Chores repeat", - body = "A chore comes round again on its own cadence: every 3 days, every " + - "month. Log it with a tap, a swipe, or by holding the phone to its NFC " + - "sticker. The colour spine and badge show how overdue it is. Chores sync " + - "across your household, so everyone sees the same list.", + icon = LucideIcons.CircleCheck, container = container, content = content, + title = "Open and finish", + body = "Tap any card to open it, then Log it (a chore) or Mark done (a task). " + + "Set when it happened with Just now, Earlier today, or Pick a time. " + + "Long-press a card to jump straight to editing.", + ) + SheetRowDivider() + HelpRow( + icon = LucideIcons.Calendar, container = container, content = content, + title = "Calendar", + body = "Add the item to your phone's calendar as an event.", ) SheetRowDivider() HelpRow( - icon = LucideIcons.CircleCheck, - container = accents.taskContainer, - content = accents.onTaskContainer, - title = "Tasks happen once", - body = "A task is a one-off with an optional due date and priority. Tick it " + - "when it is done and it drops into the Done section. Tasks sync across " + - "your household too.", + icon = LucideIcons.Pin, container = container, content = content, + title = "Pin", + body = "Keep the item on your home-screen widget.", ) SheetRowDivider() HelpRow( - icon = LucideIcons.Bell, - container = accents.reminderContainer, - content = accents.onReminderContainer, - title = "$reminderLabel nudge you", - body = "A ${reminderLabel.lowercase().trimEnd('s')} is a nudge at a set time, on its " + - "own or linked to a chore or task. Snooze it or mark it done from the alert. " + - "It stays on this phone, private to you, and never syncs.", + icon = LucideIcons.Bell, container = container, content = content, + title = "Remind", + body = "Attach a $one so the item nudges you at a set time.", + ) + SheetRowDivider() + HelpRow( + icon = LucideIcons.NfcScan, container = container, content = content, + title = "Tag", + body = "Chores only: link an NFC sticker, then a tap of the phone logs the chore.", + ) + SheetRowDivider() + HelpRow( + icon = LucideIcons.Undo, container = container, content = content, + title = "History", + body = "A chore keeps every past log. Undo reverses the last one; All opens " + + "the full list.", ) } - if (showTips) { - SheetBlock { - HelpTip("Tap a card to log or finish it. Long-press to edit.") - SheetRowDivider() - HelpTip("Tap the + to add to the page you're on. Long-press it to pick any type from the menu.") - SheetRowDivider() - HelpTip("The sort pill above each list names its order in words. Tap it to change the key or direction.") - SheetRowDivider() - HelpTip("Zen (the target icon) hides colours and counts for a calmer list. Leave with the cross.") - SheetRowDivider() - HelpTip("Settings › Colours and Settings › Categories choose what tints each card and how groups are ordered.") - } + + SectionLabel(text = "Around a list") + SheetBlock { + HelpRow( + icon = LucideIcons.Search, container = container, content = content, + title = "Search", + body = "Filter the list by name, category, or owner.", + ) + SheetRowDivider() + HelpRow( + icon = LucideIcons.User, container = container, content = content, + title = "Mine or everyone", + body = "The person icon shows just your items or the whole household's.", + ) + SheetRowDivider() + HelpRow( + icon = LucideIcons.ArrowUp, container = container, content = content, + title = "Sort", + body = "The sort pill names the order in words. Tap the label to change what " + + "it sorts by, the arrow to flip the direction.", + ) + SheetRowDivider() + HelpRow( + icon = LucideIcons.LayoutGrid, container = container, content = content, + title = "Group or flat", + body = "Group the list under category headers, or lay it out flat.", + ) + SheetRowDivider() + HelpRow( + icon = LucideIcons.Target, container = container, content = content, + title = "Zen", + body = "The target icon hides colours and counts for a calmer list. Leave it " + + "with the cross.", + ) + } + + SectionLabel(text = "Adding and hiding") + SheetBlock { + HelpRow( + icon = LucideIcons.Plus, container = container, content = content, + title = "The plus button", + body = "Adds to the page you're on. Long-press it to pick any type: a chore, " + + "a task, or a $one.", + ) + SheetRowDivider() + HelpRow( + icon = LucideIcons.Clock, container = container, content = content, + title = "Hide what isn't due", + body = "Settings › Display can hide chores until they're close to due and " + + "tasks whose due date is far off, so the list shows only what needs doing.", + ) } } } @@ -128,13 +226,3 @@ private fun HelpRow( } } } - -@Composable -private fun HelpTip(text: String) { - Text( - text, - style = MaterialTheme.typography.bodyMedium.copy(fontSize = 13.5.sp, lineHeight = 19.sp, fontWeight = FontWeight.SemiBold), - color = LocalDashTokens.current.inkFaint, - modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp), - ) -} diff --git a/app/src/main/java/com/mapgie/dash/ui/components/WelcomeSheet.kt b/app/src/main/java/com/mapgie/dash/ui/components/WelcomeSheet.kt index d29bcb1..2b2b2ce 100644 --- a/app/src/main/java/com/mapgie/dash/ui/components/WelcomeSheet.kt +++ b/app/src/main/java/com/mapgie/dash/ui/components/WelcomeSheet.kt @@ -57,7 +57,9 @@ fun WelcomeSheet( "Three kinds of thing.", style = MaterialTheme.typography.headlineMedium, ) - HelpContent(reminderLabel = reminderLabel, showTips = false) + HelpContent(reminderLabel = reminderLabel) + SectionLabel(text = "Getting around") + HelpGettingAround(reminderLabel = reminderLabel) Spacer(Modifier.height(2.dp)) Button( onClick = onDismiss, diff --git a/app/src/main/java/com/mapgie/dash/ui/screens/settings/HelpSubScreen.kt b/app/src/main/java/com/mapgie/dash/ui/screens/settings/HelpSubScreen.kt index 4c6b93d..9d66067 100644 --- a/app/src/main/java/com/mapgie/dash/ui/screens/settings/HelpSubScreen.kt +++ b/app/src/main/java/com/mapgie/dash/ui/screens/settings/HelpSubScreen.kt @@ -1,22 +1,49 @@ package com.mapgie.dash.ui.screens.settings +import androidx.compose.foundation.background +import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +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.rememberScrollState +import androidx.compose.foundation.selection.selectable import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.key +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.role +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import com.mapgie.dash.data.model.ReminderLabelStyle import com.mapgie.dash.ui.components.HelpContent +import com.mapgie.dash.ui.components.HelpGettingAround +import com.mapgie.dash.ui.theme.LocalDashTokens +import com.mapgie.dash.ui.theme.PillShape -/** Settings › Help: the chores / tasks / memos explanation plus a few tips. */ +/** + * Settings › Help: two pages behind a segmented header. "What goes where" is + * the chores / tasks / memos explanation; "Getting around" tours the controls. + */ @Composable internal fun HelpSubScreen( onBack: () -> Unit, @@ -24,19 +51,94 @@ internal fun HelpSubScreen( ) { val settings by viewModel.settings.collectAsState() val reminderLabel = (settings?.reminderLabel ?: ReminderLabelStyle.REMINDERS).displayName + var page by rememberSaveable { mutableIntStateOf(0) } + val tabs = listOf("What goes where", "Getting around") SettingsSubScreenScaffold(title = "Help", onBack = onBack) { innerPadding -> Column( modifier = Modifier .fillMaxSize() - .verticalScroll(rememberScrollState()) .padding(innerPadding) .padding(horizontal = 18.dp, vertical = 6.dp), - verticalArrangement = Arrangement.spacedBy(14.dp), ) { - SettingsSectionLabel("What goes where") - HelpContent(reminderLabel = reminderLabel) - Spacer(Modifier.height(8.dp)) + HelpPageTabs(selected = page, labels = tabs, onSelect = { page = it }) + Spacer(Modifier.height(14.dp)) + // A fresh scroll state per page so each opens at the top. + key(page) { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(14.dp), + ) { + when (page) { + 0 -> HelpContent(reminderLabel = reminderLabel) + else -> HelpGettingAround(reminderLabel = reminderLabel) + } + Spacer(Modifier.height(8.dp)) + } + } } } } + +/** Segmented header, two full-width cells; mirrors the zen "mine | all" pill. */ +@Composable +private fun HelpPageTabs( + selected: Int, + labels: List, + onSelect: (Int) -> Unit, +) { + val tokens = LocalDashTokens.current + Row( + modifier = Modifier + .fillMaxWidth() + .clip(PillShape) + .background(tokens.sheetBlock) + .border(1.dp, tokens.sheetBlockOutline, PillShape) + .padding(4.dp), + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + labels.forEachIndexed { index, label -> + HelpPageTab( + label = label, + selected = index == selected, + onClick = { onSelect(index) }, + modifier = Modifier.weight(1f), + ) + } + } +} + +@Composable +private fun HelpPageTab( + label: String, + selected: Boolean, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + val tokens = LocalDashTokens.current + Box( + contentAlignment = Alignment.Center, + modifier = modifier + .heightIn(min = 44.dp) + .clip(PillShape) + .semantics { role = Role.RadioButton } + .selectable(selected = selected, onClick = onClick), + ) { + Text( + label, + style = MaterialTheme.typography.labelMedium.copy( + fontWeight = if (selected) FontWeight.ExtraBold else FontWeight.Bold, + ), + color = if (selected) MaterialTheme.colorScheme.onSurfaceVariant else tokens.inkFaint, + textAlign = TextAlign.Center, + maxLines = 1, + modifier = Modifier + .fillMaxWidth() + .clip(PillShape) + .background(if (selected) MaterialTheme.colorScheme.surfaceContainerHigh else Color.Transparent) + .padding(horizontal = 10.dp, vertical = 9.dp), + ) + } +} diff --git a/changelog/unreleased/help-getting-around-page.json b/changelog/unreleased/help-getting-around-page.json new file mode 100644 index 0000000..535ba81 --- /dev/null +++ b/changelog/unreleased/help-getting-around-page.json @@ -0,0 +1,9 @@ +{ + "bump": "minor", + "added": [ + "Help now has a second page, Getting around, explaining the card actions, the list controls, and the display settings, grouped by where you find them." + ], + "changed": [ + "The first-run welcome sheet includes the Getting around tour." + ] +} From 8a500963583b69bd2cb54b353dd6f94b897ab9a2 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 12 Sep 2026 07:43:58 +0000 Subject: [PATCH 3/3] Fix rememberSaveable import in HelpSubScreen rememberSaveable lives in androidx.compose.runtime.saveable, not androidx.compose.runtime; the wrong package broke compileDebugKotlin (and with it the unit-test and analyze jobs). Match the import the rest of the codebase uses. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01GZHz422dXfSfQtRadVhSuf --- .../java/com/mapgie/dash/ui/screens/settings/HelpSubScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/mapgie/dash/ui/screens/settings/HelpSubScreen.kt b/app/src/main/java/com/mapgie/dash/ui/screens/settings/HelpSubScreen.kt index 9d66067..db498b1 100644 --- a/app/src/main/java/com/mapgie/dash/ui/screens/settings/HelpSubScreen.kt +++ b/app/src/main/java/com/mapgie/dash/ui/screens/settings/HelpSubScreen.kt @@ -22,7 +22,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.key import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.rememberSaveable +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier