- Updated .env.example to include configuration for OnlyOffice Document Server. - Modified the workspace configuration to remove the drive-suite path. - Adjusted TypeScript environment imports for consistency. - Enhanced Next.js configuration to disable canvas in Webpack. - Updated package.json to include new dependencies for OnlyOffice and PDF.js. - Added global styles for OnlyOffice theme integration in the CSS. - Created new layout and page components for the Drive feature, including public sharing and editing functionalities. - Updated metadata handling across various layouts to reflect the new app structure.
160 lines
5.4 KiB
TypeScript
160 lines
5.4 KiB
TypeScript
"use client"
|
|
|
|
import { create } from "zustand"
|
|
import type { DriveDragItem } from "@/lib/drive/drive-dnd"
|
|
|
|
export interface DrivePreviewTarget {
|
|
path: string
|
|
name: string
|
|
mime_type: string
|
|
is_favorite: boolean
|
|
mailAttachmentId?: string
|
|
mailMessageId?: string
|
|
}
|
|
|
|
export interface DrivePreviewContext {
|
|
allowShare: boolean
|
|
isTrash: boolean
|
|
publicShare?: { token: string; password?: string; canEdit: boolean }
|
|
mailSource?: boolean
|
|
mailMessageId?: string
|
|
}
|
|
|
|
interface DriveUIState {
|
|
sidebarCollapsed: boolean
|
|
selectedPaths: Set<string>
|
|
selectionAnchorPath: string | null
|
|
expandedSidebarPaths: Set<string>
|
|
sharePath: string | null
|
|
shareItemType: "file" | "directory" | null
|
|
previewFiles: DrivePreviewTarget[]
|
|
previewIndex: number
|
|
previewContext: DrivePreviewContext | null
|
|
/** Ignore grid card open clicks until this timestamp (menu dropdown ghost click). */
|
|
suppressCardOpenUntil: number
|
|
draggingItems: DriveDragItem[] | null
|
|
/** Multi-select mode on xs/sm — tap toggles selection instead of opening. */
|
|
selectionMode: boolean
|
|
setSelectionMode: (v: boolean) => void
|
|
enterSelectionMode: (path: string) => void
|
|
setSidebarCollapsed: (v: boolean) => void
|
|
blockCardOpen: (ms?: number) => void
|
|
toggleSelect: (path: string, selected: boolean) => void
|
|
setSelectedPaths: (paths: Iterable<string>) => void
|
|
setSelectionAnchor: (path: string | null) => void
|
|
clearSelection: () => void
|
|
setDraggingItems: (items: DriveDragItem[] | null) => void
|
|
toggleSidebarPath: (path: string) => void
|
|
ensureSidebarPathsExpanded: (paths: string[]) => void
|
|
setSharePath: (path: string | null, itemType?: "file" | "directory" | null) => void
|
|
openPreview: (
|
|
files: DrivePreviewTarget[],
|
|
index: number,
|
|
context?: Partial<DrivePreviewContext>
|
|
) => void
|
|
closePreview: () => void
|
|
stepPreview: (delta: number) => void
|
|
updatePreviewFavorite: (path: string, favorite: boolean) => void
|
|
removePreviewFile: (path: string) => void
|
|
}
|
|
|
|
export const useDriveUIStore = create<DriveUIState>((set, get) => ({
|
|
sidebarCollapsed: false,
|
|
selectionMode: false,
|
|
selectedPaths: new Set(),
|
|
selectionAnchorPath: null,
|
|
expandedSidebarPaths: new Set(["/", "/__shared_root__"]),
|
|
sharePath: null,
|
|
shareItemType: null,
|
|
previewFiles: [],
|
|
previewIndex: -1,
|
|
previewContext: null,
|
|
suppressCardOpenUntil: 0,
|
|
draggingItems: null,
|
|
setSidebarCollapsed: (sidebarCollapsed) => set({ sidebarCollapsed }),
|
|
setSelectionMode: (selectionMode) => set({ selectionMode }),
|
|
enterSelectionMode: (path) =>
|
|
set({
|
|
selectionMode: true,
|
|
selectedPaths: new Set([path]),
|
|
selectionAnchorPath: path,
|
|
}),
|
|
blockCardOpen: (ms = 700) =>
|
|
set({ suppressCardOpenUntil: Date.now() + ms }),
|
|
toggleSelect: (path, selected) => {
|
|
const next = new Set(get().selectedPaths)
|
|
if (selected) next.add(path)
|
|
else next.delete(path)
|
|
set({ selectedPaths: next })
|
|
},
|
|
setSelectedPaths: (paths) => set({ selectedPaths: new Set(paths) }),
|
|
setSelectionAnchor: (selectionAnchorPath) => set({ selectionAnchorPath }),
|
|
clearSelection: () =>
|
|
set({ selectedPaths: new Set(), selectionAnchorPath: null, selectionMode: false }),
|
|
setDraggingItems: (draggingItems) => set({ draggingItems }),
|
|
toggleSidebarPath: (path) => {
|
|
const next = new Set(get().expandedSidebarPaths)
|
|
if (next.has(path)) next.delete(path)
|
|
else next.add(path)
|
|
set({ expandedSidebarPaths: next })
|
|
},
|
|
ensureSidebarPathsExpanded: (paths) => {
|
|
const next = new Set(get().expandedSidebarPaths)
|
|
let changed = false
|
|
for (const path of paths) {
|
|
if (!next.has(path)) {
|
|
next.add(path)
|
|
changed = true
|
|
}
|
|
}
|
|
if (changed) set({ expandedSidebarPaths: next })
|
|
},
|
|
setSharePath: (sharePath, shareItemType = null) => set({ sharePath, shareItemType }),
|
|
openPreview: (previewFiles, previewIndex, context) =>
|
|
set({
|
|
previewFiles,
|
|
previewIndex,
|
|
previewContext: {
|
|
allowShare: context?.allowShare ?? true,
|
|
isTrash: context?.isTrash ?? false,
|
|
publicShare: context?.publicShare,
|
|
mailSource: context?.mailSource ?? false,
|
|
mailMessageId: context?.mailMessageId,
|
|
},
|
|
}),
|
|
closePreview: () => set({ previewFiles: [], previewIndex: -1, previewContext: null }),
|
|
stepPreview: (delta) => {
|
|
const { previewFiles, previewIndex } = get()
|
|
if (previewFiles.length === 0) return
|
|
const next = previewIndex + delta
|
|
if (next < 0 || next >= previewFiles.length) return
|
|
set({ previewIndex: next })
|
|
},
|
|
updatePreviewFavorite: (path, favorite) =>
|
|
set({
|
|
previewFiles: get().previewFiles.map((f) =>
|
|
f.path === path ? { ...f, is_favorite: favorite } : f
|
|
),
|
|
}),
|
|
removePreviewFile: (path) => {
|
|
const { previewFiles, previewIndex } = get()
|
|
const idx = previewFiles.findIndex((f) => f.path === path)
|
|
if (idx < 0) return
|
|
const next = previewFiles.filter((f) => f.path !== path)
|
|
if (next.length === 0) {
|
|
set({ previewFiles: [], previewIndex: -1, previewContext: null })
|
|
return
|
|
}
|
|
let newIndex = previewIndex
|
|
if (idx < previewIndex) newIndex = previewIndex - 1
|
|
else if (idx === previewIndex) newIndex = Math.min(previewIndex, next.length - 1)
|
|
set({ previewFiles: next, previewIndex: newIndex })
|
|
},
|
|
}))
|
|
|
|
export function usePreviewFile() {
|
|
const files = useDriveUIStore((s) => s.previewFiles)
|
|
const index = useDriveUIStore((s) => s.previewIndex)
|
|
return index >= 0 ? (files[index] ?? null) : null
|
|
}
|