Skip to content

feat(threads): pin a thread from the row quick actions - #6001

Open
inayayousfi wants to merge 2 commits into
pingdotgg:mainfrom
inayayousfi:pin-quick-action
Open

feat(threads): pin a thread from the row quick actions#6001
inayayousfi wants to merge 2 commits into
pingdotgg:mainfrom
inayayousfi:pin-quick-action

Conversation

@inayayousfi

@inayayousfi inayayousfi commented Aug 10, 2026

Copy link
Copy Markdown

Pinning took a context menu on web and a long-press on mobile, while snooze sat one click away on both. This closes that gap on the row itself.

Web

An icon-only pin button joins the card's hover cluster, leftmost, so the order reads pin, snooze, Settle. Settle keeps the right edge it has always had and snooze keeps its slot, so neither learned target moves.

It shows only on unpinned cards. A pinned card already carries its own unpin glyph beside the project label, so a second control there would be two ways to say one thing. Slim rows are untouched: the cluster lives in the card branch only.

The button reuses the sidebar's existing pin command, so it stays silent on success and toasts only on failure, exactly as the context menu entry does.

Mobile

The swipe tray took a primaryAction / secondaryAction prop pair and a width constant fixed at two slots. It now takes an ordered actions list, so it can hold three buttons.

The inbox card's tray becomes Pin, Settle, Snooze. Pin is appended on the inside, so Settle and Snooze keep their existing distance from the screen edge and no learned gesture moves. A full swipe still commits the lifecycle action and never pins.

Pin rides the card only. Slim rows are settled or snoozed, and pinning one clears that state server-side, which is too big a jump for a swipe.

Risk, and what bounds it

The tray is shared with the v1 thread list and the archive screen, neither of which gains an action here. The reveal stagger used to be two hardcoded ranges; it is now a formula over the slot index whose one and two action cases reproduce those exact numbers. thread-swipe-layout.test.ts pins them down, so a future change to the formula cannot silently shift the two lists that were not meant to move.

The layout maths moved into thread-swipe-layout.ts so it can be asserted without loading React Native.

Verification

  • tsc --noEmit clean in both apps
  • vp test run in mobile: 102 files, 633 tests passing
  • vp test run --project unit in web: 221 files, 2002 tests passing
  • vp lint clean

🤖 Generated with Claude Code


Note

Medium Risk
Touches the shared mobile swipe component and every list that uses it, but two-action geometry is regression-tested and pin/full-swipe behavior is explicitly gated so existing gestures should stay stable.

Overview
Pin is exposed on the thread row without opening a menu: web sidebar cards get a leftmost pin icon in the hover cluster (only when pinning is supported and the thread is not already pinned), wired to the existing attemptPin flow. On mobile Thread List v2 card rows, swipe reveals Pin · Settle · Snooze when eligible—pin is inserted innermost so settle/snooze stay at the same distance from the screen edge and a full swipe still commits the lifecycle action, not pin.

ThreadSwipeable no longer takes primaryAction, secondaryAction, onDelete, or threadTitle. Callers pass an ordered actions array (each action carries its own backgroundColor) plus optional fullSwipeIndex. Delete is built via deleteSwipeAction so label/color/a11y stay consistent. Tray width and reveal stagger move to thread-swipe-layout.ts, with thread-swipe-layout.test.ts locking the one- and two-action geometry so archive and v1 lists do not shift when a third slot exists.

resolveThreadListV2SwipeActions now returns actions and fullSwipeIndex (and still secondary for snooze menu wiring); pinning rules are covered by new unit tests.

Reviewed by Cursor Bugbot for commit f21a3fe. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add pin quick action to thread row swipe tray and sidebar

  • Adds a Pin action to the swipe tray in V2 thread list rows when pinning is supported, the variant is 'card', and the thread is not already pinned; the pin action appears as the innermost (rightmost) swipe button.
  • Adds a 'Pin thread' quick-action button to sidebar thread rows in Sidebar.tsx, visible only on supported servers for unpinned threads.
  • Redesigns the ThreadSwipeable API to accept an ordered actions array and a fullSwipeIndex instead of separate primary/secondary props, allowing an arbitrary number of swipe actions with consistent per-action background colors.
  • Extracts swipe tray geometry into a new thread-swipe-layout.ts module and standardizes action colors via new exports in thread-swipe-actions.tsx.
  • Behavioral Change: all existing swipe rows (archived, v1 list, v2 list) are migrated to the new actions array API; onDelete, primaryAction, secondaryAction, and threadTitle props are removed from ThreadSwipeable.

Macroscope summarized f21a3fe.

Pinning took a context menu on web and a long-press on mobile, while
snooze sat one click away on both. Close that gap on the row itself.

Web: an icon-only pin button joins the card's hover cluster, leftmost, so
the order reads pin, snooze, Settle. Settle keeps the right edge and
snooze keeps its slot, so neither learned target moves. It shows only on
unpinned cards, since a pinned card already carries its own unpin glyph
beside the project label.

Mobile: the swipe tray takes an ordered action list instead of a
primary/secondary prop pair, so it can hold three buttons. The inbox
card's tray becomes Pin, Settle, Snooze, with pin appended on the inside
so Settle and Snooze keep their distance from the screen edge. A full
swipe still commits the lifecycle action and never pins.

The reveal stagger is now a formula over the slot index. Its one and
two action cases reproduce the numbers the tray shipped with, so the v1
thread list and the archive screen animate exactly as before. A test
pins those numbers down.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bead6d32-0438-4dfa-915b-f27e05c6db21

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Aug 10, 2026
@macroscopeapp

macroscopeapp Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces a new feature (pin from quick actions) and refactors the swipe action system architecture. New user-facing capabilities and structural changes to core interaction patterns warrant human review.

You can customize Macroscope's approvability policy. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant