refactor(doclist): restructure loading and empty states, update skeletons and columns UI
Simplify document list state handling by centralizing loading and empty logic into DocumentList. Remove redundant state management from HomeContent. Refactor DocumentListSkeleton to support all view modes and icon sizes for improved visual consistency during loading. Enhance ColumnsView styling for folder and document rows, improving selection, hover, and drag feedback. Use useMemo for visible order calculation to optimize rendering. These changes streamline the document list rendering flow, unify skeleton loading visuals, and improve the clarity and responsiveness of the columns view interface.
This commit is contained in:
parent
46d5bab647
commit
0470f044ef
4 changed files with 259 additions and 147 deletions
|
|
@ -1,12 +1,8 @@
|
|||
'use client';
|
||||
|
||||
import { Header } from '@/components/Header';
|
||||
import { DocumentUploader } from '@/components/documents/DocumentUploader';
|
||||
import { DocumentList } from '@/components/doclist/DocumentList';
|
||||
import { DocumentListSkeleton } from '@/components/doclist/DocumentListSkeleton';
|
||||
import { SettingsModal } from '@/components/SettingsModal';
|
||||
import { UserMenu } from '@/components/auth/UserMenu';
|
||||
import { useDocuments } from '@/contexts/DocumentContext';
|
||||
|
||||
const Brand = () => (
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
|
|
@ -26,31 +22,6 @@ const AppActions = () => (
|
|||
);
|
||||
|
||||
export function HomeContent() {
|
||||
const { pdfDocs, epubDocs, htmlDocs, isPDFLoading } = useDocuments();
|
||||
const totalDocs = (pdfDocs?.length || 0) + (epubDocs?.length || 0) + (htmlDocs?.length || 0);
|
||||
|
||||
if (isPDFLoading) {
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col">
|
||||
<Header title={<Brand />} right={<AppActions />} />
|
||||
<div className="flex-1 min-h-0 p-3 overflow-auto">
|
||||
<DocumentListSkeleton />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (totalDocs === 0) {
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col">
|
||||
<Header title={<Brand />} right={<AppActions />} />
|
||||
<div className="flex-1 min-h-0 flex items-center justify-center p-6">
|
||||
<DocumentUploader className="py-12 w-full max-w-2xl" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full h-full">
|
||||
<DocumentList brand={<Brand />} appActions={<AppActions />} />
|
||||
|
|
|
|||
|
|
@ -409,17 +409,7 @@ function DocumentListInner({ brand, appActions }: DocumentListInnerProps) {
|
|||
[allDocuments],
|
||||
);
|
||||
|
||||
if (isPDFLoading || isEPUBLoading || isHTMLLoading) {
|
||||
return <DocumentListSkeleton viewMode={viewMode === 'list' ? 'list' : 'grid'} />;
|
||||
}
|
||||
|
||||
if (allDocuments.length === 0) {
|
||||
return (
|
||||
<div className="w-full max-w-2xl mx-auto py-12">
|
||||
<DocumentUploader />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const isLoading = isPDFLoading || isEPUBLoading || isHTMLLoading;
|
||||
|
||||
const fallbackViewMode: ViewMode =
|
||||
viewMode === 'columns' && isNarrow ? 'list' : viewMode;
|
||||
|
|
@ -473,7 +463,7 @@ function DocumentListInner({ brand, appActions }: DocumentListInnerProps) {
|
|||
sidebarOpen={sidebarOpen}
|
||||
onSidebarOpenChange={setSidebarOpen}
|
||||
>
|
||||
{showHint && allDocuments.length > 1 && (
|
||||
{!isLoading && showHint && allDocuments.length > 1 && (
|
||||
<div className="px-3 pt-3 shrink-0 bg-background">
|
||||
<div className="flex items-center justify-between bg-base border border-offbase rounded-md px-3 py-1 text-[12px]">
|
||||
<p className="text-foreground">
|
||||
|
|
@ -493,57 +483,67 @@ function DocumentListInner({ brand, appActions }: DocumentListInnerProps) {
|
|||
</div>
|
||||
)}
|
||||
|
||||
<DocumentUploader variant="overlay" className="flex-1 min-h-0 flex flex-col">
|
||||
{fallbackViewMode === 'icons' && (
|
||||
<IconsView
|
||||
folders={visibleFolders}
|
||||
unfolderedDocs={unfolderedDocs}
|
||||
iconSize={iconSize}
|
||||
collapsedFolders={collapsedFolders}
|
||||
onToggleCollapse={toggleFolderCollapse}
|
||||
onDeleteFolder={handleDeleteFolder}
|
||||
onDeleteDoc={handleDeleteDoc}
|
||||
onDropOnFolder={handleDropOnFolder}
|
||||
onMergeIntoFolder={handleMergeIntoFolder}
|
||||
/>
|
||||
)}
|
||||
{fallbackViewMode === 'list' && (
|
||||
<ListView
|
||||
folders={visibleFolders}
|
||||
unfolderedDocs={unfolderedDocs}
|
||||
sortBy={sortBy}
|
||||
sortDirection={sortDirection}
|
||||
onSortChange={(b, d) => {
|
||||
setSortBy(b);
|
||||
setSortDirection(d);
|
||||
}}
|
||||
collapsedFolders={collapsedFolders}
|
||||
onToggleCollapse={toggleFolderCollapse}
|
||||
onDeleteFolder={handleDeleteFolder}
|
||||
onDeleteDoc={handleDeleteDoc}
|
||||
onDropOnFolder={handleDropOnFolder}
|
||||
onMergeIntoFolder={handleMergeIntoFolder}
|
||||
/>
|
||||
)}
|
||||
{fallbackViewMode === 'columns' && (
|
||||
<ColumnsView
|
||||
folders={visibleFolders}
|
||||
unfolderedDocs={unfolderedDocs}
|
||||
onDeleteDoc={handleDeleteDoc}
|
||||
onDropOnFolder={handleDropOnFolder}
|
||||
onMergeIntoFolder={handleMergeIntoFolder}
|
||||
/>
|
||||
)}
|
||||
{fallbackViewMode === 'gallery' && (
|
||||
<GalleryView
|
||||
folders={visibleFolders}
|
||||
unfolderedDocs={unfolderedDocs}
|
||||
onDeleteDoc={handleDeleteDoc}
|
||||
onDropOnFolder={handleDropOnFolder}
|
||||
onMergeIntoFolder={handleMergeIntoFolder}
|
||||
/>
|
||||
)}
|
||||
</DocumentUploader>
|
||||
{isLoading ? (
|
||||
<div className="flex-1 min-h-0 overflow-hidden">
|
||||
<DocumentListSkeleton viewMode={fallbackViewMode} iconSize={iconSize} />
|
||||
</div>
|
||||
) : allDocuments.length === 0 ? (
|
||||
<div className="flex-1 min-h-0 flex items-center justify-center p-6">
|
||||
<DocumentUploader className="py-12 w-full max-w-2xl" />
|
||||
</div>
|
||||
) : (
|
||||
<DocumentUploader variant="overlay" className="flex-1 min-h-0 flex flex-col">
|
||||
{fallbackViewMode === 'icons' && (
|
||||
<IconsView
|
||||
folders={visibleFolders}
|
||||
unfolderedDocs={unfolderedDocs}
|
||||
iconSize={iconSize}
|
||||
collapsedFolders={collapsedFolders}
|
||||
onToggleCollapse={toggleFolderCollapse}
|
||||
onDeleteFolder={handleDeleteFolder}
|
||||
onDeleteDoc={handleDeleteDoc}
|
||||
onDropOnFolder={handleDropOnFolder}
|
||||
onMergeIntoFolder={handleMergeIntoFolder}
|
||||
/>
|
||||
)}
|
||||
{fallbackViewMode === 'list' && (
|
||||
<ListView
|
||||
folders={visibleFolders}
|
||||
unfolderedDocs={unfolderedDocs}
|
||||
sortBy={sortBy}
|
||||
sortDirection={sortDirection}
|
||||
onSortChange={(b, d) => {
|
||||
setSortBy(b);
|
||||
setSortDirection(d);
|
||||
}}
|
||||
collapsedFolders={collapsedFolders}
|
||||
onToggleCollapse={toggleFolderCollapse}
|
||||
onDeleteFolder={handleDeleteFolder}
|
||||
onDeleteDoc={handleDeleteDoc}
|
||||
onDropOnFolder={handleDropOnFolder}
|
||||
onMergeIntoFolder={handleMergeIntoFolder}
|
||||
/>
|
||||
)}
|
||||
{fallbackViewMode === 'columns' && (
|
||||
<ColumnsView
|
||||
folders={visibleFolders}
|
||||
unfolderedDocs={unfolderedDocs}
|
||||
onDeleteDoc={handleDeleteDoc}
|
||||
onDropOnFolder={handleDropOnFolder}
|
||||
onMergeIntoFolder={handleMergeIntoFolder}
|
||||
/>
|
||||
)}
|
||||
{fallbackViewMode === 'gallery' && (
|
||||
<GalleryView
|
||||
folders={visibleFolders}
|
||||
unfolderedDocs={unfolderedDocs}
|
||||
onDeleteDoc={handleDeleteDoc}
|
||||
onDropOnFolder={handleDropOnFolder}
|
||||
onMergeIntoFolder={handleMergeIntoFolder}
|
||||
/>
|
||||
)}
|
||||
</DocumentUploader>
|
||||
)}
|
||||
|
||||
<CreateFolderDialog
|
||||
isOpen={pendingMerge !== null}
|
||||
|
|
|
|||
|
|
@ -1,49 +1,166 @@
|
|||
'use client';
|
||||
|
||||
import type { IconSize, ViewMode } from '@/types/documents';
|
||||
|
||||
interface DocumentListSkeletonProps {
|
||||
viewMode?: 'list' | 'grid';
|
||||
viewMode?: ViewMode;
|
||||
iconSize?: IconSize;
|
||||
}
|
||||
|
||||
export function DocumentListSkeleton({ viewMode = 'grid' }: DocumentListSkeletonProps) {
|
||||
const placeholders = Array.from({ length: viewMode === 'grid' ? 10 : 6 });
|
||||
const ICON_COLS: Record<IconSize, string> = {
|
||||
sm: 'grid-cols-3 sm:grid-cols-5 md:grid-cols-7 lg:grid-cols-8',
|
||||
md: 'grid-cols-2 sm:grid-cols-4 md:grid-cols-5 lg:grid-cols-6',
|
||||
lg: 'grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5',
|
||||
xl: 'grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4',
|
||||
};
|
||||
|
||||
function IconsSkeleton({ iconSize }: { iconSize: IconSize }) {
|
||||
return (
|
||||
<div className="w-full mx-auto animate-pulse" aria-label="Loading documents" aria-busy="true">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="h-6 w-36 rounded bg-offbase" />
|
||||
<div className="h-6 w-44 rounded bg-offbase" />
|
||||
</div>
|
||||
<div className="h-3 w-48 rounded bg-offbase mb-3" />
|
||||
<div className="h-9 w-full rounded-lg border-2 border-dashed border-offbase bg-base mb-3" />
|
||||
|
||||
<div
|
||||
className={
|
||||
viewMode === 'grid'
|
||||
? 'grid w-full grid-cols-2 gap-2 sm:grid-cols-3 sm:gap-3 md:grid-cols-4 lg:grid-cols-5'
|
||||
: 'w-full space-y-1'
|
||||
}
|
||||
>
|
||||
{placeholders.map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={
|
||||
viewMode === 'grid'
|
||||
? 'overflow-hidden rounded-md border border-offbase bg-base'
|
||||
: 'h-12 rounded-md border border-offbase bg-base'
|
||||
}
|
||||
>
|
||||
{viewMode === 'grid' ? (
|
||||
<>
|
||||
<div className="aspect-[3/4] w-full bg-offbase" />
|
||||
<div className="p-2">
|
||||
<div className="h-3 w-4/5 rounded bg-offbase" />
|
||||
<div className="mt-1 h-2.5 w-1/3 rounded bg-offbase" />
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
<div className="h-full min-h-0 overflow-y-auto p-3">
|
||||
<div className={`grid gap-2 sm:gap-3 ${ICON_COLS[iconSize]}`}>
|
||||
{Array.from({ length: 12 }).map((_, index) => (
|
||||
<div key={index} className="overflow-hidden rounded-md border border-offbase bg-base">
|
||||
<div className="aspect-[3/4] w-full bg-offbase" />
|
||||
<div className="flex items-center gap-2 px-2 py-2">
|
||||
<div className="h-3.5 w-3.5 rounded bg-offbase" />
|
||||
<div className="h-2.5 w-4/5 rounded bg-offbase" />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ListSkeleton() {
|
||||
return (
|
||||
<div className="h-full min-h-0 overflow-y-auto">
|
||||
<div className="sticky top-0 z-10 bg-base border-b border-offbase grid grid-cols-[minmax(0,1fr)_72px_88px_120px_28px] sm:grid-cols-[minmax(0,1fr)_88px_96px_140px_32px]">
|
||||
<div className="h-8 flex items-center px-2">
|
||||
<div className="h-2.5 w-12 rounded bg-offbase" />
|
||||
</div>
|
||||
<div className="h-8 flex items-center px-2">
|
||||
<div className="h-2.5 w-10 rounded bg-offbase" />
|
||||
</div>
|
||||
<div className="h-8 flex items-center justify-end px-2">
|
||||
<div className="h-2.5 w-10 rounded bg-offbase" />
|
||||
</div>
|
||||
<div className="h-8 flex items-center px-2">
|
||||
<div className="h-2.5 w-14 rounded bg-offbase" />
|
||||
</div>
|
||||
<div />
|
||||
</div>
|
||||
<div>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="grid grid-cols-[minmax(0,1fr)_72px_88px_120px_28px] sm:grid-cols-[minmax(0,1fr)_88px_96px_140px_32px] items-center border-b border-offbase h-[35px]"
|
||||
>
|
||||
<div className="px-2 flex items-center gap-2">
|
||||
<div className="h-3.5 w-3.5 rounded bg-offbase" />
|
||||
<div className="h-2.5 w-2/3 rounded bg-offbase" />
|
||||
</div>
|
||||
<div className="px-2">
|
||||
<div className="h-2.5 w-8 rounded bg-offbase" />
|
||||
</div>
|
||||
<div className="px-2 flex justify-end">
|
||||
<div className="h-2.5 w-12 rounded bg-offbase" />
|
||||
</div>
|
||||
<div className="px-2">
|
||||
<div className="h-2.5 w-16 rounded bg-offbase" />
|
||||
</div>
|
||||
<div className="px-2 flex justify-center">
|
||||
<div className="h-4 w-4 rounded bg-offbase" />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ColumnsSkeleton() {
|
||||
return (
|
||||
<div className="h-full min-h-0 flex overflow-x-auto overflow-y-hidden">
|
||||
<div className="w-[260px] shrink-0 h-full bg-base border-r border-offbase overflow-y-auto">
|
||||
<div className="sticky top-0 px-3 py-1.5 border-b border-offbase bg-base">
|
||||
<div className="h-2.5 w-20 rounded bg-offbase" />
|
||||
</div>
|
||||
<div className="p-1.5 flex flex-col gap-1">
|
||||
{Array.from({ length: 11 }).map((_, rowIndex) => (
|
||||
<div key={rowIndex} className="h-7 rounded-md bg-offbase" />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 min-w-[280px] h-full bg-background overflow-y-auto p-4">
|
||||
<div className="max-w-[360px] mx-auto">
|
||||
<div className="rounded-lg overflow-hidden border border-offbase bg-base">
|
||||
<div className="aspect-[3/4] w-full bg-offbase" />
|
||||
</div>
|
||||
<div className="mt-3 h-3.5 w-4/5 rounded bg-offbase" />
|
||||
<div className="mt-2 h-2.5 w-1/3 rounded bg-offbase" />
|
||||
<div className="mt-3 flex gap-2">
|
||||
<div className="flex-1 h-8 rounded-md bg-offbase" />
|
||||
<div className="w-20 h-8 rounded-md bg-offbase" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function GallerySkeleton() {
|
||||
return (
|
||||
<div className="h-full min-h-0 flex flex-col">
|
||||
<div className="flex-1 min-h-0 flex items-center justify-center p-6 bg-background">
|
||||
<div className="flex flex-col items-center gap-3 max-w-[420px]">
|
||||
<div className="w-[260px] sm:w-[320px] aspect-[3/4] rounded-lg border border-offbase bg-offbase" />
|
||||
<div className="h-3 w-40 rounded bg-offbase" />
|
||||
<div className="h-2.5 w-28 rounded bg-offbase" />
|
||||
<div className="flex gap-2">
|
||||
<div className="h-8 w-20 rounded-md bg-offbase" />
|
||||
<div className="h-8 w-20 rounded-md bg-offbase" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="shrink-0 border-t border-offbase bg-base">
|
||||
<div className="flex gap-2 overflow-x-auto p-2">
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<div key={index} className="shrink-0 w-[88px] rounded-md overflow-hidden border border-offbase bg-base">
|
||||
<div className="aspect-[3/4] bg-offbase" />
|
||||
<div className="p-1.5">
|
||||
<div className="h-2.5 w-5/6 rounded bg-offbase" />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function DocumentListSkeleton({
|
||||
viewMode = 'icons',
|
||||
iconSize = 'md',
|
||||
}: DocumentListSkeletonProps) {
|
||||
let body;
|
||||
if (viewMode === 'list') {
|
||||
body = <ListSkeleton />;
|
||||
} else if (viewMode === 'columns') {
|
||||
body = <ColumnsSkeleton />;
|
||||
} else if (viewMode === 'gallery') {
|
||||
body = <GallerySkeleton />;
|
||||
} else {
|
||||
body = <IconsSkeleton iconSize={iconSize} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="h-full w-full min-h-0 flex flex-col animate-pulse"
|
||||
aria-label="Loading documents"
|
||||
aria-busy="true"
|
||||
>
|
||||
{body}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useDrag, useDrop } from 'react-dnd';
|
||||
import type { DocumentListDocument, Folder } from '@/types/documents';
|
||||
import { PDFIcon, EPUBIcon, FileIcon } from '@/components/icons/Icons';
|
||||
|
|
@ -74,15 +74,17 @@ function ColumnDocRow({
|
|||
data-doc-tile
|
||||
onClick={onClick}
|
||||
className={
|
||||
'flex items-center gap-2 px-2 py-1.5 rounded-md text-[12px] transition-colors duration-200 ease-out cursor-pointer ' +
|
||||
'group flex items-center gap-2 px-2 py-1 rounded-md text-[12px] border transform transition-all duration-200 ease-out cursor-pointer text-left hover:scale-[1.01] ' +
|
||||
(active || isSelected
|
||||
? 'bg-accent text-background'
|
||||
: 'text-foreground hover:bg-offbase') +
|
||||
? 'border-accent bg-offbase text-accent'
|
||||
: 'border-transparent text-foreground hover:border-accent hover:bg-offbase hover:text-accent') +
|
||||
(isOver && canDrop ? ' ring-1 ring-accent' : '') +
|
||||
(isDragging ? ' opacity-50' : '')
|
||||
}
|
||||
>
|
||||
<KindIcon doc={doc} />
|
||||
<span className={'w-4 h-4 shrink-0 flex items-center justify-center transition-colors duration-200 ' + ((active || isSelected) ? 'text-accent' : 'text-muted group-hover:text-accent')}>
|
||||
<KindIcon doc={doc} />
|
||||
</span>
|
||||
<span className="truncate flex-1">{doc.name}</span>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -111,19 +113,19 @@ function ColumnFolderRow({
|
|||
ref={dropRef as unknown as React.RefObject<HTMLDivElement>}
|
||||
onClick={onClick}
|
||||
className={
|
||||
'flex items-center gap-2 px-2 py-1.5 rounded-md text-[12px] transition-colors duration-200 ease-out cursor-pointer ' +
|
||||
'group flex items-center gap-2 px-2 py-1 rounded-md text-[12px] border transform transition-all duration-200 ease-out cursor-pointer text-left hover:scale-[1.01] ' +
|
||||
(active
|
||||
? 'bg-accent text-background'
|
||||
: 'text-foreground hover:bg-offbase') +
|
||||
? 'border-accent bg-offbase text-accent'
|
||||
: 'border-transparent text-foreground hover:border-accent hover:bg-offbase hover:text-accent') +
|
||||
(isOver && canDrop ? ' ring-1 ring-accent' : '')
|
||||
}
|
||||
>
|
||||
<FolderIcon className={'w-4 h-4 ' + (active ? 'text-background' : 'text-accent')} />
|
||||
<FolderIcon className={'w-4 h-4 ' + (active ? 'text-accent' : 'text-muted group-hover:text-accent')} />
|
||||
<span className="truncate flex-1">{folder.name}</span>
|
||||
<span className={'text-[10px] ' + (active ? 'text-background' : 'text-muted')}>
|
||||
<span className={'text-[10px] ' + (active ? 'text-accent' : 'text-muted')}>
|
||||
{folder.documents.length}
|
||||
</span>
|
||||
<ChevronRightSmall className={'w-3 h-3 ' + (active ? 'text-background' : 'text-muted')} />
|
||||
<ChevronRightSmall className={'w-3 h-3 ' + (active ? 'text-accent' : 'text-muted group-hover:text-accent')} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -150,19 +152,41 @@ export function ColumnsView({
|
|||
}: ColumnsViewProps) {
|
||||
const { setVisibleOrder } = useDocumentSelection();
|
||||
const [selected, setSelected] = useState<Selected>(null);
|
||||
const allDocs = useMemo(
|
||||
() => [...folders.flatMap((f) => f.documents), ...unfolderedDocs],
|
||||
[folders, unfolderedDocs],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const all = [...folders.flatMap((f) => f.documents), ...unfolderedDocs];
|
||||
setVisibleOrder(all);
|
||||
}, [folders, unfolderedDocs, setVisibleOrder]);
|
||||
setVisibleOrder(allDocs);
|
||||
}, [allDocs, setVisibleOrder]);
|
||||
|
||||
// Drop selection if it no longer exists.
|
||||
// Keep selection valid and default to the first document when entering this view.
|
||||
useEffect(() => {
|
||||
if (!selected) return;
|
||||
if (selected.kind === 'folder' && !folders.find((f) => f.id === selected.id)) {
|
||||
setSelected(null);
|
||||
if (allDocs.length === 0) {
|
||||
if (selected !== null) setSelected(null);
|
||||
return;
|
||||
}
|
||||
}, [folders, selected]);
|
||||
|
||||
if (!selected) {
|
||||
setSelected({ kind: 'doc', doc: allDocs[0] });
|
||||
return;
|
||||
}
|
||||
|
||||
if (selected.kind === 'folder') {
|
||||
if (!folders.find((f) => f.id === selected.id)) {
|
||||
setSelected({ kind: 'doc', doc: allDocs[0] });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedStillExists = allDocs.some(
|
||||
(d) => d.id === selected.doc.id && d.type === selected.doc.type,
|
||||
);
|
||||
if (!selectedStillExists) {
|
||||
setSelected({ kind: 'doc', doc: allDocs[0] });
|
||||
}
|
||||
}, [allDocs, folders, selected]);
|
||||
|
||||
const selectedFolder =
|
||||
selected?.kind === 'folder' ? folders.find((f) => f.id === selected.id) : undefined;
|
||||
|
|
|
|||
Loading…
Reference in a new issue