Releases: flyboy-byte/disc-tracker
Release list
v0.26 — UI/UX audit pass
A full UI/UX pass. A design audit turned up 30 findings across all five tabs; ten of them were scoped into a tracked plan and shipped here, every one verified on a physical Pixel 9 rather than just compiling. Plus one real bug you'd have hit.
The bug fix
"Back up everything" now actually saves. It was wired to the Android share sheet, which only lists apps that register an intent filter for application/json — Files isn't one of them, so "share to Files" (an iOS idiom) was never reachable. The Storage Access Framework path that writes straight to a folder was sitting right there as a secondary button. Those are now swapped: the primary action saves to a folder you pick, and sharing stays as "Send a copy…" for actually sending it somewhere.
Consistency
- One set of controls instead of five. Five hand-rolled "pick one of N" treatments — each with its own radius and tint — became a shared
SegmentedControl(single-choice) andFilterPill(narrowing a list). Used across Bag, Score setup and Settings. - Drawn icons instead of text glyphs.
▾ ▴ ⤒ ↑ ↓ ✕ ✓ › ‹were font characters that render inconsistently and can't be sized or aligned reliably. They're now SVG, matching the existing tab-bar icons. No new dependency. - Settings is a list, not nine cards. Every section was a bordered, filled, rounded box of identical weight, so Backup & Restore looked exactly as important as Default Throw View. Now separated by a hairline rule, with readable section headings.
Accessibility
- Every tap target meets Android's 44dp minimum. Seven controls were under it — worst case the three adjacent reorder arrows at 30dp. Fixed without changing how anything looks.
- No screen relies on colour alone. Score coloured stroke numbers by birdie/par/bogey with nothing else to go on — a real problem for red–green colour deficiency, and the green/amber/red palette was also the only colour in the app outside its purple language. Removed rather than patched with a second channel: it closes the accessibility gap and the inconsistency at once.
- The hardware Back button works mid-round. Back from an active scorecard jumped to the Bag tab instead of the rounds list. Round data was never at risk, but it was the wrong thing to do while you're on a course.
Fewer taps
- Undo for swipe-to-demote in Disc Suggest. A mis-swipe was a silent two-sided state change — the demotion and, in Buy mode, the learning engine's aversion write. Undo reverses both. (Building it also surfaced a latent bug shipped back in v0.23: a swiped card never reset its transform, invisible only because the list usually recycled the row first.)
- Any hole in one tap. The scorecard's ‹/› pair meant hole 17 of 18 was sixteen taps from hole 1. There's now a scrollable hole strip that follows you and shows which holes are fully scored.
- Bag actions are one button plus an overflow sheet, instead of one primary and up to five ghost buttons wrapping onto a second row. Field view moved next to the filters, where a view mode belongs.
- The catalog picker is a real radio list. Built-in / Try Discs / Custom read as buttons but behaved as a single choice, and tapping an undownloaded source silently started a network fetch. Selection and download are now separate targets.
- Flight Shaper sliders show their bounds. Value and unit share one line instead of sitting ~80dp apart with the slider between them, each track shows its min and max, and tapping a slider's value resets just that slider.
Notes
Three things were built differently from how they were scoped, each after checking the code rather than following the plan literally — Flight Shaper's arc view kept its 2×2 grid (it was already the shape the audit wanted), Score lost tier colour rather than gaining a second channel, and slider reset is a tap on the value rather than a double-tap on the slider (a double-tap would have moved the slider before resetting it). The reasoning for each is in app/plan/docs/ui-audit-plan.md.
No new permissions. Still local-only: no account, no server, no analytics.
Install: app-release.apk below (arm64-v8a + armeabi-v7a), or add https://fdroid.flyboybyte.com/fdroid/repo to an F-Droid client.
v0.25 - swipe hint, bag-report grid, scorekeeping upgrades, tutorial, F-Droid manifest fixes
Disc Suggest
- Added a quiet "Swipe a disc to reorder" hint above results in both Throw and Buy mode.
- Buy-mode learning engine now weighs brand more heavily and ramps up faster per swipe.
Bag
- "Share your bag" no longer cuts off past ~12 discs - scrolls, and big bags render as a compact 2-column grid.
Score
- New-round setup: quick 9 / 18 / Custom hole-count presets, plus course/roster prefill.
- Tap the big stroke number for one-tap quick-pick score entry.
- Scores are color-coded (eagle/birdie/par/bogey/double) on the live card and finished-round grid.
Settings
- New in-app "How to use Disc Tracker" tutorial, linked from Settings -> About.
Privacy / F-Droid readiness
- Removed an undisclosed SYSTEM_ALERT_WINDOW permission shipping in every release build.
- Disabled Android's OS-level Auto Backup (allowBackup=false).
- Both fixes implemented as durable Expo config so they survive expo prebuild --clean.
Compare: v0.24...v0.25
v0.24 — backup completeness, Brand A-Z sort, privacy disclosure
Follow-up to v0.23's swipe-to-dismiss feature.
- Full backup now includes Disc Suggest's swipe/learning state. It was silently missing before — restoring to a new device would lose all swipe history and the Buy-mode learning engine's accumulated preferences. Verified on-device: exported a real backup and confirmed both fields present with real data.
- Buy mode: "Best fit" / "Brand A-Z" sort toggle. With hundreds of near-tied "great fit" discs for a common scenario, sorting alphabetically by manufacturer is a much more useful way to actually browse for a new disc than score order alone. Verified on-device with correct locale ordering.
- Privacy disclosure. The swipe/learning local-preference data — never transmitted anywhere, but new locally-derived data about the user — is now documented in the privacy policy and README, matching this project's existing disclosure practice.
See app/plan/docs/suggest-swipe-scope.md for the full design.
v0.23 — Disc Suggest swipe-to-dismiss
Gmail-style side-to-side swipe on Disc Suggest result cards.
- Throw mode: swipe a disc away to drop it to the bottom of that scenario's list — persists across restarts, never bleeds into other scenarios.
- Buy mode: same reorder, plus a learning engine — swiping teaches it which flight numbers and brands you're passing on, re-sorting the rest of the list (never re-labeling a disc's true fit). Aggressive within a session, fades fast between launches on flight profile, fades slowly on brand. On/off toggle; off, Buy mode behaves exactly like Throw's plain reorder.
- Nothing is ever deleted — swiped discs stay reachable via paging, with a "Reset order" link to undo.
Verified on-device on a real Pixel 7, including a real bug found and fixed during that pass: a vertical scroll starting directly on a card could get captured by the card's swipe gesture instead of reaching the list — fixed with directional gesture constraints.
See app/plan/docs/suggest-swipe-scope.md for full design and verification notes.
v0.22 — Save backup directly to device
Highlights
- "Save to device" for backups — Back up everything now offers two ways out: the existing share sheet (any app, including "Save to Files"), or a new direct save straight to a folder you pick (e.g. Downloads), no share sheet round-trip. Same Storage Access Framework pattern already used by CSV export.
Verified
- 147/147 Jest tests passing,
tsc --noEmitclean. - On-device (Pixel 7): folder picker opens, permission dialog grants correctly, backup file writes to disk with correct content.
🤖 Generated with Claude Code
v0.21 — Catalog source picker, Try Discs credit compliance fix
Highlights
- Disc Catalog rework — Settings' single "downloaded vs. bundled" line is now a real three-way picker: Built-in (bundled), Try Discs (the expanded third-party catalog), and Custom (import your own catalog — a local JSON file or a self-hosted manifest URL, same format Try Discs uses). Each source caches independently, so switching between sources you've already fetched is instant — nothing is ever deleted.
- First-run prompt — on a fresh install, the app now offers to download and switch to the Try Discs catalog once, up front, instead of leaving it to be discovered in Settings.
- Compliance fix — the required "Disc data by Try Discs" credit now correctly follows the data itself (via the manifest's own
providerfield), not which picker slot it's active under. Closes a gap where importing Try Discs' data through the new Custom-URL path could have silently dropped the credit. - Disclosure — Settings, the website, and the README now state plainly that Try Discs lists 2,147 molds but only 1,874 make it into the app; the other 273 are excluded for missing flight numbers, not hidden without explanation.
Verified
- 147/147 Jest tests passing,
tsc --noEmitclean. - On-device (Pixel 7): first-run prompt, Try Discs download/activate, instant bidirectional switching between all three sources, and Custom import via URL all confirmed working end to end.
🤖 Generated with Claude Code
v0.20 — Try Discs catalog, Shareable Bag Report
Highlights
- Try Discs catalog integration — an opt-in, downloadable disc catalog (1,874 discs vs. the bundled 1,660) hosted on the project's own VPS, sourced from api.trydiscs.com under an agreed data-use arrangement. Settings → Disc Catalog → "Check for updates" downloads, SHA-256-verifies, and swaps in the larger library with no server dependency for existing bundled data. Credited as "Disc data by Try Discs" wherever the enhanced catalog is active.
- Shareable Bag Report (C7) — render your Today's Bag as a shareable image and push it through the native Android share sheet. No accounts, no server, no location/notes leakage — just your discs.
- Website (disc.flyboybyte.com) now also consumes the Try Discs catalog when available, with matching credit.
- Fixed the GitHub Pages privacy-policy build (was stuck in "building" for weeks).
- Added nginx rate-limiting on the catalog endpoints (VPS-side hardening, non-blocking).
Verified
- 141/141 Jest tests passing.
- On-device (Pixel 7): catalog download/activation/persistence, Disc Suggest buying-mode against the larger library, Bag Report share flow, all confirmed working.
- Live production site: authenticated
/api/catalogand/discsuggestioncredit element both confirmed.
🤖 Generated with Claude Code
v0.19 — Wear estimate, Disc Suggest buying mode
Two features from the suggest-engine plan, scoped and shipped this session:
- Wear estimate (1-5 scale) — supersedes the New/Seasoned/Beat field. A one-time
migration backfills existing discs' estimates from their old wear level, so nothing
suddenly looks "incomplete" in the Data Audit. - Disc Suggest buying mode — a big Throw/Buy header toggle. Buying mode ranks
library discs you don't already own, with category/stability/brand filters and a
one-line bag-gap summary (e.g. "Your bag has no distance driver for flex shot").
Also in this release:
- Fixed a real bug found live testing: two owned discs of the same mold (e.g. two
Leopard3s) collided on React key in Disc Suggest's results list. - Fixed a misleading gap-summary label: net turn+fade classified Flex Shot's ideal
disc as "stable," which masked a real cancellation (turn -2, fade +2) rather than
reflecting how the disc actually flies. - Verified Disc Suggest Phases 1-3 (Throw Style, personal stability adjustment, Flex
Shot scenario, Data Audit, role tags) live on-device. - Added adb
testIDs across the app's interactive surfaces for faster on-device
verification passes going forward.
tsc clean, 123/123 Jest tests passing.
v0.18 — Data audit scroll fix
Two small on-device UX fixes found by testing v0.17.
Fixes
- Data audit list wasn't scrollable — the "missing weight/plastic/wear-level" list in
Settings → Data audit was cut off past the modal's max height with no way to reach the rest
of the list. Now scrolls properly. - Website CSV import gets an "Allow duplicates" toggle, matching the app's existing one —
website-only change (not in this APK), noted here for the same session's completeness.
Notes
- Signed with the same production key as 0.15/0.16/0.17 → installs as an update, no uninstall.
- Two ideas floated this session, deliberately not built yet — logged in
app/plan/GRAVEYARD.md: an optional 1-5 "estimated wear" scale alongside the existing 3-tier
field, and an unscored idea for a "suggestions for buying" mode on Disc Suggest.
Toolchain: JDK 21, arm64-v8a + armeabi-v7a.
SHA256: 8984cc52545a3324e41625bd692fce71bc3eed7508febf57911b8518df2b27cc
v0.17 — Disc Suggest role tags + data audit, CSV duplicates fix
Disc Suggest gets smarter about your actual collection, plus two data-hygiene fixes.
Features
- Disc Suggest — personal role tags (Phase 3): tag any disc with your own role (e.g. "go-to roller", "backup driver") — an optional, freeform label shown alongside the disc, no effect on the scoring model itself.
- Data audit (Phase 2): new "Data audit" row in Settings surfaces discs missing weight/plastic/wear-level, with an inline-editable list (tap to fix weight/plastic, tap a pill to set wear level: New/Seasoned/Beat) — no navigating into the full edit form.
- CSV import — "Allow duplicates" toggle: import used to silently dedupe every row against your existing bag. Now optional, so multiple copies of the same disc (e.g. 3 Leopards) import correctly when you want them to.
Notes
- Signed with the same production key as 0.15/0.16 → installs as an update, no uninstall.
- Phase 2 (data audit) and Phase 3 (role tags) are code-complete and Jest-tested but not yet exercised on a physical device this release — flagging honestly rather than assuming; the CSV duplicates fix was verified on-device.
- Companion website (disc-tracker Flask app) also shipped its own parity update this session: CSV "Both" export scope, stability/role-tag fields, and a backup-file import button — website-only change, not part of this APK.
Toolchain: JDK 21, arm64-v8a + armeabi-v7a.
SHA256: 47d18b748c739702ac333cec4ad1455090197f509439616c506eb2c36f70bca1