Skip to content

Instantly share code, notes, and snippets.

@cowboyd
Created January 31, 2019 14:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cowboyd/d2fd3856cc4824abb20613efa15f7fdb to your computer and use it in GitHub Desktop.
Save cowboyd/d2fd3856cc4824abb20613efa15f7fdb to your computer and use it in GitHub Desktop.
import React, { Component } from 'react';
import Store from './store'
import Uploader from './uploader';
import UploaderController from './uploader-controller';
import { valueOf } from 'microstates';
import { append } from 'funcadelic';
import 'react-json-pretty/themes/monikai.css';
import JSONPretty from 'react-json-pretty';
class App extends Component {
render() {
return (
<div className="App">
<p>
Select one or more files to upload to https://api.frontside.io/dev/null
</p>
<Store type={Uploader} value={{ uploads: [] }} onChange={UploaderController}>
{uploader => {
let { ratioCompleted, percentageCompleted } = uploader;
return (
<div>
<input type="file" multiple onChange={e => uploader.addFiles(e.target.files)}/>
<div>
<JSONPretty data={append(valueOf(uploader), { ratioCompleted, percentageCompleted })}/>
</div>
</div>
);
}}
</Store>
</div>
);
}
}
export default App;
import Union from './union';
class Upload {
get file() {
return this.state.file;
}
get ratioCompleted() {
let { progress } = this.state;
if (progress) {
return progress.loaded / progress.total;
} else {
return 0;
}
}
get percentageCompleted() {
return this.ratioCompleted.toFixed(2) * 100;
}
get runningTimeMillis() {
let { startedAt, endedAt } = this.state;
if (startedAt) {
if (endedAt) {
return endedAt - startedAt;
} else {
return Date.now() - startedAt;
}
} else {
return 0;
}
}
}
export default Union({
New: Upload => class extends Upload {
start() {
return this.toStarted({
startedAt: Date.now(),
progress: {
lengthComputable: true,
loaded: 0,
total: this.file.size
}
});
}
},
Started: Upload => class extends Upload {
progress(progress) {
return this.toStarted({ progress });
}
finish(xhr) {
let { response, responseType } = xhr;
return this.toFinished({
response,
responseType,
endedAt: Date.now(),
})
}
error(error) {
return this.toErrored({
error,
endedAt: Date.now()
});
}
cancel() {
return this.toCancelled();
}
abort() {
return this.toAborted({
endedAt: Date.now()
});
}
},
Cancelled: Upload => class extends Upload {},
Finished: Upload => class extends Upload {},
Errored: Upload => class extends Upload {},
Aborted: Upload => class extends Upload {}
}, Upload);
import { current, valueOf } from 'microstates';
const URL = 'https://api.frontside.io/v1/dev/null';
// This is just a normal store callback.
// The current(function) accepts a microstate reference and returns the
// most recent version of it.
export default function UploaderController(uploader) {
for (let upload of uploader.uploads) {
if (upload.isNew) {
let xhr = new XMLHttpRequest();
xhr.open('POST', URL);
xhr.onload = () => current(upload).finish(xhr)
xhr.upload.onprogress = ({ lengthComputable, loaded, total }) => {
current(upload).progress({ lengthComputable, loaded, total });
};
xhr.onabort = () => current(upload).abort(xhr);
xhr.send(upload.file);
upload.start()
}
}
}
import { reduce } from 'microstates';
import Upload from './upload';
export default class Uploader {
uploads = [Upload];
get ratioCompleted() {
return reduce(this.uploads, (sum, upload) => {
return sum + upload.ratioCompleted / this.uploads.length;
}, 0);
}
get percentageCompleted() {
return this.ratioCompleted.toFixed(2) * 100;
}
addFiles(files) {
return reduce(files, (uploader, file) => {
let upload = Upload.New.create({ file });
return uploader.uploads.push(upload);
}, this);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment