Co-authored-by: ConnorYoh <[email protected]>
Co-authored-by: Connor Yoh <[email protected]>
Co-authored-by: EthanHealy01 <[email protected]>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
This commit is contained in:
Anthony Stirling
2026-03-25 11:00:40 +00:00
committed by GitHub
co-authored by ConnorYoh Connor Yoh EthanHealy01 Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
parent 47cad0a131
commit 28613caf8a
181 changed files with 25715 additions and 124 deletions
@@ -8,6 +8,8 @@ import { useFileActionIcons } from '@app/hooks/useFileActionIcons';
import CloseIcon from '@mui/icons-material/Close';
import VisibilityIcon from '@mui/icons-material/Visibility';
import UnarchiveIcon from '@mui/icons-material/Unarchive';
import CloudUploadIcon from '@mui/icons-material/CloudUpload';
import LinkIcon from '@mui/icons-material/Link';
import PushPinIcon from '@mui/icons-material/PushPin';
import PushPinOutlinedIcon from '@mui/icons-material/PushPinOutlined';
import LockOpenIcon from '@mui/icons-material/LockOpen';
@@ -26,6 +28,9 @@ import HoverActionMenu, { HoverAction } from '@app/components/shared/HoverAction
import { downloadFile } from '@app/services/downloadService';
import FileEditorFileName from '@app/components/fileEditor/FileEditorFileName';
import { PrivateContent } from '@app/components/shared/PrivateContent';
import UploadToServerModal from '@app/components/shared/UploadToServerModal';
import ShareFileModal from '@app/components/shared/ShareFileModal';
import { useAppConfig } from '@app/contexts/AppConfigContext';
@@ -60,6 +65,7 @@ const FileEditorThumbnail = ({
isSupported = true,
}: FileEditorThumbnailProps) => {
const { t } = useTranslation();
const { config } = useAppConfig();
const terminology = useFileActionTerminology();
const icons = useFileActionIcons();
const DownloadOutlinedIcon = icons.download;
@@ -80,6 +86,10 @@ const FileEditorThumbnail = ({
const [showHoverMenu, setShowHoverMenu] = useState(false);
const isMobile = useIsMobile();
const [showCloseModal, setShowCloseModal] = useState(false);
const [showUploadModal, setShowUploadModal] = useState(false);
const [showShareModal, setShowShareModal] = useState(false);
const [showSharedEditNotice, setShowSharedEditNotice] = useState(false);
const sharedEditNoticeShownRef = useRef(false);
// Resolve the actual File object for pin/unpin operations
const actualFile = useMemo(() => {
@@ -115,6 +125,17 @@ const FileEditorThumbnail = ({
const isCBZ = extLower === 'cbz';
const isCBR = extLower === 'cbr';
const uploadEnabled = config?.storageEnabled === true;
const sharingEnabled = uploadEnabled && config?.storageSharingEnabled === true;
const shareLinksEnabled = sharingEnabled && config?.storageShareLinksEnabled === true;
const isOwnedOrLocal = file.remoteOwnedByCurrentUser !== false;
const isSharedFile = file.remoteOwnedByCurrentUser === false || file.remoteSharedViaLink;
const localUpdatedAt = file.createdAt ?? file.lastModified ?? 0;
const remoteUpdatedAt = file.remoteStorageUpdatedAt ?? 0;
const isUploaded = Boolean(file.remoteStorageId);
const isUpToDate = isUploaded && remoteUpdatedAt >= localUpdatedAt;
const canUpload = uploadEnabled && isOwnedOrLocal && file.isLeaf && (!isUploaded || !isUpToDate);
const canShare = shareLinksEnabled && isOwnedOrLocal && file.isLeaf;
const pageLabel = useMemo(
() =>
@@ -248,6 +269,30 @@ const FileEditorThumbnail = ({
onDownloadFile(file.id);
},
},
...(canUpload || canShare
? [
...(canUpload ? [{
id: 'upload',
icon: <CloudUploadIcon style={{ fontSize: 20 }} />,
label: isUploaded
? t('fileManager.updateOnServer', 'Update on Server')
: t('fileManager.uploadToServer', 'Upload to Server'),
onClick: (e: React.MouseEvent) => {
e.stopPropagation();
setShowUploadModal(true);
},
}] : []),
...(canShare ? [{
id: 'share',
icon: <LinkIcon style={{ fontSize: 20 }} />,
label: t('fileManager.share', 'Share'),
onClick: (e: React.MouseEvent) => {
e.stopPropagation();
setShowShareModal(true);
},
}] : []),
]
: []),
{
id: 'unzip',
icon: <UnarchiveIcon style={{ fontSize: 20 }} />,
@@ -271,7 +316,22 @@ const FileEditorThumbnail = ({
},
color: 'red',
}
], [t, file.id, file.name, isZipFile, isCBR, onViewFile, onDownloadFile, onUnzipFile, handleCloseWithConfirmation]);
], [
t,
file.id,
file.name,
isZipFile,
isCBZ,
isCBR,
terminology,
onViewFile,
onDownloadFile,
onUnzipFile,
handleCloseWithConfirmation,
canUpload,
canShare,
isUploaded
]);
// ---- Card interactions ----
const handleCardClick = () => {
@@ -280,6 +340,10 @@ const FileEditorThumbnail = ({
if (hasError) {
try { fileActions.clearFileError(file.id); } catch (_e) { void _e; }
}
if (isSharedFile && !sharedEditNoticeShownRef.current) {
sharedEditNoticeShownRef.current = true;
setShowSharedEditNotice(true);
}
onToggleFile(file.id);
};
@@ -537,6 +601,42 @@ const FileEditorThumbnail = ({
)}
</Stack>
</Modal>
<Modal
opened={showSharedEditNotice}
onClose={() => setShowSharedEditNotice(false)}
title={t('fileManager.sharedEditNoticeTitle', 'Read-only server copy')}
centered
size="auto"
>
<Stack gap="md">
<Text size="sm">
{t(
'fileManager.sharedEditNoticeBody',
'You do not have edit rights to the server version of this file. Any edits you make will be saved as a local copy.'
)}
</Text>
<Group justify="flex-end" gap="sm">
<Button onClick={() => setShowSharedEditNotice(false)}>
{t('fileManager.sharedEditNoticeConfirm', 'Got it')}
</Button>
</Group>
</Stack>
</Modal>
{canUpload && (
<UploadToServerModal
opened={showUploadModal}
onClose={() => setShowUploadModal(false)}
file={file}
/>
)}
{canShare && (
<ShareFileModal
opened={showShareModal}
onClose={() => setShowShareModal(false)}
file={file}
/>
)}
</div>
);
};