Skip to content

Phase 5 + 6: Showdown, settings, local backup, and release - #33

Merged
Marcogn merged 7 commits into
mainfrom
claude/coverdex-native-android-6n5i27
Sep 5, 2026
Merged

Phase 5 + 6: Showdown, settings, local backup, and release#33
Marcogn merged 7 commits into
mainfrom
claude/coverdex-native-android-6n5i27

Conversation

@Marcogn

@Marcogn Marcogn commented Sep 5, 2026

Copy link
Copy Markdown
Owner

What changed

Phase 5 — Import/export and settings

  • Ported showdownParser.ts verbatim to domain/showdown/ShowdownFormat.kt: export/import of Pokémon Showdown team text, including the # Types: comment round-trip and unresolved-move placeholders that still import.
  • New Import/Export UI: ExportShowdownDialog (copy to clipboard or save a .txt file) from a team's overflow menu, and a full ImportShowdownScreen (paste or open a file, review parsed Pokémon, unresolved moves/species surfaced) reached from Settings. Import always creates a brand-new team, matching the old app's actually-reachable behaviour (useAppShell.ts's handleImportTeam), not the unused ImportExport.tsx component.
  • Renamed ThemePreferencesSettingsPreferences and added every remaining setting from the old web app: include Mega/Dynamax/Gigantamax forms, include legendaries/mythicals, include custom Pokémon in analysis (kept both as an on-screen toggle and persisted, reconciling two phase docs — see docs/implementation-decisions.md).
  • Local backup (domain/backup/, data/backup/): a single zip file holding every team and the custom roster (never the re-downloadable Pokédex cache), versioned against future-format restores, full-replace restore in one transaction with ids/timestamps preserved. Copied from Hall of Memories' pattern, minus the images/ half CoverDex's schema doesn't need.

Phase 6 — Release

  • Signing config docs (docs/release-signing.md) and two GitHub Actions workflows: build-apk.yml (manual signed-build smoke test) and release.yml (validates a x.y.z version, cuts the changelog, bumps versionCode/versionName, builds, signs, publishes the GitHub Release, pushes the version bump). Generating the actual production keystore and setting the five required secrets is explicitly left to the repository owner — an agent session has no safe way to hold a signing key or write repo secrets on someone's behalf.
  • CHANGELOG.md's [Unreleased] section cut into a real [2.0.0] release entry, led by the native-app announcement and the saved-teams data-loss warning stated plainly.
  • README.md, ROADMAP.md, .github/CONTRIBUTING.md rewritten to describe the finished native app, with no remaining web/PWA/Capacitor/npm references.
  • legacy-web/ deleted after checking every engine, string and behaviour it defined against its native Kotlin equivalent (coverage engine, ability effects, suggestion engine + its 0.5/1.0 composite-score weights, team generator + STARTER_FINALS, Showdown format, both locale files) — see docs/implementation-decisions.md, "Phase 6", for the full checklist.
  • CLAUDE.md, docs/plan/README.md, docs/STATUS.md, docs/test-plan.md updated for the finished six-phase rewrite.

Why

Continues the CoverDex native Android migration (Phases 1-4 shipped in #32); this PR closes out the plan's final two phases so the native rewrite in docs/plan/README.md is complete end to end.

Which tests cover it

  • ShowdownFormatTest (19 tests) — every case ported from showdownParser.test.ts.
  • SettingsPreferencesTest — default-false + round-trip for every new setting key.
  • BackupPayloadTest (7 tests) — DTO/JSON round-trip, future-format rejection, type fallback.
  • BackupArchiveTest (3 tests, Robolectric) — zip write/read, missing-entry rejection, future-format rejection.
  • BackupRepositoryImplTest (3 tests, Robolectric, real in-memory Room DB) — export/import round-trip, full-replace with id preservation, Pokédex cache left untouched.
  • Full suite: ./gradlew testDebugUnitTest lintDebug assembleDebug — 226 tests, 0 failures, lint clean, debug APK builds. Both release workflows YAML-validated (no Actions runner available in-session to execute them).

Test plan

  • ./gradlew testDebugUnitTest lintDebug assembleDebug green locally
  • On-device: Showdown export/import (clipboard, SAF file pickers, unknown-move/skipped-species handling) — see docs/test-plan.md, "Phase 5"
  • On-device: local backup export/restore, including across a reinstall
  • build-apk.yml / release.yml dispatched once the repository owner has set up the signing secrets — see docs/test-plan.md, "Phase 6"

🤖 Generated with Claude Code

https://claude.ai/code/session_017q6NYopgGPPVukqfTcmwrM


Generated by Claude Code

domain/showdown/ShowdownFormat.kt is a direct port of showdownParser.ts:
exportMemberToShowdown/exportTeamToShowdown, parseShowdownBlock/
parseShowdownTeam, and the higher-level importShowdownTeam that drops
unresolved-species blocks as errors rather than failing the whole import.
Both resolver parameters take the full PokemonEntry/MoveEntry rather than
the TypeScript's types-only/name-only shapes, since this app resolves a
sprite from pokedexId, never a stored URL. PokedexRepository grows
moveByName (mirroring speciesByName) so the caller can resolve a move
line without a live search subscription.

ThemePreferences is renamed to SettingsPreferences, matching the name
docs/plan/phase-5-import-export-and-settings.md's own settings table
already uses, and grows includeMegaDynamax/excludeLegendaries/
includeCustomsAnalysis alongside the existing theme/showMoves keys — one
DataStore file, not five. AnalysisViewModel now reads excludeLegendaries
and includeMegaDynamax from Settings instead of a per-screen toggle
(neither has an onChange prop in legacy-web's own TeamDetailPage.tsx —
both are read-only app-wide preferences there), and applies the
Mega/Dynamax/Gigantamax form-name regex to the suggestion pool only,
ported verbatim.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017q6NYopgGPPVukqfTcmwrM
Export: a team's overflow menu (TeamDetailScreen) opens a dialog showing
the exported block, with copy-to-clipboard and save-to-file (SAF
CreateDocument) actions.

Import: a new full screen (ui/importexport/ImportShowdownScreen) — paste
or open a .txt file, Parse shows every recognized Pokémon (flagging
unknown-move counts per slot) and any skipped/unresolved species, then
Create team writes the result into a brand-new team, prompting for its
name the same way Surprise Me's Keep does.

PokedexRepository grows allMoves() (mirroring allSpecies()) so the
import screen can resolve every move line in one bulk lookup instead of
a live search subscription per line.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017q6NYopgGPPVukqfTcmwrM
Settings gains a "Team Suggestions" section (Include Mega/Dynamax forms,
Include Legendaries — stored inverted as excludeLegendaries, matching
legacy-web's own AppSettings shape), an Import/Export section linking to
the Showdown import screen, and an App section with the version string.

Local backup (domain/backup/ + data/backup/) is a direct copy of Hall of
Memories' own pattern, adapted for CoverDex's schema: BackupPayload is a
zip's data.json holding every team and the custom roster (never the
Pokédex cache), versioned so a backup from a newer app version is
refused rather than partially applied. Restore is a full replace in one
transaction (BackupDao.replaceAll) with ids and timestamps preserved —
except the custom roster's creation order, which the domain TeamMember
model never carried in the first place; a restore reconstructs it from
each entry's position in the backup instead, since that position is the
only observable effect the original timestamp ever had.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017q6NYopgGPPVukqfTcmwrM
CHANGELOG, docs/test-plan.md, docs/implementation-decisions.md,
docs/STATUS.md, docs/plan/README.md and CLAUDE.md all updated for
Showdown import/export, the new Settings rows and local backup. Phase 5
ticked done everywhere it's tracked.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017q6NYopgGPPVukqfTcmwrM
docs/release-signing.md documents generating the release keystore
(reusing the Capacitor build's own if it still exists, so this native
build can install over it), producing the five repository secrets, and
reading the signing SHA-1 back out — copied from Hall of Memories'
version and adapted to CoverDex's own naming.

build-apk.yml (a debug-quality ad-hoc build, workflow_dispatch only) and
release.yml (validates the typed x.y.z version, cuts CHANGELOG.md's
[Unreleased] section, bumps versionName/versionCode, builds and signs,
publishes a GitHub Release with the APK attached, then pushes the
version bump) are both copied from Hall of Memories' own workflows,
renamed for this project. android-ci.yml already existed from Phase 0.

Generating the actual production keystore and setting the repository
secrets are left for the repository owner — noted explicitly in
release-signing.md's closing section, since an agent session has no way
to safely hold or transmit a production signing key.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017q6NYopgGPPVukqfTcmwrM
Cut CHANGELOG.md's Unreleased section into a real 2.0.0 release entry
led by the native-app announcement and the saved-teams data-loss
warning. Rewrite README.md, ROADMAP.md and .github/CONTRIBUTING.md to
describe the finished native app with no remaining web/PWA/Capacitor/
npm references. Tick Phase 6 done in CLAUDE.md and docs/plan/README.md,
update the Architecture section for SettingsPreferences and the new
domain/showdown, domain/backup, data/backup and ui/importexport
packages, and add the Phase 6 test-plan section and
implementation-decisions entry (signing left to the repository owner,
the legacy-web/ port checklist that was verified before deletion).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017q6NYopgGPPVukqfTcmwrM
Every engine, string and behaviour it defined has a native Kotlin
equivalent, checked before removal: the coverage engine, ability
effects (verified line-for-line), the suggestion engine and its
0.5/1.0 composite-score weights, the team generator and
STARTER_FINALS, the Showdown format (19 ported tests), and both
locale files against res/values{,-en}/strings.xml. See
docs/implementation-decisions.md, "Phase 6", for the full checklist.

docs/plan/ is kept as the historical record of how the rewrite was
built; only the parked React/Capacitor source tree is removed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017q6NYopgGPPVukqfTcmwrM
@Marcogn
Marcogn marked this pull request as ready for review September 5, 2026 06:39
@Marcogn
Marcogn merged commit a477941 into main Sep 5, 2026
1 check passed
@Marcogn
Marcogn deleted the claude/coverdex-native-android-6n5i27 branch September 5, 2026 06:39
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.

2 participants