mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
316 lines
9.6 KiB
JavaScript
316 lines
9.6 KiB
JavaScript
import { useEffect, useLayoutEffect, useRef } from "react";
|
|
|
|
import { filenameLabelForEntry } from "../../../lib/workbench/sidebar.js";
|
|
|
|
function normalizeRestoredOpenTabs(openTabs, createTabRecord, initialSelectedTabSnapshot) {
|
|
const restoredTabs = Array.isArray(openTabs) ? openTabs : [];
|
|
const defaultTabToolMode = String(initialSelectedTabSnapshot?.tabToolMode || "").trim();
|
|
const defaultDrawingTool = String(initialSelectedTabSnapshot?.drawingTool || "").trim();
|
|
|
|
if (!defaultTabToolMode || !defaultDrawingTool) {
|
|
return restoredTabs;
|
|
}
|
|
|
|
return restoredTabs.map((tab) => {
|
|
const tabToolMode = String(tab?.tabToolMode || "").trim();
|
|
const drawingTool = String(tab?.drawingTool || "").trim();
|
|
const drawingStrokes = Array.isArray(tab?.drawingStrokes) ? tab.drawingStrokes : [];
|
|
const drawingUndoStack = Array.isArray(tab?.drawingUndoStack) ? tab.drawingUndoStack : [];
|
|
const drawingRedoStack = Array.isArray(tab?.drawingRedoStack) ? tab.drawingRedoStack : [];
|
|
|
|
if (
|
|
tabToolMode !== "draw" ||
|
|
drawingTool !== defaultDrawingTool ||
|
|
drawingStrokes.length ||
|
|
drawingUndoStack.length ||
|
|
drawingRedoStack.length
|
|
) {
|
|
return tab;
|
|
}
|
|
|
|
return createTabRecord(tab.key, {
|
|
...tab,
|
|
tabToolMode: defaultTabToolMode
|
|
});
|
|
});
|
|
}
|
|
|
|
export function restoredSidebarWidthForViewport(
|
|
restoredSession,
|
|
{
|
|
desktopViewport = false,
|
|
defaultSidebarWidth = 260,
|
|
sidebarMinWidth = 0
|
|
} = {}
|
|
) {
|
|
const restoredWidth = Number(restoredSession?.sidebarWidth);
|
|
const nextWidth = Number.isFinite(restoredWidth) ? restoredWidth : defaultSidebarWidth;
|
|
|
|
if (desktopViewport && (!restoredSession?.sidebarOpen || nextWidth <= sidebarMinWidth)) {
|
|
return defaultSidebarWidth;
|
|
}
|
|
|
|
return nextWidth;
|
|
}
|
|
|
|
export function useCadWorkspaceSession({
|
|
manifestEntries,
|
|
fileKey,
|
|
readCadWorkspaceSessionState,
|
|
restoredCadWorkspaceSessionRef,
|
|
cadWorkspaceSessionBootstrappedRef,
|
|
cadWorkspaceSessionPersistenceReadyRef,
|
|
setQuery,
|
|
setExpandedDirectoryIds,
|
|
setSidebarOpen,
|
|
setSidebarWidth,
|
|
setTabToolsWidth,
|
|
setUrdfEntryAnimationEnabled,
|
|
setOpenTabs,
|
|
applyTabRecord,
|
|
selectedEntryKeyFromUrl,
|
|
createTabRecord,
|
|
initialSelectedTabSnapshot = null,
|
|
upsertTabRecord,
|
|
buildPersistedCadWorkspaceSession,
|
|
scheduleCadWorkspaceSessionPersistence = () => {},
|
|
flushCadWorkspaceSessionPersistence = () => {},
|
|
selectedEntry,
|
|
defaultDocumentTitle,
|
|
selectedKey,
|
|
entryMap,
|
|
buildActiveTabSnapshot,
|
|
catalogEntries,
|
|
manifestRevision = 0,
|
|
defaultSidebarWidth = 260,
|
|
sidebarMinWidth = 0,
|
|
readCadParam = () => null,
|
|
readCadRefQueryParams = () => [],
|
|
setPendingCadRefQueryParams = () => {},
|
|
activateEntryTab,
|
|
resetActiveWorkspace,
|
|
writeCadParam
|
|
}) {
|
|
const initialManifestRevisionRef = useRef(manifestRevision);
|
|
const initialUnresolvedUrlSelectionRef = useRef(false);
|
|
|
|
useLayoutEffect(() => {
|
|
if (cadWorkspaceSessionBootstrappedRef.current) {
|
|
return;
|
|
}
|
|
cadWorkspaceSessionBootstrappedRef.current = true;
|
|
|
|
const validEntryKeys = manifestEntries.length
|
|
? new Set(manifestEntries.map((entry) => fileKey(entry)))
|
|
: undefined;
|
|
const initialCadRefQueryParams = readCadRefQueryParams();
|
|
if (initialCadRefQueryParams.length) {
|
|
setPendingCadRefQueryParams(initialCadRefQueryParams);
|
|
}
|
|
const urlSelectedKey = selectedEntryKeyFromUrl(manifestEntries);
|
|
initialUnresolvedUrlSelectionRef.current = Boolean(readCadParam() || initialCadRefQueryParams.length) && !urlSelectedKey;
|
|
const restoredSession = readCadWorkspaceSessionState(validEntryKeys);
|
|
if (restoredSession) {
|
|
restoredCadWorkspaceSessionRef.current = true;
|
|
const desktopViewport = window.matchMedia("(min-width: 1024px)").matches;
|
|
const restoredSidebarWidth = restoredSidebarWidthForViewport(restoredSession, {
|
|
desktopViewport,
|
|
defaultSidebarWidth,
|
|
sidebarMinWidth
|
|
});
|
|
const restoredOpenTabs = normalizeRestoredOpenTabs(
|
|
restoredSession.openTabs,
|
|
createTabRecord,
|
|
initialSelectedTabSnapshot
|
|
);
|
|
setQuery(restoredSession.query);
|
|
setExpandedDirectoryIds(new Set(restoredSession.expandedDirectoryIds));
|
|
setSidebarOpen(desktopViewport ? true : restoredSession.sidebarOpen);
|
|
setSidebarWidth(restoredSidebarWidth);
|
|
setTabToolsWidth(restoredSession.tabToolsWidth);
|
|
setUrdfEntryAnimationEnabled(restoredSession.urdfEntryAnimationEnabled);
|
|
|
|
const urlActiveTab = urlSelectedKey
|
|
? (
|
|
initialCadRefQueryParams.length
|
|
? null
|
|
: restoredOpenTabs.find((tab) => tab.key === urlSelectedKey) || null
|
|
)
|
|
: null;
|
|
const activeTab = urlSelectedKey
|
|
? (urlActiveTab || createTabRecord(urlSelectedKey, initialSelectedTabSnapshot || {}))
|
|
: initialUnresolvedUrlSelectionRef.current
|
|
? null
|
|
: restoredOpenTabs.find((tab) => tab.key === restoredSession.selectedKey) || null;
|
|
|
|
setOpenTabs(urlSelectedKey
|
|
? upsertTabRecord(restoredOpenTabs, urlSelectedKey, activeTab)
|
|
: restoredOpenTabs);
|
|
if (activeTab) {
|
|
applyTabRecord(activeTab);
|
|
}
|
|
cadWorkspaceSessionPersistenceReadyRef.current = true;
|
|
return;
|
|
}
|
|
|
|
if (urlSelectedKey) {
|
|
const nextTab = createTabRecord(urlSelectedKey, initialSelectedTabSnapshot || {});
|
|
setOpenTabs((current) => upsertTabRecord(current, urlSelectedKey, nextTab));
|
|
applyTabRecord(nextTab);
|
|
}
|
|
cadWorkspaceSessionPersistenceReadyRef.current = true;
|
|
}, [
|
|
applyTabRecord,
|
|
createTabRecord,
|
|
fileKey,
|
|
manifestEntries,
|
|
defaultSidebarWidth,
|
|
readCadRefQueryParams,
|
|
readCadParam,
|
|
readCadWorkspaceSessionState,
|
|
restoredCadWorkspaceSessionRef,
|
|
selectedEntryKeyFromUrl,
|
|
setExpandedDirectoryIds,
|
|
setOpenTabs,
|
|
setPendingCadRefQueryParams,
|
|
setQuery,
|
|
setSidebarOpen,
|
|
setSidebarWidth,
|
|
setTabToolsWidth,
|
|
setUrdfEntryAnimationEnabled,
|
|
upsertTabRecord,
|
|
initialSelectedTabSnapshot,
|
|
cadWorkspaceSessionBootstrappedRef,
|
|
cadWorkspaceSessionPersistenceReadyRef,
|
|
sidebarMinWidth
|
|
]);
|
|
|
|
useEffect(() => {
|
|
if (!cadWorkspaceSessionPersistenceReadyRef.current) {
|
|
return;
|
|
}
|
|
scheduleCadWorkspaceSessionPersistence();
|
|
}, [buildPersistedCadWorkspaceSession, scheduleCadWorkspaceSessionPersistence, cadWorkspaceSessionPersistenceReadyRef]);
|
|
|
|
useEffect(() => {
|
|
const persistSession = () => {
|
|
if (!cadWorkspaceSessionPersistenceReadyRef.current) {
|
|
return;
|
|
}
|
|
flushCadWorkspaceSessionPersistence();
|
|
};
|
|
|
|
window.addEventListener("pagehide", persistSession);
|
|
window.addEventListener("beforeunload", persistSession);
|
|
|
|
return () => {
|
|
window.removeEventListener("pagehide", persistSession);
|
|
window.removeEventListener("beforeunload", persistSession);
|
|
persistSession();
|
|
};
|
|
}, [flushCadWorkspaceSessionPersistence, cadWorkspaceSessionPersistenceReadyRef]);
|
|
|
|
useEffect(() => {
|
|
const filename = filenameLabelForEntry(selectedEntry);
|
|
document.title = filename ? `${defaultDocumentTitle} | ${filename}` : defaultDocumentTitle;
|
|
}, [defaultDocumentTitle, selectedEntry]);
|
|
|
|
useEffect(() => {
|
|
if (selectedKey && entryMap.has(selectedKey)) {
|
|
setOpenTabs((current) => upsertTabRecord(current, selectedKey, buildActiveTabSnapshot()));
|
|
return;
|
|
}
|
|
|
|
const nextSelectedKey = selectedEntryKeyFromUrl(catalogEntries);
|
|
if (!nextSelectedKey) {
|
|
if (selectedKey) {
|
|
if (manifestRevision === initialManifestRevisionRef.current && !entryMap.size) {
|
|
return;
|
|
}
|
|
resetActiveWorkspace();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (nextSelectedKey !== selectedKey) {
|
|
activateEntryTab(nextSelectedKey);
|
|
}
|
|
}, [
|
|
activateEntryTab,
|
|
buildActiveTabSnapshot,
|
|
catalogEntries,
|
|
entryMap,
|
|
manifestRevision,
|
|
resetActiveWorkspace,
|
|
selectedEntryKeyFromUrl,
|
|
selectedKey,
|
|
setOpenTabs,
|
|
upsertTabRecord
|
|
]);
|
|
|
|
useEffect(() => {
|
|
const syncSelectionFromHistory = () => {
|
|
const nextCadRefQueryParams = readCadRefQueryParams();
|
|
setPendingCadRefQueryParams(nextCadRefQueryParams);
|
|
|
|
const nextSelectedKey = selectedEntryKeyFromUrl(catalogEntries);
|
|
if (nextSelectedKey) {
|
|
activateEntryTab(nextSelectedKey);
|
|
return;
|
|
}
|
|
resetActiveWorkspace();
|
|
};
|
|
|
|
window.addEventListener("popstate", syncSelectionFromHistory);
|
|
return () => {
|
|
window.removeEventListener("popstate", syncSelectionFromHistory);
|
|
};
|
|
}, [
|
|
activateEntryTab,
|
|
catalogEntries,
|
|
readCadRefQueryParams,
|
|
resetActiveWorkspace,
|
|
selectedEntryKeyFromUrl,
|
|
setPendingCadRefQueryParams
|
|
]);
|
|
|
|
useEffect(() => {
|
|
if (selectedEntry) {
|
|
writeCadParam(fileKey(selectedEntry));
|
|
return;
|
|
}
|
|
if (!selectedKey) {
|
|
const unresolvedUrlSelection = Boolean(readCadParam() || readCadRefQueryParams().length);
|
|
if (unresolvedUrlSelection) {
|
|
const urlSelectedKey = selectedEntryKeyFromUrl(catalogEntries);
|
|
if (
|
|
urlSelectedKey ||
|
|
(
|
|
initialUnresolvedUrlSelectionRef.current &&
|
|
(manifestRevision === initialManifestRevisionRef.current || !entryMap.size)
|
|
)
|
|
) {
|
|
return;
|
|
}
|
|
}
|
|
writeCadParam("");
|
|
}
|
|
}, [
|
|
catalogEntries,
|
|
fileKey,
|
|
manifestRevision,
|
|
readCadParam,
|
|
readCadRefQueryParams,
|
|
selectedEntry,
|
|
selectedEntryKeyFromUrl,
|
|
selectedKey,
|
|
writeCadParam
|
|
]);
|
|
|
|
useEffect(() => {
|
|
if (!selectedKey) {
|
|
return;
|
|
}
|
|
setOpenTabs((current) => upsertTabRecord(current, selectedKey, buildActiveTabSnapshot()));
|
|
}, [buildActiveTabSnapshot, selectedKey, setOpenTabs, upsertTabRecord]);
|
|
}
|