237 lines
5.9 KiB
TypeScript
237 lines
5.9 KiB
TypeScript
import { useCallback } from "react";
|
|
import { useMutation, useQuery, useQueryClient, type QueryClient } from "@tanstack/react-query";
|
|
import { useSessionStore } from "@/stores/session-store";
|
|
|
|
export const ARCHIVE_AGENT_PENDING_QUERY_KEY = ["archive-agent-pending"] as const;
|
|
|
|
export interface ArchiveAgentInput {
|
|
serverId: string;
|
|
agentId: string;
|
|
}
|
|
|
|
type ArchiveAgentPendingState = Record<string, true>;
|
|
|
|
interface SetAgentArchivingInput extends ArchiveAgentInput {
|
|
queryClient: QueryClient;
|
|
isArchiving: boolean;
|
|
}
|
|
|
|
interface IsAgentArchivingInput extends ArchiveAgentInput {
|
|
queryClient: QueryClient;
|
|
}
|
|
|
|
interface AgentsListQueryData {
|
|
entries?: Array<{ agent?: { id?: string | null } | null } | null>;
|
|
}
|
|
|
|
function toArchiveKey(input: ArchiveAgentInput): string {
|
|
const serverId = input.serverId.trim();
|
|
const agentId = input.agentId.trim();
|
|
if (!serverId || !agentId) {
|
|
return "";
|
|
}
|
|
return `${serverId}:${agentId}`;
|
|
}
|
|
|
|
function readPendingState(queryClient: QueryClient): ArchiveAgentPendingState {
|
|
return (
|
|
queryClient.getQueryData<ArchiveAgentPendingState>(
|
|
ARCHIVE_AGENT_PENDING_QUERY_KEY
|
|
) ?? {}
|
|
);
|
|
}
|
|
|
|
function setAgentArchiving(input: SetAgentArchivingInput): void {
|
|
const key = toArchiveKey(input);
|
|
if (!key) {
|
|
return;
|
|
}
|
|
|
|
input.queryClient.setQueryData<ArchiveAgentPendingState>(
|
|
ARCHIVE_AGENT_PENDING_QUERY_KEY,
|
|
(current) => {
|
|
const state = current ?? {};
|
|
if (input.isArchiving) {
|
|
if (state[key]) {
|
|
return state;
|
|
}
|
|
return { ...state, [key]: true };
|
|
}
|
|
|
|
if (!state[key]) {
|
|
return state;
|
|
}
|
|
|
|
const next = { ...state };
|
|
delete next[key];
|
|
return next;
|
|
}
|
|
);
|
|
}
|
|
|
|
function isAgentArchiving(input: IsAgentArchivingInput): boolean {
|
|
const key = toArchiveKey(input);
|
|
if (!key) {
|
|
return false;
|
|
}
|
|
return Boolean(readPendingState(input.queryClient)[key]);
|
|
}
|
|
|
|
function removeAgentFromListPayload<T extends AgentsListQueryData | undefined>(
|
|
payload: T,
|
|
agentId: string
|
|
): T {
|
|
if (!payload || !Array.isArray(payload.entries) || !agentId) {
|
|
return payload;
|
|
}
|
|
const filtered = payload.entries.filter((entry) => entry?.agent?.id !== agentId);
|
|
if (filtered.length === payload.entries.length) {
|
|
return payload;
|
|
}
|
|
return {
|
|
...payload,
|
|
entries: filtered,
|
|
} as T;
|
|
}
|
|
|
|
function removeAgentFromCachedLists(
|
|
queryClient: QueryClient,
|
|
input: ArchiveAgentInput
|
|
): void {
|
|
const agentId = input.agentId.trim();
|
|
if (!agentId) {
|
|
return;
|
|
}
|
|
|
|
queryClient.setQueryData<AgentsListQueryData | undefined>(
|
|
["sidebarAgentsList", input.serverId],
|
|
(current) => removeAgentFromListPayload(current, agentId)
|
|
);
|
|
queryClient.setQueryData<AgentsListQueryData | undefined>(
|
|
["allAgents", input.serverId],
|
|
(current) => removeAgentFromListPayload(current, agentId)
|
|
);
|
|
}
|
|
|
|
function markAgentArchivedInStore(input: ArchiveAgentInput & { archivedAt: string }): void {
|
|
const archivedAt = new Date(input.archivedAt);
|
|
if (Number.isNaN(archivedAt.getTime())) {
|
|
return;
|
|
}
|
|
|
|
const setAgents = useSessionStore.getState().setAgents;
|
|
setAgents(input.serverId, (prev) => {
|
|
const existing = prev.get(input.agentId);
|
|
if (!existing) {
|
|
return prev;
|
|
}
|
|
if (existing.archivedAt && existing.archivedAt.getTime() === archivedAt.getTime()) {
|
|
return prev;
|
|
}
|
|
const next = new Map(prev);
|
|
next.set(input.agentId, {
|
|
...existing,
|
|
archivedAt,
|
|
});
|
|
return next;
|
|
});
|
|
}
|
|
|
|
export function clearArchiveAgentPending(input: IsAgentArchivingInput): void {
|
|
setAgentArchiving({
|
|
...input,
|
|
isArchiving: false,
|
|
});
|
|
}
|
|
|
|
export function useArchiveAgent() {
|
|
const queryClient = useQueryClient();
|
|
|
|
const pendingQuery = useQuery({
|
|
queryKey: ARCHIVE_AGENT_PENDING_QUERY_KEY,
|
|
queryFn: async (): Promise<ArchiveAgentPendingState> => ({}),
|
|
initialData: {} as ArchiveAgentPendingState,
|
|
staleTime: Infinity,
|
|
gcTime: Infinity,
|
|
});
|
|
|
|
const archiveMutation = useMutation({
|
|
mutationFn: async (input: ArchiveAgentInput): Promise<{ archivedAt: string }> => {
|
|
const client = useSessionStore.getState().sessions[input.serverId]?.client ?? null;
|
|
if (!client) {
|
|
throw new Error("Daemon client not available");
|
|
}
|
|
return await client.archiveAgent(input.agentId);
|
|
},
|
|
onMutate: (input) => {
|
|
setAgentArchiving({
|
|
queryClient,
|
|
serverId: input.serverId,
|
|
agentId: input.agentId,
|
|
isArchiving: true,
|
|
});
|
|
},
|
|
onSuccess: (result, input) => {
|
|
markAgentArchivedInStore({
|
|
serverId: input.serverId,
|
|
agentId: input.agentId,
|
|
archivedAt: result.archivedAt,
|
|
});
|
|
removeAgentFromCachedLists(queryClient, input);
|
|
void queryClient.invalidateQueries({
|
|
queryKey: ["sidebarAgentsList", input.serverId],
|
|
});
|
|
void queryClient.invalidateQueries({
|
|
queryKey: ["allAgents", input.serverId],
|
|
});
|
|
},
|
|
onSettled: (_result, _error, input) => {
|
|
clearArchiveAgentPending({
|
|
queryClient,
|
|
serverId: input.serverId,
|
|
agentId: input.agentId,
|
|
});
|
|
},
|
|
});
|
|
|
|
const archiveAgent = useCallback(
|
|
async (input: ArchiveAgentInput): Promise<void> => {
|
|
if (
|
|
isAgentArchiving({
|
|
queryClient,
|
|
serverId: input.serverId,
|
|
agentId: input.agentId,
|
|
})
|
|
) {
|
|
return;
|
|
}
|
|
await archiveMutation.mutateAsync(input);
|
|
},
|
|
[archiveMutation, queryClient]
|
|
);
|
|
|
|
const isArchivingAgent = useCallback(
|
|
(input: ArchiveAgentInput): boolean => {
|
|
const key = toArchiveKey(input);
|
|
if (!key) {
|
|
return false;
|
|
}
|
|
return Boolean((pendingQuery.data ?? {})[key]);
|
|
},
|
|
[pendingQuery.data]
|
|
);
|
|
|
|
return {
|
|
archiveAgent,
|
|
isArchivingAgent,
|
|
};
|
|
}
|
|
|
|
export const __private__ = {
|
|
toArchiveKey,
|
|
readPendingState,
|
|
setAgentArchiving,
|
|
isAgentArchiving,
|
|
removeAgentFromListPayload,
|
|
};
|