Skip to content

Migrate legacy features → modern app: Phase 0 (nav + ViewModel) + Phase 1 (Subjects)#4

Merged
LeoColman merged 7 commits into
mainfrom
feat/migration-phase-1-subjects
Jun 30, 2026
Merged

Migrate legacy features → modern app: Phase 0 (nav + ViewModel) + Phase 1 (Subjects)#4
LeoColman merged 7 commits into
mainfrom
feat/migration-phase-1-subjects

Conversation

@LeoColman

@LeoColman LeoColman commented Jun 30, 2026

Copy link
Copy Markdown
Member

Brings legacy Jopiter functionality into the modern app (Compose + Koin + SQLDelight + Fuel/Jackson + Kotest). First two phases of a planned, phased migration. Screens are new; behavior is preserved and unit-tested.

Phase 0 — Navigation + ViewModel foundation

  • Replaces the flat enum-content drawer with Navigation Compose (JopiterNavHost); the drawer now drives a NavController, so detail screens can be pushed.
  • Establishes the ViewModel pattern: Restaurant logic moves out of the composable into RestaurantViewModel (Koin viewModel{} / koinViewModel). This is the template every later feature follows.
  • Adds kotlinx-coroutines-test; RestaurantViewModelTest + a NavHost start-destination smoke test.

Phase 1 — Subjects + class times

  • SQLDelight Subject + ClassTime tables, plus a v1→v2 .sqm migration so existing installs (which only had PreferredRestaurant) gain the new tables instead of crashing.
  • SubjectRepository (reactive Flow, transactional save/replace + delete), SubjectsViewModel + SubjectEditViewModel enforcing the legacy rules (non-blank + unique name, ≥1 class time).
  • SubjectsPage (list + FAB) and SubjectEditScreen (form + class-time dialog with day/time pickers), under a new "Disciplinas" drawer entry. pt-BR strings.

Also included

  • RestaurantMenu.isEmpty() (hard dependency of RestaurantViewModel).
  • Align stale restaurant tests with the shipping modelRestaurantTest/PreferredRestaurantRepositoryTest expected old short names but the app ships the fuller campus names (e.g. "Central - Campus Butantã", id 14 = "Fac. Direito"); these tests were already failing on main. Separate commit — easy to drop if you intended the other direction.

Validation

  • ./gradlew :app:testUnofficialDebugUnitTestgreen, 0 failures.
  • 13/13 instrumented tests pass on a Pixel_9a emulator (run via adb am instrument; gradle's connectedAndroidTest is flaky here due to the emulator's Bluetooth stack crashing).
  • Manual emulator run: drawer → Disciplinas → create subject (name + class-time dialog) → saved subject appears in the list; restaurant feature still works through the new ViewModel; the v1→v2 DB migration was exercised on a real device.

🤖 Generated with Claude Code

https://claude.ai/code/session_01X5cqJDzZvxqTxMTWcN2jvm

Screenshots

Navigation drawer Subjects Subject edit Restaurant

LeoColman and others added 7 commits June 30, 2026 15:11
Replace the flat enum-content drawer with Navigation Compose so detail
screens can be pushed in later phases, and establish the ViewModel pattern
that every migrated feature will follow.

- Page now carries a route; JopiterNavHost renders all destinations and
  MainActivity drives the drawer through a NavController.
- Restaurant logic moves out of the composable into RestaurantViewModel
  (Koin viewModel{} / koinViewModel), the canonical template for new features.
- Add kotlinx-coroutines-test for ViewModel/Flow unit tests; add
  RestaurantViewModelTest and a NavHost start-destination smoke assertion.

Validated: unit suite (testUnofficialDebugUnitTest) adds 2 passing VM tests
with no new failures; 12/12 instrumented tests pass on a Pixel_9a emulator;
manual run confirms drawer -> NavHost route switch renders the live
Restaurant page via the ViewModel.

Note: RestaurantPage.kt is included with pre-existing working-tree changes
(menu rendering, DayOfWeek.now()/asDayOfWeek) it shared with this work, since
RestaurantViewModel depends on those symbols. Unrelated WIP left untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X5cqJDzZvxqTxMTWcN2jvm
Add subject management (CRUD) with weekly class times, ported from the legacy
app's Subject/ClassTime model and its creation validation.

- SQLDelight Subject + ClassTime tables, plus a v1->v2 migration so existing
  installs (which only had PreferredRestaurant) gain the new tables instead of
  crashing on first access.
- SubjectRepository: reactive Flow of subjects with their class times;
  transactional save (insert/update + full class-time replacement) and delete.
- SubjectsViewModel (list) and SubjectEditViewModel (create/edit) enforcing the
  legacy rules: non-blank, unique name and at least one class time.
- SubjectsPage (list + FAB) and SubjectEditScreen (form + class-time dialog with
  day dropdown and time pickers), reachable from a new "Disciplinas" drawer entry.
- Shared DayOfWeek/time formatting helpers; pt-BR strings.

Tests: SubjectRepositoryTest, SubjectEditViewModelTest, SubjectsViewModelTest
(unit) and SubjectModuleTest (instrumented). Unit suite passes with no new
failures; 13/13 instrumented tests pass; the create flow and the v1->v2 DB
migration were validated on a Pixel_9a emulator.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X5cqJDzZvxqTxMTWcN2jvm
RestaurantTest and PreferredRestaurantRepositoryTest expected the old short
restaurant names ("Central", "Largo São Francisco", ...), but Restaurant.kt
ships the fuller campus names ("Central - Campus Butantã", id 14 = "Fac. Direito",
...) — these are what the app actually displays. The tests had drifted and were
failing on main; update their expectations to match the model.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X5cqJDzZvxqTxMTWcN2jvm
- Add androidx.compose.ui:ui-tooling-preview as an implementation dependency so
  @Preview (used in DrawerContent) resolves in the release variant, which was
  failing compileUnofficialReleaseKotlin.
- Clear all detekt findings so the lint gate passes: split SubjectEditScreen
  into smaller composables (LongMethod), wrap long lines (MaxLineLength), name
  the default class-time hours (MagicNumber), suppress LongParameterList on the
  Compose components, suppress InjectDispatcher on the SQLDelight flows, and
  replace !! in Restaurant with requireNotNull (UnsafeCallOnNullableType).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X5cqJDzZvxqTxMTWcN2jvm
The detekt job's summary step cats app/build/reporots/detekt/detekt.md, which
never exists, so the job failed with exit 1 even when the detekt analysis itself
passed. Fix the path in both the .main.kts source and the generated .yaml.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X5cqJDzZvxqTxMTWcN2jvm
- Wire up Spotless (com.diffplug.spotless) with licenseHeaderFile(LICENSE_HEADER)
  so the existing "Check license headers" workflow (app:spotlessCheck) actually
  has a task to run, and add the AGPL header to the five files that lacked it.
- Run the interface tests on ubuntu-latest with a KVM-enable step and JDK 17
  (AGP 8 requires it) instead of macos-latest, where the emulator never booted
  (connection refused on 5554). Regenerated ui-tests.yaml from the .main.kts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X5cqJDzZvxqTxMTWcN2jvm
Navigation drawer, subjects list, subject edit form, and the restaurant menu,
captured on a Pixel_9a emulator.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X5cqJDzZvxqTxMTWcN2jvm
@LeoColman LeoColman merged commit 2095886 into main Jun 30, 2026
10 checks passed
@LeoColman LeoColman deleted the feat/migration-phase-1-subjects branch June 30, 2026 19:53
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