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",
|
||||
"noFileSelected": "No files selected",
|
||||
"showHistory": "Show History",
|
||||
"hideHistory": "Hide History",
|
||||
"hideHistory": "Hide History",
|
||||
"fileHistory": "FileHistory",
|
||||
"loadingHistory": "Loading History...",
|
||||
"lastModified": "Last Modified",
|
||||
"toolChain": "Tools Applied",
|
||||
"addToRecents": "Add to Recents",
|
||||
"restore": "Restore",
|
||||
"searchFiles": "Search files...",
|
||||
"recent": "Recent",
|
||||
"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 { useTranslation } from 'react-i18next';
|
||||
import FileListItem from './FileListItem';
|
||||
import FileHistoryGroup from './FileHistoryGroup';
|
||||
import { useFileManagerContext } from '../../contexts/FileManagerContext';
|
||||
|
||||
interface FileListAreaProps {
|
||||
@ -22,6 +23,7 @@ const FileListArea: React.FC<FileListAreaProps> = ({
|
||||
selectedFilesSet,
|
||||
fileGroups,
|
||||
expandedFileIds,
|
||||
loadedHistoryFiles,
|
||||
onFileSelect,
|
||||
onFileRemove,
|
||||
onFileDoubleClick,
|
||||
@ -53,24 +55,34 @@ const FileListArea: React.FC<FileListAreaProps> = ({
|
||||
</Center>
|
||||
) : (
|
||||
filteredFiles.map((file, index) => {
|
||||
// Determine if this is a history file based on whether it's in the recent files or loaded as history
|
||||
const isLeafFile = recentFiles.some(rf => rf.id === file.id);
|
||||
const isHistoryFile = !isLeafFile; // If not in recent files, it's a loaded history file
|
||||
const isLatestVersion = isLeafFile; // Only leaf files (from recent files) are latest versions
|
||||
// All files in filteredFiles are now leaf files only
|
||||
const historyFiles = loadedHistoryFiles.get(file.id) || [];
|
||||
const isExpanded = expandedFileIds.has(file.id);
|
||||
|
||||
return (
|
||||
<FileListItem
|
||||
key={file.id}
|
||||
file={file}
|
||||
isSelected={selectedFilesSet.has(file.id)}
|
||||
isSupported={isFileSupported(file.name)}
|
||||
onSelect={(shiftKey) => onFileSelect(file, index, shiftKey)}
|
||||
onRemove={() => onFileRemove(index)}
|
||||
onDownload={() => onDownloadSingle(file)}
|
||||
onDoubleClick={() => onFileDoubleClick(file)}
|
||||
isHistoryFile={isHistoryFile}
|
||||
isLatestVersion={isLatestVersion}
|
||||
/>
|
||||
<React.Fragment key={file.id}>
|
||||
<FileListItem
|
||||
file={file}
|
||||
isSelected={selectedFilesSet.has(file.id)}
|
||||
isSupported={isFileSupported(file.name)}
|
||||
onSelect={(shiftKey) => onFileSelect(file, index, shiftKey)}
|
||||
onRemove={() => onFileRemove(index)}
|
||||
onDownload={() => onDownloadSingle(file)}
|
||||
onDoubleClick={() => onFileDoubleClick(file)}
|
||||
isHistoryFile={false} // All files here are leaf files
|
||||
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 AddIcon from '@mui/icons-material/Add';
|
||||
import HistoryIcon from '@mui/icons-material/History';
|
||||
import RestoreIcon from '@mui/icons-material/Restore';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { getFileSize, getFileDate } from '../../utils/fileUtils';
|
||||
import { StirlingFileStub } from '../../types/fileContext';
|
||||
@ -54,8 +55,10 @@ const FileListItem: React.FC<FileListItemProps> = ({
|
||||
<Box
|
||||
p="sm"
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
backgroundColor: isSelected ? 'var(--mantine-color-gray-1)' : (shouldShowHovered ? 'var(--mantine-color-gray-1)' : 'var(--bg-file-list)'),
|
||||
cursor: isHistoryFile ? 'default' : 'pointer',
|
||||
backgroundColor: isSelected
|
||||
? 'var(--mantine-color-gray-1)'
|
||||
: (shouldShowHovered ? 'var(--mantine-color-gray-1)' : 'var(--bg-file-list)'),
|
||||
opacity: isSupported ? 1 : 0.5,
|
||||
transition: 'background-color 0.15s ease',
|
||||
userSelect: 'none',
|
||||
@ -65,32 +68,34 @@ const FileListItem: React.FC<FileListItemProps> = ({
|
||||
paddingLeft: isHistoryFile ? '2rem' : '0.75rem', // Indent history files
|
||||
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}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
>
|
||||
<Group gap="sm">
|
||||
<Box>
|
||||
{/* Checkbox for all files */}
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
onChange={() => {}} // Handled by parent onClick
|
||||
size="sm"
|
||||
pl="sm"
|
||||
pr="xs"
|
||||
styles={{
|
||||
input: {
|
||||
cursor: 'pointer'
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
{!isHistoryFile && (
|
||||
<Box>
|
||||
{/* Checkbox for regular files only */}
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
onChange={() => {}} // Handled by parent onClick
|
||||
size="sm"
|
||||
pl="sm"
|
||||
pr="xs"
|
||||
styles={{
|
||||
input: {
|
||||
cursor: 'pointer'
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Box style={{ flex: 1, minWidth: 0 }}>
|
||||
<Group gap="xs" align="center">
|
||||
<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}
|
||||
</Badge>
|
||||
|
||||
@ -172,17 +177,17 @@ const FileListItem: React.FC<FileListItemProps> = ({
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Add to Recents option for history files */}
|
||||
{/* Restore option for history files */}
|
||||
{isHistoryFile && (
|
||||
<>
|
||||
<Menu.Item
|
||||
leftSection={<AddIcon style={{ fontSize: 16 }} />}
|
||||
leftSection={<RestoreIcon style={{ fontSize: 16 }} />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onAddToRecents(file);
|
||||
}}
|
||||
>
|
||||
{t('fileManager.addToRecents', 'Add to Recents')}
|
||||
{t('fileManager.restore', 'Restore')}
|
||||
</Menu.Item>
|
||||
<Menu.Divider />
|
||||
</>
|
||||
|
@ -17,6 +17,7 @@ interface FileManagerContextValue {
|
||||
selectedFilesSet: Set<string>;
|
||||
expandedFileIds: Set<string>;
|
||||
fileGroups: Map<string, StirlingFileStub[]>;
|
||||
loadedHistoryFiles: Map<FileId, StirlingFileStub[]>;
|
||||
|
||||
// Handlers
|
||||
onSourceChange: (source: 'recent' | 'local' | 'drive') => void;
|
||||
@ -103,24 +104,9 @@ export const FileManagerProvider: React.FC<FileManagerProviderProps> = ({
|
||||
const displayFiles = useMemo(() => {
|
||||
if (!recentFiles || recentFiles.length === 0) return [];
|
||||
|
||||
const expandedFiles = [];
|
||||
|
||||
// Since we now only load leaf files, iterate through recent files directly
|
||||
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]);
|
||||
// Only return leaf files - history files will be handled by separate components
|
||||
return recentFiles;
|
||||
}, [recentFiles]);
|
||||
|
||||
const selectedFiles = selectedFileIds.length === 0 ? [] :
|
||||
displayFiles.filter(file => selectedFilesSet.has(file.id));
|
||||
@ -533,6 +519,7 @@ export const FileManagerProvider: React.FC<FileManagerProviderProps> = ({
|
||||
selectedFilesSet,
|
||||
expandedFileIds,
|
||||
fileGroups,
|
||||
loadedHistoryFiles,
|
||||
|
||||
// Handlers
|
||||
onSourceChange: handleSourceChange,
|
||||
@ -564,6 +551,7 @@ export const FileManagerProvider: React.FC<FileManagerProviderProps> = ({
|
||||
fileInputRef,
|
||||
expandedFileIds,
|
||||
fileGroups,
|
||||
loadedHistoryFiles,
|
||||
handleSourceChange,
|
||||
handleLocalFileClick,
|
||||
handleFileSelect,
|
||||
|
Loading…
x
Reference in New Issue
Block a user