From 1b8ac64822f6f69d92ed6ecb729d59173ae30e50 Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Wed, 30 Sep 2026 14:01:24 -0400 Subject: [PATCH 01/10] fix(login): match iOS text sizes on the account switcher rows The account name used textLarge (20sp) and the secondary lines textSmall (14sp); iOS sets them at 16pt (appTextMedium) and 12pt (appTextHeading). Move the name to textMedium and the created date, owner address and balance-status lines to caption. --- .../app/login/internal/accounts/AccountRow.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt b/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt index f795d4ca3f..7cf680b80e 100644 --- a/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt +++ b/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt @@ -62,20 +62,20 @@ internal fun AccountRow( ) { Text( text = account.name, - // textLarge is the primary line on the app's other list rows (Blocklist). - style = CodeTheme.typography.textLarge, + // iOS sets the name and the balance on one 16pt line (appTextMedium). + style = CodeTheme.typography.textMedium, color = CodeTheme.colors.textMain, maxLines = 1, overflow = TextOverflow.Ellipsis, ) Text( text = stringResource(R.string.subtitle_accountCreated, relativeCreationDate), - style = CodeTheme.typography.textSmall, + style = CodeTheme.typography.caption, color = CodeTheme.colors.textSecondary, ) Text( text = account.ownerAddress, - style = CodeTheme.typography.textSmall, + style = CodeTheme.typography.caption, color = CodeTheme.colors.textSecondary, ) } @@ -92,13 +92,13 @@ internal fun AccountRow( account.notFound -> Text( text = stringResource(R.string.subtitle_accountNotFound), - style = CodeTheme.typography.textSmall, + style = CodeTheme.typography.caption, color = CodeTheme.colors.textSecondary, ) account.balanceUnavailable -> Text( text = stringResource(R.string.subtitle_balanceUnavailable), - style = CodeTheme.typography.textSmall, + style = CodeTheme.typography.caption, color = CodeTheme.colors.textSecondary, ) From 690d83718ff12cf2fbf5d227a303b9604abcc62a Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Wed, 30 Sep 2026 14:02:34 -0400 Subject: [PATCH 02/10] fix(login): match iOS row spacing on the account switcher iOS pads each row 20pt and sets the name line 9pt above the secondary lines. On the 5dp grid that is grid.x4 of vertical padding and an extra grid.x1 under the name, for 10dp. --- .../com/flipcash/app/login/internal/accounts/AccountRow.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt b/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt index 7cf680b80e..5a8a0934f2 100644 --- a/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt +++ b/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt @@ -40,7 +40,8 @@ internal fun AccountRow( onClick = onClick, onLongClick = onLongClick, ) - .padding(vertical = CodeTheme.dimens.grid.x3), + // iOS pads each row 20pt on every side; the list's contentPadding supplies the sides. + .padding(vertical = CodeTheme.dimens.grid.x4), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(CodeTheme.dimens.grid.x3), ) { @@ -61,6 +62,8 @@ internal fun AccountRow( verticalArrangement = Arrangement.spacedBy(CodeTheme.dimens.grid.x1), ) { Text( + // iOS adds 5pt under the name line on top of the column's spacing. + modifier = Modifier.padding(bottom = CodeTheme.dimens.grid.x1), text = account.name, // iOS sets the name and the balance on one 16pt line (appTextMedium). style = CodeTheme.typography.textMedium, From 0c9f186ee06951abceec171358d87cd5f725d89e Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Wed, 30 Sep 2026 14:04:24 -0400 Subject: [PATCH 03/10] fix(login): separate account switcher rows with a divider iOS draws a rowSeparator hairline (white at 10%) between the rows. Draw a 0.5dp HorizontalDivider in CodeTheme.colors.divider, which is the same White10, above every row but the first. --- .../internal/accounts/AccountSelectionContent.kt | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountSelectionContent.kt b/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountSelectionContent.kt index d5c6bbb857..64d7ed0018 100644 --- a/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountSelectionContent.kt +++ b/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountSelectionContent.kt @@ -8,14 +8,16 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material.Text +import androidx.compose.material3.HorizontalDivider import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp import com.flipcash.features.login.R import com.getcode.theme.CodeTheme import com.getcode.ui.theme.ButtonState @@ -64,7 +66,15 @@ internal fun AccountSelectionContent( modifier = Modifier.fillMaxSize(), contentPadding = PaddingValues(horizontal = CodeTheme.dimens.inset), ) { - items(state.accounts, key = { it.id }) { account -> + itemsIndexed(state.accounts, key = { _, account -> account.id }) { index, account -> + // iOS separates the rows with a hairline in rowSeparator, the same white + // at 10% as divider. + if (index > 0) { + HorizontalDivider( + color = CodeTheme.colors.divider, + thickness = 0.5.dp, + ) + } AccountRow( account = account, isCurrent = account.entropy == state.currentEntropy, From 769504bb4c622514f4481bbb6122ca0939ce0846 Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Wed, 30 Sep 2026 14:07:07 -0400 Subject: [PATCH 04/10] fix(login): put the balance on the account name line iOS sets the name and balance in one bottom-aligned HStack, 10pt apart, with the secondary lines below. Move the balance slot, including its not-found, unavailable and loading states, into a Row with the name, spaced by grid.x2. --- .../app/login/internal/accounts/AccountRow.kt | 88 ++++++++++--------- 1 file changed, 48 insertions(+), 40 deletions(-) diff --git a/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt b/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt index 5a8a0934f2..d6267ff80c 100644 --- a/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt +++ b/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt @@ -61,16 +61,55 @@ internal fun AccountRow( modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(CodeTheme.dimens.grid.x1), ) { - Text( - // iOS adds 5pt under the name line on top of the column's spacing. + // iOS sets the name and the balance on one 16pt line (appTextMedium), bottom-aligned + // 10pt apart, with 5pt under the line on top of the column's spacing. + Row( modifier = Modifier.padding(bottom = CodeTheme.dimens.grid.x1), - text = account.name, - // iOS sets the name and the balance on one 16pt line (appTextMedium). - style = CodeTheme.typography.textMedium, - color = CodeTheme.colors.textMain, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) + verticalAlignment = Alignment.Bottom, + horizontalArrangement = Arrangement.spacedBy(CodeTheme.dimens.grid.x2), + ) { + Text( + modifier = Modifier.weight(1f), + text = account.name, + style = CodeTheme.typography.textMedium, + color = CodeTheme.colors.textMain, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + + // Order matters: a resolved balance wins, then the backend's own "no such + // account", then our inability to ask, and only then is the fetch still in + // flight. An account whose balance we could not fetch must not be reported as not + // found — the two say very different things to someone checking their own wallet. + when { + account.balance != null -> Text( + text = account.balance.formatted(), + style = CodeTheme.typography.textMedium, + color = CodeTheme.colors.textMain, + ) + + account.notFound -> Text( + text = stringResource(R.string.subtitle_accountNotFound), + style = CodeTheme.typography.caption, + color = CodeTheme.colors.textSecondary, + ) + + account.balanceUnavailable -> Text( + text = stringResource(R.string.subtitle_balanceUnavailable), + style = CodeTheme.typography.caption, + color = CodeTheme.colors.textSecondary, + ) + + // The skeleton the discovery list uses, sized to a short balance on the + // textMedium line. It carries no semantics: TalkBack announces the balance + // when it lands. + else -> Box( + Modifier + .size(width = 60.dp, height = 14.dp) + .shimmer() + ) + } + } Text( text = stringResource(R.string.subtitle_accountCreated, relativeCreationDate), style = CodeTheme.typography.caption, @@ -82,37 +121,6 @@ internal fun AccountRow( color = CodeTheme.colors.textSecondary, ) } - - // Order matters: a resolved balance wins, then the backend's own "no such account", then - // our inability to ask, and only then is the fetch still in flight. An account whose balance we could not fetch must not be reported as - // not found — the two say very different things to someone checking their own wallet. - when { - account.balance != null -> Text( - text = account.balance.formatted(), - style = CodeTheme.typography.textMedium, - color = CodeTheme.colors.textMain, - ) - - account.notFound -> Text( - text = stringResource(R.string.subtitle_accountNotFound), - style = CodeTheme.typography.caption, - color = CodeTheme.colors.textSecondary, - ) - - account.balanceUnavailable -> Text( - text = stringResource(R.string.subtitle_balanceUnavailable), - style = CodeTheme.typography.caption, - color = CodeTheme.colors.textSecondary, - ) - - // The skeleton the discovery list uses, sized to a short balance on the textMedium - // line. It carries no semantics: TalkBack announces the balance when it lands. - else -> Box( - Modifier - .size(width = 60.dp, height = 14.dp) - .shimmer() - ) - } } } From 7d51b35b93da6d705348299c793499d9c361f61e Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Wed, 30 Sep 2026 14:07:07 -0400 Subject: [PATCH 05/10] fix(login): draw the account divider after each row Draw it below every row but the last, keyed on lastIndex, rather than above every row but the first. --- .../internal/accounts/AccountSelectionContent.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountSelectionContent.kt b/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountSelectionContent.kt index 64d7ed0018..9e1f38ad7f 100644 --- a/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountSelectionContent.kt +++ b/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountSelectionContent.kt @@ -67,14 +67,6 @@ internal fun AccountSelectionContent( contentPadding = PaddingValues(horizontal = CodeTheme.dimens.inset), ) { itemsIndexed(state.accounts, key = { _, account -> account.id }) { index, account -> - // iOS separates the rows with a hairline in rowSeparator, the same white - // at 10% as divider. - if (index > 0) { - HorizontalDivider( - color = CodeTheme.colors.divider, - thickness = 0.5.dp, - ) - } AccountRow( account = account, isCurrent = account.entropy == state.currentEntropy, @@ -84,6 +76,14 @@ internal fun AccountSelectionContent( onClick = { onSelect(account.entropy) }, onLongClick = { onRemove(account.entropy) }, ) + // iOS separates the rows with a hairline in rowSeparator, the same white + // at 10% as divider. + if (index < state.accounts.lastIndex) { + HorizontalDivider( + color = CodeTheme.colors.divider, + thickness = 0.5.dp, + ) + } } } } From 92ace225df715c2cdab5c8691e530ddad889f41e Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Wed, 30 Sep 2026 14:10:40 -0400 Subject: [PATCH 06/10] fix(login): show Not Found as a badge after the account name iOS marks an unknown account with Badge(decoration: .circle(.textError)): a 6pt dot in textError and the label in appTextSmall, textSecondary, set directly after the name. Draw the same with errorText (0xFFFF8383, the iOS textError value) and textSmall, and let the name wrap its width so the badge follows it. --- .../app/login/internal/accounts/AccountRow.kt | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt b/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt index d6267ff80c..21b8d743aa 100644 --- a/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt +++ b/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt @@ -2,6 +2,7 @@ package com.flipcash.app.login.internal.accounts import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image +import androidx.compose.foundation.background import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -10,6 +11,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -69,7 +71,9 @@ internal fun AccountRow( horizontalArrangement = Arrangement.spacedBy(CodeTheme.dimens.grid.x2), ) { Text( - modifier = Modifier.weight(1f), + // Without a balance to push to the end, the Not Found badge follows the name + // directly, as on iOS. + modifier = Modifier.weight(1f, fill = !account.notFound), text = account.name, style = CodeTheme.typography.textMedium, color = CodeTheme.colors.textMain, @@ -88,11 +92,7 @@ internal fun AccountRow( color = CodeTheme.colors.textMain, ) - account.notFound -> Text( - text = stringResource(R.string.subtitle_accountNotFound), - style = CodeTheme.typography.caption, - color = CodeTheme.colors.textSecondary, - ) + account.notFound -> NotFoundBadge() account.balanceUnavailable -> Text( text = stringResource(R.string.subtitle_balanceUnavailable), @@ -124,3 +124,23 @@ internal fun AccountRow( } } +/** iOS's `Badge(decoration: .circle(.textError))`: a 6pt dot in textError beside appTextSmall. */ +@Composable +private fun NotFoundBadge(modifier: Modifier = Modifier) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(CodeTheme.dimens.grid.x2), + ) { + Box( + Modifier + .size(6.dp) + .background(CodeTheme.colors.errorText, CircleShape) + ) + Text( + text = stringResource(R.string.subtitle_accountNotFound), + style = CodeTheme.typography.textSmall, + color = CodeTheme.colors.textSecondary, + ) + } +} From 15198b8d73abf2adabffcaeb74952c09adcf2cd6 Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Wed, 30 Sep 2026 14:18:14 -0400 Subject: [PATCH 07/10] fix(login): show the full owner key on the account switcher The view model cut the owner address to its first and last four characters. iOS shows the full base58 key on one line and truncates the middle only when it does not fit. Pass the full key through, set it on one line with TextOverflow.MiddleEllipsis, and drop truncateAddress and its tests. --- .../app/login/internal/accounts/AccountRow.kt | 3 +++ .../internal/accounts/AccountSelectionViewModel.kt | 6 +----- .../accounts/AccountSelectionViewModelTest.kt | 11 ----------- 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt b/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt index 21b8d743aa..c8b7e0ad98 100644 --- a/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt +++ b/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt @@ -115,10 +115,13 @@ internal fun AccountRow( style = CodeTheme.typography.caption, color = CodeTheme.colors.textSecondary, ) + // The full key on one line, as iOS shows it; the middle gives way only if it can't fit. Text( text = account.ownerAddress, style = CodeTheme.typography.caption, color = CodeTheme.colors.textSecondary, + maxLines = 1, + overflow = TextOverflow.MiddleEllipsis, ) } } diff --git a/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountSelectionViewModel.kt b/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountSelectionViewModel.kt index da0153d4dd..eeda5020af 100644 --- a/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountSelectionViewModel.kt +++ b/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountSelectionViewModel.kt @@ -239,7 +239,7 @@ class AccountSelectionViewModel @Inject constructor( mnemonicName = mnemonic?.let { displayName(it) }.orEmpty(), username = profile?.username, displayName = profile?.displayName, - ownerAddress = owner?.let { truncateAddress(it) }.orEmpty(), + ownerAddress = owner.orEmpty(), creationDate = creationDate, // A record we cannot derive from is one we can never fetch a balance for. balanceUnavailable = cluster == null, @@ -341,10 +341,6 @@ class AccountSelectionViewModel @Inject constructor( word.lowercase().replaceFirstChar { it.titlecase() } } - fun truncateAddress(address: String): String = - if (address.length <= 8) address - else "${address.take(4)}...${address.takeLast(4)}" - private val updateStateForEvent: (Event) -> ((State) -> State) = { event -> when (event) { is Event.OnAccountsLoaded -> { state -> diff --git a/apps/flipcash/features/login/src/test/kotlin/com/flipcash/app/login/internal/accounts/AccountSelectionViewModelTest.kt b/apps/flipcash/features/login/src/test/kotlin/com/flipcash/app/login/internal/accounts/AccountSelectionViewModelTest.kt index 6e036de4c9..4f2af226cb 100644 --- a/apps/flipcash/features/login/src/test/kotlin/com/flipcash/app/login/internal/accounts/AccountSelectionViewModelTest.kt +++ b/apps/flipcash/features/login/src/test/kotlin/com/flipcash/app/login/internal/accounts/AccountSelectionViewModelTest.kt @@ -24,15 +24,4 @@ class AccountSelectionViewModelTest { val name = AccountSelectionViewModel.displayName(phrase("Apple", "Elder")) assertEquals("Apple ... Elder", name) } - - @Test - fun `truncates an owner address in the middle`() { - val truncated = AccountSelectionViewModel.truncateAddress("ABCDEFGHIJKLMNOPQRSTUVWXYZ") - assertEquals("ABCD...WXYZ", truncated) - } - - @Test - fun `leaves a short address alone`() { - assertEquals("ABCD", AccountSelectionViewModel.truncateAddress("ABCD")) - } } From 78bc66d297a7ef4538b00c810100684bdd000f33 Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Wed, 30 Sep 2026 14:18:20 -0400 Subject: [PATCH 08/10] fix(login): show the currency flag beside the account balance iOS draws the balance with AmountText(flagSize: .small), a 15pt flag beside the amount. Draw it with PriceWithFlag at grid.x3 (15dp). That component hides the amount when there is no flag, so fall back to the plain amount in that case. --- .../app/login/internal/accounts/AccountRow.kt | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt b/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt index c8b7e0ad98..b017279d17 100644 --- a/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt +++ b/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt @@ -22,7 +22,10 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.flipcash.app.core.ui.shimmer import com.flipcash.features.login.R +import com.getcode.opencode.compose.LocalExchange +import com.getcode.opencode.model.financial.Fiat import com.getcode.theme.CodeTheme +import com.getcode.ui.components.PriceWithFlag @OptIn(ExperimentalFoundationApi::class) @Composable @@ -86,11 +89,7 @@ internal fun AccountRow( // flight. An account whose balance we could not fetch must not be reported as not // found — the two say very different things to someone checking their own wallet. when { - account.balance != null -> Text( - text = account.balance.formatted(), - style = CodeTheme.typography.textMedium, - color = CodeTheme.colors.textMain, - ) + account.balance != null -> BalanceWithFlag(account.balance) account.notFound -> NotFoundBadge() @@ -147,3 +146,30 @@ private fun NotFoundBadge(modifier: Modifier = Modifier) { ) } } + +/** iOS's `AmountText(flagSize: .small)`: a 15pt flag beside the balance. */ +@Composable +private fun BalanceWithFlag(balance: Fiat, modifier: Modifier = Modifier) { + val currencyCode = balance.currencyCode.name + val flag = LocalExchange.current.getFlagByCurrency(currencyCode) + val amount: @Composable (String) -> Unit = { formatted -> + Text( + text = formatted, + style = CodeTheme.typography.textMedium, + color = CodeTheme.colors.textMain, + ) + } + // PriceWithFlag draws nothing, amount included, when there is no flag to show. + if (flag == null) { + Box(modifier) { amount(balance.formatted()) } + } else { + PriceWithFlag( + modifier = modifier, + currencyCode = currencyCode, + amount = balance.formatted(), + flag = flag, + iconSize = CodeTheme.dimens.grid.x3, + text = amount, + ) + } +} From 8214d40b08aef50260e6cc01d6832b28397da8f4 Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Wed, 30 Sep 2026 14:18:20 -0400 Subject: [PATCH 09/10] fix(login): title the account picker "Select Account" iOS titles the screen "Select Account"; Android read "Select an Account". Update title_selectAccount, which both the in-app switcher and the onboarding picker use, and the two Maestro flows that assert on it. --- maestro/blockstore_account_list.yaml | 2 +- maestro/blockstore_switcher.yaml | 2 +- ui/resources/src/main/res/values/strings-localized.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/maestro/blockstore_account_list.yaml b/maestro/blockstore_account_list.yaml index a0bb3e07b0..2baa4c3537 100644 --- a/maestro/blockstore_account_list.yaml +++ b/maestro/blockstore_account_list.yaml @@ -22,7 +22,7 @@ tags: visible: id: account_selection_screen timeout: 15000 -- assertVisible: "Select an Account" +- assertVisible: "Select Account" # The row itself. The display name comes from the seed, which the flow does not know, so the # creation subtitle is what identifies a row generically. - assertVisible: "Created .*" diff --git a/maestro/blockstore_switcher.yaml b/maestro/blockstore_switcher.yaml index 934ec80165..7866538eba 100644 --- a/maestro/blockstore_switcher.yaml +++ b/maestro/blockstore_switcher.yaml @@ -17,7 +17,7 @@ tags: visible: id: account_selection_screen timeout: 15000 -- assertVisible: "Select an Account" +- assertVisible: "Select Account" - assertVisible: "Created .*" # From inside the app there is no onboarding flow to fall through to, so the footer is hidden — # which makes the back control the only way out. diff --git a/ui/resources/src/main/res/values/strings-localized.xml b/ui/resources/src/main/res/values/strings-localized.xml index 3b58ba6e5d..9e122ff4a4 100644 --- a/ui/resources/src/main/res/values/strings-localized.xml +++ b/ui/resources/src/main/res/values/strings-localized.xml @@ -375,7 +375,7 @@ Require Touch ID Results Returned - Select an Account + Select Account Select a Country Select a Currency Sent From 40aa01d0b58df2094d2fc3244ffd6a3a566aa1c7 Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Wed, 30 Sep 2026 14:20:33 -0400 Subject: [PATCH 10/10] refactor(login): draw the account balance with FlagWithFiat FlagWithFiat is the core-ui component the activity and transaction screens use for a Fiat with its flag. It looks the flag up itself and keeps the amount when there is none, so the PriceWithFlag fallback goes. --- .../app/login/internal/accounts/AccountRow.kt | 37 +++---------------- 1 file changed, 6 insertions(+), 31 deletions(-) diff --git a/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt b/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt index b017279d17..0081716f65 100644 --- a/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt +++ b/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/accounts/AccountRow.kt @@ -20,12 +20,10 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import com.flipcash.app.core.ui.FlagWithFiat import com.flipcash.app.core.ui.shimmer import com.flipcash.features.login.R -import com.getcode.opencode.compose.LocalExchange -import com.getcode.opencode.model.financial.Fiat import com.getcode.theme.CodeTheme -import com.getcode.ui.components.PriceWithFlag @OptIn(ExperimentalFoundationApi::class) @Composable @@ -89,7 +87,11 @@ internal fun AccountRow( // flight. An account whose balance we could not fetch must not be reported as not // found — the two say very different things to someone checking their own wallet. when { - account.balance != null -> BalanceWithFlag(account.balance) + // iOS's AmountText(flagSize: .small): a 15pt flag beside the balance. + account.balance != null -> FlagWithFiat( + fiat = account.balance, + iconSize = CodeTheme.dimens.grid.x3, + ) account.notFound -> NotFoundBadge() @@ -146,30 +148,3 @@ private fun NotFoundBadge(modifier: Modifier = Modifier) { ) } } - -/** iOS's `AmountText(flagSize: .small)`: a 15pt flag beside the balance. */ -@Composable -private fun BalanceWithFlag(balance: Fiat, modifier: Modifier = Modifier) { - val currencyCode = balance.currencyCode.name - val flag = LocalExchange.current.getFlagByCurrency(currencyCode) - val amount: @Composable (String) -> Unit = { formatted -> - Text( - text = formatted, - style = CodeTheme.typography.textMedium, - color = CodeTheme.colors.textMain, - ) - } - // PriceWithFlag draws nothing, amount included, when there is no flag to show. - if (flag == null) { - Box(modifier) { amount(balance.formatted()) } - } else { - PriceWithFlag( - modifier = modifier, - currencyCode = currencyCode, - amount = balance.formatted(), - flag = flag, - iconSize = CodeTheme.dimens.grid.x3, - text = amount, - ) - } -}