279 lines
8.9 KiB
TypeScript
279 lines
8.9 KiB
TypeScript
"use client"
|
||
|
||
import { useEffect, useMemo, useRef } from "react"
|
||
import { useRouter } from "next/navigation"
|
||
import { Upload, FolderPlus } from "lucide-react"
|
||
import { toast } from "sonner"
|
||
import { FileBrowser } from "@/components/drive/file-browser"
|
||
import { DriveFilterBar } from "@/components/drive/drive-filter-bar"
|
||
import { DriveViewModeToggle } from "@/components/drive/drive-view-mode-toggle"
|
||
import { DriveSortMenu } from "@/components/drive/drive-sort-menu"
|
||
import { DriveBulkToolbar } from "@/components/drive/drive-bulk-toolbar"
|
||
import { DriveMarqueeSurface } from "@/components/drive/drive-marquee-surface"
|
||
import { DriveScrollEndSpacer } from "@/components/drive/drive-scroll-end-spacer"
|
||
import { Button } from "@/components/ui/button"
|
||
import { usePublicShareMenuMutations, usePublicShareMutations } from "@/lib/api/hooks/use-public-share-mutations"
|
||
import type { DriveFileInfo } from "@/lib/api/types"
|
||
import { useDriveSettingsStore } from "@/lib/stores/drive-settings-store"
|
||
import { useDriveFilteredItems } from "@/lib/hooks/use-drive-filtered-items"
|
||
import { useDriveFiltersStore } from "@/lib/stores/drive-filters-store"
|
||
import {
|
||
sharePermCanCreate,
|
||
sharePermCanDelete,
|
||
sharePermCanEdit,
|
||
sharePermCanUpdate,
|
||
} from "@/lib/drive/drive-share-permissions"
|
||
import { openPublicShareItem, downloadPublicShareFile } from "@/lib/drive/open-public-share-item"
|
||
import { useDriveUIStore } from "@/lib/stores/drive-ui-store"
|
||
import {
|
||
DRIVE_BROWSER_CARD_CLASS,
|
||
DRIVE_CARD_PAD_X,
|
||
DRIVE_CARD_SCROLL_PT,
|
||
DRIVE_FILTER_CONTENT_GAP,
|
||
DRIVE_FILTER_LIST_CONTENT_GAP,
|
||
} from "@/lib/drive/drive-chrome-classes"
|
||
import { cn } from "@/lib/utils"
|
||
import { nextUntitledName } from "@/lib/drive/drive-default-name"
|
||
|
||
export function PublicShareFolderView({
|
||
token,
|
||
folderPath,
|
||
files,
|
||
permissions,
|
||
password,
|
||
folderTitle,
|
||
}: {
|
||
token: string
|
||
folderPath: string
|
||
files: DriveFileInfo[]
|
||
permissions: number
|
||
password?: string
|
||
folderTitle: string
|
||
}) {
|
||
const router = useRouter()
|
||
const openPreview = useDriveUIStore((s) => s.openPreview)
|
||
const selectedPaths = useDriveUIStore((s) => s.selectedPaths)
|
||
const clearSelection = useDriveUIStore((s) => s.clearSelection)
|
||
const sortField = useDriveSettingsStore((s) => s.sortField)
|
||
const sortDir = useDriveSettingsStore((s) => s.sortDir)
|
||
const folderPlacement = useDriveSettingsStore((s) => s.folderPlacement)
|
||
const viewMode = useDriveSettingsStore((s) => s.viewMode)
|
||
const filterContentGap =
|
||
viewMode === "list" ? DRIVE_FILTER_LIST_CONTENT_GAP : DRIVE_FILTER_CONTENT_GAP
|
||
const filters = useDriveFiltersStore()
|
||
const uploadInputRef = useRef<HTMLInputElement>(null)
|
||
|
||
const canEdit = sharePermCanEdit(permissions)
|
||
const canCreate = sharePermCanCreate(permissions)
|
||
const canUpdate = sharePermCanUpdate(permissions)
|
||
const canDelete = sharePermCanDelete(permissions)
|
||
const writable = canUpdate || canDelete
|
||
|
||
const mutations = usePublicShareMenuMutations(token, password)
|
||
const { uploadFile, createFolder } = usePublicShareMutations(token, password)
|
||
|
||
const filtersSnapshot = useMemo(
|
||
() => ({
|
||
types: filters.types,
|
||
sources: filters.sources,
|
||
contactEmail: filters.contactEmail,
|
||
contactName: filters.contactName,
|
||
datePreset: filters.datePreset,
|
||
dateFrom: filters.dateFrom,
|
||
dateTo: filters.dateTo,
|
||
}),
|
||
[
|
||
filters.types,
|
||
filters.sources,
|
||
filters.contactEmail,
|
||
filters.contactName,
|
||
filters.datePreset,
|
||
filters.dateFrom,
|
||
filters.dateTo,
|
||
]
|
||
)
|
||
|
||
const { filteredItems: filteredFiles } = useDriveFilteredItems(
|
||
files,
|
||
filtersSnapshot,
|
||
{ sortField, sortDir, folderPlacement },
|
||
{ scopePath: folderPath }
|
||
)
|
||
|
||
const selectedTargets = useMemo(
|
||
() => filteredFiles.filter((f) => selectedPaths.has(f.path)),
|
||
[filteredFiles, selectedPaths]
|
||
)
|
||
const showBulk = selectedTargets.length > 0
|
||
|
||
useEffect(() => {
|
||
clearSelection()
|
||
}, [folderPath, clearSelection])
|
||
|
||
const openItem = (file: DriveFileInfo) => {
|
||
openPublicShareItem(file, {
|
||
token,
|
||
password,
|
||
canEdit: sharePermCanEdit(permissions),
|
||
router,
|
||
openPreview,
|
||
contextItems: filteredFiles,
|
||
})
|
||
}
|
||
|
||
const handleUpload = async (fileList: FileList | null) => {
|
||
if (!fileList?.length || !canCreate) return
|
||
const base = folderPath === "/" ? "" : folderPath
|
||
try {
|
||
for (const file of Array.from(fileList)) {
|
||
await uploadFile(`${base}/${file.name}`.replace(/\/+/g, "/"), file)
|
||
}
|
||
toast.success("Fichier(s) importé(s)")
|
||
} catch {
|
||
toast.error("Impossible d’importer le fichier")
|
||
}
|
||
}
|
||
|
||
const handleNewFolder = async () => {
|
||
if (!canCreate) return
|
||
const name = nextUntitledName(
|
||
filteredFiles.filter((f) => f.type === "directory").map((f) => f.name),
|
||
"Dossier"
|
||
)
|
||
const base = folderPath === "/" ? "" : folderPath
|
||
const path = `${base}/${name}`.replace(/\/+/g, "/")
|
||
try {
|
||
await createFolder.mutateAsync(path)
|
||
toast.success("Dossier créé")
|
||
} catch {
|
||
toast.error("Impossible de créer le dossier")
|
||
}
|
||
}
|
||
|
||
const downloadBulk = async (targets: DriveFileInfo[]) => {
|
||
for (const file of targets) {
|
||
await downloadPublicShareFile(token, file, password)
|
||
}
|
||
}
|
||
|
||
return (
|
||
<div className={DRIVE_BROWSER_CARD_CLASS}>
|
||
<div className={cn("shrink-0", filterContentGap)}>
|
||
<div
|
||
className={cn(
|
||
"flex min-h-12 shrink-0 flex-wrap items-center justify-between gap-3 py-2",
|
||
DRIVE_CARD_PAD_X
|
||
)}
|
||
>
|
||
<p className="truncate text-sm text-muted-foreground">
|
||
<span className="font-medium text-[#3c4043] dark:text-[#e8eaed]">{folderTitle}</span>
|
||
{" · "}
|
||
{filteredFiles.length} élément{filteredFiles.length > 1 ? "s" : ""}
|
||
{canEdit ? " · Éditeur" : " · Lecture seule"}
|
||
</p>
|
||
<div className="flex flex-wrap items-center gap-2">
|
||
{canCreate ? (
|
||
<>
|
||
<input
|
||
ref={uploadInputRef}
|
||
type="file"
|
||
multiple
|
||
className="hidden"
|
||
onChange={(e) => void handleUpload(e.target.files)}
|
||
/>
|
||
<Button
|
||
type="button"
|
||
variant="outline"
|
||
size="sm"
|
||
className="gap-2"
|
||
onClick={() => uploadInputRef.current?.click()}
|
||
>
|
||
<Upload className="h-4 w-4" />
|
||
Importer
|
||
</Button>
|
||
<Button
|
||
type="button"
|
||
variant="outline"
|
||
size="sm"
|
||
className="gap-2"
|
||
onClick={() => void handleNewFolder()}
|
||
>
|
||
<FolderPlus className="h-4 w-4" />
|
||
Dossier
|
||
</Button>
|
||
</>
|
||
) : null}
|
||
<DriveSortMenu />
|
||
<DriveViewModeToggle />
|
||
</div>
|
||
</div>
|
||
{showBulk ? (
|
||
<DriveBulkToolbar
|
||
targets={selectedTargets}
|
||
allowShare={false}
|
||
allowMove={false}
|
||
allowCopy={false}
|
||
allowQuickLink={false}
|
||
allowDelete={canDelete}
|
||
mutations={mutations}
|
||
onDownloadBulk={downloadBulk}
|
||
/>
|
||
) : (
|
||
<DriveFilterBar showContacts={false} />
|
||
)}
|
||
</div>
|
||
|
||
<main
|
||
data-drive-browser-main
|
||
className="flex min-h-0 flex-1 flex-col overflow-auto"
|
||
>
|
||
<DriveMarqueeSurface
|
||
enabled={filteredFiles.length > 0}
|
||
className="min-h-full"
|
||
>
|
||
{files.length === 0 ? (
|
||
<p
|
||
className={cn(
|
||
DRIVE_CARD_PAD_X,
|
||
DRIVE_CARD_SCROLL_PT,
|
||
"py-8 text-center text-sm text-muted-foreground"
|
||
)}
|
||
>
|
||
Ce dossier est vide.
|
||
{canCreate ? " Importez un fichier ou créez un dossier." : ""}
|
||
</p>
|
||
) : filteredFiles.length === 0 ? (
|
||
<p
|
||
className={cn(
|
||
DRIVE_CARD_PAD_X,
|
||
DRIVE_CARD_SCROLL_PT,
|
||
"py-8 text-center text-sm text-muted-foreground"
|
||
)}
|
||
>
|
||
Aucun élément ne correspond aux filtres.
|
||
</p>
|
||
) : (
|
||
<FileBrowser
|
||
items={filteredFiles}
|
||
view="shared"
|
||
allowShare={false}
|
||
writable={writable}
|
||
hideFavorite
|
||
disableDnd
|
||
mutations={mutations}
|
||
publicShare={{ token, password }}
|
||
onOpenItem={openItem}
|
||
onDownloadItem={(file) =>
|
||
void downloadPublicShareFile(token, file, password).catch(() =>
|
||
toast.error("Téléchargement impossible")
|
||
)
|
||
}
|
||
/>
|
||
)}
|
||
<DriveScrollEndSpacer />
|
||
</DriveMarqueeSurface>
|
||
</main>
|
||
</div>
|
||
)
|
||
}
|