feat(search): Phase C Wave 3 — Search end-to-end Store5 migration - #2688
Conversation
End-to-end migration of the Search feature to the kmp-project-template Store5 architecture, following the per-resource API + MVI ViewModel + ScreenContent pattern. Network (core/network): - New SearchApi (Ktorfit, suspend) at core.network.search.api.SearchApi - @deprecated SearchService.searchResources + DataManagerSearch.searchResources Data (core/data): - ScreenTypes typealiases for ScreenState / DataFreshness / ScreenDataStream - Relocated SearchRepository to com.mifos.core.data.search; new impl uses SearchApi (suspend, returns List<SearchedEntity>) — no more Flow<DataState> - RepositoryModule binds the new impl - RoomSubmitOutbox.toEntry now uses try/catch instead of runCatching Feature (feature/search): - Restructured to {ui, di, navigation, components} subpackages - SearchViewModel extends BaseViewModel<SearchScreenState, Nothing, SearchAction> - SearchScreenState owns ScreenState<List<SearchedEntity>> - SearchScreen uses ScreenContent (Loading / Empty / Error / Content slots) - Search components use SearchAction (renamed from SearchScreenEvent) - SearchNavigation split: SearchRoute.kt (@serializable) + nav builder - Accessibility content descriptions moved into composeResources - @Preview functions for Loading / InitialEmpty / NoResults / Content / Error - gradle deps: projects.core.data + projects.core.ui (no projects.coreBase.*)
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review (1)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|



Summary
End-to-end migration of the Search feature to the kmp-project-template Store5 architecture. This is Wave 3 of Phase C (the per-feature wave) — the first read-only feature wave, following Wave 1 (activate, mutation) and Wave 2 (auth, mutation).
Layers touched
Network (
core/network)SearchApi(Ktorfit, suspend) atcom.mifos.core.network.search.api.SearchApi— returnsList<SearchedEntity>directly, noFlow/ noCallwrapperNetworkModule(single { get<Ktorfit>().createSearchApi() })@DeprecatedonSearchService.searchResourcesandDataManagerSearch.searchResources— to be deleted in Phase DData (
core/data)ScreenTypes.ktaddsScreenState<T>/DataFreshness/ScreenDataStream<T>typealiases over thetemplate.core.base.store.screen.*originalsSearchRepositoryinterface relocatedcom.mifos.core.data.repository→com.mifos.core.data.searchcom.mifos.core.data.search.impl.SearchRepositoryImpl— usesSearchApi, suspend, noDataState/FlowRepositoryModuleupdated to bind the new implRoomSubmitOutbox.toEntryrefactored fromrunCatching { ... }.getOrNull()to explicittry/catchper project rule (norunCatchingin production code — see plan rule update)Feature (
feature/search){ui, di, navigation, components}subpackagesSearchViewModelextendsBaseViewModel<SearchScreenState, Nothing, SearchAction>(MVI)SearchScreenStateownsresultState: ScreenState<List<SearchedEntity>>—SearchResultState(legacy 4-state sealed) deletedSearchScreenusesScreenContent<List<SearchedEntity>>with customemptyslot that distinguishes "initial blank screen" from "no results for query"SearchScreenEvent→SearchAction(UpdateSearchText / ClearSearchText / UpdateSelectedFilter / UpdateExactMatch / PerformSearch)SearchNavigation.ktsplit intoSearchRoute.kt(the@Serializableroute) +SearchNavigation.kt(theNavGraphBuilder+navigateToSearchScreenextension)contentDescriptionstrings moved from inline literals tocomposeResources(feature_search_a11y_*)@Previewfunctions onSearchScreen(Loading / InitialEmpty / NoResults / Content / Error)build.gradle.ktsdeps:projects.core.data+projects.core.uionly — noprojects.coreBase.*(per the hard rule pinned in the plan this wave)Plan rules pinned this wave
Two HARD RULES added to
plan-layer/.../store5-adoption/C-features.md:runCatchingin production code — it swallowsCancellationExceptionand breaks structured concurrency. Use explicittry/catch(re-throwingCancellationException) for read flows;SubmitHandler<R>handles mutation flows internally.projects.coreBase.*— onlyprojects.core.*. Thecore.*modules are the project's facade over the framework;core-base/*deps flow transitively viaapi(). If a feature needs atemplate.core.base.*type, add a typealias / pass-through incore/<x>/and re-run the wave.Acceptance (G-C wave 3)
SearchApiexists incore.network.search.api.*, registered inNetworkModuleSearchRepositorylives incore.data.search.*, noFlow<DataState<*>>SearchService.searchResources+DataManagerSearch.searchResourcesare@DeprecatedSearchViewModelextendsBaseViewModel<S, E, A>and ownsScreenState<List<SearchedEntity>>SearchScreenusesScreenContentand renders Loading / Empty / Error / Contentfeature/searchhas 4@Previewstates covering Loading / InitialEmpty / NoResults / Content / Errorfeature/search/build.gradle.ktshas zeroprojects.coreBase.*referencescomposeResources920c6810) ← prior wave is containedWave protocol
This branch was cut from
upstream/store5Migrationafter Wave 2 (PR #2687) merged, per the HARD RULE inC-features.md("ALWAYS syncstore5Migrationbefore starting a new wave").Next
plan-layer/.../active/store5-adoption/PLAN.mdfor the registry).@DeprecatedSearchService.searchResources/DataManagerSearch.searchResources/ the legacySearchUiState(already deleted in this PR) once every wave has landed.Test plan
androidApp+iosApp(smoke test)SearchScreenfrom drawer → empty state (no input) renders nothing in the result area