refactor(app): introduce attachment storage layer and draft lifecycle management
Replace inline data-URL image handling with a persistent attachment storage service, add draft lifecycle states (active/abandoned/sent) with generation tracking, and update fetchAgent to return project placement alongside agent snapshots.
This commit is contained in:
@@ -1,28 +1,77 @@
|
||||
import { create } from "zustand";
|
||||
import type { UserMessageImageAttachment } from "@/types/stream";
|
||||
|
||||
export type CreateFlowLifecycleState = "active" | "abandoned" | "sent";
|
||||
|
||||
type PendingCreateAttempt = {
|
||||
draftId: string;
|
||||
serverId: string;
|
||||
agentId: string | null;
|
||||
messageId: string;
|
||||
clientMessageId: string;
|
||||
text: string;
|
||||
timestamp: number;
|
||||
lifecycle: CreateFlowLifecycleState;
|
||||
images?: UserMessageImageAttachment[];
|
||||
};
|
||||
|
||||
type CreateFlowState = {
|
||||
pending: PendingCreateAttempt | null;
|
||||
setPending: (pending: PendingCreateAttempt) => void;
|
||||
setPending: (
|
||||
pending: Omit<PendingCreateAttempt, "lifecycle">
|
||||
) => void;
|
||||
updateAgentId: (agentId: string) => void;
|
||||
markLifecycle: (lifecycle: CreateFlowLifecycleState) => void;
|
||||
rekeyDraft: (input: { fromDraftId: string; toDraftId: string }) => void;
|
||||
clear: () => void;
|
||||
};
|
||||
|
||||
export const useCreateFlowStore = create<CreateFlowState>((set) => ({
|
||||
pending: null,
|
||||
setPending: (pending) => set({ pending }),
|
||||
setPending: (pending) =>
|
||||
set({
|
||||
pending: {
|
||||
...pending,
|
||||
lifecycle: "active",
|
||||
},
|
||||
}),
|
||||
updateAgentId: (agentId) =>
|
||||
set((state) =>
|
||||
state.pending ? { pending: { ...state.pending, agentId } } : state
|
||||
),
|
||||
set((state) => {
|
||||
if (!state.pending || state.pending.agentId === agentId) {
|
||||
return state;
|
||||
}
|
||||
return {
|
||||
pending: {
|
||||
...state.pending,
|
||||
agentId,
|
||||
},
|
||||
};
|
||||
}),
|
||||
markLifecycle: (lifecycle) =>
|
||||
set((state) => {
|
||||
if (!state.pending || state.pending.lifecycle === lifecycle) {
|
||||
return state;
|
||||
}
|
||||
return {
|
||||
pending: {
|
||||
...state.pending,
|
||||
lifecycle,
|
||||
},
|
||||
};
|
||||
}),
|
||||
rekeyDraft: ({ fromDraftId, toDraftId }) =>
|
||||
set((state) => {
|
||||
if (!state.pending || state.pending.draftId !== fromDraftId) {
|
||||
return state;
|
||||
}
|
||||
if (state.pending.draftId === toDraftId) {
|
||||
return state;
|
||||
}
|
||||
return {
|
||||
pending: {
|
||||
...state.pending,
|
||||
draftId: toDraftId,
|
||||
},
|
||||
};
|
||||
}),
|
||||
clear: () => set({ pending: null }),
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user