mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-09-18 01:19:24 +00:00
Show history
This commit is contained in:
parent
3cea686acd
commit
dc9acdd7cc
@ -2147,11 +2147,12 @@
|
|||||||
"supportMessage": "Powered by browser database storage for unlimited capacity",
|
"supportMessage": "Powered by browser database storage for unlimited capacity",
|
||||||
"noFileSelected": "No files selected",
|
"noFileSelected": "No files selected",
|
||||||
"showHistory": "Show History",
|
"showHistory": "Show History",
|
||||||
"hideHistory": "Hide History",
|
"hideHistory": "Hide History",
|
||||||
|
"fileHistory": "FileHistory",
|
||||||
"loadingHistory": "Loading History...",
|
"loadingHistory": "Loading History...",
|
||||||
"lastModified": "Last Modified",
|
"lastModified": "Last Modified",
|
||||||
"toolChain": "Tools Applied",
|
"toolChain": "Tools Applied",
|
||||||
"addToRecents": "Add to Recents",
|
"restore": "Restore",
|
||||||
"searchFiles": "Search files...",
|
"searchFiles": "Search files...",
|
||||||
"recent": "Recent",
|
"recent": "Recent",
|
||||||
"localFiles": "Local Files",
|
"localFiles": "Local Files",
|
||||||
|
68
frontend/src/components/fileManager/FileHistoryGroup.tsx
Normal file
68
frontend/src/components/fileManager/FileHistoryGroup.tsx
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Box, Text, Collapse, Group } from '@mantine/core';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { StirlingFileStub } from '../../types/fileContext';
|
||||||
|
import FileListItem from './FileListItem';
|
||||||
|
|
||||||
|
interface FileHistoryGroupProps {
|
||||||
|
leafFile: StirlingFileStub;
|
||||||
|
historyFiles: StirlingFileStub[];
|
||||||
|
isExpanded: boolean;
|
||||||
|
onDownloadSingle: (file: StirlingFileStub) => void;
|
||||||
|
onFileDoubleClick: (file: StirlingFileStub) => void;
|
||||||
|
onFileRemove: (index: number) => void;
|
||||||
|
isFileSupported: (fileName: string) => boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FileHistoryGroup: React.FC<FileHistoryGroupProps> = ({
|
||||||
|
leafFile,
|
||||||
|
historyFiles,
|
||||||
|
isExpanded,
|
||||||
|
onDownloadSingle,
|
||||||
|
onFileDoubleClick,
|
||||||
|
onFileRemove,
|
||||||
|
isFileSupported,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
// Sort history files by version number (oldest first, excluding the current leaf file)
|
||||||
|
const sortedHistory = historyFiles
|
||||||
|
.filter(file => file.id !== leafFile.id) // Exclude the leaf file itself
|
||||||
|
.sort((a, b) => (a.versionNumber || 1) - (b.versionNumber || 1));
|
||||||
|
|
||||||
|
if (!isExpanded || sortedHistory.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Collapse in={isExpanded}>
|
||||||
|
<Box ml="md" mt="xs" mb="sm">
|
||||||
|
<Group align="center" mb="sm">
|
||||||
|
<Text size="xs" fw={600} c="dimmed" tt="uppercase">
|
||||||
|
{t('fileManager.fileHistory', 'File History')} ({sortedHistory.length})
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Box ml="md">
|
||||||
|
{sortedHistory.map((historyFile, index) => (
|
||||||
|
<FileListItem
|
||||||
|
key={`history-${historyFile.id}-${historyFile.versionNumber || 1}`}
|
||||||
|
file={historyFile}
|
||||||
|
isSelected={false} // History files are not selectable
|
||||||
|
isSupported={isFileSupported(historyFile.name)}
|
||||||
|
onSelect={() => {}} // No selection for history files
|
||||||
|
onRemove={() => onFileRemove(index)} // Pass through remove handler
|
||||||
|
onDownload={() => onDownloadSingle(historyFile)}
|
||||||
|
onDoubleClick={() => onFileDoubleClick(historyFile)}
|
||||||
|
isHistoryFile={true} // This enables "Add to Recents" in menu
|
||||||
|
isLatestVersion={false} // History files are never latest
|
||||||
|
// onAddToRecents is accessed from context by FileListItem
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Collapse>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FileHistoryGroup;
|
@ -4,6 +4,7 @@ import CloudIcon from '@mui/icons-material/Cloud';
|
|||||||
import HistoryIcon from '@mui/icons-material/History';
|
import HistoryIcon from '@mui/icons-material/History';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import FileListItem from './FileListItem';
|
import FileListItem from './FileListItem';
|
||||||
|
import FileHistoryGroup from './FileHistoryGroup';
|
||||||
import { useFileManagerContext } from '../../contexts/FileManagerContext';
|
import { useFileManagerContext } from '../../contexts/FileManagerContext';
|
||||||
|
|
||||||
interface FileListAreaProps {
|
interface FileListAreaProps {
|
||||||
@ -22,6 +23,7 @@ const FileListArea: React.FC<FileListAreaProps> = ({
|
|||||||
selectedFilesSet,
|
selectedFilesSet,
|
||||||
fileGroups,
|
fileGroups,
|
||||||
expandedFileIds,
|
expandedFileIds,
|
||||||
|
loadedHistoryFiles,
|
||||||
onFileSelect,
|
onFileSelect,
|
||||||
onFileRemove,
|
onFileRemove,
|
||||||
onFileDoubleClick,
|
onFileDoubleClick,
|
||||||
@ -53,24 +55,34 @@ const FileListArea: React.FC<FileListAreaProps> = ({
|
|||||||
</Center>
|
</Center>
|
||||||
) : (
|
) : (
|
||||||
filteredFiles.map((file, index) => {
|
filteredFiles.map((file, index) => {
|
||||||
// Determine if this is a history file based on whether it's in the recent files or loaded as history
|
// All files in filteredFiles are now leaf files only
|
||||||
const isLeafFile = recentFiles.some(rf => rf.id === file.id);
|
const historyFiles = loadedHistoryFiles.get(file.id) || [];
|
||||||
const isHistoryFile = !isLeafFile; // If not in recent files, it's a loaded history file
|
const isExpanded = expandedFileIds.has(file.id);
|
||||||
const isLatestVersion = isLeafFile; // Only leaf files (from recent files) are latest versions
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FileListItem
|
<React.Fragment key={file.id}>
|
||||||
key={file.id}
|
<FileListItem
|
||||||
file={file}
|
file={file}
|
||||||
isSelected={selectedFilesSet.has(file.id)}
|
isSelected={selectedFilesSet.has(file.id)}
|
||||||
isSupported={isFileSupported(file.name)}
|
isSupported={isFileSupported(file.name)}
|
||||||
onSelect={(shiftKey) => onFileSelect(file, index, shiftKey)}
|
onSelect={(shiftKey) => onFileSelect(file, index, shiftKey)}
|
||||||
onRemove={() => onFileRemove(index)}
|
onRemove={() => onFileRemove(index)}
|
||||||
onDownload={() => onDownloadSingle(file)}
|
onDownload={() => onDownloadSingle(file)}
|
||||||
onDoubleClick={() => onFileDoubleClick(file)}
|
onDoubleClick={() => onFileDoubleClick(file)}
|
||||||
isHistoryFile={isHistoryFile}
|
isHistoryFile={false} // All files here are leaf files
|
||||||
isLatestVersion={isLatestVersion}
|
isLatestVersion={true} // All files here are the latest versions
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<FileHistoryGroup
|
||||||
|
leafFile={file}
|
||||||
|
historyFiles={historyFiles}
|
||||||
|
isExpanded={isExpanded}
|
||||||
|
onDownloadSingle={onDownloadSingle}
|
||||||
|
onFileDoubleClick={onFileDoubleClick}
|
||||||
|
onFileRemove={onFileRemove}
|
||||||
|
isFileSupported={isFileSupported}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
)}
|
)}
|
||||||
|
@ -5,6 +5,7 @@ import DeleteIcon from '@mui/icons-material/Delete';
|
|||||||
import DownloadIcon from '@mui/icons-material/Download';
|
import DownloadIcon from '@mui/icons-material/Download';
|
||||||
import AddIcon from '@mui/icons-material/Add';
|
import AddIcon from '@mui/icons-material/Add';
|
||||||
import HistoryIcon from '@mui/icons-material/History';
|
import HistoryIcon from '@mui/icons-material/History';
|
||||||
|
import RestoreIcon from '@mui/icons-material/Restore';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { getFileSize, getFileDate } from '../../utils/fileUtils';
|
import { getFileSize, getFileDate } from '../../utils/fileUtils';
|
||||||
import { StirlingFileStub } from '../../types/fileContext';
|
import { StirlingFileStub } from '../../types/fileContext';
|
||||||
@ -54,8 +55,10 @@ const FileListItem: React.FC<FileListItemProps> = ({
|
|||||||
<Box
|
<Box
|
||||||
p="sm"
|
p="sm"
|
||||||
style={{
|
style={{
|
||||||
cursor: 'pointer',
|
cursor: isHistoryFile ? 'default' : 'pointer',
|
||||||
backgroundColor: isSelected ? 'var(--mantine-color-gray-1)' : (shouldShowHovered ? 'var(--mantine-color-gray-1)' : 'var(--bg-file-list)'),
|
backgroundColor: isSelected
|
||||||
|
? 'var(--mantine-color-gray-1)'
|
||||||
|
: (shouldShowHovered ? 'var(--mantine-color-gray-1)' : 'var(--bg-file-list)'),
|
||||||
opacity: isSupported ? 1 : 0.5,
|
opacity: isSupported ? 1 : 0.5,
|
||||||
transition: 'background-color 0.15s ease',
|
transition: 'background-color 0.15s ease',
|
||||||
userSelect: 'none',
|
userSelect: 'none',
|
||||||
@ -65,32 +68,34 @@ const FileListItem: React.FC<FileListItemProps> = ({
|
|||||||
paddingLeft: isHistoryFile ? '2rem' : '0.75rem', // Indent history files
|
paddingLeft: isHistoryFile ? '2rem' : '0.75rem', // Indent history files
|
||||||
borderLeft: isHistoryFile ? '3px solid var(--mantine-color-blue-4)' : 'none' // Visual indicator for history
|
borderLeft: isHistoryFile ? '3px solid var(--mantine-color-blue-4)' : 'none' // Visual indicator for history
|
||||||
}}
|
}}
|
||||||
onClick={(e) => onSelect(e.shiftKey)}
|
onClick={isHistoryFile ? undefined : (e) => onSelect(e.shiftKey)}
|
||||||
onDoubleClick={onDoubleClick}
|
onDoubleClick={onDoubleClick}
|
||||||
onMouseEnter={() => setIsHovered(true)}
|
onMouseEnter={() => setIsHovered(true)}
|
||||||
onMouseLeave={() => setIsHovered(false)}
|
onMouseLeave={() => setIsHovered(false)}
|
||||||
>
|
>
|
||||||
<Group gap="sm">
|
<Group gap="sm">
|
||||||
<Box>
|
{!isHistoryFile && (
|
||||||
{/* Checkbox for all files */}
|
<Box>
|
||||||
<Checkbox
|
{/* Checkbox for regular files only */}
|
||||||
checked={isSelected}
|
<Checkbox
|
||||||
onChange={() => {}} // Handled by parent onClick
|
checked={isSelected}
|
||||||
size="sm"
|
onChange={() => {}} // Handled by parent onClick
|
||||||
pl="sm"
|
size="sm"
|
||||||
pr="xs"
|
pl="sm"
|
||||||
styles={{
|
pr="xs"
|
||||||
input: {
|
styles={{
|
||||||
cursor: 'pointer'
|
input: {
|
||||||
}
|
cursor: 'pointer'
|
||||||
}}
|
}
|
||||||
/>
|
}}
|
||||||
</Box>
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
<Box style={{ flex: 1, minWidth: 0 }}>
|
<Box style={{ flex: 1, minWidth: 0 }}>
|
||||||
<Group gap="xs" align="center">
|
<Group gap="xs" align="center">
|
||||||
<Text size="sm" fw={500} truncate style={{ flex: 1 }}>{file.name}</Text>
|
<Text size="sm" fw={500} truncate style={{ flex: 1 }}>{file.name}</Text>
|
||||||
<Badge size="xs" variant="light" color={currentVersion > 1 ? "blue" : "gray"}>
|
<Badge size="xs" variant="light" color={"blue"}>
|
||||||
v{currentVersion}
|
v{currentVersion}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|
||||||
@ -172,17 +177,17 @@ const FileListItem: React.FC<FileListItemProps> = ({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Add to Recents option for history files */}
|
{/* Restore option for history files */}
|
||||||
{isHistoryFile && (
|
{isHistoryFile && (
|
||||||
<>
|
<>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
leftSection={<AddIcon style={{ fontSize: 16 }} />}
|
leftSection={<RestoreIcon style={{ fontSize: 16 }} />}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
onAddToRecents(file);
|
onAddToRecents(file);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('fileManager.addToRecents', 'Add to Recents')}
|
{t('fileManager.restore', 'Restore')}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Divider />
|
<Menu.Divider />
|
||||||
</>
|
</>
|
||||||
|
@ -17,6 +17,7 @@ interface FileManagerContextValue {
|
|||||||
selectedFilesSet: Set<string>;
|
selectedFilesSet: Set<string>;
|
||||||
expandedFileIds: Set<string>;
|
expandedFileIds: Set<string>;
|
||||||
fileGroups: Map<string, StirlingFileStub[]>;
|
fileGroups: Map<string, StirlingFileStub[]>;
|
||||||
|
loadedHistoryFiles: Map<FileId, StirlingFileStub[]>;
|
||||||
|
|
||||||
// Handlers
|
// Handlers
|
||||||
onSourceChange: (source: 'recent' | 'local' | 'drive') => void;
|
onSourceChange: (source: 'recent' | 'local' | 'drive') => void;
|
||||||
@ -103,24 +104,9 @@ export const FileManagerProvider: React.FC<FileManagerProviderProps> = ({
|
|||||||
const displayFiles = useMemo(() => {
|
const displayFiles = useMemo(() => {
|
||||||
if (!recentFiles || recentFiles.length === 0) return [];
|
if (!recentFiles || recentFiles.length === 0) return [];
|
||||||
|
|
||||||
const expandedFiles = [];
|
// Only return leaf files - history files will be handled by separate components
|
||||||
|
return recentFiles;
|
||||||
// Since we now only load leaf files, iterate through recent files directly
|
}, [recentFiles]);
|
||||||
for (const leafFile of recentFiles) {
|
|
||||||
// Add the leaf file (main file shown in list)
|
|
||||||
expandedFiles.push(leafFile);
|
|
||||||
|
|
||||||
// If expanded, add the loaded history files
|
|
||||||
if (expandedFileIds.has(leafFile.id)) {
|
|
||||||
const historyFiles = loadedHistoryFiles.get(leafFile.id) || [];
|
|
||||||
// Sort history files by version number (oldest first)
|
|
||||||
const sortedHistory = historyFiles.sort((a, b) => (a.versionNumber || 1) - (b.versionNumber || 1));
|
|
||||||
expandedFiles.push(...sortedHistory);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return expandedFiles;
|
|
||||||
}, [recentFiles, expandedFileIds, loadedHistoryFiles]);
|
|
||||||
|
|
||||||
const selectedFiles = selectedFileIds.length === 0 ? [] :
|
const selectedFiles = selectedFileIds.length === 0 ? [] :
|
||||||
displayFiles.filter(file => selectedFilesSet.has(file.id));
|
displayFiles.filter(file => selectedFilesSet.has(file.id));
|
||||||
@ -533,6 +519,7 @@ export const FileManagerProvider: React.FC<FileManagerProviderProps> = ({
|
|||||||
selectedFilesSet,
|
selectedFilesSet,
|
||||||
expandedFileIds,
|
expandedFileIds,
|
||||||
fileGroups,
|
fileGroups,
|
||||||
|
loadedHistoryFiles,
|
||||||
|
|
||||||
// Handlers
|
// Handlers
|
||||||
onSourceChange: handleSourceChange,
|
onSourceChange: handleSourceChange,
|
||||||
@ -564,6 +551,7 @@ export const FileManagerProvider: React.FC<FileManagerProviderProps> = ({
|
|||||||
fileInputRef,
|
fileInputRef,
|
||||||
expandedFileIds,
|
expandedFileIds,
|
||||||
fileGroups,
|
fileGroups,
|
||||||
|
loadedHistoryFiles,
|
||||||
handleSourceChange,
|
handleSourceChange,
|
||||||
handleLocalFileClick,
|
handleLocalFileClick,
|
||||||
handleFileSelect,
|
handleFileSelect,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user