mirror of
https://github.com/diced/zipline.git
synced 2025-05-11 10:26:05 +02:00
ima kms
This commit is contained in:
parent
3be9f1521e
commit
adb984b2db
5 changed files with 425 additions and 15 deletions
|
@ -47,6 +47,7 @@ import { FileMeta } from '.';
|
||||||
import Type from '../Type';
|
import Type from '../Type';
|
||||||
import Tag from 'components/File/tag/Tag';
|
import Tag from 'components/File/tag/Tag';
|
||||||
import Item from 'components/File/tag/Item';
|
import Item from 'components/File/tag/Item';
|
||||||
|
import { useDeleteFileTags, useFileTags, useTags, useUpdateFileTags } from 'lib/queries/tags';
|
||||||
|
|
||||||
export default function FileModal({
|
export default function FileModal({
|
||||||
open,
|
open,
|
||||||
|
@ -70,15 +71,14 @@ export default function FileModal({
|
||||||
const deleteFile = useFileDelete();
|
const deleteFile = useFileDelete();
|
||||||
const favoriteFile = useFileFavorite();
|
const favoriteFile = useFileFavorite();
|
||||||
const folders = useFolders();
|
const folders = useFolders();
|
||||||
|
const tags = useFileTags(file.id);
|
||||||
const [overrideRender, setOverrideRender] = useState(false);
|
const updateTags = useUpdateFileTags(file.id);
|
||||||
|
const removeTags = useDeleteFileTags(file.id);
|
||||||
const clipboard = useClipboard();
|
const clipboard = useClipboard();
|
||||||
|
|
||||||
const [tags, setTags] = useState<{ label: string; value: string; color: string }[]>([
|
const allTags = useTags();
|
||||||
{ label: 'Tag 1', value: 'tag-1', color: '#ff0000' },
|
|
||||||
{ label: 'Tag 2', value: 'tag-2', color: '#00ff00' },
|
const [overrideRender, setOverrideRender] = useState(false);
|
||||||
{ label: 'Tag 3', value: 'tag-3', color: '#0000ff' },
|
|
||||||
]);
|
|
||||||
|
|
||||||
const handleDelete = async () => {
|
const handleDelete = async () => {
|
||||||
deleteFile.mutate(file.id, {
|
deleteFile.mutate(file.id, {
|
||||||
|
@ -227,6 +227,40 @@ export default function FileModal({
|
||||||
console.log('should save');
|
console.log('should save');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleAddTags = (t: string[]) => {
|
||||||
|
// filter out existing tags from t
|
||||||
|
t = t.filter((tag) => !tags.data.find((t) => t.id === tag));
|
||||||
|
|
||||||
|
const fullTag = allTags.data.find((tag) => tag.id === t[0]);
|
||||||
|
|
||||||
|
if (!fullTag) return;
|
||||||
|
|
||||||
|
updateTags.mutate([...tags.data, fullTag], {
|
||||||
|
onSuccess: () => {
|
||||||
|
showNotification({
|
||||||
|
title: 'Added tag',
|
||||||
|
message: fullTag.name,
|
||||||
|
color: 'green',
|
||||||
|
icon: <IconTags size='1rem' />,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRemoveTags = (t: string[]) => {
|
||||||
|
const fullTag = allTags.data.find((tag) => tag.id === t[0]);
|
||||||
|
|
||||||
|
removeTags.mutate(t, {
|
||||||
|
onSuccess: () =>
|
||||||
|
showNotification({
|
||||||
|
title: 'Removed tag',
|
||||||
|
message: fullTag.name,
|
||||||
|
color: 'green',
|
||||||
|
icon: <IconTags size='1rem' />,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
opened={open}
|
opened={open}
|
||||||
|
@ -298,8 +332,9 @@ export default function FileModal({
|
||||||
<Accordion.Control icon={<IconTags size='1rem' />}>Tags</Accordion.Control>
|
<Accordion.Control icon={<IconTags size='1rem' />}>Tags</Accordion.Control>
|
||||||
<Accordion.Panel>
|
<Accordion.Panel>
|
||||||
<MultiSelect
|
<MultiSelect
|
||||||
data={tags}
|
value={tags.data?.map((t) => t.id) ?? []}
|
||||||
placeholder={tags.length ? 'Add tags' : 'Add tags (optional)'}
|
data={allTags.data?.map((t) => ({ value: t.id, label: t.name, color: t.color })) ?? []}
|
||||||
|
placeholder={allTags.data?.length ? 'Add tags' : 'Add tags (optional)'}
|
||||||
icon={<IconTags size='1rem' />}
|
icon={<IconTags size='1rem' />}
|
||||||
valueComponent={Tag}
|
valueComponent={Tag}
|
||||||
itemComponent={Item}
|
itemComponent={Item}
|
||||||
|
@ -316,9 +351,11 @@ export default function FileModal({
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
)}
|
)}
|
||||||
|
// onChange={(t) => (t.length === 1 ? handleRemoveTags(t) : handleAddTags(t))}
|
||||||
|
onChange={(t) => console.log(t)}
|
||||||
onCreate={(t) => {
|
onCreate={(t) => {
|
||||||
const item = { value: t, label: t, color: colorHash(t) };
|
const item = { value: t, label: t, color: colorHash(t) };
|
||||||
setTags([...tags, item]);
|
// setLabelTags([...labelTags, item]);
|
||||||
return item;
|
return item;
|
||||||
}}
|
}}
|
||||||
onBlur={handleTagsSave}
|
onBlur={handleTagsSave}
|
||||||
|
|
197
src/components/pages/Files/TagsModal.tsx
Normal file
197
src/components/pages/Files/TagsModal.tsx
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
import {
|
||||||
|
ActionIcon,
|
||||||
|
Button,
|
||||||
|
ColorInput,
|
||||||
|
Group,
|
||||||
|
Modal,
|
||||||
|
Paper,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
} from '@mantine/core';
|
||||||
|
import { useDeleteTags, useTags } from 'lib/queries/tags';
|
||||||
|
import { showNotification } from '@mantine/notifications';
|
||||||
|
import { IconRefresh, IconTag, IconTags, IconTagsOff } from '@tabler/icons-react';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { colorHash } from 'utils/client';
|
||||||
|
import useFetch from 'hooks/useFetch';
|
||||||
|
import { useModals } from '@mantine/modals';
|
||||||
|
import MutedText from 'components/MutedText';
|
||||||
|
|
||||||
|
export function TagCard({ tags, tag }) {
|
||||||
|
const deleteTags = useDeleteTags();
|
||||||
|
const modals = useModals();
|
||||||
|
|
||||||
|
const deleteTag = () => {
|
||||||
|
modals.openConfirmModal({
|
||||||
|
zIndex: 1000,
|
||||||
|
size: 'auto',
|
||||||
|
title: (
|
||||||
|
<Title>
|
||||||
|
Delete tag <b style={{ color: tag.color }}>{tag.name}</b>?
|
||||||
|
</Title>
|
||||||
|
),
|
||||||
|
children: `This will remove the tag from ${tag.files.length} file${tag.files.length === 1 ? '' : 's'}`,
|
||||||
|
labels: {
|
||||||
|
confirm: 'Delete',
|
||||||
|
cancel: 'Cancel',
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
modals.closeAll();
|
||||||
|
},
|
||||||
|
onConfirm() {
|
||||||
|
deleteTags.mutate([tag.id], {
|
||||||
|
onSuccess: () => {
|
||||||
|
showNotification({
|
||||||
|
title: 'Tag deleted',
|
||||||
|
message: `Tag ${tag.name} was deleted`,
|
||||||
|
color: 'green',
|
||||||
|
icon: <IconTags size='1rem' />,
|
||||||
|
});
|
||||||
|
modals.closeAll();
|
||||||
|
tags.refetch();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Paper
|
||||||
|
radius='sm'
|
||||||
|
sx={(t) => ({
|
||||||
|
backgroundColor: tag.color,
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: t.fn.darken(tag.color, 0.1),
|
||||||
|
},
|
||||||
|
cursor: 'pointer',
|
||||||
|
})}
|
||||||
|
px='xs'
|
||||||
|
onClick={deleteTag}
|
||||||
|
>
|
||||||
|
<Group position='apart'>
|
||||||
|
<Text>
|
||||||
|
{tag.name} ({tag.files.length})
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
</Paper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CreateTagModal({ tags, open, onClose }) {
|
||||||
|
const [color, setColor] = useState('');
|
||||||
|
const [name, setName] = useState('');
|
||||||
|
|
||||||
|
const [colorError, setColorError] = useState('');
|
||||||
|
const [nameError, setNameError] = useState('');
|
||||||
|
|
||||||
|
const onSubmit = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setNameError('');
|
||||||
|
setColorError('');
|
||||||
|
|
||||||
|
const n = name.trim();
|
||||||
|
const c = color.trim();
|
||||||
|
|
||||||
|
if (n.length === 0 && c.length === 0) {
|
||||||
|
setNameError('Name is required');
|
||||||
|
setColorError('Color is required');
|
||||||
|
return;
|
||||||
|
} else if (n.length === 0) {
|
||||||
|
setNameError('Name is required');
|
||||||
|
setColorError('');
|
||||||
|
return;
|
||||||
|
} else if (c.length === 0) {
|
||||||
|
setNameError('');
|
||||||
|
setColorError('Color is required');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await useFetch('/api/user/tags', 'POST', {
|
||||||
|
tags: [
|
||||||
|
{
|
||||||
|
name: n,
|
||||||
|
color: c,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!data.error) {
|
||||||
|
showNotification({
|
||||||
|
title: 'Tag created',
|
||||||
|
message: (
|
||||||
|
<>
|
||||||
|
Tag <b style={{ color: color }}>{name}</b> was created
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
color: 'green',
|
||||||
|
icon: <IconTags size='1rem' />,
|
||||||
|
});
|
||||||
|
tags.refetch();
|
||||||
|
onClose();
|
||||||
|
} else {
|
||||||
|
showNotification({
|
||||||
|
title: 'Error creating tag',
|
||||||
|
message: data.error,
|
||||||
|
color: 'red',
|
||||||
|
icon: <IconTagsOff size='1rem' />,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal title={<Title>Create Tag</Title>} size='xs' opened={open} onClose={onClose} zIndex={300}>
|
||||||
|
<form onSubmit={onSubmit}>
|
||||||
|
<TextInput
|
||||||
|
icon={<IconTag size='1rem' />}
|
||||||
|
label='Name'
|
||||||
|
value={name}
|
||||||
|
onChange={(e) => setName(e.currentTarget.value)}
|
||||||
|
error={nameError}
|
||||||
|
/>
|
||||||
|
<ColorInput
|
||||||
|
dropdownZIndex={301}
|
||||||
|
label='Color'
|
||||||
|
value={color}
|
||||||
|
onChange={setColor}
|
||||||
|
error={colorError}
|
||||||
|
rightSection={
|
||||||
|
<Tooltip label='Generate color from name'>
|
||||||
|
<ActionIcon variant='subtle' onClick={() => setColor(colorHash(name))} color='primary'>
|
||||||
|
<IconRefresh size='1rem' />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button type='submit' fullWidth variant='outline' my='sm'>
|
||||||
|
Create Tag
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function TagsModal({ open, onClose }) {
|
||||||
|
const tags = useTags();
|
||||||
|
|
||||||
|
const [createOpen, setCreateOpen] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CreateTagModal tags={tags} open={createOpen} onClose={() => setCreateOpen(false)} />
|
||||||
|
<Modal title={<Title>Tags</Title>} size='auto' opened={open} onClose={onClose}>
|
||||||
|
<MutedText size='sm'>Click on a tag to delete it.</MutedText>
|
||||||
|
<Stack>
|
||||||
|
{tags.isSuccess && tags.data.map((tag) => <TagCard key={tag.id} tags={tags} tag={tag} />)}
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Button mt='xl' variant='outline' onClick={() => setCreateOpen(true)} fullWidth compact>
|
||||||
|
Create Tag
|
||||||
|
</Button>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import { Accordion, ActionIcon, Box, Group, Pagination, SimpleGrid, Title, Tooltip } from '@mantine/core';
|
import { Accordion, ActionIcon, Box, Group, Pagination, SimpleGrid, Title, Tooltip } from '@mantine/core';
|
||||||
import { IconFileUpload, IconPhotoUp } from '@tabler/icons-react';
|
import { IconFileUpload, IconPhotoUp, IconTags } from '@tabler/icons-react';
|
||||||
import File from 'components/File';
|
import File from 'components/File';
|
||||||
import useFetch from 'hooks/useFetch';
|
import useFetch from 'hooks/useFetch';
|
||||||
import { usePaginatedFiles } from 'lib/queries/files';
|
import { usePaginatedFiles } from 'lib/queries/files';
|
||||||
|
@ -7,13 +7,15 @@ import Link from 'next/link';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import FilePagation from './FilePagation';
|
import FilePagation from './FilePagation';
|
||||||
import PendingFilesModal from './PendingFilesModal';
|
import PendingFilesModal from './PendingFilesModal';
|
||||||
|
import TagsModal from 'components/pages/Files/TagsModal';
|
||||||
|
|
||||||
export default function Files({ disableMediaPreview, exifEnabled, queryPage, compress }) {
|
export default function Files({ disableMediaPreview, exifEnabled, queryPage, compress }) {
|
||||||
const [favoritePage, setFavoritePage] = useState(1);
|
const [favoritePage, setFavoritePage] = useState(1);
|
||||||
const [favoriteNumPages, setFavoriteNumPages] = useState(0);
|
const [favoriteNumPages, setFavoriteNumPages] = useState(0);
|
||||||
const favoritePages = usePaginatedFiles(favoritePage, 'media', true);
|
const favoritePages = usePaginatedFiles(favoritePage, 'media', true);
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [pendingOpen, setPendingOpen] = useState(false);
|
||||||
|
const [tagsOpen, setTagsOpen] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
|
@ -24,7 +26,8 @@ export default function Files({ disableMediaPreview, exifEnabled, queryPage, com
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PendingFilesModal open={open} onClose={() => setOpen(false)} />
|
<PendingFilesModal open={pendingOpen} onClose={() => setPendingOpen(false)} />
|
||||||
|
<TagsModal open={tagsOpen} onClose={() => setTagsOpen(false)} />
|
||||||
|
|
||||||
<Group mb='md'>
|
<Group mb='md'>
|
||||||
<Title>Files</Title>
|
<Title>Files</Title>
|
||||||
|
@ -33,10 +36,15 @@ export default function Files({ disableMediaPreview, exifEnabled, queryPage, com
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
|
|
||||||
<Tooltip label='View pending uploads'>
|
<Tooltip label='View pending uploads'>
|
||||||
<ActionIcon onClick={() => setOpen(true)} variant='filled' color='primary'>
|
<ActionIcon onClick={() => setPendingOpen(true)} variant='filled' color='primary'>
|
||||||
<IconPhotoUp size='1rem' />
|
<IconPhotoUp size='1rem' />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
<Tooltip label='View tags'>
|
||||||
|
<ActionIcon onClick={() => setTagsOpen(true)} variant='filled' color='primary'>
|
||||||
|
<IconTags size='1rem' />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
</Group>
|
</Group>
|
||||||
{favoritePages.isSuccess && favoritePages.data.length ? (
|
{favoritePages.isSuccess && favoritePages.data.length ? (
|
||||||
<Accordion
|
<Accordion
|
||||||
|
|
168
src/lib/queries/tags.ts
Normal file
168
src/lib/queries/tags.ts
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||||
|
import queryClient from 'lib/queries/client';
|
||||||
|
|
||||||
|
export type UserTagsResponse = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
color: string;
|
||||||
|
files: {
|
||||||
|
id: string;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TagsRequest = {
|
||||||
|
id?: string;
|
||||||
|
name?: string;
|
||||||
|
color?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useTags = () => {
|
||||||
|
return useQuery<UserTagsResponse[]>(['tags'], async () => {
|
||||||
|
return fetch('/api/user/tags')
|
||||||
|
.then((res) => res.json() as Promise<UserTagsResponse[]>)
|
||||||
|
.then((data) => data);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useFileTags = (id: string) => {
|
||||||
|
return useQuery<UserTagsResponse[]>(['tags', id], async () => {
|
||||||
|
return fetch(`/api/user/file/${id}/tags`)
|
||||||
|
.then((res) => res.json() as Promise<UserTagsResponse[]>)
|
||||||
|
.then((data) => data);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useUpdateFileTags = (id: string) => {
|
||||||
|
return useMutation(
|
||||||
|
(tags: TagsRequest[]) =>
|
||||||
|
fetch(`/api/user/file/${id}/tags`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ tags }),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
}).then((res) => res.json()),
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.refetchQueries(['tags', id]);
|
||||||
|
queryClient.refetchQueries(['files']);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useDeleteFileTags = (id: string) => {
|
||||||
|
return useMutation(
|
||||||
|
(tags: string[]) =>
|
||||||
|
fetch(`/api/user/file/${id}/tags`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
body: JSON.stringify({ tags }),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
}).then((res) => res.json()),
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.refetchQueries(['tags', id]);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useDeleteTags = () => {
|
||||||
|
return useMutation(
|
||||||
|
(tags: string[]) =>
|
||||||
|
fetch('/api/user/tags', {
|
||||||
|
method: 'DELETE',
|
||||||
|
body: JSON.stringify({ tags }),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
}).then((res) => res.json()),
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.refetchQueries(['tags']);
|
||||||
|
queryClient.refetchQueries(['files']);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// export const usePaginatedFiles = (page?: number, filter = 'media', favorite = null) => {
|
||||||
|
// const queryBuilder = new URLSearchParams({
|
||||||
|
// page: Number(page || '1').toString(),
|
||||||
|
// filter,
|
||||||
|
// ...(favorite !== null && { favorite: favorite.toString() }),
|
||||||
|
// });
|
||||||
|
// const queryString = queryBuilder.toString();
|
||||||
|
//
|
||||||
|
// return useQuery<UserFilesResponse[]>(['files', queryString], async () => {
|
||||||
|
// return fetch('/api/user/paged?' + queryString)
|
||||||
|
// .then((res) => res.json() as Promise<UserFilesResponse[]>)
|
||||||
|
// .then((data) =>
|
||||||
|
// data.map((x) => ({
|
||||||
|
// ...x,
|
||||||
|
// createdAt: new Date(x.createdAt),
|
||||||
|
// expiresAt: x.expiresAt ? new Date(x.expiresAt) : null,
|
||||||
|
// }))
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// export const useRecent = (filter?: string) => {
|
||||||
|
// return useQuery<UserFilesResponse[]>(['recent', filter], async () => {
|
||||||
|
// return fetch(`/api/user/recent?filter=${encodeURIComponent(filter)}`)
|
||||||
|
// .then((res) => res.json())
|
||||||
|
// .then((data) =>
|
||||||
|
// data.map((x) => ({
|
||||||
|
// ...x,
|
||||||
|
// createdAt: new Date(x.createdAt),
|
||||||
|
// expiresAt: x.expiresAt ? new Date(x.expiresAt) : null,
|
||||||
|
// }))
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// export function useFileDelete() {
|
||||||
|
// // '/api/user/files', 'DELETE', { id: image.id }
|
||||||
|
// return useMutation(
|
||||||
|
// async (id: string) => {
|
||||||
|
// return fetch('/api/user/files', {
|
||||||
|
// method: 'DELETE',
|
||||||
|
// body: JSON.stringify({ id }),
|
||||||
|
// headers: {
|
||||||
|
// 'content-type': 'application/json',
|
||||||
|
// },
|
||||||
|
// }).then((res) => res.json());
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// onSuccess: () => {
|
||||||
|
// queryClient.refetchQueries(['files']);
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// export function useFileFavorite() {
|
||||||
|
// // /api/user/files', 'PATCH', { id: image.id, favorite: !image.favorite }
|
||||||
|
// return useMutation(
|
||||||
|
// async (data: { id: string; favorite: boolean }) => {
|
||||||
|
// return fetch('/api/user/files', {
|
||||||
|
// method: 'PATCH',
|
||||||
|
// body: JSON.stringify(data),
|
||||||
|
// headers: {
|
||||||
|
// 'content-type': 'application/json',
|
||||||
|
// },
|
||||||
|
// }).then((res) => res.json());
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// onSuccess: () => {
|
||||||
|
// queryClient.refetchQueries(['files']);
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// export function invalidateFiles() {
|
||||||
|
// return queryClient.invalidateQueries(['files', 'recent', 'stats']);
|
||||||
|
// }
|
|
@ -72,7 +72,7 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
|
||||||
if (!tags) return res.badRequest('no tags');
|
if (!tags) return res.badRequest('no tags');
|
||||||
if (!tags.length) return res.badRequest('no tags');
|
if (!tags.length) return res.badRequest('no tags');
|
||||||
|
|
||||||
// if the tag has an id, it means it already exists so we just connect it
|
// if the tag has an id, it means it already exists, so we just connect it
|
||||||
// if it doesn't have an id, we create it and then connect it
|
// if it doesn't have an id, we create it and then connect it
|
||||||
const nFile = await prisma.file.update({
|
const nFile = await prisma.file.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue