mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 11:23:10 +02:00
UI redesign staging (#6149)
Co-authored-by: Reece Browne <[email protected]> Co-authored-by: James Brunton <[email protected]>
This commit is contained in:
co-authored by
Reece Browne
James Brunton
parent
beb99e273b
commit
c731d5fd5d
@@ -5,6 +5,7 @@ import React, {
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Box, Center, Text, ActionIcon, Button, Stack } from "@mantine/core";
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
import LockIcon from "@mui/icons-material/Lock";
|
||||
@@ -27,7 +28,7 @@ import { useRedaction } from "@app/contexts/RedactionContext";
|
||||
import type { RedactionPendingTrackerAPI } from "@app/components/viewer/RedactionPendingTracker";
|
||||
import { createStirlingFilesAndStubs } from "@app/services/fileStubHelpers";
|
||||
import { isStirlingFile, getFormFillFileId } from "@app/types/fileContext";
|
||||
import { useViewerRightRailButtons } from "@app/components/viewer/useViewerRightRailButtons";
|
||||
import { useViewerWorkbenchBarButtons } from "@app/components/viewer/useViewerWorkbenchBarButtons";
|
||||
import { StampPlacementOverlay } from "@app/components/viewer/StampPlacementOverlay";
|
||||
import {
|
||||
RulerOverlay,
|
||||
@@ -138,8 +139,6 @@ export interface EmbedPdfViewerProps {
|
||||
setSidebarsVisible: (v: boolean) => void;
|
||||
onClose?: () => void;
|
||||
previewFile?: File | null;
|
||||
activeFileIndex?: number;
|
||||
setActiveFileIndex?: (index: number) => void;
|
||||
}
|
||||
|
||||
const EmbedPdfViewerContent = ({
|
||||
@@ -147,9 +146,8 @@ const EmbedPdfViewerContent = ({
|
||||
setSidebarsVisible: _setSidebarsVisible,
|
||||
onClose,
|
||||
previewFile,
|
||||
activeFileIndex: externalActiveFileIndex,
|
||||
setActiveFileIndex: externalSetActiveFileIndex,
|
||||
}: EmbedPdfViewerProps) => {
|
||||
const { t } = useTranslation();
|
||||
const viewerRef = React.useRef<HTMLDivElement>(null);
|
||||
const pdfContainerRef = useRef<HTMLDivElement>(null);
|
||||
const [isViewerHovered, setIsViewerHovered] = React.useState(false);
|
||||
@@ -178,6 +176,9 @@ const EmbedPdfViewerContent = ({
|
||||
applyChanges: viewerApplyChanges,
|
||||
pdfRenderMode,
|
||||
cyclePdfRenderMode,
|
||||
setActiveFileIndex: _setActiveFileIndex,
|
||||
activeFileId,
|
||||
setActiveFileId,
|
||||
} = useViewer();
|
||||
|
||||
const scrollState = getScrollState();
|
||||
@@ -327,63 +328,25 @@ const EmbedPdfViewerContent = ({
|
||||
isInAnnotationTool && isPlacementMode && signatureConfig,
|
||||
);
|
||||
|
||||
// Track which file tab is active
|
||||
const [internalActiveFileIndex, setInternalActiveFileIndex] = useState(0);
|
||||
const activeFileIndex = externalActiveFileIndex ?? internalActiveFileIndex;
|
||||
const setActiveFileIndex =
|
||||
externalSetActiveFileIndex ?? setInternalActiveFileIndex;
|
||||
|
||||
// activeFileId (from ViewerContext) is the stable source of truth.
|
||||
// We derive activeFileIndex from it so reorders after tool operations don't lose the viewed file.
|
||||
const { activeFileId, setActiveFileId } = useViewer();
|
||||
|
||||
// Stable string key representing the current file list order.
|
||||
// Using a joined ID string avoids depending on the activeFiles array reference,
|
||||
// which is a new object every render and would cause an infinite effect loop.
|
||||
const fileIdsKey = activeFiles.map((f) => f.fileId).join(",");
|
||||
|
||||
// When the file list actually changes, re-derive activeFileIndex from the stable activeFileId.
|
||||
useEffect(() => {
|
||||
if (!activeFileId || activeFiles.length === 0) return;
|
||||
const newIndex = activeFiles.findIndex((f) => f.fileId === activeFileId);
|
||||
if (newIndex !== -1 && newIndex !== activeFileIndex) {
|
||||
setActiveFileIndex(newIndex);
|
||||
}
|
||||
}, [fileIdsKey, activeFileId]); // stable primitives — no infinite loop
|
||||
|
||||
// When the user manually switches file tabs, keep activeFileId in sync.
|
||||
// Skips the initial mount to avoid overwriting an activeFileId set by handleViewFile.
|
||||
const activeFileIndexMountedRef = useRef(false);
|
||||
useEffect(() => {
|
||||
if (!activeFileIndexMountedRef.current) {
|
||||
activeFileIndexMountedRef.current = true;
|
||||
return;
|
||||
}
|
||||
const fileId = activeFilesRef.current[activeFileIndex]?.fileId;
|
||||
if (fileId && fileId !== activeFileId) {
|
||||
setActiveFileId(fileId);
|
||||
}
|
||||
}, [activeFileIndex]);
|
||||
|
||||
// Reset active tab if it's out of bounds (safety net)
|
||||
useEffect(() => {
|
||||
if (activeFileIndex >= activeFiles.length && activeFiles.length > 0) {
|
||||
setActiveFileIndex(0);
|
||||
}
|
||||
}, [activeFiles.length, activeFileIndex]);
|
||||
|
||||
// Determine which file to display
|
||||
// Determine which file to display — use activeFileId (stable) not activeFileIndex (shifts on removal)
|
||||
const currentFile = React.useMemo(() => {
|
||||
if (previewFile) {
|
||||
return previewFile;
|
||||
} else if (activeFiles.length > 0) {
|
||||
return activeFiles[activeFileIndex] || activeFiles[0];
|
||||
const byId = activeFileId
|
||||
? activeFiles.find(
|
||||
(f) => isStirlingFile(f) && f.fileId === activeFileId,
|
||||
)
|
||||
: null;
|
||||
return byId || activeFiles[0];
|
||||
}
|
||||
return null;
|
||||
}, [previewFile, activeFiles, activeFileIndex]);
|
||||
}, [previewFile, activeFiles, activeFileId]);
|
||||
|
||||
// Get file with URL for rendering
|
||||
const fileWithUrl = useFileWithUrl(currentFile);
|
||||
// Stable id — avoids blob URL churn when FileContext recreates file objects each render.
|
||||
const currentFileStableId =
|
||||
currentFile && isStirlingFile(currentFile) ? currentFile.fileId : null;
|
||||
const fileWithUrl = useFileWithUrl(currentFile, currentFileStableId);
|
||||
|
||||
// Determine the effective file to display
|
||||
const effectiveFile = React.useMemo(() => {
|
||||
@@ -703,7 +666,7 @@ const EmbedPdfViewerContent = ({
|
||||
|
||||
// Step 3: Create StirlingFiles and stubs for version history
|
||||
// Only consume the current file, not all active files
|
||||
const currentFileId = activeFiles[activeFileIndex]?.fileId;
|
||||
const currentFileId = currentFileStableId;
|
||||
if (!currentFileId) throw new Error("Current file ID not found");
|
||||
|
||||
const parentStub = selectors.getStirlingFileStub(currentFileId);
|
||||
@@ -739,7 +702,6 @@ const EmbedPdfViewerContent = ({
|
||||
}, [
|
||||
currentFile,
|
||||
activeFiles,
|
||||
activeFileIndex,
|
||||
exportActions,
|
||||
actions,
|
||||
selectors,
|
||||
@@ -773,7 +735,7 @@ const EmbedPdfViewerContent = ({
|
||||
});
|
||||
|
||||
// Get current file info for creating the updated version
|
||||
const currentFileId = activeFiles[activeFileIndex]?.fileId;
|
||||
const currentFileId = currentFileStableId;
|
||||
if (!currentFileId) throw new Error("Current file ID not found");
|
||||
|
||||
const parentStub = selectors.getStirlingFileStub(currentFileId);
|
||||
@@ -811,7 +773,6 @@ const EmbedPdfViewerContent = ({
|
||||
[
|
||||
currentFile,
|
||||
activeFiles,
|
||||
activeFileIndex,
|
||||
actions,
|
||||
selectors,
|
||||
activeFileIds.length,
|
||||
@@ -847,7 +808,7 @@ const EmbedPdfViewerContent = ({
|
||||
type: "application/pdf",
|
||||
});
|
||||
|
||||
const currentFileId = activeFiles[activeFileIndex]?.fileId;
|
||||
const currentFileId = currentFileStableId;
|
||||
if (!currentFileId) throw new Error("Current file ID not found");
|
||||
|
||||
const parentStub = selectors.getStirlingFileStub(currentFileId);
|
||||
@@ -877,7 +838,6 @@ const EmbedPdfViewerContent = ({
|
||||
[
|
||||
currentFile,
|
||||
activeFiles,
|
||||
activeFileIndex,
|
||||
actions,
|
||||
selectors,
|
||||
activeFileIds.length,
|
||||
@@ -916,7 +876,7 @@ const EmbedPdfViewerContent = ({
|
||||
const file = new File([blob], filename, { type: "application/pdf" });
|
||||
|
||||
// Create StirlingFiles and stubs for version history
|
||||
const currentFileId = activeFiles[activeFileIndex]?.fileId;
|
||||
const currentFileId = currentFileStableId;
|
||||
if (!currentFileId) throw new Error("Current file ID not found");
|
||||
|
||||
const parentStub = selectors.getStirlingFileStub(currentFileId);
|
||||
@@ -949,7 +909,6 @@ const EmbedPdfViewerContent = ({
|
||||
redactionsApplied,
|
||||
currentFile,
|
||||
activeFiles,
|
||||
activeFileIndex,
|
||||
activeFileIds.length,
|
||||
exportActions,
|
||||
actions,
|
||||
@@ -1123,8 +1082,8 @@ const EmbedPdfViewerContent = ({
|
||||
};
|
||||
}, [effectiveFile]);
|
||||
|
||||
// Register viewer right-rail buttons
|
||||
useViewerRightRailButtons(isRulerActive, setIsRulerActive);
|
||||
// Register workbench bar buttons for the viewer
|
||||
useViewerWorkbenchBarButtons(isRulerActive, setIsRulerActive);
|
||||
|
||||
// Auto-fetch form fields when a PDF is loaded in the viewer.
|
||||
// In normal viewer mode, this uses PDFium WASM (frontend-only).
|
||||
@@ -1157,7 +1116,11 @@ const EmbedPdfViewerContent = ({
|
||||
// the effect re-fires before the async fetch completes.
|
||||
}
|
||||
|
||||
if (currentFile && (fileChanged || providerChanged)) {
|
||||
if (
|
||||
currentFile &&
|
||||
!isCurrentFileEncrypted &&
|
||||
(fileChanged || providerChanged)
|
||||
) {
|
||||
console.log("[FormFill] Fetching form fields for:", currentFileId);
|
||||
fetchFormFields(currentFile, currentFileId ?? undefined);
|
||||
}
|
||||
@@ -1219,7 +1182,12 @@ const EmbedPdfViewerContent = ({
|
||||
<Center style={{ flex: 1 }}>
|
||||
<Stack align="center" gap="md">
|
||||
<LockIcon style={{ fontSize: 48, opacity: 0.5 }} />
|
||||
<Text fw={500}>This PDF is password-protected</Text>
|
||||
<Text fw={500}>
|
||||
{t(
|
||||
"encryptedPdfUnlock.viewerLocked",
|
||||
"This PDF is password-protected",
|
||||
)}
|
||||
</Text>
|
||||
<Button
|
||||
variant="filled"
|
||||
onClick={() => {
|
||||
@@ -1228,7 +1196,7 @@ const EmbedPdfViewerContent = ({
|
||||
}
|
||||
}}
|
||||
>
|
||||
Unlock
|
||||
{t("encryptedPdfUnlock.viewerUnlock", "Unlock")}
|
||||
</Button>
|
||||
</Stack>
|
||||
</Center>
|
||||
@@ -1329,7 +1297,7 @@ const EmbedPdfViewerContent = ({
|
||||
<ThumbnailSidebar
|
||||
visible={isThumbnailSidebarVisible}
|
||||
onToggle={toggleThumbnailSidebar}
|
||||
activeFileIndex={activeFileIndex}
|
||||
activeFileId={activeFileId}
|
||||
/>
|
||||
<BookmarkSidebar
|
||||
visible={isBookmarkSidebarVisible}
|
||||
|
||||
Reference in New Issue
Block a user