d25d482dc2
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
122 lines
4.2 KiB
TypeScript
122 lines
4.2 KiB
TypeScript
import { create } from 'zustand'
|
|
import { devtools } from 'zustand/middleware'
|
|
|
|
/**
|
|
* An in-flight client upload, shown optimistically before its server import row exists or the
|
|
* table list has refreshed. Keyed by `uploadId`: a `pending_*` id (creating a new table, no row
|
|
* yet) or the target tableId (append/replace into an existing table).
|
|
*/
|
|
export interface ImportUpload {
|
|
uploadId: string
|
|
workspaceId: string
|
|
title: string
|
|
/** Byte-based upload percent from the client XHR. */
|
|
percent?: number
|
|
}
|
|
|
|
/**
|
|
* Client-only state for the import tray. The importing/terminal rows themselves are derived from
|
|
* the table list (React Query) — this store holds only what the server doesn't: optimistic uploads,
|
|
* which terminal completions to surface this session, canceled ids, and the menu's open state.
|
|
*/
|
|
interface ImportTrayState {
|
|
uploads: Record<string, ImportUpload>
|
|
/** Terminal (`ready`/`failed`) table ids to surface as a card this session. */
|
|
notified: Record<string, true>
|
|
/** Ids (upload or table) canceled so callbacks/derivation don't resurrect them. */
|
|
canceledIds: Record<string, true>
|
|
/**
|
|
* Server-listed rows the user dismissed (export jobIds). Unlike `notified` — an allow-list for
|
|
* table-derived terminals — export terminals are listed by the server for a visibility window,
|
|
* so dismissal needs a deny-list.
|
|
*/
|
|
dismissedIds: Record<string, true>
|
|
menuOpen: boolean
|
|
|
|
startUpload: (upload: ImportUpload) => void
|
|
setUploadPercent: (uploadId: string, percent: number) => void
|
|
endUpload: (uploadId: string) => void
|
|
/** Surface a terminal completion as a tray card. */
|
|
notify: (tableId: string) => void
|
|
/** Remove a terminal card (manual dismiss or auto-clear). */
|
|
dismiss: (tableId: string) => void
|
|
/** Hide a server-listed job row (export) for the rest of the session. */
|
|
dismissJob: (jobId: string) => void
|
|
/** Flag an id canceled and drop any optimistic upload for it. */
|
|
cancel: (id: string) => void
|
|
isCanceled: (id: string) => boolean
|
|
/** Returns whether the id was canceled and clears the flag (one-shot, for the kickoff handler). */
|
|
consumeCanceled: (id: string) => boolean
|
|
setMenuOpen: (open: boolean) => void
|
|
reset: () => void
|
|
}
|
|
|
|
const initialState = {
|
|
uploads: {} as Record<string, ImportUpload>,
|
|
notified: {} as Record<string, true>,
|
|
canceledIds: {} as Record<string, true>,
|
|
dismissedIds: {} as Record<string, true>,
|
|
menuOpen: false,
|
|
}
|
|
|
|
export const useImportTrayStore = create<ImportTrayState>()(
|
|
devtools(
|
|
(set, get) => ({
|
|
...initialState,
|
|
|
|
startUpload: (upload) =>
|
|
set((state) => ({ uploads: { ...state.uploads, [upload.uploadId]: upload } })),
|
|
|
|
setUploadPercent: (uploadId, percent) =>
|
|
set((state) => {
|
|
const prev = state.uploads[uploadId]
|
|
if (!prev) return state
|
|
return { uploads: { ...state.uploads, [uploadId]: { ...prev, percent } } }
|
|
}),
|
|
|
|
endUpload: (uploadId) =>
|
|
set((state) => {
|
|
if (!state.uploads[uploadId]) return state
|
|
const { [uploadId]: _removed, ...rest } = state.uploads
|
|
return { uploads: rest }
|
|
}),
|
|
|
|
notify: (tableId) => set((state) => ({ notified: { ...state.notified, [tableId]: true } })),
|
|
|
|
dismiss: (tableId) =>
|
|
set((state) => {
|
|
if (!state.notified[tableId]) return state
|
|
const { [tableId]: _removed, ...rest } = state.notified
|
|
return { notified: rest }
|
|
}),
|
|
|
|
dismissJob: (jobId) =>
|
|
set((state) => ({ dismissedIds: { ...state.dismissedIds, [jobId]: true } })),
|
|
|
|
cancel: (id) =>
|
|
set((state) => {
|
|
const { [id]: _removed, ...uploads } = state.uploads
|
|
return { uploads, canceledIds: { ...state.canceledIds, [id]: true } }
|
|
}),
|
|
|
|
isCanceled: (id) => Boolean(get().canceledIds[id]),
|
|
|
|
consumeCanceled: (id) => {
|
|
const was = Boolean(get().canceledIds[id])
|
|
if (was) {
|
|
set((state) => {
|
|
const { [id]: _removed, ...rest } = state.canceledIds
|
|
return { canceledIds: rest }
|
|
})
|
|
}
|
|
return was
|
|
},
|
|
|
|
setMenuOpen: (open) => set({ menuOpen: open }),
|
|
|
|
reset: () => set(initialState),
|
|
}),
|
|
{ name: 'import-tray-store' }
|
|
)
|
|
)
|