Skip to content

feat: add protections vault (hub, create flow, detail, withdraw) - #53

Open
nicoletting wants to merge 53 commits into
mainfrom
feat/protections-vault
Open

feat: add protections vault (hub, create flow, detail, withdraw)#53
nicoletting wants to merge 53 commits into
mainfrom
feat/protections-vault

Conversation

@nicoletting

Copy link
Copy Markdown
Contributor

What changed

Protections Vault UI, body-only (no header/nav/routing — those stay in kastle-mobile): hub, the 4-step create flow, vault detail, withdraw confirm, and clawback. Built component-first against Figma (BdTDUVIHEeOjdlHSPij0xi, page "└─ Vault").

Screens

  • ProtectionsHubScreen — entry banner + live vault status
  • VaultListScreen — 2-column vault grid, balance summary, scanning/empty states
  • VaultIntroScreen, CreateVaultAmountStep, CreateVaultWindowStep, CreateVaultRecoveryStep, CreateVaultConfirmStep — create flow
  • VaultDetailScreen — locked/withdrawing detail, backup card, clawback
  • WithdrawConfirmScreen — withdraw confirm

New shared components

  • BottomActionBar — owns action-bar spacing/message slot; every screen's CTA row now goes through it instead of hand-rolled bars
  • DetailTable — the vault/quote summary table (reused by 3 screens)
  • NotificationBanner — generalized from ProtectionsBanner (now a thin preset)
  • Keypad, VaultCard, VaultAddressCard, VaultBalanceRows, CountdownRing, ProtectionTypeCard
  • Extended: Alert, InfoSheet (rich-text runs), EstFeeSheet (free-form labels + description rows for the vault fee breakdown), StatusPill, Textarea

Why

New Protections Vault feature — locks KAS behind a time-delay with an external recovery address. UI built directly in kastle-ui; data/signing wired in by dev after merge.

Screenshots / recordings

PR title triggers the Storybook preview build — reviewers get a live link. Every screen/component was screenshot-verified against its Figma node during the build (see commit history); happy to drop specific before/after shots here if useful.

Design tokens used

All styling via src/config/theme.tscolors, background, border, typography, spacing, borderRadius, borderWidth, textStyles, fontFamilies/fontSize/fontWeight, warning/info/success semantic colors. No hardcoded hex/spacing in new work.

Known follow-ups (not blocking)

  • StatusPill and the EstFeeSheet/InfoSheet sheet chrome have a few pre-existing literal numbers (9999, 8, 4, 12, 16, 32) that exactly match existing spacing/radius tokens but weren't swapped in — flagged, zero visual risk, happy to clean up in a follow-up.

Testing

  • Checked in Storybook web preview (npm run preview)
  • Checked on Android phone via Expo Go (npm run storybook:native)
  • Checked component states (locked/withdrawing, empty/scanning, error, address actions)
  • Responsive-checked at 360/375/393/430

nicoletting and others added 30 commits July 15, 2026 17:42
Optional indicator prop (icon | dot, default icon) so vault statuses can render a dot instead of a lucide icon. Backward-compatible — existing StatusPill usages are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Textarea: add rightIcon, editable, minHeight and scrollEnabled for a read-only display + copy mode (used by the vault address). DetailKVRow: add onPressInfo for an info tooltip button on the label. Both backward-compatible.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Numeric keypad for amount entry, rebuilt in StyleSheet + theme.ts from the kastle-mobile reference (components/ui/keypad.tsx). Pure and controlled (value/onChange); del long-press clears; fraction-digit cap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Vault view (VaultCard, CountdownRing, VaultAddressCard, VaultListScreen, VaultDetailScreen), hub (ProtectionTypeCard, ProtectionsHubScreen) and create step 1 (CreateVaultAmountStep). Body-only, pure, props-driven.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Step 2 window (5 fixed presets, radio select, reuses Alert), step 3 external recovery address (reuses Textarea + Alert), step 4 confirm (VaultAddressCard + DetailKVRow summary + EstFeeSheet + InfoSheet + SwipeToConfirm). Body-only, pure, props-driven.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Withdraw confirmation bottom sheet (ActionSheet + VaultAddressCard + SwipeToConfirm), reused for Withdraw now and for starting a withdrawal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Keypad: a fixed-height message row (an info link such as How a Vault works?, replaced by a red error) so toggling it never shifts the keys. Textarea: reserveErrorSpace keeps the error line's height even when empty (no layout jump).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
VaultAddressCard gains a labeled surface-card variant (header label + copy + chain badge) for the confirm screen; the bare Textarea reuse stays for the vault detail. Amount step moves the info/error into the keypad; confirm uses the labeled address card + bg50 summary; recovery reserves error space.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Figma's withdraw confirm (node 12802:619523) is a full screen — illustration + the external recovery address card + an Arrives in / Amount / Est. Fee summary + swipe — not a bottom sheet. Replaces WithdrawSheet, which was wrong.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replaces invented copy with the strings pulled from the Figma nodes: hub cards, window presets + alert, recovery hints + alerts, confirm and detail rows, tooltips, and How a Vault works. Note 'Refundable deposit' is really 'Recovery marker'. Layout: soon cards are not dimmed and keep the active colours, no chevron, correct Soon badge colours, address info icon beside the label, keypad info/error moved above the keypad and turned blue, fiat always shown, window rows no longer resize on select (padding compensates the border) and use a check rather than a filled dot, vault cards are a fixed height, the list scans with skeletons / hides the balance behind **** / orders newest first, and the detail hides the backup card while withdrawing. Alert title is now optional since Figma's alerts are description-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
VaultIntroScreen (node 12711:223618) and ProtectionsBanner (node 13385:318218) — the entry points into the vault flow. Copy from Figma; the banner's exact styling still needs a parity pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
VaultBalanceRows (Figma node 13381:90879) — coins / lock rows whose borders collapse, Locked taps through to the vaults list. Also fills in the real How a Vault works? sheet copy (node 12824:656344) on the amount step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Toast (shared): loading/success/error pill per Figma — spinner,
check-circle-2 (success), x-circle (danger), optional text action.

VaultBalanceRows: add `state` — default / scanning ("Scanning for
vaults...", textSecondary, no balance) / loading (skeleton values).

VaultDetailScreen — corrections after re-reading Figma
12802:617586 + the clawback withdraw variants 13393:44068:
- countdown label "Funds leave when this ends" -> "Funds leave in"
- Details title 14 textPrimary -> 16 SemiBold textSecondary
- Vault Status renders a StatusPill (dot), not plain text
- Vault amount gains its fiat sub-line ($200.232 USD)
- action button: locked = outline/muted "Withdraw",
  withdrawing = orange (warning.w500) "Withdraw now", 18 Medium
- danger note ("Wasn't you? ...") sits in the action bar above the button
- backup "Done" 14 semibold -> 12 Medium
- stories: clawback countdown variants DD:HH:MM / HH:MM:SS / 90 days

DetailKVRow: three backward-compatible optional props — labelColor,
valueNode (for the pill), subValue (fiat line). Existing activity rows
render byte-identical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
All 8 tooltip sheets on the Vault page pulled from Figma; three were
missing from the build:
- Vault amount ("The KAS going into your vault...")
- Funds leave in — the countdown label carries a 12px ⓘ (Figma
  Frame 1410127708, gap 4), wired to CountdownRing.onPressInfo
- Arrives in — WithdrawConfirmScreen's first row

InfoSheet: optional `actions` row (Figma shows/hides the buttons on the
same Actionsheet, so this is a variant, not a new component). Buttons
40 high, r9999, 16 Medium, gap 12 — outline (#7b9aaa) / warning
(#fb954b), matching the detail screen's action colours.

VaultDetailScreen: "Withdraw now" raises the clawback confirm sheet
("Withdraw now?" / Back / Withdraw now).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Clawback confirm states are Toasts over the screen behind them
(Figma 12802:633805), not new screens:
- loading  "Sending to your recovery address…"      (vault detail)
- fail     "Couldn't withdraw" + Close              (vault detail)
- success  "Withdrawal complete" + View TX          (vaults list)

Story decorator padded to Figma's toast wrapper offset (120 from top).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New shared BottomActionBar (Figma "Button Container" 12698:208160) —
pad [12,20,16,20], gap 12, buttons 48 high, message slot above them.
Every screen's CTA now comes from one place instead of per-screen
hand-rolled spacing. Variants: primary / outline / warning.

Assets: vault.png + vault-intro.png exported from Figma; kaspa-logo.png
+ us-flag.png copied from kastle-mobile.

CreateVaultAmountStep
- error + "How a Vault works?" move INTO the action bar above Continue
  (Figma 12831:678052) — they were floating above the keypad
- info/error rows gain the ⓘ icon, coloured to match their text
- amount gains the Kaspa logo + "KAS" 20 SemiBold; fiat row gains the
  USD flag chip + KAS/USD swap button (Figma 12757:300288)

Keypad — measured against Figma 12831:678052
- key height 48 -> 40, row gap 16 -> 4, del icon 20 -> 18
- Max button 32 -> 36 high, label semibold -> 14 Medium
- grid inset [0,12,0,10] so keys land on their 118-wide columns

VaultDetailScreen
- real vault artwork; hero art only while locked (withdrawing shows the
  ring alone, per Figma)
- Details title gains its paddingVertical 12
- backup title: drop the alert-triangle (hidden in Figma) and give the
  header its 32 height
- Recovery address is plain white, not a blue link
- action bar -> BottomActionBar

DetailKVRow: the whole row is the tooltip tap target, and the ⓘ takes
its colour from the adjacent label.

VaultIntroScreen: real artwork + BottomActionBar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CreateVaultWindowStep: the checkbox was a p500 circle; Figma
(12757:304730) is a 16px rounded square (r6) with a 2px b400 outline,
filled with the brighter p600 (colors.link) + a white 12px check when
selected. That p500 fill is why the enabled state read as dim.

ProtectionsHubScreen: the active ProtectionTypeCard gets its
chevron-right back (20px, secondary.s500). The title icon stays off —
only the trailing chevron is in Figma.

CreateVaultConfirmStep
- the recovery card's ⓘ never opened anything: VaultAddressCard was
  rendered without onPressInfo. Wired via a new recoveryTooltip prop.
- Est. Fee opened its sheet through onPressValue, which is what turned
  the value blue and added a link icon; it now opens from the ⓘ like
  every other explainer.

VaultAddressCard: the whole label row is the tap target, and the ⓘ only
renders when it does something.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CreateVaultAmountStep: the info link can't share the action bar with the
CTA, so "How a Vault works?" moves back above the keypad (ⓘ + link blue,
pad [16,0,16,0]). The action bar's message slot now carries the
validation error only — that part was right.

BottomActionBar: a one-line message centres under the button; once it
wraps it left-aligns with the icon pinned to the first line, measured
off the rendered text height rather than a character count.

CreateVaultConfirmStep: summary labels were falling back to the row's
muted default — they take typography.t800 per Nicole.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
VaultDetailScreen: Done now dismisses the backup card (the state the
flow was missing) and gets a real 32-high tap target instead of bare
12px text. New backupDone prop + LockedBackedUp story.

CreateVaultWindowStep: checkbox is round per Nicole — the Figma node
draws a 6px-radius square, noted in the style.

ProtectionsBanner: takes an illustration that bleeds off the right edge,
with the copy inset clear of it. Awaiting Nicole's exported artwork.

VaultCard: stories pass the vault artwork — the slot existed but nothing
ever filled it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Alert: the description carried opacity 0.6, which washed the info
banner's p800 text out — Figma (12757:330135) renders it at full
strength. The severity token was right all along; the opacity was the
bug. Affects the two create-vault steps and ImportPassphraseScreen.

DetailKVRow: `emphasis` renders a total row — bold label + value on a
bg100 fill that bleeds to the card's edges (Figma 12757:476249, where
the total's Table input is filled and borderless while every other row
is transparent with a divider).

CreateVaultConfirmStep: "Total from wallet" uses it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The labeled VaultAddressCard filled with white 5%, which composites to a
shade off the bg50 summary card directly below it on the confirm step.
Figma has the two surfaces genuinely different; Nicole's call is bg50
for both. Noted in the style so the divergence from the node is
deliberate.

Affects the confirm step and the withdraw confirm screen (the vault
detail backup card uses the bare variant and is untouched).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
kastle-ui had no signing illustration — the explore sign sheets use
dApp icons, not artwork — so sign.png comes from kastle-mobile.
WithdrawConfirmScreen and CreateVaultConfirmStep both default to it
(the create step had no artwork at all).

VaultCard defaults to the vault artwork rather than the lucide glyph, so
the list grid gets it without every caller remembering to pass it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Verified against Figma 13385:286750 by screenshotting both sides rather
than reading the node dump alone.

- the stack is one r16 card; the shared border reads as a divider
- value + chevron sit flush right (the value was floating mid-row
  because the label column was a fixed 114 and the value hugged it)
- scanning: lucide LoaderCircle, not the 8-spoke Loader
- scanning label is textDimmed (#4B7D92), not textSecondary — caught
  while checking the loader, not reported
- loading: the chevron dims to 40%

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The vault address rendered in t900 white; Figma binds it to
Typography/typography600 (#9EB7C4). Resolved from the node's variable
binding, not by hex-matching — t800/t900/white are indistinguishable by
value.

Scoped to editable={false}, which only VaultAddressCard's bare variant
uses, so typed input keeps t900.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Both caught by the parity pass, neither reported by Nicole.

- Figma's Table input is 50 high with a hairline underneath
  (Border/border200, bottom edge only). DetailKVRow gains `divider`;
  the vault table draws it on every row but the last.
- The Vault amount row shows a 20px Kaspa logo and hides the ⓘ
  (logo frame > Token > Kaspa). Nicole confirmed: keep the logo, drop
  the tooltip. DetailKVRow gains `labelIcon`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Figma's Amount Info stacks [amount+KAS] / [fiat+USD chip] / [swap], with
the 32px swap button on its own row pinned right (counterAxisAlignItems
MAX). It was inline in the fiat row.

Also checked and NOT changed: the amount step has no title/subtitle in
the body. "How much to protect?" and its subtitle resolve to
Content > Header > Top nav — header content, which lives in
kastle-mobile (去頭去尾). They look missing in a side-by-side; the node
chain says they aren't ours.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Same hairline the vault detail table was missing (Figma's confirm Table
input carries a bottom stroke; the filled total row doesn't).

Labels move to typography.t900 — the node binds raw typography800, but
Nicole's semantic set (Figma "Fixed Colors": Text/Title Color -> t900,
Secondary -> t600, Tertiary -> t400) has no t800, so the binding
bypasses the system. Same #FFFFFF either way; her call is t900.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Figma's preset row is text + checkbox only: the row's logo frame slot is
hidden. The clock-in-a-circle was invented, same as the alert-triangle
on the backup title.

"Recommended" sits on the right beside the checkbox, not next to the
label.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Figma's Balance Container is edge-to-edge with a Border/border50 bottom
stroke separating it from the grid (pad [8,20,8,20]).

Kept as-is, deliberately: the card timer reads "20h:02m:02s" where the
Figma node shows "20:02:02" — Nicole asked for the units explicitly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Figma 12711:223618 stacks: ⓘ "How a Vault works?" link / "Set up now"
(primary) / "Close" (outline). The second button read "How a Vault
works?" and there was no Close — the info link belongs in the action
bar's message slot, which is what that slot is for.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
nicoletting and others added 23 commits July 16, 2026 20:47
Exported from Figma (Vault/ Notification Container's Rectangle fill)
rather than waiting on a hand-off — same route as vault.png.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Figma binds every badge label to the 800 tone of its ramp
(Success/success800 #5EEAD4 over Success/success background) — the
500/600 tones I had are the brand/indicator colours. Nicole's call:
follow Figma.

Affects the shared pill, so ActivityScreen + ActivityDetailSheet change
too — the Figma Badge component uses the same 800 tone for its own
SUCCESS/PENDING/FAILED variants, so they should match.

The dot/icon keeps the brand tone: its fill sits in a nested instance
the export doesn't resolve, so it isn't verified either way — not
guessed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Nicole's call: the vault/quote table isn't the activity row. DetailKVRow
is a two-tone label/value line for the activity sheet — closer to the
swap quote — and bending it into the vault's table had taken six added
props (labelColor, valueNode, subValue, emphasis, labelIcon, divider)
before it stopped being the same component.

- DetailKVRow reverted to main verbatim: Paul's Activity sheet renders
  exactly as before, and the PR no longer carries six props his feature
  never uses.
- New protections/DetailTable owns what the vault table actually is
  (Figma "Table Component"): bordered r16 bg50 card, 50-high rows,
  Border/border200 hairlines, white labels, pill + token-logo slots,
  fiat sub-lines, filled bold total, whole-row ⓘ targets.
- VaultDetailScreen, CreateVaultConfirmStep, WithdrawConfirmScreen all
  moved over; screenshots identical before/after.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Figma's protection hub has two states and only "user never create a
vault" was built. The other (13385:419530) drops the CTA and puts a
status pill before the chevron — the card stops selling the feature and
starts reporting on it.

ProtectionTypeCard gains `pill` (a variant, not a second card). Stories:
Locked / 1 vault withdrawing / 2 vaults withdrawing. Caption stays the
same as the active state, per Nicole.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Her exports replace mine: the intro vault keeps its glow, which my
export lost — the glow ellipses use a blend mode that only resolves over
the dark page, so a standalone node export baked them into an opaque
blob.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- CountdownRing + VaultCard timers use tabular figures. Proportional
  digits are each a different width, so a live countdown reflowed on
  every tick; this locks them to one width without freezing the layout.
- Window step rows are 68 high per Figma (they were ~46). The fixed
  height also absorbs the 1→2px border on select, so the +1 padding
  compensation is only needed horizontally now.
- Amount step: the USD chip had a surface fill I invented — Figma's
  Button has no fill.
- Intro screen: "How a Vault works?" now opens the explainer; the link
  was wired to a callback with no sheet behind it.
- Vault list: six vaults (Populated + BalanceHidden), so the grid and
  scrolling are actually exercised.
- Story rename: LockedBackedUp -> LockedAddressSaved (states should read
  as states, not as steps the user took).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The old one was the last "needs a Figma parity pass" draft and was wrong
in every dimension: ~3x too tall, no artwork, no shield, no dismiss, and
a filled CTA button where Figma has a text link.

Now matches "Vault/ Notification Container": 353x113, bg50, r16, pad
[16,16,8,16], gap 12; artwork 364 wide at x=-6 so it bleeds both edges;
shield 18; title 14 Bold; body 12 Regular typography600; "Set up now" as
a 14 Medium primary text link; 20px round dismiss on background500.

Two design-system gaps flagged inline, not guessed away:
- the border is a 3-stop linear gradient (#4adcef/#00d7ff/#0095f1) — no
  theme.ts token matches any stop, and expo-linear-gradient isn't a
  dependency (Paul's call). Solid brand tone stands in.
- the title binds no variable: raw #f4f3f2, no theme.ts counterpart.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The ring was primary.p50 dark blue. Figma (13409:25553) stacks two amber
arcs at innerRadius 0.87: a base in Warning/Warning soft background
(#b45a1a @ 24% — exactly theme's warning.softBackground) and a `start`
arc in Warning/warning500 on top, which is the progress sweep.

I recorded "solid ring, primary.p50" back when Nicole confirmed "實心環
:無問題" — she was confirming it isn't a gradient, not confirming the
colour. I took one answer as two.

- base ring now amber; the whole caption is the ⓘ tap target, not the
  12px icon
- CountdownRing stories wire a real InfoSheet, so the ⓘ opens the
  explainer in isolation instead of calling into nothing
- story names read as states: NearEnd -> UnderOneDay, LiveTicking ->
  Ticking

⚠️ The progress sweep is NOT drawn — an arc needs react-native-svg,
which isn't a dependency (Paul's call). The ring shows the track only.
Flagged to Nicole.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Both still hand-rolled their own Continue button and action bar — the
exact per-screen spacing drift BottomActionBar was created to end. Found
by auditing every protections file's shared-component imports rather
than waiting for the spacing to look wrong again.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Nicole changed the node so the copy no longer overlaps the vault art.
v2 wraps [shield + text] in one flex group, then a 46px gap, then the
close — that reserved right zone is what the graphic shows through. The
text column is ~34px narrower than before, so it wraps before the vault
instead of running over it.

Responsive:
- artwork is right-anchored (contentPosition right, bleeds -6) so the
  vault hugs the right edge at any width, not pinned to a 353 box
- content flexes; the 46 gap + 20 close reserve stays fixed on the right

Verified the copy clears the vault at 360 / 393 / 430 wide.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
VaultCard
- removed the `complete` status — Figma's vault list only has
  Locked / Withdrawing (it was invented, like the earlier clock icon)
- removed the Grid story — VaultListScreen/Populated already shows the
  real grid; Grid was a layout demo masquerading as a card state
- amount splits into number + unit: the number truncates with an
  ellipsis (flexShrink + minWidth:0) while the unit (flexShrink:0) stays
  whole on the same line, so a huge amount can't wrap "KAS" onto a second
  line and every card keeps its 222 height. New WithdrawingLongAmount
  story exercises it.
  ⚠️ truncate-number / pin-unit is my proposal for the overflow case —
  Figma doesn't specify it; confirm.

VaultAddressCard (bare): read-only value is 14px per Figma, via a new
backward-compatible `textStyle` override on the shared Textarea (Paul's
16px default is untouched).

VaultBalanceRows: amount↔chevron gap 6 -> 8 (Figma).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The root View had no style, so it hugged content in some layouts — the
bare VaultAddressCard came out ~half width next to the labeled variant.
alignSelf: stretch on the root makes it fill, matching the labeled card
and the backup card in VaultDetailScreen. Shared component, but this is
the correct default for a textarea; existing users only get wider.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CreateVaultAmountStep (Figma 12757:300288 / 12831:678052)
- token section ↔ usd section gap 8 -> 12
- amount ↔ Kaspa logo gap 8 -> 10 (logo ↔ "KAS" stays 8)
- usd value ↔ usd chip gap 8 -> 0
- action bar clears the keypad with 46 top padding; the error slot
  reserves its height so Continue never jumps

CreateVaultConfirmStep (Figma 12757:476249)
- image ↔ address card 16, address card ↔ quote 8 (was uniform 16)
- Vault amount gains its "$200.232 USD" sub-line (ConfirmRow was
  dropping subValue before it reached DetailTable)
- footer message slot added (both here and BottomActionBar) — ⚠️ the
  copy (node 12757:461001) still needs the Figma bridge to read; slot is
  wired, text pending

DetailTable: label ↔ tooltip icon gap 4 -> 8

InfoSheet: title ↔ divider was double-spaced (titleSection gap 16 +
divider marginTop 16 = 32); dropped the marginTop, now 16

VaultCard: long-amount demo uses 1,000,000,000.9999999 KAS and a real
vault name (users can't rename); also seeded into the vault list

Naming: ProtectionsBanner story -> Home/; hub stories -> NoVaultYet /
AllLocked / OneWithdrawing / TwoWithdrawing

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… gaps

Read against Figma (bridge back up):

- Alert info: bg was #1a282e; Nicole changed it to the blue soft tint
  Info/Warning soft background (#0973a8 @ 24%, fill + stroke both). Fill
  and stroke share info.softBackground now. ⚠️ shared Alert — every info
  alert gets the new tint.
- CreateVaultWindowStep checkbox: back to a 6px-radius square (Figma
  12757:304731) — Nicole had earlier said circle, then corrected it.
- VaultDetailScreen "Done": 12 Medium -> 14 Medium (Figma 13367:16830).
- CreateVaultConfirmStep footer copy filled from Figma 12757:461001:
  "You can't change this later. To use different settings, close the
  vault and make a new one."
- CreateVaultRecoveryStep: the two alerts sit 8 apart (was 16); they
  already reuse the shared Alert master. Added a Filled story
  (13493:967389) — an address entered; the 2px #4b7d92 active border is
  the Textarea's focus state.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Resolves the reuse violation: ProtectionsBanner had rebuilt what
explore/Banner already does. Both are the same Figma component set
("Notification Container").

New shared NotificationBanner (icon + title + description + LinkButton
CTA), with the two real differences as props, per Nicole:
- `art`: "cover" (faded full-bleed image, copy full width — explore) vs
  "bleedRight" (graphic hugs the right, copy reserves its 46px zone —
  the Protections banner's different caption width)
- `onPressDismiss`: the × is a toggle — pass a handler to show it, omit
  to hide, since not every banner is dismissible

ProtectionsBanner is now a thin preset of it (bleedRight + vault art +
shield + solid brand border placeholder). Renders identically.

⚠️ explore/Banner (Paul's) is untouched — it can migrate to
NotificationBanner in a follow-up PR for his review; the shared API
already covers its shape (cover art, lucide/emoji icon, remote image).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Read against Figma (bridge up).

VaultDetailScreen
- Vault address + Recovery address rows get copy + external-link icons
  (Figma 12802:620022) — DetailTable gains onPressCopy/onPressExternal
- withdraw danger line "Wasn't you? ..." is centred with no icon (Figma
  "Error message" hides its icon); colour was already Text/Invalid Color.
  BottomActionMessage gains hideIcon + alwaysCenter
- backup note padding 4 -> 8 (Figma 13367:16830)
- WithdrawingOneDayLeft now ticks the ring live in HH:MM:SS

VaultIntroScreen
- image was letterboxed: a 256x186 box on a 1.195:1 asset showed it
  ~222 wide. Box is now 256x214 (asset aspect), matching Figma's 248x213
- "How a Vault works?" gets 16 vertical padding (Figma "Info message")

WithdrawConfirmScreen (Figma 12802:619523)
- Amount gains "≈ $24,000 USD", Est. Fee gains "≈ $0 USD"
- Est. Fee gains its ⓘ tooltip. ⚠️ no vault-specific Est. Fee tooltip in
  Figma — reused the existing InfoSheet "Est. Fee" copy; confirm it fits
- address card ↔ quote gap 16 -> 8 (image ↔ address stays 16)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Removed the Toast component + stories. Per Paul, the clawback result
  toasts are wired app-side when the data flow lands; they don't belong
  in Storybook.
- Copy corrected against Figma:
  - intro title had a stray leading space -> "Your KAS just got a Vault"
  - recovery error "Invalid Kaspa address" -> "That's not a valid Kaspa
    address."
  - amount error "Amount exceeds your balance" -> "Oh, you don't have
    enough funds"

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On iPhone SE (375) the Recovery address value wrapped to a second line
beside its copy/external icons ("kaspa:pfdf…" / "v45s") because the
longer "Recovery address" label left less room and the value allowed 2
lines. Address values are already middle-ellipsised, so DetailTable now
pins them to one line (numberOfLines 1) and lets them shrink
(flexShrink + minWidth:0) when a row has the copy/external actions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The last row sat flush against the bottom when scrolled. Added 24 bottom
padding to the grid so the final card has breathing room.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Est. Fee sheet (Figma 13350:255308 / 13393:63687):
- EstFeeSheet gains free-form `label`, per-row `description`, and a
  `subtitle` prop (backward-compatible with the "<network> network fees"
  transfer usage). Vault breakdown now shows Network / Kastle / Creation
  fees with the creation note, "…for this transaction" subtitle.
- WithdrawConfirmScreen Est. Fee ⓘ opens the breakdown sheet (Network +
  Kastle) instead of a wrong text tooltip.

How a Vault works? sheet:
- InfoSheet gains `descriptionRich` (white-semibold step headings +
  inline Normal:/Theft:). Shared HOW_VAULT_WORKS constant threaded through
  the amount step + intro screen.

Vault detail (Galaxy S5 / iPhone SE):
- Bare VaultAddressCard fills width + hugs height, address wraps (no
  scroll, no overflow).
- Address values 12px; danger note amber not red; VaultCard timer keeps
  its icon and shrinks the number to stay on one line.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
onPressRecoveryInfo was a raw callback prop with no content wired to it
and never passed by the story, so VaultAddressCard's ⓘ disabled itself
(disabled={!onPressInfo}). Replaced with recoveryTooltip, mirroring
CreateVaultConfirmStep's pattern — same Figma instance (13391:560165)
both screens reuse.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Vault address / Recovery address rows carry a tooltip AND copy/external
actions at once. The whole row was one TouchableOpacity firing
onPressInfo, so tapping the copy or external icon fired the tooltip
too (or fought it) instead of its own action.

Now: label+ⓘ is its own zone (tooltip), the address text + copy icon
share one zone (copy), the external-link icon is a third, separate zone
(explorer). Verified by instrumenting the story's onPressCopy/
onPressExternal and clicking each zone's exact coordinates — each fires
only its own callback, and only the label zone opens the tooltip sheet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…stories

adjustsFontSizeToFit is a no-op in RN-web, so the countdown silently fell
back to numberOfLines ellipsis ("89d:23h:5…") instead of shrinking.
Same length-tier approach as CreateVaultAmountStep's AMOUNT_TIERS: both
real countdown formats ("20h:02m:02s", "89d:23h:59m") now render in full
down to a 360px-wide card.

Also adds DetailTable.stories.tsx — it had zero story coverage despite
being reused by 3 screens and just getting its tap-zone split; Default
covers every row shape, AddressRow isolates the ⓘ/copy/external split.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

🎨 Storybook Preview Ready!

Your Storybook preview has been deployed and is available at:

🔗 Preview URL: https://forbole.github.io/kastle-ui/preview/pr-53/

📝 What's included:

  • All components from this PR
  • Interactive component playground
  • Documentation and examples

💡 This preview updates automatically when you push new commits to this PR.

🗑️ Preview will be cleaned up when the PR is merged or closed.;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant