diff --git a/src/web/hooks.coffee b/src/web/hooks.coffee index 80012ee..30267ca 100644 --- a/src/web/hooks.coffee +++ b/src/web/hooks.coffee @@ -1,6 +1,15 @@ import React, { useState } from "react" import ReactModal from "react-modal" +# Simple abstraction for a toggling state +export useToggle = (defVal) -> + [state, setState] = useState defVal + + toggle = -> + setState (prev) -> not prev + + [state, toggle] + # A hook to support opening dialogs from the code # returns [openDialog, renderDialog] # renderDialog should always be called somewhere @@ -31,3 +40,39 @@ export useDialog = -> [openDialog, renderDialog] + +# Handles shared file-uploading logic between text / binary pasting +export usePaste = (openDialog, transformUrl, callback) -> + [pasting, setPasting] = useState false + [progress, setProgress] = useState 0 + + doPaste = (name, mime, content) -> + # Unfortunately we have to all resort to using XHR here + setProgress 0 + setPasting true + + # Build the XHR + xhr = new XMLHttpRequest() + xhr.upload.addEventListener "progress", (e) -> + if e.lengthComputable + setProgress e.loaded / e.total + xhr.addEventListener "readystatechange", -> + if xhr.readyState == XMLHttpRequest.DONE + setPasting false + openDialog do -> + if xhr.status == 200 + url = xhr.responseText + url = transformUrl url if transformUrl + + https://{window.location.hostname}{url} + + else + xhr.responseText + callback xhr.status, xhr.responseText if callback + + # Handle uploading + xhr.open 'PUT', "/paste/" + name + xhr.setRequestHeader "content-type", mime + xhr.send content + + [doPaste, pasting, progress] \ No newline at end of file diff --git a/src/web/pastebin.coffee b/src/web/pastebin.coffee index 7582e08..5efbae1 100644 --- a/src/web/pastebin.coffee +++ b/src/web/pastebin.coffee @@ -1,90 +1,52 @@ -import React from "react" +import React, { useState } from "react" +import * as hooks from "./hooks" import HelpButton from "./helpButton" import LinkButton from "./util/linkButton" import ContentEditable from "./util/contentEditable" -class Pastebin extends React.Component - constructor: (props) -> - super props - @state = - text: "" - pasting: false - highlight: false # Make this false by default to avoid blocking +export default Pastebin = -> + [openDialog, renderDialog] = hooks.useDialog() + [highlight, toggleHighlight] = hooks.useToggle false + [text, setText] = useState "" + [doPaste, pasting, _] = hooks.usePaste openDialog, null, (status) -> + setText "" if status == 200 - onEditTextUpdate: (ev) => - console.log ev.target.value - @setState - text: ev.target.value + onEditTextUpdate = (ev) -> + setText ev.target.value - toggleHighlight: (ev) => - @setState (state, props) => - { highlight: not state.highlight } + paste = -> + doPaste "web_paste.txt", "text/plain", text - paste: => - return if @state.text.trim() == "" - # Set the state first to disable the button - @setState - pasting: true - # For things pasted through the web interface, - # we always assume the name is `web_paste.txt`, - # and the content type is always `text/plain`. - resp = await fetch "/paste/web_paste.txt", - method: 'PUT' - headers: - 'content-type': 'text/plain' - body: @state.text - console.log resp - txt = await resp.text() - console.log txt - - # Dialog opening is provided by parent - @props.openDialog do -> - if resp.ok - - https://{window.location.hostname}{txt} - - else - txt - - @setState - text: "" - - # Reset the button - @setState - pasting: false - - render: -> -
- -
- - - - File Upload - - -
+
+ {renderDialog()} + +
+ + + + File Upload + +
- -export default Pastebin \ No newline at end of file +
\ No newline at end of file