authorgravatar for 73941998+UtkarshUsername@users.noreply.github.comUtkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com> 2026-08-07 16:24:11+05:30
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-07 12:54:11+02:00
log61b51ae0e343afc42ba0424d4d74b4f9a216d620
treeb0119c2fdb42e119fe2f2f5186f4f2b9c7c4c260
parentbfc69e4b4de12cd9867972dc802936acfb219b74
signature Signed by PGP key B5690EEEBB952194

feat(web): click the pin icon to unpin a thread (#5578)


1 files changed, 38 insertions(+), 7 deletions(-)

apps/web/src/components/SidebarV2.tsx+38-7
......@@ -403,8 +403,12 @@ const SidebarV2Row = memo(function SidebarV2Row(props: {
403403 snoozeSupported: boolean;
404404 // Renders the pin glyph. Pinned cards keep the full settle/snooze quick
405405 // actions: settling clears the pin server-side, and snoozing hides the
406 // card until wake with the pin intact underneath. Pin/unpin themselves
407 // live in the context menu only.
406 // card until wake with the pin intact underneath. The glyph is also the
407 // in-row pin state cue (the pinned block has no header), so it always
408 // shows while pinned; it only becomes a clickable unpin quick-action once
409 // the pinning capability is confirmed, and stays a passive marker while
410 // the descriptor is not loaded. Pinning itself lives in the context menu.
411 pinningSupported: boolean;
408412 isPinned: boolean;
409413 // Compact wake countdown ("2h") for rows in the snoozed shelf.
410414 snoozeWakeLabelText: string | null;
......@@ -432,6 +436,7 @@ const SidebarV2Row = memo(function SidebarV2Row(props: {
432436 onUnsettle: (threadRef: ScopedThreadRef) => void;
433437 onSnooze: (threadRef: ScopedThreadRef, preset: SnoozePreset) => void;
434438 onUnsnooze: (threadRef: ScopedThreadRef) => void;
439 onUnpin: (threadRef: ScopedThreadRef) => void;
435440 onAcknowledgeWoke: (threadRef: ScopedThreadRef, visitedAt: string) => void;
436441 onChangeRequestState: (threadKey: string, state: "open" | "closed" | "merged" | null) => void;
437442}) {
......@@ -450,6 +455,7 @@ const SidebarV2Row = memo(function SidebarV2Row(props: {
450455 onThreadClick,
451456 onUnsettle,
452457 onUnsnooze,
458 onUnpin,
453459 renamingTitle,
454460 thread,
455461 variant,
......@@ -704,6 +710,14 @@ const SidebarV2Row = memo(function SidebarV2Row(props: {
704710 },
705711 [onUnsnooze, threadRef],
706712 );
713 const handleUnpinClick = useCallback(
714 (event: ReactMouseEvent) => {
715 event.preventDefault();
716 event.stopPropagation();
717 onUnpin(threadRef);
718 },
719 [onUnpin, threadRef],
720 );
707721 const handleSnoozePreset = useCallback(
708722 (preset: SnoozePreset) => {
709723 onSnooze(threadRef, preset);
......@@ -1001,11 +1015,23 @@ const SidebarV2Row = memo(function SidebarV2Row(props: {
10011015 <span className="flex-1" />
10021016 )}
10031017 {props.isPinned ? (
1004 <PinIcon
1005 aria-label="Pinned"
1006 role="img"
1007 className="size-3 shrink-0 text-muted-foreground/65"
1008 />
1018 props.pinningSupported ? (
1019 <button
1020 type="button"
1021 aria-label="Unpin thread"
1022 title="Unpin thread"
1023 onClick={handleUnpinClick}
1024 className="inline-flex cursor-pointer items-center rounded-sm text-muted-foreground/65 outline-none transition-colors hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring"
1025 >
1026 <PinIcon aria-hidden className="size-3 shrink-0" />
1027 </button>
1028 ) : (
1029 <PinIcon
1030 aria-label="Pinned"
1031 role="img"
1032 className="size-3 shrink-0 text-muted-foreground/65"
1033 />
1034 )
10091035 ) : null}
10101036 {/* The visible state owns this slot's width: status at rest,
10111037 actions on hover/keyboard focus or while the popover is open. Keeping
......@@ -3080,6 +3106,10 @@ export default function SidebarV2() {
30803106 serverConfigs.get(thread.environmentId)?.environment.capabilities
30813107 .threadSnooze === true
30823108 }
3109 pinningSupported={
3110 serverConfigs.get(thread.environmentId)?.environment.capabilities
3111 .threadPinning === true
3112 }
30833113 isPinned={section === "pinned"}
30843114 snoozeWakeLabelText={
30853115 section === "snoozed" && thread.snoozedUntil != null
......@@ -3120,6 +3150,7 @@ export default function SidebarV2() {
31203150 onUnsettle={attemptUnsettle}
31213151 onSnooze={attemptSnooze}
31223152 onUnsnooze={attemptUnsnooze}
3153 onUnpin={attemptUnpin}
31233154 onAcknowledgeWoke={acknowledgeWoke}
31243155 onChangeRequestState={handleChangeRequestState}
31253156 />