pastebin: rewrite using React Hooks
This commit is contained in:
parent
597c6c9359
commit
cf57e3de42
|
@ -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 = ->
|
|||
</ReactModal>
|
||||
|
||||
[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]
|
|
@ -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
|
||||
<a href={txt} target="_blank">
|
||||
https://{window.location.hostname}{txt}
|
||||
</a>
|
||||
else
|
||||
txt
|
||||
|
||||
@setState
|
||||
text: ""
|
||||
|
||||
# Reset the button
|
||||
@setState
|
||||
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 className="content-pastebin">
|
||||
{renderDialog()}
|
||||
<ContentEditable
|
||||
className="content-pastebin-edit"
|
||||
onUpdate={onEditTextUpdate}
|
||||
value={text}
|
||||
highlightCode={highlight}
|
||||
plainText
|
||||
/>
|
||||
<div className="content-buttons">
|
||||
<HelpButton openDialog={openDialog} />
|
||||
<button
|
||||
className="button-blue"
|
||||
onClick={toggleHighlight}
|
||||
>
|
||||
Highlight: {if highlight then 'ON' else 'OFF'}
|
||||
</button>
|
||||
<LinkButton
|
||||
className="button-blue"
|
||||
disabled={pasting}
|
||||
to="/paste/binary"
|
||||
>
|
||||
File Upload
|
||||
</LinkButton>
|
||||
<button
|
||||
className="button-blue"
|
||||
disabled={pasting or text.trim() is ""}
|
||||
onClick={paste}
|
||||
>
|
||||
Paste
|
||||
</button>
|
||||
</div>
|
||||
|
||||
export default Pastebin
|
||||
</div>
|
Loading…
Reference in New Issue