Fix TOCTOU around radio reconnect

This commit is contained in:
Jack Kingsman
2026-02-23 20:37:32 -08:00
parent 1a4f57a03e
commit 47867c50b8
4 changed files with 132 additions and 11 deletions
+12 -3
View File
@@ -261,7 +261,16 @@ export function MessageList({
};
}, [messages, onResendChannelMessage]);
// Refs for scroll handler to read without causing callback recreation
const onLoadOlderRef = useRef(onLoadOlder);
const loadingOlderRef = useRef(loadingOlder);
const hasOlderMessagesRef = useRef(hasOlderMessages);
onLoadOlderRef.current = onLoadOlder;
loadingOlderRef.current = loadingOlder;
hasOlderMessagesRef.current = hasOlderMessages;
// Handle scroll - capture state and detect when user is near top/bottom
// Stable callback: reads changing values from refs, never recreated.
const handleScroll = useCallback(() => {
if (!listRef.current) return;
@@ -280,13 +289,13 @@ export function MessageList({
// Show scroll-to-bottom button when not near the bottom (more than 100px away)
setShowScrollToBottom(distanceFromBottom > 100);
if (!onLoadOlder || loadingOlder || !hasOlderMessages) return;
if (!onLoadOlderRef.current || loadingOlderRef.current || !hasOlderMessagesRef.current) return;
// Trigger load when within 100px of top
if (scrollTop < 100) {
onLoadOlder();
onLoadOlderRef.current();
}
}, [onLoadOlder, loadingOlder, hasOlderMessages]);
}, []);
// Scroll to bottom handler
const scrollToBottom = useCallback(() => {
+6 -3
View File
@@ -188,6 +188,12 @@ export function RawPacketList({ packets }: RawPacketListProps) {
}));
}, [packets]);
// Sort packets by timestamp ascending (oldest first)
const sortedPackets = useMemo(
() => [...decodedPackets].sort((a, b) => a.packet.timestamp - b.packet.timestamp),
[decodedPackets]
);
useEffect(() => {
if (listRef.current) {
listRef.current.scrollTop = listRef.current.scrollHeight;
@@ -202,9 +208,6 @@ export function RawPacketList({ packets }: RawPacketListProps) {
);
}
// Sort packets by timestamp ascending (oldest first)
const sortedPackets = [...decodedPackets].sort((a, b) => a.packet.timestamp - b.packet.timestamp);
return (
<div className="h-full overflow-y-auto p-4 flex flex-col gap-2" ref={listRef}>
{sortedPackets.map(({ packet, decoded }) => (