Commit Graph

7 Commits

Author SHA1 Message Date
Adrian Kuta
3f9cf96216 REDI-95: ViewModel unit tests (JUnit5 + Turbine + AssertK + fakes)
Test CharacterListViewModel and CharacterDetailViewModel entirely through their
MVI surface with a FakeCharacterRepository (a fake, not a mock) and a directly
constructed SavedStateHandle, on StandardTestDispatcher. Coverage: happy path,
error -> UiText + snackbar Event, pagination end-reached, the in-flight and
duplicate next-page guards, process-death restore, and both branches of OnRetry.
Also a domain test for GetCharactersPageUseCase (delegation + error
propagation).
2026-06-10 15:00:45 +02:00
Adrian Kuta
cf63095acc REDI-98: error-handling demo screen (DataError -> UiText pipeline)
A runnable MVI screen (reached from the list overflow menu) that forces a real
DataError.Network case and routes it through the same pipeline a genuine call
uses: Result.Error -> onFailure -> DataError.toUiText() -> design-system
ErrorState. Three distinct cases (NO_INTERNET, NOT_FOUND, SERVER_ERROR) each
render their mapped message; Retry re-issues the last attempt via an Action; a
successful load clears the error. Wired as intra-feature navigation
(ErrorDemoRoute) and registered in Koin (incl. the UseCase factoryOf).
2026-06-10 15:00:27 +02:00
Adrian Kuta
0542d4dc1d REDI-94: GetCharactersPageUseCase + inject into list ViewModel
Add a domain UseCase (operator invoke) in :feature:characters:domain delegating
to CharacterRepository, and have CharacterListViewModel depend on it instead of
the repository directly. The UseCase is a deliberate thin pass-through that
documents the 'when to add a UseCase' convention (real logic / multi-source
composition vs. a single forwarded call).
2026-06-10 15:00:17 +02:00
Adrian Kuta
33de7f5ef8 REDI-90: character detail screen (type-safe nav args + MVI)
Add a CharacterDetail MVI stack (State/Action/Event/ViewModel + CharacterDetailUi)
to the UI-agnostic :feature:characters:presentation. The detail ViewModel reads the
typed characterId from SavedStateHandle (populated by the type-safe CharacterDetailRoute),
so the module keeps zero navigation/Compose deps.

Add CharacterDetailScreen (Root/Screen, image header, attribute rows, loading/error)
and CharacterDetailRoute to :presentation-compose; refactor charactersGraph to drive
list->detail via NavController and expose About / Views entries as callbacks. Extract
shared CharacterStatus label/colour helpers; add an overflow menu to the list app bar.

Add material-icons-core to the compose bundle for the app-bar icons.
2026-06-10 13:44:39 +02:00
Adrian Kuta
38d8f5915b fix(characters:presentation): pagination race + silent restore failure (review)
- Race: isLoadingNextPage was set inside the launched coroutine, so a rapid second
  OnLoadNextPage passed the guard before the flag flipped -> the same page loaded twice and
  items were appended twice. Set the loading flag synchronously before launching.
- Restore: when a middle page failed after earlier pages loaded, the error was swallowed
  (error=null, no event). Now any restore failure emits a ShowSnackbar; partial restores show
  the loaded list + snackbar, full failures show the error state.

Found by the milestone review.
2026-06-10 13:03:09 +02:00
Adrian Kuta
ef50094e3e feat(characters): Koin module + nav graph + wire into :app (REDI-89)
- charactersPresentationModule: viewModelOf(::CharacterListViewModel) (in the UI-agnostic module).
- @Serializable CharacterListRoute + NavGraphBuilder.charactersGraph { composable<CharacterListRoute> }
  in presentation-compose (serialization plugin added for type-safe routes).
- :app registers coreDataModule + charactersDataModule + charactersPresentationModule in startKoin,
  and hosts a NavHost(startDestination = CharacterListRoute) calling charactersGraph.
- core:data manifest declares INTERNET (merges into :app) for live API calls.
2026-06-10 12:52:14 +02:00
Adrian Kuta
2a419df43e feat(characters:presentation): UI-agnostic MVI ViewModel (REDI-87)
- CharacterListState (characters, isLoading, isLoadingNextPage, currentPage, endReached, error: UiText?),
  CharacterListAction (OnCharacterClick/OnRetry/OnLoadNextPage), CharacterListEvent (NavigateToDetail/ShowSnackbar).
- CharacterListViewModel: state via .update, one-time events via Channel, DataError -> UiText on failure,
  pagination persisted in SavedStateHandle (rebuilds list up to the saved page after process death).
- CharacterUi + Character.toCharacterUi().
- NO Compose/Views deps: verified no androidx.compose on the compile classpath. Stability via
  ImmutableList instead of @Stable (which would require compose-runtime) — the only compose-named
  transitive is kotlinx-immutable's annotations-only stub, not the Compose framework.
2026-06-10 12:43:30 +02:00