M6b: core:design fixes after the M7 wave
Bugs: viewerPercentOf reads basis points (Samples in bp); FullSplitPicker and the
split labels use editor_split_full_on_you ("Allt på dig"); AmountInput.of restores
money with both decimals (AmountInput.percent for ratio fields), replacing M7c's
moneyInput; quickRowCategories does the D-060 fill itself; KvittScaffold resolves
resource-backed snackbars (showKvittSnackbar) while shown.
Gaps: ConfirmDialog optional and styled body; KvittButton loading form (M7a uses it,
ProgressBottomButton removed); InviteCodeField wraps after a group, placeholder,
imeAction; EmptyState icon tile and actionEnabled; DateFormatter.monthName and
dayRangeLong; AnalyticsFilterBar periodOwnRow; segmentedItem builder; screen tokens
in KvittDimens (OnboardingDimens removed); common_ok, common_undo; module-proposed
keys recorded in strings.csv; quick-row labels end in an ellipsis on line two.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
This commit is contained in:
-41
@@ -1,41 +0,0 @@
|
||||
package se.hagstedt.kvitt.feature.onboarding
|
||||
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
/**
|
||||
* Measures of design-spec §4 S-1, S-2 and S-18 that `:core:design`'s `KvittDimens` does not name yet. Kept in this one
|
||||
* file so they move to `KvittDimens` in one step (contract request in the M7a report); nothing else in this module
|
||||
* writes a size.
|
||||
*/
|
||||
internal object OnboardingDimens {
|
||||
// S-1 brand block: padding 28 / 0 / 12, app mark 88 with `receipt_long` 44.
|
||||
val brandTop = 28.dp
|
||||
val brandBottom = 12.dp
|
||||
val appMark = 88.dp
|
||||
val appMarkIcon = 44.dp
|
||||
|
||||
// S-1 column gap 14; bottom bar padding 12 / 16; progress indicator in the button 20 (stroke 2.5).
|
||||
val columnGap = 14.dp
|
||||
val bottomBarVertical = 12.dp
|
||||
val bottomBarHorizontal = 16.dp
|
||||
val buttonProgress = 20.dp
|
||||
val buttonProgressStroke = 2.5.dp
|
||||
|
||||
// S-1 help line icon 20.
|
||||
val helpIcon = 20.dp
|
||||
|
||||
// S-2 intro padding 24 / 4 / 4; card padding 22 / 20, gap 14; avatar badge 40 with icon 20 and a 3 dp card ring.
|
||||
val introTop = 24.dp
|
||||
val introSide = 4.dp
|
||||
val introBottom = 4.dp
|
||||
val cardVertical = 22.dp
|
||||
val cardHorizontal = 20.dp
|
||||
val avatarBadge = 40.dp
|
||||
val avatarBadgeIcon = 20.dp
|
||||
val avatarBadgeRing = 3.dp
|
||||
|
||||
// S-18: padding top 80, gap 14, tile 112 with `phonelink_erase` 52.
|
||||
val removedTop = 80.dp
|
||||
val removedTile = 112.dp
|
||||
val removedIcon = 52.dp
|
||||
}
|
||||
+5
-46
@@ -1,27 +1,11 @@
|
||||
package se.hagstedt.kvitt.feature.onboarding
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
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.heightIn
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import se.hagstedt.kvitt.design.KvittTheme
|
||||
import se.hagstedt.kvitt.design.component.ButtonKind
|
||||
import se.hagstedt.kvitt.design.component.ButtonSize
|
||||
import se.hagstedt.kvitt.design.component.KvittButton
|
||||
@@ -35,40 +19,15 @@ internal fun BottomActionBar(content: @Composable () -> Unit) {
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.navigationBarsPadding()
|
||||
.padding(horizontal = OnboardingDimens.bottomBarHorizontal, vertical = OnboardingDimens.bottomBarVertical),
|
||||
.padding(horizontal = KvittDimens.bottomBarHorizontal, vertical = KvittDimens.bottomBarVertical),
|
||||
) { content() }
|
||||
}
|
||||
|
||||
/** The full-width filled medium button of the bottom bars. */
|
||||
@Composable
|
||||
internal fun BottomButton(text: String, onClick: () -> Unit, icon: KvittIcon?, enabled: Boolean = true) {
|
||||
KvittButton(text, onClick, Modifier.fillMaxWidth(), kind = ButtonKind.Filled, size = ButtonSize.Medium, icon = icon, enabled = enabled)
|
||||
}
|
||||
|
||||
/**
|
||||
* S-1's "Para ihop" while pairing (design-spec S-1 "loading"): the filled medium button keeps its `primary` fill and
|
||||
* shows a 20 dp progress indicator in `on-primary` with "Parar ihop…"; it is not clickable. `KvittButton` has no
|
||||
* progress form (contract request in the M7a report), so this draws the same button with Material's `Button`.
|
||||
* The full-width filled medium button of the bottom bars. [loading] is S-1's "Parar ihop…" (design-spec S-1
|
||||
* "loading"): the fill kept, a 20 dp progress indicator in `on-primary`, not clickable ([KvittButton]'s loading form).
|
||||
*/
|
||||
@Composable
|
||||
internal fun ProgressBottomButton(text: String) {
|
||||
val scheme = MaterialTheme.colorScheme
|
||||
Button(
|
||||
onClick = {},
|
||||
modifier = Modifier.fillMaxWidth().heightIn(min = KvittDimens.buttonMedium),
|
||||
enabled = true,
|
||||
shape = CircleShape,
|
||||
colors = ButtonDefaults.buttonColors(containerColor = scheme.primary, contentColor = scheme.onPrimary),
|
||||
contentPadding = PaddingValues(horizontal = KvittDimens.buttonMediumHorizontal),
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center) {
|
||||
CircularProgressIndicator(
|
||||
Modifier.size(OnboardingDimens.buttonProgress),
|
||||
color = scheme.onPrimary,
|
||||
strokeWidth = OnboardingDimens.buttonProgressStroke,
|
||||
)
|
||||
Spacer(Modifier.width(KvittTheme.spacing.s2))
|
||||
Text(text, style = KvittTheme.typography.buttonMedium, textAlign = TextAlign.Center, maxLines = 2)
|
||||
}
|
||||
}
|
||||
internal fun BottomButton(text: String, onClick: () -> Unit, icon: KvittIcon?, enabled: Boolean = true, loading: Boolean = false) {
|
||||
KvittButton(text, onClick, Modifier.fillMaxWidth(), kind = ButtonKind.Filled, size = ButtonSize.Medium, icon = icon, enabled = enabled, loading = loading)
|
||||
}
|
||||
|
||||
+8
-6
@@ -130,7 +130,7 @@ fun PairingContent(
|
||||
bottomBar = {
|
||||
BottomActionBar {
|
||||
if (state.pairing) {
|
||||
ProgressBottomButton(stringResource(R.string.onboarding_pairing))
|
||||
BottomButton(stringResource(R.string.onboarding_pairing), {}, KvittIcon.Link, loading = true)
|
||||
} else {
|
||||
BottomButton(stringResource(R.string.onboarding_pair), actions.onPair, KvittIcon.Link, enabled = state.canPair)
|
||||
}
|
||||
@@ -143,7 +143,7 @@ fun PairingContent(
|
||||
.padding(padding)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(horizontal = KvittTheme.spacing.gutter),
|
||||
verticalArrangement = Arrangement.spacedBy(OnboardingDimens.columnGap),
|
||||
verticalArrangement = Arrangement.spacedBy(KvittDimens.onboardingGap),
|
||||
) {
|
||||
Brand(state.mode)
|
||||
val codeError = when (state.error) {
|
||||
@@ -160,6 +160,8 @@ fun PairingContent(
|
||||
modifier = Modifier.announced(codeError),
|
||||
error = codeError,
|
||||
enabled = !state.pairing,
|
||||
placeholder = stringResource(R.string.onboarding_code_placeholder),
|
||||
imeAction = if (state.nameRequired) ImeAction.Next else ImeAction.Done,
|
||||
)
|
||||
if (state.nameRequired) {
|
||||
KvittTextField(
|
||||
@@ -211,15 +213,15 @@ private fun Modifier.announced(error: String?): Modifier =
|
||||
private fun Brand(mode: PairingMode) {
|
||||
val scheme = MaterialTheme.colorScheme
|
||||
Column(
|
||||
Modifier.fillMaxWidth().padding(top = OnboardingDimens.brandTop, bottom = OnboardingDimens.brandBottom),
|
||||
Modifier.fillMaxWidth().padding(top = KvittDimens.brandTop, bottom = KvittDimens.brandBottom),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(KvittTheme.spacing.s3),
|
||||
) {
|
||||
Box(
|
||||
Modifier.size(OnboardingDimens.appMark).background(scheme.primary, KvittTheme.shapes.avatarLarge),
|
||||
Modifier.size(KvittDimens.appMark).background(scheme.primary, KvittTheme.shapes.avatarLarge),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
KvittIconImage(KvittIcon.ReceiptLong, null, filled = true, size = OnboardingDimens.appMarkIcon, tint = scheme.onPrimary)
|
||||
KvittIconImage(KvittIcon.ReceiptLong, null, filled = true, size = KvittDimens.appMarkIcon, tint = scheme.onPrimary)
|
||||
}
|
||||
Text(stringResource(DesignR.string.common_wordmark), style = MaterialTheme.typography.displaySmall, color = scheme.primary)
|
||||
if (mode == PairingMode.Again) {
|
||||
@@ -252,7 +254,7 @@ private fun HelpLine() {
|
||||
Modifier.fillMaxWidth().padding(horizontal = KvittTheme.spacing.s2),
|
||||
horizontalArrangement = Arrangement.spacedBy(KvittTheme.spacing.s3),
|
||||
) {
|
||||
KvittIconImage(KvittIcon.Info, null, size = OnboardingDimens.helpIcon, tint = muted)
|
||||
KvittIconImage(KvittIcon.Info, null, size = KvittDimens.helpIcon, tint = muted)
|
||||
Text(stringResource(R.string.onboarding_code_help_generic), style = MaterialTheme.typography.bodyMedium, color = muted)
|
||||
}
|
||||
}
|
||||
|
||||
+10
-10
@@ -169,10 +169,10 @@ private fun Intro(owner: User) {
|
||||
val ownerName = owner.displayName
|
||||
Column(
|
||||
Modifier.fillMaxWidth().padding(
|
||||
start = OnboardingDimens.introSide,
|
||||
end = OnboardingDimens.introSide,
|
||||
top = OnboardingDimens.introTop,
|
||||
bottom = OnboardingDimens.introBottom,
|
||||
start = KvittDimens.profileIntroSide,
|
||||
end = KvittDimens.profileIntroSide,
|
||||
top = KvittDimens.profileIntroTop,
|
||||
bottom = KvittDimens.profileIntroBottom,
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(KvittTheme.spacing.s2),
|
||||
@@ -197,9 +197,9 @@ private fun ProfileCard(state: ProfileStepUiState, actions: ProfileStepActions)
|
||||
val me = state.me ?: return
|
||||
KvittCard(
|
||||
Modifier.fillMaxWidth(),
|
||||
contentPadding = PaddingValues(horizontal = OnboardingDimens.cardHorizontal, vertical = OnboardingDimens.cardVertical),
|
||||
contentPadding = PaddingValues(horizontal = KvittDimens.profileCardHorizontal, vertical = KvittDimens.profileCardVertical),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(OnboardingDimens.columnGap),
|
||||
verticalArrangement = Arrangement.spacedBy(KvittDimens.onboardingGap),
|
||||
) {
|
||||
Box {
|
||||
PersonAvatar(
|
||||
@@ -212,14 +212,14 @@ private fun ProfileCard(state: ProfileStepUiState, actions: ProfileStepActions)
|
||||
Box(
|
||||
Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.size(OnboardingDimens.avatarBadge)
|
||||
.border(OnboardingDimens.avatarBadgeRing, KvittTheme.colors.card, CircleShape)
|
||||
.padding(OnboardingDimens.avatarBadgeRing)
|
||||
.size(KvittDimens.avatarBadge)
|
||||
.border(KvittDimens.avatarBadgeRing, KvittTheme.colors.card, CircleShape)
|
||||
.padding(KvittDimens.avatarBadgeRing)
|
||||
.background(MaterialTheme.colorScheme.primary, CircleShape)
|
||||
.clearAndSetSemantics {},
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
KvittIconImage(KvittIcon.AddAPhoto, null, size = OnboardingDimens.avatarBadgeIcon, tint = MaterialTheme.colorScheme.onPrimary)
|
||||
KvittIconImage(KvittIcon.AddAPhoto, null, size = KvittDimens.avatarBadgeIcon, tint = MaterialTheme.colorScheme.onPrimary)
|
||||
}
|
||||
}
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(KvittTheme.spacing.s1)) {
|
||||
|
||||
+5
-4
@@ -26,6 +26,7 @@ import se.hagstedt.kvitt.design.KvittTheme
|
||||
import se.hagstedt.kvitt.design.component.KvittIconImage
|
||||
import se.hagstedt.kvitt.design.component.KvittScaffold
|
||||
import se.hagstedt.kvitt.design.icon.KvittIcon
|
||||
import se.hagstedt.kvitt.design.theme.KvittDimens
|
||||
|
||||
/** S-18 Device removed (design-spec S-18, AC-3.3). [onPairAgain] opens S-1 after the revocation is acknowledged. */
|
||||
@Composable
|
||||
@@ -57,15 +58,15 @@ fun RemovedContent(onPairAgain: () -> Unit, modifier: Modifier = Modifier) {
|
||||
.fillMaxSize()
|
||||
.padding(padding)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(start = KvittTheme.spacing.gutter, end = KvittTheme.spacing.gutter, top = OnboardingDimens.removedTop),
|
||||
.padding(start = KvittTheme.spacing.gutter, end = KvittTheme.spacing.gutter, top = KvittDimens.removedTop),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(OnboardingDimens.columnGap),
|
||||
verticalArrangement = Arrangement.spacedBy(KvittDimens.onboardingGap),
|
||||
) {
|
||||
Box(
|
||||
Modifier.size(OnboardingDimens.removedTile).background(scheme.errorContainer, KvittTheme.shapes.illustration),
|
||||
Modifier.size(KvittDimens.removedTile).background(scheme.errorContainer, KvittTheme.shapes.illustration),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
KvittIconImage(KvittIcon.PhonelinkErase, null, size = OnboardingDimens.removedIcon, tint = scheme.onErrorContainer)
|
||||
KvittIconImage(KvittIcon.PhonelinkErase, null, size = KvittDimens.removedIcon, tint = scheme.onErrorContainer)
|
||||
}
|
||||
Text(
|
||||
stringResource(R.string.onboarding_removed_title),
|
||||
|
||||
Reference in New Issue
Block a user