2018-08-08 00:40:17 +02:00
|
|
|
/* global DEFAULTS LIMITS */
|
2017-08-24 23:54:02 +02:00
|
|
|
import FileSender from './fileSender';
|
|
|
|
import FileReceiver from './fileReceiver';
|
2018-07-31 20:09:18 +02:00
|
|
|
import { copyToClipboard, delay, openLinksInNewTab, percent } from './utils';
|
2017-08-24 23:54:02 +02:00
|
|
|
import * as metrics from './metrics';
|
2018-07-31 20:09:18 +02:00
|
|
|
import Archive from './archive';
|
2018-08-03 21:24:41 +02:00
|
|
|
import { bytes } from './utils';
|
2018-10-25 04:32:53 +02:00
|
|
|
import okDialog from './ui/okDialog';
|
2018-10-26 03:55:11 +02:00
|
|
|
import copyDialog from './ui/copyDialog';
|
2017-08-24 23:54:02 +02:00
|
|
|
|
|
|
|
export default function(state, emitter) {
|
|
|
|
let lastRender = 0;
|
2017-09-13 21:01:55 +02:00
|
|
|
let updateTitle = false;
|
2017-08-24 23:54:02 +02:00
|
|
|
|
|
|
|
function render() {
|
|
|
|
emitter.emit('render');
|
|
|
|
}
|
|
|
|
|
|
|
|
async function checkFiles() {
|
2018-08-08 00:40:17 +02:00
|
|
|
const changes = await state.user.syncFileList();
|
|
|
|
const rerender = changes.incoming || changes.downloadCount;
|
2017-08-24 23:54:02 +02:00
|
|
|
if (rerender) {
|
|
|
|
render();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-13 21:01:55 +02:00
|
|
|
function updateProgress() {
|
|
|
|
if (updateTitle) {
|
|
|
|
emitter.emit('DOMTitleChange', percent(state.transfer.progressRatio));
|
|
|
|
}
|
|
|
|
render();
|
|
|
|
}
|
|
|
|
|
|
|
|
emitter.on('DOMContentLoaded', () => {
|
|
|
|
document.addEventListener('blur', () => (updateTitle = true));
|
|
|
|
document.addEventListener('focus', () => {
|
|
|
|
updateTitle = false;
|
|
|
|
emitter.emit('DOMTitleChange', 'Firefox Send');
|
|
|
|
});
|
|
|
|
checkFiles();
|
|
|
|
});
|
2017-08-24 23:54:02 +02:00
|
|
|
|
2018-06-25 23:01:08 +02:00
|
|
|
emitter.on('navigate', checkFiles);
|
2017-08-24 23:54:02 +02:00
|
|
|
|
|
|
|
emitter.on('render', () => {
|
|
|
|
lastRender = Date.now();
|
|
|
|
});
|
|
|
|
|
2018-09-24 21:01:39 +02:00
|
|
|
emitter.on('login', email => {
|
|
|
|
state.user.login(email);
|
2018-08-08 00:40:17 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
emitter.on('logout', () => {
|
|
|
|
state.user.logout();
|
2018-09-25 00:25:08 +02:00
|
|
|
state.timeLimit = DEFAULTS.EXPIRE_SECONDS;
|
|
|
|
state.downloadCount = 1;
|
2018-08-31 23:20:15 +02:00
|
|
|
emitter.emit('pushState', '/');
|
2018-08-08 00:40:17 +02:00
|
|
|
});
|
|
|
|
|
2017-11-30 22:41:09 +01:00
|
|
|
emitter.on('changeLimit', async ({ file, value }) => {
|
2018-08-31 19:59:26 +02:00
|
|
|
const ok = await file.changeLimit(value, state.user);
|
|
|
|
if (!ok) {
|
|
|
|
// TODO
|
|
|
|
return;
|
|
|
|
}
|
2018-01-24 19:23:13 +01:00
|
|
|
state.storage.writeFile(file);
|
2017-11-30 22:41:09 +01:00
|
|
|
metrics.changedDownloadLimit(file);
|
|
|
|
});
|
|
|
|
|
2018-10-25 04:07:10 +02:00
|
|
|
emitter.on('removeUpload', file => {
|
|
|
|
state.archive.remove(file);
|
|
|
|
if (state.archive.numFiles === 0) {
|
|
|
|
state.archive = null;
|
|
|
|
}
|
2018-08-03 21:24:41 +02:00
|
|
|
render();
|
2018-07-31 20:09:18 +02:00
|
|
|
});
|
|
|
|
|
2017-08-24 23:54:02 +02:00
|
|
|
emitter.on('delete', async ({ file, location }) => {
|
|
|
|
try {
|
|
|
|
metrics.deletedUpload({
|
|
|
|
size: file.size,
|
|
|
|
time: file.time,
|
|
|
|
speed: file.speed,
|
|
|
|
type: file.type,
|
|
|
|
ttl: file.expiresAt - Date.now(),
|
|
|
|
location
|
|
|
|
});
|
|
|
|
state.storage.remove(file.id);
|
2018-01-24 19:23:13 +01:00
|
|
|
await file.del();
|
2017-08-24 23:54:02 +02:00
|
|
|
} catch (e) {
|
|
|
|
state.raven.captureException(e);
|
|
|
|
}
|
2018-10-25 04:07:10 +02:00
|
|
|
render();
|
2017-08-24 23:54:02 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
emitter.on('cancel', () => {
|
|
|
|
state.transfer.cancel();
|
|
|
|
});
|
|
|
|
|
2018-07-31 20:09:18 +02:00
|
|
|
emitter.on('addFiles', async ({ files }) => {
|
2018-11-19 19:48:52 +01:00
|
|
|
if (files.length < 1) {
|
|
|
|
return;
|
|
|
|
}
|
2018-08-08 00:40:17 +02:00
|
|
|
const maxSize = state.user.maxSize;
|
|
|
|
state.archive = state.archive || new Archive();
|
|
|
|
try {
|
|
|
|
state.archive.addFiles(files, maxSize);
|
|
|
|
} catch (e) {
|
2018-09-07 19:53:40 +02:00
|
|
|
state.modal = okDialog(
|
2018-08-08 00:40:17 +02:00
|
|
|
state.translate(e.message, {
|
|
|
|
size: bytes(maxSize),
|
|
|
|
count: LIMITS.MAX_FILES_PER_ARCHIVE
|
|
|
|
})
|
|
|
|
);
|
2018-10-25 04:32:53 +02:00
|
|
|
if (state.archive.numFiles === 0) {
|
|
|
|
state.archive = null;
|
|
|
|
}
|
2018-07-31 20:09:18 +02:00
|
|
|
}
|
|
|
|
render();
|
|
|
|
});
|
|
|
|
|
2018-08-31 23:20:15 +02:00
|
|
|
emitter.on('upload', async ({ type, dlimit, password }) => {
|
2018-08-03 21:24:41 +02:00
|
|
|
if (!state.archive) return;
|
2018-08-08 00:40:17 +02:00
|
|
|
if (state.storage.files.length >= LIMITS.MAX_ARCHIVES_PER_USER) {
|
2018-09-07 19:53:40 +02:00
|
|
|
state.modal = okDialog(
|
2018-08-08 00:40:17 +02:00
|
|
|
state.translate('tooManyArchives', {
|
|
|
|
count: LIMITS.MAX_ARCHIVES_PER_USER
|
|
|
|
})
|
|
|
|
);
|
2018-09-07 19:53:40 +02:00
|
|
|
return render();
|
2018-08-08 00:40:17 +02:00
|
|
|
}
|
2018-08-03 21:24:41 +02:00
|
|
|
const size = state.archive.size;
|
2018-08-08 00:40:17 +02:00
|
|
|
if (!state.timeLimit) state.timeLimit = DEFAULTS.EXPIRE_SECONDS;
|
2018-08-31 23:20:15 +02:00
|
|
|
const sender = new FileSender();
|
2018-08-08 20:07:09 +02:00
|
|
|
|
2017-09-13 21:01:55 +02:00
|
|
|
sender.on('progress', updateProgress);
|
2017-08-24 23:54:02 +02:00
|
|
|
sender.on('encrypting', render);
|
2018-07-31 20:09:18 +02:00
|
|
|
sender.on('complete', render);
|
2017-08-24 23:54:02 +02:00
|
|
|
state.transfer = sender;
|
2018-02-01 00:47:34 +01:00
|
|
|
state.uploading = true;
|
2017-08-24 23:54:02 +02:00
|
|
|
render();
|
2018-01-24 19:23:13 +01:00
|
|
|
|
2017-08-24 23:54:02 +02:00
|
|
|
const links = openLinksInNewTab();
|
|
|
|
await delay(200);
|
|
|
|
try {
|
|
|
|
metrics.startedUpload({ size, type });
|
2018-07-31 20:09:18 +02:00
|
|
|
|
2018-08-31 23:20:15 +02:00
|
|
|
const ownedFile = await sender.upload(
|
|
|
|
state.archive,
|
|
|
|
state.timeLimit,
|
|
|
|
dlimit,
|
|
|
|
state.user.bearerToken
|
|
|
|
);
|
2018-02-02 19:15:17 +01:00
|
|
|
ownedFile.type = type;
|
2018-01-24 19:23:13 +01:00
|
|
|
state.storage.totalUploads += 1;
|
|
|
|
metrics.completedUpload(ownedFile);
|
|
|
|
|
|
|
|
state.storage.addFile(ownedFile);
|
2018-08-31 23:20:15 +02:00
|
|
|
// TODO integrate password into /upload request
|
2018-07-31 20:09:18 +02:00
|
|
|
if (password) {
|
|
|
|
emitter.emit('password', { password, file: ownedFile });
|
|
|
|
}
|
2018-10-29 17:52:24 +01:00
|
|
|
state.modal = copyDialog(ownedFile.name, ownedFile.url);
|
2017-08-24 23:54:02 +02:00
|
|
|
} catch (err) {
|
|
|
|
if (err.message === '0') {
|
|
|
|
//cancelled. do nothing
|
|
|
|
metrics.cancelledUpload({ size, type });
|
2018-03-12 18:15:11 +01:00
|
|
|
render();
|
|
|
|
} else {
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
console.error(err);
|
|
|
|
state.raven.captureException(err);
|
|
|
|
metrics.stoppedUpload({ size, type, err });
|
|
|
|
emitter.emit('pushState', '/error');
|
2017-08-24 23:54:02 +02:00
|
|
|
}
|
2018-02-01 00:47:34 +01:00
|
|
|
} finally {
|
2018-03-12 18:15:11 +01:00
|
|
|
openLinksInNewTab(links, false);
|
2018-08-08 20:07:09 +02:00
|
|
|
state.archive = null;
|
2018-07-31 20:09:18 +02:00
|
|
|
state.password = '';
|
2018-02-01 00:47:34 +01:00
|
|
|
state.uploading = false;
|
|
|
|
state.transfer = null;
|
2018-10-25 04:07:10 +02:00
|
|
|
render();
|
2017-08-24 23:54:02 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2018-01-24 19:23:13 +01:00
|
|
|
emitter.on('password', async ({ password, file }) => {
|
2017-08-31 18:43:36 +02:00
|
|
|
try {
|
2018-02-16 21:56:53 +01:00
|
|
|
state.settingPassword = true;
|
|
|
|
render();
|
2018-01-24 19:23:13 +01:00
|
|
|
await file.setPassword(password);
|
|
|
|
state.storage.writeFile(file);
|
2017-08-31 18:43:36 +02:00
|
|
|
metrics.addedPassword({ size: file.size });
|
2018-02-16 21:56:53 +01:00
|
|
|
await delay(1000);
|
2018-01-24 19:23:13 +01:00
|
|
|
} catch (err) {
|
2018-03-02 06:36:45 +01:00
|
|
|
// eslint-disable-next-line no-console
|
2018-01-24 19:23:13 +01:00
|
|
|
console.error(err);
|
2018-02-21 21:35:52 +01:00
|
|
|
state.passwordSetError = err;
|
|
|
|
} finally {
|
|
|
|
state.settingPassword = false;
|
2017-08-31 18:43:36 +02:00
|
|
|
}
|
|
|
|
render();
|
|
|
|
});
|
|
|
|
|
2018-01-24 19:23:13 +01:00
|
|
|
emitter.on('getMetadata', async () => {
|
2017-08-31 18:43:36 +02:00
|
|
|
const file = state.fileInfo;
|
2018-07-07 00:49:50 +02:00
|
|
|
|
2018-01-24 19:23:13 +01:00
|
|
|
const receiver = new FileReceiver(file);
|
2017-08-31 18:43:36 +02:00
|
|
|
try {
|
2018-01-24 19:23:13 +01:00
|
|
|
await receiver.getMetadata();
|
|
|
|
state.transfer = receiver;
|
2017-08-31 18:43:36 +02:00
|
|
|
} catch (e) {
|
2018-08-08 00:40:17 +02:00
|
|
|
if (e.message === '401' || e.message === '404') {
|
2017-08-31 18:43:36 +02:00
|
|
|
file.password = null;
|
2018-01-24 19:23:13 +01:00
|
|
|
if (!file.requiresPassword) {
|
2017-08-31 18:43:36 +02:00
|
|
|
return emitter.emit('pushState', '/404');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-07-05 21:40:49 +02:00
|
|
|
|
2017-08-24 23:54:02 +02:00
|
|
|
render();
|
2017-08-31 18:43:36 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
emitter.on('download', async file => {
|
2018-02-24 20:24:12 +01:00
|
|
|
state.transfer.on('progress', updateProgress);
|
2017-08-31 18:43:36 +02:00
|
|
|
state.transfer.on('decrypting', render);
|
2018-07-31 20:09:18 +02:00
|
|
|
state.transfer.on('complete', render);
|
2017-08-31 18:43:36 +02:00
|
|
|
const links = openLinksInNewTab();
|
|
|
|
const size = file.size;
|
2017-08-24 23:54:02 +02:00
|
|
|
try {
|
|
|
|
const start = Date.now();
|
|
|
|
metrics.startedDownload({ size: file.size, ttl: file.ttl });
|
2018-07-31 20:29:26 +02:00
|
|
|
const dl = state.transfer.download({
|
|
|
|
stream: state.capabilities.streamDownload
|
|
|
|
});
|
2018-02-24 20:24:12 +01:00
|
|
|
render();
|
|
|
|
await dl;
|
2017-08-24 23:54:02 +02:00
|
|
|
const time = Date.now() - start;
|
|
|
|
const speed = size / (time / 1000);
|
|
|
|
state.storage.totalDownloads += 1;
|
|
|
|
metrics.completedDownload({ size, time, speed });
|
|
|
|
} catch (err) {
|
2018-01-24 19:23:13 +01:00
|
|
|
if (err.message === '0') {
|
|
|
|
// download cancelled
|
2018-02-05 18:11:42 +01:00
|
|
|
state.transfer.reset();
|
2018-03-12 18:15:11 +01:00
|
|
|
render();
|
|
|
|
} else {
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
console.error(err);
|
|
|
|
state.transfer = null;
|
|
|
|
const location = err.message === '404' ? '/404' : '/error';
|
|
|
|
if (location === '/error') {
|
|
|
|
state.raven.captureException(err);
|
|
|
|
metrics.stoppedDownload({ size, err });
|
|
|
|
}
|
|
|
|
emitter.emit('pushState', location);
|
2017-08-24 23:54:02 +02:00
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
openLinksInNewTab(links, false);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
emitter.on('copy', ({ url, location }) => {
|
|
|
|
copyToClipboard(url);
|
|
|
|
metrics.copiedLink({ location });
|
|
|
|
});
|
|
|
|
|
2018-01-24 19:23:13 +01:00
|
|
|
setInterval(() => {
|
|
|
|
// poll for updates of the download counts
|
|
|
|
// TODO something for the share page: || state.route === '/share/:id'
|
|
|
|
if (state.route === '/') {
|
|
|
|
checkFiles();
|
|
|
|
}
|
|
|
|
}, 2 * 60 * 1000);
|
|
|
|
|
2017-08-24 23:54:02 +02:00
|
|
|
setInterval(() => {
|
|
|
|
// poll for rerendering the file list countdown timers
|
|
|
|
if (
|
|
|
|
state.route === '/' &&
|
|
|
|
state.storage.files.length > 0 &&
|
|
|
|
Date.now() - lastRender > 30000
|
|
|
|
) {
|
|
|
|
render();
|
|
|
|
}
|
|
|
|
}, 60000);
|
|
|
|
}
|