mirror of
https://github.com/diced/zipline.git
synced 2026-01-14 22:03:48 -08:00
feat: implement settings.warnDeletion
This commit is contained in:
@@ -34,6 +34,7 @@ import useSWR from 'swr';
|
||||
import { Response } from '@/lib/api/response';
|
||||
import { Folder } from '@/lib/db/models/folder';
|
||||
import { bytes } from '@/lib/bytes';
|
||||
import { useSettingsStore } from '@/lib/store/settings';
|
||||
|
||||
function ActionButton({
|
||||
Icon,
|
||||
@@ -67,6 +68,7 @@ export default function FileModal({
|
||||
reduce?: boolean;
|
||||
}) {
|
||||
const clipboard = useClipboard();
|
||||
const warnDeletion = useSettingsStore((state) => state.settings.warnDeletion);
|
||||
|
||||
const { data: folders } = useSWR<Extract<Response['/api/user/folders'], Folder[]>>(
|
||||
'/api/user/folders?noincl=true'
|
||||
@@ -145,7 +147,7 @@ export default function FileModal({
|
||||
<>
|
||||
<ActionButton
|
||||
Icon={IconTrashFilled}
|
||||
onClick={() => deleteFile(file, setOpen)}
|
||||
onClick={() => deleteFile(warnDeletion, file, setOpen)}
|
||||
tooltip='Delete file'
|
||||
color='red'
|
||||
/>
|
||||
|
||||
@@ -2,8 +2,11 @@ import { Response } from '@/lib/api/response';
|
||||
import type { File } from '@/lib/db/models/file';
|
||||
import { Folder } from '@/lib/db/models/folder';
|
||||
import { fetchApi } from '@/lib/fetchApi';
|
||||
import { useSettingsStore } from '@/lib/store/settings';
|
||||
import { conditionalWarning } from '@/lib/warningModal';
|
||||
import { Anchor } from '@mantine/core';
|
||||
import { useClipboard } from '@mantine/hooks';
|
||||
import { modals } from '@mantine/modals';
|
||||
import { notifications, notifications as notifs } from '@mantine/notifications';
|
||||
import {
|
||||
IconCopy,
|
||||
@@ -45,7 +48,15 @@ export function copyFile(file: File, clipboard: ReturnType<typeof useClipboard>)
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteFile(file: File, setOpen: (open: boolean) => void) {
|
||||
export async function deleteFile(warnDeletion: boolean, file: File, setOpen: (open: boolean) => void) {
|
||||
conditionalWarning(warnDeletion, {
|
||||
confirmLabel: `Delete ${file.name}`,
|
||||
message: `Are you sure you want to delete ${file.name}? This action cannot be undone.`,
|
||||
onConfirm: () => handleDeleteFile(file, setOpen),
|
||||
});
|
||||
}
|
||||
|
||||
export async function handleDeleteFile(file: File, setOpen: (open: boolean) => void) {
|
||||
const { error } = await fetchApi(`/api/user/files/${file.id}`, 'DELETE');
|
||||
|
||||
if (error) {
|
||||
|
||||
@@ -81,7 +81,10 @@ function SearchFilter({
|
||||
export default function FileTable({ id }: { id?: string }) {
|
||||
const router = useRouter();
|
||||
const clipboard = useClipboard();
|
||||
const searchThreshold = useSettingsStore((state) => state.settings.searchThreshold);
|
||||
const [searchThreshold, warnDeletion] = useSettingsStore((state) => [
|
||||
state.settings.searchThreshold,
|
||||
state.settings.warnDeletion,
|
||||
]);
|
||||
|
||||
const [page, setPage] = useState<number>(router.query.page ? parseInt(router.query.page as string) : 1);
|
||||
const [perpage, setPerpage] = useState<number>(20);
|
||||
@@ -321,7 +324,7 @@ export default function FileTable({ id }: { id?: string }) {
|
||||
color='red'
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
deleteFile(file, () => {});
|
||||
deleteFile(warnDeletion, file, () => {});
|
||||
}}
|
||||
>
|
||||
<IconTrashFilled size='1rem' />
|
||||
|
||||
@@ -4,10 +4,13 @@ import { ActionIcon, Card, Group, Menu, Stack, Text } from '@mantine/core';
|
||||
import { IconCopy, IconDots, IconTrashFilled } from '@tabler/icons-react';
|
||||
import { copyInviteUrl, deleteInvite } from './actions';
|
||||
import { useClipboard } from '@mantine/hooks';
|
||||
import { useSettingsStore } from '@/lib/store/settings';
|
||||
|
||||
export default function InviteCard({ invite }: { invite: Invite }) {
|
||||
const clipboard = useClipboard();
|
||||
|
||||
const warnDeletion = useSettingsStore((state) => state.settings.warnDeletion);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card withBorder shadow='sm' radius='sm'>
|
||||
@@ -31,7 +34,7 @@ export default function InviteCard({ invite }: { invite: Invite }) {
|
||||
<Menu.Item
|
||||
icon={<IconTrashFilled size='1rem' />}
|
||||
color='red'
|
||||
onClick={() => deleteInvite(invite)}
|
||||
onClick={() => deleteInvite(warnDeletion, invite)}
|
||||
>
|
||||
Delete
|
||||
</Menu.Item>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Response } from '@/lib/api/response';
|
||||
import { Invite } from '@/lib/db/models/invite';
|
||||
import { fetchApi } from '@/lib/fetchApi';
|
||||
import { conditionalWarning } from '@/lib/warningModal';
|
||||
import { Anchor, Title } from '@mantine/core';
|
||||
import { useClipboard } from '@mantine/hooks';
|
||||
import { modals } from '@mantine/modals';
|
||||
@@ -9,17 +10,11 @@ import { IconCheck, IconCopy, IconTagOff } from '@tabler/icons-react';
|
||||
import Link from 'next/link';
|
||||
import { mutate } from 'swr';
|
||||
|
||||
export async function deleteInvite(invite: Invite) {
|
||||
modals.openConfirmModal({
|
||||
title: <Title>Delete invite "{invite.code}"</Title>,
|
||||
children: `Are you sure you want to delete invite ${invite.code}? This action cannot be undone.`,
|
||||
labels: {
|
||||
cancel: 'Cancel',
|
||||
confirm: 'Delete',
|
||||
},
|
||||
confirmProps: { color: 'red' },
|
||||
export async function deleteInvite(warnDeletion: boolean, invite: Invite) {
|
||||
conditionalWarning(warnDeletion, {
|
||||
message: `Are you sure you want to delete invite ${invite.code}? This action cannot be undone.`,
|
||||
onConfirm: () => handleDeleteInvite(invite),
|
||||
onCancel: modals.closeAll,
|
||||
confirmLabel: `Delete ${invite.code}`,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { useConfig } from '@/components/ConfigProvider';
|
||||
import RelativeDate from '@/components/RelativeDate';
|
||||
import { Response } from '@/lib/api/response';
|
||||
import { Invite } from '@/lib/db/models/invite';
|
||||
@@ -9,10 +8,11 @@ import { DataTable, DataTableSortStatus } from 'mantine-datatable';
|
||||
import { useEffect, useState } from 'react';
|
||||
import useSWR from 'swr';
|
||||
import { copyInviteUrl, deleteInvite } from '../actions';
|
||||
import { useSettingsStore } from '@/lib/store/settings';
|
||||
|
||||
export default function InviteTableView() {
|
||||
const config = useConfig();
|
||||
const clipboard = useClipboard();
|
||||
const warnDeletion = useSettingsStore((state) => state.settings.warnDeletion);
|
||||
|
||||
const { data, isLoading } = useSWR<Extract<Response['/api/auth/invites'], Invite[]>>('/api/auth/invites');
|
||||
|
||||
@@ -105,7 +105,7 @@ export default function InviteTableView() {
|
||||
color='red'
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
deleteInvite(invite);
|
||||
deleteInvite(warnDeletion, invite);
|
||||
}}
|
||||
>
|
||||
<IconTrashFilled size='1rem' />
|
||||
|
||||
@@ -65,7 +65,7 @@ export default function SettingsDashboard() {
|
||||
/>
|
||||
<Switch
|
||||
label='Warn on deletion'
|
||||
description='Show a warning when deleting files. This is useful to prevent accidental deletion of files.'
|
||||
description='Show a warning when deleting stuff. When this is disabled, files, urls, etc will be deleted with no prior warning! Folders, users, and bulk-transactions are exempt from this rule and will always warn you before deleting anything.'
|
||||
checked={settings.warnDeletion}
|
||||
onChange={(event) => update('warnDeletion', event.currentTarget.checked)}
|
||||
/>
|
||||
@@ -73,7 +73,7 @@ export default function SettingsDashboard() {
|
||||
|
||||
<NumberInput
|
||||
label='Search Threshold'
|
||||
description='When performing a similarity check on file searches, this is the minimum percentage of similarity required to show the file. The lower the number, the more results will be shown though they will be less relevant to the actual search query. A recomended value is between 0.1 and 0.4 as this will yield moderate-relevancy results that should match your queries.'
|
||||
description='When performing a similarity check on file/url searches, this is the minimum percentage of similarity required to show the file/url. The lower the number, the more results will be shown though they will be less relevant to the actual search query. A recomended value is between 0.1 and 0.4 as this will yield moderate-relevancy results that should match your queries.'
|
||||
min={0}
|
||||
max={100}
|
||||
value={settings.searchThreshold}
|
||||
|
||||
@@ -3,15 +3,17 @@ import RelativeDate from '@/components/RelativeDate';
|
||||
import { Url } from '@/lib/db/models/url';
|
||||
import { formatRootUrl } from '@/lib/url';
|
||||
import { ActionIcon, Anchor, Card, Group, Menu, Stack, Text } from '@mantine/core';
|
||||
import { IconCopy, IconDots, IconTrashFilled } from '@tabler/icons-react';
|
||||
import { useState } from 'react';
|
||||
import { copyUrl, deleteUrl } from './actions';
|
||||
import { useClipboard } from '@mantine/hooks';
|
||||
import { IconCopy, IconDots, IconTrashFilled } from '@tabler/icons-react';
|
||||
import { copyUrl, deleteUrl } from './actions';
|
||||
import { useSettingsStore } from '@/lib/store/settings';
|
||||
|
||||
export default function UserCard({ url }: { url: Url }) {
|
||||
const config = useConfig();
|
||||
const clipboard = useClipboard();
|
||||
|
||||
const warnDeletion = useSettingsStore((state) => state.settings.warnDeletion);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card withBorder shadow='sm' radius='sm'>
|
||||
@@ -40,7 +42,11 @@ export default function UserCard({ url }: { url: Url }) {
|
||||
<Menu.Item icon={<IconCopy size='1rem' />} onClick={() => copyUrl(url, config, clipboard)}>
|
||||
Copy
|
||||
</Menu.Item>
|
||||
<Menu.Item icon={<IconTrashFilled size='1rem' />} color='red' onClick={() => deleteUrl(url)}>
|
||||
<Menu.Item
|
||||
icon={<IconTrashFilled size='1rem' />}
|
||||
color='red'
|
||||
onClick={() => deleteUrl(warnDeletion, url)}
|
||||
>
|
||||
Delete
|
||||
</Menu.Item>
|
||||
</Menu.Dropdown>
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { SafeConfig } from '@/lib/config/safe';
|
||||
import { Url } from '@/lib/db/models/url';
|
||||
import { fetchApi } from '@/lib/fetchApi';
|
||||
import { formatRootUrl } from '@/lib/url';
|
||||
import { conditionalWarning } from '@/lib/warningModal';
|
||||
import { Anchor, Title } from '@mantine/core';
|
||||
import { useClipboard } from '@mantine/hooks';
|
||||
import { modals } from '@mantine/modals';
|
||||
@@ -11,18 +12,11 @@ import { IconCheck, IconCopy, IconLinkOff } from '@tabler/icons-react';
|
||||
import Link from 'next/link';
|
||||
import { mutate } from 'swr';
|
||||
|
||||
export async function deleteUrl(url: Url) {
|
||||
modals.openConfirmModal({
|
||||
centered: true,
|
||||
title: <Title>Delete {url.code ?? url.vanity}?</Title>,
|
||||
children: `Are you sure you want to delete ${url.code ?? url.vanity}? This action cannot be undone.`,
|
||||
labels: {
|
||||
cancel: 'Cancel',
|
||||
confirm: 'Delete',
|
||||
},
|
||||
confirmProps: { color: 'red' },
|
||||
export async function deleteUrl(warnDeletion: boolean, url: Url) {
|
||||
conditionalWarning(warnDeletion, {
|
||||
message: `Are you sure you want to delete ${url.code ?? url.vanity}? This action cannot be undone.`,
|
||||
onConfirm: () => handleDeleteUrl(url),
|
||||
onCancel: modals.closeAll,
|
||||
confirmLabel: `Delete '${url.code ?? url.vanity}'`,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,10 @@ export default function UrlTableView() {
|
||||
destination: '',
|
||||
}
|
||||
);
|
||||
const searchThreshold = useSettingsStore((s) => s.settings.searchThreshold);
|
||||
const [warnDeletion, searchThreshold] = useSettingsStore((s) => [
|
||||
s.settings.warnDeletion,
|
||||
s.settings.searchThreshold,
|
||||
]);
|
||||
|
||||
const { data, isLoading } = useSWR<Extract<Response['/api/user/urls'], Url[]>>(
|
||||
{
|
||||
@@ -259,7 +262,7 @@ export default function UrlTableView() {
|
||||
color='red'
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
deleteUrl(url);
|
||||
deleteUrl(warnDeletion, url);
|
||||
}}
|
||||
>
|
||||
<IconTrashFilled size='1rem' />
|
||||
|
||||
33
src/lib/warningModal.tsx
Normal file
33
src/lib/warningModal.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Title } from '@mantine/core';
|
||||
import { modals } from '@mantine/modals';
|
||||
|
||||
type WarningModalOptions = {
|
||||
message: string | React.ReactNode;
|
||||
confirmLabel: string;
|
||||
onConfirm: () => void;
|
||||
};
|
||||
|
||||
export function openWarningModal(options: WarningModalOptions) {
|
||||
modals.openConfirmModal({
|
||||
title: <Title order={3}>Are you sure?</Title>,
|
||||
labels: {
|
||||
cancel: 'Cancel',
|
||||
confirm: options.confirmLabel,
|
||||
},
|
||||
children: options.message,
|
||||
confirmProps: {
|
||||
color: 'red',
|
||||
},
|
||||
onCancel: () => modals.closeAll(),
|
||||
onConfirm: options.onConfirm,
|
||||
zIndex: 10320948239487,
|
||||
});
|
||||
}
|
||||
|
||||
export function conditionalWarning(on: boolean, options: WarningModalOptions) {
|
||||
if (on) {
|
||||
openWarningModal(options);
|
||||
} else {
|
||||
options.onConfirm();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user