implement binary upload

This commit is contained in:
Peter Cai 2020-02-18 17:39:38 +08:00
parent f4adb0030c
commit d705107d35
No known key found for this signature in database
GPG key ID: 71F5FB4E4F3FD54F
8 changed files with 381 additions and 1 deletions

195
package-lock.json generated
View file

@ -925,6 +925,23 @@
"@babel/plugin-transform-react-jsx-source": "^7.8.3"
}
},
"@babel/runtime": {
"version": "7.8.4",
"resolved": "https://registry.npm.taobao.org/@babel/runtime/download/@babel/runtime-7.8.4.tgz",
"integrity": "sha1-159aIED3yqJNU+VjqtScvAVYEwg=",
"dev": true,
"requires": {
"regenerator-runtime": "^0.13.2"
},
"dependencies": {
"regenerator-runtime": {
"version": "0.13.3",
"resolved": "https://registry.npm.taobao.org/regenerator-runtime/download/regenerator-runtime-0.13.3.tgz",
"integrity": "sha1-fPanfY9cb2Drc8X8GVWyzrAea/U=",
"dev": true
}
}
},
"@babel/template": {
"version": "7.8.3",
"resolved": "https://registry.npm.taobao.org/@babel/template/download/@babel/template-7.8.3.tgz",
@ -1376,6 +1393,12 @@
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
"dev": true
},
"attr-accept": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/attr-accept/download/attr-accept-2.0.0.tgz",
"integrity": "sha1-hCL+9e5KURwgd5bIiCJ6td4DMG8=",
"dev": true
},
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npm.taobao.org/aws-sign2/download/aws-sign2-0.7.0.tgz",
@ -2817,6 +2840,15 @@
"integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==",
"dev": true
},
"file-selector": {
"version": "0.1.12",
"resolved": "https://registry.npm.taobao.org/file-selector/download/file-selector-0.1.12.tgz",
"integrity": "sha1-/nJlR74hmnh6ncxkBXWgSgMrH9A=",
"dev": true,
"requires": {
"tslib": "^1.9.0"
}
},
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
@ -3707,6 +3739,12 @@
"integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
"dev": true
},
"gud": {
"version": "1.0.0",
"resolved": "https://registry.npm.taobao.org/gud/download/gud-1.0.0.tgz",
"integrity": "sha1-pIlYGxfmpwvsqavjrlfeekmYUsA=",
"dev": true
},
"har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/har-schema/download/har-schema-2.0.0.tgz",
@ -3823,6 +3861,20 @@
"integrity": "sha1-7SGqAB/mJSuxCj121HVzxlOf4Tw=",
"dev": true
},
"history": {
"version": "4.10.1",
"resolved": "https://registry.npm.taobao.org/history/download/history-4.10.1.tgz?cache=0&sync_timestamp=1581115672888&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhistory%2Fdownload%2Fhistory-4.10.1.tgz",
"integrity": "sha1-MzcaZeOoOyZ0NOKz87G0xYqtTPM=",
"dev": true,
"requires": {
"@babel/runtime": "^7.1.2",
"loose-envify": "^1.2.0",
"resolve-pathname": "^3.0.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0",
"value-equal": "^1.0.1"
}
},
"hmac-drbg": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
@ -3834,6 +3886,15 @@
"minimalistic-crypto-utils": "^1.0.1"
}
},
"hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npm.taobao.org/hoist-non-react-statics/download/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha1-7OCsr3HWLClpwuxZ/v9CpLGoW0U=",
"dev": true,
"requires": {
"react-is": "^16.7.0"
}
},
"homedir-polyfill": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
@ -4654,6 +4715,17 @@
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
"dev": true
},
"mini-create-react-context": {
"version": "0.3.2",
"resolved": "https://registry.npm.taobao.org/mini-create-react-context/download/mini-create-react-context-0.3.2.tgz",
"integrity": "sha1-efxZjyg91iPajgiLBduM3aslAYk=",
"dev": true,
"requires": {
"@babel/runtime": "^7.4.0",
"gud": "^1.0.0",
"tiny-warning": "^1.0.2"
}
},
"minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
@ -5310,6 +5382,23 @@
"integrity": "sha1-1i27VnlAXXLEc37FhgDp3c8G0kw=",
"dev": true
},
"path-to-regexp": {
"version": "1.8.0",
"resolved": "https://registry.npm.taobao.org/path-to-regexp/download/path-to-regexp-1.8.0.tgz",
"integrity": "sha1-iHs7qdhDk+h6CgufTLdWGYtTVIo=",
"dev": true,
"requires": {
"isarray": "0.0.1"
},
"dependencies": {
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npm.taobao.org/isarray/download/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
"dev": true
}
}
},
"path-type": {
"version": "1.1.0",
"resolved": "https://registry.npm.taobao.org/path-type/download/path-type-1.1.0.tgz",
@ -5595,6 +5684,15 @@
"integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
"dev": true
},
"raf": {
"version": "3.4.1",
"resolved": "https://registry.npm.taobao.org/raf/download/raf-3.4.1.tgz",
"integrity": "sha1-B0LpmkplUvRF1z4+4DKK8P8e3jk=",
"dev": true,
"requires": {
"performance-now": "^2.1.0"
}
},
"randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@ -5659,6 +5757,17 @@
"scheduler": "^0.18.0"
}
},
"react-dropzone": {
"version": "10.2.1",
"resolved": "https://registry.npm.taobao.org/react-dropzone/download/react-dropzone-10.2.1.tgz",
"integrity": "sha1-t1IBJMSjtm+W1J94eQJ8ekdeqiA=",
"dev": true,
"requires": {
"attr-accept": "^2.0.0",
"file-selector": "^0.1.12",
"prop-types": "^15.7.2"
}
},
"react-is": {
"version": "16.12.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz",
@ -5683,6 +5792,68 @@
"warning": "^4.0.3"
}
},
"react-motion": {
"version": "0.5.2",
"resolved": "https://registry.npm.taobao.org/react-motion/download/react-motion-0.5.2.tgz",
"integrity": "sha1-DdOmnkETFlZ5J5F8ZiZVG6BgcxY=",
"dev": true,
"requires": {
"performance-now": "^0.2.0",
"prop-types": "^15.5.8",
"raf": "^3.1.0"
},
"dependencies": {
"performance-now": {
"version": "0.2.0",
"resolved": "https://registry.npm.taobao.org/performance-now/download/performance-now-0.2.0.tgz",
"integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=",
"dev": true
}
}
},
"react-router": {
"version": "5.1.2",
"resolved": "https://registry.npm.taobao.org/react-router/download/react-router-5.1.2.tgz",
"integrity": "sha1-bqUdeJyzamvhul98DUjdnoF9NBg=",
"dev": true,
"requires": {
"@babel/runtime": "^7.1.2",
"history": "^4.9.0",
"hoist-non-react-statics": "^3.1.0",
"loose-envify": "^1.3.1",
"mini-create-react-context": "^0.3.0",
"path-to-regexp": "^1.7.0",
"prop-types": "^15.6.2",
"react-is": "^16.6.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0"
}
},
"react-router-dom": {
"version": "5.1.2",
"resolved": "https://registry.npm.taobao.org/react-router-dom/download/react-router-dom-5.1.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-router-dom%2Fdownload%2Freact-router-dom-5.1.2.tgz",
"integrity": "sha1-BnAbg0NS9E03+7YxH4cPhMdrnBg=",
"dev": true,
"requires": {
"@babel/runtime": "^7.1.2",
"history": "^4.9.0",
"loose-envify": "^1.3.1",
"prop-types": "^15.6.2",
"react-router": "5.1.2",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0"
}
},
"react-router-transition": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/react-router-transition/download/react-router-transition-2.0.0.tgz",
"integrity": "sha1-wApCQPCq1fRBUs8d5JxoK/AOQ6w=",
"dev": true,
"requires": {
"prop-types": "^15.7.2",
"react-motion": "^0.5.2"
}
},
"read-pkg": {
"version": "1.1.0",
"resolved": "https://registry.npm.taobao.org/read-pkg/download/read-pkg-1.1.0.tgz",
@ -5971,6 +6142,12 @@
"integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
"dev": true
},
"resolve-pathname": {
"version": "3.0.0",
"resolved": "https://registry.npm.taobao.org/resolve-pathname/download/resolve-pathname-3.0.0.tgz",
"integrity": "sha1-mdAiJNPPJjaJvsuzk7xWAxMCXc0=",
"dev": true
},
"resolve-url": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
@ -6830,6 +7007,18 @@
"setimmediate": "^1.0.4"
}
},
"tiny-invariant": {
"version": "1.1.0",
"resolved": "https://registry.npm.taobao.org/tiny-invariant/download/tiny-invariant-1.1.0.tgz",
"integrity": "sha1-Y0xfjv3CdxS384bDXmdgmR0jCHU=",
"dev": true
},
"tiny-warning": {
"version": "1.0.3",
"resolved": "https://registry.npm.taobao.org/tiny-warning/download/tiny-warning-1.0.3.tgz",
"integrity": "sha1-lKMNtFPfTGQ9D9VmBg1gqHXYR1Q=",
"dev": true
},
"to-arraybuffer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
@ -7211,6 +7400,12 @@
"spdx-expression-parse": "^3.0.0"
}
},
"value-equal": {
"version": "1.0.1",
"resolved": "https://registry.npm.taobao.org/value-equal/download/value-equal-1.0.1.tgz",
"integrity": "sha1-Hgt5THNMXAyt4XnEN9NW2TGjTWw=",
"dev": true
},
"verror": {
"version": "1.10.0",
"resolved": "https://registry.npm.taobao.org/verror/download/verror-1.10.0.tgz",

View file

@ -31,7 +31,10 @@
"raw-loader": "^4.0.0",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-dropzone": "^10.2.1",
"react-modal": "^3.11.1",
"react-router-dom": "^5.1.2",
"react-router-transition": "^2.0.0",
"sass-loader": "^8.0.2",
"style-loader": "^1.1.3",
"url-loader": "^3.0.0",

View file

@ -0,0 +1,97 @@
import React from "react"
import { Redirect } from "react-router-dom"
import Dropzone from "react-dropzone"
class BinaryUpload extends React.Component
constructor: (props) ->
super props
@state =
file: null
uploading: false
progress: 0
switchToText: false
onDrop: (files) =>
@setState
file: files[0]
doUpload: =>
@setState
uploading: true
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
file: null
@props.openDialog do ->
if xhr.status == 200
<a href={xhr.responseText} target="_blank">
https://{window.location.hostname}{xhr.responseText}
</a>
else
xhr.responseText
xhr.open 'PUT', '/paste/' + @state.file.name
xhr.send @state.file
progressText: ->
txt = (@state.progress * 100).toFixed(2) + "%"
if @state.progress < 0.1
"0" + txt
else
txt
render: ->
if @state.switchToText
return <Redirect to="/paste/text" />
<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">
<button
className="button-blue"
disabled={@state.uploading}
onClick={(ev) => @setState { switchToText: true }}
>
Text Mode
</button>
<button
className="button-blue"
disabled={@state.uploading or not @state.file}
onClick={@doUpload}
>
{
if not @state.uploading
"Upload"
else
@progressText()
}
</button>
</div>
</div>
export default BinaryUpload

View file

@ -1,6 +1,9 @@
import React from "react"
import { BrowserRouter as Router, Route, Switch, Redirect } from "react-router-dom"
import { AnimatedSwitch } from 'react-router-transition'
import ReactModal from "react-modal"
import Pastebin from "./pastebin"
import BinaryUpload from "./binaryUpload"
class Home extends React.Component
constructor: (props) ->
@ -17,7 +20,24 @@ class Home extends React.Component
render: ->
<div className="content-wrapper">
<div className="content">
<Pastebin openDialog={@openDialog}/>
<Router>
<AnimatedSwitch
atEnter={{ opacity: 0 }}
atLeave={{ opacity: 0 }}
atActive={{ opacity: 1 }}
className="switch-wrapper"
>
<Redirect exact from="/" to="/paste/text" />
<Route
exact path="/paste/text"
component={() => <Pastebin openDialog={@openDialog}/>}
/>
<Route
exact path="/paste/binary"
component={() => <BinaryUpload openDialog={@openDialog}/>}
/>
</AnimatedSwitch>
</Router>
</div>
{
# Provide modal dialog for all child

View file

@ -1,4 +1,5 @@
import React from "react"
import { Redirect } from "react-router-dom"
import ContentEditable from "./util/contentEditable"
class Pastebin extends React.Component
@ -8,6 +9,7 @@ class Pastebin extends React.Component
text: ""
pasting: false
highlight: true
switchToUpload: false
onEditTextUpdate: (ev) =>
console.log ev.target.value
@ -52,6 +54,9 @@ class Pastebin extends React.Component
pasting: false
render: ->
if @state.switchToUpload
return <Redirect to="/paste/binary/" />
<div className="content-pastebin">
<ContentEditable
className="content-pastebin-edit"
@ -67,6 +72,13 @@ class Pastebin extends React.Component
>
Highlight: {if @state.highlight then 'ON' else 'OFF'}
</button>
<button
className="button-blue"
disabled={@state.pasting}
onClick={(ev) => @setState { switchToUpload: true }}
>
File Upload
</button>
<button
className="button-blue"
disabled={@state.pasting}

View file

@ -0,0 +1,39 @@
.container {
display: flex;
flex-direction: column;
flex: 1;
font-family: sans-serif;
text-align: left;
}
.container > p {
font-size: 1rem;
}
.container > em {
font-size: 0.8rem;
}
.dropzone {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
border-width: 2px;
border-radius: 2px;
border-color: #eeeeee;
border-style: dashed;
background-color: #fafafa;
color: #bdbdbd;
outline: none;
transition: border 0.24s ease-in-out;
}
.dropzone:focus {
border-color: #2196f3;
}
.dropzone.disabled {
opacity: 0.6;
}

View file

@ -15,6 +15,19 @@
box-shadow: $shadow-medium;
border-radius: $content-radius;
padding: $content-inset;
.switch-wrapper, .switch-wrapper > div {
width: 100%;
height: 100%;
}
.switch-wrapper {
position: relative;
}
.switch-wrapper > div {
position: absolute;
}
}
.content-buttons {

View file

@ -6,6 +6,7 @@
@import './home.scss';
@import './pastebin.scss';
@import './buttons.scss';
@import './dropzone.scss';
body, html {
margin: 0px;