From bbe91a24d8c1109824d7177dbd3df606b885182e Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Sat, 19 Sep 2026 17:32:15 -0400 Subject: [PATCH] fix(timeline): remember 'Read more' expansion per message for the session (#152) CollapsibleBody kept its collapsed flag in component state, which the timeline's windowed rendering throws away when a row unmounts. A module-level Set (never persisted, cleared on reload) now seeds the state, so scrolling away and back, or a re-render, keeps an expanded message open. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA --- .../components/message/MsgTypeRenderers.tsx | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/app/components/message/MsgTypeRenderers.tsx b/src/app/components/message/MsgTypeRenderers.tsx index e124b9cfe..e8738644c 100644 --- a/src/app/components/message/MsgTypeRenderers.tsx +++ b/src/app/components/message/MsgTypeRenderers.tsx @@ -46,14 +46,31 @@ type CollapsibleBodyProps = { eventId?: string; children: ReactNode; }; +// [Gitea #152] Messages the user expanded this session. The timeline unmounts +// rows as they scroll away, so component state alone forgot the choice; +// module-scoped (never persisted) survives remounts and clears on reload. +const expandedMessages = new Set(); + function CollapsibleBody({ eventId, children }: CollapsibleBodyProps) { const bodyRef = useRef(null); const [needsCollapse, setNeedsCollapse] = useState(false); - const [collapsed, setCollapsed] = useState(true); + const [collapsed, setCollapsedState] = useState( + () => !(eventId && expandedMessages.has(eventId)), + ); + const setCollapsed = (next: boolean | ((c: boolean) => boolean)) => { + setCollapsedState((c) => { + const value = typeof next === 'function' ? next(c) : next; + if (eventId) { + if (value) expandedMessages.delete(eventId); + else expandedMessages.add(eventId); + } + return value; + }); + }; // Reset collapsed state when the event changes (new message) useEffect(() => { - setCollapsed(true); + setCollapsedState(!(eventId && expandedMessages.has(eventId))); setNeedsCollapse(false); }, [eventId]);