From f214bb9d5426e0e7cbdef0dc079a8f5d931296a4 Mon Sep 17 00:00:00 2001 From: Thomas Petersen Date: Thu, 30 Apr 2026 20:19:04 -0400 Subject: [PATCH] Polish message timeline affordances Made-with: Cursor --- .../src/features/messages/ui/DayDivider.tsx | 2 +- .../messages/ui/MessageComposerToolbar.tsx | 39 +++++------- .../messages/ui/MessageThreadSummaryRow.tsx | 7 ++- .../features/messages/ui/MessageTimeline.tsx | 13 ---- .../messages/ui/useStickyDayHeader.ts | 63 ------------------- 5 files changed, 22 insertions(+), 102 deletions(-) delete mode 100644 desktop/src/features/messages/ui/useStickyDayHeader.ts diff --git a/desktop/src/features/messages/ui/DayDivider.tsx b/desktop/src/features/messages/ui/DayDivider.tsx index 8ca39cb945..053aed6dbf 100644 --- a/desktop/src/features/messages/ui/DayDivider.tsx +++ b/desktop/src/features/messages/ui/DayDivider.tsx @@ -4,7 +4,7 @@ export function DayDivider({ label }: { label: string }) { return (
diff --git a/desktop/src/features/messages/ui/MessageComposerToolbar.tsx b/desktop/src/features/messages/ui/MessageComposerToolbar.tsx index 3206b29843..c0578a655b 100644 --- a/desktop/src/features/messages/ui/MessageComposerToolbar.tsx +++ b/desktop/src/features/messages/ui/MessageComposerToolbar.tsx @@ -244,28 +244,23 @@ export const MessageComposerToolbar = React.memo(
{extraActions} - - - - - Send (Enter) - +
); diff --git a/desktop/src/features/messages/ui/MessageThreadSummaryRow.tsx b/desktop/src/features/messages/ui/MessageThreadSummaryRow.tsx index 4d8d0a2f98..cfe8ff34fd 100644 --- a/desktop/src/features/messages/ui/MessageThreadSummaryRow.tsx +++ b/desktop/src/features/messages/ui/MessageThreadSummaryRow.tsx @@ -40,14 +40,15 @@ export function MessageThreadSummaryRow({ summary: TimelineThreadSummary; }) { const visibleDepth = Math.min(Math.max(depth, 0), 6); - const marginLeftPx = visibleDepth * 28; + const messageTextOffsetPx = 50; + const marginLeftPx = visibleDepth * 28 + messageTextOffsetPx; const depthGuideOffsets = Array.from( { length: visibleDepth }, (_, index) => 14 + index * 28, ); return ( -
+
{depthGuideOffsets.length > 0 ? (
onOpenThread(message)} diff --git a/desktop/src/features/messages/ui/MessageTimeline.tsx b/desktop/src/features/messages/ui/MessageTimeline.tsx index 0283821fe4..09e6524ccb 100644 --- a/desktop/src/features/messages/ui/MessageTimeline.tsx +++ b/desktop/src/features/messages/ui/MessageTimeline.tsx @@ -10,7 +10,6 @@ import { TooltipProvider } from "@/shared/ui/tooltip"; import { TimelineSkeleton } from "./TimelineSkeleton"; import { TimelineMessageList } from "./TimelineMessageList"; import { useLoadOlderOnScroll } from "./useLoadOlderOnScroll"; -import { useStickyDayHeader } from "./useStickyDayHeader"; import { useTimelineScrollManager } from "./useTimelineScrollManager"; type MessageTimelineProps = { @@ -121,21 +120,9 @@ export const MessageTimeline = React.memo(function MessageTimeline({ sentinelRef: topSentinelRef, }); - const stickyDayLabel = useStickyDayHeader(scrollContainerRef); - return (
- {stickyDayLabel && !isAtBottom ? ( -
-

- {stickyDayLabel} -

-
- ) : null}
, -) { - const [label, setLabel] = React.useState(null); - - const update = React.useCallback(() => { - const container = scrollContainerRef.current; - if (!container) { - return; - } - - const dividers = container.querySelectorAll( - "[data-testid='message-timeline-day-divider']", - ); - if (dividers.length === 0) { - setLabel(null); - return; - } - - const containerTop = container.getBoundingClientRect().top; - const stickyTriggerTop = containerTop + STICKY_DAY_TRIGGER_OFFSET_PX; - - // Walk dividers from the end — the last one whose top has reached the - // floating label's visual zone is the "current" day. - let current: string | null = null; - for (let i = dividers.length - 1; i >= 0; i--) { - const rect = dividers[i].getBoundingClientRect(); - if (rect.top <= stickyTriggerTop) { - current = dividers[i].getAttribute("data-day-label"); - break; - } - } - - setLabel(current); - }, [scrollContainerRef]); - - React.useEffect(() => { - const container = scrollContainerRef.current; - if (!container) { - return; - } - - container.addEventListener("scroll", update, { passive: true }); - // Run once on mount so the header appears if already scrolled. - update(); - return () => { - container.removeEventListener("scroll", update); - }; - }, [scrollContainerRef, update]); - - return label; -}