feat(threads): pin a thread from the row quick actions - #6001
feat(threads): pin a thread from the row quick actions#6001inayayousfi wants to merge 2 commits into
Conversation
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>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
ApprovabilityVerdict: 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. |
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/secondaryActionprop pair and a width constant fixed at two slots. It now takes an orderedactionslist, 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.tspins 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.tsso it can be asserted without loading React Native.Verification
tsc --noEmitclean in both appsvp test runin mobile: 102 files, 633 tests passingvp test run --project unitin web: 221 files, 2002 tests passingvp lintclean🤖 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
attemptPinflow. 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.ThreadSwipeableno longer takesprimaryAction,secondaryAction,onDelete, orthreadTitle. Callers pass an orderedactionsarray (each action carries its ownbackgroundColor) plus optionalfullSwipeIndex. Delete is built viadeleteSwipeActionso label/color/a11y stay consistent. Tray width and reveal stagger move tothread-swipe-layout.ts, withthread-swipe-layout.test.tslocking the one- and two-action geometry so archive and v1 lists do not shift when a third slot exists.resolveThreadListV2SwipeActionsnow returnsactionsandfullSwipeIndex(and stillsecondaryfor 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
ThreadSwipeableAPI to accept an orderedactionsarray and afullSwipeIndexinstead of separate primary/secondary props, allowing an arbitrary number of swipe actions with consistent per-action background colors.actionsarray API;onDelete,primaryAction,secondaryAction, andthreadTitleprops are removed fromThreadSwipeable.Macroscope summarized f21a3fe.