Skip to content

feat: show a shareable link on takeable orders - #669

Closed
21Mill wants to merge 1 commit into
MostroP2P:mainfrom
21Mill:feat/order-share-link
Closed

feat: show a shareable link on takeable orders#669
21Mill wants to merge 1 commit into
MostroP2P:mainfrom
21Mill:feat/order-share-link

Conversation

@21Mill

@21Mill 21Mill commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Depends on #673

#673 fixes the receiving end (#670): until it lands, a recipient opening one of these links with the app closed does not reliably reach the order. Merge order: #673 → this.

Closes #451

Problem

There is no way to share a specific order from the app. Someone who creates an order and waits for a taker can only read its id aloud, leaving the other side to look it up by hand.

What makes this odd is that half the work was already there, pointing the other way: the app knows how to open order links but never produces one.

  • The mostro: scheme is registered in android/app/src/main/AndroidManifest.xml.
  • NostrUtils.isValidMostroUrl / parseMostroUrl define the format: mostro:<order-id>?relays=wss://r1,wss://r2&mostro=<pubkey-hex>, where relays is mandatory and mostro is optional but must be 64-char hex.
  • DeepLinkService.processMostroLink resolves the order from those relays, and DeepLinkHandler even switches Mostro instance when the link points at a different one.

So no format had to be invented and nothing had to be agreed with other clients — only the encoder was missing.

Change

NostrUtils.buildMostroUrl, placed right next to the two functions that read the same format, so all three stay together:

static String? buildMostroUrl({
  required String orderId,
  required List<String> relays,
  String? mostroPubkey,
  int maxRelays = 3,
})

It keeps only relays carrying a WebSocket scheme (same check isValidMostroUrl applies), drops duplicates, caps the list so the link stays pasteable, and emits the instance pubkey only when it passes the exact validation parseMostroUrl uses. It returns null when no usable relay is left, since the format cannot resolve an order without one. The invariant: anything this builds is something the project's own parser accepts and reads back unchanged.

OrderShareLinkCard shows the link with a copy button, following the shape of the OrderIdCard beside it, and renders nothing when the link is null. It reads the active relays and instance pubkey from settingsProvider.

The card on a sell order's detail screen, below the order id:

Order link card showing a mostro: link below the order id card

Copying puts the whole link on the clipboard — three relays and the instance pubkey — even though the card itself elides it:

Copy confirmation with the system clipboard preview showing the full link, relays and mostro pubkey included

It appears on the order book's detail screen, and in My Trades only while the order is still pending: a link to a trade already under way is of no use to whoever opens it.

Adds orderLinkLabel and orderLinkCopiedMessage to the six locales. No OrderIdCard refactor and no new automation id, so the automation contract is untouched.

Copying is the whole of what #451 asked for, so that is all this does. Offering the system share sheet as well is tracked separately in #671.

Tests

  • test/shared/utils/deep_link_parsing_test.dart — new buildMostroUrl group: round-trip through isValidMostroUrl + parseMostroUrl; non-WebSocket relays dropped; duplicates removed; relay count capped; null with no usable relay and with no order id; pubkey accepted upper-case or 0x-prefixed, and omitted when malformed rather than emitted for the parser to discard.
  • test/shared/widgets/order_cards_test.dart — the card renders the full link, the copy button puts exactly that string on the clipboard (asserted through SystemChannels.platform), and nothing renders when no relay is configured.

Test plan

  • flutter gen-l10n — no untranslated messages
  • flutter analyze on lib/shared and test/shared — no issues
  • flutter test test/shared/ — 203 passing
  • Full flutter test — 899 passing, 11 pre-existing failures (stale test/mocks.mocks.dart, repo issue Dart run build_runner build fails on Flutter 3.44.0, source_gen 3.1.0 incompatible with analyzer 8.x #606; same list before and after this branch)
  • On device (Android 16, debug build): the card renders under the order id; copying shows the confirmation and the system clipboard preview shows the whole link — three relays plus &mostro=…
  • End-to-end: firing that generated link as a VIEW intent opens that exact order, so the encoder is validated against the real parser and not only in tests
  • CI green

Unrelated pre-existing bug found while testing

Opening a mostro: link while the app is not in the foreground crashes it:

'package:go_router/src/match.dart': Failed assertion: line 245 pos 12:
'uriPathToCompare.startsWith(newMatchedLocationToCompare)': is not true.

Reproduced by leaving an order detail screen with Back until the app drops out of the foreground, then opening the link. I built and installed main (c3c2d7a7) without this branch and it fails identically, so it is not introduced here — being an assert, release builds would not show the red screen but the navigation is just as broken.

It is worth flagging alongside this PR: until now almost nobody had a mostro: link to open, and this feature hands one to every user. I will file it separately.

The app could already open order links but never produced one, so sharing
an order meant reading its id aloud and having the other side look it up
by hand.

The `mostro:` scheme is registered in the manifest, NostrUtils parses
`mostro:<order-id>?relays=...&mostro=<pubkey>`, and DeepLinkService
resolves the order from those relays, switching Mostro instance when the
link points at a different one. Only the encoder was missing.

NostrUtils.buildMostroUrl now writes that same format, next to the two
functions that read it. It keeps only relays carrying a WebSocket scheme,
drops duplicates, caps the list so the link stays pasteable, and emits the
instance pubkey only when it passes the check the parser applies, so a
link this builds is always one the parser accepts.

OrderShareLinkCard shows the link with a copy button, following the shape
of the OrderIdCard beside it, and renders nothing when no relay can
resolve the order. It appears on the order book's detail screen and, in My
Trades, only while the order is still pending: a link to a trade already
under way is of no use to whoever opens it.

Adds orderLinkLabel and orderLinkCopiedMessage to the six locales.
@21Mill

21Mill commented Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

Filed the pre-existing deep-link crash mentioned above as #670, with the reproduction and the check against main.

@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@21Mill, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 16 minutes

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

Wait for the limit to reset, then comment @coderabbitai review or push new commits to the PR.

An organization admin can change what happens after included review limits in Billing.

How do review limits work?

CodeRabbit enforces per-developer PR review limits within each organization.

For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6f3cb9dd-c688-4638-ab9a-0f34add7b77d

📥 Commits

Reviewing files that changed from the base of the PR and between c3c2d7a and b0b1f1a.

📒 Files selected for processing (12)
  • lib/features/order/screens/take_order_screen.dart
  • lib/features/trades/screens/trade_detail_screen.dart
  • lib/l10n/intl_de.arb
  • lib/l10n/intl_en.arb
  • lib/l10n/intl_es.arb
  • lib/l10n/intl_fr.arb
  • lib/l10n/intl_it.arb
  • lib/l10n/intl_pt.arb
  • lib/shared/utils/nostr_utils.dart
  • lib/shared/widgets/order_cards.dart
  • test/shared/utils/deep_link_parsing_test.dart
  • test/shared/widgets/order_cards_test.dart

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ermeme ermeme Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: Request changes

The link builder/card implementation is small and mostly looks sound: the generated URLs round-trip through the existing parser, the new ARB keys are present in the touched locales, and the widget copy path is covered.

I do not think this should merge as-is while it closes #451, though. The feature creates mostro: links specifically so another user can open the order directly, but the PR body documents that opening one while the app is not foregrounded currently crashes/does not navigate (#670). That is the common receive path for links shared through Telegram/Signal/etc. If this lands before the receiver-side fix, the app starts handing users links that are known to fail in the scenario this feature is meant to support.

Please make this PR explicitly depend on a merged fix for #670 (or include the receiver-side fix here) before closing #451. Otherwise the UI copy action works, but the shared link is not reliably usable by the recipient.

Verification performed:

  • Reviewed current head b0b1f1ab4e0514d530d969678cfd127ac8790042 against base c3c2d7a7b318e70b2d7555f43d49ef1bfc009624.
  • Read the PR body, linked issue #451, PR comments, and current CI state.
  • Parsed the touched ARB locale files with duplicate-key detection; the new keys are present. The only parity drift found is pre-existing myActiveTrades extra in intl_es.arb.
  • Ran git diff --check on the changed files successfully.
  • Could not run flutter test locally because this environment does not have flutter on PATH; GitHub's build check is currently green for this head.

@21Mill

21Mill commented Aug 23, 2026

Copy link
Copy Markdown
Contributor Author

Agreed — handing out links that the recipient cannot open is the wrong order to ship this in.

The PR body now says so explicitly: this depends on #673, which fixes the receiving side (#670). Merge order is #673 → this one. No code change here; the dependency was already argued in the body, it just was not stated as a blocking one.

@grunch

grunch commented Aug 24, 2026

Copy link
Copy Markdown
Member

Hi @21Mill this kind of UI new features needs to be discussed before implementing them, in fact we are not working on new UI features in this app, new features are going to the new app https://github.com/MostroP2P/app

please redirect this efforts to create new issues and then PRs on https://github.com/MostroP2P/app and your contributions can help us to have the new app faster

@grunch grunch closed this Aug 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Display order link with copy option in order details

2 participants