Commit Graph
16 Commits
Author SHA1 Message Date
tyiuandClaude Sonnet 5 94508c291c Hide the "Not priced by Manual" label for Manual
Manual's default-currency field being unpriced just means no rate has
been typed in yet, not an actual pricing error, so the label (and the
red error styling that came with it) no longer applies when it's the
active source.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FXcrACM5sok3M1KQJ8kByy
2026-09-09 23:45:30 +03:00
tyiuandClaude Sonnet 5 0927a04fe8 Hide the Manual status line, clear fiat amounts when its rate is gone
"via Manual" had nothing useful left to say once the "updated" part
was dropped, so statusLine() now returns empty for Manual and both
UIs skip rendering it entirely instead of showing a blank line/footer.

onManualRateChanged also now clears manualSource.rate whenever the
field doesn't parse (emptied, or mid-edit on an incomplete number)
rather than only ever updating it on a successful parse. refresh()'s
early-return for a null Manual rate now also clears the cached rates
and pricedCurrencyCodes, so a cleared/incomplete manual rate stops
fiat amounts from continuing to show a stale computed value.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FXcrACM5sok3M1KQJ8kByy
2026-09-09 23:42:23 +03:00
tyiuandClaude Sonnet 5 583b9bd14e Hide refresh for Manual, seed its rate from another source
Refreshing has no meaning for a manually-typed rate, so the button
(and its loading spinner) is now hidden while Manual is selected.

Switching to Manual for the first time now seeds its rate field from
whichever source was active before, falling back to any other
source's last cached rate for the default currency. When neither is
available, the rate and amount fields are left empty instead of
showing stale values.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FXcrACM5sok3M1KQJ8kByy
2026-09-09 09:03:18 +03:00
tyiuandClaude Sonnet 5 f3f018741c Hide currency flags shared by more than 3 countries
XCD, XOF, and XAF each have 6-8 issuing countries, which is too many
flags to cram side by side next to a currency code.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FXcrACM5sok3M1KQJ8kByy
2026-09-09 08:57:19 +03:00
tyiuandClaude Sonnet 5 dda836274c Simplify Manual source UI to just the default currency
The manual rate source only ever prices the default currency, so the
currency selector, additional currency rows, and the "updated" part
of the status line no longer make sense for it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FXcrACM5sok3M1KQJ8kByy
2026-09-09 08:56:24 +03:00
tyiuandClaude Sonnet 5 f21bdd48f8 Show a flag emoji next to each currency code
Derives a country flag from the ISO 4217 code's country prefix, with
an explicit map of country codes for currencies shared by multiple
countries (XAF, XCD, XCG, XOF, XPF) showing every issuing country's
flag, and the EU flag as a special case for EUR.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QpjMKWGoiT5aJBzhxvXkwp
2026-09-09 08:47:35 +03:00
tyiuandClaude Sonnet 5 1a16f96739 Simplify currency row menu labels, add move to top/bottom
Move up/down/Remove no longer repeat the currency code in the menu item
text — the row's own field already shows it right next to the button.
Also adds Move to top / Move to bottom, enabled under the same conditions
as the existing up/down actions.

Switches from material-icons-core to material-icons-extended: the core set
is a bare ~50-icon subset with nothing suited to "move to top/bottom" (no
double-chevron or vertical-align icons), so KeyboardDoubleArrowUp/Down
needed the fuller set.

Compose-only: the native SwiftUI screen has no three-dot menu, it uses
native list reordering instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QpjMKWGoiT5aJBzhxvXkwp
2026-09-09 08:09:21 +03:00
tyiuandClaude Sonnet 5 5587428ca7 Add locale-aware digit grouping to displayed amounts
Adds groupDigits(), a display-only formatter that inserts the current
locale's digit-grouping separators into an amount's integer part (e.g.
"1,000,000.5" in en-US vs "1.000.000,5" in de-DE, where the roles of ","
and "." are swapped, or hi-IN's irregular "12,34,567"). Delegates to each
platform's native locale APIs (java.text.NumberFormat/DecimalFormatSymbols,
NSNumberFormatter/NSLocale, Intl.NumberFormat with BigInt for arbitrary
precision) rather than reimplementing grouping rules, and never touches the
canonical '.'-decimal strings the rest of the app parses and stores.

Applied via a Compose VisualTransformation (BTC/Sats/fiat/manual-rate
fields, plus the read-only rate display) and, on the native SwiftUI screen,
by re-deriving the field's displayed text on every change. Also made input
parsing locale-aware: sanitizeDecimalInput now recognizes the locale's own
decimal separator (e.g. what a locale-aware decimal keypad sends), not just
"." — with a stricter rule on the SwiftUI side specifically, since its
single text buffer re-feeds the grouped display back through sanitize, so
a "." can be grouping noise there rather than a decimal point.

The exceeds-max-supply warning's "21,000,000 BTC" is no longer baked into
the localized string with hardcoded grouping; it's substituted in at
runtime via groupDigits so the link-detection text always matches exactly,
in every locale.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QpjMKWGoiT5aJBzhxvXkwp
2026-09-09 07:37:46 +03:00
tyiuandClaude Sonnet 5 323ca43472 Exclude withdrawn currencies from the picker, keep precious metals
Currency.getAvailableCurrencies() (JVM/Android) and NSLocale.ISOCurrencyCodes
(iOS/macOS) return every ISO 4217 code the platform has ever known about —
withdrawn currencies like the Deutsche Mark and French Franc, and test/
placeholder codes (XTS, XXX), included. Measured on the JVM: 232 total
codes, only 156 still assigned to a country.

Filters the list down to currencies actually assigned to an ISO country
today, which drops the withdrawn ones without needing a maintained
exclusion list. Precious metals (XAU, XAG, XPD, XPT) are added back in
since they're still actively traded despite not being tied to any country.
Web is left unfiltered — there's no reliable country-to-currency API there.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QpjMKWGoiT5aJBzhxvXkwp
2026-09-08 17:20:09 +03:00
tyiuandClaude Sonnet 5 b0f3744851 Always show all currencies, flagging ones the active source doesn't price
Previously a currency the active price source had no rate for was hidden
entirely, so switching sources (e.g. to CoinGecko, which quotes far fewer
fiat currencies than Coinbase) could silently drop it from the picker and
even from the user's own selection.

Every system currency is now always offered. ConverterUiState tracks which
codes the latest fetch actually priced, and both UIs (Compose and the
native SwiftUI screen, since iOS/macOS don't render Compose UI at all) show
a "Not priced by <source>" indicator for the rest, with the amount field
emptied and disabled rather than showing a stale or meaningless value.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QpjMKWGoiT5aJBzhxvXkwp
2026-09-08 17:19:48 +03:00
tyiuandClaude Sonnet 5 f35fb05e7c Make the max-supply warning's BTC amount a link on every platform
Tapping "21,000,000 BTC" in the exceeds-max-supply warning now sets the BTC
field to that value, on both Compose (Android/Desktop/Web) and the native
SwiftUI screen (iOS/macOS) — Compose UI isn't rendered on iOS/macOS at all,
so that side needed its own AttributedString-based link.

The link is styled to match the surrounding warning text: Compose's
LinkAnnotation otherwise falls back to the theme's accent color unless every
interaction state's style is set explicitly, and SwiftUI renders `.link`
runs in the accent color regardless of the Text's own foregroundColor
modifier unless it's set directly on the link's AttributedString range.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QpjMKWGoiT5aJBzhxvXkwp
2026-09-08 10:30:10 +03:00
tyiuandClaude Sonnet 5 b77455bfc8 Move currency row overflow menu to the right of the amount field
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QpjMKWGoiT5aJBzhxvXkwp
2026-09-08 09:46:38 +03:00
tyiuandClaude Sonnet 5 14298d450e Persist exchange rates, selected currencies, and price source via SQLDelight
Adds a SQLDelight-backed AppDatabase (Android, Desktop, iOS/macOS) so the
converter survives restarts: the last-known rates per price source, the
user's selected fiat currencies (with order preserved), and the last-used
price source all reload before the first network fetch completes. Web isn't
a shipped platform and SQLDelight's driver there needs a worker plus a wasm
sqlite binary, so it gets a lighter localStorage-backed implementation of
the same store interfaces instead.

Android needs an app Context for its driver, wired via a new
SatsPriceApplication. iOS/macOS need libsqlite3 linked explicitly (Swift's
autolinking only covers Shared.framework itself, not its C library
dependencies), plus two extension-loading symbols marked optional since
macOS's system sqlite build omits them.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QpjMKWGoiT5aJBzhxvXkwp
2026-09-08 09:34:21 +03:00
tyiu 14ec044c44 Fix currencies screen padding and section header styling 2026-09-07 22:39:35 +03:00
tyiu 2925c845bd Add manual currency sorting and improve currency categorization 2026-09-07 22:15:29 +03:00
tyiu abf7cf2e83 Replace Skip implementation with Kotlin Multiplatform implementation 2026-09-02 22:11:32 +03:00