feat: add deep links to every screen - #1119
Conversation
Greptile SummaryAdds development-mode deep links for root navigation destinations and manually selected sheet routes.
Confidence Score: 4/5The backup-success and hardware mid-flow links should be removed or made to establish their required state before this PR is merged. Direct sheet entry can report a backup that never occurred or render hardware searching and paired states without starting discovery or establishing a paired device. Files Needing Attention: app/src/main/java/to/bitkit/ui/utils/SheetDeepLinks.kt
|
| Filename | Overview |
|---|---|
| app/src/main/java/to/bitkit/ui/utils/SheetDeepLinks.kt | Registers direct sheet entry points, including backup and hardware flow states that require work or initialization performed by earlier steps. |
| app/src/main/java/to/bitkit/ui/utils/ScreenDeepLinks.kt | Derives root-route deep links and excludes an explicit set of sensitive routes. |
| app/src/main/java/to/bitkit/ui/ContentView.kt | Replays queued links through either the sheet host or root NavController. |
| app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt | Gates screen links on development mode and stores one pending URI for UI consumption. |
| app/src/main/java/to/bitkit/ui/utils/Transitions.kt | Makes generated screen links the default for root composable and nested-graph registrations. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["bitkit://screen/... intent"] --> B["AppViewModel checks dev mode"]
B -->|disabled| C["Ignore link"]
B -->|enabled| D["pendingScreenDeepLink"]
D --> E{"SheetDeepLinks match?"}
E -->|yes| F["showSheet with nested start route"]
E -->|no| G["NavController.handleDeepLink"]
Reviews (1): Last reviewed commit: "feat: add deep links to bottom sheet scr..." | Re-trigger Greptile
There was a problem hiding this comment.
Thanks for addressing the existing backup and hardware findings; I confirmed both fixes on 511250c.
I found three blocking issues: cold-start links bypass the dev-mode gate, bitkit://screen/recovery-mode is consumed by the legacy parser before the gate, and external-confirm can reach invalid fresh state and crash. I also left two non-blocking corrections for Home registration and the adb -W guidance.
There was a problem hiding this comment.
Thanks for addressing the earlier findings. Three blocking issues remain:
- Cold-start coverage is missing at the Activity/
NavHostboundary. - Late transfer destinations allow entry without the flow state they require.
- Root-screen links route behind an active sheet.
ovitrif
left a comment
There was a problem hiding this comment.
One blocking navigation regression remains: rejected screen links dismiss an active sheet before route handling determines that the URI is invalid.
| return@LaunchedEffect | ||
| } | ||
|
|
||
| if (shouldDismissSheetForScreenLink(uri, appViewModel.currentSheet.value)) { |
There was a problem hiding this comment.
backup/show-mnemonic is denied by SheetDeepLinks, but it reaches this branch and hides the active backup intro before handleDeepLink reports the URI as unhandled. The new sheet journey therefore returns to the wallet overview instead of leaving the visible screen unchanged; any rejected screen URI can similarly dismiss the current sheet. Could we confirm that the URI matches a root destination before hiding the sheet and add regression coverage for a denied URI while a sheet is open?
Fixes #659
Refs #1118
Refs #1126
This PR adds a
bitkit://screen/...URI for every screen in the app, bottom sheets included, gated on dev mode.Description
Screens in the root graph register themselves.
ScreenDeepLinksderives the id from the route type by kebab-casing its class name andcomposableWithDefaultTransitionsattaches the link, soRoutes.RgsServeris reachable atbitkit://screen/rgs-serverand a new screen works as soon as it joins the graph. Arguments without a default become path segments, arguments with a default become query parameters.Bottom sheets needed a second mechanism. They are not in the root graph: they are
Sheetvalues rendered bySheetHost, each carrying the start route of its own nestedNavHost, soNavController.handleDeepLinkcannot reach them.SheetDeepLinksmaps a path to aSheetandContentViewhands it toshowSheetbefore falling through to the nav graph. Paths are still derived from class names via the sharedkebabIdhelper, sobitkit://screen/widgets/price-editcomes fromSheet.WidgetsplusWidgetsRoute.PriceEdit. A bare sheet id opens its first registered route.send,receive,backup,widgets,hardwareand the three sheets with no nested graph.isDevModeEnabledinAppViewModel.handleDeeplinkIntent, and holds one until the wallet is loaded;ContentViewreplays it.MainActivityonce the gated pipeline has read it, so graph creation cannot navigate outside the dev-mode gate.RecoveryMnemonic,AuthCheck,LegacyRnRecovery,LnurlChannel,CriticalUpdate,RecoveryMode, the threeExternal*routes and the six late transfer destinations, thebackup/show-mnemonicandshow-passphrasesheet routes, thepin/change-pin/disable-pinsheets, andforce-transfer. Links navigate and prefill only, and never send, broadcast or change a setting without the usual confirmation.bitcoin:,lightning:,lnurl*) on the scanner decode path, untouched.NavController, while a sheet URI still replaces the open sheet.docs/deeplinks.md.FeeNavsub-graph, and letting the confirm and liquidity screens tolerate an empty flow, are follow-ups.Sheet routes are registered by hand because not every route is a valid start destination. I fired all 51 against an emulator, then re-ran each rejected one in isolation against logcat. Thirteen are excluded, for three reasons:
SendRoute.FeeRateandFeeCustomsit insidenavigationWithDefaultTransitions<SendRoute.FeeNav>. A nested graph's child cannot be aNavHoststart destination, so both throwIllegalStateException: Cannot find startDestination ... from NavGraph.send/fee-navresolves but renders only the screen title.send/quick-paythrows onrequireNotNull(quickPayData)(SendSheet.kt:309).send/confirmand the four receive confirm/liquidity routes do not crash but render empty or zero-amount screens.send/confirmoffers "Swipe To Pay" over a payment that was never built.backup/successreports a backup that never ran, and its OK button persistsbackupVerified = true(BackupNavSheetViewModel.onSuccessContinue).backup/warningis one tap upstream of the same write.hardware/searchingwaits forever because discovery starts from the intro's continue action, andhardware/pairedclaims a paired device over default state.A test pins all thirteen, and a final sweep confirmed they no-op with the wallet overview intact and zero fatal exceptions.
Preview
N/A
QA Notes
Dev mode is on by default on debug builds (Settings ▸ Advanced ▸ Dev Settings). The app must be past onboarding.
Manual Tests
adb shell am start -a android.intent.action.VIEW -d "bitkit://screen/settings" to.bitkit.dev→ Settings opens. A mistyped id still resolvesMainActivity, because the manifest accepts everybitkit:URI by scheme, so check logcat forUnhandled screen deeplinkrather than relying on-W.bitkit://screen/send→ Send sheet on the recipient picker.bitkit://screen/widgets/price-edit→ Bitcoin Price editor.bitkit://screen/recovery-mnemonicandbitkit://screen/backup/show-mnemonic→ screen unchanged, recovery phrase never shown, logcat carriesUnhandled screen deeplink.bitkit://screen/send/fee-rateandbitkit://screen/backup/success→ screen unchanged, no crash, andbackupVerifiedis untouched.bitkit://screen/...link is ignored, on warm start and on cold start. Settings ▸ Support, tap Version five times to toggle.regression:scan abitcoin:/lightning:/lnurlURI → still decodes through the scanner path.Automated Checks
ScreenDeepLinksTest.kt: id derivation, path vs query argument placement, and that every denied route yields no link.SheetDeepLinksTest.kt: bare-id defaults, sub-route selection, case-insensitive lookup, and that sensitive and mid-flow paths resolve to null rather than falling back to the sheet default.ScreenDeepLinkDetachmentTest: graph creation stays on Home after detachment, the captured URI reaches Settings only through the replay, and a denied route is not matched.journeys/deeplinks/, including cold-start cases for dev mode off and on.just compile,just test,just lintall pass, no new detekt findings.