Makelore 2.0 initial clean snapshot
This commit is contained in:
188
src/stores/image-workspace.ts
Normal file
188
src/stores/image-workspace.ts
Normal file
@@ -0,0 +1,188 @@
|
||||
import { create } from 'zustand';
|
||||
import {
|
||||
addImageWorkspaceAgent,
|
||||
createImageWorkspaceProject,
|
||||
fetchImageWorkspace,
|
||||
ImageWorkspaceApiError,
|
||||
sendImageWorkspaceMessage,
|
||||
uploadImageWorkspaceReference,
|
||||
} from '@/lib/image-workspace';
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
import {
|
||||
IMAGE_WORKSPACE_UNAVAILABLE_CODE,
|
||||
type ImageWorkspaceProject,
|
||||
type ImageWorkspaceReferenceUploadInput,
|
||||
type ImageWorkspaceReferenceUploadResult,
|
||||
type ImageWorkspaceSendMessageInput,
|
||||
type ImageWorkspaceSnapshot,
|
||||
} from '../../shared/image-workspace';
|
||||
|
||||
export type ImageWorkspaceLoadStatus = 'idle' | 'loading' | 'ready' | 'unavailable' | 'error';
|
||||
|
||||
type ImageWorkspaceState = {
|
||||
status: ImageWorkspaceLoadStatus;
|
||||
snapshot: ImageWorkspaceSnapshot | null;
|
||||
activeProjectId: string | null;
|
||||
activeAgentIds: Record<string, string>;
|
||||
error: string | null;
|
||||
load: () => Promise<ImageWorkspaceSnapshot | null>;
|
||||
createProject: (name: string) => Promise<ImageWorkspaceSnapshot>;
|
||||
addAgent: (projectId: string) => Promise<ImageWorkspaceSnapshot>;
|
||||
sendMessage: (input: ImageWorkspaceSendMessageInput) => Promise<ImageWorkspaceSnapshot>;
|
||||
uploadReference: (input: ImageWorkspaceReferenceUploadInput) => Promise<ImageWorkspaceReferenceUploadResult>;
|
||||
selectProject: (projectId: string) => void;
|
||||
selectAgent: (projectId: string, agentId: string) => void;
|
||||
reset: () => void;
|
||||
};
|
||||
|
||||
let inFlightLoad: Promise<ImageWorkspaceSnapshot | null> | null = null;
|
||||
|
||||
function findProject(snapshot: ImageWorkspaceSnapshot, projectId: string | null): ImageWorkspaceProject | undefined {
|
||||
return snapshot.projects.find((project) => project.id === projectId);
|
||||
}
|
||||
|
||||
function resolveProjectId(snapshot: ImageWorkspaceSnapshot, currentProjectId: string | null): string | null {
|
||||
if (findProject(snapshot, currentProjectId)) return currentProjectId;
|
||||
if (findProject(snapshot, snapshot.activeProjectId ?? null)) return snapshot.activeProjectId ?? null;
|
||||
return snapshot.projects[0]?.id ?? null;
|
||||
}
|
||||
|
||||
function resolveAgentIds(
|
||||
snapshot: ImageWorkspaceSnapshot,
|
||||
currentAgentIds: Record<string, string>,
|
||||
): Record<string, string> {
|
||||
return Object.fromEntries(snapshot.projects.flatMap((project) => {
|
||||
const currentAgentId = currentAgentIds[project.id];
|
||||
let selectedAgentId: string | undefined;
|
||||
if (currentAgentId && project.agents.some((agent) => agent.id === currentAgentId)) {
|
||||
selectedAgentId = currentAgentId;
|
||||
} else if (project.activeAgentId
|
||||
&& project.agents.some((agent) => agent.id === project.activeAgentId)) {
|
||||
selectedAgentId = project.activeAgentId;
|
||||
} else {
|
||||
selectedAgentId = project.agents[0]?.id;
|
||||
}
|
||||
return selectedAgentId ? [[project.id, selectedAgentId]] : [];
|
||||
}));
|
||||
}
|
||||
|
||||
function unavailable(error: unknown): boolean {
|
||||
return error instanceof ImageWorkspaceApiError
|
||||
&& (error.status === 501 || error.code === IMAGE_WORKSPACE_UNAVAILABLE_CODE);
|
||||
}
|
||||
|
||||
async function getOptionalAccessToken(): Promise<string | null> {
|
||||
return await useAuthStore.getState().getValidAccessToken();
|
||||
}
|
||||
|
||||
async function getRequiredAccessToken(): Promise<string> {
|
||||
const accessToken = await useAuthStore.getState().getValidAccessToken();
|
||||
if (!accessToken) throw new Error('请先登录后再使用创作空间');
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
function usesAnonymousDevelopmentAdapter(): boolean {
|
||||
return typeof window !== 'undefined'
|
||||
&& window.electron?.imageWorkspaceLocalDevelopment === true;
|
||||
}
|
||||
|
||||
export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) => {
|
||||
const applySnapshot = (snapshot: ImageWorkspaceSnapshot) => {
|
||||
set((state) => ({
|
||||
status: 'ready',
|
||||
snapshot,
|
||||
activeProjectId: resolveProjectId(snapshot, state.activeProjectId),
|
||||
activeAgentIds: resolveAgentIds(snapshot, state.activeAgentIds),
|
||||
error: null,
|
||||
}));
|
||||
return snapshot;
|
||||
};
|
||||
|
||||
const failLoad = (error: unknown) => {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
set({
|
||||
status: unavailable(error) ? 'unavailable' : 'error',
|
||||
snapshot: null,
|
||||
activeProjectId: null,
|
||||
activeAgentIds: {},
|
||||
error: message,
|
||||
});
|
||||
};
|
||||
|
||||
const mutate = async (
|
||||
request: (accessToken: string | null) => Promise<ImageWorkspaceSnapshot>,
|
||||
): Promise<ImageWorkspaceSnapshot> => {
|
||||
try {
|
||||
const accessToken = usesAnonymousDevelopmentAdapter()
|
||||
? null
|
||||
: await getRequiredAccessToken();
|
||||
return applySnapshot(await request(accessToken));
|
||||
} catch (error) {
|
||||
set({ error: error instanceof Error ? error.message : String(error) });
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
status: 'idle',
|
||||
snapshot: null,
|
||||
activeProjectId: null,
|
||||
activeAgentIds: {},
|
||||
error: null,
|
||||
|
||||
load: () => {
|
||||
if (inFlightLoad) return inFlightLoad;
|
||||
set({ status: 'loading', error: null });
|
||||
inFlightLoad = (async () => {
|
||||
try {
|
||||
return applySnapshot(await fetchImageWorkspace(await getOptionalAccessToken()));
|
||||
} catch (error) {
|
||||
failLoad(error);
|
||||
return null;
|
||||
} finally {
|
||||
inFlightLoad = null;
|
||||
}
|
||||
})();
|
||||
return inFlightLoad;
|
||||
},
|
||||
|
||||
createProject: (name) => mutate((accessToken) => createImageWorkspaceProject(accessToken, name)),
|
||||
addAgent: (projectId) => mutate((accessToken) => addImageWorkspaceAgent(accessToken, projectId)),
|
||||
sendMessage: (input) => mutate((accessToken) => sendImageWorkspaceMessage(accessToken, input)),
|
||||
uploadReference: async (input) => {
|
||||
try {
|
||||
const accessToken = usesAnonymousDevelopmentAdapter()
|
||||
? null
|
||||
: await getRequiredAccessToken();
|
||||
const result = await uploadImageWorkspaceReference(accessToken, input);
|
||||
applySnapshot(result.workspace);
|
||||
return result;
|
||||
} catch (error) {
|
||||
set({ error: error instanceof Error ? error.message : String(error) });
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
selectProject: (projectId) => {
|
||||
if (!get().snapshot?.projects.some((project) => project.id === projectId)) return;
|
||||
set({ activeProjectId: projectId });
|
||||
},
|
||||
|
||||
selectAgent: (projectId, agentId) => {
|
||||
const project = get().snapshot?.projects.find((item) => item.id === projectId);
|
||||
if (!project?.agents.some((agent) => agent.id === agentId)) return;
|
||||
set((state) => ({ activeAgentIds: { ...state.activeAgentIds, [projectId]: agentId } }));
|
||||
},
|
||||
|
||||
reset: () => {
|
||||
inFlightLoad = null;
|
||||
set({
|
||||
status: 'idle',
|
||||
snapshot: null,
|
||||
activeProjectId: null,
|
||||
activeAgentIds: {},
|
||||
error: null,
|
||||
});
|
||||
},
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user