Merge pull request #452 from mozilla/refactor-metrics

refactored metrics
This commit is contained in:
Danny Coates 2017-08-06 09:00:16 -07:00 committed by GitHub
commit 6d17b86d28
8 changed files with 330 additions and 258 deletions

View File

@ -1,61 +1,22 @@
const testPilotGA = require('testpilot-ga');
const Raven = require('raven-js'); const Raven = require('raven-js');
const { unsupported } = require('./metrics');
if (navigator.doNotTrack !== '1' && window.RAVEN_CONFIG) { if (navigator.doNotTrack !== '1' && window.RAVEN_CONFIG) {
Raven.config(window.SENTRY_ID, window.RAVEN_CONFIG).install(); Raven.config(window.SENTRY_ID, window.RAVEN_CONFIG).install();
} }
const analytics = new testPilotGA({
an: 'Firefox Send',
ds: 'web',
tid: window.GOOGLE_ANALYTICS_ID
});
function sendEvent() {
return analytics.sendEvent.apply(analytics, arguments).catch(() => 0);
}
function findMetric(href) {
switch (href) {
case 'https://www.mozilla.org/':
return 'mozilla';
case 'https://www.mozilla.org/about/legal':
return 'legal';
case 'https://testpilot.firefox.com/about':
return 'about';
case 'https://testpilot.firefox.com/privacy':
return 'privacy';
case 'https://testpilot.firefox.com/terms':
return 'terms';
case 'https://www.mozilla.org/privacy/websites/#cookies':
return 'cookies';
case 'https://github.com/mozilla/send':
return 'github';
case 'https://twitter.com/FxTestPilot':
return 'twitter';
case 'https://www.mozilla.org/firefox/new/?scene=2':
return 'download-firefox';
default:
return 'other';
}
}
const ua = navigator.userAgent.toLowerCase(); const ua = navigator.userAgent.toLowerCase();
if ( if (
ua.indexOf('firefox') > -1 && ua.indexOf('firefox') > -1 &&
parseInt(ua.match(/firefox\/*([^\n\r]*)\./)[1], 10) <= 49 parseInt(ua.match(/firefox\/*([^\n\r]*)\./)[1], 10) <= 49
) { ) {
const isSender = !location.pathname.includes('/download'); unsupported({
const ec = isSender ? 'sender' : 'recipient'; err: new Error('Firefox is outdated.')
sendEvent(ec, 'unsupported', {
cd6: new Error('Firefox is outdated.')
}).then(() => { }).then(() => {
location.replace('/unsupported/outdated'); location.replace('/unsupported/outdated');
}); });
} }
module.exports = { module.exports = {
Raven, Raven
sendEvent,
findMetric
}; };

View File

@ -1,10 +1,11 @@
const { Raven, findMetric, sendEvent } = require('./common'); const { Raven } = require('./common');
const FileReceiver = require('./fileReceiver'); const FileReceiver = require('./fileReceiver');
const { notify, gcmCompliant } = require('./utils'); const { notify, gcmCompliant } = require('./utils');
const bytes = require('bytes'); const bytes = require('bytes');
const Storage = require('./storage'); const Storage = require('./storage');
const storage = new Storage(localStorage); const storage = new Storage(localStorage);
const links = require('./links'); const links = require('./links');
const metrics = require('./metrics');
const $ = require('jquery'); const $ = require('jquery');
require('jquery-circle-progress'); require('jquery-circle-progress');
@ -13,28 +14,13 @@ $(() => {
gcmCompliant() gcmCompliant()
.then(() => { .then(() => {
const $downloadBtn = $('#download-btn'); const $downloadBtn = $('#download-btn');
const $sendNew = $('.send-new');
const $dlProgress = $('#dl-progress'); const $dlProgress = $('#dl-progress');
const $progressText = $('.progress-text'); const $progressText = $('.progress-text');
const $title = $('.title'); const $title = $('.title');
$sendNew.on('click', () => {
sendEvent('recipient', 'restarted', {
cd2: 'completed'
});
});
$('.legal-links a, .social-links a, #dl-firefox').on('click', function(target) {
const metric = findMetric(target.currentTarget.href);
// record exited event by recipient
sendEvent('recipient', 'exited', {
cd3: metric
});
});
const filename = $('#dl-filename').text(); const filename = $('#dl-filename').text();
const bytelength = Number($('#dl-bytelength').text()); const size = Number($('#dl-size').text());
const timeToExpiry = Number($('#dl-ttl').text()); const ttl = Number($('#dl-ttl').text());
//initiate progress bar //initiate progress bar
$dlProgress.circleProgress({ $dlProgress.circleProgress({
@ -50,22 +36,11 @@ $(() => {
$downloadBtn.attr('disabled', 'disabled'); $downloadBtn.attr('disabled', 'disabled');
links.setOpenInNewTab(true); links.setOpenInNewTab(true);
storage.totalDownloads += 1;
const fileReceiver = new FileReceiver(); const fileReceiver = new FileReceiver();
const unexpiredFiles = storage.numFiles;
fileReceiver.on('progress', progress => { fileReceiver.on('progress', progress => {
window.onunload = function() { window.onunload = function() {
storage.referrer = 'cancelled-download'; metrics.cancelledDownload({ size });
// record download-stopped (cancelled by tab close or reload)
sendEvent('recipient', 'download-stopped', {
cm1: bytelength,
cm5: storage.totalUploads,
cm6: unexpiredFiles,
cm7: storage.totalDownloads,
cd2: 'cancelled'
});
}; };
$('#download-page-one').attr('hidden', true); $('#download-page-one').attr('hidden', true);
@ -115,27 +90,12 @@ $(() => {
const startTime = Date.now(); const startTime = Date.now();
// record download-started by recipient metrics.startedDownload({ size, ttl });
sendEvent('recipient', 'download-started', {
cm1: bytelength,
cm4: timeToExpiry,
cm5: storage.totalUploads,
cm6: unexpiredFiles,
cm7: storage.totalDownloads
});
fileReceiver fileReceiver
.download() .download()
.catch(err => { .catch(err => {
// record download-stopped (errored) by recipient metrics.stoppedDownload({ size, err });
sendEvent('recipient', 'download-stopped', {
cm1: bytelength,
cm5: storage.totalUploads,
cm6: unexpiredFiles,
cm7: storage.totalDownloads,
cd2: 'errored',
cd6: err
});
if (err.message === 'notfound') { if (err.message === 'notfound') {
location.reload(); location.reload();
@ -150,21 +110,11 @@ $(() => {
}) })
.then(([decrypted, fname]) => { .then(([decrypted, fname]) => {
const endTime = Date.now(); const endTime = Date.now();
const totalTime = endTime - startTime; const time = endTime - startTime;
const downloadTime = endTime - downloadEnd; const downloadTime = endTime - downloadEnd;
const downloadSpeed = bytelength / (downloadTime / 1000); const speed = size / (downloadTime / 1000);
storage.totalDownloads += 1;
storage.referrer = 'completed-download'; metrics.completedDownload({ size, time, speed });
// record download-stopped (completed) by recipient
sendEvent('recipient', 'download-stopped', {
cm1: bytelength,
cm2: totalTime,
cm3: downloadSpeed,
cm5: storage.totalUploads,
cm6: unexpiredFiles,
cm7: storage.totalDownloads,
cd2: 'completed'
});
const dataView = new DataView(decrypted); const dataView = new DataView(decrypted);
const blob = new Blob([dataView]); const blob = new Blob([dataView]);
@ -186,14 +136,12 @@ $(() => {
return Promise.reject(err); return Promise.reject(err);
}) })
.then(() => links.setOpenInNewTab(false)); .then(() => links.setOpenInNewTab(false));
} };
$downloadBtn.on('click', download); $downloadBtn.on('click', download);
}) })
.catch(err => { .catch(err => {
sendEvent('sender', 'unsupported', { metrics.unsupported({ err }).then(() => {
cd6: err
}).then(() => {
location.replace('/unsupported/gcm'); location.replace('/unsupported/gcm');
}); });
}); });

234
frontend/src/metrics.js Normal file
View File

@ -0,0 +1,234 @@
const testPilotGA = require('testpilot-ga');
const Storage = require('./storage');
const storage = new Storage(localStorage);
const analytics = new testPilotGA({
an: 'Firefox Send',
ds: 'web',
tid: window.GOOGLE_ANALYTICS_ID
});
const category = location.pathname.includes('/download')
? 'recipient'
: 'sender';
document.addEventListener('DOMContentLoaded', function() {
addExitHandlers();
addRestartHandlers();
});
function sendEvent() {
return analytics.sendEvent.apply(analytics, arguments).catch(() => 0);
}
function urlToMetric(url) {
switch (url) {
case 'https://www.mozilla.org/':
return 'mozilla';
case 'https://www.mozilla.org/about/legal':
return 'legal';
case 'https://testpilot.firefox.com/about':
return 'about';
case 'https://testpilot.firefox.com/privacy':
return 'privacy';
case 'https://testpilot.firefox.com/terms':
return 'terms';
case 'https://www.mozilla.org/privacy/websites/#cookies':
return 'cookies';
case 'https://github.com/mozilla/send':
return 'github';
case 'https://twitter.com/FxTestPilot':
return 'twitter';
case 'https://www.mozilla.org/firefox/new/?scene=2':
return 'download-firefox';
default:
return 'other';
}
}
function setReferrer(state) {
if (category === 'sender') {
if (state) {
storage.referrer = `${state}-upload`;
}
} else if (category === 'recipient') {
if (state) {
storage.referrer = `${state}-download`;
}
}
}
function externalReferrer() {
if (/^https:\/\/testpilot\.firefox\.com/.test(document.referrer)) {
return 'testpilot';
}
return 'external';
}
function takeReferrer() {
const referrer = storage.referrer || externalReferrer();
storage.referrer = null;
return referrer;
}
function startedUpload(params) {
return sendEvent(category, 'upload-started', {
cm1: params.size,
cm5: storage.totalUploads,
cm6: storage.numFiles + 1,
cm7: storage.totalDownloads,
cd1: params.type,
cd5: takeReferrer()
});
}
function cancelledUpload(params) {
setReferrer('cancelled');
return sendEvent(category, 'upload-stopped', {
cm1: params.size,
cm5: storage.totalUploads,
cm6: storage.numFiles,
cm7: storage.totalDownloads,
cd1: params.type,
cd2: 'cancelled'
});
}
function completedUpload(params) {
return sendEvent(category, 'upload-stopped', {
cm1: params.size,
cm2: params.time,
cm3: params.speed,
cm5: storage.totalUploads,
cm6: storage.numFiles,
cm7: storage.totalDownloads,
cd1: params.type,
cd2: 'completed'
});
}
function startedDownload(params) {
return sendEvent(category, 'download-started', {
cm1: params.size,
cm4: params.ttl,
cm5: storage.totalUploads,
cm6: storage.numFiles,
cm7: storage.totalDownloads
});
}
function stoppedDownload(params) {
return sendEvent(category, 'download-stopped', {
cm1: params.size,
cm5: storage.totalUploads,
cm6: storage.numFiles,
cm7: storage.totalDownloads,
cd2: 'errored',
cd6: params.err
});
}
function cancelledDownload(params) {
setReferrer('cancelled');
return sendEvent(category, 'download-stopped', {
cm1: params.size,
cm5: storage.totalUploads,
cm6: storage.numFiles,
cm7: storage.totalDownloads,
cd2: 'cancelled'
});
}
function stoppedUpload(params) {
return sendEvent(category, 'upload-stopped', {
cm1: params.size,
cm5: storage.totalUploads,
cm6: storage.numFiles,
cm7: storage.totalDownloads,
cd1: params.type,
cd2: 'errored',
cd6: params.err
});
}
function completedDownload(params) {
return sendEvent(category, 'download-stopped', {
cm1: params.size,
cm2: params.time,
cm3: params.speed,
cm5: storage.totalUploads,
cm6: storage.numFiles,
cm7: storage.totalDownloads,
cd2: 'completed'
});
}
function deletedUpload(params) {
return sendEvent(category, 'upload-deleted', {
cm1: params.size,
cm2: params.time,
cm3: params.speed,
cm4: params.ttl,
cm5: storage.totalUploads,
cm6: storage.numFiles,
cm7: storage.totalDownloads,
cd1: params.type,
cd4: params.location
});
}
function unsupported(params) {
return sendEvent(category, 'unsupported', {
cd6: params.err
});
}
function copiedLink(params) {
return sendEvent(category, 'copied', {
cd4: params.location
});
}
function exitEvent(target) {
return sendEvent(category, 'exited', {
cd3: urlToMetric(target.currentTarget.href)
});
}
function addExitHandlers() {
const links = document.querySelectorAll('a');
links.forEach(l => {
if (/^http/.test(l.href)) {
l.addEventListener('click', exitEvent);
}
});
}
function restartEvent(state) {
setReferrer(state);
return sendEvent(category, 'restarted', {
cd2: state
});
}
function addRestartHandlers() {
const elements = document.querySelectorAll('.send-new');
elements.forEach(el => {
const state = el.getAttribute('data-state');
el.addEventListener('click', restartEvent.bind(null, state));
});
}
module.exports = {
copiedLink,
startedUpload,
cancelledUpload,
stoppedUpload,
completedUpload,
deletedUpload,
startedDownload,
cancelledDownload,
stoppedDownload,
completedDownload,
unsupported
};

View File

@ -1,5 +1,5 @@
/* global MAXFILESIZE EXPIRE_SECONDS */ /* global MAXFILESIZE EXPIRE_SECONDS */
const { Raven, findMetric, sendEvent } = require('./common'); const { Raven } = require('./common');
const FileSender = require('./fileSender'); const FileSender = require('./fileSender');
const { const {
copyToClipboard, copyToClipboard,
@ -10,17 +10,11 @@ const {
const bytes = require('bytes'); const bytes = require('bytes');
const Storage = require('./storage'); const Storage = require('./storage');
const storage = new Storage(localStorage); const storage = new Storage(localStorage);
const metrics = require('./metrics');
const $ = require('jquery'); const $ = require('jquery');
require('jquery-circle-progress'); require('jquery-circle-progress');
if (storage.has('referrer')) {
window.referrer = storage.referrer;
storage.remove('referrer');
} else {
window.referrer = 'external';
}
const allowedCopy = () => { const allowedCopy = () => {
const support = !!document.queryCommandSupported; const support = !!document.queryCommandSupported;
return support ? document.queryCommandSupported('copy') : false; return support ? document.queryCommandSupported('copy') : false;
@ -28,7 +22,7 @@ const allowedCopy = () => {
$(() => { $(() => {
gcmCompliant() gcmCompliant()
.then(function () { .then(function() {
const $pageOne = $('#page-one'); const $pageOne = $('#page-one');
const $copyBtn = $('#copy-btn'); const $copyBtn = $('#copy-btn');
const $link = $('#link'); const $link = $('#link');
@ -42,38 +36,13 @@ $(() => {
$pageOne.removeAttr('hidden'); $pageOne.removeAttr('hidden');
$('#file-upload').on('change', onUpload); $('#file-upload').on('change', onUpload);
$('.legal-links a, .social-links a, #dl-firefox').on('click', function(target) { $(document.body).on('dragover', allowDrop).on('drop', onUpload);
// record exited event by recipient
sendEvent('sender', 'exited', {
cd3: findMetric(target.currentTarget.href)
});
});
$('#send-new-completed').on('click', function() {
// record restarted event
storage.referrer = 'errored-upload';
sendEvent('sender', 'restarted', {
cd2: 'completed'
});
});
$('#send-new-error').on('click', function() {
// record restarted event
storage.referrer = 'errored-upload';
sendEvent('sender', 'restarted', {
cd2: 'errored'
});
});
$(document.body)
.on('dragover', allowDrop)
.on('drop', onUpload);
// reset copy button // reset copy button
$copyBtn.attr({ $copyBtn.attr({
disabled: !allowedCopy(), disabled: !allowedCopy(),
'data-l10n-id': 'copyUrlFormButton' 'data-l10n-id': 'copyUrlFormButton'
}) });
$link.attr('disabled', false); $link.attr('disabled', false);
@ -84,7 +53,7 @@ $(() => {
} else { } else {
$fileList.removeAttr('hidden'); $fileList.removeAttr('hidden');
} }
} };
const files = storage.files; const files = storage.files;
if (files.length === 0) { if (files.length === 0) {
@ -101,10 +70,7 @@ $(() => {
// copy link to clipboard // copy link to clipboard
$copyBtn.on('click', () => { $copyBtn.on('click', () => {
if (allowedCopy() && copyToClipboard($link.attr('value'))) { if (allowedCopy() && copyToClipboard($link.attr('value'))) {
// record copied event from success screen metrics.copiedLink({ location: 'success-screen' });
sendEvent('sender', 'copied', {
cd4: 'success-screen'
});
//disable button for 3s //disable button for 3s
$copyBtn.attr('disabled', true); $copyBtn.attr('disabled', true);
@ -122,12 +88,13 @@ $(() => {
} }
}); });
$uploadWindow.on('dragover', () => { $uploadWindow
$uploadWindow.addClass('ondrag'); .on('dragover', () => {
}) $uploadWindow.addClass('ondrag');
.on('dragleave', () => { })
$uploadWindow.removeClass('ondrag'); .on('dragleave', () => {
}); $uploadWindow.removeClass('ondrag');
});
//initiate progress bar //initiate progress bar
$ulProgress.circleProgress({ $ulProgress.circleProgress({
@ -144,6 +111,7 @@ $(() => {
// on file upload by browse or drag & drop // on file upload by browse or drag & drop
function onUpload(event) { function onUpload(event) {
event.preventDefault(); event.preventDefault();
const clickOrDrop = event.type === 'drop' ? 'drop' : 'click';
// don't allow upload if not on upload page // don't allow upload if not on upload page
if ($pageOne.attr('hidden')) { if ($pageOne.attr('hidden')) {
@ -153,7 +121,7 @@ $(() => {
storage.totalUploads += 1; storage.totalUploads += 1;
let file = ''; let file = '';
if (event.type === 'drop') { if (clickOrDrop === 'drop') {
if (!event.originalEvent.dataTransfer.files[0]) { if (!event.originalEvent.dataTransfer.files[0]) {
$uploadWindow.removeClass('ondrag'); $uploadWindow.removeClass('ondrag');
return; return;
@ -193,16 +161,9 @@ $(() => {
const fileSender = new FileSender(file); const fileSender = new FileSender(file);
$('#cancel-upload').on('click', () => { $('#cancel-upload').on('click', () => {
fileSender.cancel(); fileSender.cancel();
storage.referrer = 'cancelled-upload'; metrics.cancelledUpload({
size: file.size,
// record upload-stopped (cancelled) by sender type: clickOrDrop
sendEvent('sender', 'upload-stopped', {
cm1: file.size,
cm5: storage.totalUploads,
cm6: unexpiredFiles,
cm7: storage.totalDownloads,
cd1: event.type === 'drop' ? 'drop' : 'click',
cd2: 'cancelled'
}); });
location.reload(); location.reload();
}); });
@ -245,18 +206,10 @@ $(() => {
let t; let t;
const startTime = Date.now(); const startTime = Date.now();
const unexpiredFiles = storage.numFiles + 1; metrics.startedUpload({
size: file.size,
// record upload-started event by sender type: clickOrDrop
sendEvent('sender', 'upload-started', {
cm1: file.size,
cm5: storage.totalUploads,
cm6: unexpiredFiles,
cm7: storage.totalDownloads,
cd1: event.type === 'drop' ? 'drop' : 'click',
cd5: window.referrer
}); });
// For large files we need to give the ui a tick to breathe and update // For large files we need to give the ui a tick to breathe and update
// before we kick off the FileSender // before we kick off the FileSender
setTimeout(() => { setTimeout(() => {
@ -264,21 +217,16 @@ $(() => {
.upload() .upload()
.then(info => { .then(info => {
const endTime = Date.now(); const endTime = Date.now();
const totalTime = endTime - startTime; const time = endTime - startTime;
const uploadTime = endTime - uploadStart; const uploadTime = endTime - uploadStart;
const uploadSpeed = file.size / (uploadTime / 1000); const speed = file.size / (uploadTime / 1000);
const expiration = EXPIRE_SECONDS * 1000; const expiration = EXPIRE_SECONDS * 1000;
// record upload-stopped (completed) by sender metrics.completedUpload({
sendEvent('sender', 'upload-stopped', { size: file.size,
cm1: file.size, time,
cm2: totalTime, speed,
cm3: uploadSpeed, type: clickOrDrop
cm5: storage.totalUploads,
cm6: unexpiredFiles,
cm7: storage.totalDownloads,
cd1: event.type === 'drop' ? 'drop' : 'click',
cd2: 'completed'
}); });
const fileData = { const fileData = {
@ -290,9 +238,9 @@ $(() => {
deleteToken: info.deleteToken, deleteToken: info.deleteToken,
creationDate: new Date(), creationDate: new Date(),
expiry: expiration, expiry: expiration,
totalTime: totalTime, totalTime: time,
typeOfUpload: event.type === 'drop' ? 'drop' : 'click', typeOfUpload: clickOrDrop,
uploadSpeed: uploadSpeed uploadSpeed: speed
}; };
storage.addFile(info.fileId, fileData); storage.addFile(info.fileId, fileData);
@ -324,15 +272,10 @@ $(() => {
$uploadError.removeAttr('hidden'); $uploadError.removeAttr('hidden');
window.clearTimeout(t); window.clearTimeout(t);
// record upload-stopped (errored) by sender metrics.stoppedUpload({
sendEvent('sender', 'upload-stopped', { size: file.size,
cm1: file.size, type: clickOrDrop,
cm5: storage.totalUploads, err
cm6: unexpiredFiles,
cm7: storage.totalDownloads,
cd1: event.type === 'drop' ? 'drop' : 'click',
cd2: 'errored',
cd6: err
}); });
}); });
}, 10); }, 10);
@ -363,7 +306,7 @@ $(() => {
} }
//update file table with current files in storage //update file table with current files in storage
const populateFileList = (file) => { const populateFileList = file => {
const row = document.createElement('tr'); const row = document.createElement('tr');
const name = document.createElement('td'); const name = document.createElement('td');
const link = document.createElement('td'); const link = document.createElement('td');
@ -385,7 +328,7 @@ $(() => {
const cellText = document.createTextNode(file.name); const cellText = document.createTextNode(file.name);
const url = file.url.trim() + `#${file.secretKey}`.trim(); const url = file.url.trim() + `#${file.secretKey}`.trim();
$link.attr('value', url); $link.attr('value', url);
$('#copy-text') $('#copy-text')
.attr('data-l10n-args', `{"filename": "${file.name}"}`) .attr('data-l10n-args', `{"filename": "${file.name}"}`)
@ -404,9 +347,7 @@ $(() => {
del.appendChild(delSpan); del.appendChild(delSpan);
const linkSpan = document.createElement('span'); const linkSpan = document.createElement('span');
$(linkSpan) $(linkSpan).addClass('icon-docs').attr('data-l10n-id', 'copyUrlHover');
.addClass('icon-docs')
.attr('data-l10n-id', 'copyUrlHover');
link.appendChild(linkSpan); link.appendChild(linkSpan);
link.style.color = '#0A8DFF'; link.style.color = '#0A8DFF';
@ -414,9 +355,7 @@ $(() => {
//copy link to clipboard when icon clicked //copy link to clipboard when icon clicked
$copyIcon.on('click', () => { $copyIcon.on('click', () => {
// record copied event from upload list // record copied event from upload list
sendEvent('sender', 'copied', { metrics.copiedLink({ location: 'upload-list' });
cd4: 'upload-list'
});
copyToClipboard(url); copyToClipboard(url);
document.l10n.formatValue('copiedUrl').then(translated => { document.l10n.formatValue('copiedUrl').then(translated => {
link.innerHTML = translated; link.innerHTML = translated;
@ -468,7 +407,7 @@ $(() => {
window.clearTimeout(t); window.clearTimeout(t);
toggleHeader(); toggleHeader();
} }
} };
poll(); poll();
@ -496,59 +435,51 @@ $(() => {
row.appendChild(del); row.appendChild(del);
$('tbody').append(row); //add row to table $('tbody').append(row); //add row to table
const unexpiredFiles = storage.numFiles;
// delete file // delete file
$popupText.find('.popup-yes').on('click', e => { $popupText.find('.popup-yes').on('click', e => {
FileSender.delete(file.fileId, file.deleteToken).then(() => { FileSender.delete(file.fileId, file.deleteToken).then(() => {
$(e.target).parents('tr').remove(); $(e.target).parents('tr').remove();
const timeToExpiry = const ttl =
ONE_DAY_IN_MS - (Date.now() - file.creationDate.getTime()); ONE_DAY_IN_MS - (Date.now() - file.creationDate.getTime());
// record upload-deleted from file list metrics
sendEvent('sender', 'upload-deleted', { .deletedUpload({
cm1: file.size, size: file.size,
cm2: file.totalTime, time: file.totalTime,
cm3: file.uploadSpeed, speed: file.uploadSpeed,
cm4: timeToExpiry, type: file.typeOfUpload,
cm5: storage.totalUploads, location: 'upload-list',
cm6: unexpiredFiles, ttl
cm7: storage.totalDownloads, })
cd1: file.typeOfUpload, .then(() => {
cd4: 'upload-list' storage.remove(file.fileId);
}).then(() => { });
storage.remove(file.fileId);
});
toggleHeader(); toggleHeader();
}); });
}); });
$('#delete-file').on('click', () => { $('#delete-file').on('click', () => {
FileSender.delete(file.fileId, file.deleteToken).then(() => { FileSender.delete(file.fileId, file.deleteToken).then(() => {
const timeToExpiry = const ttl =
ONE_DAY_IN_MS - (Date.now() - file.creationDate.getTime()); ONE_DAY_IN_MS - (Date.now() - file.creationDate.getTime());
// record upload-deleted from success screen metrics
sendEvent('sender', 'upload-deleted', { .deletedUpload({
cm1: file.size, size: file.size,
cm2: file.totalTime, time: file.totalTime,
cm3: file.uploadSpeed, speed: file.uploadSpeed,
cm4: timeToExpiry, type: file.typeOfUpload,
cm5: storage.totalUploads, location: 'success-screen',
cm6: unexpiredFiles, ttl
cm7: storage.totalDownloads, })
cd1: file.typeOfUpload, .then(() => {
cd4: 'success-screen' storage.remove(file.fileId);
}).then(() => { location.reload();
storage.remove(file.fileId); });
location.reload();
});
}); });
}); });
// show popup // show popup
$delIcon.on('click', () => { $delIcon.on('click', () => {
$popupText $popupText.addClass('show').focus();
.addClass('show')
.focus();
}); });
// hide popup // hide popup
@ -567,12 +498,10 @@ $(() => {
}); });
toggleHeader(); toggleHeader();
} };
}) })
.catch(err => { .catch(err => {
sendEvent('sender', 'unsupported', { metrics.unsupported({ err }).then(() => {
cd6: err
}).then(() => {
location.replace('/unsupported/gcm'); location.replace('/unsupported/gcm');
}); });
}); });

View File

@ -143,12 +143,12 @@ app.get('/download/:id', async (req, res) => {
try { try {
const filename = await storage.filename(id); const filename = await storage.filename(id);
const contentLength = await storage.length(id); const contentLength = await storage.length(id);
const timeToExpiry = await storage.ttl(id); const ttl = await storage.ttl(id);
res.render('download', { res.render('download', {
filename: decodeURIComponent(filename), filename: decodeURIComponent(filename),
filesize: bytes(contentLength), filesize: bytes(contentLength),
sizeInBytes: contentLength, sizeInBytes: contentLength,
timeToExpiry: timeToExpiry ttl
}); });
} catch (e) { } catch (e) {
res.status(404).render('notfound'); res.status(404).render('notfound');

View File

@ -7,8 +7,8 @@
data-l10n-args='{"filename": "{{filename}}"}'></span> data-l10n-args='{"filename": "{{filename}}"}'></span>
<span data-l10n-id="downloadFileSize" <span data-l10n-id="downloadFileSize"
data-l10n-args='{"size": "{{filesize}}"}'></span> data-l10n-args='{"size": "{{filesize}}"}'></span>
<span id="dl-bytelength" hidden="true">{{sizeInBytes}}</span> <span id="dl-size" hidden="true">{{sizeInBytes}}</span>
<span id="dl-ttl" hidden="true">{{timeToExpiry}}</span> <span id="dl-ttl" hidden="true">{{ttl}}</span>
</div> </div>
<div class="description" data-l10n-id="downloadMessage"></div> <div class="description" data-l10n-id="downloadMessage"></div>
<img src="/resources/illustration_download.svg" id="download-img" data-l10n-id="downloadAltText"/> <img src="/resources/illustration_download.svg" id="download-img" data-l10n-id="downloadAltText"/>
@ -35,5 +35,5 @@
</div> </div>
</div> </div>
<a class="send-new" data-l10n-id="sendYourFilesLink" href="/"></a> <a class="send-new" data-state="completed" data-l10n-id="sendYourFilesLink" href="/"></a>
</div> </div>

View File

@ -61,7 +61,7 @@
<button id="copy-btn" data-l10n-id="copyUrlFormButton"></button> <button id="copy-btn" data-l10n-id="copyUrlFormButton"></button>
</div> </div>
<button id="delete-file" data-l10n-id="deleteFileButton"></button> <button id="delete-file" data-l10n-id="deleteFileButton"></button>
<a class="send-new" id="send-new-completed" data-l10n-id="sendAnotherFileLink"></a> <a class="send-new" data-state="completed" data-l10n-id="sendAnotherFileLink"></a>
</div> </div>
</div> </div>
@ -69,5 +69,5 @@
<div class="title" data-l10n-id="errorPageHeader"></div> <div class="title" data-l10n-id="errorPageHeader"></div>
<div class="expired-description" data-l10n-id="errorPageMessage"></div> <div class="expired-description" data-l10n-id="errorPageMessage"></div>
<img id="upload-error-img" data-l10n-id="errorAltText" src="/resources/illustration_error.svg"/> <img id="upload-error-img" data-l10n-id="errorAltText" src="/resources/illustration_error.svg"/>
<a class="send-new" id="send-new-error" data-l10n-id="sendAnotherFileLink"></a> <a class="send-new" data-state="errored" data-l10n-id="sendAnotherFileLink"></a>
</div> </div>

View File

@ -4,5 +4,5 @@
<img src="/resources/illustration_expired.svg" id="expired-img" data-l10n-id="linkExpiredAlt"/> <img src="/resources/illustration_expired.svg" id="expired-img" data-l10n-id="linkExpiredAlt"/>
</div> </div>
<div class="expired-description" data-l10n-id="uploadPageExplainer"></div> <div class="expired-description" data-l10n-id="uploadPageExplainer"></div>
<a class="send-new" href="/" id="expired-send-new" data-l10n-id="sendYourFilesLink"></a> <a class="send-new" href="/" data-state="notfound" data-l10n-id="sendYourFilesLink"></a>
</div> </div>