feat: overhaul image upload
This commit is contained in:
parent
516e93cee2
commit
894b5c5c6c
6 changed files with 135 additions and 81 deletions
|
@ -37,8 +37,11 @@ function Placeholder({ text, Icon, ...props }) {
|
|||
}
|
||||
|
||||
export default function Type({ file, popup = false, disableMediaPreview, ...props }) {
|
||||
const type = (file.type || file.mimetype).split('/')[0];
|
||||
const name = file.name || file.file;
|
||||
const type =
|
||||
(file.type ?? file.mimetype) === ''
|
||||
? file.name.split('.').pop()
|
||||
: (file.type ?? file.mimetype).split('/')[0];
|
||||
const name = file.name ?? file.file;
|
||||
|
||||
const media = /^(video|audio|image|text)/.test(type);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Group, Text, useMantineTheme } from '@mantine/core';
|
||||
import { Box, Group, SimpleGrid, Text, useMantineTheme } from '@mantine/core';
|
||||
import { Dropzone as MantineDropzone } from '@mantine/dropzone';
|
||||
import { ImageIcon } from 'components/icons';
|
||||
|
||||
|
@ -6,16 +6,23 @@ export default function Dropzone({ loading, onDrop, children }) {
|
|||
const theme = useMantineTheme();
|
||||
|
||||
return (
|
||||
<MantineDropzone onDrop={onDrop}>
|
||||
<Group position='center' spacing='xl' style={{ minHeight: 440 }}>
|
||||
<ImageIcon size={80} />
|
||||
<SimpleGrid
|
||||
cols={2}
|
||||
breakpoints={[
|
||||
{ maxWidth: 'md', cols: 1 },
|
||||
{ maxWidth: 'xs', cols: 1 },
|
||||
]}
|
||||
>
|
||||
<MantineDropzone onDrop={onDrop} styles={{ inner: { pointerEvents: 'none' } }}>
|
||||
<Group position='center' spacing='xl' style={{ minHeight: 440 }}>
|
||||
<ImageIcon size={80} />
|
||||
|
||||
<Text size='xl' inline>
|
||||
Drag files here or click to select files
|
||||
</Text>
|
||||
</Group>
|
||||
|
||||
<div style={{ pointerEvents: 'all' }}>{children}</div>
|
||||
</MantineDropzone>
|
||||
<Text size='xl' inline>
|
||||
Drag files here or click to select files
|
||||
</Text>
|
||||
</Group>
|
||||
</MantineDropzone>
|
||||
<Box>{children}</Box>
|
||||
</SimpleGrid>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Badge, Group, HoverCard, Table, useMantineTheme } from '@mantine/core';
|
||||
import { ActionIcon, Badge, Box, Card, Group, HoverCard, Table, useMantineTheme } from '@mantine/core';
|
||||
import Type from 'components/Type';
|
||||
import { X } from 'react-feather';
|
||||
|
||||
export function FilePreview({ file }: { file: File }) {
|
||||
return (
|
||||
|
@ -16,15 +17,36 @@ export function FilePreview({ file }: { file: File }) {
|
|||
);
|
||||
}
|
||||
|
||||
export default function FileDropzone({ file }: { file: File }) {
|
||||
export default function FileDropzone({ file, onRemove }: { file: File; onRemove: () => void }) {
|
||||
const theme = useMantineTheme();
|
||||
|
||||
return (
|
||||
<HoverCard shadow='md'>
|
||||
<HoverCard.Target>
|
||||
<Badge size='lg'>{file.name}</Badge>
|
||||
{/* <Badge size='lg'>{file.name}</Badge> */}
|
||||
<Card shadow='sm' radius='sm' p='sm'>
|
||||
<Group position='center' spacing='xl'>
|
||||
{file.name}
|
||||
</Group>
|
||||
</Card>
|
||||
</HoverCard.Target>
|
||||
<HoverCard.Dropdown>
|
||||
{/* x button that will remove file */}
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: 0,
|
||||
zIndex: 1,
|
||||
color: theme.colorScheme === 'dark' ? 'white' : 'white',
|
||||
}}
|
||||
m='xs'
|
||||
>
|
||||
<ActionIcon onClick={onRemove} size='sm' color='red' variant='filled'>
|
||||
<X />
|
||||
</ActionIcon>
|
||||
</Box>
|
||||
|
||||
<Group grow>
|
||||
<FilePreview file={file} />
|
||||
|
||||
|
|
|
@ -60,8 +60,6 @@ export default function Flameshot({ user, open, setOpen }) {
|
|||
curl.push(`"${key}: ${value}"`);
|
||||
}
|
||||
|
||||
console.log(curl);
|
||||
|
||||
let shell;
|
||||
if (values.type === 'upload-file') {
|
||||
shell = `#!/bin/bash${values.wlCompositorNotSupported ? '\nexport XDG_CURRENT_DESKTOP=sway\n' : ''}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {
|
||||
Box,
|
||||
Button,
|
||||
Collapse,
|
||||
Group,
|
||||
|
@ -6,6 +7,8 @@ import {
|
|||
PasswordInput,
|
||||
Progress,
|
||||
Select,
|
||||
Stack,
|
||||
Text,
|
||||
Title,
|
||||
Tooltip,
|
||||
} from '@mantine/core';
|
||||
|
@ -15,6 +18,7 @@ import { showNotification, updateNotification } from '@mantine/notifications';
|
|||
import Dropzone from 'components/dropzone/Dropzone';
|
||||
import FileDropzone from 'components/dropzone/DropzoneFile';
|
||||
import { ClockIcon, CrossIcon, UploadIcon } from 'components/icons';
|
||||
import MutedText from 'components/MutedText';
|
||||
import { invalidateFiles } from 'lib/queries/files';
|
||||
import { userSelector } from 'lib/recoil/user';
|
||||
import { expireReadToDate, randomChars } from 'lib/utils/client';
|
||||
|
@ -281,28 +285,46 @@ export default function File({ chunks: chunks_config }) {
|
|||
<Title mb='md'>Upload Files</Title>
|
||||
|
||||
<Dropzone loading={loading} onDrop={(f) => setFiles([...files, ...f])}>
|
||||
<Group position='center' spacing='md'>
|
||||
{files.map((file) => (
|
||||
<FileDropzone key={randomId()} file={file} />
|
||||
))}
|
||||
</Group>
|
||||
<Stack justify='space-between' h='100%'>
|
||||
{files.length ? (
|
||||
<Group spacing='md'>
|
||||
{files.map((file) => (
|
||||
<FileDropzone
|
||||
key={randomId()}
|
||||
file={file}
|
||||
onRemove={() => setFiles(files.filter((f) => f !== file))}
|
||||
/>
|
||||
))}
|
||||
</Group>
|
||||
) : (
|
||||
<Group position='center'>
|
||||
<MutedText>Files will appear here once you drop/select them</MutedText>
|
||||
</Group>
|
||||
)}
|
||||
|
||||
<Stack>
|
||||
<Group position='right' mt='md'>
|
||||
<Button onClick={() => setOpened(true)} variant='outline'>
|
||||
Options
|
||||
</Button>
|
||||
<Button onClick={() => setFiles([])} color='red' variant='outline'>
|
||||
Clear Files
|
||||
</Button>
|
||||
<Button
|
||||
leftIcon={<UploadIcon />}
|
||||
onClick={handleUpload}
|
||||
disabled={files.length === 0 ? true : false}
|
||||
>
|
||||
Upload
|
||||
</Button>
|
||||
</Group>
|
||||
|
||||
<Collapse in={progress !== 0}>
|
||||
{progress !== 0 && <Progress mt='md' value={progress} animate />}
|
||||
</Collapse>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Dropzone>
|
||||
|
||||
<Collapse in={progress !== 0}>
|
||||
{progress !== 0 && <Progress mt='md' value={progress} animate />}
|
||||
</Collapse>
|
||||
|
||||
<Group position='right' mt='md'>
|
||||
<Button onClick={() => setOpened(true)} variant='outline'>
|
||||
Options
|
||||
</Button>
|
||||
<Button onClick={() => setFiles([])} color='red' variant='outline'>
|
||||
Clear Files
|
||||
</Button>
|
||||
<Button leftIcon={<UploadIcon />} onClick={handleUpload} disabled={files.length === 0 ? true : false}>
|
||||
Upload
|
||||
</Button>
|
||||
</Group>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
Title,
|
||||
} from '@mantine/core';
|
||||
import { ClockIcon, ImageIcon, KeyIcon, TypeIcon, UserIcon } from 'components/icons';
|
||||
import React, { Dispatch, SetStateAction, useState } from 'react';
|
||||
import React, { Dispatch, SetStateAction, useReducer, useState } from 'react';
|
||||
|
||||
export default function useUploadOptions(): [
|
||||
{
|
||||
|
@ -25,24 +25,38 @@ export default function useUploadOptions(): [
|
|||
Dispatch<SetStateAction<boolean>>,
|
||||
React.FC
|
||||
] {
|
||||
const [expires, setExpires] = useState('never');
|
||||
const [password, setPassword] = useState('');
|
||||
const [maxViews, setMaxViews] = useState(0);
|
||||
const [compression, setCompression] = useState<string>('none');
|
||||
const [zeroWidth, setZeroWidth] = useState(false);
|
||||
const [embedded, setEmbedded] = useState(false);
|
||||
const [format, setFormat] = useState('default');
|
||||
// const [expires, setExpires] = useState('never');
|
||||
// const [password, setPassword] = useState('');
|
||||
// const [maxViews, setMaxViews] = useState(0);
|
||||
// const [compression, setCompression] = useState<string>('none');
|
||||
// const [zeroWidth, setZeroWidth] = useState(false);
|
||||
// const [embedded, setEmbedded] = useState(false);
|
||||
// const [format, setFormat] = useState('default');
|
||||
|
||||
// migrate this to useReducer
|
||||
|
||||
const [state, setState] = useReducer((state, newState) => ({ ...state, ...newState }), {
|
||||
expires: 'never',
|
||||
password: '',
|
||||
maxViews: 0,
|
||||
compression: 'none',
|
||||
zeroWidth: false,
|
||||
embedded: false,
|
||||
format: 'default',
|
||||
});
|
||||
|
||||
const [opened, setOpened] = useState(false);
|
||||
|
||||
const reset = () => {
|
||||
setExpires('never');
|
||||
setPassword('');
|
||||
setMaxViews(0);
|
||||
setCompression('none');
|
||||
setZeroWidth(false);
|
||||
setEmbedded(false);
|
||||
setFormat('default');
|
||||
setState({
|
||||
expires: 'never',
|
||||
password: '',
|
||||
maxViews: 0,
|
||||
compression: 'none',
|
||||
zeroWidth: false,
|
||||
embedded: false,
|
||||
format: 'default',
|
||||
});
|
||||
};
|
||||
|
||||
const OptionsModal: React.FC = () => (
|
||||
|
@ -51,8 +65,8 @@ export default function useUploadOptions(): [
|
|||
<NumberInput
|
||||
label='Max Views'
|
||||
description='The maximum number of times this file can be viewed. Leave blank for unlimited views.'
|
||||
value={maxViews}
|
||||
onChange={setMaxViews}
|
||||
value={state.maxViews}
|
||||
onChange={(e) => setState({ maxViews: e })}
|
||||
min={0}
|
||||
icon={<UserIcon />}
|
||||
/>
|
||||
|
@ -60,8 +74,8 @@ export default function useUploadOptions(): [
|
|||
<Select
|
||||
label='Expires'
|
||||
description='The date and time this file will expire. Leave blank for never.'
|
||||
value={expires}
|
||||
onChange={(e) => setExpires(e)}
|
||||
value={state.expires}
|
||||
onChange={(e) => setState({ expires: e })}
|
||||
icon={<ClockIcon size={14} />}
|
||||
data={[
|
||||
{ value: 'never', label: 'Never' },
|
||||
|
@ -98,8 +112,8 @@ export default function useUploadOptions(): [
|
|||
<Select
|
||||
label='Compression'
|
||||
description='The compression level to use when uploading this file. Leave blank for default.'
|
||||
value={compression}
|
||||
onChange={(e) => setCompression(e)}
|
||||
value={state.compression}
|
||||
onChange={(e) => setState({ compression: e })}
|
||||
icon={<ImageIcon />}
|
||||
data={[
|
||||
{ value: 'none', label: 'None' },
|
||||
|
@ -112,8 +126,8 @@ export default function useUploadOptions(): [
|
|||
<Select
|
||||
label='Format'
|
||||
description="The file name format to use when uploading this file. Leave blank for the server's default."
|
||||
value={format}
|
||||
onChange={(e) => setFormat(e)}
|
||||
value={state.format}
|
||||
onChange={(e) => setState({ format: e })}
|
||||
icon={<TypeIcon />}
|
||||
data={[
|
||||
{ value: 'default', label: 'Default' },
|
||||
|
@ -127,8 +141,8 @@ export default function useUploadOptions(): [
|
|||
<PasswordInput
|
||||
label='Password'
|
||||
description='The password required to view this file. Leave blank for no password.'
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.currentTarget.value)}
|
||||
value={state.password}
|
||||
onChange={(e) => setState({ password: e })}
|
||||
icon={<KeyIcon />}
|
||||
/>
|
||||
|
||||
|
@ -136,15 +150,15 @@ export default function useUploadOptions(): [
|
|||
<Switch
|
||||
label='Zero Width'
|
||||
description='Whether or not to use zero width characters for the file name.'
|
||||
checked={zeroWidth}
|
||||
onChange={(e) => setZeroWidth(e.currentTarget.checked)}
|
||||
checked={state.zeroWidth}
|
||||
onChange={(e) => setState({ zeroWidth: e.currentTarget.checked })}
|
||||
/>
|
||||
|
||||
<Switch
|
||||
label='Embedded'
|
||||
description='Whether or not to embed with OG tags for this file.'
|
||||
checked={embedded}
|
||||
onChange={(e) => setEmbedded(e.currentTarget.checked)}
|
||||
checked={state.embedded}
|
||||
onChange={(e) => setState({ embedded: e.currentTarget.checked })}
|
||||
/>
|
||||
</Group>
|
||||
|
||||
|
@ -158,17 +172,5 @@ export default function useUploadOptions(): [
|
|||
</Modal>
|
||||
);
|
||||
|
||||
return [
|
||||
{
|
||||
expires,
|
||||
password,
|
||||
maxViews,
|
||||
compression,
|
||||
zeroWidth,
|
||||
embedded,
|
||||
format,
|
||||
},
|
||||
setOpened,
|
||||
OptionsModal,
|
||||
];
|
||||
return [state, setOpened, OptionsModal];
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue