added /api/info/:id route

This commit is contained in:
Danny Coates 2018-01-30 12:15:09 -08:00
parent 81534d2c13
commit 97ad674be2
No known key found for this signature in database
GPG key ID: 4C442633C62E00CB
20 changed files with 74 additions and 38 deletions

6
app/pages/blank.js Normal file
View file

@ -0,0 +1,6 @@
const html = require('choo/html');
module.exports = function() {
const div = html`<div></div>`;
return div;
};

34
app/pages/completed.js Normal file
View file

@ -0,0 +1,34 @@
const html = require('choo/html');
const progress = require('../templates/progress');
const { fadeOut } = require('../utils');
module.exports = function(state, emit) {
const div = html`
<div id="page-one">
<div id="download" class="fadeIn">
<div id="download-progress">
<div id="dl-title" class="title">
${state.translate('downloadFinish')}
</div>
<div class="description"></div>
${progress(1)}
<div class="upload">
<div class="progress-text"></div>
</div>
</div>
<a class="send-new"
data-state="completed"
href="/"
onclick=${sendNew}>${state.translate('sendYourFilesLink')}</a>
</div>
</div>
`;
async function sendNew(e) {
e.preventDefault();
await fadeOut('download');
emit('pushState', '/');
}
return div;
};

43
app/pages/download.js Normal file
View file

@ -0,0 +1,43 @@
const html = require('choo/html');
const progress = require('../templates/progress');
const { bytes } = require('../utils');
module.exports = function(state, emit) {
const transfer = state.transfer;
const div = html`
<div id="page-one">
<div id="download">
<div id="download-progress" class="fadeIn">
<div id="dl-title" class="title">${state.translate(
'downloadingPageProgress',
{
filename: state.fileInfo.name,
size: bytes(state.fileInfo.size)
}
)}</div>
<div class="description">${state.translate(
'downloadingPageMessage'
)}</div>
${progress(transfer.progressRatio)}
<div class="upload">
<div class="progress-text">${state.translate(
transfer.msg,
transfer.sizes
)}</div>
<button
id="cancel-upload"
title="${state.translate('deletePopupCancel')}"
onclick=${cancel}>${state.translate('deletePopupCancel')}</button>
</div>
</div>
</div>
</div>
`;
function cancel() {
const btn = document.getElementById('cancel-upload');
btn.remove();
emit('cancel');
}
return div;
};

10
app/pages/error.js Normal file
View file

@ -0,0 +1,10 @@
const html = require('choo/html');
const assets = require('../../common/assets');
module.exports = function(state) {
return html`
<div id="upload-error">
<div class="title">${state.translate('errorPageHeader')}</div>
<img id="upload-error-img" src="${assets.get('illustration_error.svg')}"/>
</div>`;
};

34
app/pages/legal.js Normal file
View file

@ -0,0 +1,34 @@
const html = require('choo/html');
function replaceLinks(str, urls) {
let i = -1;
const s = str.replace(/<a>([^<]+)<\/a>/g, (m, v) => {
i++;
return `<a href="${urls[i]}">${v}</a>`;
});
return [`<div class="description">${s}</div>`];
}
module.exports = function(state) {
const div = html`
<div id="page-one">
<div id="legal">
<div class="title">${state.translate('legalHeader')}</div>
${html(
replaceLinks(state.translate('legalNoticeTestPilot'), [
'https://testpilot.firefox.com/terms',
'https://testpilot.firefox.com/privacy',
'https://testpilot.firefox.com/experiments/send'
])
)}
${html(
replaceLinks(state.translate('legalNoticeMozilla'), [
'https://www.mozilla.org/privacy/websites/',
'https://www.mozilla.org/about/legal/terms/mozilla/'
])
)}
</div>
</div>
`;
return div;
};

21
app/pages/notFound.js Normal file
View file

@ -0,0 +1,21 @@
const html = require('choo/html');
const assets = require('../../common/assets');
module.exports = function(state) {
const div = html`
<div id="page-one">
<div id="download">
<div class="title">${state.translate('expiredPageHeader')}</div>
<div class="share-window">
<img src="${assets.get('illustration_expired.svg')}" id="expired-img">
</div>
<div class="expired-description">
${state.translate('uploadPageExplainer')}
</div>
<a class="send-new" href="/" data-state="notfound">
${state.translate('sendYourFilesLink')}
</a>
</div>
</div>`;
return div;
};

37
app/pages/preview.js Normal file
View file

@ -0,0 +1,37 @@
const html = require('choo/html');
const assets = require('../../common/assets');
const { bytes } = require('../utils');
module.exports = function(state, pageAction) {
const fileInfo = state.fileInfo;
const size = fileInfo.size
? state.translate('downloadFileSize', { size: bytes(fileInfo.size) })
: '';
const title = fileInfo.name
? state.translate('downloadFileName', { filename: fileInfo.name })
: state.translate('downloadFileTitle');
const div = html`
<div id="page-one">
<div id="download">
<div id="download-page-one">
<div class="title">
<span id="dl-file"
data-nonce="${fileInfo.nonce}"
data-requires-password="${fileInfo.requiresPassword}"
>${title}</span>
<span id="dl-filesize">${' ' + size}</span>
</div>
<div class="description">${state.translate('downloadMessage')}</div>
<img
src="${assets.get('illustration_download.svg')}"
id="download-img"
alt="${state.translate('downloadAltText')}"/>
${pageAction}
</div>
<a class="send-new" href="/">${state.translate('sendYourFilesLink')}</a>
</div>
</div>
`;
return div;
};

126
app/pages/share.js Normal file
View file

@ -0,0 +1,126 @@
/* global EXPIRE_SECONDS */
const html = require('choo/html');
const assets = require('../../common/assets');
const notFound = require('./notFound');
const uploadPasswordSet = require('../templates/uploadPasswordSet');
const uploadPasswordUnset = require('../templates/uploadPasswordUnset');
const selectbox = require('../templates/selectbox');
const { allowedCopy, delay, fadeOut } = require('../utils');
function expireInfo(file, translate, emit) {
const hours = Math.floor(EXPIRE_SECONDS / 60 / 60);
const el = html([
`<div>${translate('expireInfo', {
downloadCount: '<select></select>',
timespan: translate('timespanHours', { num: hours })
})}</div>`
]);
const select = el.querySelector('select');
const options = [1, 2, 3, 4, 5, 20].filter(i => i > (file.dtotal || 0));
const t = num => translate('downloadCount', { num });
const changed = value => emit('changeLimit', { file, value });
select.parentNode.replaceChild(
selectbox(file.dlimit || 1, options, t, changed),
select
);
return el;
}
module.exports = function(state, emit) {
const file = state.storage.getFileById(state.params.id);
if (!file) {
return notFound(state, emit);
}
const passwordSection = file.hasPassword
? uploadPasswordSet(state, emit)
: uploadPasswordUnset(state, emit);
const div = html`
<div id="share-link" class="fadeIn">
<div class="title">${expireInfo(file, state.translate, emit)}</div>
<div id="share-window">
<div id="copy-text">
${state.translate('copyUrlFormLabelWithName', { filename: file.name })}
</div>
<div id="copy">
<input id="link" type="url" value="${file.url}" readonly="true"/>
<button id="copy-btn"
class="btn"
title="${state.translate('copyUrlFormButton')}"
onclick=${copyLink}>${state.translate('copyUrlFormButton')}</button>
</div>
${passwordSection}
<button id="delete-file"
class="btn"
title="${state.translate('deleteFileButton')}"
onclick=${showPopup}>${state.translate('deleteFileButton')}
</button>
<div id="deletePopup" class="popup">
<div class="popuptext" onblur=${cancel} tabindex="-1">
<div class="popup-message">${state.translate('deletePopupText')}
</div>
<div class="popup-action">
<span class="popup-no" onclick=${cancel}>
${state.translate('deletePopupCancel')}
</span>
<span class="popup-yes" onclick=${deleteFile}>
${state.translate('deletePopupYes')}
</span>
</div>
</div>
</div>
<a class="send-new"
data-state="completed"
href="/"
onclick=${sendNew}>${state.translate('sendAnotherFileLink')}</a>
</div>
</div>
`;
function showPopup() {
const popupText = document.querySelector('.popuptext');
popupText.classList.add('show');
popupText.focus();
}
function cancel(e) {
e.stopPropagation();
const popupText = document.querySelector('.popuptext');
popupText.classList.remove('show');
}
async function sendNew(e) {
e.preventDefault();
await fadeOut('share-link');
emit('pushState', '/');
}
async function copyLink() {
if (allowedCopy()) {
emit('copy', { url: file.url, location: 'success-screen' });
const input = document.getElementById('link');
input.disabled = true;
const copyBtn = document.getElementById('copy-btn');
copyBtn.disabled = true;
copyBtn.classList.add('success');
copyBtn.replaceChild(
html`<img src="${assets.get('check-16.svg')}" class="icon-check">`,
copyBtn.firstChild
);
await delay(2000);
input.disabled = false;
if (!copyBtn.parentNode.classList.contains('wait-password')) {
copyBtn.disabled = false;
}
copyBtn.classList.remove('success');
copyBtn.textContent = state.translate('copyUrlFormButton');
}
}
async function deleteFile() {
emit('delete', { file, location: 'success-screen' });
await fadeOut('share-link');
emit('pushState', '/');
}
return div;
};

52
app/pages/unsupported.js Normal file
View file

@ -0,0 +1,52 @@
const html = require('choo/html');
const assets = require('../../common/assets');
module.exports = function(state) {
const msg =
state.params.reason === 'outdated'
? html`
<div id="unsupported-browser">
<div class="title">${state.translate('notSupportedHeader')}</div>
<div class="description">
${state.translate('notSupportedOutdatedDetail')}
</div>
<a
id="update-firefox"
href="https://support.mozilla.org/kb/update-firefox-latest-version">
<img
src="${assets.get('firefox_logo-only.svg')}"
class="firefox-logo"
alt="Firefox"/>
<div class="unsupported-button-text">
${state.translate('updateFirefox')}
</div>
</a>
<div class="unsupported-description">
${state.translate('uploadPageExplainer')}
</div>
</div>`
: html`
<div id="unsupported-browser">
<div class="title">${state.translate('notSupportedHeader')}</div>
<div class="description">${state.translate('notSupportedDetail')}</div>
<div class="description">
<a href="https://github.com/mozilla/send/blob/master/docs/faq.md#why-is-my-browser-not-supported">
${state.translate('notSupportedLink')}
</a>
</div>
<a id="dl-firefox" href="https://www.mozilla.org/firefox/new/?utm_campaign=send-acquisition&utm_medium=referral&utm_source=send.firefox.com">
<img
src="${assets.get('firefox_logo-only.svg')}"
class="firefox-logo"
alt="Firefox"/>
<div class="unsupported-button-text">Firefox<br>
<span>${state.translate('downloadFirefoxButtonSub')}</span>
</div>
</a>
<div class="unsupported-description">
${state.translate('uploadPageExplainer')}
</div>
</div>`;
const div = html`<div id="page-one">${msg}</div>`;
return div;
};

41
app/pages/upload.js Normal file
View file

@ -0,0 +1,41 @@
const html = require('choo/html');
const progress = require('../templates/progress');
const { bytes } = require('../utils');
module.exports = function(state, emit) {
const transfer = state.transfer;
const div = html`
<div id="download">
<div id="upload-progress" class="fadeIn">
<div class="title" id="upload-filename">
${state.translate('uploadingPageProgress', {
filename: transfer.file.name,
size: bytes(transfer.file.size)
})}
</div>
<div class="description"></div>
${progress(transfer.progressRatio)}
<div class="upload">
<div class="progress-text">
${state.translate(transfer.msg, transfer.sizes)}
</div>
<button
id="cancel-upload"
title="${state.translate('uploadingPageCancel')}"
onclick=${cancel}>
${state.translate('uploadingPageCancel')}
</button>
</div>
</div>
</div>
`;
function cancel() {
const btn = document.getElementById('cancel-upload');
btn.disabled = true;
btn.textContent = state.translate('uploadCancelNotification');
emit('cancel');
}
return div;
};

82
app/pages/welcome.js Normal file
View file

@ -0,0 +1,82 @@
/* global MAXFILESIZE */
const html = require('choo/html');
const assets = require('../../common/assets');
const fileList = require('../templates/fileList');
const { bytes, fadeOut } = require('../utils');
module.exports = function(state, emit) {
const div = html`
<div id="page-one" class="fadeIn">
<div class="title">${state.translate('uploadPageHeader')}</div>
<div class="description">
<div>${state.translate('uploadPageExplainer')}</div>
<a
href="https://testpilot.firefox.com/experiments/send"
class="link">
${state.translate('uploadPageLearnMore')}
</a>
</div>
<div class="upload-window"
ondragover=${dragover}
ondragleave=${dragleave}>
<div id="upload-img">
<img
src="${assets.get('upload.svg')}"
title="${state.translate('uploadSvgAlt')}"/>
</div>
<div id="upload-text">${state.translate('uploadPageDropMessage')}</div>
<span id="file-size-msg">
<em>${state.translate('uploadPageSizeMessage')}</em>
</span>
<input id="file-upload"
type="file"
name="fileUploaded"
onfocus=${onfocus}
onblur=${onblur}
onchange=${upload} />
<label for="file-upload"
id="browse"
class="btn browse"
title="${state.translate('uploadPageBrowseButton1')}">
${state.translate('uploadPageBrowseButton1')}
</label>
</div>
${fileList(state, emit)}
</div>
`;
function dragover(event) {
const div = document.querySelector('.upload-window');
div.classList.add('ondrag');
}
function dragleave(event) {
const div = document.querySelector('.upload-window');
div.classList.remove('ondrag');
}
function onfocus(event) {
event.target.classList.add('has-focus');
}
function onblur(event) {
event.target.classList.remove('has-focus');
}
async function upload(event) {
event.preventDefault();
const target = event.target;
const file = target.files[0];
if (file.size === 0) {
return;
}
if (file.size > MAXFILESIZE) {
window.alert(state.translate('fileTooBig', { size: bytes(MAXFILESIZE) }));
return;
}
await fadeOut('page-one');
emit('upload', { file, type: 'click' });
}
return div;
};