Files
buzz/mobile/lib
Tom Brow a7a534d476 mobile: long-press a message link for open, copy and share (#9135)
Holding a link in a message used to open the whole-message actions
sheet, so there was no way to copy or share just the link. Now a hold on
a link opens a small **Link** sheet showing the URL with **Open link**,
**Copy link** and **Share link**. Web links share as URLs so the share
sheet can preview them; other links (such as `buzz://`) share as text.
Open link appears only for destinations the app can open (web and
`buzz://` links), so a `mailto:` link offers Copy and Share. Repo, PR
and issue chips, which have no mobile destination, also offer Copy and
Share on hold. While a finger is down, the link gets a light tint behind
it, the same kind of press highlight the message row paints. A hold
anywhere else in the message still opens the message actions, and a tap
on a link still opens it directly.

The link gets its own long-press recognizer with the same hold deadline
as the message row. Pointer events reach the deeper recognizer first, so
at equal deadlines the link wins the arena before the message row, the
same mechanism reaction pills already use to claim their own hold.
Rendering is otherwise unchanged: the link widget tree only gains a
`RawGestureDetector`, a `Semantics` wrapper and a `DecoratedBox` for the
tint, press state lives in the link widget so only the link rebuilds,
and the sheet is built only when opened. Open reuses the existing tap
path (external browser, authenticated relay-media download, or Buzz deep
link).

### Validation

Added tests: a journey in `message_content_test.dart`, run with both iOS
(200 ms) and Android (default) hold timing, that holds surrounding text
(message menu opens, link sheet doesn't), holds the link (link sheet,
not the message menu), copies it (clipboard gets the URL), opens it from
the sheet and by a plain tap, and checks the press tint appears on touch
and clears when the finger moves away. Removing the link recognizer or
the tint makes it fail. Another test holds a `mailto:` link and checks
the sheet offers Copy and Share but not Open. Swipe-to-reply tests in
`swipe_reply_tests.dart` drag a message starting on its link and beside
it, with both hold timings, and both open the reply.

Full `just mobile-check mobile-test` passes (after rebasing onto main,
two `hosted_backup_service_test.dart` tests failed once under load and
pass on rerun; this PR doesn't touch that code).

Automated app run on an iPhone 17 Pro simulator (iOS 27) signed in to a
Builderlab community, in a private QA channel: held message text
(message actions sheet, dismissed and composer focus restored as
before), held a link (Link sheet), Copy link (toast, simulator
pasteboard holds the URL), held again and Share link (system share sheet
with the page preview). The simulator's share sheet then stopped
responding to dismissal, a known simulator share-extension quirk
unrelated to this change. After adding the tint, a second simulator run
held the link and captured the pressed state (below). Not covered:
Android device run, VoiceOver.


https://github.com/user-attachments/assets/6f5bda14-14af-450e-b021-f98ebc072f9b

| Held | Link sheet | Copied |
| --- | --- | --- |
| <img
src="https://github.com/user-attachments/assets/86262d85-b745-41b4-8c40-96372d0cec61"
width="300" alt="Link held, tinted"> | <img
src="https://github.com/user-attachments/assets/7c9fe05a-17a0-47c4-851e-e89456bc4520"
width="300" alt="Link sheet"> | <img
src="https://github.com/user-attachments/assets/d97c417a-a79b-4dd2-a43e-35ace7ea5552"
width="300" alt="Link copied"> |

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: Tom Brow <tomb@squareup.com>
Co-authored-by: Tom Brow <tomb@squareup.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 15:40:16 -08:00
..