pastebin: rewrite using React Hooks

This commit is contained in:
Peter Cai 2020-02-20 09:20:58 +08:00
parent 597c6c9359
commit cf57e3de42
No known key found for this signature in database
GPG Key ID: 71F5FB4E4F3FD54F
2 changed files with 89 additions and 82 deletions

View File

@ -1,6 +1,15 @@
import React, { useState } from "react" import React, { useState } from "react"
import ReactModal from "react-modal" 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 # A hook to support opening dialogs from the code
# returns [openDialog, renderDialog] # returns [openDialog, renderDialog]
# renderDialog should always be called somewhere # renderDialog should always be called somewhere
@ -31,3 +40,39 @@ export useDialog = ->
</ReactModal> </ReactModal>
[openDialog, renderDialog] [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
<a href={url} target="_blank">
https://{window.location.hostname}{url}
</a>
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]

View File

@ -1,90 +1,52 @@
import React from "react" import React, { useState } from "react"
import * as hooks from "./hooks"
import HelpButton from "./helpButton" import HelpButton from "./helpButton"
import LinkButton from "./util/linkButton" import LinkButton from "./util/linkButton"
import ContentEditable from "./util/contentEditable" import ContentEditable from "./util/contentEditable"
class Pastebin extends React.Component export default Pastebin = ->
constructor: (props) -> [openDialog, renderDialog] = hooks.useDialog()
super props [highlight, toggleHighlight] = hooks.useToggle false
@state = [text, setText] = useState ""
text: "" [doPaste, pasting, _] = hooks.usePaste openDialog, null, (status) ->
pasting: false setText "" if status == 200
highlight: false # Make this false by default to avoid blocking
onEditTextUpdate: (ev) => onEditTextUpdate = (ev) ->
console.log ev.target.value setText ev.target.value
@setState
text: ev.target.value
toggleHighlight: (ev) => paste = ->
@setState (state, props) => doPaste "web_paste.txt", "text/plain", text
{ highlight: not state.highlight }
paste: => <div className="content-pastebin">
return if @state.text.trim() == "" {renderDialog()}
# Set the state first to disable the button <ContentEditable
@setState className="content-pastebin-edit"
pasting: true onUpdate={onEditTextUpdate}
# For things pasted through the web interface, value={text}
# we always assume the name is `web_paste.txt`, highlightCode={highlight}
# and the content type is always `text/plain`. plainText
resp = await fetch "/paste/web_paste.txt", />
method: 'PUT' <div className="content-buttons">
headers: <HelpButton openDialog={openDialog} />
'content-type': 'text/plain' <button
body: @state.text className="button-blue"
console.log resp onClick={toggleHighlight}
txt = await resp.text() >
console.log txt Highlight: {if highlight then 'ON' else 'OFF'}
</button>
# Dialog opening is provided by parent <LinkButton
@props.openDialog do -> className="button-blue"
if resp.ok disabled={pasting}
<a href={txt} target="_blank"> to="/paste/binary"
https://{window.location.hostname}{txt} >
</a> File Upload
else </LinkButton>
txt <button
className="button-blue"
@setState disabled={pasting or text.trim() is ""}
text: "" onClick={paste}
>
# Reset the button Paste
@setState </button>
pasting: false
render: ->
<div className="content-pastebin">
<ContentEditable
className="content-pastebin-edit"
onUpdate={@onEditTextUpdate}
value={@state.text}
highlightCode={@state.highlight}
plainText
/>
<div className="content-buttons">
<HelpButton openDialog={@props.openDialog} />
<button
className="button-blue"
onClick={@toggleHighlight}
>
Highlight: {if @state.highlight then 'ON' else 'OFF'}
</button>
<LinkButton
className="button-blue"
disabled={@state.pasting}
to="/paste/binary"
>
File Upload
</LinkButton>
<button
className="button-blue"
disabled={@state.pasting or @state.text.trim() is ""}
onClick={@paste}
>
Paste
</button>
</div>
</div> </div>
</div>
export default Pastebin