mirror of
https://github.com/diced/zipline.git
synced 2025-04-11 23:31:17 -05:00
feat: overhaul qs system
This commit is contained in:
parent
e8207addba
commit
9611e6d5a5
19 changed files with 117 additions and 133 deletions
|
@ -65,6 +65,7 @@
|
|||
"ms": "^2.1.3",
|
||||
"multer": "1.4.5-lts.1",
|
||||
"next": "^15.2.4",
|
||||
"nuqs": "^2.4.1",
|
||||
"otplib": "^12.0.1",
|
||||
"prisma": "^6.4.1",
|
||||
"qrcode": "^1.5.4",
|
||||
|
|
33
pnpm-lock.yaml
generated
33
pnpm-lock.yaml
generated
|
@ -143,6 +143,9 @@ importers:
|
|||
next:
|
||||
specifier: ^15.2.4
|
||||
version: 15.2.4(@babel/core@7.26.9)(@opentelemetry/api@1.7.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.86.1)
|
||||
nuqs:
|
||||
specifier: ^2.4.1
|
||||
version: 2.4.1(next@15.2.4(@babel/core@7.26.9)(@opentelemetry/api@1.7.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.86.1))(react@19.0.0)
|
||||
otplib:
|
||||
specifier: ^12.0.1
|
||||
version: 12.0.1
|
||||
|
@ -3598,6 +3601,9 @@ packages:
|
|||
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
|
||||
mitt@3.0.1:
|
||||
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
|
||||
|
||||
mkdirp@0.5.6:
|
||||
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
|
||||
hasBin: true
|
||||
|
@ -3673,6 +3679,24 @@ packages:
|
|||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
nuqs@2.4.1:
|
||||
resolution: {integrity: sha512-u6sngTspqDe3jWHtcmqHQg3dl35niizCZAsm5gy7PBlgG2rwl71Dp2QUv5hwBaWKI9qz0wqILZY86TsRxq66SQ==}
|
||||
peerDependencies:
|
||||
'@remix-run/react': '>=2'
|
||||
next: '>=14.2.0'
|
||||
react: '>=18.2.0 || ^19.0.0-0'
|
||||
react-router: ^6 || ^7
|
||||
react-router-dom: ^6 || ^7
|
||||
peerDependenciesMeta:
|
||||
'@remix-run/react':
|
||||
optional: true
|
||||
next:
|
||||
optional: true
|
||||
react-router:
|
||||
optional: true
|
||||
react-router-dom:
|
||||
optional: true
|
||||
|
||||
nwsapi@2.2.16:
|
||||
resolution: {integrity: sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==}
|
||||
|
||||
|
@ -8955,6 +8979,8 @@ snapshots:
|
|||
|
||||
minipass@7.1.2: {}
|
||||
|
||||
mitt@3.0.1: {}
|
||||
|
||||
mkdirp@0.5.6:
|
||||
dependencies:
|
||||
minimist: 1.2.8
|
||||
|
@ -9029,6 +9055,13 @@ snapshots:
|
|||
|
||||
normalize-path@3.0.0: {}
|
||||
|
||||
nuqs@2.4.1(next@15.2.4(@babel/core@7.26.9)(@opentelemetry/api@1.7.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.86.1))(react@19.0.0):
|
||||
dependencies:
|
||||
mitt: 3.0.1
|
||||
react: 19.0.0
|
||||
optionalDependencies:
|
||||
next: 15.2.4(@babel/core@7.26.9)(@opentelemetry/api@1.7.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.86.1)
|
||||
|
||||
nwsapi@2.2.16: {}
|
||||
|
||||
object-assign@4.1.1: {}
|
||||
|
|
|
@ -15,6 +15,7 @@ import { useEffect, useState } from 'react';
|
|||
import { renderMode } from '../pages/upload/renderMode';
|
||||
import Render from '../render/Render';
|
||||
import fileIcon from './fileIcon';
|
||||
import { parseAsStringLiteral, useQueryState } from 'nuqs';
|
||||
|
||||
function PlaceholderContent({ text, Icon }: { text: string; Icon: Icon }) {
|
||||
return (
|
||||
|
@ -77,6 +78,8 @@ export default function DashboardFileType({
|
|||
code?: boolean;
|
||||
allowZoom?: boolean;
|
||||
}) {
|
||||
const [overrideType] = useQueryState('otype', parseAsStringLiteral(['video', 'audio', 'image', 'text']));
|
||||
|
||||
const disableMediaPreview = useSettingsStore((state) => state.settings.disableMediaPreview);
|
||||
|
||||
const dbFile = 'id' in file;
|
||||
|
@ -129,7 +132,7 @@ export default function DashboardFileType({
|
|||
if (code) {
|
||||
setType('text');
|
||||
gettext();
|
||||
} else if (type === 'text') {
|
||||
} else if (overrideType === 'text' || type === 'text') {
|
||||
gettext();
|
||||
} else {
|
||||
return;
|
||||
|
@ -153,7 +156,7 @@ export default function DashboardFileType({
|
|||
</Paper>
|
||||
);
|
||||
|
||||
switch (type) {
|
||||
switch (overrideType || type) {
|
||||
case 'video':
|
||||
return show ? (
|
||||
<video
|
||||
|
|
|
@ -5,8 +5,8 @@ import { ActionIcon, Badge, Button, Card, Group, Modal, Paper, Stack, Text, Tool
|
|||
import { showNotification } from '@mantine/notifications';
|
||||
import { IncompleteFileStatus } from '@prisma/client';
|
||||
import { IconFileDots, IconTrashFilled } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { ReactNode, useEffect, useState } from 'react';
|
||||
import { parseAsBoolean, useQueryState } from 'nuqs';
|
||||
import { ReactNode } from 'react';
|
||||
import useSWR from 'swr';
|
||||
|
||||
const badgeMap: Record<IncompleteFileStatus, ReactNode> = {
|
||||
|
@ -33,9 +33,7 @@ const badgeMap: Record<IncompleteFileStatus, ReactNode> = {
|
|||
};
|
||||
|
||||
export default function PendingFilesButton() {
|
||||
const router = useRouter();
|
||||
|
||||
const [open, setOpen] = useState(router.query.pending !== undefined);
|
||||
const [open, setOpen] = useQueryState('popen', parseAsBoolean.withDefault(false));
|
||||
|
||||
const { data: incompleteFiles, mutate } = useSWR<
|
||||
Extract<IncompleteFile[], Response['/api/user/files/incomplete']>
|
||||
|
@ -68,15 +66,6 @@ export default function PendingFilesButton() {
|
|||
mutate();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
router.push({ query: { ...router.query, pending: 'true' } }, undefined, { shallow: true });
|
||||
} else {
|
||||
delete router.query.pending;
|
||||
router.push({ query: router.query }, undefined, { shallow: true });
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal opened={open} onClose={() => setOpen(false)} title='Pending Files'>
|
||||
|
|
|
@ -26,7 +26,7 @@ export default function CreateTagModal({ open, onClose }: { open: boolean; onClo
|
|||
const color = values.color.trim() === '' ? colorHash(values.name) : values.color.trim();
|
||||
|
||||
if (!color.startsWith('#')) {
|
||||
form.setFieldError('color', 'Color must start with #');
|
||||
return form.setFieldError('color', 'Color must start with #');
|
||||
}
|
||||
|
||||
const { data, error } = await fetchApi<Extract<Response['/api/user/tags'], Tag>>(
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
import { mutateFiles } from '@/components/file/actions';
|
||||
import { Response } from '@/lib/api/response';
|
||||
import { Tag } from '@/lib/db/models/tag';
|
||||
import { ActionIcon, Group, Modal, Paper, Stack, Text, Title, Tooltip } from '@mantine/core';
|
||||
import { IconPencil, IconPlus, IconTagOff, IconTags, IconTrashFilled } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useState } from 'react';
|
||||
import useSWR from 'swr';
|
||||
import TagPill from './TagPill';
|
||||
import { fetchApi } from '@/lib/fetchApi';
|
||||
import { ActionIcon, Group, Modal, Paper, Stack, Text, Title, Tooltip } from '@mantine/core';
|
||||
import { showNotification } from '@mantine/notifications';
|
||||
import { IconPencil, IconPlus, IconTagOff, IconTags, IconTrashFilled } from '@tabler/icons-react';
|
||||
import { parseAsBoolean, useQueryState } from 'nuqs';
|
||||
import { useState } from 'react';
|
||||
import useSWR from 'swr';
|
||||
import CreateTagModal from './CreateTagModal';
|
||||
import EditTagModal from './EditTagModal';
|
||||
import { mutateFiles } from '@/components/file/actions';
|
||||
import TagPill from './TagPill';
|
||||
|
||||
export default function TagsButton() {
|
||||
const router = useRouter();
|
||||
|
||||
const [open, setOpen] = useState(router.query.tags !== undefined);
|
||||
const [createModalOpen, setCreateModalOpen] = useState(false);
|
||||
const [open, setOpen] = useQueryState('topen', parseAsBoolean.withDefault(false));
|
||||
const [createModalOpen, setCreateModalOpen] = useQueryState('ctopen', parseAsBoolean.withDefault(false));
|
||||
const [selectedTag, setSelectedTag] = useState<Tag | null>(null);
|
||||
|
||||
const { data: tags, mutate } = useSWR<Extract<Tag[], Response['/api/user/tags']>>('/api/user/tags');
|
||||
|
@ -44,15 +42,6 @@ export default function TagsButton() {
|
|||
mutateFiles();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
router.push({ query: { ...router.query, tags: 'true' } }, undefined, { shallow: true });
|
||||
} else {
|
||||
delete router.query.tags;
|
||||
router.push({ query: router.query }, undefined, { shallow: true });
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<CreateTagModal open={createModalOpen} onClose={() => setCreateModalOpen(false)} />
|
||||
|
|
|
@ -7,7 +7,17 @@ type ApiPaginationOptions = {
|
|||
filter?: string;
|
||||
perpage?: number;
|
||||
favorite?: boolean;
|
||||
sort?: keyof Prisma.FileOrderByWithAggregationInput;
|
||||
sort?:
|
||||
| 'name'
|
||||
| 'id'
|
||||
| 'createdAt'
|
||||
| 'updatedAt'
|
||||
| 'deletesAt'
|
||||
| 'originalName'
|
||||
| 'size'
|
||||
| 'type'
|
||||
| 'views'
|
||||
| 'favorite';
|
||||
order?: 'asc' | 'desc';
|
||||
id?: string;
|
||||
search?: {
|
||||
|
|
|
@ -12,47 +12,24 @@ import {
|
|||
Title,
|
||||
} from '@mantine/core';
|
||||
import { IconFileUpload, IconFilesOff } from '@tabler/icons-react';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useApiPagination } from '../useApiPagination';
|
||||
import dynamic from 'next/dynamic';
|
||||
import Link from 'next/link';
|
||||
import { parseAsInteger, useQueryState } from 'nuqs';
|
||||
import { useApiPagination } from '../useApiPagination';
|
||||
|
||||
const DashboardFile = dynamic(() => import('@/components/file/DashboardFile'), {
|
||||
loading: () => <Skeleton height={350} animate />,
|
||||
});
|
||||
|
||||
export default function FavoriteFiles() {
|
||||
const router = useRouter();
|
||||
const [page, setPage] = useQueryState('fpage', parseAsInteger.withDefault(1));
|
||||
|
||||
const [page, setPage] = useState<number>(
|
||||
router.query.favoritePage ? parseInt(router.query.favoritePage as string) : 1,
|
||||
);
|
||||
const { data, isLoading } = useApiPagination({
|
||||
page,
|
||||
favorite: true,
|
||||
filter: 'dashboard',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
router.replace(
|
||||
{
|
||||
query: {
|
||||
...router.query,
|
||||
favoritePage: page,
|
||||
},
|
||||
},
|
||||
undefined,
|
||||
{ shallow: true },
|
||||
);
|
||||
}, [page]);
|
||||
|
||||
if (!isLoading && !data?.page.length && router.query.favoritePage) {
|
||||
delete router.query.favoritePage;
|
||||
router.replace({ query: router.query }, undefined, { shallow: true });
|
||||
setPage(1);
|
||||
}
|
||||
|
||||
if (!isLoading && !data?.page.length) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ import {
|
|||
useCombobox,
|
||||
} from '@mantine/core';
|
||||
import { useClipboard } from '@mantine/hooks';
|
||||
import type { Prisma } from '@prisma/client';
|
||||
import {
|
||||
IconCopy,
|
||||
IconExternalLink,
|
||||
|
@ -39,7 +38,7 @@ import {
|
|||
} from '@tabler/icons-react';
|
||||
import { DataTable } from 'mantine-datatable';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import { parseAsBoolean, parseAsInteger, parseAsStringLiteral, useQueryState } from 'nuqs';
|
||||
import { useEffect, useReducer, useState } from 'react';
|
||||
import useSWR from 'swr';
|
||||
import { bulkDelete, bulkFavorite } from '../bulk';
|
||||
|
@ -179,7 +178,6 @@ function TagsFilter({
|
|||
}
|
||||
|
||||
export default function FileTable({ id }: { id?: string }) {
|
||||
const router = useRouter();
|
||||
const clipboard = useClipboard();
|
||||
const warnDeletion = useSettingsStore((state) => state.settings.warnDeletion);
|
||||
|
||||
|
@ -187,13 +185,30 @@ export default function FileTable({ id }: { id?: string }) {
|
|||
'/api/user/folders?noincl=true',
|
||||
);
|
||||
|
||||
const [page, setPage] = useState<number>(router.query.page ? parseInt(router.query.page as string) : 1);
|
||||
const [page, setPage] = useQueryState('page', parseAsInteger.withDefault(1));
|
||||
const [perpage, setPerpage] = useState<number>(20);
|
||||
const [sort, setSort] = useState<keyof Prisma.FileOrderByWithAggregationInput>('createdAt');
|
||||
const [order, setOrder] = useState<'asc' | 'desc'>('desc');
|
||||
const [sort, setSort] = useQueryState(
|
||||
'sort',
|
||||
parseAsStringLiteral([
|
||||
'id',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'deletesAt',
|
||||
'name',
|
||||
'originalName',
|
||||
'size',
|
||||
'type',
|
||||
'views',
|
||||
'favorite',
|
||||
]).withDefault('createdAt'),
|
||||
);
|
||||
const [order, setOrder] = useQueryState<'asc' | 'desc'>(
|
||||
'order',
|
||||
parseAsStringLiteral(['asc', 'desc']).withDefault('desc'),
|
||||
);
|
||||
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
||||
|
||||
const [idSearchOpen, setIdSearchOpen] = useState(false);
|
||||
const [idSearchOpen, setIdSearchOpen] = useQueryState('idsearch', parseAsBoolean.withDefault(false));
|
||||
const [searchField, setSearchField] = useState<'name' | 'originalName' | 'type' | 'tags' | 'id'>('name');
|
||||
const [searchQuery, setSearchQuery] = useReducer(
|
||||
(state: ReducerQuery['state'], action: ReducerQuery['action']) => {
|
||||
|
@ -253,19 +268,6 @@ export default function FileTable({ id }: { id?: string }) {
|
|||
}),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
router.replace(
|
||||
{
|
||||
query: {
|
||||
...router.query,
|
||||
page: page,
|
||||
},
|
||||
},
|
||||
undefined,
|
||||
{ shallow: true },
|
||||
);
|
||||
}, [page]);
|
||||
|
||||
useEffect(() => {
|
||||
if (data && selectedFile) {
|
||||
const file = data.page.find((x) => x.id === selectedFile.id);
|
||||
|
@ -546,7 +548,7 @@ export default function FileTable({ id }: { id?: string }) {
|
|||
direction: order,
|
||||
}}
|
||||
onSortStatusChange={(data) => {
|
||||
setSort(data.columnAccessor as keyof Prisma.FileOrderByWithAggregationInput);
|
||||
setSort(data.columnAccessor as any);
|
||||
setOrder(data.direction);
|
||||
}}
|
||||
onCellClick={({ record }) => setSelectedFile(record)}
|
||||
|
|
|
@ -17,6 +17,7 @@ import Link from 'next/link';
|
|||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useApiPagination } from '../useApiPagination';
|
||||
import { parseAsInteger, useQueryState } from 'nuqs';
|
||||
|
||||
const DashboardFile = dynamic(() => import('@/components/file/DashboardFile'), {
|
||||
loading: () => <Skeleton height={350} animate />,
|
||||
|
@ -27,7 +28,7 @@ const PER_PAGE_OPTIONS = [9, 12, 15, 30, 45];
|
|||
export default function Files({ id }: { id?: string }) {
|
||||
const router = useRouter();
|
||||
|
||||
const [page, setPage] = useState<number>(router.query.page ? parseInt(router.query.page as string) : 1);
|
||||
const [page, setPage] = useQueryState('page', parseAsInteger.withDefault(1));
|
||||
const [perpage, setPerpage] = useState<number>(15);
|
||||
const [cachedPages, setCachedPages] = useState<number>(1);
|
||||
|
||||
|
@ -43,19 +44,6 @@ export default function Files({ id }: { id?: string }) {
|
|||
}
|
||||
}, [data?.pages]);
|
||||
|
||||
useEffect(() => {
|
||||
router.replace(
|
||||
{
|
||||
query: {
|
||||
...router.query,
|
||||
page: page,
|
||||
},
|
||||
},
|
||||
undefined,
|
||||
{ shallow: true },
|
||||
);
|
||||
}, [page]);
|
||||
|
||||
const from = (page - 1) * perpage + 1;
|
||||
const to = Math.min(page * perpage, data?.total ?? 0);
|
||||
const totalRecords = data?.total ?? 0;
|
||||
|
|
|
@ -13,35 +13,17 @@ import {
|
|||
} from '@mantine/core';
|
||||
import { IconFileUpload, IconFilesOff } from '@tabler/icons-react';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { parseAsInteger, useQueryState } from 'nuqs';
|
||||
import { useApiPagination } from '../files/useApiPagination';
|
||||
|
||||
export default function FavoriteFiles() {
|
||||
const router = useRouter();
|
||||
|
||||
const [page, setPage] = useState<number>(
|
||||
router.query.favoritePage ? parseInt(router.query.favoritePage as string) : 1,
|
||||
);
|
||||
const [page, setPage] = useQueryState('fpage', parseAsInteger.withDefault(1));
|
||||
const { data, isLoading } = useApiPagination({
|
||||
page,
|
||||
favorite: true,
|
||||
filter: 'dashboard',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
router.replace(
|
||||
{
|
||||
query: {
|
||||
...router.query,
|
||||
favoritePage: page,
|
||||
},
|
||||
},
|
||||
undefined,
|
||||
{ shallow: true },
|
||||
);
|
||||
}, [page]);
|
||||
|
||||
if (!isLoading && data?.page.length === 0) return null;
|
||||
|
||||
return (
|
||||
|
|
|
@ -11,11 +11,12 @@ import { useState } from 'react';
|
|||
import { mutate } from 'swr';
|
||||
import FolderGridView from './views/FolderGridView';
|
||||
import FolderTableView from './views/FolderTableView';
|
||||
import { parseAsBoolean, useQueryState } from 'nuqs';
|
||||
|
||||
export default function DashboardFolders() {
|
||||
const view = useViewStore((state) => state.folders);
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [open, setOpen] = useQueryState('cfopen', parseAsBoolean.withDefault(false));
|
||||
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
|
|
|
@ -11,10 +11,11 @@ import { Response } from '@/lib/api/response';
|
|||
import { notifications } from '@mantine/notifications';
|
||||
import { Invite } from '@/lib/db/models/invite';
|
||||
import { mutate } from 'swr';
|
||||
import { parseAsBoolean, useQueryState } from 'nuqs';
|
||||
|
||||
export default function DashboardInvites() {
|
||||
const view = useViewStore((state) => state.invites);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [open, setOpen] = useQueryState('ciopen', parseAsBoolean.withDefault(false));
|
||||
|
||||
const form = useForm<{
|
||||
maxUses: number | '';
|
||||
|
|
|
@ -32,6 +32,7 @@ import {
|
|||
} from '@tabler/icons-react';
|
||||
import ms, { StringValue } from 'ms';
|
||||
import Link from 'next/link';
|
||||
import { parseAsBoolean, useQueryState } from 'nuqs';
|
||||
import { useEffect, useState } from 'react';
|
||||
import useSWR from 'swr';
|
||||
import { useShallow } from 'zustand/shallow';
|
||||
|
@ -39,7 +40,7 @@ import { useShallow } from 'zustand/shallow';
|
|||
export default function UploadOptionsButton({ folder, numFiles }: { folder?: string; numFiles: number }) {
|
||||
const config = useConfig();
|
||||
|
||||
const [opened, setOpen] = useState(false);
|
||||
const [opened, setOpen] = useQueryState('upopen', parseAsBoolean.withDefault(false));
|
||||
const [options, ephemeral, setOption, setEphemeral, changes, clearEphemeral, clearOptions] =
|
||||
useUploadOptionsStore(
|
||||
useShallow((state) => [
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
export enum RenderMode {
|
||||
Katex,
|
||||
Markdown,
|
||||
Highlight,
|
||||
Katex = 'katex',
|
||||
Markdown = 'md',
|
||||
Highlight = 'hl',
|
||||
}
|
||||
|
||||
export function renderMode(extension: string) {
|
||||
switch (extension) {
|
||||
case 'tex':
|
||||
case 'katex':
|
||||
return RenderMode.Katex;
|
||||
case 'md':
|
||||
return RenderMode.Markdown;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import GridTableSwitcher from '@/components/GridTableSwitcher';
|
||||
import { Response } from '@/lib/api/response';
|
||||
import { Url } from '@/lib/db/models/url';
|
||||
import { fetchApi } from '@/lib/fetchApi';
|
||||
import { useViewStore } from '@/lib/store/view';
|
||||
import {
|
||||
|
@ -23,17 +24,16 @@ import { modals } from '@mantine/modals';
|
|||
import { notifications } from '@mantine/notifications';
|
||||
import { IconClipboardCopy, IconExternalLink, IconLink, IconLinkOff } from '@tabler/icons-react';
|
||||
import Link from 'next/link';
|
||||
import { useState } from 'react';
|
||||
import { parseAsBoolean, useQueryState } from 'nuqs';
|
||||
import { mutate } from 'swr';
|
||||
import UrlGridView from './views/UrlGridView';
|
||||
import UrlTableView from './views/UrlTableView';
|
||||
import { Url } from '@/lib/db/models/url';
|
||||
|
||||
export default function DashboardURLs() {
|
||||
const clipboard = useClipboard();
|
||||
const view = useViewStore((state) => state.urls);
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [open, setOpen] = useQueryState('cuopen', parseAsBoolean.withDefault(false));
|
||||
|
||||
const form = useForm<{
|
||||
url: string;
|
||||
|
|
|
@ -22,7 +22,7 @@ import {
|
|||
import { useForm } from '@mantine/form';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import { IconPhotoMinus, IconUserCancel, IconUserPlus } from '@tabler/icons-react';
|
||||
import { useState } from 'react';
|
||||
import { parseAsBoolean, useQueryState } from 'nuqs';
|
||||
import { mutate } from 'swr';
|
||||
import UserGridView from './views/UserGridView';
|
||||
import UserTableView from './views/UserTableView';
|
||||
|
@ -30,7 +30,7 @@ import UserTableView from './views/UserTableView';
|
|||
export default function DashboardUsers() {
|
||||
const currentUser = useUserStore((state) => state.user);
|
||||
const view = useViewStore((state) => state.users);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [open, setOpen] = useQueryState('cuseropen', parseAsBoolean.withDefault(false));
|
||||
|
||||
const form = useForm<{
|
||||
username: string;
|
||||
|
|
|
@ -5,6 +5,7 @@ import { useState } from 'react';
|
|||
import KaTeX from './KaTeX';
|
||||
import Markdown from './Markdown';
|
||||
import HighlightCode from './code/HighlightCode';
|
||||
import { parseAsStringEnum, parseAsStringLiteral, useQueryState } from 'nuqs';
|
||||
|
||||
export function RenderAlert({
|
||||
renderer,
|
||||
|
@ -46,9 +47,11 @@ export default function Render({
|
|||
language: string;
|
||||
code: string;
|
||||
}) {
|
||||
const [overrideRender] = useQueryState('orender', parseAsStringEnum<RenderMode>(Object.values(RenderMode)));
|
||||
|
||||
const [highlight, setHighlight] = useState(false);
|
||||
|
||||
switch (mode) {
|
||||
switch (overrideRender || mode) {
|
||||
case RenderMode.Katex:
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { AppProps } from 'next/app';
|
||||
import { NuqsAdapter } from 'nuqs/adapters/next/pages';
|
||||
import Head from 'next/head';
|
||||
import { SWRConfig } from 'swr';
|
||||
import { ModalsProvider } from '@mantine/modals';
|
||||
|
@ -60,7 +61,9 @@ export default function App({
|
|||
}}
|
||||
>
|
||||
<Notifications zIndex={100000000} />
|
||||
<Component {...pageProps} />
|
||||
<NuqsAdapter>
|
||||
<Component {...pageProps} />
|
||||
</NuqsAdapter>
|
||||
</ModalsProvider>
|
||||
</Theming>
|
||||
</SWRConfig>
|
||||
|
|
Loading…
Add table
Reference in a new issue