Skip to content

refactor: address code-quality audit (DRY, magic numbers, typed flags)#60

Merged
netqo merged 2 commits into
devfrom
chore/code-quality-cleanup
Jun 24, 2026
Merged

refactor: address code-quality audit (DRY, magic numbers, typed flags)#60
netqo merged 2 commits into
devfrom
chore/code-quality-cleanup

Conversation

@netqo

@netqo netqo commented Jun 24, 2026

Copy link
Copy Markdown
Owner

Summary

Behavior-preserving code-quality refactor that works the audit backlog into the
codebase: removes duplicated formatters and widgets, slims the five game
screens, tightens the game repository, and replaces string-based UI inference
with typed state. Net change is roughly -1300 lines.

Rationale

The five game screens and several ViewModels had drifted into near-copies. The
work consolidates the clear duplication where a shared abstraction genuinely
pays off, and intentionally stops short where it would not:

  • Shared formatting/util layer (Money, TimestampFormat, ShareText) instead
    of per-ViewModel String.format / SimpleDateFormat / share-intent copies.
  • A shared game UI kit (GameHeader, StatCell, GameActionButton,
    RulesSheet, GameTokens) so each screen keeps only its game-specific stage.
    The five *RulesSheet files collapse to thin data wrappers.
  • One HairlineDivider/VerticalHairline, Modifier.cardSurface, SearchField
    and StackOutlineButton replacing the per-feature redeclarations; the
    GameKey -> label map moves onto GameKey.displayName.
  • GameRepositoryImpl keeps its public interface but shares the repeated draw,
    engine-call, settlement-guard and entity-build logic via small private
    helpers (newDraw, evaluate, charge/creditPayout, gameRoundEntity,
    safeMultiplier) plus promoted constants.
  • ViewModels now expose typed flags (sessionProfitPositive, OutcomeTone) so
    the screens tint profit / win-loss from semantics instead of inspecting the
    formatted label text.
  • String-embedded magic (KYC progress %, house-wallet reveal seconds, mines
    bounds message) is derived from the existing constants; blackjack gets named
    OPENING_CARDS / DOUBLE_FACTOR.

Trade-offs / deliberately deferred (kept out to avoid over-engineering and a
mixed PR):

  • The full per-game strategy split of GameRepository / RoundDetailViewModel:
    churning the 11-method public interface and all callers/tests is not worth the
    gain right now.
  • MonogramAvatar / IconButtonChip consolidation and the relative-time ladder
    merge (news vs notifications use intentionally different ladders, so merging
    would change one screen's copy).

Verification

Full local gate, all green:

  • ./gradlew :app:compileDebugKotlin
  • ./gradlew :app:compileDebugAndroidTestKotlin
  • ./gradlew :app:ktlintMainSourceSetCheck
  • ./gradlew :app:detekt
  • ./gradlew :app:testDebugUnitTest (entire unit suite, including
    GameRepositoryImplTest and the game ViewModel tests)

Test plan

  • Unit test suite passes unchanged (no test assertions needed updating).
  • ktlint + detekt clean on the new and modified sources.
  • Smoke-run the five game screens on a device/emulator to confirm headers,
    stats strips, rules sheets and action buttons render as before.

Related issues

Checklist

  • Commit messages follow Conventional Commits (see CONTRIBUTING.md).
  • Branch name follows the naming convention (feat/, fix/, chore/, etc.).
  • The change is scoped: no unrelated edits sneaked into this PR.
  • No user-visible behavior changed, so CHANGELOG.md is intentionally untouched.
  • No new dependency was added.

Work the CODE_QUALITY_AUDIT.md backlog into the codebase. Behavior is
unchanged; the full gate (compile, ktlint, detekt, unit tests) is green.

Highlights:
- util: centralize money/multiplier formatting (Money.kt), UI timestamp
  formatting (TimestampFormat.kt) and the share-sheet helper (ShareText.kt);
  wire them into the 9 game/wallet/history/profile/rounddetail/assistant
  ViewModels, removing the per-VM String.format/SimpleDateFormat copies.
- ui: extract a shared game UI kit (GameHeader, StatCell, GameActionButton,
  RulesSheet, GameTokens) and collapse the five *RulesSheet files into thin
  data wrappers; the five game screens shrink ~15-23%.
- ui: consolidate per-feature widgets onto one HairlineDivider/VerticalHairline,
  Modifier.cardSurface, SearchField and StackOutlineButton; promote the
  GameKey -> display label map to GameKey.displayName on the model; dedup the
  FilterChipRow branches.
- data: DRY GameRepositoryImpl with newDraw(), an engine evaluate() helper,
  charge()/creditPayout() settlement guards, a gameRoundEntity() factory,
  safeMultiplier(), and ENGINE_UNAVAILABLE / ENGINE_CSV_DELIMITER constants.
- ui: expose typed UiState flags (sessionProfitPositive, OutcomeTone) so the
  screens stop inferring profit/win-loss from formatted strings.
- non-ASCII: remove all em-dashes and any unicode from comments/docstrings
  (unicode in visible UI strings is kept per design).
- magic numbers: derive the KYC progress label, house-wallet reveal label and
  mines bounds message from their constants; name blackjack OPENING_CARDS /
  DOUBLE_FACTOR.
- dead code: reuse the shared gridBackground() in Crash, drop a no-op Spacer,
  and document the lobby retry/use-cache no-ops.

Deferred (noted for follow-up): per-game strategy split of GameRepository and
RoundDetailViewModel, MonogramAvatar / IconButtonChip consolidation, and the
relative-time ladder merge (kept separate to preserve distinct screen copy).
@netqo netqo changed the base branch from main to dev June 24, 2026 15:54
ProportionBar laid out both segments with Modifier.weight(wonFraction)
and weight(1f - wonFraction). Compose rejects a zero/negative weight, so
a profile with a 0% or 100% win rate (wonFraction 0f or 1f) crashed the
screen with "invalid weight; must be greater than zero". Render each
segment only when its share is non-zero. Pre-existing latent bug,
surfaced while smoke-testing this branch on-device.
@netqo netqo merged commit 3fe0c61 into dev Jun 24, 2026
3 checks passed
@netqo netqo deleted the chore/code-quality-cleanup branch June 24, 2026 16:49
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