Add file pasting and drag-n-drop.

This commit is contained in:
Paul 2021-06-22 12:08:39 +01:00
parent 454ee7fd6e
commit 56dda66c1c
2 changed files with 74 additions and 3 deletions

View file

@ -255,6 +255,13 @@ function MessageBox({ channel, draft, dispatcher }: Props) {
remove={async () => setUploadState({ type: "none" })} remove={async () => setUploadState({ type: "none" })}
onChange={files => setUploadState({ type: "attached", files })} onChange={files => setUploadState({ type: "attached", files })}
cancel={() => uploadState.type === 'uploading' && uploadState.cancel.cancel("cancel")} cancel={() => uploadState.type === 'uploading' && uploadState.cancel.cancel("cancel")}
append={files => {
if (uploadState.type === 'none') {
setUploadState({ type: 'attached', files });
} else if (uploadState.type === 'attached') {
setUploadState({ type: 'attached', files: [ ...uploadState.files, ...files ] });
}
}}
/> />
</Action> </Action>
<TextAreaAutoSize <TextAreaAutoSize

View file

@ -4,7 +4,7 @@ import classNames from "classnames";
import { AppContext } from "./RevoltClient"; import { AppContext } from "./RevoltClient";
import styles from './FileUploads.module.scss'; import styles from './FileUploads.module.scss';
import Axios, { AxiosRequestConfig } from "axios"; import Axios, { AxiosRequestConfig } from "axios";
import { useContext, useState } from "preact/hooks"; import { useContext, useEffect, useState } from "preact/hooks";
import Preloader from "../../components/ui/Preloader"; import Preloader from "../../components/ui/Preloader";
import { determineFileSize } from "../../lib/fileSize"; import { determineFileSize } from "../../lib/fileSize";
import IconButton from '../../components/ui/IconButton'; import IconButton from '../../components/ui/IconButton';
@ -17,8 +17,8 @@ type Props = {
fileType: 'backgrounds' | 'icons' | 'avatars' | 'attachments' | 'banners' fileType: 'backgrounds' | 'icons' | 'avatars' | 'attachments' | 'banners'
} & ( } & (
{ behaviour: 'ask', onChange: (file: File) => void } | { behaviour: 'ask', onChange: (file: File) => void } |
{ behaviour: 'multi', onChange: (files: File[]) => void } | { behaviour: 'upload', onUpload: (id: string) => Promise<void> } |
{ behaviour: 'upload', onUpload: (id: string) => Promise<void> } { behaviour: 'multi', onChange: (files: File[]) => void, append?: (files: File[]) => void }
) & ( ) & (
{ style: 'icon' | 'banner', defaultPreview?: string, previewURL?: string, width?: number, height?: number } | { style: 'icon' | 'banner', defaultPreview?: string, previewURL?: string, width?: number, height?: number } |
{ style: 'attachment', attached: boolean, uploading: boolean, cancel: () => void, size?: number } { style: 'attachment', attached: boolean, uploading: boolean, cancel: () => void, size?: number }
@ -107,6 +107,70 @@ export function FileUploader(props: Props) {
} }
} }
if (props.behaviour === 'multi' && props.append) {
useEffect(() => {
// File pasting.
function paste(e: ClipboardEvent) {
const items = e.clipboardData?.items;
if (typeof items === "undefined") return;
if (props.behaviour !== 'multi' || !props.append) return;
let files = [];
for (const item of items) {
if (!item.type.startsWith("text/")) {
const blob = item.getAsFile();
if (blob) {
if (blob.size > props.maxFileSize) {
openScreen({ id: 'error', error: 'FileTooLarge' });
}
files.push(blob);
}
}
}
props.append(files);
}
// Let the browser know we can drop files.
function dragover(e: DragEvent) {
e.stopPropagation();
e.preventDefault();
if (e.dataTransfer) e.dataTransfer.dropEffect = "copy";
}
// File dropping.
function drop(e: DragEvent) {
e.preventDefault();
if (props.behaviour !== 'multi' || !props.append) return;
const dropped = e.dataTransfer?.files;
if (dropped) {
let files = [];
for (const item of dropped) {
if (item.size > props.maxFileSize) {
openScreen({ id: 'error', error: 'FileTooLarge' });
}
files.push(item);
}
props.append(files);
}
}
document.addEventListener("paste", paste);
document.addEventListener("dragover", dragover);
document.addEventListener("drop", drop);
return () => {
document.removeEventListener("paste", paste);
document.removeEventListener("dragover", dragover);
document.removeEventListener("drop", drop);
};
}, [ props.append ]);
}
if (props.style === 'icon' || props.style === 'banner') { if (props.style === 'icon' || props.style === 'banner') {
const { style, previewURL, defaultPreview, width, height } = props; const { style, previewURL, defaultPreview, width, height } = props;
return ( return (