binaryUpload: refactor to use hooks
This commit is contained in:
parent
cf095911b0
commit
8c81b9e66f
|
@ -1,125 +1,87 @@
|
||||||
import React from "react"
|
import React, { useState, useCallback } from "react"
|
||||||
import Dropzone from "react-dropzone"
|
import { useDropzone } from "react-dropzone"
|
||||||
import HelpButton from "./helpButton"
|
import HelpButton from "./helpButton"
|
||||||
import LinkButton from "./util/linkButton"
|
import LinkButton from "./util/linkButton"
|
||||||
|
import * as hooks from "./hooks"
|
||||||
import * as crypto from "../crypto"
|
import * as crypto from "../crypto"
|
||||||
import * as util from "../util"
|
import * as util from "../util"
|
||||||
|
|
||||||
class BinaryUpload extends React.Component
|
export default BinaryUpload = ->
|
||||||
constructor: (props) ->
|
[openDialog, renderDialog] = hooks.useDialog()
|
||||||
super props
|
[encrypt, toggleEncrypt] = hooks.useToggle false
|
||||||
@state =
|
[encrypting, setEncrypting] = useState false
|
||||||
file: null
|
[file, setFile] = useState null
|
||||||
uploading: false
|
|
||||||
progress: 0
|
|
||||||
encrypt: false
|
|
||||||
encrypting: false
|
|
||||||
|
|
||||||
onDrop: (files) =>
|
# Paste hook and event
|
||||||
@setState
|
clearFile = (status) ->
|
||||||
file: files[0]
|
setFile null if status == 200
|
||||||
|
[doPaste, pasting, progress] = hooks.usePaste openDialog,
|
||||||
|
useCallback clearFile, []
|
||||||
|
|
||||||
doUpload: =>
|
# Dropzone hook and event
|
||||||
key = null
|
onDrop = (files) ->
|
||||||
iv = null
|
setFile files[0]
|
||||||
@setState
|
{getRootProps, getInputProps, isDragActive} = useDropzone
|
||||||
uploading: true
|
onDrop: useCallback onDrop, []
|
||||||
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
|
|
||||||
<a href={url} target="_blank">
|
|
||||||
https://{window.location.hostname}{url}
|
|
||||||
</a>
|
|
||||||
else
|
|
||||||
xhr.responseText
|
|
||||||
|
|
||||||
if not @state.encrypt
|
# Upload handler, we basically extend the Paste hook with encryption
|
||||||
xhr.open 'PUT', '/paste/' + @state.file.name
|
doUpload = ->
|
||||||
xhr.send @state.file
|
if not encrypt
|
||||||
|
doPaste file.name, file.type, file
|
||||||
else
|
else
|
||||||
[key, iv, name, mime, encrypted] = await crypto.encryptFile @state.file
|
# Handle encryption
|
||||||
xhr.open 'PUT', '/paste/' + name
|
setEncrypting true
|
||||||
xhr.setRequestHeader 'content-type', mime
|
[key, iv, name, mime, encrypted] = await crypto.encryptFile file
|
||||||
xhr.send encrypted
|
setEncrypting false
|
||||||
@setState
|
doPaste name, mime, encrypted, (url) ->
|
||||||
encrypting: false
|
url + "?crypt#" + key + "+" + iv
|
||||||
|
doUpload = useCallback doUpload, [file, encrypt, doPaste]
|
||||||
|
|
||||||
progressText: ->
|
<div className="content-pastebin">
|
||||||
util.progressText @state.progress
|
{renderDialog()}
|
||||||
|
<section className="container">
|
||||||
toggleEncrypt: =>
|
<div {...getRootProps({className: 'dropzone'})}>
|
||||||
@setState (state, props) ->
|
<input {...getInputProps()} />
|
||||||
{ encrypt: not state.encrypt }
|
<p>Drag 'n' drop a file here to upload, or click to select</p>
|
||||||
|
|
||||||
render: ->
|
|
||||||
<div className="content-pastebin">
|
|
||||||
<Dropzone onDrop={@onDrop}>
|
|
||||||
{({getRootProps, getInputProps}) =>
|
|
||||||
<section className="container">
|
|
||||||
<div {...getRootProps({className: 'dropzone'})}>
|
|
||||||
<input {...getInputProps()} />
|
|
||||||
<p>Drag 'n' drop a file here to upload, or click to select</p>
|
|
||||||
</div>
|
|
||||||
<aside>
|
|
||||||
<p>{
|
|
||||||
if not @state.file
|
|
||||||
"No Selected File"
|
|
||||||
else
|
|
||||||
"Selected File: #{@state.file.name}"
|
|
||||||
}</p>
|
|
||||||
</aside>
|
|
||||||
</section>
|
|
||||||
}
|
|
||||||
</Dropzone>
|
|
||||||
<div className="content-buttons">
|
|
||||||
<HelpButton openDialog={@props.openDialog} />
|
|
||||||
<button
|
|
||||||
className="button-blue"
|
|
||||||
disabled={@state.uploading}
|
|
||||||
onClick={@toggleEncrypt}
|
|
||||||
>
|
|
||||||
{ "Encrypt: " + if @state.encrypt then "ON" else "OFF" }
|
|
||||||
</button>
|
|
||||||
<LinkButton
|
|
||||||
className="button-blue"
|
|
||||||
disabled={@state.uploading}
|
|
||||||
to="/paste/text"
|
|
||||||
>
|
|
||||||
Text Mode
|
|
||||||
</LinkButton>
|
|
||||||
<button
|
|
||||||
className="button-blue"
|
|
||||||
disabled={@state.uploading or not @state.file}
|
|
||||||
onClick={@doUpload}
|
|
||||||
>
|
|
||||||
{
|
|
||||||
if not @state.uploading
|
|
||||||
"Upload"
|
|
||||||
else if @state.encrypting
|
|
||||||
"Encrypting"
|
|
||||||
else
|
|
||||||
@progressText()
|
|
||||||
}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
<aside>
|
||||||
|
<p>{
|
||||||
|
if not file
|
||||||
|
"No Selected File"
|
||||||
|
else
|
||||||
|
"Selected File: #{file.name}"
|
||||||
|
}</p>
|
||||||
|
</aside>
|
||||||
|
</section>
|
||||||
|
<div className="content-buttons">
|
||||||
|
<HelpButton openDialog={openDialog} />
|
||||||
|
<button
|
||||||
|
className="button-blue"
|
||||||
|
disabled={pasting}
|
||||||
|
onClick={toggleEncrypt}
|
||||||
|
>
|
||||||
|
{ "Encrypt: " + if encrypt then "ON" else "OFF" }
|
||||||
|
</button>
|
||||||
|
<LinkButton
|
||||||
|
className="button-blue"
|
||||||
|
disabled={pasting}
|
||||||
|
to="/paste/text"
|
||||||
|
>
|
||||||
|
Text Mode
|
||||||
|
</LinkButton>
|
||||||
|
<button
|
||||||
|
className="button-blue"
|
||||||
|
disabled={pasting or not file}
|
||||||
|
onClick={doUpload}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
if encrypting
|
||||||
|
"Encrypting"
|
||||||
|
else if not pasting
|
||||||
|
"Upload"
|
||||||
|
else
|
||||||
|
util.progressText progress
|
||||||
|
}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
export default BinaryUpload
|
|
|
@ -51,11 +51,11 @@ export useDialog = ->
|
||||||
]
|
]
|
||||||
|
|
||||||
# Handles shared file-uploading logic between text / binary pasting
|
# Handles shared file-uploading logic between text / binary pasting
|
||||||
export usePaste = (openDialog, transformUrl, callback) ->
|
export usePaste = (openDialog, callback) ->
|
||||||
[pasting, setPasting] = useState false
|
[pasting, setPasting] = useState false
|
||||||
[progress, setProgress] = useState 0
|
[progress, setProgress] = useState 0
|
||||||
|
|
||||||
doPaste = (name, mime, content) ->
|
doPaste = (name, mime, content, transformUrl) ->
|
||||||
# Unfortunately we have to all resort to using XHR here
|
# Unfortunately we have to all resort to using XHR here
|
||||||
setProgress 0
|
setProgress 0
|
||||||
setPasting true
|
setPasting true
|
||||||
|
@ -86,7 +86,8 @@ export usePaste = (openDialog, transformUrl, callback) ->
|
||||||
|
|
||||||
[
|
[
|
||||||
# our paste only depends on *setting* states, no reading required
|
# 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,
|
pasting,
|
||||||
progress
|
progress
|
||||||
]
|
]
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState } from "react"
|
import React, { useState, useCallback } from "react"
|
||||||
import * as hooks from "./hooks"
|
import * as hooks from "./hooks"
|
||||||
import HelpButton from "./helpButton"
|
import HelpButton from "./helpButton"
|
||||||
import LinkButton from "./util/linkButton"
|
import LinkButton from "./util/linkButton"
|
||||||
|
@ -8,8 +8,12 @@ export default Pastebin = ->
|
||||||
[openDialog, renderDialog] = hooks.useDialog()
|
[openDialog, renderDialog] = hooks.useDialog()
|
||||||
[highlight, toggleHighlight] = hooks.useToggle false
|
[highlight, toggleHighlight] = hooks.useToggle false
|
||||||
[text, setText] = useState ""
|
[text, setText] = useState ""
|
||||||
[doPaste, pasting, _] = hooks.usePaste openDialog, null, (status) ->
|
|
||||||
|
# Paste hook and events
|
||||||
|
clearText = (status) ->
|
||||||
setText "" if status == 200
|
setText "" if status == 200
|
||||||
|
[doPaste, pasting, _] = hooks.usePaste openDialog,
|
||||||
|
useCallback clearText, []
|
||||||
|
|
||||||
onEditTextUpdate = (ev) ->
|
onEditTextUpdate = (ev) ->
|
||||||
setText ev.target.value
|
setText ev.target.value
|
||||||
|
|
Loading…
Reference in a new issue