1
0
mirror of https://gitlab.com/timvisee/send.git synced 2024-11-10 21:22:35 +01:00
send/app/ui/archiveTile.js

457 lines
13 KiB
JavaScript
Raw Normal View History

2019-01-16 18:05:39 +01:00
/* global Android */
2018-10-25 04:07:10 +02:00
const html = require('choo/html');
const raw = require('choo/html/raw');
const assets = require('../../common/assets');
2019-01-16 22:20:15 +01:00
const {
browserName,
bytes,
copyToClipboard,
list,
percent,
timeLeft
} = require('../utils');
2018-10-25 04:07:10 +02:00
const expiryOptions = require('./expiryOptions');
function expiryInfo(translate, archive) {
const l10n = timeLeft(archive.expiresAt - Date.now());
return raw(
translate('frontPageExpireInfo', {
downloadCount: translate('downloadCount', {
num: archive.dlimit - archive.dtotal
}),
timespan: translate(l10n.id, l10n)
})
);
}
2018-10-30 03:06:15 +01:00
function password(state) {
const MAX_LENGTH = 32;
return html`
2019-01-16 21:38:43 +01:00
<div class="my-2 px-4 md:px-0">
2018-11-09 01:24:32 +01:00
<div class="checkbox inline-block mr-3">
<input
id="add-password"
type="checkbox"
${state.password ? 'checked' : ''}
autocomplete="off"
onchange="${togglePasswordInput}"
/>
<label for="add-password">
${state.translate('addPasswordMessage')}
</label>
</div>
2018-10-30 03:06:15 +01:00
<input
2018-11-09 01:24:32 +01:00
id="password-input"
class="${
state.password ? '' : 'invisible'
} border rounded-sm focus:border-blue leading-normal my-2 py-1 px-2 h-8"
2018-10-30 03:06:15 +01:00
autocomplete="off"
2018-11-09 01:24:32 +01:00
maxlength="${MAX_LENGTH}"
type="password"
oninput="${inputChanged}"
onfocus="${focused}"
placeholder="${state.translate('unlockInputPlaceholder')}"
value="${state.password || ''}"
/>
<label
id="password-msg"
for="password-input"
class="block text-xs text-grey-darker mt-1"
></label>
2018-10-30 03:06:15 +01:00
</div>
2018-11-09 01:24:32 +01:00
`;
2018-10-30 03:06:15 +01:00
function togglePasswordInput(event) {
event.stopPropagation();
const checked = event.target.checked;
const input = document.getElementById('password-input');
if (checked) {
input.classList.remove('invisible');
input.focus();
} else {
input.classList.add('invisible');
input.value = '';
document.getElementById('password-msg').textContent = '';
state.password = null;
}
}
function inputChanged() {
const passwordInput = document.getElementById('password-input');
const pwdmsg = document.getElementById('password-msg');
const password = passwordInput.value;
const length = password.length;
if (length === MAX_LENGTH) {
pwdmsg.textContent = state.translate('maxPasswordLength', {
length: MAX_LENGTH
});
} else {
pwdmsg.textContent = '';
}
state.password = password;
}
function focused(event) {
event.preventDefault();
const el = document.getElementById('password-input');
if (el.placeholder !== state.translate('unlockInputPlaceholder')) {
el.placeholder = '';
}
}
}
2018-10-25 04:07:10 +02:00
function fileInfo(file, action) {
return html`
<send-file class="flex flex-row items-center p-3 w-full">
2018-10-25 04:07:10 +02:00
<img class="" src="${assets.get('blue_file.svg')}"/>
2018-11-02 10:27:59 +01:00
<p class="ml-4 w-full">
2018-11-06 00:46:49 +01:00
<h1 class="text-sm font-medium word-break-all">${file.name}</h1>
2018-11-02 10:27:59 +01:00
<div class="text-xs font-normal opacity-75 pt-1">${bytes(
file.size
)}</div>
2018-10-25 04:07:10 +02:00
<div class="hidden">${file.type}</div>
</p>
${action}
</send-file>`;
2018-10-25 04:07:10 +02:00
}
function archiveDetails(translate, archive) {
if (archive.manifest.files.length > 1) {
return html`
2018-11-09 01:24:32 +01:00
<details
class="w-full pb-1 overflow-y-scroll"
${archive.open ? 'open' : ''}
ontoggle="${toggled}"
>
<summary
>${
translate('fileCount', {
num: archive.manifest.files.length
})
}</summary
>
${
list(
archive.manifest.files.map(f => fileInfo(f)),
'list-reset h-full'
)
}
</details>
`;
2018-10-25 04:07:10 +02:00
}
2018-10-30 19:37:33 +01:00
function toggled(event) {
event.stopPropagation();
archive.open = event.target.open;
}
2018-10-25 04:07:10 +02:00
}
module.exports = function(state, emit, archive) {
2019-01-16 18:05:39 +01:00
const copyOrShare =
2019-01-16 22:20:15 +01:00
browserName() !== 'android-app'
2019-01-16 18:05:39 +01:00
? html`
<button
class="text-blue hover:text-blue-dark focus:text-blue-darker self-end font-medium flex items-center"
onclick=${copy}
>
<img src="${assets.get('copy-16.svg')}" class="mr-2" /> ${
state.translate('copyUrlHover')
}
</button>
`
: html`
<button
class="text-blue hover:text-blue-dark focus:text-blue-darker self-end font-medium flex items-center"
onclick=${share}
>
<img src="${assets.get('share-16.svg')}" class="mr-2" /> Share
</button>
`;
2018-10-25 04:07:10 +02:00
return html`
<send-archive
id="archive-${archive.id}"
class="flex flex-col items-start border border-grey-light bg-white p-4 w-full">
2018-10-25 04:07:10 +02:00
<p class="w-full">
<img class="float-left mr-3" src="${assets.get('blue_file.svg')}"/>
<input
type="image"
class="float-right self-center text-white"
alt="Delete"
src="${assets.get('close-16.svg')}"
onclick=${del}/>
2018-11-06 00:46:49 +01:00
<h1 class="text-sm font-medium word-break-all">${archive.name}</h1>
2018-11-02 10:27:59 +01:00
<div class="text-xs font-normal opacity-75 pt-1">${bytes(
archive.size
)}</div>
2018-10-25 04:07:10 +02:00
</p>
<div class="text-xs text-grey-dark w-full mt-2 mb-2">
${expiryInfo(state.translate, archive)}
</div>
${archiveDetails(state.translate, archive)}
2018-11-02 10:27:59 +01:00
<hr class="w-full border-t my-4">
2019-01-16 18:05:39 +01:00
${copyOrShare}
</send-archive>`;
2018-10-25 04:07:10 +02:00
function copy(event) {
event.stopPropagation();
copyToClipboard(archive.url);
const text = event.target.lastChild;
text.textContent = state.translate('copiedUrl');
setTimeout(
() => (text.textContent = state.translate('copyUrlHover')),
1000
);
2018-10-25 04:07:10 +02:00
}
function del(event) {
event.stopPropagation();
emit('delete', { file: archive, location: 'success-screen' });
}
2019-01-16 18:05:39 +01:00
function share(event) {
event.stopPropagation();
Android.shareUrl(archive.url);
}
2018-10-25 04:07:10 +02:00
};
module.exports.wip = function(state, emit) {
return html`
<send-upload-area
class="flex flex-col bg-white z-20 md:h-full w-full"
id="wip"
>
2018-11-09 01:24:32 +01:00
${
list(
Array.from(state.archive.files)
.reverse()
.map(f => fileInfo(f, remove(f))),
2018-11-15 18:27:17 +01:00
'list-reset overflow-y-scroll px-4 bg-blue-lightest md:h-full md:max-h-half-screen',
2018-11-09 01:24:32 +01:00
'bg-white px-2 mt-3 border border-grey-light rounded'
)
}
<div class="flex-grow p-4 bg-blue-lightest mb-6 font-medium">
<input
id="file-upload"
class="hidden"
type="file"
multiple
onchange="${add}"
/>
<label
for="file-upload"
2018-11-17 02:17:57 +01:00
class="flex flex-row items-center justify-between w-full p-2 cursor-pointer"
2018-11-09 01:24:32 +01:00
title="${state.translate('addFilesButton')}"
>
2018-11-17 02:17:57 +01:00
<div class="flex items-center">
<img src="${assets.get('addfiles.svg')}" class="w-6 h-6 mr-2" /> ${
state.translate('addFilesButton')
}
</div>
<div class="font-normal text-sm text-grey-darker">
${state.translate('totalSize', { size: bytes(state.archive.size) })}
</div>
2018-11-09 01:24:32 +01:00
</label>
</div>
${expiryOptions(state, emit)} ${password(state, emit)}
<button
id="upload-btn"
2019-01-16 21:38:43 +01:00
class="btn md:rounded flex-no-shrink"
2018-12-12 00:11:38 +01:00
title="${state.translate('uploadFilesButton')}"
2018-11-09 01:24:32 +01:00
onclick="${upload}"
>
2018-12-12 00:11:38 +01:00
${state.translate('uploadFilesButton')}
2018-11-09 01:24:32 +01:00
</button>
</send-upload-area>
2018-11-09 01:24:32 +01:00
`;
2018-10-25 04:07:10 +02:00
function upload(event) {
2018-11-05 09:12:40 +01:00
window.scrollTo(0, 0);
2018-10-25 04:07:10 +02:00
event.preventDefault();
event.target.disabled = true;
if (!state.uploading) {
emit('upload', {
type: 'click',
dlimit: state.downloadCount || 1,
password: state.password
});
}
}
function add(event) {
event.preventDefault();
const newFiles = Array.from(event.target.files);
emit('addFiles', { files: newFiles });
setTimeout(() => {
document
.querySelector('#wip > ul > li:first-child')
.scrollIntoView({ block: 'center' });
});
2018-10-25 04:07:10 +02:00
}
function remove(file) {
return html`
2018-11-09 01:24:32 +01:00
<input
type="image"
class="self-center text-white ml-4"
alt="Delete"
src="${assets.get('close-16.svg')}"
onclick="${del}"
/>
`;
2018-10-25 04:07:10 +02:00
function del(event) {
event.stopPropagation();
emit('removeUpload', file);
}
}
};
module.exports.uploading = function(state, emit) {
const progress = state.transfer.progressRatio;
const progressPercent = percent(progress);
const archive = state.archive;
return html`
<send-upload-area
2018-10-25 04:07:10 +02:00
id="${archive.id}"
class="z-20 flex flex-col items-start border border-grey-light bg-white p-4 w-full">
2018-10-25 04:07:10 +02:00
<p class="w-full">
<img class="float-left mr-3" src="${assets.get('blue_file.svg')}"/>
2018-11-06 00:46:49 +01:00
<h1 class="text-sm font-medium word-break-all">${archive.name}</h1>
2018-11-02 10:27:59 +01:00
<div class="text-xs font-normal opacity-75 pt-1">${bytes(
archive.size
)}</div>
2018-10-25 04:07:10 +02:00
</p>
<div class="text-xs text-grey-dark w-full mt-2 mb-2">
${expiryInfo(state.translate, {
dlimit: state.downloadCount || 1,
dtotal: 0,
expiresAt: Date.now() + 500 + state.timeLimit * 1000
})}
</div>
2018-11-02 10:27:59 +01:00
<div class="text-blue text-sm font-medium mt-2">${progressPercent}</div>
<progress class="my-3" value="${progress}">${progressPercent}</progress>
2018-10-25 04:07:10 +02:00
<button
2019-01-11 10:15:03 +01:00
class="text-blue hover:text-blue-dark focus:text-blue-darker self-end font-medium"
2018-10-25 04:07:10 +02:00
onclick=${cancel}>
${state.translate('uploadingPageCancel')}
</button>
</send-upload-area>`;
2018-10-25 04:07:10 +02:00
function cancel(event) {
event.stopPropagation();
event.target.disabled = true;
emit('cancel');
}
};
module.exports.empty = function(state, emit) {
return html`
<send-upload-area
class="flex flex-col items-center justify-center border-2 border-dashed border-blue-light px-6 py-16 h-full w-full"
2018-11-09 01:24:32 +01:00
onclick="${
e => {
if (e.target.tagName !== 'LABEL') {
document.getElementById('file-upload').click();
}
}
}"
>
<img src="${assets.get('addfiles.svg')}" width="48" height="48" />
<div
class="pt-6 pb-2 text-center text-lg font-bold uppercase tracking-wide"
>
${state.translate('uploadDropDragMessage')}
</div>
<div class="pb-6 text-center text-base italic">
${state.translate('uploadDropClickMessage')}
</div>
<input
id="file-upload"
class="hidden"
type="file"
multiple
onchange="${add}"
onclick="${e => e.stopPropagation()}"
/>
<label
for="file-upload"
role="button"
2019-01-16 21:38:43 +01:00
class="btn rounded flex items-center mt-4"
2018-11-09 01:24:32 +01:00
title="${state.translate('addFilesButton')}"
>
2018-10-25 04:07:10 +02:00
${state.translate('addFilesButton')}
2018-11-09 01:24:32 +01:00
</label>
</send-upload-area>
2018-11-09 01:24:32 +01:00
`;
2018-10-25 04:07:10 +02:00
function add(event) {
event.preventDefault();
const newFiles = Array.from(event.target.files);
emit('addFiles', { files: newFiles });
}
};
module.exports.preview = function(state, emit) {
const archive = state.fileInfo;
2018-10-30 19:37:33 +01:00
if (archive.open === undefined) {
archive.open = true;
}
2018-10-25 04:07:10 +02:00
return html`
<send-archive class="flex flex-col max-h-full bg-white border border-grey-light p-4 z-20 w-full">
2018-11-02 10:27:59 +01:00
<p class="w-full mb-4">
2018-10-25 04:07:10 +02:00
<img class="float-left mr-3" src="${assets.get('blue_file.svg')}"/>
2018-11-06 00:46:49 +01:00
<h1 class="text-sm font-medium word-break-all">${archive.name}</h1>
2018-11-02 10:27:59 +01:00
<div class="text-xs font-normal opacity-75 pt-1">${bytes(
archive.size
)}</div>
2018-10-25 04:07:10 +02:00
</p>
${archiveDetails(state.translate, archive)}
<button
2018-10-31 19:31:17 +01:00
id="download-btn"
2019-01-16 21:38:43 +01:00
class="btn rounded mt-4 w-full flex-no-shrink"
2018-10-25 04:07:10 +02:00
title="${state.translate('downloadButtonLabel')}"
onclick=${download}>
${state.translate('downloadButtonLabel')}
</button>
</send-archive>`;
2018-10-25 04:07:10 +02:00
function download(event) {
event.preventDefault();
event.target.disabled = true;
emit('download', archive);
}
};
module.exports.downloading = function(state, emit) {
const archive = state.fileInfo;
const progress = state.transfer.progressRatio;
const progressPercent = percent(progress);
return html`
<send-archive class="flex flex-col bg-white border border-grey-light p-4 z-20 w-full">
2018-10-25 04:07:10 +02:00
<p class="w-full mb-4">
<img class="float-left mr-3" src="${assets.get('blue_file.svg')}"/>
2018-11-06 00:46:49 +01:00
<h1 class="text-sm font-medium word-break-all">${archive.name}</h1>
2018-11-02 10:27:59 +01:00
<div class="text-xs font-normal opacity-75 pt-1">${bytes(
archive.size
)}</div>
2018-10-25 04:07:10 +02:00
</p>
2018-11-02 10:27:59 +01:00
<div class="text-blue text-sm font-medium mt-2">${progressPercent}</div>
<progress class="my-3" value="${progress}">${progressPercent}</progress>
2018-10-25 04:07:10 +02:00
<button
2018-11-02 10:27:59 +01:00
class="border rounded bg-grey-dark text-white mt-2 text-center py-2 px-6 h-12 w-full flex flex-no-shrink items-center justify-center font-semibold"
2018-10-25 04:07:10 +02:00
title="${state.translate('downloadCancel')}"
onclick=${cancel}>
${state.translate('downloadCancel')}
</button>
</send-archive>`;
2018-10-25 04:07:10 +02:00
function cancel(event) {
event.preventDefault();
event.target.disabled = true;
2018-11-19 19:34:58 +01:00
emit('cancel');
2018-10-25 04:07:10 +02:00
}
};