feat: refactor document opening/preview logic to support preview/sidepanel actions.
This commit is contained in:
@@ -4,8 +4,8 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
import {
|
import {
|
||||||
DocumentsFilterProvider,
|
DocumentsFilterProvider,
|
||||||
} from '../documents/context/DocumentsFilterContext';
|
} from '../documents/context/DocumentsFilterContext';
|
||||||
import { PreviewProvider } from '../preview/PreviewContext';
|
import { PreviewProvider, usePreviewContext } from '../preview/PreviewContext';
|
||||||
import { DocumentOpenProvider } from '../context/DocumentOpenContext';
|
import { DocumentOpenProvider } from '../contexts/DocumentOpenContext';
|
||||||
import { useWorkspaceSurface } from './useWorkspaceSurface';
|
import { useWorkspaceSurface } from './useWorkspaceSurface';
|
||||||
import { DocumentsHeaderBreadcrumb } from '../documents/panel/DocumentsPanelHeader';
|
import { DocumentsHeaderBreadcrumb } from '../documents/panel/DocumentsPanelHeader';
|
||||||
import { SidebarProvider, useSidebarContext } from '../sidebar/SidebarContext';
|
import { SidebarProvider, useSidebarContext } from '../sidebar/SidebarContext';
|
||||||
@@ -24,8 +24,11 @@ const DocumentsRouteContent: React.FC = () => {
|
|||||||
const {
|
const {
|
||||||
sidebarSuppressed,
|
sidebarSuppressed,
|
||||||
expandSidebar,
|
expandSidebar,
|
||||||
|
openDetailPanel,
|
||||||
} = usePanelManager();
|
} = usePanelManager();
|
||||||
|
|
||||||
|
const { openPreview } = usePreviewContext();
|
||||||
|
|
||||||
const sidebarHidden = sidebarCollapsed || sidebarSuppressed;
|
const sidebarHidden = sidebarCollapsed || sidebarSuppressed;
|
||||||
|
|
||||||
const handleHeaderBreadcrumbClick = useCallback((crumb: DocumentsHeaderBreadcrumb) => {
|
const handleHeaderBreadcrumbClick = useCallback((crumb: DocumentsHeaderBreadcrumb) => {
|
||||||
@@ -40,6 +43,12 @@ const DocumentsRouteContent: React.FC = () => {
|
|||||||
navigate(`/documents/${documentId}`);
|
navigate(`/documents/${documentId}`);
|
||||||
}, [navigate]);
|
}, [navigate]);
|
||||||
|
|
||||||
|
const handleOpenSidepanel = useCallback((docId: string) => {
|
||||||
|
if (openDetailPanel) {
|
||||||
|
openDetailPanel({ documentIds: [docId] });
|
||||||
|
}
|
||||||
|
}, [openDetailPanel]);
|
||||||
|
|
||||||
const documentsTablePropsWithNav = useMemo(() => (
|
const documentsTablePropsWithNav = useMemo(() => (
|
||||||
surfaceConfig.documentsTableProps
|
surfaceConfig.documentsTableProps
|
||||||
? { ...surfaceConfig.documentsTableProps, onBreadcrumbNavigate: handleHeaderBreadcrumbClick }
|
? { ...surfaceConfig.documentsTableProps, onBreadcrumbNavigate: handleHeaderBreadcrumbClick }
|
||||||
@@ -84,7 +93,11 @@ const DocumentsRouteContent: React.FC = () => {
|
|||||||
<PreviewProvider
|
<PreviewProvider
|
||||||
onNavigate={handleDocumentNavigate}
|
onNavigate={handleDocumentNavigate}
|
||||||
>
|
>
|
||||||
<DocumentOpenProvider onOpenViewer={handleDocumentNavigate}>
|
<DocumentOpenProvider
|
||||||
|
onOpenViewer={handleDocumentNavigate}
|
||||||
|
onOpenPreview={openPreview}
|
||||||
|
onOpenSidepanel={handleOpenSidepanel}
|
||||||
|
>
|
||||||
{content}
|
{content}
|
||||||
</DocumentOpenProvider>
|
</DocumentOpenProvider>
|
||||||
</PreviewProvider>
|
</PreviewProvider>
|
||||||
|
|||||||
+18
-14
@@ -1,6 +1,4 @@
|
|||||||
import React, { createContext, useContext, useCallback } from 'react';
|
import React, { createContext, useContext, useCallback } from 'react';
|
||||||
import { usePreviewContext } from '../preview/PreviewContext';
|
|
||||||
import { useAppShell } from '../appShellContext';
|
|
||||||
import type { Document } from '../types/documents';
|
import type { Document } from '../types/documents';
|
||||||
import type { Identifier } from '../types/identifiers';
|
import type { Identifier } from '../types/identifiers';
|
||||||
|
|
||||||
@@ -20,25 +18,31 @@ export const useDocumentOpen = () => {
|
|||||||
return context;
|
return context;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DocumentOpenProvider: React.FC<{ children: React.ReactNode; onOpenViewer?: (docId: Identifier) => void }> = ({ children, onOpenViewer }) => {
|
interface DocumentOpenProviderProps {
|
||||||
const { openPreview } = usePreviewContext();
|
children: React.ReactNode;
|
||||||
const appShell = useAppShell();
|
onOpenViewer?: (docId: Identifier) => void;
|
||||||
|
onOpenPreview?: (doc: Document) => void;
|
||||||
// We cast appShell.openDetailPanel because AppShellContext is loosely typed
|
onOpenSidepanel?: (docId: Identifier) => void;
|
||||||
const openDetailPanel = appShell.openDetailPanel as ((args: { documentIds: Identifier[] }) => void) | undefined;
|
}
|
||||||
|
|
||||||
|
export const DocumentOpenProvider: React.FC<DocumentOpenProviderProps> = ({
|
||||||
|
children,
|
||||||
|
onOpenViewer,
|
||||||
|
onOpenPreview,
|
||||||
|
onOpenSidepanel,
|
||||||
|
}) => {
|
||||||
const openDocument = useCallback((doc: Document, target: DocumentOpenTarget = 'preview') => {
|
const openDocument = useCallback((doc: Document, target: DocumentOpenTarget = 'preview') => {
|
||||||
if (!doc) return;
|
if (!doc) return;
|
||||||
|
|
||||||
switch (target) {
|
switch (target) {
|
||||||
case 'preview':
|
case 'preview':
|
||||||
openPreview(doc);
|
if (onOpenPreview) {
|
||||||
|
onOpenPreview(doc);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'sidepanel':
|
case 'sidepanel':
|
||||||
if (openDetailPanel) {
|
if (onOpenSidepanel) {
|
||||||
openDetailPanel({ documentIds: [doc.id] });
|
onOpenSidepanel(doc.id);
|
||||||
} else {
|
|
||||||
console.warn('openDetailPanel is not available in AppShellContext');
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'viewer':
|
case 'viewer':
|
||||||
@@ -47,7 +51,7 @@ export const DocumentOpenProvider: React.FC<{ children: React.ReactNode; onOpenV
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}, [openPreview, openDetailPanel, onOpenViewer]);
|
}, [onOpenPreview, onOpenSidepanel, onOpenViewer]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DocumentOpenContext.Provider value={{ openDocument }}>
|
<DocumentOpenContext.Provider value={{ openDocument }}>
|
||||||
@@ -19,7 +19,7 @@ import { PointerTrackingProvider, usePointerTracking } from './PointerTrackingCo
|
|||||||
import type { Identifier } from '../types/identifiers';
|
import type { Identifier } from '../types/identifiers';
|
||||||
import type { DocumentsListEntry, Document } from '../types/documents';
|
import type { DocumentsListEntry, Document } from '../types/documents';
|
||||||
import { useAppState } from '../app/appState';
|
import { useAppState } from '../app/appState';
|
||||||
import { useDocumentOpen } from '../context/DocumentOpenContext';
|
import { useDocumentOpen } from '../contexts/DocumentOpenContext';
|
||||||
|
|
||||||
type TagLike = { id?: Identifier | null; label?: string; color?: string | null } | null;
|
type TagLike = { id?: Identifier | null; label?: string; color?: string | null } | null;
|
||||||
type OverlaySource = { url: string; alt?: string | null; mimeType?: string | null; };
|
type OverlaySource = { url: string; alt?: string | null; mimeType?: string | null; };
|
||||||
|
|||||||
@@ -7,13 +7,9 @@ type FetchDocument = (id: DocumentId) => Promise<unknown>;
|
|||||||
|
|
||||||
class DocumentsManager<T extends ManagedDocument = ManagedDocument> {
|
class DocumentsManager<T extends ManagedDocument = ManagedDocument> {
|
||||||
private byId: Map<DocumentId, T>;
|
private byId: Map<DocumentId, T>;
|
||||||
|
|
||||||
private fetcher?: FetchDocument;
|
private fetcher?: FetchDocument;
|
||||||
|
|
||||||
private inflight: Map<DocumentId, Promise<T | null>>;
|
private inflight: Map<DocumentId, Promise<T | null>>;
|
||||||
|
|
||||||
private listeners: Set<() => void>;
|
private listeners: Set<() => void>;
|
||||||
|
|
||||||
private emitScheduled: boolean;
|
private emitScheduled: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { type DragEvent } from 'react';
|
import React, { type DragEvent } from 'react';
|
||||||
import { parseTagTransferPayload } from '../tagTransfer';
|
import { parseTagTransferPayload } from '../tagTransfer';
|
||||||
import { createDocumentEntryKey } from '../../app/entryKey';
|
import { createDocumentEntryKey } from '../../app/entryKey';
|
||||||
import { useDocumentOpen } from '../../context/DocumentOpenContext';
|
import { useDocumentOpen } from '../../contexts/DocumentOpenContext';
|
||||||
import type { Document } from '../../types/documents';
|
import type { Document } from '../../types/documents';
|
||||||
import type { DocumentsViewProps } from '../panel/DocumentsPanel';
|
import type { DocumentsViewProps } from '../panel/DocumentsPanel';
|
||||||
import type { DocumentViewLogic } from './useDocumentViewLogic';
|
import type { DocumentViewLogic } from './useDocumentViewLogic';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useCallback, useMemo } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
import type { DocumentsListEntry } from '../../types/documents';
|
import type { DocumentsListEntry } from '../../types/documents';
|
||||||
import { useWorkspaceSelectionContext } from '../../app/WorkspaceSelectionContext';
|
import { useWorkspaceSelectionContext } from '../../app/WorkspaceSelectionContext';
|
||||||
import { useDocumentOpen } from '../../context/DocumentOpenContext';
|
import { useDocumentOpen } from '../../contexts/DocumentOpenContext';
|
||||||
|
|
||||||
interface UseDocumentsNavigationProps {
|
interface UseDocumentsNavigationProps {
|
||||||
entries: DocumentsListEntry[];
|
entries: DocumentsListEntry[];
|
||||||
|
|||||||
Reference in New Issue
Block a user