295 lines
9.3 KiB
TypeScript
295 lines
9.3 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useMemo, useState } from "react"
|
|
import { useParams, useSearchParams } from "next/navigation"
|
|
import { DriveHeader } from "@/components/drive/drive-header"
|
|
import { DriveMobileBottomBar } from "@/components/drive/drive-mobile-bottom-bar"
|
|
import { DriveBrowserChrome } from "@/components/drive/drive-browser-chrome"
|
|
import { FileBrowser } from "@/components/drive/file-browser"
|
|
import { DriveMarqueeSurface } from "@/components/drive/drive-marquee-surface"
|
|
import { DriveScrollEndSpacer } from "@/components/drive/drive-scroll-end-spacer"
|
|
import { parseDriveSegments, folderPathFromSegments } from "@/lib/drive/drive-url"
|
|
import {
|
|
type DriveSearchScope,
|
|
defaultDriveSearchScope,
|
|
fileBrowserViewForSearchScope,
|
|
parseDriveSearchParams,
|
|
} from "@/lib/drive/drive-search"
|
|
import { useDriveFilteredItems } from "@/lib/hooks/use-drive-filtered-items"
|
|
import { useDriveFiltersStore } from "@/lib/stores/drive-filters-store"
|
|
import { useDriveSettingsStore } from "@/lib/stores/drive-settings-store"
|
|
import {
|
|
DRIVE_BROWSER_CARD_CLASS,
|
|
DRIVE_CARD_PAD_X,
|
|
DRIVE_CARD_SCROLL_PT,
|
|
DRIVE_MAIN_INSET_X,
|
|
} from "@/lib/drive/drive-chrome-classes"
|
|
import { cn } from "@/lib/utils"
|
|
import {
|
|
useDriveList,
|
|
useDriveMountList,
|
|
useDriveOrgList,
|
|
useDriveRecent,
|
|
useDriveSearch,
|
|
useDriveSharedWithMe,
|
|
useDriveStarred,
|
|
useDriveTrash,
|
|
} from "@/lib/api/hooks/use-drive-queries"
|
|
import { pathRefFromRoute } from "@/lib/api/drive-roots"
|
|
|
|
export default function DriveBrowserPage() {
|
|
const params = useParams()
|
|
const urlSearchParams = useSearchParams()
|
|
const segments = params.segments as string[] | undefined
|
|
const route = useMemo(() => parseDriveSegments(segments), [segments])
|
|
|
|
const folderPath = folderPathFromSegments(route.pathSegments)
|
|
const contextView =
|
|
route.view === "shared"
|
|
? "shared"
|
|
: route.view === "search"
|
|
? "files"
|
|
: route.view === "org" || route.view === "mount"
|
|
? route.view
|
|
: route.view
|
|
const fallbackScope = defaultDriveSearchScope(
|
|
route.view === "shared" ? "shared" : "files",
|
|
folderPath
|
|
)
|
|
|
|
const committedSearch = useMemo(() => {
|
|
if (route.view !== "search") return null
|
|
return parseDriveSearchParams(urlSearchParams, {
|
|
scope: fallbackScope,
|
|
folderPath,
|
|
})
|
|
}, [route.view, urlSearchParams, fallbackScope, folderPath])
|
|
|
|
const [searchInput, setSearchInput] = useState("")
|
|
const [searchScope, setSearchScope] = useState<DriveSearchScope>(fallbackScope)
|
|
|
|
useEffect(() => {
|
|
if (route.view === "search" && committedSearch) {
|
|
setSearchInput(committedSearch.query)
|
|
setSearchScope(committedSearch.scope)
|
|
}
|
|
}, [route.view, committedSearch?.query, committedSearch?.scope])
|
|
|
|
useEffect(() => {
|
|
if (route.view !== "search") {
|
|
setSearchScope(fallbackScope)
|
|
}
|
|
}, [route.view, fallbackScope])
|
|
|
|
const filters = useDriveFiltersStore()
|
|
const sortField = useDriveSettingsStore((s) => s.sortField)
|
|
const sortDir = useDriveSettingsStore((s) => s.sortDir)
|
|
const folderPlacement = useDriveSettingsStore((s) => s.folderPlacement)
|
|
|
|
const list = useDriveList(folderPath, route.page, "", route.view === "files")
|
|
const orgList = useDriveOrgList(route.rootId ?? "", folderPath, route.page, route.view === "org" && Boolean(route.rootId))
|
|
const mountList = useDriveMountList(route.rootId ?? "", folderPath, route.page, route.view === "mount" && Boolean(route.rootId))
|
|
const shared = useDriveSharedWithMe(
|
|
route.page,
|
|
"",
|
|
route.view === "shared" && route.pathSegments.length === 0
|
|
)
|
|
const sharedFolder = useDriveList(
|
|
folderPath,
|
|
route.page,
|
|
"",
|
|
route.view === "shared" && route.pathSegments.length > 0
|
|
)
|
|
const recent = useDriveRecent()
|
|
const starred = useDriveStarred(folderPath)
|
|
const trash = useDriveTrash()
|
|
const searchResults = useDriveSearch(
|
|
committedSearch?.query ?? "",
|
|
committedSearch?.scope ?? "all",
|
|
committedSearch?.scope === "folder" ? committedSearch.folderPath : "/",
|
|
route.page,
|
|
route.view === "search" && Boolean(committedSearch?.query)
|
|
)
|
|
|
|
const active =
|
|
route.view === "search"
|
|
? searchResults
|
|
: route.view === "recent"
|
|
? recent
|
|
: route.view === "starred"
|
|
? starred
|
|
: route.view === "trash"
|
|
? trash
|
|
: route.view === "shared"
|
|
? route.pathSegments.length === 0
|
|
? shared
|
|
: sharedFolder
|
|
: route.view === "org"
|
|
? orgList
|
|
: route.view === "mount"
|
|
? mountList
|
|
: list
|
|
|
|
const files = active.data?.files ?? []
|
|
|
|
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 browseWithSubtree =
|
|
route.view === "files" ||
|
|
(route.view === "shared" && route.pathSegments.length > 0)
|
|
|
|
const { filteredItems: filteredFiles, corpusLoading } = useDriveFilteredItems(
|
|
files,
|
|
filtersSnapshot,
|
|
{ sortField, sortDir, folderPlacement },
|
|
{
|
|
recursiveCorpus: browseWithSubtree,
|
|
scopePath: folderPath,
|
|
}
|
|
)
|
|
|
|
const isLoading = active.isLoading || corpusLoading
|
|
|
|
const isTrash = route.view === "trash"
|
|
const isSearchView = route.view === "search"
|
|
const searchBrowserView = committedSearch
|
|
? fileBrowserViewForSearchScope(committedSearch.scope)
|
|
: "files"
|
|
|
|
const emptyMessage = isSearchView
|
|
? committedSearch?.query
|
|
? "Aucun résultat pour cette recherche."
|
|
: "Saisissez un terme de recherche."
|
|
: "Ce dossier est vide."
|
|
|
|
return (
|
|
<>
|
|
<DriveHeader
|
|
search={searchInput}
|
|
onSearchChange={setSearchInput}
|
|
searchScope={searchScope}
|
|
onSearchScopeChange={setSearchScope}
|
|
folderPath={folderPath}
|
|
contextView={contextView}
|
|
/>
|
|
<div
|
|
className={cn(
|
|
"flex min-h-0 flex-1 flex-col pb-1 max-sm:pb-0",
|
|
DRIVE_MAIN_INSET_X
|
|
)}
|
|
>
|
|
<div className={DRIVE_BROWSER_CARD_CLASS} data-drive-browser-card>
|
|
<DriveBrowserChrome
|
|
view={route.view}
|
|
segments={route.pathSegments}
|
|
rootId={route.rootId}
|
|
isTrash={isTrash}
|
|
items={filteredFiles}
|
|
searchState={committedSearch}
|
|
/>
|
|
<main
|
|
data-drive-browser-main
|
|
className="flex min-h-0 flex-1 flex-col overflow-auto"
|
|
>
|
|
<DriveMarqueeSurface
|
|
enabled={!isLoading && !active.isError && filteredFiles.length > 0}
|
|
className="min-h-full"
|
|
>
|
|
{isLoading && (
|
|
<p
|
|
className={cn(
|
|
DRIVE_CARD_PAD_X,
|
|
DRIVE_CARD_SCROLL_PT,
|
|
"py-8 text-center text-muted-foreground"
|
|
)}
|
|
>
|
|
Chargement…
|
|
</p>
|
|
)}
|
|
{active.isError && (
|
|
<p
|
|
className={cn(
|
|
DRIVE_CARD_PAD_X,
|
|
DRIVE_CARD_SCROLL_PT,
|
|
"py-8 text-center text-destructive"
|
|
)}
|
|
>
|
|
{isSearchView
|
|
? "Impossible de charger les résultats de recherche."
|
|
: "Impossible de charger les fichiers."}
|
|
</p>
|
|
)}
|
|
{!isLoading && !active.isError && files.length === 0 && (
|
|
<p
|
|
className={cn(
|
|
DRIVE_CARD_PAD_X,
|
|
DRIVE_CARD_SCROLL_PT,
|
|
"py-8 text-center text-muted-foreground"
|
|
)}
|
|
>
|
|
{emptyMessage}
|
|
</p>
|
|
)}
|
|
{!isLoading && !active.isError && files.length > 0 && filteredFiles.length === 0 && (
|
|
<p
|
|
className={cn(
|
|
DRIVE_CARD_PAD_X,
|
|
DRIVE_CARD_SCROLL_PT,
|
|
"py-8 text-center text-muted-foreground"
|
|
)}
|
|
>
|
|
Aucun élément ne correspond aux filtres.
|
|
</p>
|
|
)}
|
|
{filteredFiles.length > 0 ? (
|
|
<FileBrowser
|
|
items={filteredFiles}
|
|
view={
|
|
isSearchView
|
|
? searchBrowserView
|
|
: route.view === "shared"
|
|
? "shared"
|
|
: route.view
|
|
}
|
|
rootId={route.rootId}
|
|
isTrash={isTrash}
|
|
/>
|
|
) : null}
|
|
<DriveScrollEndSpacer />
|
|
</DriveMarqueeSurface>
|
|
</main>
|
|
</div>
|
|
</div>
|
|
<DriveMobileBottomBar
|
|
search={searchInput}
|
|
onSearchChange={setSearchInput}
|
|
searchScope={searchScope}
|
|
onSearchScopeChange={setSearchScope}
|
|
folderPath={folderPath}
|
|
contextView={contextView}
|
|
resultsMode={isSearchView}
|
|
parentPath={folderPath}
|
|
/>
|
|
</>
|
|
)
|
|
}
|