Phase 5 + 6: Showdown, settings, local backup, and release - #33
Merged
Conversation
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
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.
What changed
Phase 5 — Import/export and settings
showdownParser.tsverbatim todomain/showdown/ShowdownFormat.kt: export/import of Pokémon Showdown team text, including the# Types:comment round-trip and unresolved-move placeholders that still import.ExportShowdownDialog(copy to clipboard or save a.txtfile) from a team's overflow menu, and a fullImportShowdownScreen(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'shandleImportTeam), not the unusedImportExport.tsxcomponent.ThemePreferences→SettingsPreferencesand 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 — seedocs/implementation-decisions.md).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 theimages/half CoverDex's schema doesn't need.Phase 6 — Release
docs/release-signing.md) and two GitHub Actions workflows:build-apk.yml(manual signed-build smoke test) andrelease.yml(validates ax.y.zversion, cuts the changelog, bumpsversionCode/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.mdrewritten 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) — seedocs/implementation-decisions.md, "Phase 6", for the full checklist.CLAUDE.md,docs/plan/README.md,docs/STATUS.md,docs/test-plan.mdupdated 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.mdis complete end to end.Which tests cover it
ShowdownFormatTest(19 tests) — every case ported fromshowdownParser.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../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 assembleDebuggreen locallydocs/test-plan.md, "Phase 5"build-apk.yml/release.ymldispatched once the repository owner has set up the signing secrets — seedocs/test-plan.md, "Phase 6"🤖 Generated with Claude Code
https://claude.ai/code/session_017q6NYopgGPPVukqfTcmwrM
Generated by Claude Code