Meshtastic-Android/docs/agent-playbooks/common-practices.md
James Rich d076361c55
refactor: migrate core UI and features to KMP, adopt Navigation 3 (#4750)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
2026-03-10 17:29:47 +00:00

52 lines
3.3 KiB
Markdown

# Common Practices Playbook
This document captures discoverable patterns that are already used in the repository.
## 1) Module and layering boundaries
- Keep domain logic in KMP modules (`commonMain`) and keep Android framework wiring in `app` or `androidMain`.
- Use `core:*` for shared logic, `feature:*` for user-facing flows, and `app` for Android entrypoints and integration wiring.
- Example: `feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt` contains shared ViewModel logic, while `app/src/main/kotlin/org/meshtastic/app/messaging/AndroidMessageViewModel.kt` provides the Android/Koin wrapper.
## 2) Dependency injection conventions (Koin)
- Use Koin annotations (`@Module`, `@ComponentScan`, `@KoinViewModel`, `@KoinWorker`) and keep DI wiring discoverable from `app`.
- Example app scan module: `app/src/main/kotlin/org/meshtastic/app/MainKoinModule.kt`.
- Example app startup and module registration: `app/src/main/kotlin/org/meshtastic/app/MeshUtilApplication.kt`.
- Ensure feature/core modules are included in the app root module: `app/src/main/kotlin/org/meshtastic/app/di/AppKoinModule.kt`.
- Prefer DI-agnostic shared logic in `commonMain`; inject from Android wrappers.
## 3) Navigation conventions (Navigation 3)
- Use Navigation 3 types (`NavKey`, `NavBackStack`, entry providers) instead of legacy controller-first patterns.
- Example graph using `EntryProviderScope<NavKey>` and `backStack.add/removeLastOrNull`: `app/src/main/kotlin/org/meshtastic/app/navigation/SettingsNavigation.kt`.
- Example feature flow using `rememberNavBackStack` and `NavDisplay<NavKey>`: `feature/intro/src/androidMain/kotlin/org/meshtastic/feature/intro/AppIntroductionScreen.kt`.
## 4) UI and resources
- Keep shared dialogs/components in `core:ui` where possible.
- Put localizable UI strings in Compose Multiplatform resources: `core/resources/src/commonMain/composeResources/values/strings.xml`.
- Use `stringResource(Res.string.key)` from shared resources in feature screens.
- Example usage: `feature/node/src/androidMain/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt`.
## 5) Platform abstraction in shared UI
- Use `CompositionLocal` providers in `app` to inject Android/flavor-specific UI behavior into shared modules.
- Example provider wiring in `MainActivity`: `app/src/main/kotlin/org/meshtastic/app/MainActivity.kt`.
- Example abstraction contract: `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/util/MapViewProvider.kt`.
## 6) I/O and concurrency in shared code
- In `commonMain`, use Okio streams (`BufferedSource`/`BufferedSink`) and coroutines/Flow.
- For ViewModel state exposure, prefer `stateInWhileSubscribed(...)` in shared ViewModels and collect in UI with `collectAsStateWithLifecycle()`.
- Example shared extension: `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/viewmodel/ViewModelExtensions.kt`.
- Example Okio usage in shared domain code:
- `core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ImportProfileUseCase.kt`
- `core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ExportDataUseCase.kt`
## 7) Namespace and compatibility
- New code should use `org.meshtastic.*`.
- Keep compatibility constraints where required (notably legacy app ID and intent signatures for external integration).