mirror of
https://github.com/urbit/shrub.git
synced 2024-12-24 11:24:21 +03:00
s3: improves drag-and-drop behavior
Fixes #3992 ...to the extent possible. Adds handlers for better releasing drag state
This commit is contained in:
parent
fb6488bc68
commit
1eb6c47c83
@ -1,15 +1,39 @@
|
||||
import { useState, useCallback, useMemo, useEffect } from "react";
|
||||
|
||||
function validateDragEvent(e: DragEvent): FileList | null {
|
||||
const files = e.dataTransfer?.files;
|
||||
console.log(files);
|
||||
if(!files?.length) {
|
||||
return null;
|
||||
function validateDragEvent(e: DragEvent): FileList | File[] | true | null {
|
||||
const files: File[] = [];
|
||||
let valid = false;
|
||||
if (e.dataTransfer?.files) {
|
||||
Array.from(e.dataTransfer.files).forEach(f => files.push(f));
|
||||
}
|
||||
return files || null;
|
||||
if (e.dataTransfer?.items) {
|
||||
Array.from(e.dataTransfer.items || [])
|
||||
.filter((i) => i.kind === 'file')
|
||||
.forEach(f => {
|
||||
valid = true; // Valid if file exists, but on DragOver, won't reveal its contents for security
|
||||
const data = f.getAsFile();
|
||||
if (data) {
|
||||
files.push(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (files.length) {
|
||||
return [...new Set(files)];
|
||||
}
|
||||
if (navigator.userAgent.includes('Safari')) {
|
||||
if (e.dataTransfer?.effectAllowed === 'all') {
|
||||
valid = true;
|
||||
} else if (e.dataTransfer?.files.length) {
|
||||
return e.dataTransfer.files;
|
||||
}
|
||||
}
|
||||
if (valid) {
|
||||
return true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function useFileDrag(dragged: (f: FileList) => void) {
|
||||
export function useFileDrag(dragged: (f: FileList | File[]) => void) {
|
||||
const [dragging, setDragging] = useState(false);
|
||||
|
||||
const onDragEnter = useCallback(
|
||||
@ -25,11 +49,11 @@ export function useFileDrag(dragged: (f: FileList) => void) {
|
||||
const onDrop = useCallback(
|
||||
(e: DragEvent) => {
|
||||
setDragging(false);
|
||||
e.preventDefault();
|
||||
const files = validateDragEvent(e);
|
||||
if (!files) {
|
||||
if (!files || files === true) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
dragged(files);
|
||||
},
|
||||
[setDragging, dragged]
|
||||
@ -37,6 +61,9 @@ export function useFileDrag(dragged: (f: FileList) => void) {
|
||||
|
||||
const onDragOver = useCallback(
|
||||
(e: DragEvent) => {
|
||||
if (!validateDragEvent(e)) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
setDragging(true);
|
||||
},
|
||||
@ -53,6 +80,18 @@ export function useFileDrag(dragged: (f: FileList) => void) {
|
||||
[setDragging]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const mouseleave = (e) => {
|
||||
if (!e.relatedTarget && !e.toElement) {
|
||||
setDragging(false);
|
||||
}
|
||||
};
|
||||
document.body.addEventListener('mouseout', mouseleave);
|
||||
return () => {
|
||||
document.body.removeEventListener('mouseout', mouseleave);
|
||||
}
|
||||
}, [setDragging]);
|
||||
|
||||
const bind = {
|
||||
onDragLeave,
|
||||
onDragOver,
|
||||
|
@ -68,7 +68,7 @@ export function ChatResource(props: ChatResourceProps) {
|
||||
const chatInput = useRef<ChatInput>();
|
||||
|
||||
const onFileDrag = useCallback(
|
||||
(files: FileList) => {
|
||||
(files: FileList | File[]) => {
|
||||
if (!chatInput.current) {
|
||||
return;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import ChatEditor from './chat-editor';
|
||||
import { S3Upload } from '~/views/components/s3-upload' ;
|
||||
import { uxToHex } from '~/logic/lib/util';
|
||||
import { fileListFromFileArray, uxToHex } from '~/logic/lib/util';
|
||||
import { Sigil } from '~/logic/lib/sigil';
|
||||
import tokenizeMessage, { isUrl } from '~/logic/lib/tokenizeMessage';
|
||||
import GlobalApi from '~/logic/api/global';
|
||||
@ -150,16 +150,14 @@ export default class ChatInput extends Component<ChatInputProps, ChatInputState>
|
||||
this.uploadFiles(event.clipboardData.files);
|
||||
}
|
||||
|
||||
uploadFiles(files: FileList) {
|
||||
uploadFiles(files: FileList | File[]) {
|
||||
if (!this.readyToUpload()) {
|
||||
return;
|
||||
}
|
||||
if (!this.s3Uploader.current || !this.s3Uploader.current.inputRef.current)
|
||||
return;
|
||||
this.s3Uploader.current.inputRef.current.files = files;
|
||||
const fire = document.createEvent('HTMLEvents');
|
||||
fire.initEvent('change', true, true);
|
||||
this.s3Uploader.current?.inputRef.current?.dispatchEvent(fire);
|
||||
if (!this.s3Uploader.current) {
|
||||
return;
|
||||
}
|
||||
this.s3Uploader.current.uploadFiles(files);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -51,6 +51,7 @@ export class S3Upload extends Component<S3UploadProps, S3UploadState> {
|
||||
this.s3 = new S3Client();
|
||||
this.setCredentials(props.credentials, props.configuration);
|
||||
this.inputRef = React.createRef();
|
||||
this.uploadFiles = this.uploadFiles.bind(this);
|
||||
}
|
||||
|
||||
isReady(creds, config): boolean {
|
||||
@ -84,13 +85,9 @@ export class S3Upload extends Component<S3UploadProps, S3UploadState> {
|
||||
);
|
||||
}
|
||||
|
||||
onChange(): void {
|
||||
uploadFiles(files: FileList | File[]) {
|
||||
const { props } = this;
|
||||
if (!this.inputRef.current) { return; }
|
||||
let files = this.inputRef.current.files;
|
||||
if (!files || files.length <= 0) { return; }
|
||||
|
||||
let file = files.item(0);
|
||||
let file = ('item' in files) ? files.item(0) : files[0];
|
||||
if (!file) { return; }
|
||||
const fileParts = file.name.split('.');
|
||||
const fileName = fileParts.slice(0, -1);
|
||||
@ -118,6 +115,13 @@ export class S3Upload extends Component<S3UploadProps, S3UploadState> {
|
||||
}, 200);
|
||||
}
|
||||
|
||||
onChange(): void {
|
||||
if (!this.inputRef.current) { return; }
|
||||
let files = this.inputRef.current.files;
|
||||
if (!files || files.length <= 0) { return; }
|
||||
this.uploadFiles(files);
|
||||
}
|
||||
|
||||
onClick() {
|
||||
if (!this.inputRef.current) { return; }
|
||||
this.inputRef.current.click();
|
||||
|
Loading…
Reference in New Issue
Block a user