From 4bb26fedcbc52956eca66df19bda779c8c802c92 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 01:14:50 +0000 Subject: [PATCH 1/3] fix(dashboard): Keep mobile conversations at latest Co-Authored-By: Pierre Massat --- .../client/conversations/ConversationPage.tsx | 19 ++++++++++++++++++- .../conversations/transcriptBottomPinning.ts | 16 ++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/packages/junior-dashboard/src/client/conversations/ConversationPage.tsx b/packages/junior-dashboard/src/client/conversations/ConversationPage.tsx index 98d494093..2a7507c01 100644 --- a/packages/junior-dashboard/src/client/conversations/ConversationPage.tsx +++ b/packages/junior-dashboard/src/client/conversations/ConversationPage.tsx @@ -1,4 +1,4 @@ -import { useCallback, useRef, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import type { ConversationDetailReport, ConversationFeed, @@ -268,6 +268,23 @@ function ConversationReplyFooter(props: { cancelPendingMessagesRef.current = cancelPendingMessages; const onPinRequestRef = useRef(props.onPinRequest); onPinRequestRef.current = props.onPinRequest; + const pendingMessageVersion = props.pendingMessages + .map((message) => + [ + message.inboundMessageId, + message.messageId, + message.clientStatus, + message.delivery, + ].join(":"), + ) + .join("|"); + const pendingMessageVersionRef = useRef(pendingMessageVersion); + useEffect(() => { + if (pendingMessageVersionRef.current === pendingMessageVersion) return; + pendingMessageVersionRef.current = pendingMessageVersion; + if (!window.matchMedia("(max-width: 767px)").matches) return; + onPinRequestRef.current(); + }, [pendingMessageVersion]); const onSubmit = useCallback( async (message: string, idempotencyKey: string) => { await appendMessageRef.current.mutateAsync({ diff --git a/packages/junior-dashboard/src/client/conversations/transcriptBottomPinning.ts b/packages/junior-dashboard/src/client/conversations/transcriptBottomPinning.ts index 596741b4c..489a2daca 100644 --- a/packages/junior-dashboard/src/client/conversations/transcriptBottomPinning.ts +++ b/packages/junior-dashboard/src/client/conversations/transcriptBottomPinning.ts @@ -13,6 +13,7 @@ import type { ConversationTranscript, TranscriptViewPart } from "../types"; import { conversationTranscriptMessages } from "./eventTranscript"; const BOTTOM_PROXIMITY_PX = 96; +const MOBILE_MEDIA_QUERY = "(max-width: 767px)"; const USER_SCROLL_DELTA_PX = 2; type ScrollRoot = HTMLElement | Window; @@ -166,6 +167,7 @@ export function usePinnedTranscriptBottom(input: { const previousScrollTopRef = useRef(null); const prependSnapshotRef = useRef(null); const pinRequestVersionRef = useRef(input.pinRequestVersion ?? 0); + const versionRef = useRef(input.version); const programmaticScrollGenerationRef = useRef(0); const [following, setFollowing] = useState(false); const [hasPendingUpdate, setHasPendingUpdate] = useState(false); @@ -338,6 +340,20 @@ export function usePinnedTranscriptBottom(input: { measurePosition("measure"); }, [measurePosition, scrollToBottom]); + useBrowserLayoutEffect(() => { + if (versionRef.current === input.version) return; + versionRef.current = input.version; + if ( + typeof window === "undefined" || + !window.matchMedia(MOBILE_MEDIA_QUERY).matches + ) { + return; + } + setFollowingIntent(true); + setHasPendingUpdate(false); + scrollToBottom("auto"); + }, [input.version, scrollToBottom, setFollowingIntent]); + useBrowserLayoutEffect(() => { const wasEnabled = enabledRef.current; const shouldTrack = input.enabled || wasEnabled; From 710d334a9f64f3b24ca9bd32a1319d5d14074401 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 01:35:40 +0000 Subject: [PATCH 2/3] fix(dashboard): Keep mobile composer above queue expand Pin the transcript when the pending stack resizes, keep the composer outside the queue scroller, and anchor the reply footer to the bottom of the mobile conversation shell. Co-Authored-By: Pierre Massat --- .../client/conversations/ConversationPage.tsx | 54 +++++++++++-------- .../conversations/PendingMailboxStack.tsx | 22 +++++++- .../conversations/transcriptBottomPinning.ts | 4 ++ 3 files changed, 57 insertions(+), 23 deletions(-) diff --git a/packages/junior-dashboard/src/client/conversations/ConversationPage.tsx b/packages/junior-dashboard/src/client/conversations/ConversationPage.tsx index 2a7507c01..a57b0ac47 100644 --- a/packages/junior-dashboard/src/client/conversations/ConversationPage.tsx +++ b/packages/junior-dashboard/src/client/conversations/ConversationPage.tsx @@ -82,10 +82,10 @@ export function ConversationPage(props: { }, []); return ( -
+
@@ -334,29 +334,39 @@ function ConversationReplyFooter(props: { ), ); + const onMailboxLayoutChange = useCallback(() => { + if (!window.matchMedia("(max-width: 767px)").matches) return; + onPinRequestRef.current(); + }, []); + return ( -
- {props.live ? ( -
-