refactor: stabilize session ID reference with activity status from server
This commit is contained in:
@@ -234,6 +234,10 @@ export const useEventStream = () => {
|
|||||||
|
|
||||||
const sessionCooldownTimersRef = React.useRef<Map<string, NodeJS.Timeout>>(new Map());
|
const sessionCooldownTimersRef = React.useRef<Map<string, NodeJS.Timeout>>(new Map());
|
||||||
const sessionActivityPhaseRef = React.useRef<Map<string, 'idle' | 'busy' | 'cooldown'>>(new Map());
|
const sessionActivityPhaseRef = React.useRef<Map<string, 'idle' | 'busy' | 'cooldown'>>(new Map());
|
||||||
|
const currentSessionIdRef = React.useRef<string | null>(currentSessionId);
|
||||||
|
React.useEffect(() => {
|
||||||
|
currentSessionIdRef.current = currentSessionId;
|
||||||
|
}, [currentSessionId]);
|
||||||
|
|
||||||
const requestSessionMetadataRefresh = React.useCallback(
|
const requestSessionMetadataRefresh = React.useCallback(
|
||||||
(sessionId: string | undefined | null) => {
|
(sessionId: string | undefined | null) => {
|
||||||
@@ -309,6 +313,21 @@ export const useEventStream = () => {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const refreshSessionActivityStatus = React.useCallback(async () => {
|
||||||
|
try {
|
||||||
|
const statusMap = await opencodeClient.getSessionStatus();
|
||||||
|
if (!statusMap) return;
|
||||||
|
|
||||||
|
Object.entries(statusMap).forEach(([sessionId, raw]) => {
|
||||||
|
if (!sessionId || !raw) return;
|
||||||
|
const status = raw as { type?: string };
|
||||||
|
const phase: 'idle' | 'busy' =
|
||||||
|
status.type === 'busy' || status.type === 'retry' ? 'busy' : 'idle';
|
||||||
|
updateSessionActivityPhase(sessionId, phase);
|
||||||
|
});
|
||||||
|
} catch { /* ignored */ }
|
||||||
|
}, [updateSessionActivityPhase]);
|
||||||
|
|
||||||
const handleEvent = React.useCallback((event: EventData) => {
|
const handleEvent = React.useCallback((event: EventData) => {
|
||||||
lastEventTimestampRef.current = Date.now();
|
lastEventTimestampRef.current = Date.now();
|
||||||
|
|
||||||
@@ -869,7 +888,7 @@ export const useEventStream = () => {
|
|||||||
console.debug('[useEventStream] Connection state:', {
|
console.debug('[useEventStream] Connection state:', {
|
||||||
isDesktopRuntime: isDesktopRuntimeRef.current,
|
isDesktopRuntime: isDesktopRuntimeRef.current,
|
||||||
hasUnsubscribe: Boolean(unsubscribeRef.current),
|
hasUnsubscribe: Boolean(unsubscribeRef.current),
|
||||||
currentSessionId,
|
currentSessionId: currentSessionIdRef.current,
|
||||||
effectiveDirectory,
|
effectiveDirectory,
|
||||||
onlineStatus: onlineStatusRef.current,
|
onlineStatus: onlineStatusRef.current,
|
||||||
visibilityState: visibilityStateRef.current,
|
visibilityState: visibilityStateRef.current,
|
||||||
@@ -877,7 +896,7 @@ export const useEventStream = () => {
|
|||||||
reconnectAttempts: reconnectAttemptsRef.current,
|
reconnectAttempts: reconnectAttemptsRef.current,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [currentSessionId, effectiveDirectory]);
|
}, [effectiveDirectory]);
|
||||||
|
|
||||||
const waitForDesktopBridge = React.useCallback(async (): Promise<boolean> => true, []);
|
const waitForDesktopBridge = React.useCallback(async (): Promise<boolean> => true, []);
|
||||||
|
|
||||||
@@ -957,14 +976,17 @@ export const useEventStream = () => {
|
|||||||
|
|
||||||
if (shouldRefresh) {
|
if (shouldRefresh) {
|
||||||
void bootstrapState('sse_reconnected');
|
void bootstrapState('sse_reconnected');
|
||||||
} else if (currentSessionId) {
|
} else {
|
||||||
setTimeout(() => {
|
const sessionId = currentSessionIdRef.current;
|
||||||
loadMessages(currentSessionId)
|
if (sessionId) {
|
||||||
.then(() => requestSessionMetadataRefresh(currentSessionId))
|
setTimeout(() => {
|
||||||
.catch((error) => {
|
loadMessages(sessionId)
|
||||||
console.warn('[useEventStream] Failed to resync messages after reconnect:', error);
|
.then(() => requestSessionMetadataRefresh(sessionId))
|
||||||
});
|
.catch((error) => {
|
||||||
}, 0);
|
console.warn('[useEventStream] Failed to resync messages after reconnect:', error);
|
||||||
|
});
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1007,7 +1029,6 @@ export const useEventStream = () => {
|
|||||||
stopStream,
|
stopStream,
|
||||||
publishStatus,
|
publishStatus,
|
||||||
checkConnection,
|
checkConnection,
|
||||||
currentSessionId,
|
|
||||||
loadMessages,
|
loadMessages,
|
||||||
requestSessionMetadataRefresh,
|
requestSessionMetadataRefresh,
|
||||||
requestSessionListRefresh,
|
requestSessionListRefresh,
|
||||||
@@ -1104,14 +1125,13 @@ export const useEventStream = () => {
|
|||||||
maybeBootstrapIfStale('visibility_restore');
|
maybeBootstrapIfStale('visibility_restore');
|
||||||
if (pendingResumeRef.current || !unsubscribeRef.current) {
|
if (pendingResumeRef.current || !unsubscribeRef.current) {
|
||||||
console.info('[useEventStream] Visibility restored, triggering soft refresh...');
|
console.info('[useEventStream] Visibility restored, triggering soft refresh...');
|
||||||
if (!isDesktopRuntimeRef.current) {
|
const sessionId = currentSessionIdRef.current;
|
||||||
sessionActivityPhaseRef.current.clear();
|
if (sessionId) {
|
||||||
}
|
loadMessages(sessionId).catch(() => {});
|
||||||
if (currentSessionId) {
|
requestSessionMetadataRefresh(sessionId);
|
||||||
loadMessages(currentSessionId).catch(() => {});
|
|
||||||
requestSessionMetadataRefresh(currentSessionId);
|
|
||||||
}
|
}
|
||||||
void loadSessions();
|
void loadSessions();
|
||||||
|
void refreshSessionActivityStatus();
|
||||||
publishStatus('connecting', 'Resuming stream');
|
publishStatus('connecting', 'Resuming stream');
|
||||||
startStream({ resetAttempts: true });
|
startStream({ resetAttempts: true });
|
||||||
}
|
}
|
||||||
@@ -1130,17 +1150,15 @@ export const useEventStream = () => {
|
|||||||
|
|
||||||
if (pendingResumeRef.current || !unsubscribeRef.current) {
|
if (pendingResumeRef.current || !unsubscribeRef.current) {
|
||||||
console.info('[useEventStream] Window focused after pause, triggering soft refresh...');
|
console.info('[useEventStream] Window focused after pause, triggering soft refresh...');
|
||||||
if (!isDesktopRuntimeRef.current) {
|
const sessionId = currentSessionIdRef.current;
|
||||||
sessionActivityPhaseRef.current.clear();
|
if (sessionId) {
|
||||||
}
|
requestSessionMetadataRefresh(sessionId);
|
||||||
|
loadMessages(sessionId)
|
||||||
if (currentSessionId) {
|
|
||||||
requestSessionMetadataRefresh(currentSessionId);
|
|
||||||
loadMessages(currentSessionId)
|
|
||||||
.then(() => console.info('[useEventStream] Messages refreshed on focus'))
|
.then(() => console.info('[useEventStream] Messages refreshed on focus'))
|
||||||
.catch((err) => console.warn('[useEventStream] Failed to refresh messages:', err));
|
.catch((err) => console.warn('[useEventStream] Failed to refresh messages:', err));
|
||||||
}
|
}
|
||||||
void loadSessions();
|
void loadSessions();
|
||||||
|
void refreshSessionActivityStatus();
|
||||||
|
|
||||||
publishStatus('connecting', 'Resuming stream');
|
publishStatus('connecting', 'Resuming stream');
|
||||||
startStream({ resetAttempts: true });
|
startStream({ resetAttempts: true });
|
||||||
@@ -1228,11 +1246,9 @@ export const useEventStream = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cooldownTimers = sessionCooldownTimersRef.current;
|
const cooldownTimers = sessionCooldownTimersRef.current;
|
||||||
const activityPhase = sessionActivityPhaseRef.current;
|
|
||||||
|
|
||||||
cooldownTimers.forEach((timer) => clearTimeout(timer));
|
cooldownTimers.forEach((timer) => clearTimeout(timer));
|
||||||
cooldownTimers.clear();
|
cooldownTimers.clear();
|
||||||
activityPhase.clear();
|
|
||||||
messageCache.clear();
|
messageCache.clear();
|
||||||
|
|
||||||
pendingResumeRef.current = false;
|
pendingResumeRef.current = false;
|
||||||
@@ -1249,7 +1265,6 @@ export const useEventStream = () => {
|
|||||||
publishStatus('idle', null);
|
publishStatus('idle', null);
|
||||||
};
|
};
|
||||||
}, [
|
}, [
|
||||||
currentSessionId,
|
|
||||||
effectiveDirectory,
|
effectiveDirectory,
|
||||||
trackMessage,
|
trackMessage,
|
||||||
resolveVisibilityState,
|
resolveVisibilityState,
|
||||||
@@ -1260,6 +1275,7 @@ export const useEventStream = () => {
|
|||||||
loadMessages,
|
loadMessages,
|
||||||
requestSessionMetadataRefresh,
|
requestSessionMetadataRefresh,
|
||||||
updateSessionActivityPhase,
|
updateSessionActivityPhase,
|
||||||
|
refreshSessionActivityStatus,
|
||||||
shouldHoldConnection
|
shouldHoldConnection
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user