mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-08 22:22:17 +02:00
- Added the ensure_publication function to create and verify the zero_publication if it is missing, ensuring idempotency during database initialization. - Integrated ensure_publication into the create_db_and_tables function to prevent zero-cache crash loops on startup. - Introduced a self-check script to validate the ensure_publication functionality on a create_all-bootstrapped database. - Updated various components to reflect the transition from search space to workspace, including adjustments in imports and routing paths.
126 lines
3.7 KiB
TypeScript
126 lines
3.7 KiB
TypeScript
"use client";
|
|
|
|
import { useAtomValue } from "jotai";
|
|
import {
|
|
createContext,
|
|
type FC,
|
|
type ReactNode,
|
|
useCallback,
|
|
useContext,
|
|
useRef,
|
|
useState,
|
|
} from "react";
|
|
import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
|
|
import { DocumentUploadTab } from "@/components/sources/DocumentUploadTab";
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from "@/components/ui/dialog";
|
|
|
|
// Context for opening the dialog from anywhere
|
|
interface DocumentUploadDialogContextType {
|
|
openDialog: () => void;
|
|
closeDialog: () => void;
|
|
}
|
|
|
|
const DocumentUploadDialogContext = createContext<DocumentUploadDialogContextType | null>(null);
|
|
|
|
const NOOP_DIALOG: DocumentUploadDialogContextType = {
|
|
openDialog: () => {},
|
|
closeDialog: () => {},
|
|
};
|
|
|
|
export const useDocumentUploadDialog = (): DocumentUploadDialogContextType => {
|
|
const context = useContext(DocumentUploadDialogContext);
|
|
return context ?? NOOP_DIALOG;
|
|
};
|
|
|
|
// Provider component
|
|
export const DocumentUploadDialogProvider: FC<{ children: ReactNode }> = ({ children }) => {
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
const isClosingRef = useRef(false);
|
|
|
|
const openDialog = useCallback(() => {
|
|
// Prevent opening if we just closed (debounce)
|
|
if (isClosingRef.current) {
|
|
return;
|
|
}
|
|
setIsOpen(true);
|
|
}, []);
|
|
|
|
const closeDialog = useCallback(() => {
|
|
isClosingRef.current = true;
|
|
setIsOpen(false);
|
|
// Reset the flag after a short delay to allow for file picker to close
|
|
setTimeout(() => {
|
|
isClosingRef.current = false;
|
|
}, 300);
|
|
}, []);
|
|
|
|
const handleOpenChange = useCallback(
|
|
(open: boolean) => {
|
|
if (!open) {
|
|
// Only close if not already in closing state
|
|
if (!isClosingRef.current) {
|
|
closeDialog();
|
|
}
|
|
} else {
|
|
// Only open if not in the middle of closing
|
|
if (!isClosingRef.current) {
|
|
setIsOpen(true);
|
|
}
|
|
}
|
|
},
|
|
[closeDialog]
|
|
);
|
|
|
|
return (
|
|
<DocumentUploadDialogContext.Provider value={{ openDialog, closeDialog }}>
|
|
{children}
|
|
<DocumentUploadPopupContent isOpen={isOpen} onOpenChange={handleOpenChange} />
|
|
</DocumentUploadDialogContext.Provider>
|
|
);
|
|
};
|
|
|
|
// Internal component that renders the dialog
|
|
const DocumentUploadPopupContent: FC<{
|
|
isOpen: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
}> = ({ isOpen, onOpenChange }) => {
|
|
const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
|
|
|
|
if (!searchSpaceId) return null;
|
|
|
|
const handleSuccess = () => {
|
|
onOpenChange(false);
|
|
};
|
|
|
|
return (
|
|
<Dialog open={isOpen} onOpenChange={onOpenChange}>
|
|
<DialogContent
|
|
onPointerDownOutside={(e) => e.preventDefault()}
|
|
onInteractOutside={(e) => e.preventDefault()}
|
|
onEscapeKeyDown={(e) => e.preventDefault()}
|
|
className="select-none max-w-2xl w-[95vw] sm:w-[640px] h-[min(440px,75dvh)] sm:h-[min(520px,80vh)] flex flex-col p-0 gap-0 overflow-hidden ring-0 [&>button]:right-3 sm:[&>button]:right-6 [&>button]:top-5 sm:[&>button]:top-8 [&>button]:opacity-80 [&>button]:hover:opacity-100 [&>button]:hover:bg-accent [&>button]:hover:text-accent-foreground [&>button]:z-[100] [&>button>svg]:size-4 sm:[&>button>svg]:size-5"
|
|
>
|
|
<div className="flex-1 min-h-0 overflow-y-auto overscroll-contain">
|
|
<DialogHeader className="sticky top-0 z-20 bg-popover px-4 sm:px-6 pt-6 sm:pt-8 pb-10">
|
|
<DialogTitle className="text-xl sm:text-3xl font-semibold tracking-tight pr-8 sm:pr-0">
|
|
Upload Documents
|
|
</DialogTitle>
|
|
<DialogDescription className="text-xs sm:text-base text-muted-foreground/80 line-clamp-1">
|
|
Upload and sync your documents to your search space
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<div className="px-4 sm:px-6 pb-4 sm:pb-6">
|
|
<DocumentUploadTab searchSpaceId={searchSpaceId} onSuccess={handleSuccess} />
|
|
</div>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
};
|