import React, { createContext, useContext, useCallback, useState, useEffect } from 'react'; import api from '../api'; import { useUserFiles, useFileOperations, UserFile } from '../hooks/useFiles'; import type { FolderInfo } from '../api/fileApi'; export type { FolderInfo }; interface FileContextType { files: UserFile[]; loading: boolean; error: string | null; refetch: () => Promise; handleFileUpload: (file: File, workflowId?: string) => Promise<{ success: boolean; fileData?: any; error?: string }>; handleFileDelete: (fileId: string, onOptimisticDelete?: () => void) => Promise; handleFilePreview: (fileId: string, fileName: string, mimeType?: string) => Promise<{ success: boolean; previewUrl?: string | null; blob?: Blob | null; isJsonContent?: boolean; decodedContent?: any; isTextContent?: boolean; error?: string }>; handleFileDownload: (fileId: string, fileName: string) => Promise; uploadingFile: boolean; deletingFiles: Set; previewingFiles: Set; downloadingFiles: Set; folders: FolderInfo[]; foldersLoading: boolean; refreshFolders: () => Promise; handleCreateFolder: (name: string, parentId: string | null) => Promise; handleRenameFolder: (folderId: string, newName: string) => Promise; handleDeleteFolder: (folderId: string) => Promise; handleMoveFolder: (folderId: string, targetParentId: string | null) => Promise; handleMoveFile: (fileId: string, targetFolderId: string | null) => Promise; handleMoveFiles: (fileIds: string[], targetFolderId: string | null) => Promise; handleMoveFolders: (folderIds: string[], targetParentId: string | null) => Promise; handleDownloadFolder: (folderId: string, folderName: string) => Promise; expandedFolderIds: Set; toggleFolderExpanded: (id: string) => void; } export const FileContext = createContext(undefined); export function FileProvider({ children }: { children: React.ReactNode }) { const { data: files, loading, error, refetch: refetchFiles, removeFileOptimistically } = useUserFiles(); const { handleFileUpload: hookHandleFileUpload, handleFileDelete: hookHandleFileDelete, handleFilePreview, handleFileDownload, uploadingFile, deletingFiles, previewingFiles, downloadingFiles } = useFileOperations(); useEffect(() => { refetchFiles(); }, []); // ── Folder expanded state (persisted in localStorage) ─────────────────── const _STORAGE_KEY = 'folderTree-expandedIds'; const [expandedFolderIds, setExpandedFolderIds] = useState>(() => { try { const stored = localStorage.getItem(_STORAGE_KEY); return stored ? new Set(JSON.parse(stored)) : new Set(); } catch { return new Set(); } }); const toggleFolderExpanded = useCallback((id: string) => { setExpandedFolderIds(prev => { const next = new Set(prev); if (next.has(id)) next.delete(id); else next.add(id); try { localStorage.setItem(_STORAGE_KEY, JSON.stringify([...next])); } catch {} return next; }); }, []); // ── Folder state (single source of truth) ────────────────────────────── const [folders, setFolders] = useState([]); const [foldersLoading, setFoldersLoading] = useState(false); const refreshFolders = useCallback(async () => { setFoldersLoading(true); try { const response = await api.get('/api/files/folders'); const data = Array.isArray(response.data) ? response.data : []; setFolders(data); } catch (err) { console.error('Failed to load folders:', err); } finally { setFoldersLoading(false); } }, []); useEffect(() => { refreshFolders(); }, [refreshFolders]); const handleCreateFolder = useCallback(async (name: string, parentId: string | null) => { await api.post('/api/files/folders', { name, parentId: parentId || null }); await refreshFolders(); }, [refreshFolders]); const handleRenameFolder = useCallback(async (folderId: string, newName: string) => { await api.put(`/api/files/folders/${folderId}`, { name: newName }); await refreshFolders(); }, [refreshFolders]); const handleDeleteFolder = useCallback(async (folderId: string) => { await api.delete(`/api/files/folders/${folderId}`, { params: { recursive: true } }); await refreshFolders(); await refetchFiles(); }, [refreshFolders, refetchFiles]); const handleMoveFolder = useCallback(async (folderId: string, targetParentId: string | null) => { await api.post(`/api/files/folders/${folderId}/move`, { targetParentId }); await refreshFolders(); }, [refreshFolders]); const handleMoveFile = useCallback(async (fileId: string, targetFolderId: string | null) => { await api.post(`/api/files/${fileId}/move`, { targetFolderId }); await refetchFiles(); }, [refetchFiles]); const handleMoveFiles = useCallback(async (fileIds: string[], targetFolderId: string | null) => { await api.post('/api/files/batch-move', { fileIds, targetFolderId }); await refetchFiles(); }, [refetchFiles]); const handleMoveFolders = useCallback(async (folderIds: string[], targetParentId: string | null) => { await api.post('/api/files/batch-move', { folderIds, targetParentId }); await refreshFolders(); }, [refreshFolders]); const handleDownloadFolder = useCallback(async (folderId: string, folderName: string) => { try { const response = await api.get(`/api/files/folders/${folderId}/download`, { responseType: 'blob', }); const url = window.URL.createObjectURL(response.data); const link = document.createElement('a'); link.href = url; link.setAttribute('download', `${folderName}.zip`); document.body.appendChild(link); link.click(); link.remove(); window.URL.revokeObjectURL(url); } catch (err) { console.error('Failed to download folder:', err); } }, []); // ── File operations ──────────────────────────────────────────────────── const handleFileUpload = useCallback(async (file: File, workflowId?: string) => { const result = await hookHandleFileUpload(file, workflowId); if (result.success && result.fileData) { await refetchFiles(); } return result; }, [hookHandleFileUpload, refetchFiles]); const handleFileDelete = useCallback(async (fileId: string, onOptimisticDelete?: () => void) => { const success = await hookHandleFileDelete(fileId, () => { removeFileOptimistically(fileId); onOptimisticDelete?.(); }); if (success) { await refetchFiles(); } return success; }, [hookHandleFileDelete, removeFileOptimistically, refetchFiles]); const refetch = useCallback(async () => { await refetchFiles(); }, [refetchFiles]); return ( { await handleFileDownload(fileId, fileName); }, uploadingFile, deletingFiles, previewingFiles, downloadingFiles, folders, foldersLoading, refreshFolders, handleCreateFolder, handleRenameFolder, handleDeleteFolder, handleMoveFolder, handleMoveFile, handleMoveFiles, handleMoveFolders, handleDownloadFolder, expandedFolderIds, toggleFolderExpanded, }} > {children} ); } export function useFileContext() { const context = useContext(FileContext); if (context === undefined) { throw new Error('useFileContext must be used within a FileProvider'); } return context; }