feat(ui): add the 6 reusable UI components consumed by the primary screens#21
Merged
Conversation
…reens
The next round of feature work (Lobby, Wallet, History, Profile shells)
all share the same building blocks. Building them once here keeps each
screen PR focused on layout rather than re-implementing the same card
or chip styling five times. Every component below is mockup-faithful;
spec was extracted directly from mockup/js/components.js,
mockup/js/screens/*.js and mockup/styles.css.
* StackCard - generic bordered container (border-line bg-surface).
Slots a single content lambda; optional leftAccent draws the 3dp
win/loss/deposit/withdrawal strip; elevated swaps surface tone;
onClick toggles ripple feedback. Defaults to 16dp content padding
(p-4 in the mockup), overridable via contentPadding.
* BalancePill - balance hero (lobby + wallet headers). Uppercased
label + 36sp bold amount with tabular-nums; isHidden swaps the
amount for six dots; onToggleVisibility wires the eye icon when
provided, hides the control otherwise (wallet's locked-balance row
is read-only).
* FilterChipRow<T> - single-select chip row. Generic key type keeps
callers type-safe (enums, sealed types, ids). Active chips use
violet fill + white text + violet border; inactive ones are
transparent with txt-mid label and line border. scrollable=true
swaps the wrapped Row for a horizontal-scroll lane (news source
filters use this; wallet tabs do not).
* EmptyState - centered icon + uppercased title + message + optional
CTA. Icon slot lets the caller choose imageVector / drawable per
context (history, KYC, filtered news). Reuses the mockup recipe:
56dp icon container, max-width 280dp, vertical py-12 padding.
* ErrorState - danger-tinted container with icon, title, message,
required primary action plus optional secondary action and footer
text. Covers the three observed flavors: profile/news single-CTA,
lobby dual-CTA (Retry + Use cache), news with footer ("Last
successful fetch 2h ago"). Action buttons share an internal
ErrorActionButton helper so the styling stays one source of truth.
* Skeleton - loading-placeholder primitive. Ports Tailwind's
animate-pulse exactly: opacity 1 -> 0.5 -> 1 over 1000ms using
cubic-bezier(0.4, 0, 0.6, 1). The caller decides the shape via the
Modifier (Modifier.size(...), Modifier.fillMaxWidth().height(...),
etc.), which matches the mockup's inline placeholders where every
screen sizes the block to its target slot.
Each file ships @Preview composables for every state variant so the
Android Studio preview pane covers the design surface.
Convention adhered to per CR feedback:
* const val tokens use SCREAMING_SNAKE_CASE; plain val tokens use
PascalCase (matches ktlint property-naming).
* Every numeric literal lives at the bottom of its file as a named
token; no inline magic numbers in the production composables.
* detekt.yml: extend the @Preview exemption to MagicNumber too -
preview composables intentionally use throwaway fractional widths
to demonstrate variants.
Tests (instrumented, run via emulator lane once card 15 lands):
* FilterChipRowTest: every label renders uppercased; selection
callback fires with the typed key on click.
* ErrorStateTest: title/message/primary render; primary click invokes
callback; secondary action gates on both label+callback; footer is
uppercased when supplied.
* BalancePillTest: label uppercased; hidden state swaps to six dots
with the show/hide content description; toggle click flips state;
eye control absent when callback is null.
netqo
added a commit
that referenced
this pull request
May 27, 2026
Wires the Lobby destination to a real screen rendering the success, loading and error variants from the mockup. The success state uses the StackCard/BalancePill/Skeleton/ErrorState components shipped in #21. - Header: avatar, greeting and notifications button with unread badge. - Balance hero: AVAILABLE label, PnL chip on its own slot (fades to alpha 0 on hide so the row never shifts), session stats on a second line, amount stacked with the masked dots so toggling visibility does not shift any surrounding layout, currency dropdown switching USDC and USDT, and the locked column on the right. - Games grid: Roulette, Blackjack, Crash (LAST PLAYED, badge inset 8dp with the icon dropped 16dp and a shorter icon-to-title gap so the card height matches its row siblings), Mines and a full-width Coinflip card. - Quick Actions: Deposit (down arrow, ok-green) and Withdraw (up arrow, warn-amber) routing to the wallet. - Recent activity: three rows backed by StackCard with the win/loss left accent stripe. - Nep FAB overlay aligned to the bottom-right of the scroll area. LobbyPreviewData seeds the @Preview composables and the live nav-host until the Firestore-backed ViewModel ships in a later card. Promotes Modifier.gridBackground out of LoginScreenEffects into ui/components so the lobby and the upcoming primary screens share it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Foundation for the next round of feature work (Lobby, Wallet, History, Profile shells). All 6 components are mockup-faithful, spec extracted directly from
mockup/js/components.js,mockup/js/screens/*.jsandmockup/styles.css.Components
StackCard- generic bordered container. OptionalleftAccent: Color?(3dp win/loss/etc. strip),elevatedswaps surface tone,onClicktoggles ripple. Default 16dp padding.BalancePill- balance hero. Uppercased label + 36sp bold amount withtnum;isHiddenswaps amount for six dots;onToggleVisibilitywires the eye icon (or hides it for read-only rows like wallet's locked balance).FilterChipRow<T>- generic single-select chip row. Active = violet fill + white text; inactive = transparent + txt-mid + line border.scrollable=trueswaps the wrapped Row for a horizontal-scroll lane.EmptyState- centered icon slot + title + message + optional action. Mockup's 56dp icon container, max-width 280dp, py-12 padding.ErrorState- danger-tinted container with icon, title, message, required primary action plus optional secondary action and footer. Covers profile/news single-CTA, lobby dual-CTA, and news with "Last successful fetch ..." footer.Skeleton- loading placeholder. Ports Tailwindanimate-pulse: opacity 1->0.5->1 over 1000ms withcubic-bezier(0.4, 0, 0.6, 1). Caller decides shape via theModifier.Convention
const valtokens useSCREAMING_SNAKE_CASE;valtokens usePascalCase(matches ktlint).detekt.yml: extend the@Previewexemption toMagicNumbertoo - preview composables intentionally use throwaway fractional widths.Tests
Three instrumented Compose UI tests (run via emulator lane once card 15 lands):
FilterChipRowTest: labels uppercased; selection fires with typed key on click.ErrorStateTest: title/message/primary render; primary click invokes callback; secondary action gates on both label+callback; footer uppercased.BalancePillTest: label uppercased; hidden swaps to six dots; toggle flips state; eye absent when callback null.Test plan
./gradlew :app:assembleDebuggreen./gradlew :app:testDebugUnitTestgreen (34/34, no regressions)./gradlew ktlintCheck detektgreen