Migrate legacy features → modern app: Phase 0 (nav + ViewModel) + Phase 1 (Subjects)#4
Merged
Merged
Conversation
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
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.
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
JopiterNavHost); the drawer now drives aNavController, so detail screens can be pushed.RestaurantViewModel(KoinviewModel{}/koinViewModel). This is the template every later feature follows.kotlinx-coroutines-test;RestaurantViewModelTest+ a NavHost start-destination smoke test.Phase 1 — Subjects + class times
Subject+ClassTimetables, plus a v1→v2.sqmmigration so existing installs (which only hadPreferredRestaurant) gain the new tables instead of crashing.SubjectRepository(reactiveFlow, transactional save/replace + delete),SubjectsViewModel+SubjectEditViewModelenforcing the legacy rules (non-blank + unique name, ≥1 class time).SubjectsPage(list + FAB) andSubjectEditScreen(form + class-time dialog with day/time pickers), under a new "Disciplinas" drawer entry. pt-BR strings.Also included
RestaurantMenu.isEmpty()(hard dependency ofRestaurantViewModel).RestaurantTest/PreferredRestaurantRepositoryTestexpected 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 onmain. Separate commit — easy to drop if you intended the other direction.Validation
./gradlew :app:testUnofficialDebugUnitTest— green, 0 failures.adb am instrument; gradle'sconnectedAndroidTestis flaky here due to the emulator's Bluetooth stack crashing).🤖 Generated with Claude Code
https://claude.ai/code/session_01X5cqJDzZvxqTxMTWcN2jvm
Screenshots