From 78fe7676a2608c524bc755b104220dcc1a3a9b4c Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Sun, 28 Jun 2026 21:01:01 +0200 Subject: [PATCH] refactor(onboarding): draw the onboarding shell from floret-kit components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OnboardingScaffold, OnboardingSpace and BenefitRow were identical to the kit's components forms, so delete the app-local copies and repoint the permission and reminder onboarding screens at floret-kit. BrandHero — Calendula's own launcher mark with the permission-denied lock badge — stays app-local in its own file (each sibling app keeps its own hero). Compile + unit tests + lintDebug green. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../calendula/ui/permission/BrandHero.kt | 66 +++++++ .../ui/permission/OnboardingScaffold.kt | 163 ------------------ .../ui/permission/PermissionScreen.kt | 4 + .../ui/permission/ReminderOnboardingScreen.kt | 4 + 4 files changed, 74 insertions(+), 163 deletions(-) create mode 100644 app/src/main/java/de/jeanlucmakiola/calendula/ui/permission/BrandHero.kt delete mode 100644 app/src/main/java/de/jeanlucmakiola/calendula/ui/permission/OnboardingScaffold.kt diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/permission/BrandHero.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/permission/BrandHero.kt new file mode 100644 index 0000000..de52799 --- /dev/null +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/permission/BrandHero.kt @@ -0,0 +1,66 @@ +package de.jeanlucmakiola.calendula.ui.permission + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Lock +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import de.jeanlucmakiola.calendula.R + +/** + * The app's adaptive launcher mark, reconstructed as a large branded squircle — + * the hero of the onboarding screens (floret-kit's OnboardingScaffold supplies + * the shell; the mark stays app-local so each sibling keeps its own identity). + * A lock badge overlays the corner when permission has been [denied]. + */ +@Composable +internal fun BrandHero(denied: Boolean) { + Box(contentAlignment = Alignment.Center) { + Box( + modifier = Modifier + .size(128.dp) + .clip(RoundedCornerShape(34.dp)) + .background(colorResource(R.color.ic_launcher_background)), + ) { + Image( + painter = painterResource(R.drawable.ic_launcher_foreground), + contentDescription = stringResource(R.string.app_name), + modifier = Modifier.fillMaxSize(), + ) + } + if (denied) { + // A small lock badge sits over the corner to signal "blocked". + Box( + modifier = Modifier + .align(Alignment.BottomEnd) + .offset(x = 10.dp, y = 10.dp) + .size(44.dp) + .clip(CircleShape) + .background(MaterialTheme.colorScheme.errorContainer), + contentAlignment = Alignment.Center, + ) { + Icon( + imageVector = Icons.Filled.Lock, + contentDescription = null, + tint = MaterialTheme.colorScheme.onErrorContainer, + modifier = Modifier.size(24.dp), + ) + } + } + } +} diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/permission/OnboardingScaffold.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/permission/OnboardingScaffold.kt deleted file mode 100644 index fbc5f58..0000000 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/permission/OnboardingScaffold.kt +++ /dev/null @@ -1,163 +0,0 @@ -package de.jeanlucmakiola.calendula.ui.permission - -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope -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.navigationBarsPadding -import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Lock -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.colorResource -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import de.jeanlucmakiola.calendula.R - -/** MD3 8dp spacing scale shared by the onboarding screens. */ -internal object OnboardingSpace { - val xs = 8.dp - val sm = 16.dp - val md = 24.dp - val lg = 32.dp - val xl = 48.dp -} - -/** - * Shared onboarding shell (calendar grant, reminder step): a scrollable, - * centred hero + body with the call(s) to action pinned to the bottom (clear - * of the navigation bar). The content slot is centred horizontally; benefit - * rows fill the width so their own content left-aligns. - */ -@Composable -internal fun OnboardingScaffold( - hero: @Composable () -> Unit, - actions: @Composable ColumnScope.() -> Unit, - modifier: Modifier = Modifier, - body: @Composable ColumnScope.() -> Unit, -) { - Scaffold( - modifier = modifier, - containerColor = MaterialTheme.colorScheme.surface, - bottomBar = { - Column( - modifier = Modifier - .fillMaxWidth() - .navigationBarsPadding() - .padding(horizontal = OnboardingSpace.md, vertical = OnboardingSpace.sm), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(4.dp), - content = actions, - ) - }, - ) { innerPadding -> - Column( - modifier = Modifier - .fillMaxSize() - .padding(innerPadding) - .verticalScroll(rememberScrollState()) - .padding(horizontal = OnboardingSpace.md), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Spacer(Modifier.height(OnboardingSpace.xl)) - hero() - Spacer(Modifier.height(OnboardingSpace.lg)) - body() - Spacer(Modifier.height(OnboardingSpace.md)) - } - } -} - -/** The app's adaptive launcher mark, reconstructed as a large branded squircle. */ -@Composable -internal fun BrandHero(denied: Boolean) { - Box(contentAlignment = Alignment.Center) { - Box( - modifier = Modifier - .size(128.dp) - .clip(RoundedCornerShape(34.dp)) - .background(colorResource(R.color.ic_launcher_background)), - ) { - Image( - painter = painterResource(R.drawable.ic_launcher_foreground), - contentDescription = stringResource(R.string.app_name), - modifier = Modifier.fillMaxSize(), - ) - } - if (denied) { - // A small lock badge sits over the corner to signal "blocked". - Box( - modifier = Modifier - .align(Alignment.BottomEnd) - .offset(x = 10.dp, y = 10.dp) - .size(44.dp) - .clip(CircleShape) - .background(MaterialTheme.colorScheme.errorContainer), - contentAlignment = Alignment.Center, - ) { - Icon( - imageVector = Icons.Filled.Lock, - contentDescription = null, - tint = MaterialTheme.colorScheme.onErrorContainer, - modifier = Modifier.size(24.dp), - ) - } - } - } -} - -/** One trust point: a tonal icon chip on the left, title + supporting text right. */ -@Composable -internal fun BenefitRow(icon: ImageVector, title: String, body: String) { - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - ) { - Box( - modifier = Modifier - .size(44.dp) - .clip(CircleShape) - .background(MaterialTheme.colorScheme.secondaryContainer), - contentAlignment = Alignment.Center, - ) { - Icon( - imageVector = icon, - contentDescription = null, - tint = MaterialTheme.colorScheme.onSecondaryContainer, - modifier = Modifier.size(22.dp), - ) - } - Spacer(Modifier.width(OnboardingSpace.sm)) - Column(modifier = Modifier.weight(1f)) { - Text(text = title, style = MaterialTheme.typography.titleMedium) - Text( - text = body, - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) - } - } -} diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/permission/PermissionScreen.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/permission/PermissionScreen.kt index c7f3f9d..da293e1 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/permission/PermissionScreen.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/permission/PermissionScreen.kt @@ -1,5 +1,9 @@ package de.jeanlucmakiola.calendula.ui.permission +import de.jeanlucmakiola.floret.components.BenefitRow +import de.jeanlucmakiola.floret.components.OnboardingScaffold +import de.jeanlucmakiola.floret.components.OnboardingSpace + import android.Manifest import android.content.Intent import android.net.Uri diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/permission/ReminderOnboardingScreen.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/permission/ReminderOnboardingScreen.kt index 6f20e3c..27f7e21 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/permission/ReminderOnboardingScreen.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/permission/ReminderOnboardingScreen.kt @@ -1,5 +1,9 @@ package de.jeanlucmakiola.calendula.ui.permission +import de.jeanlucmakiola.floret.components.BenefitRow +import de.jeanlucmakiola.floret.components.OnboardingScaffold +import de.jeanlucmakiola.floret.components.OnboardingSpace + import android.Manifest import android.os.Build import androidx.activity.compose.rememberLauncherForActivityResult