Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 144 additions & 48 deletions app/src/main/java/com/mapgie/dash/ui/components/HelpContent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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").
Expand All @@ -39,53 +40,158 @@ 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.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.GripVertical, container = container, content = content,
title = "Swipe",
body = "Swipe a card sideways to log, snooze, archive, or delete it. " +
"Settings › Swipe actions sets which direction does what.",
)
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.Pin, container = container, content = content,
title = "Pin",
body = "Keep the item on your home-screen widget.",
)
SheetRowDivider()
HelpRow(
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.",
)
}

SectionLabel(text = "Around a list")
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.",
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.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.",
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.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.",
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.",
)
}
if (showTips) {
SheetBlock {
HelpTip("Tap a card to log or finish it. Long-press to edit.")
SheetRowDivider()
HelpTip("Swipe a card sideways to log, snooze, archive or delete it. Settings › Swipe actions chooses which.")
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 = "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.",
)
}
}
}
Expand Down Expand Up @@ -127,13 +233,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),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
114 changes: 108 additions & 6 deletions app/src/main/java/com/mapgie/dash/ui/screens/settings/HelpSubScreen.kt
Original file line number Diff line number Diff line change
@@ -1,42 +1,144 @@
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.saveable.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,
viewModel: SettingsViewModel,
) {
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<String>,
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),
)
}
}
Loading
Loading