137 lines
4.6 KiB
TypeScript
137 lines
4.6 KiB
TypeScript
import { useEffect, useRef, useCallback } from 'react';
|
|
import { useShallow } from 'zustand/react/shallow';
|
|
import { useAppStore, useAppStoreApi } from '@/components/state-provider';
|
|
import { getWebSocketClient } from '@/lib/ws/connection';
|
|
import type { GitSnapshot } from '@/lib/state/slices/session-runtime/types';
|
|
|
|
/**
|
|
* Hook to get the current git status for a session.
|
|
* Git status is populated via WebSocket from git snapshot updates.
|
|
* On mount, fetches the latest snapshot to populate initial status.
|
|
* For historical snapshots, use useSessionGitSnapshots hook.
|
|
*/
|
|
export function useSessionGitStatus(sessionId: string | null) {
|
|
// Use shallow comparison to prevent re-renders when object reference changes but values are the same
|
|
const gitStatus = useAppStore(
|
|
useShallow((state) =>
|
|
sessionId ? state.gitStatus.bySessionId[sessionId] : undefined
|
|
)
|
|
);
|
|
const connectionStatus = useAppStore((state) => state.connection.status);
|
|
const storeApi = useAppStoreApi();
|
|
const prevSessionIdRef = useRef<string | null>(null);
|
|
const hasFetchedRef = useRef(false);
|
|
|
|
// Stable reference to fetch function
|
|
const fetchSnapshot = useCallback(async (sid: string) => {
|
|
const client = getWebSocketClient();
|
|
if (!client) return;
|
|
|
|
const setGitStatus = storeApi.getState().setGitStatus;
|
|
|
|
try {
|
|
const response = await client.request<{ snapshots?: GitSnapshot[] }>(
|
|
'session.git.snapshots',
|
|
{
|
|
session_id: sid,
|
|
limit: 1, // Only fetch the latest snapshot
|
|
}
|
|
);
|
|
|
|
if (response?.snapshots && response.snapshots.length > 0) {
|
|
const latest = response.snapshots[0];
|
|
|
|
// Extract file paths by status from the files object
|
|
const modified: string[] = [];
|
|
const added: string[] = [];
|
|
const deleted: string[] = [];
|
|
const untracked: string[] = [];
|
|
const renamed: string[] = [];
|
|
|
|
if (latest.files) {
|
|
Object.entries(latest.files).forEach(([path, fileInfo]) => {
|
|
const status = fileInfo.status?.toLowerCase();
|
|
if (status === 'modified') modified.push(path);
|
|
else if (status === 'added') added.push(path);
|
|
else if (status === 'deleted') deleted.push(path);
|
|
else if (status === 'untracked') untracked.push(path);
|
|
else if (status === 'renamed') renamed.push(path);
|
|
});
|
|
}
|
|
|
|
// Populate git status from latest snapshot
|
|
setGitStatus(sid, {
|
|
branch: latest.branch,
|
|
remote_branch: latest.remote_branch,
|
|
modified,
|
|
added,
|
|
deleted,
|
|
untracked,
|
|
renamed,
|
|
ahead: latest.ahead,
|
|
behind: latest.behind,
|
|
files: latest.files,
|
|
timestamp: latest.created_at,
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to fetch latest git snapshot:', error);
|
|
}
|
|
}, [storeApi]);
|
|
|
|
// Fetch initial status on mount if not already loaded
|
|
// Also refetch when switching to a different session or when WebSocket connects
|
|
useEffect(() => {
|
|
if (!sessionId) return;
|
|
|
|
// Wait for WebSocket to be connected before fetching
|
|
if (connectionStatus !== 'connected') return;
|
|
|
|
// Detect session change to force refetch
|
|
const sessionChanged = prevSessionIdRef.current !== null &&
|
|
prevSessionIdRef.current !== sessionId;
|
|
|
|
if (sessionChanged) {
|
|
hasFetchedRef.current = false;
|
|
}
|
|
prevSessionIdRef.current = sessionId;
|
|
|
|
// Skip fetch if we already fetched for this session (and session hasn't changed)
|
|
// When session changed, always refetch to get the latest status even if cached
|
|
if (hasFetchedRef.current) {
|
|
return;
|
|
}
|
|
|
|
// If we haven't fetched yet and status already exists (e.g., from WebSocket), skip
|
|
if (!sessionChanged) {
|
|
const currentStatus = storeApi.getState().gitStatus.bySessionId[sessionId];
|
|
if (currentStatus) {
|
|
hasFetchedRef.current = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
hasFetchedRef.current = true;
|
|
fetchSnapshot(sessionId);
|
|
}, [sessionId, connectionStatus, fetchSnapshot, storeApi]);
|
|
|
|
// Subscribe to session updates to receive git status via WebSocket
|
|
useEffect(() => {
|
|
if (!sessionId) return;
|
|
|
|
// Wait for WebSocket to be connected before subscribing
|
|
if (connectionStatus !== 'connected') return;
|
|
|
|
const client = getWebSocketClient();
|
|
if (client) {
|
|
const unsubscribe = client.subscribeSession(sessionId);
|
|
return () => {
|
|
unsubscribe();
|
|
// Don't clear git status on cleanup - keep it cached for when user switches back
|
|
};
|
|
}
|
|
}, [sessionId, connectionStatus]);
|
|
|
|
return gitStatus;
|
|
}
|