[#617][Part 2/3] Migrate to navigation 3 - Sample compose project#619
[#617][Part 2/3] Migrate to navigation 3 - Sample compose project#619eeeeaa wants to merge 8 commits intochore/#617-migrate-to-navigation-3-template-composefrom
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughRefactors navigation from a BaseDestination/NavHost system to a retained-scope Navigator with backStack, adds deep-link parsing/matching, introduces ResultEventBus and ResultEffect for inter-screen results, migrates UiModel to Kotlinx Serialization, updates DI and MainActivity wiring, and bumps dependency versions. Changes
Sequence Diagram(s)sequenceDiagram
participant Activity as MainActivity
participant Nav as Navigator
participant DLM as DeepLinkMatcher
participant REB as ResultEventBus
participant Entry as EntryProvider
Activity->>Nav: injected via DI
Activity->>REB: create & provide LocalResultEventBus
Activity->>Activity: handleNewIntent(intent)
Activity->>DLM: DeepLinkMatcher(DeepLinkRequest, pattern).match()
alt match found
DLM-->>Activity: keyBuilder + args
Activity->>Nav: goTo(keyBuilder(args))
end
Entry->>Nav: compose screen for current destination
Entry->>REB: use LocalResultEventBus for sending/getting results
sequenceDiagram
participant Source as CallingScreen
participant Nav as Navigator
participant Second as SecondScreen
participant REB as ResultEventBus
Source->>Nav: goTo(Second(id))
Nav->>Second: display SecondScreen(id)
Second->>REB: sendResult(key, value)
Second->>Nav: goBack()
Source->>REB: collect ResultEffect(key)
REB-->>Source: result received
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
Kover report for template-compose:🧛 Template - Compose Unit Tests Code Coverage:
|
| File | Coverage |
|---|---|
BaseScreen.kt |
100.00% |
BaseViewModel.kt |
85.71% |
HomeScreen.kt |
92.00% |
HomeViewModel.kt |
100.00% |
UiModel.kt |
100.00% |
Modified Files Not Found In Coverage Report:
ComponentActivityExt.kt
ComposableUtil.kt
Constants.kt
DeepLinkMatcher.kt
DeepLinkPattern.kt
DeepLinkRequest.kt
FakeNavigator.kt
FakeNavigator.kt
HomeScreenTest.kt
HomeScreenTest.kt
HomeViewModelTest.kt
KeyDecoder.kt
MainActivity.kt
MainActivityModule.kt
Navigator.kt
NavigatorImpl.kt
ResultEffect.kt
ResultEventBus.kt
SecondScreen.kt
ThirdScreen.kt
build.gradle.kts
build.gradle.kts
gradle-wrapper.properties
libs.versions.toml
Codebase cunningly covered by count Shroud 🧛
Generated by 🚫 Danger
5d653e5 to
b44c618
Compare
b44c618 to
33c4f7b
Compare
33c4f7b to
1491c46
Compare
|
Also, shouldn't we work on |
|
@kaungkhantsoe I aim to migrate to nav3 step by step else the PR size will be too big, so I will work on template-compose on a separate PR, we could determine the order later when we merge, wdyt? |
|
@eeeeaa I am good with merging the template first 🤝 |
26dbd65 to
d19f547
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/MainActivity.kt (1)
79-96: Consider extracting duplicated pop transition specs.The blocks on Line 79-87 and Line 88-96 are identical. A shared helper would reduce drift risk.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/MainActivity.kt` around lines 79 - 96, The two identical lambda blocks assigned to popTransitionSpec and predictivePopTransitionSpec should be extracted into a single helper to avoid duplication: create a reusable function or val (e.g., createPopTransitionSpec or popTransitionSpecHelper) that returns the combined slideInHorizontally togetherWith slideOutHorizontally using TWEEN_DURATION_IN_MILLIS, then replace both popTransitionSpec and predictivePopTransitionSpec usages with that helper; reference the existing symbols popTransitionSpec, predictivePopTransitionSpec, slideInHorizontally, slideOutHorizontally and TWEEN_DURATION_IN_MILLIS when implementing the change.sample-compose/app/src/main/java/co/nimblehq/sample/compose/di/modules/main/MainActivityModule.kt (1)
41-46: Use an explicit result key for this update event.On Line 41/45/62, the default
Boolean-typed key is used. This can cause cross-screen collisions when another Boolean result channel is introduced.♻️ Proposed refactor
object MainActivityModule { + private const val RESULT_KEY_SECOND_UPDATED = "result_second_updated" `@Provides` `@ActivityRetainedScoped` fun provideNavigator(): Navigator = NavigatorImpl(startDestination = Home) @@ - ResultEffect<Boolean> { isUpdated -> + ResultEffect<Boolean>(resultKey = RESULT_KEY_SECOND_UPDATED) { isUpdated -> if (isUpdated) { context.showToast(context.getString(R.string.message_updated)) } - eventBus.removeResult<Boolean>() + eventBus.removeResult<Boolean>(resultKey = RESULT_KEY_SECOND_UPDATED) } @@ onUpdateClick = { - eventBus.sendResult<Boolean>(result = true) + eventBus.sendResult<Boolean>( + resultKey = RESULT_KEY_SECOND_UPDATED, + result = true + ) navigator.goBack() } ) }Also applies to: 61-63
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sample-compose/app/src/main/java/co/nimblehq/sample/compose/di/modules/main/MainActivityModule.kt` around lines 41 - 46, The ResultEffect<Boolean> block uses the implicit Boolean-typed default key which can collide across screens; change it to use an explicit result key constant (e.g., val UPDATE_RESULT_KEY = "update_result") and pass that key into ResultEffect<Boolean>(key = UPDATE_RESULT_KEY) and into eventBus.removeResult<Boolean>(UPDATE_RESULT_KEY); update both occurrences (the ResultEffect<Boolean> where context.showToast(context.getString(R.string.message_updated)) is called and the matching eventBus.removeResult<Boolean> calls) so the result channel is uniquely namespaced.sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/main/third/ThirdScreen.kt (1)
22-23: AlignThirdScreennullability withThirdkey contract.Line 22 defines
Third.modelas non-null, but Line 27/38 accept nullable values. Tightening this keeps the API consistent and avoids accidental"null"rendering.♻️ Proposed refactor
data class Third(val model: UiModel) `@Suppress`("UnusedPrivateMember") `@Composable` fun ThirdScreen( - model: UiModel?, + model: UiModel, navigator: Navigator, viewModel: ThirdViewModel = hiltViewModel(), ) = BaseScreen( @@ private fun ThirdScreenContent( - data: UiModel?, + data: UiModel, modifier: Modifier = Modifier, ) {Also applies to: 27-39
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/main/third/ThirdScreen.kt` around lines 22 - 23, ThirdScreen currently accepts nullable models while the Third data class declares model as non-null (data class Third(val model: UiModel)); update the API to be consistent by changing ThirdScreen’s parameters and any call sites to require a non-null Third (or non-null UiModel) instead of nullable types—locate the ThirdScreen composable/function that takes a nullable Third or UiModel and make its parameter non-null, remove null checks or default "null" rendering, and ensure callers always pass a Third (or Third.model) that is non-null to match data class Third(val model: UiModel).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/MainActivity.kt`:
- Around line 44-47: The deep-link key construction currently force-unwraps
args["id"] in the DeepLinkPattern keyBuilder (and keyBuilder is invoked
unguarded elsewhere), which can crash on malformed links; update the
DeepLinkPattern entries in deepLinkPatterns so keyBuilder safely handles a
missing or non-numeric "id" (e.g., parse/validate args["id"] and return a
nullable key or a Result) and then update the place that executes keyBuilder
(the code that calls keyBuilder around lines 110-115) to check the returned
value for null/failure before using it (skip navigation or show an error)
instead of assuming a non-null Second(id = ...); reference DeepLinkPattern,
keyBuilder, Second, and deepLinkPatterns when making the changes.
In
`@sample-compose/app/src/main/java/co/nimblehq/sample/compose/util/DeepLinkMatcher.kt`:
- Around line 13-16: Validate the incoming URI's scheme and authority/host
before comparing path segments: in DeepLinkMatcher.kt, before the existing
pathSegments.size check and before the argument-matching block (the blocks
around request.pathSegments vs deepLinkPattern.pathSegments and the later 20-35
logic), first compare request.scheme (or request.uri.scheme) and
request.authority/host with deepLinkPattern's expected scheme/authority; if they
differ return null. Apply the same scheme/authority check in both the
exact-match branch that returns DeepLinkMatchResult(deepLinkPattern.keyBuilder,
mapOf()) and in the argument-extraction branch so URIs from other hosts/schemes
with identical path shapes are rejected.
---
Nitpick comments:
In
`@sample-compose/app/src/main/java/co/nimblehq/sample/compose/di/modules/main/MainActivityModule.kt`:
- Around line 41-46: The ResultEffect<Boolean> block uses the implicit
Boolean-typed default key which can collide across screens; change it to use an
explicit result key constant (e.g., val UPDATE_RESULT_KEY = "update_result") and
pass that key into ResultEffect<Boolean>(key = UPDATE_RESULT_KEY) and into
eventBus.removeResult<Boolean>(UPDATE_RESULT_KEY); update both occurrences (the
ResultEffect<Boolean> where
context.showToast(context.getString(R.string.message_updated)) is called and the
matching eventBus.removeResult<Boolean> calls) so the result channel is uniquely
namespaced.
In
`@sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/main/third/ThirdScreen.kt`:
- Around line 22-23: ThirdScreen currently accepts nullable models while the
Third data class declares model as non-null (data class Third(val model:
UiModel)); update the API to be consistent by changing ThirdScreen’s parameters
and any call sites to require a non-null Third (or non-null UiModel) instead of
nullable types—locate the ThirdScreen composable/function that takes a nullable
Third or UiModel and make its parameter non-null, remove null checks or default
"null" rendering, and ensure callers always pass a Third (or Third.model) that
is non-null to match data class Third(val model: UiModel).
In
`@sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/MainActivity.kt`:
- Around line 79-96: The two identical lambda blocks assigned to
popTransitionSpec and predictivePopTransitionSpec should be extracted into a
single helper to avoid duplication: create a reusable function or val (e.g.,
createPopTransitionSpec or popTransitionSpecHelper) that returns the combined
slideInHorizontally togetherWith slideOutHorizontally using
TWEEN_DURATION_IN_MILLIS, then replace both popTransitionSpec and
predictivePopTransitionSpec usages with that helper; reference the existing
symbols popTransitionSpec, predictivePopTransitionSpec, slideInHorizontally,
slideOutHorizontally and TWEEN_DURATION_IN_MILLIS when implementing the change.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
sample-compose/app/src/androidTest/java/co/nimblehq/sample/compose/ui/screens/main/home/HomeScreenTest.ktsample-compose/app/src/main/java/co/nimblehq/sample/compose/di/modules/main/MainActivityModule.ktsample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/MainActivity.ktsample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/main/MainDestination.ktsample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/main/home/HomeScreen.ktsample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/main/home/HomeViewModel.ktsample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/main/second/SecondScreen.ktsample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/main/third/ThirdScreen.ktsample-compose/app/src/main/java/co/nimblehq/sample/compose/util/DeepLinkMatcher.ktsample-compose/app/src/main/java/co/nimblehq/sample/compose/util/DeepLinkPattern.ktsample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/main/home/HomeScreenTest.ktsample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/main/home/HomeViewModelTest.kt
💤 Files with no reviewable changes (1)
- sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/main/MainDestination.kt
🚧 Files skipped from review as they are similar to previous changes (4)
- sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/main/second/SecondScreen.kt
- sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/main/home/HomeViewModel.kt
- sample-compose/app/src/main/java/co/nimblehq/sample/compose/util/DeepLinkPattern.kt
- sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/main/home/HomeViewModelTest.kt
There was a problem hiding this comment.
♻️ Duplicate comments (1)
sample-compose/app/src/main/java/co/nimblehq/sample/compose/util/DeepLinkMatcher.kt (1)
13-16:⚠️ Potential issue | 🟠 MajorReject cross-host/cross-scheme URIs before path matching.
At Line 13, matching starts with path shape only. A URI from a different scheme/authority but same path pattern can still match and build a key.
🛡️ Proposed fix
fun match(): DeepLinkMatchResult<T>? { + if (request.uri.scheme != deepLinkPattern.uriPattern.scheme) return null + if (request.uri.authority != deepLinkPattern.uriPattern.authority) return null if (request.pathSegments.size != deepLinkPattern.pathSegments.size) return null // exact match (url does not contain any arguments) if (request.uri == deepLinkPattern.uriPattern) return DeepLinkMatchResult(deepLinkPattern.keyBuilder, mapOf())Also applies to: 20-35
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sample-compose/app/src/main/java/co/nimblehq/sample/compose/util/DeepLinkMatcher.kt` around lines 13 - 16, The matcher currently compares only path shape (request.pathSegments vs deepLinkPattern.pathSegments) before constructing a key, allowing URIs with different schemes/hosts to match; update the matching logic in DeepLinkMatcher (the block using request.pathSegments, request.uri and deepLinkPattern.uriPattern and the later matching logic around lines ~20-35) to first compare scheme and authority/host between the incoming request and the deepLinkPattern (reject if scheme or host differ), and only then proceed to path-segment or exact uriPattern comparisons and return DeepLinkMatchResult(keyBuilder, ...).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In
`@sample-compose/app/src/main/java/co/nimblehq/sample/compose/util/DeepLinkMatcher.kt`:
- Around line 13-16: The matcher currently compares only path shape
(request.pathSegments vs deepLinkPattern.pathSegments) before constructing a
key, allowing URIs with different schemes/hosts to match; update the matching
logic in DeepLinkMatcher (the block using request.pathSegments, request.uri and
deepLinkPattern.uriPattern and the later matching logic around lines ~20-35) to
first compare scheme and authority/host between the incoming request and the
deepLinkPattern (reject if scheme or host differ), and only then proceed to
path-segment or exact uriPattern comparisons and return
DeepLinkMatchResult(keyBuilder, ...).
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
sample-compose/app/src/androidTest/java/co/nimblehq/sample/compose/ui/screens/main/home/HomeScreenTest.ktsample-compose/app/src/main/java/co/nimblehq/sample/compose/di/modules/main/MainActivityModule.ktsample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/MainActivity.ktsample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/main/MainDestination.ktsample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/main/home/HomeScreen.ktsample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/main/home/HomeViewModel.ktsample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/main/second/SecondScreen.ktsample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/main/third/ThirdScreen.ktsample-compose/app/src/main/java/co/nimblehq/sample/compose/util/DeepLinkMatcher.ktsample-compose/app/src/main/java/co/nimblehq/sample/compose/util/DeepLinkPattern.ktsample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/main/home/HomeScreenTest.ktsample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/main/home/HomeViewModelTest.kt
💤 Files with no reviewable changes (1)
- sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/main/MainDestination.kt
🚧 Files skipped from review as they are similar to previous changes (2)
- sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/main/third/ThirdScreen.kt
- sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/main/home/HomeViewModel.kt
d19f547 to
aba7ac7
Compare
aba7ac7 to
7492383
Compare
ac0c62b to
2d22049
Compare
…el-compose based on comment
…from home, and move destination to screens
#617
What happened 👀
Note
Split migration into 3 Parts
Part 1 - migrate template compose
Part 2 - migrate sample compose
Part 3 - refactor sample compose examples
Insight 📝
Reference:
Proof Of Work 📹
Sample project work the same as before migration
Screen.Recording.2569-02-11.at.12.04.04.mov
Summary by CodeRabbit
New Features
Refactor
Tests
Chores