fix: #332
This commit is contained in:
parent
3c66c18c77
commit
3cbc345c00
1 changed files with 267 additions and 94 deletions
|
@ -3,12 +3,15 @@ import { useClipboard } from '@mantine/hooks';
|
|||
import { useModals } from '@mantine/modals';
|
||||
import { showNotification } from '@mantine/notifications';
|
||||
import {
|
||||
IconClipboardCheck,
|
||||
IconClipboardCopy,
|
||||
IconExternalLink,
|
||||
IconFiles,
|
||||
IconFolderMinus,
|
||||
IconFolderPlus,
|
||||
IconFolderX,
|
||||
IconGridDots,
|
||||
IconList,
|
||||
IconLock,
|
||||
IconLockAccessOff,
|
||||
IconLockOpen,
|
||||
|
@ -17,9 +20,12 @@ import Link from 'components/Link';
|
|||
import MutedText from 'components/MutedText';
|
||||
import useFetch from 'hooks/useFetch';
|
||||
import { useFolders } from 'lib/queries/folders';
|
||||
import { listViewFoldersSelector } from 'lib/recoil/settings';
|
||||
import { relativeTime } from 'lib/utils/client';
|
||||
import { DataTable, DataTableSortStatus } from 'mantine-datatable';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import CreateFolderModal from './CreateFolderModal';
|
||||
import ViewFolderFilesModal from './ViewFolderFilesModal';
|
||||
|
||||
|
@ -34,6 +40,32 @@ export default function Folders({ disableMediaPreview, exifEnabled, compress })
|
|||
const clipboard = useClipboard();
|
||||
const router = useRouter();
|
||||
|
||||
const [listView, setListView] = useRecoilState(listViewFoldersSelector);
|
||||
|
||||
const [sortStatus, setSortStatus] = useState<DataTableSortStatus>({
|
||||
columnAccessor: 'updatedAt',
|
||||
direction: 'desc',
|
||||
});
|
||||
const [records, setRecords] = useState(folders.data);
|
||||
|
||||
useEffect(() => {
|
||||
setRecords(folders.data);
|
||||
}, [folders.data]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!records || records.length === 0) return;
|
||||
|
||||
const sortedRecords = [...records].sort((a, b) => {
|
||||
if (sortStatus.direction === 'asc') {
|
||||
return a[sortStatus.columnAccessor] > b[sortStatus.columnAccessor] ? 1 : -1;
|
||||
}
|
||||
|
||||
return a[sortStatus.columnAccessor] < b[sortStatus.columnAccessor] ? 1 : -1;
|
||||
});
|
||||
|
||||
setRecords(sortedRecords);
|
||||
}, [sortStatus]);
|
||||
|
||||
useEffect(() => {
|
||||
if (router.query.create) {
|
||||
setCreateOpen(true);
|
||||
|
@ -122,8 +154,147 @@ export default function Folders({ disableMediaPreview, exifEnabled, compress })
|
|||
<ActionIcon onClick={() => setCreateOpen(!createOpen)} component='a' variant='filled' color='primary'>
|
||||
<IconFolderPlus size='1rem' />
|
||||
</ActionIcon>
|
||||
<Tooltip label={listView ? 'Switch to grid view' : 'Switch to list view'}>
|
||||
<ActionIcon variant='filled' color='primary' onClick={() => setListView(!listView)}>
|
||||
{listView ? <IconList size='1rem' /> : <IconGridDots size='1rem' />}
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Group>
|
||||
|
||||
{listView ? (
|
||||
<DataTable
|
||||
withBorder
|
||||
borderRadius='md'
|
||||
highlightOnHover
|
||||
verticalSpacing='sm'
|
||||
columns={[
|
||||
{ accessor: 'id', title: 'ID', sortable: true },
|
||||
{ accessor: 'name', sortable: true },
|
||||
|
||||
{
|
||||
accessor: 'public',
|
||||
sortable: true,
|
||||
render: (folder) => (folder.public ? 'Public' : 'Private'),
|
||||
},
|
||||
{
|
||||
accessor: 'createdAt',
|
||||
title: 'Created',
|
||||
sortable: true,
|
||||
render: (folder) => new Date(folder.createdAt).toLocaleString(),
|
||||
},
|
||||
{
|
||||
accessor: 'updatedAt',
|
||||
title: 'Last updated',
|
||||
sortable: true,
|
||||
render: (folder) => new Date(folder.updatedAt).toLocaleString(),
|
||||
},
|
||||
{
|
||||
accessor: 'actions',
|
||||
textAlignment: 'right',
|
||||
render: (folder) => (
|
||||
<Group spacing={4} position='right' noWrap>
|
||||
<Tooltip label='View files in folder'>
|
||||
<ActionIcon
|
||||
onClick={() => {
|
||||
setViewOpen(true);
|
||||
setActiveFolderId(folder.id);
|
||||
}}
|
||||
variant='subtle'
|
||||
color='primary'
|
||||
>
|
||||
<IconFiles size='1rem' />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={folder.public ? 'Make folder private' : 'Make folder public'}>
|
||||
<ActionIcon onClick={() => makePublic(folder)} variant='subtle' color='primary'>
|
||||
{folder.public ? <IconLockOpen size='1rem' /> : <IconLock size='1rem' />}
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label='Open folder in new tab'>
|
||||
<ActionIcon
|
||||
onClick={() => window.open(`/folder/${folder.id}`, '_blank')}
|
||||
variant='subtle'
|
||||
color='primary'
|
||||
>
|
||||
<IconExternalLink size='1rem' />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label='Copy folder link'>
|
||||
<ActionIcon
|
||||
onClick={() => {
|
||||
clipboard.copy(`${window.location.origin}/folder/${folder.id}`);
|
||||
showNotification({
|
||||
title: 'Copied folder link',
|
||||
message: 'Copied folder link to clipboard',
|
||||
color: 'green',
|
||||
icon: <IconClipboardCheck size='1rem' />,
|
||||
});
|
||||
}}
|
||||
variant='subtle'
|
||||
color='primary'
|
||||
>
|
||||
<IconClipboardCopy size='1rem' />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label='Delete folder'>
|
||||
<ActionIcon onClick={() => deleteFolder(folder)} variant='subtle' color='red'>
|
||||
<IconFolderX size='1rem' />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Group>
|
||||
),
|
||||
},
|
||||
]}
|
||||
sortStatus={sortStatus}
|
||||
onSortStatusChange={setSortStatus}
|
||||
records={records ?? []}
|
||||
fetching={folders.isLoading}
|
||||
minHeight={160}
|
||||
loaderBackgroundBlur={5}
|
||||
loaderVariant='dots'
|
||||
rowContextMenu={{
|
||||
shadow: 'xl',
|
||||
borderRadius: 'md',
|
||||
items: (folder) => [
|
||||
{
|
||||
key: 'viewFiles',
|
||||
title: 'View files in folder',
|
||||
icon: <IconFiles size='1rem' />,
|
||||
onClick: () => {
|
||||
setViewOpen(true);
|
||||
setActiveFolderId(folder.id);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'makePublic',
|
||||
title: folder.public ? 'Make folder private' : 'Make folder public',
|
||||
icon: folder.public ? <IconLockOpen size='1rem' /> : <IconLock size='1rem' />,
|
||||
onClick: () => makePublic(folder),
|
||||
},
|
||||
{
|
||||
key: 'openFolder',
|
||||
title: 'Open folder in a new tab',
|
||||
icon: <IconExternalLink size='1rem' />,
|
||||
onClick: () => window.open(`/folder/${folder.id}`, '_blank'),
|
||||
},
|
||||
{
|
||||
key: 'copyLink',
|
||||
title: 'Copy folder link to clipboard',
|
||||
icon: <IconClipboardCopy size='1rem' />,
|
||||
onClick: () => {
|
||||
clipboard.copy(`${window.location.origin}/folder/${folder.id}`);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'deleteFolder',
|
||||
title: 'Delete folder',
|
||||
icon: <IconFolderX size='1rem' />,
|
||||
onClick: () => deleteFolder(folder),
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<SimpleGrid cols={3} spacing='lg' breakpoints={[{ maxWidth: 'sm', cols: 1, spacing: 'sm' }]}>
|
||||
{folders.isSuccess
|
||||
? folders.data.length
|
||||
|
@ -195,7 +366,8 @@ export default function Folders({ disableMediaPreview, exifEnabled, compress })
|
|||
title: 'Copied folder link',
|
||||
message: (
|
||||
<>
|
||||
Copied <Link href={`/folder/${folder.id}`}>folder link</Link> to clipboard
|
||||
Copied <Link href={`/folder/${folder.id}`}>folder link</Link> to
|
||||
clipboard
|
||||
</>
|
||||
),
|
||||
color: 'green',
|
||||
|
@ -223,6 +395,7 @@ export default function Folders({ disableMediaPreview, exifEnabled, compress })
|
|||
</div>
|
||||
))}
|
||||
</SimpleGrid>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue