mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-29 19:35:20 +02:00
refactor: remove unused Electron API check and update search space ID handling in document upload
This commit is contained in:
parent
e0b35cfbab
commit
530db10539
7 changed files with 100 additions and 101 deletions
|
|
@ -19,6 +19,6 @@ export const IPC_CHANNELS = {
|
|||
FOLDER_SYNC_RENDERER_READY: 'folder-sync:renderer-ready',
|
||||
FOLDER_SYNC_GET_PENDING_EVENTS: 'folder-sync:get-pending-events',
|
||||
FOLDER_SYNC_ACK_EVENTS: 'folder-sync:ack-events',
|
||||
BROWSE_FILE_OR_FOLDER: 'browse:file-or-folder',
|
||||
BROWSE_FILES: 'browse:files',
|
||||
READ_LOCAL_FILES: 'browse:read-local-files',
|
||||
} as const;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
pauseWatcher,
|
||||
resumeWatcher,
|
||||
markRendererReady,
|
||||
browseFileOrFolder,
|
||||
browseFiles,
|
||||
readLocalFiles,
|
||||
} from '../modules/folder-watcher';
|
||||
|
||||
|
|
@ -62,7 +62,7 @@ export function registerIpcHandlers(): void {
|
|||
acknowledgeFileEvents(eventIds)
|
||||
);
|
||||
|
||||
ipcMain.handle(IPC_CHANNELS.BROWSE_FILE_OR_FOLDER, () => browseFileOrFolder());
|
||||
ipcMain.handle(IPC_CHANNELS.BROWSE_FILES, () => browseFiles());
|
||||
|
||||
ipcMain.handle(IPC_CHANNELS.READ_LOCAL_FILES, (_event, paths: string[]) =>
|
||||
readLocalFiles(paths)
|
||||
|
|
|
|||
|
|
@ -475,23 +475,13 @@ export async function unregisterFolderWatcher(): Promise<void> {
|
|||
watchers.clear();
|
||||
}
|
||||
|
||||
export interface BrowseResult {
|
||||
type: 'files' | 'folder';
|
||||
paths: string[];
|
||||
}
|
||||
|
||||
export async function browseFileOrFolder(): Promise<BrowseResult | null> {
|
||||
export async function browseFiles(): Promise<string[] | null> {
|
||||
const result = await dialog.showOpenDialog({
|
||||
properties: ['openFile', 'openDirectory', 'multiSelections'],
|
||||
title: 'Select files or a folder',
|
||||
properties: ['openFile', 'multiSelections'],
|
||||
title: 'Select files',
|
||||
});
|
||||
if (result.canceled || result.filePaths.length === 0) return null;
|
||||
|
||||
const stat = fs.statSync(result.filePaths[0]);
|
||||
if (stat.isDirectory()) {
|
||||
return { type: 'folder', paths: [result.filePaths[0]] };
|
||||
}
|
||||
return { type: 'files', paths: result.filePaths };
|
||||
return result.filePaths;
|
||||
}
|
||||
|
||||
const MIME_MAP: Record<string, string> = {
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||
getPendingFileEvents: () => ipcRenderer.invoke(IPC_CHANNELS.FOLDER_SYNC_GET_PENDING_EVENTS),
|
||||
acknowledgeFileEvents: (eventIds: string[]) => ipcRenderer.invoke(IPC_CHANNELS.FOLDER_SYNC_ACK_EVENTS, eventIds),
|
||||
|
||||
// Unified browse (files + folders)
|
||||
browseFileOrFolder: () => ipcRenderer.invoke(IPC_CHANNELS.BROWSE_FILE_OR_FOLDER),
|
||||
// Browse files via native dialog
|
||||
browseFiles: () => ipcRenderer.invoke(IPC_CHANNELS.BROWSE_FILES),
|
||||
readLocalFiles: (paths: string[]) => ipcRenderer.invoke(IPC_CHANNELS.READ_LOCAL_FILES, paths),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -277,8 +277,6 @@ export function DocumentsSidebar({
|
|||
[createFolderParentId, searchSpaceId, setExpandedFolderMap]
|
||||
);
|
||||
|
||||
const isElectron = typeof window !== "undefined" && !!window.electronAPI;
|
||||
|
||||
const handleRescanFolder = useCallback(
|
||||
async (folder: FolderDisplay) => {
|
||||
const api = window.electronAPI;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import { useAtom } from "jotai";
|
||||
import { CheckCircle2, FileType, FolderOpen, Info, Upload, X } from "lucide-react";
|
||||
import { CheckCircle2, ChevronDown, File as FileIcon, FileType, FolderOpen, Info, Upload, X } from "lucide-react";
|
||||
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useCallback, useMemo, useRef, useState } from "react";
|
||||
|
|
@ -19,6 +19,12 @@ import { Alert, AlertDescription } from "@/components/ui/alert";
|
|||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Progress } from "@/components/ui/progress";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
|
|
@ -146,7 +152,7 @@ export function DocumentUploadTab({
|
|||
const [selectedFolder, setSelectedFolder] = useState<SelectedFolder | null>(null);
|
||||
const [watchFolder, setWatchFolder] = useState(true);
|
||||
const [folderSubmitting, setFolderSubmitting] = useState(false);
|
||||
const isElectron = typeof window !== "undefined" && !!window.electronAPI?.browseFileOrFolder;
|
||||
const isElectron = typeof window !== "undefined" && !!window.electronAPI?.browseFiles;
|
||||
|
||||
const acceptedFileTypes = useMemo(() => {
|
||||
const etlService = process.env.NEXT_PUBLIC_ETL_SERVICE;
|
||||
|
|
@ -193,7 +199,7 @@ export function DocumentUploadTab({
|
|||
onDrop,
|
||||
accept: acceptedFileTypes,
|
||||
maxSize: 50 * 1024 * 1024, // 50MB per file
|
||||
noClick: !isElectron,
|
||||
noClick: isElectron,
|
||||
disabled: files.length >= MAX_FILES,
|
||||
});
|
||||
|
||||
|
|
@ -201,52 +207,51 @@ export function DocumentUploadTab({
|
|||
e.stopPropagation();
|
||||
}, []);
|
||||
|
||||
const handleBrowse = useCallback(async (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
const handleBrowseFiles = useCallback(async () => {
|
||||
const api = window.electronAPI;
|
||||
if (!api?.browseFileOrFolder) {
|
||||
fileInputRef.current?.click();
|
||||
return;
|
||||
}
|
||||
if (!api?.browseFiles) return;
|
||||
|
||||
const result = await api.browseFileOrFolder();
|
||||
if (!result) return;
|
||||
const paths = await api.browseFiles();
|
||||
if (!paths || paths.length === 0) return;
|
||||
|
||||
if (result.type === "folder") {
|
||||
const folderPath = result.paths[0];
|
||||
const folderName = folderPath.split("/").pop() || folderPath.split("\\").pop() || folderPath;
|
||||
setFiles([]);
|
||||
setSelectedFolder({ path: folderPath, name: folderName });
|
||||
setWatchFolder(true);
|
||||
} else {
|
||||
setSelectedFolder(null);
|
||||
const fileDataList = await api.readLocalFiles(result.paths);
|
||||
const newFiles: FileWithId[] = fileDataList.map((fd) => ({
|
||||
id: crypto.randomUUID?.() ?? `file-${Date.now()}-${Math.random().toString(36)}`,
|
||||
file: new File([fd.data], fd.name, { type: fd.mimeType }),
|
||||
}));
|
||||
setFiles((prev) => {
|
||||
const merged = [...prev, ...newFiles];
|
||||
if (merged.length > MAX_FILES) {
|
||||
toast.error(t("max_files_exceeded"), {
|
||||
description: t("max_files_exceeded_desc", { max: MAX_FILES }),
|
||||
});
|
||||
return prev;
|
||||
}
|
||||
const totalSize = merged.reduce((sum, e) => sum + e.file.size, 0);
|
||||
if (totalSize > MAX_TOTAL_SIZE_BYTES) {
|
||||
toast.error(t("max_size_exceeded"), {
|
||||
description: t("max_size_exceeded_desc", { max: MAX_TOTAL_SIZE_MB }),
|
||||
});
|
||||
return prev;
|
||||
}
|
||||
return merged;
|
||||
});
|
||||
}
|
||||
setSelectedFolder(null);
|
||||
const fileDataList = await api.readLocalFiles(paths);
|
||||
const newFiles: FileWithId[] = fileDataList.map((fd) => ({
|
||||
id: crypto.randomUUID?.() ?? `file-${Date.now()}-${Math.random().toString(36)}`,
|
||||
file: new File([fd.data], fd.name, { type: fd.mimeType }),
|
||||
}));
|
||||
setFiles((prev) => {
|
||||
const merged = [...prev, ...newFiles];
|
||||
if (merged.length > MAX_FILES) {
|
||||
toast.error(t("max_files_exceeded"), {
|
||||
description: t("max_files_exceeded_desc", { max: MAX_FILES }),
|
||||
});
|
||||
return prev;
|
||||
}
|
||||
const totalSize = merged.reduce((sum, e) => sum + e.file.size, 0);
|
||||
if (totalSize > MAX_TOTAL_SIZE_BYTES) {
|
||||
toast.error(t("max_size_exceeded"), {
|
||||
description: t("max_size_exceeded_desc", { max: MAX_TOTAL_SIZE_MB }),
|
||||
});
|
||||
return prev;
|
||||
}
|
||||
return merged;
|
||||
});
|
||||
}, [t]);
|
||||
|
||||
const handleBrowseFolder = useCallback(async () => {
|
||||
const api = window.electronAPI;
|
||||
if (!api?.selectFolder) return;
|
||||
|
||||
const folderPath = await api.selectFolder();
|
||||
if (!folderPath) return;
|
||||
|
||||
const folderName = folderPath.split("/").pop() || folderPath.split("\\").pop() || folderPath;
|
||||
setFiles([]);
|
||||
setSelectedFolder({ path: folderPath, name: folderName });
|
||||
setWatchFolder(true);
|
||||
}, []);
|
||||
|
||||
const formatFileSize = (bytes: number) => {
|
||||
if (bytes === 0) return "0 Bytes";
|
||||
const k = 1024;
|
||||
|
|
@ -280,10 +285,11 @@ export function DocumentUploadTab({
|
|||
|
||||
setFolderSubmitting(true);
|
||||
try {
|
||||
const result = await documentsApiService.folderIndex(Number(searchSpaceId), {
|
||||
const numericSpaceId = Number(searchSpaceId);
|
||||
const result = await documentsApiService.folderIndex(numericSpaceId, {
|
||||
folder_path: selectedFolder.path,
|
||||
folder_name: selectedFolder.name,
|
||||
search_space_id: searchSpaceId,
|
||||
search_space_id: numericSpaceId,
|
||||
enable_summary: shouldSummarize,
|
||||
});
|
||||
|
||||
|
|
@ -409,33 +415,43 @@ export function DocumentUploadTab({
|
|||
)}
|
||||
</div>
|
||||
)}
|
||||
{!isFileCountLimitReached && (
|
||||
<div className="mt-2 sm:mt-4">
|
||||
{isElectron ? (
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
className="text-xs sm:text-sm"
|
||||
onClick={handleBrowse}
|
||||
>
|
||||
{t("browse_files")}
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
className="text-xs sm:text-sm"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
fileInputRef.current?.click();
|
||||
}}
|
||||
>
|
||||
{t("browse_files")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{!isFileCountLimitReached && (
|
||||
<div className="mt-2 sm:mt-4">
|
||||
{isElectron ? (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild onClick={(e) => e.stopPropagation()}>
|
||||
<Button variant="secondary" size="sm" className="text-xs sm:text-sm gap-1">
|
||||
{t("browse_files")}
|
||||
<ChevronDown className="h-3 w-3 opacity-60" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="center" onClick={(e) => e.stopPropagation()}>
|
||||
<DropdownMenuItem onClick={handleBrowseFiles}>
|
||||
<FileIcon className="h-4 w-4 mr-2" />
|
||||
Files
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={handleBrowseFolder}>
|
||||
<FolderOpen className="h-4 w-4 mr-2" />
|
||||
Folder
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
) : (
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
className="text-xs sm:text-sm"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
fileInputRef.current?.click();
|
||||
}}
|
||||
>
|
||||
{t("browse_files")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
|
|||
9
surfsense_web/types/window.d.ts
vendored
9
surfsense_web/types/window.d.ts
vendored
|
|
@ -27,11 +27,6 @@ interface FolderSyncWatcherReadyEvent {
|
|||
folderPath: string;
|
||||
}
|
||||
|
||||
interface BrowseResult {
|
||||
type: "files" | "folder";
|
||||
paths: string[];
|
||||
}
|
||||
|
||||
interface LocalFileData {
|
||||
name: string;
|
||||
data: ArrayBuffer;
|
||||
|
|
@ -66,8 +61,8 @@ interface ElectronAPI {
|
|||
signalRendererReady: () => Promise<void>;
|
||||
getPendingFileEvents: () => Promise<FolderSyncFileChangedEvent[]>;
|
||||
acknowledgeFileEvents: (eventIds: string[]) => Promise<{ acknowledged: number }>;
|
||||
// Unified browse
|
||||
browseFileOrFolder: () => Promise<BrowseResult | null>;
|
||||
// Browse files/folders via native dialogs
|
||||
browseFiles: () => Promise<string[] | null>;
|
||||
readLocalFiles: (paths: string[]) => Promise<LocalFileData[]>;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue