hook multifile to ui
@ -65,6 +65,14 @@ export async function fileInfo(id, owner_token) {
|
|||||||
throw new Error(response.status);
|
throw new Error(response.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function hasPassword(id) {
|
||||||
|
const response = await fetch(`/api/exists/${id}`);
|
||||||
|
if (response.ok) {
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
throw new Error(response.status);
|
||||||
|
}
|
||||||
|
|
||||||
export async function metadata(id, keychain) {
|
export async function metadata(id, keychain) {
|
||||||
const result = await fetchWithAuthAndRetry(
|
const result = await fetchWithAuthAndRetry(
|
||||||
`/api/metadata/${id}`,
|
`/api/metadata/${id}`,
|
||||||
|
@ -17,6 +17,10 @@ export default class Archive {
|
|||||||
return this.files.reduce((total, file) => total + file.size, 0);
|
return this.files.reduce((total, file) => total + file.size, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get numFiles() {
|
||||||
|
return this.files.length;
|
||||||
|
}
|
||||||
|
|
||||||
get manifest() {
|
get manifest() {
|
||||||
return {
|
return {
|
||||||
files: this.files.map(file => ({
|
files: this.files.map(file => ({
|
||||||
|
183
app/base.css
@ -1,14 +1,16 @@
|
|||||||
:root {
|
:root {
|
||||||
--pageBGColor: #fff;
|
--pageBGColor: #fff;
|
||||||
--primaryControlBGColor: #0297f8;
|
--lightControlBGColor: #e6e6e6;
|
||||||
|
--primaryControlBGColor: #0a84ff;
|
||||||
--primaryControlFGColor: #fff;
|
--primaryControlFGColor: #fff;
|
||||||
--primaryControlHoverColor: #0287e8;
|
--primaryControlHoverColor: #0473e2;
|
||||||
--inputTextColor: #737373;
|
--inputTextColor: #737373;
|
||||||
--errorColor: #d70022;
|
--errorColor: #d70022;
|
||||||
--linkColor: #0094fb;
|
--linkColor: #0094fb;
|
||||||
--textColor: #0c0c0d;
|
--textColor: #0c0c0d;
|
||||||
|
--lightBorderColor: rgba(12, 12, 12, 0.2);
|
||||||
--lightTextColor: #737373;
|
--lightTextColor: #737373;
|
||||||
--successControlBGColor: #05a700;
|
--successControlBGColor: #12bc00;
|
||||||
--successControlFGColor: #fff;
|
--successControlFGColor: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,17 +43,21 @@ a {
|
|||||||
|
|
||||||
.main {
|
.main {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
|
||||||
flex: auto;
|
flex: auto;
|
||||||
padding: 0 20px;
|
padding: 0 25px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
min-height: 500px;
|
||||||
|
max-height: 630px;
|
||||||
|
height: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stripedBox {
|
.stripedBox {
|
||||||
width: 480px;
|
flex: none;
|
||||||
|
position: relative;
|
||||||
|
width: 400px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
box-shadow: 0 0 0 3px rgba(155, 155, 155, 0.4);
|
box-shadow: 0 0 0 3px rgba(12, 12, 13, 0.2);
|
||||||
background-image: repeating-linear-gradient(
|
background-image: repeating-linear-gradient(
|
||||||
45deg,
|
45deg,
|
||||||
white,
|
white,
|
||||||
@ -68,8 +74,11 @@ a {
|
|||||||
.mainContent {
|
.mainContent {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
box-sizing: border-box;
|
||||||
margin: 0 10px;
|
margin: 0 10px;
|
||||||
padding: 1px 10px 0 10px; /* top wtf? */
|
padding: 10px 10px 28px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spacer {
|
.spacer {
|
||||||
@ -77,7 +86,8 @@ a {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.uploads {
|
.uploads {
|
||||||
flex: auto;
|
flex: 0 0 262px;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.noscript {
|
.noscript {
|
||||||
@ -87,13 +97,19 @@ a {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
font-size: 15px;
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 70px;
|
||||||
|
line-height: 70px;
|
||||||
|
font-size: 21px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--primaryControlFGColor);
|
text-transform: uppercase;
|
||||||
cursor: pointer;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
letter-spacing: 0.56px;
|
||||||
|
color: var(--primaryControlFGColor);
|
||||||
background: var(--primaryControlBGColor);
|
background: var(--primaryControlBGColor);
|
||||||
border: 1px solid var(--primaryControlBGColor);
|
cursor: pointer;
|
||||||
|
border: 0;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,75 +117,52 @@ a {
|
|||||||
background-color: var(--primaryControlHoverColor);
|
background-color: var(--primaryControlHoverColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn--cancel {
|
.btn--stripes {
|
||||||
color: var(--errorColor);
|
background: repeating-linear-gradient(
|
||||||
background: var(--pageBGColor);
|
-65deg,
|
||||||
font-size: 15px;
|
#7c7c7c 0,
|
||||||
border: 0;
|
#7c7c7c 17px,
|
||||||
cursor: pointer;
|
#737373 17px,
|
||||||
text-decoration: underline;
|
#737373 30px
|
||||||
|
);
|
||||||
|
background-size: 300% 300%;
|
||||||
|
animation: barberpole 12s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn--cancel:disabled {
|
@keyframes barberpole {
|
||||||
text-decoration: none;
|
0% {
|
||||||
cursor: auto;
|
background-position: 100% 0%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn--cancel:hover {
|
100% {
|
||||||
background-color: var(--pageBGColor);
|
background-position: 0% 0%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
flex: 2 0 auto;
|
border: 1px solid var(--lightBorderColor);
|
||||||
border: 1px solid var(--primaryControlBGColor);
|
|
||||||
border-radius: 6px 0 0 6px;
|
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
color: var(--inputTextColor);
|
color: var(--inputTextColor);
|
||||||
font-family: 'SF Pro Text', sans-serif;
|
font-family: 'SF Pro Text', sans-serif;
|
||||||
letter-spacing: 0;
|
|
||||||
line-height: 23px;
|
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
height: 46px;
|
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input--error {
|
|
||||||
border-color: var(--errorColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
.input--noBtn {
|
.input--noBtn {
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inputBtn {
|
.input--error {
|
||||||
flex: auto;
|
border-color: var(--errorColor);
|
||||||
background: var(--primaryControlBGColor);
|
|
||||||
border-radius: 0 6px 6px 0;
|
|
||||||
border: 1px solid var(--primaryControlBGColor);
|
|
||||||
color: var(--primaryControlFGColor);
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
/* Force flat button look */
|
|
||||||
/* stylelint-disable-next-line plugin/no-unsupported-browser-features */
|
|
||||||
appearance: none;
|
|
||||||
font-size: 15px;
|
|
||||||
padding-bottom: 3px;
|
|
||||||
padding-left: 10px;
|
|
||||||
padding-right: 10px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.inputBtn:disabled {
|
.inputBtn.inputError {
|
||||||
cursor: auto;
|
background-color: var(--errorColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
.inputBtn:hover {
|
.inputBtn.inputError:hover {
|
||||||
background-color: var(--primaryControlHoverColor);
|
background-color: var(--errorColor);
|
||||||
}
|
|
||||||
|
|
||||||
.inputBtn--hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cursor--pointer {
|
.cursor--pointer {
|
||||||
@ -188,15 +181,15 @@ a {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.link--action {
|
.link--action {
|
||||||
text-decoration: underline;
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page {
|
.page {
|
||||||
margin: 0 auto 30px;
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
@ -223,6 +216,13 @@ a {
|
|||||||
animation: fadeout 200ms linear;
|
animation: fadeout 200ms linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.goBackButton {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
margin: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes fadeout {
|
@keyframes fadeout {
|
||||||
0% {
|
0% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@ -250,6 +250,7 @@ a {
|
|||||||
|
|
||||||
.error {
|
.error {
|
||||||
color: var(--errorColor);
|
color: var(--errorColor);
|
||||||
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
@ -264,34 +265,50 @@ a {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
font-size: 15px;
|
font-size: 13px;
|
||||||
line-height: 23px;
|
text-align: left;
|
||||||
max-width: 630px;
|
margin: 14px auto;
|
||||||
text-align: center;
|
color: var(--lightTextColor);
|
||||||
margin: 0 auto 60px;
|
width: 95%;
|
||||||
color: var(--textColor);
|
|
||||||
width: 92%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-device-width: 768px), (max-width: 768px) {
|
.visible {
|
||||||
|
visibility: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noDisplay {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flexible {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-device-width: 750px), (max-width: 750px) {
|
||||||
.description {
|
.description {
|
||||||
margin: 0 auto 25px;
|
margin: 0 auto 25px;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-device-width: 520px), (max-width: 520px) {
|
.main {
|
||||||
.input {
|
flex-direction: column;
|
||||||
font-size: 22px;
|
min-height: 700px;
|
||||||
padding: 10px 10px;
|
|
||||||
border-radius: 6px 6px 0 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.inputBtn {
|
.spacer {
|
||||||
border-radius: 0 0 6px 6px;
|
flex: none;
|
||||||
flex: 0 1 65px;
|
height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input--noBtn {
|
.stripedBox {
|
||||||
border-radius: 6px;
|
max-height: 550px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uploads {
|
||||||
|
flex: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
margin: 15px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
/* global MAXFILESIZE */
|
import { checkSize } from './utils';
|
||||||
import Archive from './archive';
|
|
||||||
import { bytes } from './utils';
|
|
||||||
|
|
||||||
export default function(state, emitter) {
|
export default function(state, emitter) {
|
||||||
emitter.on('DOMContentLoaded', () => {
|
emitter.on('DOMContentLoaded', () => {
|
||||||
document.body.addEventListener('dragover', event => {
|
document.body.addEventListener('dragover', event => {
|
||||||
if (state.route === '/') {
|
if (state.route === '/') {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
const files = document.querySelector('.uploadedFilesWrapper');
|
||||||
|
files.classList.add('uploadArea--noEvents');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
document.body.addEventListener('drop', event => {
|
document.body.addEventListener('drop', event => {
|
||||||
@ -15,21 +15,12 @@ export default function(state, emitter) {
|
|||||||
document
|
document
|
||||||
.querySelector('.uploadArea')
|
.querySelector('.uploadArea')
|
||||||
.classList.remove('uploadArea--dragging');
|
.classList.remove('uploadArea--dragging');
|
||||||
const target = event.dataTransfer;
|
|
||||||
if (target.files.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const file = new Archive(target.files);
|
|
||||||
|
|
||||||
if (file.size === 0) {
|
const target = event.dataTransfer;
|
||||||
return;
|
|
||||||
}
|
checkSize(target.files, state.files);
|
||||||
if (file.size > MAXFILESIZE) {
|
|
||||||
// eslint-disable-next-line no-alert
|
emitter.emit('addFiles', { files: target.files });
|
||||||
alert(state.translate('fileTooBig', { size: bytes(MAXFILESIZE) }));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
emitter.emit('upload', { file, type: 'drop' });
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,17 +1,14 @@
|
|||||||
import FileSender from './fileSender';
|
import FileSender from './fileSender';
|
||||||
import FileReceiver from './fileReceiver';
|
import FileReceiver from './fileReceiver';
|
||||||
import {
|
import { copyToClipboard, delay, openLinksInNewTab, percent } from './utils';
|
||||||
copyToClipboard,
|
|
||||||
delay,
|
|
||||||
fadeOut,
|
|
||||||
openLinksInNewTab,
|
|
||||||
percent
|
|
||||||
} from './utils';
|
|
||||||
import * as metrics from './metrics';
|
import * as metrics from './metrics';
|
||||||
|
import { hasPassword } from './api';
|
||||||
|
import Archive from './archive';
|
||||||
|
|
||||||
export default function(state, emitter) {
|
export default function(state, emitter) {
|
||||||
let lastRender = 0;
|
let lastRender = 0;
|
||||||
let updateTitle = false;
|
let updateTitle = false;
|
||||||
|
state.files = [];
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
emitter.emit('render');
|
emitter.emit('render');
|
||||||
@ -64,6 +61,16 @@ export default function(state, emitter) {
|
|||||||
metrics.changedDownloadLimit(file);
|
metrics.changedDownloadLimit(file);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
emitter.on('removeUpload', async ({ file }) => {
|
||||||
|
for (let i = 0; i < state.files.length; i++) {
|
||||||
|
if (state.files[i] === file) {
|
||||||
|
state.files.splice(i, 1);
|
||||||
|
render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
emitter.on('delete', async ({ file, location }) => {
|
emitter.on('delete', async ({ file, location }) => {
|
||||||
try {
|
try {
|
||||||
metrics.deletedUpload({
|
metrics.deletedUpload({
|
||||||
@ -85,11 +92,22 @@ export default function(state, emitter) {
|
|||||||
state.transfer.cancel();
|
state.transfer.cancel();
|
||||||
});
|
});
|
||||||
|
|
||||||
emitter.on('upload', async ({ file, type }) => {
|
emitter.on('addFiles', async ({ files }) => {
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
state.files.push(files[i]);
|
||||||
|
}
|
||||||
|
render();
|
||||||
|
});
|
||||||
|
|
||||||
|
//TODO: hook up to multi-file upload functionality
|
||||||
|
emitter.on('upload', async ({ files, type, dlCount, password }) => {
|
||||||
|
const file = new Archive(files);
|
||||||
|
|
||||||
const size = file.size;
|
const size = file.size;
|
||||||
const sender = new FileSender(file);
|
const sender = new FileSender(file);
|
||||||
sender.on('progress', updateProgress);
|
sender.on('progress', updateProgress);
|
||||||
sender.on('encrypting', render);
|
sender.on('encrypting', render);
|
||||||
|
sender.on('complete', render);
|
||||||
state.transfer = sender;
|
state.transfer = sender;
|
||||||
state.uploading = true;
|
state.uploading = true;
|
||||||
render();
|
render();
|
||||||
@ -98,19 +116,25 @@ export default function(state, emitter) {
|
|||||||
await delay(200);
|
await delay(200);
|
||||||
try {
|
try {
|
||||||
metrics.startedUpload({ size, type });
|
metrics.startedUpload({ size, type });
|
||||||
|
|
||||||
const ownedFile = await sender.upload();
|
const ownedFile = await sender.upload();
|
||||||
ownedFile.type = type;
|
ownedFile.type = type;
|
||||||
state.storage.totalUploads += 1;
|
state.storage.totalUploads += 1;
|
||||||
metrics.completedUpload(ownedFile);
|
metrics.completedUpload(ownedFile);
|
||||||
|
|
||||||
state.storage.addFile(ownedFile);
|
state.storage.addFile(ownedFile);
|
||||||
|
|
||||||
|
if (password) {
|
||||||
|
emitter.emit('password', { password, file: ownedFile });
|
||||||
|
}
|
||||||
|
emitter.emit('changeLimit', { file: ownedFile, value: dlCount });
|
||||||
|
|
||||||
const cancelBtn = document.getElementById('cancel-upload');
|
const cancelBtn = document.getElementById('cancel-upload');
|
||||||
if (cancelBtn) {
|
if (cancelBtn) {
|
||||||
cancelBtn.hidden = 'hidden';
|
cancelBtn.hidden = 'hidden';
|
||||||
}
|
}
|
||||||
if (document.querySelector('.page')) {
|
if (document.querySelector('.page')) {
|
||||||
await delay(1000);
|
await delay(1000);
|
||||||
await fadeOut('.page');
|
|
||||||
}
|
}
|
||||||
emitter.emit('pushState', `/share/${ownedFile.id}`);
|
emitter.emit('pushState', `/share/${ownedFile.id}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -127,6 +151,8 @@ export default function(state, emitter) {
|
|||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
openLinksInNewTab(links, false);
|
openLinksInNewTab(links, false);
|
||||||
|
state.files = [];
|
||||||
|
state.password = '';
|
||||||
state.uploading = false;
|
state.uploading = false;
|
||||||
state.transfer = null;
|
state.transfer = null;
|
||||||
}
|
}
|
||||||
@ -150,6 +176,17 @@ export default function(state, emitter) {
|
|||||||
render();
|
render();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
emitter.on('getPasswordExist', async ({ id }) => {
|
||||||
|
try {
|
||||||
|
state.fileInfo = await hasPassword(id);
|
||||||
|
render();
|
||||||
|
} catch (e) {
|
||||||
|
if (e.message === '404') {
|
||||||
|
return emitter.emit('pushState', '/404');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
emitter.on('getMetadata', async () => {
|
emitter.on('getMetadata', async () => {
|
||||||
const file = state.fileInfo;
|
const file = state.fileInfo;
|
||||||
|
|
||||||
@ -172,6 +209,7 @@ export default function(state, emitter) {
|
|||||||
emitter.on('download', async file => {
|
emitter.on('download', async file => {
|
||||||
state.transfer.on('progress', updateProgress);
|
state.transfer.on('progress', updateProgress);
|
||||||
state.transfer.on('decrypting', render);
|
state.transfer.on('decrypting', render);
|
||||||
|
state.transfer.on('complete', render);
|
||||||
const links = openLinksInNewTab();
|
const links = openLinksInNewTab();
|
||||||
const size = file.size;
|
const size = file.size;
|
||||||
try {
|
try {
|
||||||
@ -186,12 +224,11 @@ export default function(state, emitter) {
|
|||||||
const speed = size / (time / 1000);
|
const speed = size / (time / 1000);
|
||||||
if (document.querySelector('.page')) {
|
if (document.querySelector('.page')) {
|
||||||
await delay(1000);
|
await delay(1000);
|
||||||
await fadeOut('.page');
|
|
||||||
}
|
}
|
||||||
state.storage.totalDownloads += 1;
|
state.storage.totalDownloads += 1;
|
||||||
state.transfer.reset();
|
state.transfer.reset();
|
||||||
metrics.completedDownload({ size, time, speed });
|
metrics.completedDownload({ size, time, speed });
|
||||||
emitter.emit('pushState', '/completed');
|
//emitter.emit('pushState', '/completed');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.message === '0') {
|
if (err.message === '0') {
|
||||||
// download cancelled
|
// download cancelled
|
||||||
|
@ -172,6 +172,7 @@ export default class FileReceiver extends Nanobus {
|
|||||||
|
|
||||||
this.downloadRequest = null;
|
this.downloadRequest = null;
|
||||||
this.msg = 'downloadFinish';
|
this.msg = 'downloadFinish';
|
||||||
|
this.emit('complete');
|
||||||
this.state = 'complete';
|
this.state = 'complete';
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.downloadRequest = null;
|
this.downloadRequest = null;
|
||||||
|
@ -93,6 +93,7 @@ export default class FileSender extends Nanobus {
|
|||||||
url: `${result.url}#${secretKey}`,
|
url: `${result.url}#${secretKey}`,
|
||||||
name: this.file.name,
|
name: this.file.name,
|
||||||
size: this.file.size,
|
size: this.file.size,
|
||||||
|
manifest: this.file.manifest,
|
||||||
time: time,
|
time: time,
|
||||||
speed: this.file.size / (time / 1000),
|
speed: this.file.size / (time / 1000),
|
||||||
createdAt: Date.now(),
|
createdAt: Date.now(),
|
||||||
@ -101,6 +102,7 @@ export default class FileSender extends Nanobus {
|
|||||||
nonce: this.keychain.nonce,
|
nonce: this.keychain.nonce,
|
||||||
ownerToken: result.ownerToken
|
ownerToken: result.ownerToken
|
||||||
});
|
});
|
||||||
|
|
||||||
return ownedFile;
|
return ownedFile;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.msg = 'errorPageHeader';
|
this.msg = 'errorPageHeader';
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
@import './templates/activeBackground/activeBackground.css';
|
@import './templates/activeBackground/activeBackground.css';
|
||||||
@import './templates/header/header.css';
|
@import './templates/header/header.css';
|
||||||
@import './templates/downloadButton/downloadButton.css';
|
@import './templates/downloadButton/downloadButton.css';
|
||||||
@import './templates/progress/progress.css';
|
|
||||||
@import './templates/passwordInput/passwordInput.css';
|
@import './templates/passwordInput/passwordInput.css';
|
||||||
@import './templates/downloadPassword/downloadPassword.css';
|
@import './templates/downloadPassword/downloadPassword.css';
|
||||||
@import './templates/setPasswordSection/setPasswordSection.css';
|
@import './templates/setPasswordSection/setPasswordSection.css';
|
||||||
@ -11,7 +10,14 @@
|
|||||||
@import './templates/selectbox/selectbox.css';
|
@import './templates/selectbox/selectbox.css';
|
||||||
@import './templates/fileList/fileList.css';
|
@import './templates/fileList/fileList.css';
|
||||||
@import './templates/file/file.css';
|
@import './templates/file/file.css';
|
||||||
|
@import './templates/uploadedFile/uploadedFile.css';
|
||||||
|
@import './templates/uploadedFileList/uploadedFileList.css';
|
||||||
@import './templates/popup/popup.css';
|
@import './templates/popup/popup.css';
|
||||||
|
@import './templates/title/title.css';
|
||||||
|
@import './templates/fileIcon/fileIcon.css';
|
||||||
|
@import './templates/signupPromo/signupPromo.css';
|
||||||
|
@import './templates/userAccount/userAccount.css';
|
||||||
@import './pages/welcome/welcome.css';
|
@import './pages/welcome/welcome.css';
|
||||||
@import './pages/share/share.css';
|
@import './pages/share/share.css';
|
||||||
|
@import './pages/signin/signin.css';
|
||||||
@import './pages/unsupported/unsupported.css';
|
@import './pages/unsupported/unsupported.css';
|
||||||
|
@ -9,6 +9,7 @@ export default class OwnedFile {
|
|||||||
this.name = obj.name;
|
this.name = obj.name;
|
||||||
this.size = obj.size;
|
this.size = obj.size;
|
||||||
this.type = obj.type;
|
this.type = obj.type;
|
||||||
|
this.manifest = obj.manifest;
|
||||||
this.time = obj.time;
|
this.time = obj.time;
|
||||||
this.speed = obj.speed;
|
this.speed = obj.speed;
|
||||||
this.createdAt = obj.createdAt;
|
this.createdAt = obj.createdAt;
|
||||||
@ -70,6 +71,7 @@ export default class OwnedFile {
|
|||||||
name: this.name,
|
name: this.name,
|
||||||
size: this.size,
|
size: this.size,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
|
manifest: this.manifest,
|
||||||
time: this.time,
|
time: this.time,
|
||||||
speed: this.speed,
|
speed: this.speed,
|
||||||
createdAt: this.createdAt,
|
createdAt: this.createdAt,
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
const html = require('choo/html');
|
|
||||||
const progress = require('../../templates/progress');
|
|
||||||
const { fadeOut } = require('../../utils');
|
|
||||||
|
|
||||||
module.exports = function(state, emit) {
|
|
||||||
return html`
|
|
||||||
<div class="page effect--fadeIn">
|
|
||||||
<div class="title">
|
|
||||||
${state.translate('downloadFinish')}
|
|
||||||
</div>
|
|
||||||
<div class="description"></div>
|
|
||||||
${progress(1)}
|
|
||||||
<div class="progressSection">
|
|
||||||
<div class="progressSection__text"></div>
|
|
||||||
</div>
|
|
||||||
<a class="link link--action"
|
|
||||||
href="/"
|
|
||||||
onclick=${sendNew}>${state.translate('sendYourFilesLink')}</a>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
async function sendNew(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
await fadeOut('.page');
|
|
||||||
emit('pushState', '/');
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,42 +0,0 @@
|
|||||||
const html = require('choo/html');
|
|
||||||
const progress = require('../../templates/progress');
|
|
||||||
const { bytes } = require('../../utils');
|
|
||||||
|
|
||||||
module.exports = function(state, emit) {
|
|
||||||
const transfer = state.transfer;
|
|
||||||
const cancelBtn = html`
|
|
||||||
<button
|
|
||||||
id="cancel"
|
|
||||||
class="btn btn--cancel"
|
|
||||||
title="${state.translate('deletePopupCancel')}"
|
|
||||||
onclick=${cancel}>
|
|
||||||
${state.translate('deletePopupCancel')}
|
|
||||||
</button>`;
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<div class="page effect--fadeIn">
|
|
||||||
<div 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, transfer.progressIndefinite)}
|
|
||||||
<div class="progressSection">
|
|
||||||
<div class="progressSection__text">
|
|
||||||
${state.translate(transfer.msg, transfer.sizes)}
|
|
||||||
</div>
|
|
||||||
${transfer.state === 'downloading' ? cancelBtn : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
function cancel() {
|
|
||||||
const btn = document.getElementById('cancel');
|
|
||||||
btn.remove();
|
|
||||||
emit('cancel');
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,10 +1,22 @@
|
|||||||
const html = require('choo/html');
|
const html = require('choo/html');
|
||||||
const assets = require('../../../common/assets');
|
const assets = require('../../../common/assets');
|
||||||
|
const title = require('../../templates/title');
|
||||||
|
|
||||||
module.exports = function(state) {
|
module.exports = function(state) {
|
||||||
return html`
|
return html`
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<div class="title">${state.translate('errorPageHeader')}</div>
|
|
||||||
<img src="${assets.get('illustration_error.svg')}"/>
|
${title(state)}
|
||||||
|
|
||||||
|
<div class="error">${state.translate('errorPageHeader')}</div>
|
||||||
|
<img class="flexible" src="${assets.get('illustration_error.svg')}"/>
|
||||||
|
|
||||||
|
<div class="description">
|
||||||
|
${state.translate('uploadPageExplainer')}
|
||||||
|
</div>
|
||||||
|
<a class="link link--action" href="/">
|
||||||
|
${state.translate('sendYourFilesLink')}
|
||||||
|
</a>
|
||||||
|
|
||||||
</div>`;
|
</div>`;
|
||||||
};
|
};
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
const html = require('choo/html');
|
const html = require('choo/html');
|
||||||
const assets = require('../../../common/assets');
|
const assets = require('../../../common/assets');
|
||||||
|
const title = require('../../templates/title');
|
||||||
|
|
||||||
module.exports = function(state) {
|
module.exports = function(state) {
|
||||||
return html`
|
return html`
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<div class="title">${state.translate('expiredPageHeader')}</div>
|
|
||||||
<img src="${assets.get('illustration_expired.svg')}" id="expired-img">
|
${title(state)}
|
||||||
|
|
||||||
|
<div class="error">${state.translate('expiredPageHeader')}</div>
|
||||||
|
<img src="${assets.get(
|
||||||
|
'illustration_expired.svg'
|
||||||
|
)}" class="flexible" id="expired-img">
|
||||||
<div class="description">
|
<div class="description">
|
||||||
${state.translate('uploadPageExplainer')}
|
${state.translate('uploadPageExplainer')}
|
||||||
</div>
|
</div>
|
||||||
|
19
app/pages/password/index.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
const html = require('choo/html');
|
||||||
|
const titleSection = require('../../templates/title');
|
||||||
|
const downloadPassword = require('../../templates/downloadPassword');
|
||||||
|
|
||||||
|
module.exports = function(state, emit) {
|
||||||
|
return html`
|
||||||
|
<div class="page">
|
||||||
|
${titleSection(state)}
|
||||||
|
|
||||||
|
<div class="description">${state.translate('downloadMessage2')}</div>
|
||||||
|
${downloadPassword(state, emit)}
|
||||||
|
|
||||||
|
<a class="link link--action" href="/">
|
||||||
|
${state.translate('sendYourFilesLink')}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
};
|
@ -1,40 +1,25 @@
|
|||||||
const html = require('choo/html');
|
const html = require('choo/html');
|
||||||
const assets = require('../../../common/assets');
|
const titleSection = require('../../templates/title');
|
||||||
const { bytes } = require('../../utils');
|
const downloadButton = require('../../templates/downloadButton');
|
||||||
|
const downloadedFiles = require('../../templates/uploadedFileList');
|
||||||
|
|
||||||
module.exports = function(state, pageAction) {
|
module.exports = function(state, emit) {
|
||||||
const fileInfo = state.fileInfo;
|
const storageFile = state.storage.getFileById(state.params.id);
|
||||||
|
|
||||||
const size = fileInfo.size
|
const multifiles = Array.from(storageFile.manifest.files);
|
||||||
? state.translate('downloadFileSize', { size: bytes(fileInfo.size) })
|
|
||||||
: '';
|
|
||||||
|
|
||||||
const title = fileInfo.name
|
|
||||||
? state.translate('downloadFileName', { filename: fileInfo.name })
|
|
||||||
: state.translate('downloadFileTitle');
|
|
||||||
|
|
||||||
const info = html`
|
|
||||||
<div id="dl-file"
|
|
||||||
data-nonce="${fileInfo.nonce}"
|
|
||||||
data-requires-password="${fileInfo.requiresPassword}"></div>`;
|
|
||||||
if (!pageAction) {
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
return html`
|
return html`
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<div class="title">
|
${titleSection(state)}
|
||||||
<span>${title}</span>
|
|
||||||
<span>${' ' + size}</span>
|
${downloadedFiles(multifiles, state, emit)}
|
||||||
</div>
|
<div class="description">${state.translate('downloadMessage2')}</div>
|
||||||
<div class="description">${state.translate('downloadMessage')}</div>
|
${downloadButton(state, emit)}
|
||||||
<img
|
|
||||||
src="${assets.get('illustration_download.svg')}"
|
|
||||||
title="${state.translate('downloadAltText')}"/>
|
|
||||||
${pageAction}
|
|
||||||
<a class="link link--action" href="/">
|
<a class="link link--action" href="/">
|
||||||
${state.translate('sendYourFilesLink')}
|
${state.translate('sendYourFilesLink')}
|
||||||
</a>
|
</a>
|
||||||
${info}
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
@ -3,42 +3,51 @@ const html = require('choo/html');
|
|||||||
const raw = require('choo/html/raw');
|
const raw = require('choo/html/raw');
|
||||||
const assets = require('../../../common/assets');
|
const assets = require('../../../common/assets');
|
||||||
const notFound = require('../notFound');
|
const notFound = require('../notFound');
|
||||||
const setPasswordSection = require('../../templates/setPasswordSection');
|
|
||||||
const selectbox = require('../../templates/selectbox');
|
|
||||||
const deletePopup = require('../../templates/popup');
|
const deletePopup = require('../../templates/popup');
|
||||||
|
const uploadedFiles = require('../../templates/uploadedFileList');
|
||||||
const { allowedCopy, delay, fadeOut } = require('../../utils');
|
const { allowedCopy, delay, fadeOut } = require('../../utils');
|
||||||
|
|
||||||
module.exports = function(state, emit) {
|
module.exports = function(state, emit) {
|
||||||
const file = state.storage.getFileById(state.params.id);
|
const file = state.storage.getFileById(state.params.id);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return notFound(state, emit);
|
return notFound(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const passwordReminderClass = file._hasPassword
|
||||||
|
? ''
|
||||||
|
: 'passwordReminder--hidden';
|
||||||
|
|
||||||
|
const multifiles = Array.from(file.manifest.files);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div id="shareWrapper" class="effect--fadeIn">
|
|
||||||
${expireInfo(file, state.translate, emit)}
|
<div class="page effect--fadeIn" id="shareWrapper">
|
||||||
<div class="sharePage">
|
<a href="/" class="goBackButton">
|
||||||
|
<img src="${assets.get('back-arrow.svg')}"/>
|
||||||
|
</a>
|
||||||
|
${expireInfo(file, state.translate)}
|
||||||
|
|
||||||
|
${uploadedFiles(multifiles, state, emit)}
|
||||||
|
|
||||||
|
|
||||||
<div class="sharePage__copyText">
|
<div class="sharePage__copyText">
|
||||||
${state.translate('copyUrlFormLabelWithName', { filename: file.name })}
|
${state.translate('copyUrlFormLabelWithName', { filename: '' })}
|
||||||
|
<div class="sharePage__passwordReminder ${passwordReminderClass}">(don't forget the password too)</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="copySection">
|
|
||||||
<input
|
<input
|
||||||
id="fileUrl"
|
id="fileUrl"
|
||||||
class="copySection__url"
|
class="copySection__url"
|
||||||
type="url"
|
type="url"
|
||||||
value="${file.url}"
|
value="${file.url}"
|
||||||
readonly="true"/>
|
readonly="true"/>
|
||||||
|
|
||||||
<button id="copyBtn"
|
<button id="copyBtn"
|
||||||
class="inputBtn inputBtn--copy"
|
class="btn copyBtn"
|
||||||
title="${state.translate('copyUrlFormButton')}"
|
title="${state.translate('copyUrlFormButton')}"
|
||||||
onclick=${copyLink}>${state.translate('copyUrlFormButton')}</button>
|
onclick=${copyLink}>${state.translate('copyUrlFormButton')}
|
||||||
</div>
|
|
||||||
${setPasswordSection(state, emit)}
|
|
||||||
<button
|
|
||||||
class="btn btn--delete"
|
|
||||||
title="${state.translate('deleteFileButton')}"
|
|
||||||
onclick=${showPopup}>${state.translate('deleteFileButton')}
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="sharePage__deletePopup">
|
<div class="sharePage__deletePopup">
|
||||||
${deletePopup(
|
${deletePopup(
|
||||||
state.translate('deletePopupText'),
|
state.translate('deletePopupText'),
|
||||||
@ -47,11 +56,15 @@ module.exports = function(state, emit) {
|
|||||||
deleteFile
|
deleteFile
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<a class="link link--action"
|
|
||||||
href="/"
|
<a
|
||||||
onclick=${sendNew}>${state.translate('sendAnotherFileLink')}</a>
|
class="error btn--delete"
|
||||||
</div>
|
title="${state.translate('deleteFileButton')}"
|
||||||
|
onclick=${showPopup}>${state.translate('deleteFileButton')}
|
||||||
|
</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function showPopup() {
|
function showPopup() {
|
||||||
@ -60,30 +73,22 @@ module.exports = function(state, emit) {
|
|||||||
popup.focus();
|
popup.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendNew(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
await fadeOut('#shareWrapper');
|
|
||||||
emit('pushState', '/');
|
|
||||||
}
|
|
||||||
|
|
||||||
async function copyLink() {
|
async function copyLink() {
|
||||||
if (allowedCopy()) {
|
if (allowedCopy()) {
|
||||||
emit('copy', { url: file.url, location: 'success-screen' });
|
emit('copy', { url: file.url, location: 'success-screen' });
|
||||||
const input = document.getElementById('fileUrl');
|
const input = document.getElementById('fileUrl');
|
||||||
input.disabled = true;
|
input.disabled = true;
|
||||||
input.classList.add('input--copied');
|
|
||||||
const copyBtn = document.getElementById('copyBtn');
|
const copyBtn = document.getElementById('copyBtn');
|
||||||
copyBtn.disabled = true;
|
copyBtn.disabled = true;
|
||||||
copyBtn.classList.add('inputBtn--copied');
|
copyBtn.classList.add('copyBtn--copied');
|
||||||
copyBtn.replaceChild(
|
copyBtn.replaceChild(
|
||||||
html`<img src="${assets.get('check-16.svg')}" class="cursor--pointer">`,
|
html`<label>${state.translate('copiedUrl')}</label>`,
|
||||||
copyBtn.firstChild
|
copyBtn.firstChild
|
||||||
);
|
);
|
||||||
await delay(2000);
|
await delay(2000);
|
||||||
input.disabled = false;
|
input.disabled = false;
|
||||||
input.classList.remove('input--copied');
|
|
||||||
copyBtn.disabled = false;
|
copyBtn.disabled = false;
|
||||||
copyBtn.classList.remove('inputBtn--copied');
|
copyBtn.classList.remove('copyBtn--copied');
|
||||||
copyBtn.textContent = state.translate('copyUrlFormButton');
|
copyBtn.textContent = state.translate('copyUrlFormButton');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,18 +100,14 @@ module.exports = function(state, emit) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function expireInfo(file, translate, emit) {
|
function expireInfo(file, translate) {
|
||||||
const hours = Math.floor(EXPIRE_SECONDS / 60 / 60);
|
const hours = Math.floor(EXPIRE_SECONDS / 60 / 60);
|
||||||
const el = html`<div class="title">${raw(
|
const el = html`<div class="shareTitle">${raw(
|
||||||
translate('expireInfo', {
|
translate('expireInfo', {
|
||||||
downloadCount: '<select></select>',
|
downloadCount: translate('downloadCount', { num: file.dlimit }),
|
||||||
timespan: translate('timespanHours', { num: hours })
|
timespan: translate('timespanHours', { num: hours })
|
||||||
})
|
})
|
||||||
)}</div>`;
|
)}</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 });
|
|
||||||
el.replaceChild(selectbox(file.dlimit || 1, options, t, changed), select);
|
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
@ -1,112 +1,86 @@
|
|||||||
.sharePage {
|
.sharePage__copyText {
|
||||||
margin: 0 auto;
|
margin: 8px 0 8px;
|
||||||
display: flex;
|
word-wrap: break-word;
|
||||||
justify-content: center;
|
font-size: 15px;
|
||||||
flex-direction: column;
|
color: var(--lightTextColor);
|
||||||
width: 100%;
|
text-align: center;
|
||||||
max-width: 640px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sharePage__copyText {
|
.sharePage__passwordReminder {
|
||||||
align-self: flex-start;
|
font-size: 11px;
|
||||||
margin-top: 60px;
|
font-style: italic;
|
||||||
margin-bottom: 10px;
|
}
|
||||||
color: var(--textColor);
|
|
||||||
max-width: 614px;
|
.passwordReminder--hidden {
|
||||||
word-wrap: break-word;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sharePage__deletePopup {
|
.sharePage__deletePopup {
|
||||||
position: relative;
|
position: relative;
|
||||||
align-self: center;
|
margin-top: -70px;
|
||||||
bottom: 50px;
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shareTitle {
|
||||||
|
color: var(--textColor);
|
||||||
|
margin: 8px auto 15px;
|
||||||
|
text-align: center;
|
||||||
|
font-family: 'SF Pro Text', sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: italic;
|
||||||
|
width: 280px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.copySection {
|
.copySection {
|
||||||
display: flex;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.copySection__url {
|
.copySection__url {
|
||||||
flex: 1;
|
box-sizing: border-box;
|
||||||
height: 56px;
|
height: 30px;
|
||||||
border: 1px solid var(--primaryControlBGColor);
|
border: 1px solid var(--lightBorderColor);
|
||||||
border-radius: 6px 0 0 6px;
|
border-radius: 4px;
|
||||||
font-size: 20px;
|
font-size: 15px;
|
||||||
color: var(--inputTextColor);
|
color: var(--primaryControlBGColor);
|
||||||
|
margin: 0 0 6px;
|
||||||
|
padding: 6px;
|
||||||
font-family: 'SF Pro Text', sans-serif;
|
font-family: 'SF Pro Text', sans-serif;
|
||||||
letter-spacing: 0;
|
letter-spacing: 0;
|
||||||
line-height: 23px;
|
line-height: 18px;
|
||||||
font-weight: 300;
|
font-weight: 500;
|
||||||
padding-left: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.copySection__url:disabled {
|
.copySection__url:disabled {
|
||||||
border: 1px solid var(--successControlBGColor);
|
|
||||||
background: var(--successControlFGColor);
|
background: var(--successControlFGColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
.inputBtn--copy {
|
|
||||||
flex: 0 1 165px;
|
|
||||||
padding-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input--copied {
|
.input--copied {
|
||||||
border-color: var(--successControlBGColor);
|
border-color: var(--successControlBGColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
.inputBtn--copied,
|
.copyBtn {
|
||||||
.inputBtn--copied:hover {
|
transition: background 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copyBtn--copied,
|
||||||
|
.copyBtn--copied:hover {
|
||||||
background: var(--successControlBGColor);
|
background: var(--successControlBGColor);
|
||||||
border: 1px solid var(--successControlBGColor);
|
|
||||||
color: var(--successControlFGColor);
|
color: var(--successControlFGColor);
|
||||||
|
transition: background 0s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn--delete {
|
.btn--delete {
|
||||||
|
border: none;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
width: 176px;
|
width: 176px;
|
||||||
height: 44px;
|
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-color: rgba(12, 12, 13, 0.3);
|
margin: 10px 0 0;
|
||||||
margin-top: 50px;
|
font-size: 14px;
|
||||||
margin-bottom: 12px;
|
line-height: 16px;
|
||||||
color: #313131;
|
color: var(--errorColor);
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn--delete:hover {
|
.btn--delete:hover {
|
||||||
background: #efeff1;
|
text-decoration: underline;
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-device-width: 768px), (max-width: 768px) {
|
|
||||||
.copySection {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.copySection__url {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-device-width: 520px), (max-width: 520px) {
|
|
||||||
.copySection {
|
|
||||||
width: 100%;
|
|
||||||
flex-direction: column;
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.copySection__url {
|
|
||||||
font-size: 22px;
|
|
||||||
padding: 15px 10px;
|
|
||||||
border-radius: 6px 6px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sharePage__copyText {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inputBtn--copy {
|
|
||||||
border-radius: 0 0 6px 6px;
|
|
||||||
flex: 0 1 65px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
60
app/pages/signin/index.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
const html = require('choo/html');
|
||||||
|
const assets = require('../../../common/assets');
|
||||||
|
const title = require('../../templates/title');
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
module.exports = function(state, emit) {
|
||||||
|
return html`
|
||||||
|
|
||||||
|
<div class="page signInPage">
|
||||||
|
<a href="/" class="goBackButton">
|
||||||
|
<img src="${assets.get('back-arrow.svg')}"/>
|
||||||
|
</a>
|
||||||
|
${title(state)}
|
||||||
|
|
||||||
|
<div class="signIn__info flexible">
|
||||||
|
${state.translate('accountBenefitTitle')}
|
||||||
|
<ul>
|
||||||
|
<li>${state.translate('accountBenefitMultiFile')}</li>
|
||||||
|
<li>${state.translate('accountBenefitLargeFiles')}</li>
|
||||||
|
<li>${state.translate('accountBenefitExpiry')}</li>
|
||||||
|
<li>${state.translate('accountBenefitSync')}</li>
|
||||||
|
<li>${state.translate('accountBenefitNotify')}</li>
|
||||||
|
<li>${state.translate('accountBenefitMore')}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="signIn__form flexible">
|
||||||
|
|
||||||
|
<img class="signIn__firefoxLogo"
|
||||||
|
src="${assets.get('firefox_logo-only.svg')}"
|
||||||
|
width=56 height=56
|
||||||
|
alt="Firefox logo"/>
|
||||||
|
|
||||||
|
<div class="signIn__emailLabel">
|
||||||
|
${state.translate('signInEmailEnter')}
|
||||||
|
</div>
|
||||||
|
${state.translate('signInContinueMessage')}
|
||||||
|
|
||||||
|
<form
|
||||||
|
data-no-csrf>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="signIn__emailInput"
|
||||||
|
placeholder=${state.translate('emailEntryPlaceholder')}/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
class='noDisplay'
|
||||||
|
id="emailSubmit"
|
||||||
|
type="submit"/>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="btn" for="emailSubmit">
|
||||||
|
${state.translate('signInContinueButton')}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
};
|
33
app/pages/signin/signin.css
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
.signInPage {
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 18px;
|
||||||
|
color: var(--lightTextColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
.signIn__info {
|
||||||
|
width: 308px;
|
||||||
|
margin: 16px auto 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signIn__firefoxLogo {
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signIn__emailLabel {
|
||||||
|
font-size: 22px;
|
||||||
|
margin: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signIn__emailInput {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 310px;
|
||||||
|
height: 40px;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 8px;
|
||||||
|
font-size: 18px;
|
||||||
|
color: var(--lightTextColor);
|
||||||
|
}
|
@ -1,11 +1,13 @@
|
|||||||
const html = require('choo/html');
|
const html = require('choo/html');
|
||||||
const assets = require('../../../common/assets');
|
const assets = require('../../../common/assets');
|
||||||
|
const title = require('../../templates/title');
|
||||||
|
|
||||||
module.exports = function(state) {
|
module.exports = function(state) {
|
||||||
let strings = {};
|
let strings = {};
|
||||||
let why = '';
|
let why = '';
|
||||||
let url = '';
|
let url = '';
|
||||||
let buttonAction = '';
|
let buttonAction = '';
|
||||||
|
|
||||||
if (state.params.reason !== 'outdated') {
|
if (state.params.reason !== 'outdated') {
|
||||||
strings = unsupportedStrings(state);
|
strings = unsupportedStrings(state);
|
||||||
why = html`
|
why = html`
|
||||||
@ -28,20 +30,26 @@ module.exports = function(state) {
|
|||||||
${strings.button}
|
${strings.button}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="unsupportedPage">
|
<div class="page unsupportedPage">
|
||||||
<div class="title">${strings.title}</div>
|
${title(state)}
|
||||||
<div class="description">
|
<div class="error unsupportedPage__error">${strings.header}</div>
|
||||||
|
<div class="description flexible">
|
||||||
${strings.description}
|
${strings.description}
|
||||||
</div>
|
|
||||||
${why}
|
${why}
|
||||||
<a href="${url}" class="firefoxDownload">
|
</div>
|
||||||
|
|
||||||
|
<div class="flexible firefoxDownload">
|
||||||
|
<a href="${url}" class="firefoxDownload__button">
|
||||||
<img
|
<img
|
||||||
src="${assets.get('firefox_logo-only.svg')}"
|
src="${assets.get('firefox_logo-only.svg')}"
|
||||||
class="firefoxDownload__logo"
|
class="firefoxDownload__logo"
|
||||||
alt="Firefox"/>
|
alt="Firefox"/>
|
||||||
${buttonAction}
|
${buttonAction}
|
||||||
</a>
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="unsupportedPage__info">
|
<div class="unsupportedPage__info">
|
||||||
${strings.explainer}
|
${strings.explainer}
|
||||||
</div>
|
</div>
|
||||||
@ -50,7 +58,7 @@ module.exports = function(state) {
|
|||||||
|
|
||||||
function outdatedStrings(state) {
|
function outdatedStrings(state) {
|
||||||
return {
|
return {
|
||||||
title: state.translate('notSupportedHeader'),
|
header: state.translate('notSupportedHeader'),
|
||||||
description: state.translate('notSupportedOutdatedDetail'),
|
description: state.translate('notSupportedOutdatedDetail'),
|
||||||
button: state.translate('updateFirefox'),
|
button: state.translate('updateFirefox'),
|
||||||
explainer: state.translate('uploadPageExplainer')
|
explainer: state.translate('uploadPageExplainer')
|
||||||
@ -59,7 +67,7 @@ function outdatedStrings(state) {
|
|||||||
|
|
||||||
function unsupportedStrings(state) {
|
function unsupportedStrings(state) {
|
||||||
return {
|
return {
|
||||||
title: state.translate('notSupportedHeader'),
|
header: state.translate('notSupportedHeader'),
|
||||||
description: state.translate('notSupportedDetail'),
|
description: state.translate('notSupportedDetail'),
|
||||||
button: state.translate('downloadFirefoxButtonSub'),
|
button: state.translate('downloadFirefoxButtonSub'),
|
||||||
explainer: state.translate('uploadPageExplainer')
|
explainer: state.translate('uploadPageExplainer')
|
||||||
|
@ -1,26 +1,30 @@
|
|||||||
.unsupportedPage {
|
.unsupportedPage {
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-direction: column;
|
}
|
||||||
|
|
||||||
|
.unsupportedPage__error {
|
||||||
|
margin: 10px 0 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.unsupportedPage__info {
|
.unsupportedPage__info {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 23px;
|
|
||||||
text-align: center;
|
|
||||||
color: var(--lightTextColor);
|
color: var(--lightTextColor);
|
||||||
margin: 0 auto 23px;
|
margin: 0 auto 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.firefoxDownload {
|
.firefoxDownload {
|
||||||
margin-bottom: 181px;
|
flex: 2;
|
||||||
height: 80px;
|
}
|
||||||
background: #98e02b;
|
|
||||||
|
.firefoxDownload__button {
|
||||||
|
margin: 0 auto 20px;
|
||||||
|
height: 70px;
|
||||||
|
width: 250px;
|
||||||
|
background: #12bc00;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border: 0;
|
border: 0;
|
||||||
box-shadow: 0 5px 3px rgb(234, 234, 234);
|
|
||||||
font-family: 'Fira Sans', 'segoe ui', sans-serif;
|
font-family: 'Fira Sans', 'segoe ui', sans-serif;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--primaryControlFGColor);
|
color: var(--primaryControlFGColor);
|
||||||
@ -29,21 +33,22 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
padding: 0 25px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.firefoxDownload__logo {
|
.firefoxDownload__logo {
|
||||||
width: 70px;
|
width: 55px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.firefoxDownload__action {
|
.firefoxDownload__action {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
margin-left: 20.4px;
|
text-transform: uppercase;
|
||||||
|
margin-left: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.firefoxDownload__text {
|
.firefoxDownload__text {
|
||||||
|
text-transform: none;
|
||||||
font-family: 'Fira Sans', 'segoe ui', sans-serif;
|
font-family: 'Fira Sans', 'segoe ui', sans-serif;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
font-size: 18px;
|
font-size: 17px;
|
||||||
letter-spacing: -0.69px;
|
|
||||||
}
|
}
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
const html = require('choo/html');
|
|
||||||
const progress = require('../../templates/progress');
|
|
||||||
const { bytes } = require('../../utils');
|
|
||||||
|
|
||||||
module.exports = function(state, emit) {
|
|
||||||
const transfer = state.transfer;
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<div class="page effect--fadeIn">
|
|
||||||
<div class="title">
|
|
||||||
${state.translate('uploadingPageProgress', {
|
|
||||||
filename: transfer.file.name,
|
|
||||||
size: bytes(transfer.file.size)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<div class="description"></div>
|
|
||||||
${progress(transfer.progressRatio, transfer.progressIndefinite)}
|
|
||||||
<div class="progressSection">
|
|
||||||
<div class="progressSection__text">
|
|
||||||
${state.translate(transfer.msg, transfer.sizes)}
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
id="cancel-upload"
|
|
||||||
class="btn btn--cancel"
|
|
||||||
title="${state.translate('uploadingPageCancel')}"
|
|
||||||
onclick=${cancel}>
|
|
||||||
${state.translate('uploadingPageCancel')}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
function cancel() {
|
|
||||||
const btn = document.getElementById('cancel-upload');
|
|
||||||
btn.disabled = true;
|
|
||||||
btn.textContent = state.translate('uploadCancelNotification');
|
|
||||||
emit('cancel');
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,50 +1,86 @@
|
|||||||
/* global MAXFILESIZE */
|
|
||||||
const html = require('choo/html');
|
const html = require('choo/html');
|
||||||
const assets = require('../../../common/assets');
|
const assets = require('../../../common/assets');
|
||||||
const fileList = require('../../templates/fileList');
|
const { checkSize } = require('../../utils');
|
||||||
const { bytes, fadeOut } = require('../../utils');
|
const title = require('../../templates/title');
|
||||||
|
const setPasswordSection = require('../../templates/setPasswordSection');
|
||||||
|
const uploadBox = require('../../templates/uploadedFileList');
|
||||||
|
const expireInfo = require('../../templates/expireInfo');
|
||||||
|
|
||||||
module.exports = function(state, emit) {
|
module.exports = function(state, emit) {
|
||||||
// the page flickers if both the server and browser set 'effect--fadeIn'
|
// the page flickers if both the server and browser set 'effect--fadeIn'
|
||||||
const fade = state.layout ? '' : 'effect--fadeIn';
|
const fade = state.layout ? '' : 'effect--fadeIn';
|
||||||
|
const files = state.files ? state.files : [];
|
||||||
|
|
||||||
|
const optionClass = state.uploading ? 'uploadOptions--faded' : '';
|
||||||
|
const btnUploading = state.uploading ? 'btn--stripes' : '';
|
||||||
|
const faded = files.length > 0 ? 'uploadArea--faded' : '';
|
||||||
|
const selectFileClass = files.length > 0 ? 'btn--hidden' : '';
|
||||||
|
const sendFileClass = files.length > 0 ? '' : 'btn--hidden';
|
||||||
|
|
||||||
|
let btnText = '';
|
||||||
|
|
||||||
|
if (state.encrypting) {
|
||||||
|
btnText = state.translate('encryptingFile');
|
||||||
|
} else if (state.uploading) {
|
||||||
|
btnText = `sending... ${Math.floor(state.transfer.progressRatio * 100)}%`;
|
||||||
|
} else {
|
||||||
|
//default pre-upload text
|
||||||
|
btnText = state.translate('uploadSuccessConfirmHeader');
|
||||||
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div id="page-one" class="${fade}">
|
<div id="page-one" class="page ${fade}">
|
||||||
<div class="title">${state.translate('uploadPageHeader')}</div>
|
${title(state)}
|
||||||
<div class="description">
|
|
||||||
<div>${state.translate('uploadPageExplainer')}</div>
|
<label class="uploadArea"
|
||||||
<a
|
|
||||||
href="https://testpilot.firefox.com/experiments/send"
|
|
||||||
class="link">
|
|
||||||
${state.translate('uploadPageLearnMore')}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="uploadArea"
|
|
||||||
ondragover=${dragover}
|
ondragover=${dragover}
|
||||||
ondragleave=${dragleave}>
|
ondragleave=${dragleave}>
|
||||||
|
|
||||||
|
${uploadBox(files, state, emit)}
|
||||||
|
|
||||||
|
<div class="uploadedFilesWrapper ${faded}">
|
||||||
<img
|
<img
|
||||||
src="${assets.get('upload.svg')}"
|
class="uploadArea__icon"
|
||||||
|
src="${assets.get('addfile.svg')}"
|
||||||
title="${state.translate('uploadSvgAlt')}"/>
|
title="${state.translate('uploadSvgAlt')}"/>
|
||||||
<div class="uploadArea__msg">
|
<div class="uploadArea__msg">
|
||||||
${state.translate('uploadPageDropMessage')}
|
${state.translate('uploadDropDragMessage')}
|
||||||
</div>
|
</div>
|
||||||
<span class="uploadArea__sizeMsg">
|
|
||||||
${state.translate('uploadPageSizeMessage')}
|
<span class="uploadArea__clickMsg">
|
||||||
|
${state.translate('uploadDropClickMessage')}
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<input id="file-upload"
|
<input id="file-upload"
|
||||||
class="inputFile"
|
class="inputFile fileBox"
|
||||||
type="file"
|
type="file"
|
||||||
multiple
|
multiple
|
||||||
name="fileUploaded"
|
name="fileUploaded"
|
||||||
onfocus=${onfocus}
|
onfocus=${onfocus}
|
||||||
onblur=${onblur}
|
onblur=${onblur}
|
||||||
onchange=${upload} />
|
onchange=${addFiles} />
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="uploadOptions ${optionClass}">
|
||||||
|
${expireInfo(state)}
|
||||||
|
${setPasswordSection(state)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<label for="file-upload"
|
<label for="file-upload"
|
||||||
class="btn btn--file"
|
class="btn btn--file ${selectFileClass}"
|
||||||
title="${state.translate('uploadPageBrowseButton1')}">
|
title="${state.translate('uploadPageBrowseButton1')}">
|
||||||
${state.translate('uploadPageBrowseButton1')}
|
${state.translate('uploadPageBrowseButton1')}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
|
||||||
${fileList(state, emit)}
|
<button
|
||||||
|
class="btn ${btnUploading} ${sendFileClass}"
|
||||||
|
onclick=${upload}
|
||||||
|
title="${btnText}">
|
||||||
|
${btnText}
|
||||||
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -66,22 +102,23 @@ module.exports = function(state, emit) {
|
|||||||
event.target.classList.remove('inputFile--focused');
|
event.target.classList.remove('inputFile--focused');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function addFiles(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const target = event.target;
|
||||||
|
checkSize(target.files, state.files);
|
||||||
|
emit('addFiles', { files: target.files });
|
||||||
|
}
|
||||||
|
|
||||||
async function upload(event) {
|
async function upload(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const Archive = require('../../archive').default;
|
|
||||||
const target = event.target;
|
|
||||||
const file = new Archive(target.files);
|
|
||||||
|
|
||||||
if (file.size === 0) {
|
if (files.length > 0) {
|
||||||
return;
|
emit('upload', {
|
||||||
|
files,
|
||||||
|
type: 'click',
|
||||||
|
dlCount: state.downloadCount,
|
||||||
|
password: state.password
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (file.size > MAXFILESIZE) {
|
|
||||||
// eslint-disable-next-line no-alert
|
|
||||||
alert(state.translate('fileTooBig', { size: bytes(MAXFILESIZE) }));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await fadeOut('#page-one');
|
|
||||||
emit('upload', { file, type: 'click' });
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,61 +1,64 @@
|
|||||||
.uploadArea {
|
.uploadArea {
|
||||||
border: 3px dashed rgba(0, 148, 251, 0.5);
|
position: relative;
|
||||||
margin: 0 auto 10px;
|
|
||||||
height: 255px;
|
|
||||||
border-radius: 4px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
border: 1px dashed rgba(12, 12, 13, 0.2);
|
||||||
|
margin: 0 0 10px;
|
||||||
|
height: 400px;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: scroll;
|
||||||
transition: transform 150ms;
|
transition: transform 150ms;
|
||||||
padding: 15px;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.uploadArea__msg {
|
.uploadArea__msg {
|
||||||
font-size: 22px;
|
font-size: 15px;
|
||||||
color: var(--lightTextColor);
|
color: var(--lightTextColor);
|
||||||
margin: 20px 0 10px;
|
margin: 12px 0 0;
|
||||||
font-family: 'SF Pro Text', sans-serif;
|
font-family: 'SF Pro Text', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.uploadArea__sizeMsg {
|
.uploadArea__clickMsg {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 16px;
|
line-height: 12px;
|
||||||
color: var(--lightTextColor);
|
color: var(--lightTextColor);
|
||||||
margin-bottom: 22px;
|
margin: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.uploadArea--dragging {
|
.uploadArea--dragging {
|
||||||
border: 5px dashed rgba(0, 148, 251, 0.5);
|
border: 1px dashed rgba(12, 12, 13, 0.4);
|
||||||
height: 251px;
|
|
||||||
transform: scale(1.04);
|
transform: scale(1.04);
|
||||||
border-radius: 4.2px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
flex-direction: column;
|
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.uploadArea--dragging * {
|
.uploadArea--faded * {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uploadArea--noEvents,
|
||||||
|
.uploadArea--noEvents * {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn--file {
|
.btn--file {
|
||||||
font-size: 20px;
|
display: inline-block;
|
||||||
min-width: 240px;
|
background-color: #737373;
|
||||||
height: 60px;
|
}
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
.btn--file:hover {
|
||||||
align-items: center;
|
background-color: #636363;
|
||||||
padding: 0 10px;
|
}
|
||||||
|
|
||||||
|
.btn--hidden {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inputFile {
|
.inputFile {
|
||||||
opacity: 0;
|
display: none;
|
||||||
position: absolute;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.inputFile--focused + .btn--file {
|
.inputFile--focused + .btn--file {
|
||||||
@ -63,3 +66,24 @@
|
|||||||
outline: 1px dotted #000;
|
outline: 1px dotted #000;
|
||||||
outline: -webkit-focus-ring-color auto 5px;
|
outline: -webkit-focus-ring-color auto 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.uploadArea > .uploadedFiles {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
flex: none;
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uploadOptions {
|
||||||
|
text-align: left;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--lightTextColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
.uploadOptions--faded {
|
||||||
|
opacity: 0.5;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
@ -1,60 +1,24 @@
|
|||||||
const preview = require('../pages/preview');
|
const preview = require('../pages/preview');
|
||||||
const download = require('../pages/download');
|
const password = require('../pages/password');
|
||||||
const notFound = require('../pages/notFound');
|
|
||||||
const downloadPassword = require('../templates/downloadPassword');
|
|
||||||
const downloadButton = require('../templates/downloadButton');
|
|
||||||
|
|
||||||
function hasFileInfo() {
|
|
||||||
return !!document.getElementById('dl-file');
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFileInfoFromDOM() {
|
|
||||||
const el = document.getElementById('dl-file');
|
|
||||||
if (!el) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
nonce: el.getAttribute('data-nonce'),
|
|
||||||
requiresPassword: !!+el.getAttribute('data-requires-password')
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createFileInfo(state) {
|
|
||||||
const metadata = getFileInfoFromDOM();
|
|
||||||
return {
|
|
||||||
id: state.params.id,
|
|
||||||
secretKey: state.params.key,
|
|
||||||
nonce: metadata.nonce,
|
|
||||||
requiresPassword: metadata.requiresPassword
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = function(state, emit) {
|
module.exports = function(state, emit) {
|
||||||
if (!state.fileInfo) {
|
if (!state.fileInfo) {
|
||||||
// This is a fresh page load
|
emit('getPasswordExist', { id: state.params.id });
|
||||||
// We need to parse the file info from the server's html
|
return;
|
||||||
if (!hasFileInfo()) {
|
|
||||||
return notFound(state, emit);
|
|
||||||
}
|
}
|
||||||
state.fileInfo = createFileInfo(state);
|
|
||||||
|
|
||||||
if (!state.fileInfo.requiresPassword) {
|
state.fileInfo.id = state.params.id;
|
||||||
|
state.fileInfo.secretKey = state.params.key;
|
||||||
|
|
||||||
|
if (!state.transfer && !state.fileInfo.requiresPassword) {
|
||||||
emit('getMetadata');
|
emit('getMetadata');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state.transfer) {
|
||||||
|
return preview(state, emit);
|
||||||
}
|
}
|
||||||
|
|
||||||
let pageAction = null; //default state: we don't have file metadata
|
if (state.fileInfo.requiresPassword && !state.fileInfo.password) {
|
||||||
if (state.transfer) {
|
return password(state, emit);
|
||||||
const s = state.transfer.state;
|
|
||||||
if (['downloading', 'decrypting', 'complete'].indexOf(s) > -1) {
|
|
||||||
// Downloading is in progress
|
|
||||||
return download(state, emit);
|
|
||||||
}
|
}
|
||||||
// we have file metadata
|
|
||||||
pageAction = downloadButton(state, emit);
|
|
||||||
} else if (state.fileInfo.requiresPassword && !state.fileInfo.password) {
|
|
||||||
// we're waiting on the user for a valid password
|
|
||||||
pageAction = downloadPassword(state, emit);
|
|
||||||
}
|
|
||||||
return preview(state, pageAction);
|
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
const welcome = require('../pages/welcome');
|
const welcome = require('../pages/welcome');
|
||||||
const upload = require('../pages/upload');
|
|
||||||
|
|
||||||
module.exports = function(state, emit) {
|
module.exports = function(state, emit) {
|
||||||
if (state.uploading) {
|
|
||||||
return upload(state, emit);
|
|
||||||
}
|
|
||||||
return welcome(state, emit);
|
return welcome(state, emit);
|
||||||
};
|
};
|
||||||
|
@ -5,7 +5,10 @@ const download = require('./download');
|
|||||||
const header = require('../templates/header');
|
const header = require('../templates/header');
|
||||||
const footer = require('../templates/footer');
|
const footer = require('../templates/footer');
|
||||||
const fxPromo = require('../templates/fxPromo');
|
const fxPromo = require('../templates/fxPromo');
|
||||||
|
const signupPromo = require('../templates/signupPromo');
|
||||||
const activeBackground = require('../templates/activeBackground');
|
const activeBackground = require('../templates/activeBackground');
|
||||||
|
const fileList = require('../templates/fileList');
|
||||||
|
const profile = require('../templates/userAccount');
|
||||||
|
|
||||||
nanotiming.disabled = true;
|
nanotiming.disabled = true;
|
||||||
const app = choo();
|
const app = choo();
|
||||||
@ -20,6 +23,7 @@ function body(template) {
|
|||||||
return function(state, emit) {
|
return function(state, emit) {
|
||||||
const b = html`<body class="background ${activeBackground(state)}">
|
const b = html`<body class="background ${activeBackground(state)}">
|
||||||
${banner(state, emit)}
|
${banner(state, emit)}
|
||||||
|
${signupPromo(state)}
|
||||||
${header(state)}
|
${header(state)}
|
||||||
<main class="main">
|
<main class="main">
|
||||||
<noscript>
|
<noscript>
|
||||||
@ -35,11 +39,17 @@ function body(template) {
|
|||||||
</noscript>
|
</noscript>
|
||||||
<div class="stripedBox">
|
<div class="stripedBox">
|
||||||
<div class="mainContent">
|
<div class="mainContent">
|
||||||
|
|
||||||
|
${profile(state)}
|
||||||
|
|
||||||
${template(state, emit)}
|
${template(state, emit)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="spacer"></div>
|
<div class="spacer"></div>
|
||||||
<div class="uploads"></div>
|
<div class="uploads">
|
||||||
|
${fileList(state)}
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
${footer(state)}
|
${footer(state)}
|
||||||
</body>`;
|
</body>`;
|
||||||
@ -55,11 +65,11 @@ app.route('/', body(require('./home')));
|
|||||||
app.route('/share/:id', body(require('../pages/share')));
|
app.route('/share/:id', body(require('../pages/share')));
|
||||||
app.route('/download/:id', body(download));
|
app.route('/download/:id', body(download));
|
||||||
app.route('/download/:id/:key', body(download));
|
app.route('/download/:id/:key', body(download));
|
||||||
app.route('/completed', body(require('../pages/completed')));
|
|
||||||
app.route('/unsupported/:reason', body(require('../pages/unsupported')));
|
app.route('/unsupported/:reason', body(require('../pages/unsupported')));
|
||||||
app.route('/legal', body(require('../pages/legal')));
|
app.route('/legal', body(require('../pages/legal')));
|
||||||
app.route('/error', body(require('../pages/error')));
|
app.route('/error', body(require('../pages/error')));
|
||||||
app.route('/blank', body(require('../pages/blank')));
|
app.route('/blank', body(require('../pages/blank')));
|
||||||
app.route('*', body(require('../pages/notFound')));
|
app.route('*', body(require('../pages/notFound')));
|
||||||
|
app.route('/signin', body(require('../pages/signin')));
|
||||||
|
|
||||||
module.exports = app;
|
module.exports = app;
|
||||||
|
@ -47,6 +47,7 @@ class Storage {
|
|||||||
if (!f.id) {
|
if (!f.id) {
|
||||||
f.id = f.fileId;
|
f.id = f.fileId;
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.push(f);
|
fs.push(f);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// obviously you're not a golfer
|
// obviously you're not a golfer
|
||||||
|
@ -1,6 +1,20 @@
|
|||||||
.btn--download {
|
.btn--download {
|
||||||
width: 180px;
|
margin: 0 0 13px;
|
||||||
height: 44px;
|
}
|
||||||
margin-top: 20px;
|
|
||||||
margin-bottom: 30px;
|
.btn--complete,
|
||||||
|
.btn--complete:hover {
|
||||||
|
background-color: var(--successControlBGColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn--blueStripes {
|
||||||
|
background: repeating-linear-gradient(
|
||||||
|
-65deg,
|
||||||
|
#3282f2 0,
|
||||||
|
#3282f2 17px,
|
||||||
|
#3c87eb 17px,
|
||||||
|
#3c87eb 30px
|
||||||
|
);
|
||||||
|
background-size: 300% 300%;
|
||||||
|
animation: barberpole 12s linear infinite;
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,36 @@
|
|||||||
const html = require('choo/html');
|
const html = require('choo/html');
|
||||||
|
const percent = require('../../utils').percent;
|
||||||
|
|
||||||
module.exports = function(state, emit) {
|
module.exports = function(state, emit) {
|
||||||
|
const downloadState = state.transfer.state;
|
||||||
|
const progress = percent(state.transfer.progressRatio);
|
||||||
|
|
||||||
|
let btnText = '';
|
||||||
|
let btnClass = '';
|
||||||
|
|
||||||
|
if (downloadState === 'complete') {
|
||||||
|
btnText = state.translate('downloadFinish');
|
||||||
|
btnClass = 'btn--complete';
|
||||||
|
} else if (downloadState === 'decrypting') {
|
||||||
|
btnText = state.translate('decryptingFile');
|
||||||
|
btnClass = 'btn--blueStripes';
|
||||||
|
} else if (downloadState === 'downloading') {
|
||||||
|
btnText = state.translate('downloadProgressButton', { progress });
|
||||||
|
btnClass = 'btn--blueStripes';
|
||||||
|
} else {
|
||||||
|
btnText = state.translate('downloadButtonLabel');
|
||||||
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<button class="btn btn--download"
|
<button class="btn btn--download ${btnClass}"
|
||||||
onclick=${download}>${state.translate('downloadButtonLabel')}
|
onclick=${download}>
|
||||||
|
${btnText}
|
||||||
</button>`;
|
</button>`;
|
||||||
|
|
||||||
function download(event) {
|
function download(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
if (downloadState !== 'complete') {
|
||||||
emit('download', state.fileInfo);
|
emit('download', state.fileInfo);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,22 +1,31 @@
|
|||||||
.passwordSection {
|
.passwordSection {
|
||||||
text-align: left;
|
margin: auto;
|
||||||
|
text-align: center;
|
||||||
padding: 40px 0;
|
padding: 40px 0;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.passwordForm {
|
.passwordForm {
|
||||||
display: flex;
|
margin: 13px;
|
||||||
flex-wrap: nowrap;
|
|
||||||
width: 100%;
|
|
||||||
padding: 10px 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-device-width: 520px), (max-width: 520px) {
|
.passwordForm__input {
|
||||||
.passwordSection {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
height: 40px;
|
||||||
|
box-sizing: border-box;
|
||||||
.passwordForm {
|
}
|
||||||
flex-direction: column;
|
|
||||||
}
|
.unlockBtn {
|
||||||
|
margin-top: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unlockBtn--error,
|
||||||
|
.unlockBtn--error:hover {
|
||||||
|
background-color: var(--errorColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
.passwordForm__error {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
@ -3,33 +3,33 @@ const html = require('choo/html');
|
|||||||
module.exports = function(state, emit) {
|
module.exports = function(state, emit) {
|
||||||
const fileInfo = state.fileInfo;
|
const fileInfo = state.fileInfo;
|
||||||
const invalid = fileInfo.password === null;
|
const invalid = fileInfo.password === null;
|
||||||
const label = invalid
|
|
||||||
? html`
|
const visible = invalid ? 'visible' : '';
|
||||||
<label class="error" for="password-input">
|
const invalidBtn = invalid ? 'unlockBtn--error' : '';
|
||||||
${state.translate('passwordTryAgain')}
|
|
||||||
</label>`
|
|
||||||
: html`
|
|
||||||
<label for="password-input">
|
|
||||||
${state.translate('unlockInputLabel')}
|
|
||||||
</label>`;
|
|
||||||
const inputClass = invalid
|
|
||||||
? 'input input--noBtn input--error'
|
|
||||||
: 'input input--noBtn';
|
|
||||||
const div = html`
|
const div = html`
|
||||||
<div class="passwordSection">
|
<div class="passwordSection">
|
||||||
${label}
|
|
||||||
|
<label
|
||||||
|
class="error passwordForm__error ${visible}"
|
||||||
|
for="password-input">
|
||||||
|
${state.translate('passwordTryAgain')}
|
||||||
|
</label>
|
||||||
|
|
||||||
<form class="passwordForm" onsubmit=${checkPassword} data-no-csrf>
|
<form class="passwordForm" onsubmit=${checkPassword} data-no-csrf>
|
||||||
<input id="password-input"
|
<input id="password-input"
|
||||||
class="${inputClass}"
|
class="input passwordForm__input"
|
||||||
maxlength="64"
|
maxlength="64"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
placeholder="${state.translate('unlockInputPlaceholder')}"
|
placeholder="${state.translate('unlockInputPlaceholder')}"
|
||||||
oninput=${inputChanged}
|
oninput=${inputChanged}
|
||||||
type="password" />
|
type="password" />
|
||||||
|
|
||||||
<input type="submit"
|
<input type="submit"
|
||||||
id="password-btn"
|
id="password-btn"
|
||||||
class="inputBtn inputBtn--hidden"
|
class="btn unlockBtn ${invalidBtn}"
|
||||||
value="${state.translate('unlockButtonLabel')}"/>
|
value="${state.translate('unlockInputLabel')}"/>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
@ -38,16 +38,10 @@ module.exports = function(state, emit) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function inputChanged() {
|
function inputChanged() {
|
||||||
const input = document.getElementById('password-input');
|
const input = document.querySelector('.passwordForm__error');
|
||||||
|
input.classList.remove('visible');
|
||||||
const btn = document.getElementById('password-btn');
|
const btn = document.getElementById('password-btn');
|
||||||
input.classList.remove('input--error');
|
btn.classList.remove('unlockBtn--error');
|
||||||
if (input.value.length > 0) {
|
|
||||||
btn.classList.remove('inputBtn--hidden');
|
|
||||||
input.classList.remove('input--noBtn');
|
|
||||||
} else {
|
|
||||||
btn.classList.add('inputBtn--hidden');
|
|
||||||
input.classList.add('input--noBtn');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkPassword(event) {
|
function checkPassword(event) {
|
||||||
|
36
app/templates/expireInfo/index.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
const html = require('choo/html');
|
||||||
|
const raw = require('choo/html/raw');
|
||||||
|
const selectbox = require('../selectbox');
|
||||||
|
|
||||||
|
module.exports = function(state) {
|
||||||
|
const el = html`<div> ${raw(
|
||||||
|
state.translate('frontPageExpireInfo', {
|
||||||
|
downloadCount: '<select id=dlCount></select>',
|
||||||
|
timespan: state.translate('timespanHours', { num: 24 }) //'<select id=timespan></select>'
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
const dlCountSelect = el.querySelector('#dlCount');
|
||||||
|
el.replaceChild(
|
||||||
|
selectbox(
|
||||||
|
state.downloadCount || 1,
|
||||||
|
[1, 2, 3, 4, 5, 20],
|
||||||
|
num => state.translate('downloadCount', { num }),
|
||||||
|
value => {
|
||||||
|
state.downloadCount = value;
|
||||||
|
}
|
||||||
|
),
|
||||||
|
dlCountSelect
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
const timeSelect = el.querySelector('#timespan');
|
||||||
|
el.replaceChild(
|
||||||
|
selectbox(1, [1, 2, 3, 4, 5], num => num, () => {}),
|
||||||
|
timeSelect
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
|
||||||
|
return el;
|
||||||
|
};
|
@ -1,26 +1,94 @@
|
|||||||
.fileData {
|
.fileToast {
|
||||||
font-size: 15px;
|
margin: 13px 0 0;
|
||||||
vertical-align: top;
|
|
||||||
color: var(--lightTextColor);
|
|
||||||
padding: 17px 19px 0;
|
|
||||||
line-height: 23px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fileData--overflow {
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
max-width: 0;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
font-size: 11px;
|
||||||
|
line-height: 18px;
|
||||||
|
color: var(--lightTextColor);
|
||||||
|
background-color: var(--pageBGColor);
|
||||||
|
position: relative;
|
||||||
|
box-shadow: 0 0 0 3px rgba(12, 12, 12, 0.2);
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 53px;
|
||||||
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fileData--center {
|
.fileToast__content {
|
||||||
text-align: center;
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-device-width: 520px), (max-width: 520px) {
|
.fileToast::after {
|
||||||
.fileData {
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
content: '';
|
||||||
|
transition: all 0.25s;
|
||||||
|
top: 0;
|
||||||
|
left: 50%;
|
||||||
|
width: 0;
|
||||||
|
height: 100%;
|
||||||
|
background-color: var(--primaryControlBGColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileToast:hover {
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileToast--active {
|
||||||
|
color: var(--primaryControlFGColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileToast--active::after {
|
||||||
|
left: 0%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileData {
|
||||||
|
margin: 8px 16px 8px 44px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileName {
|
||||||
|
margin: 0;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
padding: 17px 5px 0;
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileInfo {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileToast .fileIcon {
|
||||||
|
margin: 2px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-device-width: 750px), (max-width: 750px) {
|
||||||
|
.fileToast {
|
||||||
|
height: 32px;
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileToast__content {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileData {
|
||||||
|
flex: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileInfo {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileToast .fileIcon {
|
||||||
|
margin: 0;
|
||||||
|
transform: scale(0.5);
|
||||||
|
color: transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,73 +1,50 @@
|
|||||||
const html = require('choo/html');
|
const html = require('choo/html');
|
||||||
const assets = require('../../../common/assets');
|
|
||||||
const number = require('../../utils').number;
|
const number = require('../../utils').number;
|
||||||
const deletePopup = require('../popup');
|
const bytes = require('../../utils').bytes;
|
||||||
|
const fileIcon = require('../fileIcon');
|
||||||
|
|
||||||
module.exports = function(file, state, emit) {
|
module.exports = function(file, state) {
|
||||||
const ttl = file.expiresAt - Date.now();
|
const ttl = file.expiresAt - Date.now();
|
||||||
const remainingTime =
|
const remainingTime =
|
||||||
timeLeft(ttl, state) || state.translate('linkExpiredAlt');
|
timeLeft(ttl, state) || state.translate('linkExpiredAlt');
|
||||||
const downloadLimit = file.dlimit || 1;
|
const downloadLimit = file.dlimit || 1;
|
||||||
const totalDownloads = file.dtotal || 0;
|
const totalDownloads = file.dtotal || 0;
|
||||||
|
|
||||||
|
const multiFiles = file.manifest.files;
|
||||||
|
const fileName =
|
||||||
|
multiFiles.length > 1
|
||||||
|
? `${multiFiles[0].name} + ${state.translate('fileCount', {
|
||||||
|
num: multiFiles.length - 1
|
||||||
|
})}`
|
||||||
|
: file.name;
|
||||||
|
|
||||||
|
const activeClass = isOnSharePage() ? 'fileToast--active' : '';
|
||||||
return html`
|
return html`
|
||||||
<tr id="${file.id}">
|
<a href=${toastClick()}>
|
||||||
<td class="fileData fileData--overflow" title="${file.name}">
|
<li class="fileToast ${activeClass}" id="${file.id}">
|
||||||
<a class="link" href="/share/${file.id}">${file.name}</a>
|
<div class="fileToast__content">
|
||||||
</td>
|
${fileIcon(file.name, file._hasPassword)}
|
||||||
<td class="fileData fileData--center">
|
<div class="fileData">
|
||||||
<img
|
<p class="fileName">${fileName}</p>
|
||||||
onclick=${copyClick}
|
<p class="fileInfo">
|
||||||
src="${assets.get('copy-16.svg')}"
|
<span>${bytes(file.size)}</span> ·
|
||||||
class="cursor--pointer"
|
<span>${state.translate('downloadCount', {
|
||||||
title="${state.translate('copyUrlHover')}"
|
num: `${number(totalDownloads)} / ${number(downloadLimit)}`
|
||||||
tabindex="0">
|
})}</span>
|
||||||
<span hidden="true">
|
<span>${remainingTime}</span>
|
||||||
${state.translate('copiedUrl')}
|
</p>
|
||||||
</span>
|
</div>
|
||||||
</td>
|
</div>
|
||||||
<td class="fileData fileData--overflow">${remainingTime}</td>
|
</li>
|
||||||
<td class="fileData fileData--center">${number(totalDownloads)} / ${number(
|
</a>
|
||||||
downloadLimit
|
|
||||||
)}</td>
|
|
||||||
<td class="fileData fileData--center">
|
|
||||||
<img
|
|
||||||
onclick=${showPopup}
|
|
||||||
src="${assets.get('close-16.svg')}"
|
|
||||||
class="cursor--pointer"
|
|
||||||
title="${state.translate('deleteButtonHover')}"
|
|
||||||
tabindex="0">
|
|
||||||
${deletePopup(
|
|
||||||
state.translate('deletePopupText'),
|
|
||||||
state.translate('deletePopupYes'),
|
|
||||||
state.translate('deletePopupCancel'),
|
|
||||||
deleteFile
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function copyClick(e) {
|
function toastClick() {
|
||||||
emit('copy', { url: file.url, location: 'upload-list' });
|
return isOnSharePage() ? '/' : `/share/${file.id}`;
|
||||||
const icon = e.target;
|
|
||||||
const text = e.target.nextSibling;
|
|
||||||
icon.hidden = true;
|
|
||||||
text.hidden = false;
|
|
||||||
setTimeout(() => {
|
|
||||||
icon.hidden = false;
|
|
||||||
text.hidden = true;
|
|
||||||
}, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function showPopup() {
|
function isOnSharePage() {
|
||||||
const tr = document.getElementById(file.id);
|
return state.href.includes('/share/') && state.params.id === file.id;
|
||||||
const popup = tr.querySelector('.popup');
|
|
||||||
popup.classList.add('popup--show');
|
|
||||||
popup.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteFile() {
|
|
||||||
emit('delete', { file, location: 'upload-list' });
|
|
||||||
emit('render');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
28
app/templates/fileIcon/fileIcon.css
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
.fileIcon {
|
||||||
|
position: relative;
|
||||||
|
float: left;
|
||||||
|
pointer-events: none;
|
||||||
|
margin: 8px;
|
||||||
|
color: #fff;
|
||||||
|
background-image: url('../assets/red_file.svg');
|
||||||
|
width: 22px;
|
||||||
|
height: 32px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileIcon__lock {
|
||||||
|
margin: 7px 0 0 5px;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileIcon__lock--visible {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileIcon__fileType {
|
||||||
|
position: absolute;
|
||||||
|
margin: 16px 0 0 2px;
|
||||||
|
font-size: 7px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
17
app/templates/fileIcon/index.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
const html = require('choo/html');
|
||||||
|
const assets = require('../../../common/assets');
|
||||||
|
|
||||||
|
module.exports = function(name, hasPassword) {
|
||||||
|
let type = '';
|
||||||
|
if (name) {
|
||||||
|
type = name.split('.').pop();
|
||||||
|
}
|
||||||
|
const lockClass = hasPassword ? 'fileIcon__lock--visible' : '';
|
||||||
|
return html`
|
||||||
|
<div class="fileIcon">
|
||||||
|
<div class="fileIcon__fileType">${type}</div>
|
||||||
|
<img class="fileIcon__lock ${lockClass}"src="${assets.get(
|
||||||
|
'lock-white.svg'
|
||||||
|
)}"/>
|
||||||
|
</div>`;
|
||||||
|
};
|
@ -1,52 +1,21 @@
|
|||||||
.fileList {
|
.fileList {
|
||||||
margin: 45.3px auto;
|
position: absolute;
|
||||||
table-layout: fixed;
|
bottom: 0;
|
||||||
border-collapse: collapse;
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 3px;
|
||||||
font-family: 'Segoe UI', 'SF Pro Text', sans-serif;
|
font-family: 'Segoe UI', 'SF Pro Text', sans-serif;
|
||||||
|
width: 262px;
|
||||||
|
max-height: 80%;
|
||||||
|
overflow-y: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fileList__header {
|
@media (max-device-width: 750px), (max-width: 750px) {
|
||||||
font-size: 16px;
|
.fileList {
|
||||||
color: var(--lightTextColor);
|
position: static;
|
||||||
font-weight: lighter;
|
width: 400px;
|
||||||
text-align: left;
|
max-height: 200px;
|
||||||
background: rgba(0, 148, 251, 0.05);
|
margin: 6px 0 0 -3px;
|
||||||
height: 40px;
|
|
||||||
border-top: 1px solid rgba(0, 148, 251, 0.1);
|
|
||||||
padding: 0 19px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fileList__body {
|
|
||||||
word-wrap: break-word;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fileList__nameCol {
|
|
||||||
width: 35%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fileList__copyCol {
|
|
||||||
text-align: center;
|
|
||||||
width: 25%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fileList__expireCol {
|
|
||||||
width: 25%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fileList__dlCol {
|
|
||||||
width: 8%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fileList__delCol {
|
|
||||||
text-align: center;
|
|
||||||
width: 7%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-device-width: 520px), (max-width: 520px) {
|
|
||||||
.fileList__header {
|
|
||||||
font-size: 14px;
|
|
||||||
padding: 0 5px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,12 @@
|
|||||||
const html = require('choo/html');
|
const html = require('choo/html');
|
||||||
const file = require('../file');
|
const file = require('../file');
|
||||||
|
|
||||||
module.exports = function(state, emit) {
|
module.exports = function(state) {
|
||||||
if (state.storage.files.length) {
|
if (state.storage.files.length) {
|
||||||
return html`
|
return html`
|
||||||
<table class="fileList">
|
<ul class="fileList">
|
||||||
<thead>
|
${state.storage.files.map(f => file(f, state))}
|
||||||
<tr>
|
</ul>
|
||||||
<th class="fileList__header fileList__nameCol">
|
|
||||||
${state.translate('uploadedFile')}
|
|
||||||
</th>
|
|
||||||
<th class="fileList__header fileList__copyCol">
|
|
||||||
${state.translate('copyFileList')}
|
|
||||||
</th>
|
|
||||||
<th class="fileList__header fileList__expireCol" >
|
|
||||||
${state.translate('timeFileList')}
|
|
||||||
</th>
|
|
||||||
<th class="fileList__header fileList__dlCol" >
|
|
||||||
${state.translate('downloadsFileList')}
|
|
||||||
</th>
|
|
||||||
<th class="fileList__header fileList__delCol">
|
|
||||||
${state.translate('deleteFileList')}
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody class="fileList__body">
|
|
||||||
${state.storage.files.map(f => file(f, state, emit))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -3,13 +3,14 @@
|
|||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
|
||||||
padding: 50px 31px 41px;
|
padding: 50px 31px 41px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
.legalSection {
|
.legalSection {
|
||||||
@ -20,18 +21,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.legalSection__link {
|
.legalSection__link {
|
||||||
color: var(--lightTextColor);
|
color: #fff;
|
||||||
opacity: 0.9;
|
text-shadow: 0 0 3px #000;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
margin-right: 2vw;
|
margin-right: 2vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
.legalSection__link:hover {
|
.legalSection__link:visited {
|
||||||
opacity: 1;
|
color: #ededf0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.legalSection__link:visited {
|
.legalSection__link:hover {
|
||||||
color: var(--lightTextColor);
|
color: #d7d7db;
|
||||||
}
|
}
|
||||||
|
|
||||||
.legalSection__mozLogo {
|
.legalSection__mozLogo {
|
||||||
@ -60,12 +61,12 @@
|
|||||||
margin-bottom: -5px;
|
margin-bottom: -5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-device-width: 768px), (max-width: 768px) {
|
@media (max-device-width: 750px), (max-width: 750px) {
|
||||||
.footer {
|
.footer {
|
||||||
flex-direction: column;
|
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
max-width: 630px;
|
max-width: 630px;
|
||||||
|
padding: 20px 31px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,8 +38,6 @@ module.exports = function(state) {
|
|||||||
class="legalSection__link">
|
class="legalSection__link">
|
||||||
${state.translate('reportIPInfringement')}
|
${state.translate('reportIPInfringement')}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
|
||||||
<div class="socialSection">
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/mozilla/send"
|
href="https://github.com/mozilla/send"
|
||||||
class="socialSection__link">
|
class="socialSection__link">
|
||||||
@ -52,9 +50,9 @@ module.exports = function(state) {
|
|||||||
href="https://twitter.com/FxTestPilot"
|
href="https://twitter.com/FxTestPilot"
|
||||||
class="socialSection__link">
|
class="socialSection__link">
|
||||||
<img
|
<img
|
||||||
class="socialSection__icon"
|
class="legalSection__mozLogo"
|
||||||
src="${assets.get('twitter-icon.svg')}"
|
src="${assets.get('mozilla-logo.svg')}"
|
||||||
alt="twitter"/>
|
alt="mozilla"/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</footer>`;
|
</footer>`;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 31px;
|
padding: 20px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +62,7 @@
|
|||||||
color: var(--primaryControlFGColor);
|
color: var(--primaryControlFGColor);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: block;
|
display: block;
|
||||||
float: right;
|
float: left;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 12px;
|
line-height: 12px;
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
@ -88,17 +88,10 @@
|
|||||||
background-color: var(--primaryControlHoverColor);
|
background-color: var(--primaryControlHoverColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-device-width: 520px), (max-width: 520px) {
|
@media (max-device-width: 750px), (max-width: 750px) {
|
||||||
.header {
|
.header {
|
||||||
|
padding-top: 60px;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.feedback {
|
|
||||||
margin-top: 10px;
|
|
||||||
min-width: 30px;
|
|
||||||
max-width: 300px;
|
|
||||||
text-indent: 2px;
|
|
||||||
padding: 5px 5px 5px 20px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,6 @@ module.exports = function(state) {
|
|||||||
const feedbackUrl = `https://qsurvey.mozilla.com/s3/txp-firefox-send?ver=${version}&browser=${browser}`;
|
const feedbackUrl = `https://qsurvey.mozilla.com/s3/txp-firefox-send?ver=${version}&browser=${browser}`;
|
||||||
const header = html`
|
const header = html`
|
||||||
<header class="header">
|
<header class="header">
|
||||||
<div class="logo">
|
|
||||||
</div>
|
|
||||||
<a href="${feedbackUrl}"
|
<a href="${feedbackUrl}"
|
||||||
rel="noreferrer noopener"
|
rel="noreferrer noopener"
|
||||||
class="feedback"
|
class="feedback"
|
||||||
|
@ -1,76 +1,39 @@
|
|||||||
const html = require('choo/html');
|
const html = require('choo/html');
|
||||||
const MAX_LENGTH = 32;
|
|
||||||
|
|
||||||
module.exports = function(file, state, emit) {
|
module.exports = function(state) {
|
||||||
const loading = state.settingPassword;
|
const placeholder =
|
||||||
const pwd = file.hasPassword;
|
state.route === '/' ? '' : state.translate('unlockInputPlaceholder');
|
||||||
const sectionClass =
|
const hasPassword = !!state.password;
|
||||||
pwd || state.passwordSetError
|
const sectionClass = hasPassword
|
||||||
? 'passwordInput'
|
? 'passwordInput'
|
||||||
: 'passwordInput passwordInput--hidden';
|
: 'passwordInput passwordInput--hidden';
|
||||||
const inputClass = loading || pwd ? 'input' : 'input input--noBtn';
|
|
||||||
let btnClass = 'inputBtn inputBtn--password inputBtn--hidden';
|
|
||||||
if (loading) {
|
|
||||||
btnClass = 'inputBtn inputBtn--password inputBtn--loading';
|
|
||||||
} else if (pwd) {
|
|
||||||
btnClass = 'inputBtn inputBtn--password';
|
|
||||||
}
|
|
||||||
const action = pwd
|
|
||||||
? state.translate('changePasswordButton')
|
|
||||||
: state.translate('addPasswordButton');
|
|
||||||
return html`
|
return html`
|
||||||
<div class="${sectionClass}">
|
<div class="${sectionClass}">
|
||||||
<form
|
<form
|
||||||
class="passwordInput__form"
|
onsubmit=${onSubmit}
|
||||||
onsubmit=${setPassword}
|
|
||||||
data-no-csrf>
|
data-no-csrf>
|
||||||
|
|
||||||
<input id="password-input"
|
<input id="password-input"
|
||||||
${loading ? 'disabled' : ''}
|
class="input passwordInput__fill"
|
||||||
class="${inputClass}"
|
|
||||||
maxlength="${MAX_LENGTH}"
|
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
type="password"
|
type="password"
|
||||||
oninput=${inputChanged}
|
oninput=${inputChanged}
|
||||||
onfocus=${focused}
|
onfocus=${focused}
|
||||||
placeholder="${
|
placeholder="${
|
||||||
pwd && !state.passwordSetError
|
hasPassword ? passwordPlaceholder(state.password) : placeholder
|
||||||
? passwordPlaceholder(file.password)
|
|
||||||
: state.translate('unlockInputPlaceholder')
|
|
||||||
}">
|
|
||||||
<input type="submit"
|
|
||||||
id="password-btn"
|
|
||||||
${loading ? 'disabled' : ''}
|
|
||||||
class="${btnClass}"
|
|
||||||
value="${loading ? '' : action}">
|
|
||||||
</form>
|
|
||||||
<label
|
|
||||||
class="passwordInput__msg ${
|
|
||||||
state.passwordSetError ? 'passwordInput__msg--error' : ''
|
|
||||||
}"
|
}"
|
||||||
for="password-input">${message(state, pwd)}</label>
|
>
|
||||||
|
</form>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
function inputChanged() {
|
function onSubmit() {
|
||||||
state.passwordSetError = null;
|
event.preventDefault();
|
||||||
const resetInput = document.getElementById('password-input');
|
}
|
||||||
const resetBtn = document.getElementById('password-btn');
|
|
||||||
const pwdmsg = document.querySelector('.passwordInput__msg');
|
|
||||||
const length = resetInput.value.length;
|
|
||||||
|
|
||||||
if (length === MAX_LENGTH) {
|
function inputChanged() {
|
||||||
pwdmsg.textContent = state.translate('maxPasswordLength', {
|
const password = document.getElementById('password-input').value;
|
||||||
length: MAX_LENGTH
|
state.password = password;
|
||||||
});
|
|
||||||
} else {
|
|
||||||
pwdmsg.textContent = '';
|
|
||||||
}
|
|
||||||
if (length > 0) {
|
|
||||||
resetBtn.classList.remove('inputBtn--hidden');
|
|
||||||
resetInput.classList.remove('input--noBtn');
|
|
||||||
} else {
|
|
||||||
resetBtn.classList.add('inputBtn--hidden');
|
|
||||||
resetInput.classList.add('input--noBtn');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function focused(event) {
|
function focused(event) {
|
||||||
@ -80,30 +43,8 @@ module.exports = function(file, state, emit) {
|
|||||||
el.placeholder = '';
|
el.placeholder = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setPassword(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
const el = document.getElementById('password-input');
|
|
||||||
const password = el.value;
|
|
||||||
if (password.length > 0) {
|
|
||||||
emit('password', { password, file });
|
|
||||||
} else {
|
|
||||||
el.focus();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function passwordPlaceholder(password) {
|
function passwordPlaceholder(password) {
|
||||||
return password ? password.replace(/./g, '●') : '●●●●●●●●●●●●';
|
return password ? password.replace(/./g, '•') : '••••••••••••';
|
||||||
}
|
|
||||||
|
|
||||||
function message(state, pwd) {
|
|
||||||
if (state.passwordSetError) {
|
|
||||||
return state.translate('passwordSetError');
|
|
||||||
}
|
|
||||||
if (state.settingPassword || !pwd) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return state.translate('passwordIsSet');
|
|
||||||
}
|
}
|
||||||
|
@ -1,41 +1,31 @@
|
|||||||
.passwordInput {
|
.passwordInput {
|
||||||
width: 90%;
|
display: inline;
|
||||||
height: 100px;
|
|
||||||
padding: 10px 5px 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.passwordInput--hidden {
|
.passwordInput--hidden {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.passwordInput__form {
|
.passwordInput__fill {
|
||||||
display: flex;
|
height: 24px;
|
||||||
flex-wrap: nowrap;
|
box-sizing: border-box;
|
||||||
padding-bottom: 5px;
|
padding: 4px;
|
||||||
|
font-size: 18px;
|
||||||
|
border: none;
|
||||||
|
background-color: var(--lightControlBGColor);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.passwordInput__fill:focus {
|
||||||
|
border: 1px solid rgba(12, 12, 13, 0.2);
|
||||||
|
background-color: var(--pageBGColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
.passwordInput__msg {
|
.passwordInput__msg {
|
||||||
font-size: 15px;
|
font-size: 12px;
|
||||||
color: var(--lightTextColor);
|
color: var(--lightTextColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
.passwordInput__msg--error {
|
.passwordInput__msg--error {
|
||||||
color: var(--errorColor);
|
color: var(--errorColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
.inputBtn--loading {
|
|
||||||
background-image: url('../assets/spinner.svg');
|
|
||||||
background-position: center;
|
|
||||||
background-size: 30px 30px;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inputBtn--password {
|
|
||||||
flex: 0 0 200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-device-width: 520px), (max-width: 520px) {
|
|
||||||
.passwordInput__form {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -2,16 +2,14 @@ const html = require('choo/html');
|
|||||||
|
|
||||||
module.exports = function(msg, confirmText, cancelText, confirmCallback) {
|
module.exports = function(msg, confirmText, cancelText, confirmCallback) {
|
||||||
return html`
|
return html`
|
||||||
<div class="popup__wrapper">
|
|
||||||
<div class="popup" onblur=${hide} tabindex="-1">
|
<div class="popup" onblur=${hide} tabindex="-1">
|
||||||
<div class="popup__message">${msg}</div>
|
<div class="popup__message">${msg}</div>
|
||||||
<div class="popup__action">
|
<div class="popup__action">
|
||||||
<span class="popup__no" onclick=${hide}>
|
<div>
|
||||||
${cancelText}
|
<span class="popup__no" onclick=${hide}>${cancelText}</span>
|
||||||
</span>
|
</div>
|
||||||
<span class="popup__yes" onclick=${confirmCallback}>
|
<div>
|
||||||
${confirmText}
|
<span class="popup__yes" onclick=${confirmCallback}>${confirmText}</span>
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
@ -1,122 +1,79 @@
|
|||||||
.popup {
|
.popup {
|
||||||
visibility: hidden;
|
display: block;
|
||||||
min-width: 204px;
|
width: 100%;
|
||||||
min-height: 105px;
|
height: 70px;
|
||||||
background-color: var(--pageBGColor);
|
background-color: var(--errorColor);
|
||||||
color: var(--textColor);
|
color: var(--textColor);
|
||||||
border: 1px solid #d7d7db;
|
padding: 0;
|
||||||
padding: 15px 24px;
|
box-sizing: border-box;
|
||||||
box-sizing: content-box;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-radius: 5px;
|
border-radius: 4px;
|
||||||
position: absolute;
|
|
||||||
z-index: 1;
|
|
||||||
bottom: 20px;
|
|
||||||
left: -40px;
|
|
||||||
transition: opacity 0.5s;
|
transition: opacity 0.5s;
|
||||||
opacity: 0;
|
|
||||||
outline: 0;
|
outline: 0;
|
||||||
box-shadow: 3px 3px 7px rgba(136, 136, 136, 0.3);
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup::after {
|
.popup::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -11px;
|
top: 100%;
|
||||||
left: 20px;
|
left: 50%;
|
||||||
background-color: #fff;
|
width: 0;
|
||||||
display: block;
|
height: 0;
|
||||||
width: 20px;
|
border: 8px solid;
|
||||||
height: 20px;
|
border-color: var(--errorColor) transparent transparent;
|
||||||
transform: rotate(45deg);
|
margin-left: -8px;
|
||||||
border-radius: 0 0 5px;
|
pointer-events: none;
|
||||||
border-right: 1px solid #d7d7db;
|
|
||||||
border-bottom: 1px solid #d7d7db;
|
|
||||||
border-left: 1px solid #fff;
|
|
||||||
border-top: 1px solid #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popup__wrapper {
|
|
||||||
position: absolute;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup__message {
|
.popup__message {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
display: flex;
|
padding: 10px;
|
||||||
justify-content: center;
|
box-sizing: border-box;
|
||||||
align-items: center;
|
text-align: center;
|
||||||
border-bottom: 1px #ebebeb solid;
|
color: var(--primaryControlFGColor);
|
||||||
color: var(--textColor);
|
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
font-weight: normal;
|
font-style: italic;
|
||||||
padding-bottom: 15px;
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
width: calc(100% + 48px);
|
|
||||||
margin-left: -24px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup__action {
|
.popup__action {
|
||||||
margin-top: 15px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
text-transform: uppercase;
|
||||||
justify-content: space-between;
|
}
|
||||||
|
|
||||||
|
.popup__action > div {
|
||||||
|
flex: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup__no {
|
.popup__no {
|
||||||
color: #4a4a4a;
|
color: var(--primaryControlFGColor);
|
||||||
background-color: #fbfbfb;
|
padding: 5px;
|
||||||
border: 1px #c1c1c1 solid;
|
font-weight: bold;
|
||||||
border-radius: 5px;
|
|
||||||
padding: 5px 25px;
|
|
||||||
font-weight: normal;
|
|
||||||
min-width: 94px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup__no:hover {
|
.popup__no:hover {
|
||||||
background-color: #efeff1;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup__yes {
|
.popup__yes {
|
||||||
color: var(--primaryControlFGColor);
|
color: var(--primaryControlFGColor);
|
||||||
background-color: var(--primaryControlBGColor);
|
padding: 5px;
|
||||||
border-radius: 5px;
|
|
||||||
padding: 5px 25px;
|
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
min-width: 94px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
margin-left: 12px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup__yes:hover {
|
.popup__yes:hover {
|
||||||
background-color: var(--primaryControlHoverColor);
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup--show {
|
.popup--show {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
pointer-events: auto;
|
||||||
|
|
||||||
@media (max-device-width: 992px), (max-width: 992px) {
|
|
||||||
.popup {
|
|
||||||
left: auto;
|
|
||||||
right: -40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popup::after {
|
|
||||||
left: auto;
|
|
||||||
right: 36px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-device-width: 520px), (max-width: 520px) {
|
|
||||||
.popup::after {
|
|
||||||
left: 125px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
const html = require('choo/html');
|
|
||||||
const percent = require('../../utils').percent;
|
|
||||||
|
|
||||||
const radius = 73;
|
|
||||||
const oRadius = radius + 10;
|
|
||||||
const oDiameter = oRadius * 2;
|
|
||||||
const circumference = 2 * Math.PI * radius;
|
|
||||||
|
|
||||||
module.exports = function(progressRatio, indefinite = false) {
|
|
||||||
// HACK - never indefinite for MS Edge
|
|
||||||
if (/edge/i.test(navigator.userAgent)) {
|
|
||||||
indefinite = false;
|
|
||||||
}
|
|
||||||
const p = indefinite ? 0.2 : progressRatio;
|
|
||||||
const dashOffset = (1 - p) * circumference;
|
|
||||||
const progressPercent = html`
|
|
||||||
<text class="progress__percent" text-anchor="middle" x="50%" y="98">
|
|
||||||
${percent(progressRatio)}
|
|
||||||
</text>`;
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<div class="progress">
|
|
||||||
<svg
|
|
||||||
width="${oDiameter}"
|
|
||||||
height="${oDiameter}"
|
|
||||||
viewPort="0 0 ${oDiameter} ${oDiameter}"
|
|
||||||
version="1.1">
|
|
||||||
<circle
|
|
||||||
class="progress__bg"
|
|
||||||
r="${radius}"
|
|
||||||
cx="${oRadius}"
|
|
||||||
cy="${oRadius}"
|
|
||||||
fill="transparent"/>
|
|
||||||
<circle
|
|
||||||
class="progress__indefinite ${indefinite ? '' : 'progress--invisible'}"
|
|
||||||
r="${radius}"
|
|
||||||
cx="${oRadius}"
|
|
||||||
cy="${oRadius}"
|
|
||||||
fill="transparent"
|
|
||||||
transform="rotate(-90 ${oRadius} ${oRadius})"
|
|
||||||
stroke-dasharray="${circumference}"
|
|
||||||
stroke-dashoffset="${dashOffset}"/>
|
|
||||||
<circle
|
|
||||||
class="progress__bar ${indefinite ? 'progress--invisible' : ''}"
|
|
||||||
r="${radius}"
|
|
||||||
cx="${oRadius}"
|
|
||||||
cy="${oRadius}"
|
|
||||||
fill="transparent"
|
|
||||||
transform="rotate(-90 ${oRadius} ${oRadius})"
|
|
||||||
stroke-dasharray="${circumference}"
|
|
||||||
stroke-dashoffset="${dashOffset}"/>
|
|
||||||
${indefinite ? '' : progressPercent}
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
};
|
|
@ -1,43 +0,0 @@
|
|||||||
.progress {
|
|
||||||
margin-top: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress__bg {
|
|
||||||
stroke: #eee;
|
|
||||||
stroke-width: 0.75em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress__bar {
|
|
||||||
stroke: #3b9dff;
|
|
||||||
stroke-width: 0.75em;
|
|
||||||
transition: stroke-dashoffset 300ms linear;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress__indefinite {
|
|
||||||
stroke: #3b9dff;
|
|
||||||
stroke-width: 0.75em;
|
|
||||||
animation: 1s linear infinite spin;
|
|
||||||
transform-origin: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
from {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress__percent {
|
|
||||||
font-family: 'Segoe UI', 'SF Pro Text', sans-serif;
|
|
||||||
font-size: 43.2px;
|
|
||||||
letter-spacing: -0.78px;
|
|
||||||
line-height: 58px;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress--invisible {
|
|
||||||
display: none;
|
|
||||||
}
|
|
@ -5,16 +5,14 @@ module.exports = function(selected, options, translate, changed) {
|
|||||||
let x = selected;
|
let x = selected;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="select">
|
<select class="selectBox" id="${id}" onchange=${choose}>
|
||||||
<select id="${id}" onchange=${choose}>
|
|
||||||
${options.map(
|
${options.map(
|
||||||
i =>
|
i =>
|
||||||
html`<option value="${i}" ${
|
html`<option value="${i}" ${
|
||||||
i === selected ? 'selected' : ''
|
i === selected ? 'selected' : ''
|
||||||
}>${translate(i)}</option>`
|
}>${translate(i)}</option>`
|
||||||
)}
|
)}
|
||||||
</select>
|
</select>`;
|
||||||
</div>`;
|
|
||||||
|
|
||||||
function choose(event) {
|
function choose(event) {
|
||||||
const target = event.target;
|
const target = event.target;
|
||||||
|
@ -1,46 +1,22 @@
|
|||||||
.select {
|
|
||||||
background-color: var(--pageBGColor);
|
|
||||||
overflow: hidden;
|
|
||||||
padding: 4px 2px 4px 2px;
|
|
||||||
border: 1px dotted #0094fb88;
|
|
||||||
border-radius: 4px;
|
|
||||||
display: inline;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select::after {
|
|
||||||
color: #0094fb;
|
|
||||||
content: '\25BC';
|
|
||||||
pointer-events: none;
|
|
||||||
font-size: 20px;
|
|
||||||
margin-left: -30px;
|
|
||||||
padding-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
option {
|
option {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
.selectBox {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
border: 0;
|
border: none;
|
||||||
background: #fff;
|
border-radius: 0;
|
||||||
background-image: none;
|
background-color: #e6e6e6;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
font-weight: 200;
|
font-weight: 200;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: #0094fb;
|
padding: 4px 2px 4px 2px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding-right: 40px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
select:active {
|
select:active {
|
||||||
background-color: var(--pageBGColor);
|
background-color: var(--pageBGColor);
|
||||||
border: 0;
|
border: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#arrow {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
@ -1,25 +1,26 @@
|
|||||||
const html = require('choo/html');
|
const html = require('choo/html');
|
||||||
const passwordInput = require('../passwordInput');
|
const passwordInput = require('../passwordInput');
|
||||||
|
|
||||||
module.exports = function(state, emit) {
|
module.exports = function(state) {
|
||||||
const file = state.storage.getFileById(state.params.id);
|
const checked = state.password ? 'checked' : '';
|
||||||
|
const label = state.password ? 'addPasswordLabel' : 'addPasswordMessage';
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="setPasswordSection">
|
<div class="setPasswordSection">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<input
|
<input
|
||||||
${file.hasPassword ? 'disabled' : ''}
|
class="checkbox__input" id="add-password"
|
||||||
${file.hasPassword || state.passwordSetError ? 'checked' : ''}
|
|
||||||
class="checkbox__input"
|
|
||||||
id="add-password"
|
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
|
${checked}
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
onchange=${togglePasswordInput}/>
|
onchange=${togglePasswordInput}/>
|
||||||
<label class="checkbox__label" for="add-password">
|
<label class="checkbox__label" for="add-password">
|
||||||
${state.translate('requirePasswordCheckbox')}
|
${state.translate(label)}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
${passwordInput(file, state, emit)}
|
|
||||||
|
${passwordInput(state)}
|
||||||
|
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
function togglePasswordInput(e) {
|
function togglePasswordInput(e) {
|
||||||
@ -28,9 +29,13 @@ module.exports = function(state, emit) {
|
|||||||
document
|
document
|
||||||
.querySelector('.passwordInput')
|
.querySelector('.passwordInput')
|
||||||
.classList.toggle('passwordInput--hidden', !boxChecked);
|
.classList.toggle('passwordInput--hidden', !boxChecked);
|
||||||
|
|
||||||
|
const label = document.querySelector('.checkbox__label');
|
||||||
if (boxChecked) {
|
if (boxChecked) {
|
||||||
|
label.innerHTML = state.translate('addPasswordLabel');
|
||||||
unlockInput.focus();
|
unlockInput.focus();
|
||||||
} else {
|
} else {
|
||||||
|
label.innerHTML = state.translate('addPasswordMessage');
|
||||||
unlockInput.value = '';
|
unlockInput.value = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
.setPasswordSection {
|
.setPasswordSection {
|
||||||
|
display: flex;
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
overflow-wrap: break-word;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox {
|
.checkbox {
|
||||||
min-height: 24px;
|
flex: auto;
|
||||||
|
height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox__input {
|
.checkbox__input {
|
||||||
@ -14,7 +15,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.checkbox__label {
|
.checkbox__label {
|
||||||
line-height: 23px;
|
font-size: 13px;
|
||||||
|
line-height: 20px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: var(--lightTextColor);
|
color: var(--lightTextColor);
|
||||||
user-select: none;
|
user-select: none;
|
||||||
@ -22,27 +24,21 @@
|
|||||||
|
|
||||||
.checkbox__label::before {
|
.checkbox__label::before {
|
||||||
content: '';
|
content: '';
|
||||||
height: 20px;
|
height: 24px;
|
||||||
width: 20px;
|
width: 24px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
margin-left: 5px;
|
|
||||||
float: left;
|
float: left;
|
||||||
border: 1px solid rgba(12, 12, 13, 0.3);
|
background-color: #e6e6e6;
|
||||||
border-radius: 2px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox__input:focus + .checkbox__label::before,
|
.checkbox__label:hover::before {
|
||||||
.checkbox:hover .checkbox__label::before {
|
background-color: #d6d6d6;
|
||||||
border: 1px solid var(--primaryControlBGColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox__input:checked + .checkbox__label {
|
|
||||||
color: var(--textColor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox__input:checked + .checkbox__label::before {
|
.checkbox__input:checked + .checkbox__label::before {
|
||||||
background-image: url('../assets/check-16-blue.svg');
|
background-image: url('../assets/lock.svg');
|
||||||
background-position: 2px 1px;
|
background-position: 2px 2px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox__input:disabled + .checkbox__label {
|
.checkbox__input:disabled + .checkbox__label {
|
||||||
@ -50,20 +46,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.checkbox__input:disabled + .checkbox__label::before {
|
.checkbox__input:disabled + .checkbox__label::before {
|
||||||
background-image: url('../assets/check-16-blue.svg');
|
background-image: url('../assets/lock.svg');
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: 26px 26px;
|
background-size: 26px 26px;
|
||||||
border: none;
|
border: none;
|
||||||
cursor: auto;
|
cursor: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-device-width: 520px), (max-width: 520px) {
|
.setPasswordSection > .passwordInput--hidden {
|
||||||
.setPasswordSection {
|
display: none;
|
||||||
align-self: center;
|
|
||||||
min-width: 95%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox__label::before {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
15
app/templates/signupPromo/index.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
const html = require('choo/html');
|
||||||
|
|
||||||
|
module.exports = function(state) {
|
||||||
|
return html`
|
||||||
|
<div class="signupPromo">
|
||||||
|
<div class="signupPromo__title">${state.translate('signInPromoText')}</div>
|
||||||
|
<div class="signupPromo__info">${state.translate('signInExplanation')}</div>
|
||||||
|
<a href="/signin"
|
||||||
|
class="link signupPromo__link"
|
||||||
|
>
|
||||||
|
${state.translate('signInLearnMore')}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
};
|
85
app/templates/signupPromo/signupPromo.css
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
.signupPromo {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
height: 140px;
|
||||||
|
width: 150px;
|
||||||
|
background: #ffe900;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-align: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signupPromo::before {
|
||||||
|
content: '';
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0 35px 140px 0;
|
||||||
|
border-color: transparent #ffe900 transparent;
|
||||||
|
position: absolute;
|
||||||
|
right: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signupPromo::after {
|
||||||
|
content: '';
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0 150px 35px 0;
|
||||||
|
border-color: transparent #ffe900 transparent;
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signupPromo__title {
|
||||||
|
color: #0a84ff;
|
||||||
|
font-size: 22px;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signupPromo__info {
|
||||||
|
color: var(--lightTextColor);
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signupPromo__link {
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signupPromo__link:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-device-width: 750px), (max-width: 750px) {
|
||||||
|
.signupPromo {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
height: 40px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signupPromo::before {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signupPromo::after {
|
||||||
|
border-width: 15px 50vw 0 50vw;
|
||||||
|
border-color: #ffe900 transparent transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-device-width: 500px), (max-width: 500px) {
|
||||||
|
.signupPromo__link {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signupPromo {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
11
app/templates/title/index.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
const html = require('choo/html');
|
||||||
|
|
||||||
|
module.exports = function(state) {
|
||||||
|
return html`
|
||||||
|
<div class="boxTitle">
|
||||||
|
${state.translate('uploadPageHeader')}
|
||||||
|
<div class="boxSubtitle">
|
||||||
|
${state.translate('pageHeaderCredits')}
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
};
|
13
app/templates/title/title.css
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
.boxTitle {
|
||||||
|
font-size: 15px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 500;
|
||||||
|
margin: 9px 0 19px 0;
|
||||||
|
color: var(--lightTextColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
.boxSubtitle {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
64
app/templates/uploadedFile/index.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
const html = require('choo/html');
|
||||||
|
const assets = require('../../../common/assets');
|
||||||
|
const bytes = require('../../utils').bytes;
|
||||||
|
const fileIcon = require('../fileIcon');
|
||||||
|
|
||||||
|
module.exports = function(file, state, emit) {
|
||||||
|
const transfer = state.transfer;
|
||||||
|
const transferState = transfer ? transfer.state : null;
|
||||||
|
const transferring = state.uploading || state.downloading;
|
||||||
|
const share = state.route.includes('share/');
|
||||||
|
const complete = share ? 'uploadedFile--completed' : '';
|
||||||
|
|
||||||
|
const cancelVisible =
|
||||||
|
transferring || state.route === '/' ? 'uploadedFile__cancel--visible' : '';
|
||||||
|
|
||||||
|
const stampClass =
|
||||||
|
share || transferState === 'complete' ? 'uploadedFile__stamp--visible' : '';
|
||||||
|
|
||||||
|
function cancel(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const btn = document.querySelector('.uploadedFile__cancel');
|
||||||
|
btn.disabled = true;
|
||||||
|
if (transferring) {
|
||||||
|
emit('cancel');
|
||||||
|
} else if (state.route === '/') {
|
||||||
|
emit('removeUpload', { file });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//const percent = share ? 100 : Math.floor(progressRatio * 100);
|
||||||
|
/*
|
||||||
|
style="
|
||||||
|
background: linear-gradient(to right,
|
||||||
|
#e8f2fe 0%,
|
||||||
|
#e8f2fe ${percent}%,
|
||||||
|
#fff ${percent}%,
|
||||||
|
#fff 100%);"
|
||||||
|
*/
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<li class="uploadedFile ${complete}" id="${file.id}"
|
||||||
|
>
|
||||||
|
|
||||||
|
${fileIcon(file.name, file._hasPassword)}
|
||||||
|
|
||||||
|
<div class="uploadedFile__cancel ${cancelVisible}"
|
||||||
|
onclick=${cancel}>
|
||||||
|
<img
|
||||||
|
src="${assets.get('close-16.svg')}"
|
||||||
|
alt="cancel"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="uploadedFile__fileData">
|
||||||
|
<p class="uploadedFile__fileName">${file.name}</p>
|
||||||
|
<p class="uploadedFile__fileInfo">
|
||||||
|
<span>${bytes(file.size)}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<img src="${assets.get('sent-done.svg')}"
|
||||||
|
class="uploadedFile__stamp ${stampClass}"/>
|
||||||
|
</li>
|
||||||
|
`;
|
||||||
|
};
|
70
app/templates/uploadedFile/uploadedFile.css
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
.uploadedFile {
|
||||||
|
margin: 11px;
|
||||||
|
list-style-type: none;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 18px;
|
||||||
|
text-align: initial;
|
||||||
|
color: var(--lightTextColor);
|
||||||
|
background-color: var(--pageBGColor);
|
||||||
|
border: 1px solid #cececf;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 53px;
|
||||||
|
border-radius: 4px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uploadedFile--completed {
|
||||||
|
background-color: #e8f2fe;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uploadedFile__fileData {
|
||||||
|
margin: 8px 16px 8px 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uploadedFile__fileName {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uploadedFile__fileInfo {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uploadedFile__cancel {
|
||||||
|
float: right;
|
||||||
|
margin: 6px;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uploadedFile:hover .uploadedFile__cancel--visible {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uploadedFile__stamp {
|
||||||
|
position: absolute;
|
||||||
|
top: -4px;
|
||||||
|
right: -8px;
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uploadedFile__stamp--visible {
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
animation: stampDown 0.2s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes stampDown {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(1.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
12
app/templates/uploadedFileList/index.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
const html = require('choo/html');
|
||||||
|
const file = require('../uploadedFile');
|
||||||
|
|
||||||
|
module.exports = function(files, state, emit) {
|
||||||
|
//const progressRatio = state.transfer ? state.transfer.progressRatio : 0;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ul class="uploadedFiles">
|
||||||
|
${files.map(f => file(f, state, emit))}
|
||||||
|
</ul>
|
||||||
|
`;
|
||||||
|
};
|
10
app/templates/uploadedFileList/uploadedFileList.css
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
.uploadedFiles {
|
||||||
|
border: 1px solid rgba(12, 12, 13, 0.1);
|
||||||
|
border-radius: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
align-content: center;
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
24
app/templates/userAccount/index.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
const html = require('choo/html');
|
||||||
|
const assets = require('../../../common/assets');
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
module.exports = function(state) {
|
||||||
|
return html`
|
||||||
|
<div class="account">
|
||||||
|
<img
|
||||||
|
src="${assets.get('user.svg')}"
|
||||||
|
onclick=${onclick}
|
||||||
|
alt="account"/>
|
||||||
|
|
||||||
|
<ul class=account_dropdown>
|
||||||
|
<li class=account_dropdown__item>Placeholder</li>
|
||||||
|
<li class=account_dropdown__item>Placeholder</li>
|
||||||
|
</ul>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
function onclick(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const dropdown = document.querySelector('.account_dropdown');
|
||||||
|
dropdown.classList.toggle('visible');
|
||||||
|
}
|
||||||
|
};
|
33
app/templates/userAccount/userAccount.css
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
.account {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
margin: 0 21px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.account_dropdown {
|
||||||
|
z-index: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 25px;
|
||||||
|
left: -10px;
|
||||||
|
width: 150px;
|
||||||
|
list-style-type: none;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: var(--pageBGColor);
|
||||||
|
box-shadow: 0 5px 12px 0 rgba(0, 0, 0, 0.2);
|
||||||
|
padding: 11px 0;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.account_dropdown__item {
|
||||||
|
padding: 0 14px;
|
||||||
|
color: var(--lightTextColor);
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.account_dropdown__item:hover {
|
||||||
|
background-color: var(--primaryControlBGColor);
|
||||||
|
color: var(--primaryControlFGColor);
|
||||||
|
}
|
25
app/utils.js
@ -1,3 +1,4 @@
|
|||||||
|
/* global MAXFILESIZE */
|
||||||
const b64 = require('base64-js');
|
const b64 = require('base64-js');
|
||||||
|
|
||||||
function arrayToB64(array) {
|
function arrayToB64(array) {
|
||||||
@ -128,6 +129,27 @@ function openLinksInNewTab(links, should = true) {
|
|||||||
return links;
|
return links;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkSize(files, oldfiles, translate) {
|
||||||
|
function size(arr) {
|
||||||
|
let total = 0;
|
||||||
|
for (let i = 0; i < arr.length; i++) {
|
||||||
|
total += arr[i].size;
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
const addSize = size(files);
|
||||||
|
if (addSize === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const totalSize = addSize + size(oldfiles);
|
||||||
|
if (totalSize > MAXFILESIZE) {
|
||||||
|
// eslint-disable-next-line no-alert
|
||||||
|
alert(translate('fileTooBig', { size: bytes(MAXFILESIZE) }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
fadeOut,
|
fadeOut,
|
||||||
delay,
|
delay,
|
||||||
@ -140,5 +162,6 @@ module.exports = {
|
|||||||
b64ToArray,
|
b64ToArray,
|
||||||
loadShim,
|
loadShim,
|
||||||
isFile,
|
isFile,
|
||||||
openLinksInNewTab
|
openLinksInNewTab,
|
||||||
|
checkSize
|
||||||
};
|
};
|
||||||
|
10
assets/addfile.svg
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="33px" height="33px" viewBox="0 0 33 33" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 51.1 (57501) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>Shape</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<defs></defs>
|
||||||
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<path d="M13.799417,16.627417 L9.213417,21.213417 C8.45547258,21.9981748 8.46631225,23.2455866 9.23777985,24.0170541 C10.0092474,24.7885217 11.2566592,24.7993614 12.041417,24.041417 L16.627417,19.455417 L21.213417,24.041417 C21.9981748,24.7993614 23.2455866,24.7885217 24.0170541,24.0170541 C24.7885217,23.2455866 24.7993614,21.9981748 24.041417,21.213417 L19.455417,16.627417 L24.041417,12.041417 C24.5613302,11.5392681 24.7698422,10.7956612 24.5868113,10.0964024 C24.4037804,9.39714349 23.8576905,8.85105357 23.1584316,8.66802268 C22.4591728,8.4849918 21.7155659,8.69350383 21.213417,9.213417 L16.627417,13.799417 L12.041417,9.213417 C11.2566592,8.45547258 10.0092474,8.46631225 9.23777985,9.23777985 C8.46631225,10.0092474 8.45547258,11.2566592 9.213417,12.041417 L13.799417,16.627417 Z M16.627417,0.627416998 C25.463973,0.627416998 32.627417,7.790861 32.627417,16.627417 C32.627417,25.463973 25.463973,32.627417 16.627417,32.627417 C7.790861,32.627417 0.627416998,25.463973 0.627416998,16.627417 C0.627416998,7.790861 7.790861,0.627416998 16.627417,0.627416998 Z" id="Shape" fill="#737373" transform="translate(16.627417, 16.627417) rotate(45.000000) translate(-16.627417, -16.627417) "></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
4
assets/back-arrow.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M15.707 4.293a1 1 0 0 1 0 1.414L9.414 12l6.293 6.293a1 1 0 1 1-1.414 1.414l-7-7a1 1 0 0 1 0-1.414l7-7a1 1 0 0 1 1.414 0z" fill="#0A84FF" fill-opacity="1"></path></svg>
|
After Width: | Height: | Size: 523 B |
13
assets/blue_file.svg
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="22px" height="32px" viewBox="0 0 22 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 51.1 (57501) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>Group 8</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<defs></defs>
|
||||||
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" fill-opacity="0.5">
|
||||||
|
<g id="Group-8" fill="#0A84FF">
|
||||||
|
<path d="M2,0 L11.9428571,0 L21.665206,8.66172902 C21.8781869,8.85147558 22,9.12314407 22,9.40838886 L22,30 C22,31.1045695 21.1045695,32 20,32 L2,32 C0.8954305,32 1.3527075e-16,31.1045695 0,30 L0,2 C-1.3527075e-16,0.8954305 0.8954305,2.02906125e-16 2,0 Z" id="Rectangle-14"></path>
|
||||||
|
<path d="M12,0 L22,9 L15,9 C13.3431458,9 12,7.65685425 12,6 L12,0 Z" id="Triangle"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 917 B |
4
assets/lock-white.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M9 7a3 3 0 1 1 6 0v3H9V7zm-2 3V7a5 5 0 1 1 10 0v3h1a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h1z" fill="#FFFFFF" fill-opacity="1"></path></svg>
|
After Width: | Height: | Size: 520 B |
4
assets/lock.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M9 7a3 3 0 1 1 6 0v3H9V7zm-2 3V7a5 5 0 1 1 10 0v3h1a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h1z" fill="#0A84FF" fill-opacity="1"></path></svg>
|
After Width: | Height: | Size: 520 B |
13
assets/red_file.svg
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="22px" height="32px" viewBox="0 0 22 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 51.1 (57501) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>Group 8</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<defs></defs>
|
||||||
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" fill-opacity="0.5">
|
||||||
|
<g id="Group-8" fill="#D70022">
|
||||||
|
<path d="M2,0 L11.9428571,0 L21.665206,8.66172902 C21.8781869,8.85147558 22,9.12314407 22,9.40838886 L22,30 C22,31.1045695 21.1045695,32 20,32 L2,32 C0.8954305,32 1.3527075e-16,31.1045695 0,30 L0,2 C-1.3527075e-16,0.8954305 0.8954305,2.02906125e-16 2,0 Z" id="Rectangle-14"></path>
|
||||||
|
<path d="M12,0 L22,9 L15,9 C13.3431458,9 12,7.65685425 12,6 L12,0 Z" id="Triangle"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 917 B |
19
assets/sent-done.svg
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="118px" height="59px" viewBox="0 0 118 59" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 51.1 (57501) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>Group</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<defs></defs>
|
||||||
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="Group" transform="translate(2.000000, -5.000000)">
|
||||||
|
<g id="Group-5" transform="translate(86.841503, 34.526577) rotate(3.000000) translate(-86.841503, -34.526577) translate(54.341503, 2.526577)">
|
||||||
|
<circle id="Oval-2" stroke-opacity="0.5" stroke="#0C0C0D" stroke-width="3" transform="translate(32.499214, 31.954331) rotate(11.000000) translate(-32.499214, -31.954331) " cx="32.4992143" cy="31.9543312" r="27.0650466"></circle>
|
||||||
|
<path d="M28.020207,44.1875925 C27.4974152,44.1874763 26.9960763,43.9712589 26.6264624,43.5864984 L20.7124121,37.431951 C19.9653252,36.6269796 19.9760096,35.3474371 20.736426,34.5560981 C21.4968423,33.7647591 22.726385,33.7536403 23.4999012,34.5311077 L27.7521034,38.9562273 L40.2031506,20.4454003 C40.831247,19.5292815 42.051282,19.3107606 42.9375457,19.9556419 C43.8238095,20.6005231 44.0454042,21.868032 43.4341935,22.7964374 L29.6347427,43.3115953 C29.3022783,43.8117461 28.7737147,44.1326132 28.1917144,44.1875925 C28.1345816,44.1905933 28.0773398,44.1905933 28.020207,44.1875925 Z" id="Shape" fill="#12BC00" fill-rule="nonzero"></path>
|
||||||
|
</g>
|
||||||
|
<path d="M0,19.8728801 C3.80326861,19.2140466 7.96993528,19.2140466 12.5,19.8728801 C19.2950971,20.8611303 28.3355757,25.3728801 35,25.3728801 C41.6644243,25.3728801 47.8955852,19.5814393 55.5,18.3728801 C60.5696098,17.5671739 65.4029432,18.0671739 70,19.8728801" id="Line" stroke-opacity="0.5" stroke="#0C0C0D" stroke-width="3" stroke-linecap="square"></path>
|
||||||
|
<path d="M0,29.8728801 C3.80326861,29.2140466 7.96993528,29.2140466 12.5,29.8728801 C19.2950971,30.8611303 28.3355757,35.3728801 35,35.3728801 C41.6644243,35.3728801 47.8955852,29.5814393 55.5,28.3728801 C60.5696098,27.5671739 65.4029432,28.0671739 70,29.8728801" id="Line" stroke-opacity="0.5" stroke="#0C0C0D" stroke-width="3" stroke-linecap="square"></path>
|
||||||
|
<path d="M0,38.8728801 C3.80326861,38.2140466 7.96993528,38.2140466 12.5,38.8728801 C19.2950971,39.8611303 28.3355757,44.3728801 35,44.3728801 C41.6644243,44.3728801 47.8955852,38.5814393 55.5,37.3728801 C60.5696098,36.5671739 65.4029432,37.0671739 70,38.8728801" id="Line" stroke-opacity="0.5" stroke="#0C0C0D" stroke-width="3" stroke-linecap="square"></path>
|
||||||
|
<path d="M0,48.8728801 C3.80326861,48.2140466 7.96993528,48.2140466 12.5,48.8728801 C19.2950971,49.8611303 28.3355757,54.3728801 35,54.3728801 C41.6644243,54.3728801 47.8955852,48.5814393 55.5,47.3728801 C60.5696098,46.5671739 65.4029432,47.0671739 70,48.8728801" id="Line" stroke-opacity="0.5" stroke="#0C0C0D" stroke-width="3" stroke-linecap="square"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.0 KiB |
@ -1 +0,0 @@
|
|||||||
<svg width="57" height="57" viewBox="0 0 57 57" xmlns="http://www.w3.org/2000/svg"><title>upload</title><g transform="translate(1 1)" stroke-width="2" stroke="#7FC9FD" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"><path d="M18 24l10-9 10 9M28 39.545V15"/><circle cx="27.5" cy="27.5" r="27.5"/></g></svg>
|
|
Before Width: | Height: | Size: 336 B |
1
assets/user.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="194 -104 1000 1000"><style>.st0{fill:#C3CFD8;} .st1{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}</style><path class="st0" d="M694-104.3c276.1 0 500 223.9 500 500s-223.9 500-500 500-500-223.9-500-500c0-276.2 223.9-500 500-500z"/><path class="st1" d="M892.4 585.9c10 3.1 19.1 5.7 27.5 8.2 34.5 10 44.8 54.6 17.5 78.1-65.4 56.5-150.7 90.8-244 90.8-92.8 0-177.6-33.8-242.9-89.8-27.4-23.5-17.3-68.2 17.4-78.3 9.2-2.7 19.2-5.5 30.2-9 62.6-19.5 92.6-43.7 98.2-68.7 0-.1 0-.2.1-.2 3.6-16.1-2.8-32.9-15.5-43.5-26.4-22.1-37.1-59.8-44.1-87.5-.8-3.2-1.7-6.5-2.5-9.8-12.1-2.1-25.4-17.3-32.2-38.5-8.2-25.5-3.9-49.8 9.6-54.1 1.3-.4 2.6-.4 3.9-.5-3.1-18.2-6.9-45.4-7.3-69.3-.1-5.2-.2-10.9-.2-16.9 0-3 .1-6.1.1-9.3 0-1.6.1-3.2.2-4.8.1-1.6.2-3.2.3-4.9.9-13.1 2.9-26.8 7-40 7.4-23.7 21.6-45.4 47.4-57.3 5.8-2.7 11-6.4 15.1-11.3 22.4-26.4 49.1-39.6 74.2-45.4 6.9-1.6 13.6-2.6 20.1-3.2 3.2-.3 6.4-.5 9.5-.6 1.6-.1 3.1-.1 4.6-.1h4.5c11.7.3 22 1.8 29.6 3.7 50 12.3 89.2 38 116.4 69.5 13.5 15.8 23.9 33 30.7 50.7 3.4 8.9 5.9 17.9 7.4 26.9.8 4.5 1.3 9 1.6 13.5.3 4.5.3 8.9.1 13.4-1.5 27.1-4.4 45.9-7.3 60.1-2.3 11.1.1 22.2 5 32.4 4.9 10.3 5.3 26.7.2 43.9-6.1 20.3-18.3 35.3-29.8 38.7-2.2 8.1-3.8 13.5-3.9 13.5-3.8 29-10.7 59.8-35.3 82.9-10.5 9.8-15 24.5-13.1 38.7.5 3.5 1 6.6 1.6 9.2 5.6 25.1 35.5 49.3 98.1 68.8z"/></svg>
|
After Width: | Height: | Size: 1.4 KiB |
@ -26,6 +26,7 @@ uploadSuccessConfirmHeader = Ready to Send
|
|||||||
uploadSvgAlt = Upload
|
uploadSvgAlt = Upload
|
||||||
uploadSuccessTimingHeader = The link to your file will expire after 1 download or in 24 hours.
|
uploadSuccessTimingHeader = The link to your file will expire after 1 download or in 24 hours.
|
||||||
expireInfo = The link to your file will expire after { $downloadCount } or { $timespan }.
|
expireInfo = The link to your file will expire after { $downloadCount } or { $timespan }.
|
||||||
|
frontPageExpireInfo = Expires after { $downloadCount } or { $timespan }
|
||||||
downloadCount = { $num ->
|
downloadCount = { $num ->
|
||||||
[one] 1 download
|
[one] 1 download
|
||||||
*[other] { $num } downloads
|
*[other] { $num } downloads
|
||||||
@ -34,6 +35,14 @@ timespanHours = { $num ->
|
|||||||
[one] 1 hour
|
[one] 1 hour
|
||||||
*[other] { $num } hours
|
*[other] { $num } hours
|
||||||
}
|
}
|
||||||
|
timespanMinutes = { $num ->
|
||||||
|
[one] 1 minute
|
||||||
|
*[other] { $num } minutes
|
||||||
|
}
|
||||||
|
timespanWeeks = { $num ->
|
||||||
|
[one] 1 week
|
||||||
|
*[other] { $num } weeks
|
||||||
|
}
|
||||||
copyUrlFormLabelWithName = Copy and share the link to send your file: { $filename }
|
copyUrlFormLabelWithName = Copy and share the link to send your file: { $filename }
|
||||||
copyUrlFormButton = Copy to clipboard
|
copyUrlFormButton = Copy to clipboard
|
||||||
copiedUrl = Copied!
|
copiedUrl = Copied!
|
||||||
@ -117,3 +126,30 @@ passwordIsSet = Password set
|
|||||||
maxPasswordLength = Maximum password length: { $length }
|
maxPasswordLength = Maximum password length: { $length }
|
||||||
# A short status message shown when there was an error setting the password
|
# A short status message shown when there was an error setting the password
|
||||||
passwordSetError = This password could not be set
|
passwordSetError = This password could not be set
|
||||||
|
pageHeaderCredits = from the makers of Firefox
|
||||||
|
addFilesButton = Add file(s)
|
||||||
|
uploadFilesButton = Send
|
||||||
|
uploadFileProgress = Sending
|
||||||
|
uploadDropDragMessage = Drop files here
|
||||||
|
uploadDropClickMessage = or click to select a file
|
||||||
|
addPasswordMessage = Protect with password
|
||||||
|
addPasswordLabel = Password:
|
||||||
|
copyUrlLabel = Copy and share this link:
|
||||||
|
passwordReminder = don't forget the password too
|
||||||
|
signInPromoText = Sign In/Up!
|
||||||
|
signInExplanation = It's free and gives you many more Send options
|
||||||
|
signInLearnMore = Learn more!
|
||||||
|
downloadProgressButton = Downloading... { $progress }
|
||||||
|
downloadMessage2 = Firefox Send lets you share files with a safe, private, and encrypted link that automatically expires to ensure your stuff does not remain online forever.
|
||||||
|
signInEmailEnter = Enter your Email
|
||||||
|
emailEntryPlaceholder = Email
|
||||||
|
signInContinueMessage = to continue to Firefox Send
|
||||||
|
signInContinueButton = Continue
|
||||||
|
accountBenefitTitle = With a free Firefox Account with Send you can:
|
||||||
|
accountBenefitMultiFile = Send multiple files at once
|
||||||
|
accountBenefitLargeFiles = Upload larger files (up to { $size } GB)
|
||||||
|
accountBenefitExpiry = Have more expiry options
|
||||||
|
accountBenefitSync = Manage your uploads across devices
|
||||||
|
accountBenefitNotify = Be notified when your files are downloaded
|
||||||
|
accountBenefitMore = Do a lot more!
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ module.exports = async (req, res) => {
|
|||||||
const meta = await storage.metadata(req.params.id);
|
const meta = await storage.metadata(req.params.id);
|
||||||
res.set('WWW-Authenticate', `send-v1 ${meta.nonce}`);
|
res.set('WWW-Authenticate', `send-v1 ${meta.nonce}`);
|
||||||
res.send({
|
res.send({
|
||||||
password: meta.pwd
|
requiresPassword: meta.pwd
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.sendStatus(404);
|
res.sendStatus(404);
|
||||||
|
@ -49,7 +49,7 @@ module.exports = function(app) {
|
|||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.get('/', language, pages.index);
|
app.get('/', language, pages.blank);
|
||||||
app.get('/legal', language, pages.legal);
|
app.get('/legal', language, pages.legal);
|
||||||
app.get('/jsconfig.js', require('./jsconfig'));
|
app.get('/jsconfig.js', require('./jsconfig'));
|
||||||
app.get(`/share/:id${ID_REGEX}`, language, pages.blank);
|
app.get(`/share/:id${ID_REGEX}`, language, pages.blank);
|
||||||
|
@ -19,16 +19,15 @@ module.exports = {
|
|||||||
|
|
||||||
download: async function(req, res, next) {
|
download: async function(req, res, next) {
|
||||||
const id = req.params.id;
|
const id = req.params.id;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { nonce, pwd } = await storage.metadata(id);
|
const { nonce, pwd } = await storage.metadata(id);
|
||||||
res.set('WWW-Authenticate', `send-v1 ${nonce}`);
|
res.set('WWW-Authenticate', `send-v1 ${nonce}`);
|
||||||
res.send(
|
res.send(
|
||||||
stripEvents(
|
stripEvents(
|
||||||
routes.toString(
|
routes.toString(
|
||||||
`/download/${req.params.id}`,
|
`/download/${id}`,
|
||||||
Object.assign(state(req), {
|
Object.assign(state(req), {
|
||||||
fileInfo: { nonce, requiresPassword: +pwd }
|
fileInfo: { nonce, requiresPassword: pwd }
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|