diff --git a/src/web/binaryUpload.coffee b/src/web/binaryUpload.coffee index 81e5ecb..1788339 100644 --- a/src/web/binaryUpload.coffee +++ b/src/web/binaryUpload.coffee @@ -1,125 +1,87 @@ -import React from "react" -import Dropzone from "react-dropzone" +import React, { useState, useCallback } from "react" +import { useDropzone } from "react-dropzone" import HelpButton from "./helpButton" import LinkButton from "./util/linkButton" +import * as hooks from "./hooks" import * as crypto from "../crypto" import * as util from "../util" -class BinaryUpload extends React.Component - constructor: (props) -> - super props - @state = - file: null - uploading: false - progress: 0 - encrypt: false - encrypting: false +export default BinaryUpload = -> + [openDialog, renderDialog] = hooks.useDialog() + [encrypt, toggleEncrypt] = hooks.useToggle false + [encrypting, setEncrypting] = useState false + [file, setFile] = useState null - onDrop: (files) => - @setState - file: files[0] + # Paste hook and event + clearFile = (status) -> + setFile null if status == 200 + [doPaste, pasting, progress] = hooks.usePaste openDialog, + useCallback clearFile, [] - doUpload: => - key = null - iv = null - @setState - uploading: true - encrypting: @state.encrypt - progress: 0 - # Due to the lack of progress feature in current Fetch API - # We have to use XHR for now. Dang. - xhr = new XMLHttpRequest() - xhr.upload.addEventListener "progress", (e) => - if e.lengthComputable - @setState - progress: e.loaded / e.total - xhr.addEventListener "readystatechange", => - if xhr.readyState == XMLHttpRequest.DONE - @setState - uploading: false - encrypting: false - file: null - @props.openDialog do => - if xhr.status == 200 - url = if not @state.encrypt - xhr.responseText - else - xhr.responseText + "?crypt#" + key + "+" + iv - - https://{window.location.hostname}{url} - - else - xhr.responseText + # Dropzone hook and event + onDrop = (files) -> + setFile files[0] + {getRootProps, getInputProps, isDragActive} = useDropzone + onDrop: useCallback onDrop, [] - if not @state.encrypt - xhr.open 'PUT', '/paste/' + @state.file.name - xhr.send @state.file + # Upload handler, we basically extend the Paste hook with encryption + doUpload = -> + if not encrypt + doPaste file.name, file.type, file else - [key, iv, name, mime, encrypted] = await crypto.encryptFile @state.file - xhr.open 'PUT', '/paste/' + name - xhr.setRequestHeader 'content-type', mime - xhr.send encrypted - @setState - encrypting: false + # Handle encryption + setEncrypting true + [key, iv, name, mime, encrypted] = await crypto.encryptFile file + setEncrypting false + doPaste name, mime, encrypted, (url) -> + url + "?crypt#" + key + "+" + iv + doUpload = useCallback doUpload, [file, encrypt, doPaste] - progressText: -> - util.progressText @state.progress - - toggleEncrypt: => - @setState (state, props) -> - { encrypt: not state.encrypt } - - render: -> -
- - {({getRootProps, getInputProps}) => -
-
- -

Drag 'n' drop a file here to upload, or click to select

-
- -
- } -
-
- - - - Text Mode - - +
+ {renderDialog()} +
+
+ +

Drag 'n' drop a file here to upload, or click to select

+ +
+
+ + + + Text Mode + +
- -export default BinaryUpload \ No newline at end of file +
\ No newline at end of file diff --git a/src/web/hooks.coffee b/src/web/hooks.coffee index 9afa651..e88cdb2 100644 --- a/src/web/hooks.coffee +++ b/src/web/hooks.coffee @@ -51,11 +51,11 @@ export useDialog = -> ] # Handles shared file-uploading logic between text / binary pasting -export usePaste = (openDialog, transformUrl, callback) -> +export usePaste = (openDialog, callback) -> [pasting, setPasting] = useState false [progress, setProgress] = useState 0 - doPaste = (name, mime, content) -> + doPaste = (name, mime, content, transformUrl) -> # Unfortunately we have to all resort to using XHR here setProgress 0 setPasting true @@ -86,7 +86,8 @@ export usePaste = (openDialog, transformUrl, callback) -> [ # our paste only depends on *setting* states, no reading required - useCallback(doPaste, []), + # but all the callback it reads from its closure may change + useCallback(doPaste, [openDialog, callback]), pasting, progress ] \ No newline at end of file diff --git a/src/web/pastebin.coffee b/src/web/pastebin.coffee index 5efbae1..6a5db3e 100644 --- a/src/web/pastebin.coffee +++ b/src/web/pastebin.coffee @@ -1,4 +1,4 @@ -import React, { useState } from "react" +import React, { useState, useCallback } from "react" import * as hooks from "./hooks" import HelpButton from "./helpButton" import LinkButton from "./util/linkButton" @@ -8,8 +8,12 @@ export default Pastebin = -> [openDialog, renderDialog] = hooks.useDialog() [highlight, toggleHighlight] = hooks.useToggle false [text, setText] = useState "" - [doPaste, pasting, _] = hooks.usePaste openDialog, null, (status) -> + + # Paste hook and events + clearText = (status) -> setText "" if status == 200 + [doPaste, pasting, _] = hooks.usePaste openDialog, + useCallback clearText, [] onEditTextUpdate = (ev) -> setText ev.target.value