Add deep linking for pool positions and payment requests - #121
Merged
Conversation
Custom-scheme only: https://nodus.fi App Links would need a hosted .well-known/assetlinks.json for domain verification, which is out of scope for this repo.
Sealed class with two recognized link kinds: PoolPositionLink and PaymentRequestLink (validated address + positive amount). Parsing is a pure function so it's unit testable without a real platform channel.
Buffers every parsed link since construction and replays them once to the first subscriber, then streams new ones live. Meant to be constructed early (in main()) so app_links can capture a cold-start launch link, while actually acting on it is deferred to whenever something chooses to subscribe.
Read-only preview of a nodusprotocol://pay request -- amount, token, requester address, copy-to-clipboard. This app has no peer-to-peer send flow yet, so it deliberately doesn't pretend to fulfill the request, only display it.
Reacts to deep links by pushing PoolDetailScreen or PaymentRequestScreen. Deliberately meant to be placed as AppLockGate's child rather than wrapping it: this widget's subscription doesn't exist until the gate has actually unlocked, so a deep link can never navigate past the biometric lock -- any link that arrives before then just sits in DeepLinkService's buffer.
Threaded through AMMobileApp so it's constructed before runApp(), letting app_links capture the link that launched the app from a cold start. Not yet wired into the widget tree.
Placed as AppLockGate's child so its subscription -- and any navigation it triggers -- only ever exists once the app is unlocked.
Covers scheme/host rejection, a well-formed pool link, a well-formed payment request, and payment requests rejected for a missing parameter, invalid address, non-numeric amount, or zero/negative amount.
Covers pushing PaymentRequestScreen for a payment request link and staying put with no events. The pool-position path isn't covered here since it reads PoolProvider, which has no test seam (always constructs a real, network-backed PoolService) -- reviewed by hand instead.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements "Implement deep linking for sharing pool positions and payment requests." Custom URI scheme only (
nodusprotocol://) — see note onhttps://nodus.fiApp Links below.nodusprotocol://pool— opens the (single) AMM pool's detail screen. This app has exactly one pool (PoolProvideralways loads a singlePoolStats, no multi-pool routing exists), so there's no pool ID to thread through.nodusprotocol://pay?to=<address>&amount=<decimal>&token=<symbol>— opens a newPaymentRequestScreenshowing the request (amount, token, requester address, copy-to-clipboard). This app has no peer-to-peer send flow yet, so this deliberately doesn't pretend to fulfill the request — it's a real, complete "view a payment request" feature, not a stub for a send flow that doesn't exist.AppDeepLink/parseDeepLink: a sealed-class model and pure parsing function, validating the address via the existingValidation.stellarPublicKeyand rejecting non-positive/non-numeric amounts. Fully unit tested.DeepLinkService: wrapsapp_links, constructed early inmain()(beforerunApp) so it can capture a cold-start launch link, buffering every link it receives and replaying them once to the first subscriber.DeepLinkListener: the piece that actually reacts to links and navigates. Security-relevant design choice: it's placed asAppLockGate'schildrather than wrapping it, so its subscription — and any navigation it triggers — doesn't exist in the widget tree until the app has actually unlocked. A deep link arriving while the app is locked just sits inDeepLinkService's buffer instead of being able to navigate past the biometric lock.nodusprotocol://intent-filter.https://nodus.fiApp Links would need a hosted.well-known/assetlinks.jsonfor domain verification, which is out of scope for this repo (no server-side access).Test plan
parseDeepLinkunit tests: scheme/host rejection, well-formed pool + payment links, payment requests rejected for missing param / invalid address / non-numeric or non-positive amountDeepLinkListenerwidget tests: pushesPaymentRequestScreenfor a payment link, stays put with no events. The pool-position path isn't covered by a widget test — it readsPoolProvider, which always constructs a real network-backedPoolServicewith no test seam — reviewed by hand instead.flutter analyze/flutter testlocally — same sandbox limitation as the last two PRs. Please watch CI closely.Note on account access
This PR is from my personal fork rather than a branch on the org repo — my org access was revoked partway through this session (confirmed via
gh api repos/.../permissions:push: falsewhere it wastrueearlier). Everything else about the change is unaffected.