ui(cashu): @Preview composables for the new wallet bits

Adds Android-Studio-visible previews for the recently-authored
composables on the Cashu wallet stack, so designers / reviewers can
eyeball them without building and launching the app. All previews use
the existing ThemeComparisonColumn helper to render dark + light side
by side, matching the convention used by SensitivityWarning and the
other existing preview files.

CashuWalletSettingsScreen:
- SettingsRow (with + without subtitle)
- EmptyRecommendationsHint
- AddRecommendationRow (empty + typing state)
- RecommendationSuggestionList
- RecommendationRow (plain + with review text)

AddCashuWalletScreen:
- MintSuggestionList (multi + single)

For RecommendationRow a tiny `fakeRecommendation` helper builds a
synthetic kind:38000 with stable tag layout (d / k / u) so the preview
exercises the same `mintUrls()` + `dTag()` paths the real renderer uses
without touching the signer.

Skipped: top-level screen composables (CashuWalletSettingsScreen,
CashuWalletScreen, AddCashuWalletScreen) — those take AccountViewModel /
CashuWalletViewModel / INav, which can't be cheaply faked. Anyone who
needs to see them previewed should iterate on the smaller sub-
composables included here.

https://claude.ai/code/session_01MdWddiar819f8XYt5N8BjP
This commit is contained in:
Claude
2026-05-27 15:17:43 +00:00
parent 8fa636bbe8
commit b0bb2885ea
2 changed files with 164 additions and 0 deletions
@@ -60,6 +60,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R
@@ -69,6 +70,7 @@ import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -463,3 +465,34 @@ private fun MintSuggestionList(
}
}
}
// ============================================================
// @Preview composables — Android Studio rendering only
// ============================================================
@Preview
@Composable
fun MintSuggestionListPreview() {
ThemeComparisonColumn {
MintSuggestionList(
suggestions =
listOf(
"https://mint.minibits.cash/bitcoin",
"https://mint.coinos.io",
"https://nutmix.cash",
),
onPick = {},
)
}
}
@Preview
@Composable
fun MintSuggestionListSinglePreview() {
ThemeComparisonColumn {
MintSuggestionList(
suggestions = listOf("https://mint.minibits.cash/bitcoin"),
onPick = {},
)
}
}
@@ -57,6 +57,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R
@@ -68,6 +69,7 @@ import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn
import com.vitorpamplona.quartz.nip87Ecash.recommendation.MintRecommendationEvent
/**
@@ -453,3 +455,132 @@ private fun RecommendationRow(
}
}
}
// ============================================================
// @Preview composables — Android Studio rendering only
// ============================================================
// Each preview is rendered in both dark and light themes via
// ThemeComparisonColumn so we can eyeball both at once. Plain-data
// composables only — anything that takes AccountViewModel / INav /
// CashuWalletViewModel can't be cheaply mocked, so those are skipped.
/** Synthesise a [MintRecommendationEvent] for previews without touching the signer. */
private fun fakeRecommendation(
mintUrl: String,
review: String = "",
): MintRecommendationEvent =
MintRecommendationEvent(
id = "0".repeat(64),
pubKey = "1".repeat(64),
createdAt = 1_700_000_000L,
tags =
arrayOf(
arrayOf("d", mintUrl),
arrayOf("k", "38172"),
arrayOf("u", mintUrl),
),
content = review,
sig = "2".repeat(128),
)
@Preview
@Composable
fun SettingsRowEditWalletPreview() {
ThemeComparisonColumn {
SettingsRow(
icon = MaterialSymbols.Edit,
title = "Edit wallet details",
subtitle = "Mints, nutzap key",
onClick = {},
)
}
}
@Preview
@Composable
fun SettingsRowNoSubtitlePreview() {
ThemeComparisonColumn {
SettingsRow(
icon = MaterialSymbols.Settings,
title = "Some setting",
subtitle = null,
onClick = {},
)
}
}
@Preview
@Composable
fun EmptyRecommendationsHintPreview() {
ThemeComparisonColumn {
EmptyRecommendationsHint()
}
}
@Preview
@Composable
fun AddRecommendationRowEmptyPreview() {
ThemeComparisonColumn {
AddRecommendationRow(
input = "",
onInputChange = {},
canAdd = false,
onAdd = {},
)
}
}
@Preview
@Composable
fun AddRecommendationRowTypingPreview() {
ThemeComparisonColumn {
AddRecommendationRow(
input = "https://mint.minibits.cash",
onInputChange = {},
canAdd = true,
onAdd = {},
)
}
}
@Preview
@Composable
fun RecommendationSuggestionListPreview() {
ThemeComparisonColumn {
RecommendationSuggestionList(
suggestions =
listOf(
"https://mint.minibits.cash/bitcoin",
"https://mint.coinos.io",
"https://nutmix.cash",
),
onPick = {},
)
}
}
@Preview
@Composable
fun RecommendationRowPreview() {
ThemeComparisonColumn {
RecommendationRow(
event = fakeRecommendation("https://mint.minibits.cash/bitcoin"),
onDelete = {},
)
}
}
@Preview
@Composable
fun RecommendationRowWithReviewPreview() {
ThemeComparisonColumn {
RecommendationRow(
event =
fakeRecommendation(
mintUrl = "https://mint.coinos.io",
review = "Fast and reliable, been using for months.",
),
onDelete = {},
)
}
}