mirror of https://github.com/kern/filepizza
Use alt for Flux.
parent
b642fa6588
commit
22ecf9cb12
@ -1,16 +0,0 @@
|
|||||||
import alt from './alt';
|
|
||||||
|
|
||||||
export default alt.createActions(class Actions {
|
|
||||||
constructor() {
|
|
||||||
this.generateActions(
|
|
||||||
'download',
|
|
||||||
'receiveData',
|
|
||||||
'requestDownload',
|
|
||||||
'sendToDownloader',
|
|
||||||
'setDownloadInfo',
|
|
||||||
'setPeerID',
|
|
||||||
'setUploadToken',
|
|
||||||
'upload'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
import DropZone from './DropZone';
|
|
||||||
import FileDescription from './FileDescription';
|
|
||||||
import React from 'react';
|
|
||||||
import Tempalink from './Tempalink';
|
|
||||||
import socket from './socket';
|
|
||||||
import Actions from './Actions';
|
|
||||||
import Store from './Store';
|
|
||||||
|
|
||||||
export default class App extends React.Component {
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
|
|
||||||
this.state = Store.getState();
|
|
||||||
this._onChange = function() {
|
|
||||||
this.setState(Store.getState());
|
|
||||||
}.bind(this);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
Store.listen(this._onChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUnmount() {
|
|
||||||
Store.unlisten(this._onChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
uploadFile(file) {
|
|
||||||
Actions.upload(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadFile() {
|
|
||||||
Actions.requestDownload();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
if (this.state.readyToUpload) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<FileDescription file={this.state.uploadFile} />
|
|
||||||
<Tempalink token={this.state.uploadToken} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
} else if (this.state.readyToDownload) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<FileDescription file={this.state.downloadMetadata} />
|
|
||||||
<button onClick={this.downloadFile.bind(this)}>Download</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return <DropZone onDrop={this.uploadFile.bind(this)} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,31 +1,35 @@
|
|||||||
|
const chunkSize = 32;
|
||||||
|
|
||||||
|
function blobLength(b) {
|
||||||
|
if (typeof b.byteLength !== 'undefined') return b.byteLength;
|
||||||
|
if (typeof b.size !== 'undefined') return b.size;
|
||||||
|
return b.length;
|
||||||
|
}
|
||||||
|
|
||||||
export default class ChunkedBlob {
|
export default class ChunkedBlob {
|
||||||
|
|
||||||
constructor(n) {
|
constructor() {
|
||||||
this.n = n;
|
this.count = 0;
|
||||||
this.count = n;
|
this.size = 0;
|
||||||
this.chunks = [];
|
this.chunks = [];
|
||||||
|
this.lastChunk = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
setChunk(i, b) {
|
add(b) {
|
||||||
if (i < 0 || i >= this.n)
|
this.count++;
|
||||||
throw new Error('Chunk out of range');
|
this.size += blobLength(b);
|
||||||
|
this.lastChunk.push(b);
|
||||||
|
|
||||||
if (this.chunks[i])
|
if (this.lastChunk.length === chunkSize) {
|
||||||
throw new Error('Chunk already set');
|
let chunk = new Blob(this.lastChunk);
|
||||||
|
this.chunks.push(chunk);
|
||||||
this.count--;
|
this.lastChunk = [];
|
||||||
this.chunks[i] = b;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ready() {
|
|
||||||
return this.count === 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toBlob() {
|
toBlob() {
|
||||||
if (!this.ready())
|
let allChunks = this.chunks.concat(this.lastChunk);
|
||||||
throw new Error('Incomplete blob');
|
return new Blob(allChunks);
|
||||||
|
|
||||||
return new Blob(this.chunks);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,38 @@
|
|||||||
|
import ChunkedBlob from './ChunkedBlob';
|
||||||
|
|
||||||
|
export default class DownloadFile {
|
||||||
|
|
||||||
|
constructor(name, size, type) {
|
||||||
|
this.name = name;
|
||||||
|
this.size = size;
|
||||||
|
this.type = type;
|
||||||
|
this.packets = new ChunkedBlob();
|
||||||
|
}
|
||||||
|
|
||||||
|
addPacket(b) {
|
||||||
|
this.packets.add(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearPackets() {
|
||||||
|
this.packets = new ChunkedBlob();
|
||||||
|
}
|
||||||
|
|
||||||
|
isComplete() {
|
||||||
|
return this.packets.size === this.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
getProgress() {
|
||||||
|
return this.packets.size / this.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
download() {
|
||||||
|
let blob = this.packets.toBlob();
|
||||||
|
let url = URL.createObjectURL(blob);
|
||||||
|
let a = document.createElement('a');
|
||||||
|
a.download = this.name;
|
||||||
|
a.href = url;
|
||||||
|
a.click();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
function statusPredicate(v) {
|
||||||
|
if (v.length === 1) {
|
||||||
|
return v.toUpperCase();
|
||||||
|
} else {
|
||||||
|
return 'is' + statusPredicate(v.charAt(0)) + v.substring(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Status {
|
||||||
|
|
||||||
|
constructor(values) {
|
||||||
|
this.value = values[0];
|
||||||
|
this.values = values;
|
||||||
|
|
||||||
|
for (let v of values) {
|
||||||
|
this[statusPredicate(v)] = function () {
|
||||||
|
return this.value === v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
set(value) {
|
||||||
|
if (this.values.indexOf(value) === -1)
|
||||||
|
throw new Error('Unknown value');
|
||||||
|
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,106 +0,0 @@
|
|||||||
import Actions from './Actions';
|
|
||||||
import ChunkedBlob from './ChunkedBlob';
|
|
||||||
import alt from './alt';
|
|
||||||
import peer from './peer';
|
|
||||||
import socket from './socket';
|
|
||||||
|
|
||||||
const chunkSize = 1024;
|
|
||||||
|
|
||||||
function downloadFile(name, blob) {
|
|
||||||
let url = URL.createObjectURL(blob);
|
|
||||||
let a = document.createElement('a');
|
|
||||||
a.download = name;
|
|
||||||
a.href = url;
|
|
||||||
a.click();
|
|
||||||
URL.revokeObjectURL(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default alt.createStore(class Store {
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.bindActions(Actions);
|
|
||||||
|
|
||||||
this.peerID = null;
|
|
||||||
|
|
||||||
this.uploadToken = null;
|
|
||||||
this.uploadFile = null;
|
|
||||||
this.readyToUpload = false;
|
|
||||||
|
|
||||||
this.downloadToken = null;
|
|
||||||
this.downloadMetadata = null;
|
|
||||||
this.downloadChunks = null;
|
|
||||||
this.readyToDownload = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateReadyStatus() {
|
|
||||||
this.readyToUpload = !!this.peerID && !!this.uploadToken && !!this.uploadFile;
|
|
||||||
this.readyToDownload = !!this.peerID && !!this.downloadToken && !!this.downloadMetadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
onSetPeerID(id) {
|
|
||||||
this.peerID = id;
|
|
||||||
this.updateReadyStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
onSetUploadToken(token) {
|
|
||||||
this.uploadToken = token;
|
|
||||||
this.updateReadyStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
onUpload(file) {
|
|
||||||
this.uploadFile = file;
|
|
||||||
this.updateReadyStatus();
|
|
||||||
|
|
||||||
socket.emit('upload', {
|
|
||||||
name: file.name,
|
|
||||||
size: file.size,
|
|
||||||
type: file.type
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onSetDownloadInfo(info) {
|
|
||||||
this.downloadToken = info.token;
|
|
||||||
this.downloadMetadata = info.metadata;
|
|
||||||
this.updateReadyStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
onRequestDownload() {
|
|
||||||
if (!this.readyToDownload) return;
|
|
||||||
|
|
||||||
socket.emit('download', {
|
|
||||||
peerID: this.peerID,
|
|
||||||
token: this.downloadToken
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onSendToDownloader(peerID) {
|
|
||||||
if (!this.readyToUpload) return;
|
|
||||||
|
|
||||||
let file = this.uploadFile;
|
|
||||||
let conn = peer.connect(peerID);
|
|
||||||
conn.on('open', function () {
|
|
||||||
let chunks = Math.ceil(file.size / chunkSize);
|
|
||||||
for (let i = 0; i < chunks; i++) {
|
|
||||||
let start = i * chunkSize;
|
|
||||||
let end = start + chunkSize;
|
|
||||||
let chunk = file.slice(start, end);
|
|
||||||
conn.send({ n: chunks, i: i, b: chunk });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onReceiveData(data) {
|
|
||||||
if (!this.readyToDownload) return;
|
|
||||||
|
|
||||||
if (!this.downloadChunks)
|
|
||||||
this.downloadChunks = new ChunkedBlob(data.n);
|
|
||||||
|
|
||||||
this.downloadChunks.setChunk(data.i, data.b);
|
|
||||||
|
|
||||||
if (this.downloadChunks.ready()) {
|
|
||||||
let blob = this.downloadChunks.toBlob();
|
|
||||||
downloadFile(this.downloadMetadata.name, blob);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
const packetSize = 16 * 1024;
|
||||||
|
|
||||||
|
export default class UploadFile {
|
||||||
|
|
||||||
|
constructor(file) {
|
||||||
|
this.name = file.name;
|
||||||
|
this.size = file.size;
|
||||||
|
this.type = file.type;
|
||||||
|
this.blob = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
countPackets() {
|
||||||
|
return Math.ceil(this.size / packetSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
getPacket(i) {
|
||||||
|
if (i < 0 || i >= this.countPackets())
|
||||||
|
throw new Error('Packet out of bounds');
|
||||||
|
|
||||||
|
let start = i * packetSize;
|
||||||
|
let end = Math.min(start + packetSize, this.size);
|
||||||
|
return this.blob.slice(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import alt from '../alt';
|
||||||
|
|
||||||
|
export default alt.createActions(class DownloadActions {
|
||||||
|
constructor() {
|
||||||
|
this.generateActions(
|
||||||
|
'beginDownload',
|
||||||
|
'requestDownload',
|
||||||
|
'cancelDownlaod',
|
||||||
|
'setDownloadInfo'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
import alt from '../alt';
|
||||||
|
|
||||||
|
export default alt.createActions(class PeerActions {
|
||||||
|
constructor() {
|
||||||
|
this.generateActions(
|
||||||
|
'setPeerID'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import alt from '../alt';
|
||||||
|
|
||||||
|
export default alt.createActions(class UploadActions {
|
||||||
|
constructor() {
|
||||||
|
this.generateActions(
|
||||||
|
'sendToDownloader',
|
||||||
|
'setUploadToken',
|
||||||
|
'uploadFile'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
import DownloadActions from '../actions/DownloadActions';
|
||||||
|
import DownloadStore from '../stores/DownloadStore';
|
||||||
|
import DropZone from './DropZone';
|
||||||
|
import FileDescription from './FileDescription';
|
||||||
|
import PeerStore from '../stores/PeerStore';
|
||||||
|
import React from 'react';
|
||||||
|
import Tempalink from './Tempalink';
|
||||||
|
import UploadActions from '../actions/UploadActions';
|
||||||
|
import UploadStore from '../stores/UploadStore';
|
||||||
|
|
||||||
|
function getState() {
|
||||||
|
return {
|
||||||
|
peerID: PeerStore.getPeerID(),
|
||||||
|
readyToUpload: UploadStore.getState().status.isUploading(),
|
||||||
|
uploadFile: UploadStore.getState().file,
|
||||||
|
uploadToken: UploadStore.getState().token,
|
||||||
|
downloadFile: DownloadStore.getState().file,
|
||||||
|
downloadToken: DownloadStore.getState().token,
|
||||||
|
readyToDownload: DownloadStore.getState().status.isReady()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class App extends React.Component {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
|
||||||
|
this.state = getState();
|
||||||
|
this._onChange = function() {
|
||||||
|
this.setState(getState());
|
||||||
|
}.bind(this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
PeerStore.listen(this._onChange);
|
||||||
|
UploadStore.listen(this._onChange);
|
||||||
|
DownloadStore.listen(this._onChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUnmount() {
|
||||||
|
PeerStore.unlisten(this._onChange);
|
||||||
|
UploadStore.unlisten(this._onChange);
|
||||||
|
DownloadStore.unlisten(this._onChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadFile(file) {
|
||||||
|
UploadActions.uploadFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadFile() {
|
||||||
|
DownloadActions.requestDownload();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.state.readyToUpload) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<FileDescription file={this.state.uploadFile} />
|
||||||
|
<Tempalink token={this.state.uploadToken} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (this.state.readyToDownload) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<FileDescription file={this.state.downloadFile} />
|
||||||
|
<button onClick={this.downloadFile.bind(this)}>Download</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <DropZone onDrop={this.uploadFile.bind(this)} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,6 +1,12 @@
|
|||||||
import App from './App';
|
import App from './components/App';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Actions from './Actions';
|
import DownloadActions from './actions/DownloadActions';
|
||||||
|
|
||||||
|
if (window.WebDrop) DownloadActions.setDownloadInfo({
|
||||||
|
token: window.WebDrop.token,
|
||||||
|
name: window.WebDrop.metadata.name,
|
||||||
|
size: window.WebDrop.metadata.size,
|
||||||
|
type: window.WebDrop.metadata.type
|
||||||
|
})
|
||||||
|
|
||||||
if (window.WebDrop) Actions.setDownloadInfo(window.WebDrop);
|
|
||||||
React.render(<App />, document.getElementById('app'));
|
React.render(<App />, document.getElementById('app'));
|
||||||
|
|||||||
@ -1,14 +1,13 @@
|
|||||||
import Actions from './Actions';
|
import DownloadActions from './actions/DownloadActions';
|
||||||
import Peer from 'peerjs';
|
import Peer from 'peerjs';
|
||||||
|
import PeerActions from './actions/PeerActions';
|
||||||
|
|
||||||
var peer = module.exports = new Peer({ key: '8w3x9m637e0o1or' });
|
var peer = module.exports = new Peer({ key: '8w3x9m637e0o1or' });
|
||||||
|
|
||||||
peer.on('open', function () {
|
peer.on('open', function () {
|
||||||
Actions.setPeerID(peer.id);
|
PeerActions.setPeerID(peer.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
peer.on('connection', function (conn) {
|
peer.on('connection', function (conn) {
|
||||||
conn.on('data', function (data) {
|
DownloadActions.beginDownload(conn);
|
||||||
Actions.receiveData(data);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,12 +1,8 @@
|
|||||||
import io from 'socket.io-client';
|
import io from 'socket.io-client';
|
||||||
import Actions from './Actions';
|
import UploadActions from './actions/UploadActions';
|
||||||
|
|
||||||
var socket = module.exports = io.connect(window.location.origin);
|
var socket = module.exports = io.connect();
|
||||||
|
|
||||||
socket.on('token', function (token) {
|
|
||||||
Actions.setUploadToken(token);
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on('download', function (peerID) {
|
socket.on('download', function (peerID) {
|
||||||
Actions.sendToDownloader(peerID);
|
UploadActions.sendToDownloader(peerID);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -0,0 +1,97 @@
|
|||||||
|
import DownloadActions from '../actions/DownloadActions';
|
||||||
|
import DownloadFile from '../DownloadFile';
|
||||||
|
import PeerActions from '../actions/PeerActions';
|
||||||
|
import PeerStore from './PeerStore';
|
||||||
|
import Status from '../Status';
|
||||||
|
import alt from '../alt';
|
||||||
|
import socket from '../socket';
|
||||||
|
|
||||||
|
class DownloadStatus extends Status {
|
||||||
|
constructor() {
|
||||||
|
super([
|
||||||
|
'offline',
|
||||||
|
'ready',
|
||||||
|
'requesting',
|
||||||
|
'downloading',
|
||||||
|
'cancelled',
|
||||||
|
'done'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default alt.createStore(class DownloadStore {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.bindActions(DownloadActions);
|
||||||
|
this.bindActions(PeerActions);
|
||||||
|
|
||||||
|
this.conn = null;
|
||||||
|
this.token = null;
|
||||||
|
this.file = null;
|
||||||
|
this.status = new DownloadStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
onSetPeerID() {
|
||||||
|
this.waitFor(PeerStore.dispatchToken);
|
||||||
|
if (this.status.isOffline() && this.token) this.status.set('ready');
|
||||||
|
}
|
||||||
|
|
||||||
|
onSetDownloadInfo(info) {
|
||||||
|
this.token = info.token;
|
||||||
|
this.file = new DownloadFile(info.name, info.size, info.type);
|
||||||
|
if (this.status.isOffline() && PeerStore.getPeerID()) this.status.set('ready');
|
||||||
|
}
|
||||||
|
|
||||||
|
onRequestDownload() {
|
||||||
|
if (!this.status.isReady()) return;
|
||||||
|
this.status.set('requesting');
|
||||||
|
|
||||||
|
socket.emit('download', {
|
||||||
|
peerID: PeerStore.getPeerID(),
|
||||||
|
token: this.token
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeginDownload(conn) {
|
||||||
|
if (!this.status.isRequesting()) return;
|
||||||
|
this.status.set('downloading');
|
||||||
|
|
||||||
|
this.conn = conn;
|
||||||
|
let chunkSize = conn.metadata.chunkSize;
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
conn.on('data', (data) => {
|
||||||
|
if (!this.status.isDownloading()) return;
|
||||||
|
|
||||||
|
this.file.addPacket(data);
|
||||||
|
i++;
|
||||||
|
|
||||||
|
if (this.file.isComplete()) {
|
||||||
|
this.status.set('done');
|
||||||
|
this.file.download();
|
||||||
|
conn.close();
|
||||||
|
} else if (i % chunkSize === 0) {
|
||||||
|
conn.send('more');
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
conn.on('close', () => {
|
||||||
|
if (!this.status.isDownloading()) return;
|
||||||
|
this._cancel();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onCancelDownload() {
|
||||||
|
if (!this.status.isRequesting() && !this.status.isDownloading()) return;
|
||||||
|
this._cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
_cancel() {
|
||||||
|
this.status.set('cancelled');
|
||||||
|
if (this.conn) this.conn.close();
|
||||||
|
this.conn = null;
|
||||||
|
this.file.clearPackets();
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
import PeerActions from '../actions/PeerActions';
|
||||||
|
import alt from '../alt';
|
||||||
|
|
||||||
|
export default alt.createStore(class PeerStore {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.bindActions(PeerActions);
|
||||||
|
this.peerID = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSetPeerID(id) {
|
||||||
|
this.peerID = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getPeerID() {
|
||||||
|
return this.getState().peerID;
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
import PeerActions from '../actions/PeerActions';
|
||||||
|
import PeerStore from './PeerStore';
|
||||||
|
import Status from '../Status';
|
||||||
|
import UploadActions from '../actions/UploadActions';
|
||||||
|
import UploadFile from '../UploadFile';
|
||||||
|
import alt from '../alt';
|
||||||
|
import peer from '../peer';
|
||||||
|
import socket from '../socket';
|
||||||
|
|
||||||
|
const chunkSize = 32;
|
||||||
|
|
||||||
|
class UploadStatus extends Status {
|
||||||
|
constructor() {
|
||||||
|
super([
|
||||||
|
'offline',
|
||||||
|
'ready',
|
||||||
|
'processing',
|
||||||
|
'uploading'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default alt.createStore(class UploadStore {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.bindActions(PeerActions);
|
||||||
|
this.bindActions(UploadActions);
|
||||||
|
|
||||||
|
this.token = null;
|
||||||
|
this.file = null;
|
||||||
|
this.status = new UploadStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
onSetPeerID(id) {
|
||||||
|
this.waitFor(PeerStore.dispatchToken);
|
||||||
|
if (this.status.isOffline()) this.status.set('ready');
|
||||||
|
}
|
||||||
|
|
||||||
|
onUploadFile(file) {
|
||||||
|
if (!this.status.isReady()) return;
|
||||||
|
this.status.set('processing');
|
||||||
|
|
||||||
|
this.file = new UploadFile(file);
|
||||||
|
socket.emit('upload', {
|
||||||
|
name: this.file.name,
|
||||||
|
size: this.file.size,
|
||||||
|
type: this.file.type
|
||||||
|
}, (token) => {
|
||||||
|
this.status.set('uploading');
|
||||||
|
this.token = token;
|
||||||
|
this.emitChange();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSendToDownloader(peerID) {
|
||||||
|
if (!this.status.isUploading()) return;
|
||||||
|
|
||||||
|
let file = this.file;
|
||||||
|
let conn = peer.connect(peerID, {
|
||||||
|
reliable: true,
|
||||||
|
metadata: { chunkSize: chunkSize }
|
||||||
|
});
|
||||||
|
|
||||||
|
conn.on('open', function () {
|
||||||
|
|
||||||
|
let packets = file.countPackets();
|
||||||
|
let packet = 0;
|
||||||
|
|
||||||
|
function sendNextChunk() {
|
||||||
|
for (let i = 0; i < chunkSize; i++) {
|
||||||
|
if (packet >= packets) break;
|
||||||
|
let b = file.getPacket(packet);
|
||||||
|
conn.send(b);
|
||||||
|
packet++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.on('data', function (data) {
|
||||||
|
if (data === 'more') sendNextChunk();
|
||||||
|
});
|
||||||
|
|
||||||
|
sendNextChunk();
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue