Try to beautify code by adding line breaks

This commit is contained in:
schlagmichdoch 2023-11-02 02:56:01 +01:00
parent c068a2e329
commit d9270a5560
13 changed files with 714 additions and 470 deletions

View file

@ -54,7 +54,8 @@ const RTC_CONFIG = process.env.RTC_CONFIG
let rateLimit = false; let rateLimit = false;
if (process.argv.includes('--rate-limit') || process.env.RATE_LIMIT === "true") { if (process.argv.includes('--rate-limit') || process.env.RATE_LIMIT === "true") {
rateLimit = 5; rateLimit = 5;
} else { }
else {
let envRateLimit = parseInt(process.env.RATE_LIMIT); let envRateLimit = parseInt(process.env.RATE_LIMIT);
if (!isNaN(envRateLimit)) { if (!isNaN(envRateLimit)) {
rateLimit = envRateLimit; rateLimit = envRateLimit;
@ -126,7 +127,8 @@ if (RATE_LIMIT) {
if (WS_FALLBACK) { if (WS_FALLBACK) {
app.use(express.static('public_included_ws_fallback')); app.use(express.static('public_included_ws_fallback'));
} else { }
else {
app.use(express.static('public')); app.use(express.static('public'));
} }
@ -159,7 +161,8 @@ const server = http.createServer(app);
if (LOCALHOST_ONLY) { if (LOCALHOST_ONLY) {
server.listen(PORT, '127.0.0.1'); server.listen(PORT, '127.0.0.1');
} else { }
else {
server.listen(PORT); server.listen(PORT);
} }
@ -670,9 +673,11 @@ class Peer {
_setIP(request) { _setIP(request) {
if (request.headers['cf-connecting-ip']) { if (request.headers['cf-connecting-ip']) {
this.ip = request.headers['cf-connecting-ip'].split(/\s*,\s*/)[0]; this.ip = request.headers['cf-connecting-ip'].split(/\s*,\s*/)[0];
} else if (request.headers['x-forwarded-for']) { }
else if (request.headers['x-forwarded-for']) {
this.ip = request.headers['x-forwarded-for'].split(/\s*,\s*/)[0]; this.ip = request.headers['x-forwarded-for'].split(/\s*,\s*/)[0];
} else { }
else {
this.ip = request.connection.remoteAddress; this.ip = request.connection.remoteAddress;
} }
@ -748,7 +753,8 @@ class Peer {
let peerIdHash = searchParams.get("peer_id_hash"); let peerIdHash = searchParams.get("peer_id_hash");
if (peerId && Peer.isValidUuid(peerId) && this.isPeerIdHashValid(peerId, peerIdHash)) { if (peerId && Peer.isValidUuid(peerId) && this.isPeerIdHashValid(peerId, peerIdHash)) {
this.id = peerId; this.id = peerId;
} else { }
else {
this.id = crypto.randomUUID(); this.id = crypto.randomUUID();
} }
} }
@ -769,7 +775,8 @@ class Peer {
if (ua.device.model) { if (ua.device.model) {
deviceName += ua.device.model; deviceName += ua.device.model;
} else { }
else {
deviceName += ua.browser.name; deviceName += ua.browser.name;
} }

View file

@ -15,7 +15,8 @@ class Localization {
? storedLanguageCode ? storedLanguageCode
: Localization.systemLocale; : Localization.systemLocale;
Localization.setTranslation(Localization.initialLocale) Localization
.setTranslation(Localization.initialLocale)
.then(_ => { .then(_ => {
console.log("Initial translation successful."); console.log("Initial translation successful.");
Events.fire("initial-translation-loaded"); Events.fire("initial-translation-loaded");
@ -50,7 +51,8 @@ class Localization {
if (Localization.isRTLLanguage(locale)) { if (Localization.isRTLLanguage(locale)) {
htmlRootNode.setAttribute('dir', 'rtl'); htmlRootNode.setAttribute('dir', 'rtl');
} else { }
else {
htmlRootNode.removeAttribute('dir'); htmlRootNode.removeAttribute('dir');
} }
@ -112,7 +114,8 @@ class Localization {
let attr = attrs[i]; let attr = attrs[i];
if (attr === "text") { if (attr === "text") {
element.innerText = Localization.getTranslation(key); element.innerText = Localization.getTranslation(key);
} else { }
else {
if (attr.startsWith("data-")) { if (attr.startsWith("data-")) {
let dataAttr = attr.substring(5); let dataAttr = attr.substring(5);
element.dataset.dataAttr = Localization.getTranslation(key, attr); element.dataset.dataAttr = Localization.getTranslation(key, attr);
@ -156,7 +159,8 @@ class Localization {
console.warn(`Missing translation entry for your language ${Localization.locale.toUpperCase()}. Using ${Localization.defaultLocale.toUpperCase()} instead.`, key, attr); console.warn(`Missing translation entry for your language ${Localization.locale.toUpperCase()}. Using ${Localization.defaultLocale.toUpperCase()} instead.`, key, attr);
console.warn(`Translate this string here: https://hosted.weblate.org/browse/pairdrop/pairdrop-spa/${Localization.locale.toLowerCase()}/?q=${key}`) console.warn(`Translate this string here: https://hosted.weblate.org/browse/pairdrop/pairdrop-spa/${Localization.locale.toLowerCase()}/?q=${key}`)
console.log("Help translating PairDrop: https://hosted.weblate.org/engage/pairdrop/"); console.log("Help translating PairDrop: https://hosted.weblate.org/engage/pairdrop/");
} else { }
else {
console.warn("Missing translation in default language:", key, attr); console.warn("Missing translation in default language:", key, attr);
} }
} }

View file

@ -191,13 +191,16 @@ class ServerConnection {
sessionStorage.setItem('peer_id_hash', msg.peerIdHash); sessionStorage.setItem('peer_id_hash', msg.peerIdHash);
// Add peerId to localStorage to mark it for other PairDrop tabs on the same browser // Add peerId to localStorage to mark it for other PairDrop tabs on the same browser
BrowserTabsConnector.addPeerIdToLocalStorage().then(peerId => { BrowserTabsConnector
.addPeerIdToLocalStorage()
.then(peerId => {
if (!peerId) return; if (!peerId) return;
console.log("successfully added peerId to localStorage"); console.log("successfully added peerId to localStorage");
// Only now join rooms // Only now join rooms
Events.fire('join-ip-room'); Events.fire('join-ip-room');
PersistentStorage.getAllRoomSecrets().then(roomSecrets => { PersistentStorage.getAllRoomSecrets()
.then(roomSecrets => {
Events.fire('room-secrets', roomSecrets); Events.fire('room-secrets', roomSecrets);
}); });
}); });
@ -223,7 +226,9 @@ class ServerConnection {
this.send({ type: 'disconnect' }); this.send({ type: 'disconnect' });
const peerId = sessionStorage.getItem('peer_id'); const peerId = sessionStorage.getItem('peer_id');
BrowserTabsConnector.removePeerIdFromLocalStorage(peerId).then(_ => { BrowserTabsConnector
.removePeerIdFromLocalStorage(peerId)
.then(_ => {
console.log("successfully removed peerId from localStorage"); console.log("successfully removed peerId from localStorage");
}); });
@ -318,7 +323,8 @@ class Peer {
// -> do not delete duplicates and do not regenerate room secrets // -> do not delete duplicates and do not regenerate room secrets
if (!this._isSameBrowser() && roomType === "secret" && this._isPaired() && this._getPairSecret() !== roomId) { if (!this._isSameBrowser() && roomType === "secret" && this._isPaired() && this._getPairSecret() !== roomId) {
// multiple roomSecrets with same peer -> delete old roomSecret // multiple roomSecrets with same peer -> delete old roomSecret
PersistentStorage.deleteRoomSecret(this._getPairSecret()) PersistentStorage
.deleteRoomSecret(this._getPairSecret())
.then(deletedRoomSecret => { .then(deletedRoomSecret => {
if (deletedRoomSecret) console.log("Successfully deleted duplicate room secret with same peer: ", deletedRoomSecret); if (deletedRoomSecret) console.log("Successfully deleted duplicate room secret with same peer: ", deletedRoomSecret);
}); });
@ -348,7 +354,8 @@ class Peer {
return; return;
} }
PersistentStorage.getRoomSecretEntry(this._getPairSecret()) PersistentStorage
.getRoomSecretEntry(this._getPairSecret())
.then(roomSecretEntry => { .then(roomSecretEntry => {
const autoAccept = roomSecretEntry const autoAccept = roomSecretEntry
? roomSecretEntry.entry.auto_accept ? roomSecretEntry.entry.auto_accept
@ -379,13 +386,16 @@ class Peer {
if (width && height) { if (width && height) {
canvas.width = width; canvas.width = width;
canvas.height = height; canvas.height = height;
} else if (width) { }
else if (width) {
canvas.width = width; canvas.width = width;
canvas.height = Math.floor(imageHeight * width / imageWidth) canvas.height = Math.floor(imageHeight * width / imageWidth)
} else if (height) { }
else if (height) {
canvas.width = Math.floor(imageWidth * height / imageHeight); canvas.width = Math.floor(imageWidth * height / imageHeight);
canvas.height = height; canvas.height = height;
} else { }
else {
canvas.width = imageWidth; canvas.width = imageWidth;
canvas.height = imageHeight canvas.height = imageHeight
} }
@ -397,9 +407,11 @@ class Peer {
resolve(dataUrl); resolve(dataUrl);
} }
image.onerror = _ => reject(`Could not create an image thumbnail from type ${file.type}`); image.onerror = _ => reject(`Could not create an image thumbnail from type ${file.type}`);
}).then(dataUrl => { })
.then(dataUrl => {
return dataUrl; return dataUrl;
}).catch(e => console.error(e)); })
.catch(e => console.error(e));
} }
async requestFileTransfer(files) { async requestFileTransfer(files) {
@ -634,7 +646,8 @@ class Peer {
this._busy = false; this._busy = false;
Events.fire('notify-user', Localization.getTranslation("notifications.file-transfer-completed")); Events.fire('notify-user', Localization.getTranslation("notifications.file-transfer-completed"));
Events.fire('files-sent'); // used by 'Snapdrop & PairDrop for Android' app Events.fire('files-sent'); // used by 'Snapdrop & PairDrop for Android' app
} else { }
else {
this._dequeueFile(); this._dequeueFile();
} }
} }
@ -697,7 +710,8 @@ class RTCPeer extends Peer {
if (this._isCaller) { if (this._isCaller) {
this._openChannel(); this._openChannel();
} else { }
else {
this._conn.ondatachannel = e => this._onChannelOpened(e); this._conn.ondatachannel = e => this._onChannelOpened(e);
} }
} }
@ -727,7 +741,8 @@ class RTCPeer extends Peer {
_onDescription(description) { _onDescription(description) {
// description.sdp = description.sdp.replace('b=AS:30', 'b=AS:1638400'); // description.sdp = description.sdp.replace('b=AS:30', 'b=AS:1638400');
this._conn.setLocalDescription(description) this._conn
.setLocalDescription(description)
.then(_ => this._sendSignal({ sdp: description })) .then(_ => this._sendSignal({ sdp: description }))
.catch(e => this._onError(e)); .catch(e => this._onError(e));
} }
@ -741,16 +756,20 @@ class RTCPeer extends Peer {
if (!this._conn) this._connect(); if (!this._conn) this._connect();
if (message.sdp) { if (message.sdp) {
this._conn.setRemoteDescription(message.sdp) this._conn
.setRemoteDescription(message.sdp)
.then(_ => { .then(_ => {
if (message.sdp.type === 'offer') { if (message.sdp.type === 'offer') {
return this._conn.createAnswer() return this._conn
.createAnswer()
.then(d => this._onDescription(d)); .then(d => this._onDescription(d));
} }
}) })
.catch(e => this._onError(e)); .catch(e => this._onError(e));
} else if (message.ice) { }
this._conn.addIceCandidate(new RTCIceCandidate(message.ice)) else if (message.ice) {
this._conn
.addIceCandidate(new RTCIceCandidate(message.ice))
.catch(e => this._onError(e)); .catch(e => this._onError(e));
} }
} }
@ -997,7 +1016,8 @@ class PeersManager {
// If no peers are connected anymore, we can safely assume that no other tab on the same browser is connected: // If no peers are connected anymore, we can safely assume that no other tab on the same browser is connected:
// Tidy up peerIds in localStorage // Tidy up peerIds in localStorage
if (Object.keys(this.peers).length === 0) { if (Object.keys(this.peers).length === 0) {
BrowserTabsConnector.removeOtherPeerIdsFromLocalStorage().then(peerIds => { BrowserTabsConnector.removeOtherPeerIdsFromLocalStorage()
.then(peerIds => {
if (!peerIds) return; if (!peerIds) return;
console.log("successfully removed other peerIds from localStorage"); console.log("successfully removed other peerIds from localStorage");
}); });
@ -1050,13 +1070,16 @@ class PeersManager {
if (peer._getRoomTypes().length > 1) { if (peer._getRoomTypes().length > 1) {
peer._removeRoomType(roomType); peer._removeRoomType(roomType);
} else { }
else {
Events.fire('peer-disconnected', peerId); Events.fire('peer-disconnected', peerId);
} }
} }
_onRoomSecretRegenerated(message) { _onRoomSecretRegenerated(message) {
PersistentStorage.updateRoomSecret(message.oldRoomSecret, message.newRoomSecret).then(_ => { PersistentStorage
.updateRoomSecret(message.oldRoomSecret, message.newRoomSecret)
.then(_ => {
console.log("successfully regenerated room secret"); console.log("successfully regenerated room secret");
Events.fire("room-secrets", [message.newRoomSecret]); Events.fire("room-secrets", [message.newRoomSecret]);
}) })

View file

@ -11,28 +11,32 @@
if (currentTheme === 'dark') { if (currentTheme === 'dark') {
setModeToDark(); setModeToDark();
} else if (currentTheme === 'light') { }
else if (currentTheme === 'light') {
setModeToLight(); setModeToLight();
} }
$themeAuto.addEventListener('click', _ => { $themeAuto.addEventListener('click', _ => {
if (currentTheme) { if (currentTheme) {
setModeToAuto(); setModeToAuto();
} else { }
else {
setModeToDark(); setModeToDark();
} }
}); });
$themeLight.addEventListener('click', _ => { $themeLight.addEventListener('click', _ => {
if (currentTheme !== 'light') { if (currentTheme !== 'light') {
setModeToLight(); setModeToLight();
} else { }
else {
setModeToAuto(); setModeToAuto();
} }
}); });
$themeDark.addEventListener('click', _ => { $themeDark.addEventListener('click', _ => {
if (currentTheme !== 'dark') { if (currentTheme !== 'dark') {
setModeToDark(); setModeToDark();
} else { }
else {
setModeToLight(); setModeToLight();
} }
}); });
@ -64,7 +68,8 @@
document.body.classList.remove('light-theme'); document.body.classList.remove('light-theme');
if (prefersDarkTheme) { if (prefersDarkTheme) {
document.body.classList.add('dark-theme'); document.body.classList.add('dark-theme');
} else if (prefersLightTheme) { }
else if (prefersLightTheme) {
document.body.classList.add('light-theme'); document.body.classList.add('light-theme');
} }
localStorage.removeItem('theme'); localStorage.removeItem('theme');

View file

@ -64,9 +64,12 @@ class PeersUI {
} }
_loadSavedDisplayName() { _loadSavedDisplayName() {
this._getSavedDisplayName().then(displayName => { this._getSavedDisplayName()
.then(displayName => {
console.log("Retrieved edited display name:", displayName) console.log("Retrieved edited display name:", displayName)
if (displayName) Events.fire('self-display-name-changed', displayName); if (displayName) {
Events.fire('self-display-name-changed', displayName);
}
}); });
} }
@ -104,7 +107,8 @@ class PeersUI {
if (this.$discoveryWrapper.querySelectorAll('div:last-of-type > span[hidden]').length < 2) { if (this.$discoveryWrapper.querySelectorAll('div:last-of-type > span[hidden]').length < 2) {
this.$discoveryWrapper.classList.remove('row'); this.$discoveryWrapper.classList.remove('row');
this.$discoveryWrapper.classList.add('column'); this.$discoveryWrapper.classList.add('column');
} else { }
else {
this.$discoveryWrapper.classList.remove('column'); this.$discoveryWrapper.classList.remove('column');
this.$discoveryWrapper.classList.add('row'); this.$discoveryWrapper.classList.add('row');
} }
@ -147,7 +151,8 @@ class PeersUI {
Events.fire('self-display-name-changed', newDisplayName); Events.fire('self-display-name-changed', newDisplayName);
Events.fire('broadcast-send', {type: 'self-display-name-changed', detail: newDisplayName}); Events.fire('broadcast-send', {type: 'self-display-name-changed', detail: newDisplayName});
}); });
} else { }
else {
PersistentStorage.delete('editedDisplayName') PersistentStorage.delete('editedDisplayName')
.catch(_ => { .catch(_ => {
console.log("This browser does not support IndexedDB. Use localStorage instead.") console.log("This browser does not support IndexedDB. Use localStorage instead.")
@ -188,8 +193,12 @@ class PeersUI {
this._changePeerDisplayName(e.detail.peerId, e.detail.displayName); this._changePeerDisplayName(e.detail.peerId, e.detail.displayName);
} }
_noDialogShown() {
return document.querySelectorAll('x-dialog[show]').length === 0;
}
_onKeyDown(e) { _onKeyDown(e) {
if (document.querySelectorAll('x-dialog[show]').length === 0 && window.pasteMode.activated && e.code === "Escape") { if (this._noDialogShown() && window.pasteMode.activated && e.code === "Escape") {
Events.fire('deactivate-paste-mode'); Events.fire('deactivate-paste-mode');
} }
@ -245,7 +254,8 @@ class PeersUI {
_evaluateOverflowing() { _evaluateOverflowing() {
if (this.$xPeers.clientHeight < this.$xPeers.scrollHeight) { if (this.$xPeers.clientHeight < this.$xPeers.scrollHeight) {
this.$xPeers.classList.add('overflowing'); this.$xPeers.classList.add('overflowing');
} else { }
else {
this.$xPeers.classList.remove('overflowing'); this.$xPeers.classList.remove('overflowing');
} }
} }
@ -319,9 +329,11 @@ class PeersUI {
if (files.length === 1) { if (files.length === 1) {
descriptor = `<i>${files[0].name}</i>`; descriptor = `<i>${files[0].name}</i>`;
} else if (files.length > 1) { }
else if (files.length > 1) {
descriptor = `<i>${files[0].name}</i><br>${andOtherFiles}`; descriptor = `<i>${files[0].name}</i><br>${andOtherFiles}`;
} else { }
else {
descriptor = sharedText; descriptor = sharedText;
} }
@ -380,7 +392,8 @@ class PeersUI {
files: files, files: files,
to: peerId to: peerId
}); });
} else if (text.length > 0) { }
else if (text.length > 0) {
Events.fire('send-text', { Events.fire('send-text', {
text: text, text: text,
to: peerId to: peerId
@ -408,7 +421,8 @@ class PeerUI {
let input = ''; let input = '';
if (window.pasteMode.activated) { if (window.pasteMode.activated) {
title = Localization.getTranslation("peer-ui.click-to-send-paste-mode", null, {descriptor: window.pasteMode.descriptor}); title = Localization.getTranslation("peer-ui.click-to-send-paste-mode", null, {descriptor: window.pasteMode.descriptor});
} else { }
else {
title = Localization.getTranslation("peer-ui.click-to-send"); title = Localization.getTranslation("peer-ui.click-to-send");
input = '<input type="file" multiple>'; input = '<input type="file" multiple>';
} }
@ -498,7 +512,8 @@ class PeerUI {
this.$el.addEventListener('contextmenu', this._callbackContextMenu); this.$el.addEventListener('contextmenu', this._callbackContextMenu);
this.$el.addEventListener('touchstart', this._callbackTouchStart); this.$el.addEventListener('touchstart', this._callbackTouchStart);
this.$el.addEventListener('touchend', this._callbackTouchEnd); this.$el.addEventListener('touchend', this._callbackTouchEnd);
} else { }
else {
// Remove Events Normal Mode // Remove Events Normal Mode
this.$el.removeEventListener('click', this._callbackClickSleep); this.$el.removeEventListener('click', this._callbackClickSleep);
this.$el.removeEventListener('touchstart', this._callbackTouchStartSleep); this.$el.removeEventListener('touchstart', this._callbackTouchStartSleep);
@ -566,7 +581,8 @@ class PeerUI {
const $progress = this.$el.querySelector('.progress'); const $progress = this.$el.querySelector('.progress');
if (0.5 < progress && progress < 1) { if (0.5 < progress && progress < 1) {
$progress.classList.add('over50'); $progress.classList.add('over50');
} else { }
else {
$progress.classList.remove('over50'); $progress.classList.remove('over50');
} }
if (progress < 1) { if (progress < 1) {
@ -582,7 +598,8 @@ class PeerUI {
this.$el.querySelector('.status').innerText = statusName; this.$el.querySelector('.status').innerText = statusName;
this.currentStatus = status; this.currentStatus = status;
} }
} else { }
else {
this.$el.removeAttribute('status'); this.$el.removeAttribute('status');
this.$el.querySelector('.status').innerHTML = ''; this.$el.querySelector('.status').innerHTML = '';
progress = 0; progress = 0;
@ -627,7 +644,8 @@ class PeerUI {
_onTouchEnd(e) { _onTouchEnd(e) {
if (Date.now() - this._touchStart < 500) { if (Date.now() - this._touchStart < 500) {
clearTimeout(this._touchTimer); clearTimeout(this._touchTimer);
} else if (this._touchTimer) { // this was a long tap }
else if (this._touchTimer) { // this was a long tap
e.preventDefault(); e.preventDefault();
Events.fire('text-recipient', { Events.fire('text-recipient', {
peerId: this._peer.id, peerId: this._peer.id,
@ -641,7 +659,9 @@ class PeerUI {
class Dialog { class Dialog {
constructor(id) { constructor(id) {
this.$el = $(id); this.$el = $(id);
this.$el.querySelectorAll('[close]').forEach(el => el.addEventListener('click', _ => this.hide())); this.$el.querySelectorAll('[close]').forEach(el => {
el.addEventListener('click', _ => this.hide())
});
this.$autoFocus = this.$el.querySelector('[autofocus]'); this.$autoFocus = this.$el.querySelector('[autofocus]');
Events.on('peer-disconnected', e => this._onPeerDisconnected(e.detail)); Events.on('peer-disconnected', e => this._onPeerDisconnected(e.detail));
@ -699,7 +719,8 @@ class LanguageSelectDialog extends Dialog {
show() { show() {
if (Localization.isSystemLocale()) { if (Localization.isSystemLocale()) {
this.$languageButtons[0].focus(); this.$languageButtons[0].focus();
} else { }
else {
let locale = Localization.getLocale(); let locale = Localization.getLocale();
for (let i=0; i<this.$languageButtons.length; i++) { for (let i=0; i<this.$languageButtons.length; i++) {
const $btn = this.$languageButtons[i]; const $btn = this.$languageButtons[i];
@ -718,7 +739,8 @@ class LanguageSelectDialog extends Dialog {
if (languageCode) { if (languageCode) {
localStorage.setItem('language-code', languageCode); localStorage.setItem('language-code', languageCode);
} else { }
else {
localStorage.removeItem('language-code'); localStorage.removeItem('language-code');
} }
@ -745,11 +767,14 @@ class ReceiveDialog extends Dialog {
// 1024^2 = 104876; 1024^3 = 1073741824 // 1024^2 = 104876; 1024^3 = 1073741824
if (bytes >= 1073741824) { if (bytes >= 1073741824) {
return Math.round(10 * bytes / 1073741824) / 10 + ' GB'; return Math.round(10 * bytes / 1073741824) / 10 + ' GB';
} else if (bytes >= 1048576) { }
else if (bytes >= 1048576) {
return Math.round(bytes / 1048576) + ' MB'; return Math.round(bytes / 1048576) + ' MB';
} else if (bytes > 1024) { }
else if (bytes > 1024) {
return Math.round(bytes / 1024) + ' KB'; return Math.round(bytes / 1024) + ' KB';
} else { }
else {
return bytes + ' Bytes'; return bytes + ' Bytes';
} }
} }
@ -761,7 +786,8 @@ class ReceiveDialog extends Dialog {
fileOther = imagesOnly fileOther = imagesOnly
? Localization.getTranslation("dialogs.file-other-description-image") ? Localization.getTranslation("dialogs.file-other-description-image")
: Localization.getTranslation("dialogs.file-other-description-file"); : Localization.getTranslation("dialogs.file-other-description-file");
} else if (files.length >= 2) { }
else if (files.length >= 2) {
fileOther = imagesOnly fileOther = imagesOnly
? Localization.getTranslation("dialogs.file-other-description-image-plural", null, {count: files.length - 1}) ? Localization.getTranslation("dialogs.file-other-description-image-plural", null, {count: files.length - 1})
: Localization.getTranslation("dialogs.file-other-description-file-plural", null, {count: files.length - 1}); : Localization.getTranslation("dialogs.file-other-description-file-plural", null, {count: files.length - 1});
@ -845,7 +871,8 @@ class ReceiveFileDialog extends ReceiveDialog {
if (Object.keys(previewElement).indexOf(mime) === -1) { if (Object.keys(previewElement).indexOf(mime) === -1) {
resolve(false); resolve(false);
} else { }
else {
let element = document.createElement(previewElement[mime]); let element = document.createElement(previewElement[mime]);
element.controls = true; element.controls = true;
element.onload = _ => { element.onload = _ => {
@ -875,7 +902,8 @@ class ReceiveFileDialog extends ReceiveDialog {
descriptor = imagesOnly descriptor = imagesOnly
? Localization.getTranslation("dialogs.title-image") ? Localization.getTranslation("dialogs.title-image")
: Localization.getTranslation("dialogs.title-file"); : Localization.getTranslation("dialogs.title-file");
} else { }
else {
descriptor = imagesOnly descriptor = imagesOnly
? Localization.getTranslation("dialogs.title-image-plural") ? Localization.getTranslation("dialogs.title-image-plural")
: Localization.getTranslation("dialogs.title-file-plural"); : Localization.getTranslation("dialogs.title-file-plural");
@ -937,7 +965,8 @@ class ReceiveFileDialog extends ReceiveDialog {
tmpZipBtn.download = filenameDownload; tmpZipBtn.download = filenameDownload;
tmpZipBtn.href = url; tmpZipBtn.href = url;
tmpZipBtn.click(); tmpZipBtn.click();
} else { }
else {
this._downloadFilesIndividually(files); this._downloadFilesIndividually(files);
} }
@ -960,7 +989,8 @@ class ReceiveFileDialog extends ReceiveDialog {
setTimeout(() => { setTimeout(() => {
if (canShare) { if (canShare) {
this.$shareBtn.click(); this.$shareBtn.click();
} else { }
else {
this.$downloadBtn.click(); this.$downloadBtn.click();
} }
}, 500); }, 500);
@ -969,7 +999,8 @@ class ReceiveFileDialog extends ReceiveDialog {
.then(canPreview => { .then(canPreview => {
if (canPreview) { if (canPreview) {
console.log('the file is able to preview'); console.log('the file is able to preview');
} else { }
else {
console.log('the file is not able to preview'); console.log('the file is not able to preview');
} }
}) })
@ -1132,10 +1163,12 @@ class InputKeyContainer {
if (e.key === "Backspace" && previousSibling && !e.target.value) { if (e.key === "Backspace" && previousSibling && !e.target.value) {
previousSibling.value = ''; previousSibling.value = '';
previousSibling.focus(); previousSibling.focus();
} else if (e.key === "ArrowRight" && nextSibling) { }
else if (e.key === "ArrowRight" && nextSibling) {
e.preventDefault(); e.preventDefault();
nextSibling.focus(); nextSibling.focus();
} else if (e.key === "ArrowLeft" && previousSibling) { }
else if (e.key === "ArrowLeft" && previousSibling) {
e.preventDefault(); e.preventDefault();
previousSibling.focus(); previousSibling.focus();
} }
@ -1171,7 +1204,8 @@ class InputKeyContainer {
_evaluateKeyChars() { _evaluateKeyChars() {
if (this.$inputKeyContainer.querySelectorAll('input:placeholder-shown').length > 0) { if (this.$inputKeyContainer.querySelectorAll('input:placeholder-shown').length > 0) {
this._onNotAllCharsFilled(); this._onNotAllCharsFilled();
} else { }
else {
this._onAllCharsFilled(); this._onAllCharsFilled();
const lastCharFocused = document.activeElement === this.$inputKeyChars[this.$inputKeyChars.length - 1]; const lastCharFocused = document.activeElement === this.$inputKeyChars[this.$inputKeyChars.length - 1];
@ -1241,7 +1275,10 @@ class PairDeviceDialog extends Dialog {
_onPaste(e) { _onPaste(e) {
e.preventDefault(); e.preventDefault();
let pastedKey = e.clipboardData.getData("Text").replace(/\D/g,'').substring(0, 6); let pastedKey = e.clipboardData
.getData("Text")
.replace(/\D/g,'')
.substring(0, 6);
this.inputKeyContainer._onPaste(pastedKey); this.inputKeyContainer._onPaste(pastedKey);
} }
@ -1364,7 +1401,8 @@ class PairDeviceDialog extends Dialog {
deviceName = $peer.ui._peer.name.deviceName; deviceName = $peer.ui._peer.name.deviceName;
} }
PersistentStorage.addRoomSecret(roomSecret, displayName, deviceName) PersistentStorage
.addRoomSecret(roomSecret, displayName, deviceName)
.then(_ => { .then(_ => {
Events.fire('notify-user', Localization.getTranslation("notifications.pairing-success")); Events.fire('notify-user', Localization.getTranslation("notifications.pairing-success"));
this._evaluateNumberRoomSecrets(); this._evaluateNumberRoomSecrets();
@ -1405,18 +1443,22 @@ class PairDeviceDialog extends Dialog {
} }
_onSecretRoomDeleted(roomSecret) { _onSecretRoomDeleted(roomSecret) {
PersistentStorage.deleteRoomSecret(roomSecret).then(_ => { PersistentStorage
.deleteRoomSecret(roomSecret)
.then(_ => {
this._evaluateNumberRoomSecrets(); this._evaluateNumberRoomSecrets();
}); });
} }
_evaluateNumberRoomSecrets() { _evaluateNumberRoomSecrets() {
PersistentStorage.getAllRoomSecrets() PersistentStorage
.getAllRoomSecrets()
.then(roomSecrets => { .then(roomSecrets => {
if (roomSecrets.length > 0) { if (roomSecrets.length > 0) {
this.$editPairedDevicesHeaderBtn.removeAttribute('hidden'); this.$editPairedDevicesHeaderBtn.removeAttribute('hidden');
this.$footerInstructionsPairedDevices.removeAttribute('hidden'); this.$footerInstructionsPairedDevices.removeAttribute('hidden');
} else { }
else {
this.$editPairedDevicesHeaderBtn.setAttribute('hidden', ''); this.$editPairedDevicesHeaderBtn.setAttribute('hidden', '');
this.$footerInstructionsPairedDevices.setAttribute('hidden', ''); this.$footerInstructionsPairedDevices.setAttribute('hidden', '');
} }
@ -1450,7 +1492,8 @@ class EditPairedDevicesDialog extends Dialog {
const autoAcceptString = Localization.getTranslation("dialogs.auto-accept").toLowerCase(); const autoAcceptString = Localization.getTranslation("dialogs.auto-accept").toLowerCase();
const roomSecretsEntries = await PersistentStorage.getAllRoomSecretEntries(); const roomSecretsEntries = await PersistentStorage.getAllRoomSecretEntries();
roomSecretsEntries.forEach(roomSecretsEntry => { roomSecretsEntries
.forEach(roomSecretsEntry => {
let $pairedDevice = document.createElement('div'); let $pairedDevice = document.createElement('div');
$pairedDevice.classList = ["paired-device"]; $pairedDevice.classList = ["paired-device"];
@ -1468,8 +1511,11 @@ class EditPairedDevicesDialog extends Dialog {
<button class="button" type="button">${unpairString}</button> <button class="button" type="button">${unpairString}</button>
</div>` </div>`
$pairedDevice.querySelector('input[type="checkbox"]').addEventListener('click', e => { $pairedDevice
PersistentStorage.updateRoomSecretAutoAccept(roomSecretsEntry.secret, e.target.checked).then(roomSecretsEntry => { .querySelector('input[type="checkbox"]')
.addEventListener('click', e => {
PersistentStorage.updateRoomSecretAutoAccept(roomSecretsEntry.secret, e.target.checked)
.then(roomSecretsEntry => {
Events.fire('auto-accept-updated', { Events.fire('auto-accept-updated', {
'roomSecret': roomSecretsEntry.entry.secret, 'roomSecret': roomSecretsEntry.entry.secret,
'autoAccept': e.target.checked 'autoAccept': e.target.checked
@ -1477,8 +1523,11 @@ class EditPairedDevicesDialog extends Dialog {
}); });
}); });
$pairedDevice.querySelector('button').addEventListener('click', e => { $pairedDevice
PersistentStorage.deleteRoomSecret(roomSecretsEntry.secret).then(roomSecret => { .querySelector('button')
.addEventListener('click', e => {
PersistentStorage.deleteRoomSecret(roomSecretsEntry.secret)
.then(roomSecret => {
Events.fire('room-secrets-deleted', [roomSecret]); Events.fire('room-secrets-deleted', [roomSecret]);
Events.fire('evaluate-number-room-secrets'); Events.fire('evaluate-number-room-secrets');
e.target.parentNode.parentNode.remove(); e.target.parentNode.parentNode.remove();
@ -1488,7 +1537,6 @@ class EditPairedDevicesDialog extends Dialog {
this.$pairedDevicesWrapper.html = ""; this.$pairedDevicesWrapper.html = "";
this.$pairedDevicesWrapper.appendChild($pairedDevice) this.$pairedDevicesWrapper.appendChild($pairedDevice)
}) })
} }
hide() { hide() {
@ -1503,9 +1551,12 @@ class EditPairedDevicesDialog extends Dialog {
} }
_clearRoomSecrets() { _clearRoomSecrets() {
PersistentStorage.getAllRoomSecrets() PersistentStorage
.getAllRoomSecrets()
.then(roomSecrets => { .then(roomSecrets => {
PersistentStorage.clearRoomSecrets().finally(() => { PersistentStorage
.clearRoomSecrets()
.finally(() => {
Events.fire('room-secrets-deleted', roomSecrets); Events.fire('room-secrets-deleted', roomSecrets);
Events.fire('evaluate-number-room-secrets'); Events.fire('evaluate-number-room-secrets');
Events.fire('notify-user', Localization.getTranslation("notifications.pairing-cleared")); Events.fire('notify-user', Localization.getTranslation("notifications.pairing-cleared"));
@ -1524,7 +1575,9 @@ class EditPairedDevicesDialog extends Dialog {
if (!peer || !peer._roomIds["secret"]) return; if (!peer || !peer._roomIds["secret"]) return;
PersistentStorage.updateRoomSecretNames(peer._roomIds["secret"], peer.name.displayName, peer.name.deviceName).then(roomSecretEntry => { PersistentStorage
.updateRoomSecretNames(peer._roomIds["secret"], peer.name.displayName, peer.name.deviceName)
.then(roomSecretEntry => {
console.log(`Successfully updated DisplayName and DeviceName for roomSecretEntry ${roomSecretEntry.key}`); console.log(`Successfully updated DisplayName and DeviceName for roomSecretEntry ${roomSecretEntry.key}`);
}) })
} }
@ -1589,7 +1642,8 @@ class PublicRoomDialog extends Dialog {
_onHeaderBtnClick() { _onHeaderBtnClick() {
if (this.roomId) { if (this.roomId) {
this.show(); this.show();
} else { }
else {
this._createPublicRoom(); this._createPublicRoom();
} }
} }
@ -1782,7 +1836,8 @@ class SendTextDialog extends Dialog {
if (e.code === "Escape") { if (e.code === "Escape") {
this.hide(); this.hide();
} else if (e.code === "Enter" && (e.ctrlKey || e.metaKey)) { }
else if (e.code === "Enter" && (e.ctrlKey || e.metaKey)) {
if (this._textInputEmpty()) return; if (this._textInputEmpty()) return;
this._send(); this._send();
} }
@ -1795,7 +1850,8 @@ class SendTextDialog extends Dialog {
_onChange(e) { _onChange(e) {
if (this._textInputEmpty()) { if (this._textInputEmpty()) {
this.$submit.setAttribute('disabled', ''); this.$submit.setAttribute('disabled', '');
} else { }
else {
this.$submit.removeAttribute('disabled'); this.$submit.removeAttribute('disabled');
} }
} }
@ -1853,7 +1909,8 @@ class ReceiveTextDialog extends Dialog {
if (e.code === "KeyC" && (e.ctrlKey || e.metaKey)) { if (e.code === "KeyC" && (e.ctrlKey || e.metaKey)) {
await this._onCopy() await this._onCopy()
this.hide(); this.hide();
} else if (e.code === "Escape") { }
else if (e.code === "Escape") {
this.hide(); this.hide();
} }
} }
@ -1903,7 +1960,8 @@ class ReceiveTextDialog extends Dialog {
async _onCopy() { async _onCopy() {
const sanitizedText = this.$text.innerText.replace(/\u00A0/gm, ' '); const sanitizedText = this.$text.innerText.replace(/\u00A0/gm, ' ');
navigator.clipboard.writeText(sanitizedText) navigator.clipboard
.writeText(sanitizedText)
.then(_ => { .then(_ => {
Events.fire('notify-user', Localization.getTranslation("notifications.copied-to-clipboard")); Events.fire('notify-user', Localization.getTranslation("notifications.copied-to-clipboard"));
this.hide(); this.hide();
@ -1937,7 +1995,8 @@ class Base64ZipDialog extends Dialog {
// ?base64text=paste // ?base64text=paste
// base64 encoded string is ready to be pasted from clipboard // base64 encoded string is ready to be pasted from clipboard
this.preparePasting('text'); this.preparePasting('text');
} else if (base64Text === 'hash') { }
else if (base64Text === 'hash') {
// ?base64text=hash#BASE64ENCODED // ?base64text=hash#BASE64ENCODED
// base64 encoded string is url hash which is never sent to server and faster (recommended) // base64 encoded string is url hash which is never sent to server and faster (recommended)
this.processBase64Text(base64Hash) this.processBase64Text(base64Hash)
@ -1947,7 +2006,8 @@ class Base64ZipDialog extends Dialog {
}).finally(() => { }).finally(() => {
this.hide(); this.hide();
}); });
} else { }
else {
// ?base64text=BASE64ENCODED // ?base64text=BASE64ENCODED
// base64 encoded string was part of url param (not recommended) // base64 encoded string was part of url param (not recommended)
this.processBase64Text(base64Text) this.processBase64Text(base64Text)
@ -1958,7 +2018,8 @@ class Base64ZipDialog extends Dialog {
this.hide(); this.hide();
}); });
} }
} else if (base64Zip) { }
else if (base64Zip) {
this.show(); this.show();
if (base64Zip === "hash") { if (base64Zip === "hash") {
// ?base64zip=hash#BASE64ENCODED // ?base64zip=hash#BASE64ENCODED
@ -1970,7 +2031,8 @@ class Base64ZipDialog extends Dialog {
}).finally(() => { }).finally(() => {
this.hide(); this.hide();
}); });
} else { }
else {
// ?base64zip=paste || ?base64zip=true // ?base64zip=paste || ?base64zip=true
this.preparePasting('files'); this.preparePasting('files');
} }
@ -1991,7 +2053,8 @@ class Base64ZipDialog extends Dialog {
this.$pasteBtn.innerText = Localization.getTranslation("dialogs.base64-tap-to-paste", null, {type: translateType}); this.$pasteBtn.innerText = Localization.getTranslation("dialogs.base64-tap-to-paste", null, {type: translateType});
this._clickCallback = _ => this.processClipboard(type); this._clickCallback = _ => this.processClipboard(type);
this.$pasteBtn.addEventListener('click', _ => this._clickCallback()); this.$pasteBtn.addEventListener('click', _ => this._clickCallback());
} else { }
else {
console.log("`navigator.clipboard.readText()` is not available on your browser.\nOn Firefox you can set `dom.events.asyncClipboard.readText` to true under `about:config` for convenience.") console.log("`navigator.clipboard.readText()` is not available on your browser.\nOn Firefox you can set `dom.events.asyncClipboard.readText` to true under `about:config` for convenience.")
this.$pasteBtn.setAttribute('hidden', ''); this.$pasteBtn.setAttribute('hidden', '');
this.$fallbackTextarea.setAttribute('placeholder', Localization.getTranslation("dialogs.base64-paste-to-send", null, {type: translateType})); this.$fallbackTextarea.setAttribute('placeholder', Localization.getTranslation("dialogs.base64-paste-to-send", null, {type: translateType}));
@ -2030,7 +2093,8 @@ class Base64ZipDialog extends Dialog {
try { try {
if (type === 'text') { if (type === 'text') {
await this.processBase64Text(base64); await this.processBase64Text(base64);
} else { }
else {
await this.processBase64Zip(base64); await this.processBase64Zip(base64);
} }
} catch(_) { } catch(_) {
@ -2161,7 +2225,8 @@ class Notifications {
if (/^((https?:\/\/|www)[abcdefghijklmnopqrstuvwxyz0123456789\-._~:\/?#\[\]@!$&'()*+,;=]+)$/.test(message.toLowerCase())) { if (/^((https?:\/\/|www)[abcdefghijklmnopqrstuvwxyz0123456789\-._~:\/?#\[\]@!$&'()*+,;=]+)$/.test(message.toLowerCase())) {
const notification = this._notify(Localization.getTranslation("notifications.link-received", null, {name: peerDisplayName}), message); const notification = this._notify(Localization.getTranslation("notifications.link-received", null, {name: peerDisplayName}), message);
this._bind(notification, _ => window.open(message, '_blank', "noreferrer")); this._bind(notification, _ => window.open(message, '_blank', "noreferrer"));
} else { }
else {
const notification = this._notify(Localization.getTranslation("notifications.message-received", null, {name: peerDisplayName}), message); const notification = this._notify(Localization.getTranslation("notifications.message-received", null, {name: peerDisplayName}), message);
this._bind(notification, _ => this._copyText(message, notification)); this._bind(notification, _ => this._copyText(message, notification));
} }
@ -2180,13 +2245,15 @@ class Notifications {
let title; let title;
if (files.length === 1) { if (files.length === 1) {
title = `${files[0].name}`; title = `${files[0].name}`;
} else { }
else {
let fileOther; let fileOther;
if (files.length === 2) { if (files.length === 2) {
fileOther = imagesOnly fileOther = imagesOnly
? Localization.getTranslation("dialogs.file-other-description-image") ? Localization.getTranslation("dialogs.file-other-description-image")
: Localization.getTranslation("dialogs.file-other-description-file"); : Localization.getTranslation("dialogs.file-other-description-file");
} else { }
else {
fileOther = imagesOnly fileOther = imagesOnly
? Localization.getTranslation("dialogs.file-other-description-image-plural", null, {count: files.length - 1}) ? Localization.getTranslation("dialogs.file-other-description-image-plural", null, {count: files.length - 1})
: Localization.getTranslation("dialogs.file-other-description-file-plural", null, {count: files.length - 1}); : Localization.getTranslation("dialogs.file-other-description-file-plural", null, {count: files.length - 1});
@ -2215,13 +2282,15 @@ class Notifications {
descriptor = imagesOnly descriptor = imagesOnly
? Localization.getTranslation("dialogs.title-image") ? Localization.getTranslation("dialogs.title-image")
: Localization.getTranslation("dialogs.title-file"); : Localization.getTranslation("dialogs.title-file");
} else { }
else {
descriptor = imagesOnly descriptor = imagesOnly
? Localization.getTranslation("dialogs.title-image-plural") ? Localization.getTranslation("dialogs.title-image-plural")
: Localization.getTranslation("dialogs.title-file-plural"); : Localization.getTranslation("dialogs.title-file-plural");
} }
let title = Localization.getTranslation("notifications.request-title", null, { let title = Localization
.getTranslation("notifications.request-title", null, {
name: displayName, name: displayName,
count: request.header.length, count: request.header.length,
descriptor: descriptor.toLowerCase() descriptor: descriptor.toLowerCase()
@ -2240,17 +2309,23 @@ class Notifications {
if (await navigator.clipboard.writeText(message)) { if (await navigator.clipboard.writeText(message)) {
notification.close(); notification.close();
this._notify(Localization.getTranslation("notifications.copied-text")); this._notify(Localization.getTranslation("notifications.copied-text"));
} else { }
else {
this._notify(Localization.getTranslation("notifications.copied-text-error")); this._notify(Localization.getTranslation("notifications.copied-text-error"));
} }
} }
_bind(notification, handler) { _bind(notification, handler) {
if (notification.then) { if (notification.then) {
notification.then(_ => serviceWorker.getNotifications().then(_ => { notification.then(_ => {
serviceWorker
.getNotifications()
.then(_ => {
serviceWorker.addEventListener('notificationclick', handler); serviceWorker.addEventListener('notificationclick', handler);
})); })
} else { });
}
else {
notification.onclick = handler; notification.onclick = handler;
} }
} }
@ -2289,14 +2364,17 @@ class WebShareTargetUI {
if (url) { if (url) {
shareTargetText = url; // we share only the link - no text. shareTargetText = url; // we share only the link - no text.
} else if (title && text) { }
else if (title && text) {
shareTargetText = title + '\r\n' + text; shareTargetText = title + '\r\n' + text;
} else { }
else {
shareTargetText = title + text; shareTargetText = title + text;
} }
Events.fire('activate-paste-mode', {files: [], text: shareTargetText}) Events.fire('activate-paste-mode', {files: [], text: shareTargetText})
} else if (share_target_type === "files") { }
else if (share_target_type === "files") {
let openRequest = window.indexedDB.open('pairdrop_store') let openRequest = window.indexedDB.open('pairdrop_store')
openRequest.onsuccess = e => { openRequest.onsuccess = e => {
const db = e.target.result; const db = e.target.result;
@ -2818,7 +2896,8 @@ const pairDrop = new PairDrop();
const localization = new Localization(); const localization = new Localization();
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js') navigator.serviceWorker
.register('/service-worker.js')
.then(serviceWorker => { .then(serviceWorker => {
console.log('Service Worker registered'); console.log('Service Worker registered');
window.serviceWorker = serviceWorker window.serviceWorker = serviceWorker

View file

@ -55,7 +55,8 @@ const zipper = (() => {
const blobURL = URL.createObjectURL(await zipWriter.close()); const blobURL = URL.createObjectURL(await zipWriter.close());
zipWriter = null; zipWriter = null;
return blobURL; return blobURL;
} else { }
else {
throw new Error("Zip file closed"); throw new Error("Zip file closed");
} }
}, },
@ -64,7 +65,8 @@ const zipper = (() => {
const file = new File([await zipWriter.close()], filename, {type: "application/zip"}); const file = new File([await zipWriter.close()], filename, {type: "application/zip"});
zipWriter = null; zipWriter = null;
return file; return file;
} else { }
else {
throw new Error("Zip file closed"); throw new Error("Zip file closed");
} }
}, },

View file

@ -90,13 +90,16 @@ self.addEventListener('fetch', function(event) {
const share_url = await evaluateRequestData(event.request); const share_url = await evaluateRequestData(event.request);
return Response.redirect(encodeURI(share_url), 302); return Response.redirect(encodeURI(share_url), 302);
})()); })());
} else { }
else {
// Regular requests not related to Web Share Target. // Regular requests not related to Web Share Target.
if (forceFetch) { if (forceFetch) {
event.respondWith(fromNetwork(event.request, 10000)); event.respondWith(fromNetwork(event.request, 10000));
} else { }
else {
event.respondWith( event.respondWith(
fromCache(event.request).then(rsp => { fromCache(event.request)
.then(rsp => {
// if fromCache resolves to undefined fetch from network instead // if fromCache resolves to undefined fetch from network instead
return rsp || fromNetwork(event.request, 10000); return rsp || fromNetwork(event.request, 10000);
}) })
@ -109,7 +112,8 @@ self.addEventListener('fetch', function(event) {
// on activation, we clean up the previously registered service workers // on activation, we clean up the previously registered service workers
self.addEventListener('activate', evt => { self.addEventListener('activate', evt => {
return evt.waitUntil( return evt.waitUntil(
caches.keys().then(cacheNames => { caches.keys()
.then(cacheNames => {
return Promise.all( return Promise.all(
cacheNames.map(cacheName => { cacheNames.map(cacheName => {
if (cacheName !== cacheTitle) { if (cacheName !== cacheTitle) {
@ -157,7 +161,8 @@ const evaluateRequestData = function (request) {
DBOpenRequest.onerror = _ => { DBOpenRequest.onerror = _ => {
resolve(pairDropUrl); resolve(pairDropUrl);
} }
} else { }
else {
let urlArgument = '?share-target=text'; let urlArgument = '?share-target=text';
if (title) urlArgument += `&title=${title}`; if (title) urlArgument += `&title=${title}`;

View file

@ -15,7 +15,8 @@ class Localization {
? storedLanguageCode ? storedLanguageCode
: Localization.systemLocale; : Localization.systemLocale;
Localization.setTranslation(Localization.initialLocale) Localization
.setTranslation(Localization.initialLocale)
.then(_ => { .then(_ => {
console.log("Initial translation successful."); console.log("Initial translation successful.");
Events.fire("initial-translation-loaded"); Events.fire("initial-translation-loaded");
@ -50,7 +51,8 @@ class Localization {
if (Localization.isRTLLanguage(locale)) { if (Localization.isRTLLanguage(locale)) {
htmlRootNode.setAttribute('dir', 'rtl'); htmlRootNode.setAttribute('dir', 'rtl');
} else { }
else {
htmlRootNode.removeAttribute('dir'); htmlRootNode.removeAttribute('dir');
} }
@ -112,7 +114,8 @@ class Localization {
let attr = attrs[i]; let attr = attrs[i];
if (attr === "text") { if (attr === "text") {
element.innerText = Localization.getTranslation(key); element.innerText = Localization.getTranslation(key);
} else { }
else {
if (attr.startsWith("data-")) { if (attr.startsWith("data-")) {
let dataAttr = attr.substring(5); let dataAttr = attr.substring(5);
element.dataset.dataAttr = Localization.getTranslation(key, attr); element.dataset.dataAttr = Localization.getTranslation(key, attr);
@ -156,7 +159,8 @@ class Localization {
console.warn(`Missing translation entry for your language ${Localization.locale.toUpperCase()}. Using ${Localization.defaultLocale.toUpperCase()} instead.`, key, attr); console.warn(`Missing translation entry for your language ${Localization.locale.toUpperCase()}. Using ${Localization.defaultLocale.toUpperCase()} instead.`, key, attr);
console.warn(`Translate this string here: https://hosted.weblate.org/browse/pairdrop/pairdrop-spa/${Localization.locale.toLowerCase()}/?q=${key}`) console.warn(`Translate this string here: https://hosted.weblate.org/browse/pairdrop/pairdrop-spa/${Localization.locale.toLowerCase()}/?q=${key}`)
console.log("Help translating PairDrop: https://hosted.weblate.org/engage/pairdrop/"); console.log("Help translating PairDrop: https://hosted.weblate.org/engage/pairdrop/");
} else { }
else {
console.warn("Missing translation in default language:", key, attr); console.warn("Missing translation in default language:", key, attr);
} }
} }

View file

@ -204,13 +204,16 @@ class ServerConnection {
sessionStorage.setItem('peer_id_hash', msg.peerIdHash); sessionStorage.setItem('peer_id_hash', msg.peerIdHash);
// Add peerId to localStorage to mark it for other PairDrop tabs on the same browser // Add peerId to localStorage to mark it for other PairDrop tabs on the same browser
BrowserTabsConnector.addPeerIdToLocalStorage().then(peerId => { BrowserTabsConnector
.addPeerIdToLocalStorage()
.then(peerId => {
if (!peerId) return; if (!peerId) return;
console.log("successfully added peerId to localStorage"); console.log("successfully added peerId to localStorage");
// Only now join rooms // Only now join rooms
Events.fire('join-ip-room'); Events.fire('join-ip-room');
PersistentStorage.getAllRoomSecrets().then(roomSecrets => { PersistentStorage.getAllRoomSecrets()
.then(roomSecrets => {
Events.fire('room-secrets', roomSecrets); Events.fire('room-secrets', roomSecrets);
}); });
}); });
@ -236,7 +239,9 @@ class ServerConnection {
this.send({ type: 'disconnect' }); this.send({ type: 'disconnect' });
const peerId = sessionStorage.getItem('peer_id'); const peerId = sessionStorage.getItem('peer_id');
BrowserTabsConnector.removePeerIdFromLocalStorage(peerId).then(_ => { BrowserTabsConnector
.removePeerIdFromLocalStorage(peerId)
.then(_ => {
console.log("successfully removed peerId from localStorage"); console.log("successfully removed peerId from localStorage");
}); });
@ -331,7 +336,8 @@ class Peer {
// -> do not delete duplicates and do not regenerate room secrets // -> do not delete duplicates and do not regenerate room secrets
if (!this._isSameBrowser() && roomType === "secret" && this._isPaired() && this._getPairSecret() !== roomId) { if (!this._isSameBrowser() && roomType === "secret" && this._isPaired() && this._getPairSecret() !== roomId) {
// multiple roomSecrets with same peer -> delete old roomSecret // multiple roomSecrets with same peer -> delete old roomSecret
PersistentStorage.deleteRoomSecret(this._getPairSecret()) PersistentStorage
.deleteRoomSecret(this._getPairSecret())
.then(deletedRoomSecret => { .then(deletedRoomSecret => {
if (deletedRoomSecret) console.log("Successfully deleted duplicate room secret with same peer: ", deletedRoomSecret); if (deletedRoomSecret) console.log("Successfully deleted duplicate room secret with same peer: ", deletedRoomSecret);
}); });
@ -361,7 +367,8 @@ class Peer {
return; return;
} }
PersistentStorage.getRoomSecretEntry(this._getPairSecret()) PersistentStorage
.getRoomSecretEntry(this._getPairSecret())
.then(roomSecretEntry => { .then(roomSecretEntry => {
const autoAccept = roomSecretEntry const autoAccept = roomSecretEntry
? roomSecretEntry.entry.auto_accept ? roomSecretEntry.entry.auto_accept
@ -392,13 +399,16 @@ class Peer {
if (width && height) { if (width && height) {
canvas.width = width; canvas.width = width;
canvas.height = height; canvas.height = height;
} else if (width) { }
else if (width) {
canvas.width = width; canvas.width = width;
canvas.height = Math.floor(imageHeight * width / imageWidth) canvas.height = Math.floor(imageHeight * width / imageWidth)
} else if (height) { }
else if (height) {
canvas.width = Math.floor(imageWidth * height / imageHeight); canvas.width = Math.floor(imageWidth * height / imageHeight);
canvas.height = height; canvas.height = height;
} else { }
else {
canvas.width = imageWidth; canvas.width = imageWidth;
canvas.height = imageHeight canvas.height = imageHeight
} }
@ -410,9 +420,11 @@ class Peer {
resolve(dataUrl); resolve(dataUrl);
} }
image.onerror = _ => reject(`Could not create an image thumbnail from type ${file.type}`); image.onerror = _ => reject(`Could not create an image thumbnail from type ${file.type}`);
}).then(dataUrl => { })
.then(dataUrl => {
return dataUrl; return dataUrl;
}).catch(e => console.error(e)); })
.catch(e => console.error(e));
} }
async requestFileTransfer(files) { async requestFileTransfer(files) {
@ -647,7 +659,8 @@ class Peer {
this._busy = false; this._busy = false;
Events.fire('notify-user', Localization.getTranslation("notifications.file-transfer-completed")); Events.fire('notify-user', Localization.getTranslation("notifications.file-transfer-completed"));
Events.fire('files-sent'); // used by 'Snapdrop & PairDrop for Android' app Events.fire('files-sent'); // used by 'Snapdrop & PairDrop for Android' app
} else { }
else {
this._dequeueFile(); this._dequeueFile();
} }
} }
@ -710,7 +723,8 @@ class RTCPeer extends Peer {
if (this._isCaller) { if (this._isCaller) {
this._openChannel(); this._openChannel();
} else { }
else {
this._conn.ondatachannel = e => this._onChannelOpened(e); this._conn.ondatachannel = e => this._onChannelOpened(e);
} }
} }
@ -740,7 +754,8 @@ class RTCPeer extends Peer {
_onDescription(description) { _onDescription(description) {
// description.sdp = description.sdp.replace('b=AS:30', 'b=AS:1638400'); // description.sdp = description.sdp.replace('b=AS:30', 'b=AS:1638400');
this._conn.setLocalDescription(description) this._conn
.setLocalDescription(description)
.then(_ => this._sendSignal({ sdp: description })) .then(_ => this._sendSignal({ sdp: description }))
.catch(e => this._onError(e)); .catch(e => this._onError(e));
} }
@ -754,16 +769,20 @@ class RTCPeer extends Peer {
if (!this._conn) this._connect(); if (!this._conn) this._connect();
if (message.sdp) { if (message.sdp) {
this._conn.setRemoteDescription(message.sdp) this._conn
.setRemoteDescription(message.sdp)
.then(_ => { .then(_ => {
if (message.sdp.type === 'offer') { if (message.sdp.type === 'offer') {
return this._conn.createAnswer() return this._conn
.createAnswer()
.then(d => this._onDescription(d)); .then(d => this._onDescription(d));
} }
}) })
.catch(e => this._onError(e)); .catch(e => this._onError(e));
} else if (message.ice) { }
this._conn.addIceCandidate(new RTCIceCandidate(message.ice)) else if (message.ice) {
this._conn
.addIceCandidate(new RTCIceCandidate(message.ice))
.catch(e => this._onError(e)); .catch(e => this._onError(e));
} }
} }
@ -1007,7 +1026,8 @@ class PeersManager {
if (window.isRtcSupported && rtcSupported) { if (window.isRtcSupported && rtcSupported) {
this.peers[peerId] = new RTCPeer(this._server,isCaller, peerId, roomType, roomId); this.peers[peerId] = new RTCPeer(this._server,isCaller, peerId, roomType, roomId);
} else { }
else {
this.peers[peerId] = new WSPeer(this._server, isCaller, peerId, roomType, roomId); this.peers[peerId] = new WSPeer(this._server, isCaller, peerId, roomType, roomId);
} }
} }
@ -1063,7 +1083,8 @@ class PeersManager {
// If no peers are connected anymore, we can safely assume that no other tab on the same browser is connected: // If no peers are connected anymore, we can safely assume that no other tab on the same browser is connected:
// Tidy up peerIds in localStorage // Tidy up peerIds in localStorage
if (Object.keys(this.peers).length === 0) { if (Object.keys(this.peers).length === 0) {
BrowserTabsConnector.removeOtherPeerIdsFromLocalStorage().then(peerIds => { BrowserTabsConnector.removeOtherPeerIdsFromLocalStorage()
.then(peerIds => {
if (!peerIds) return; if (!peerIds) return;
console.log("successfully removed other peerIds from localStorage"); console.log("successfully removed other peerIds from localStorage");
}); });
@ -1124,13 +1145,16 @@ class PeersManager {
if (peer._getRoomTypes().length > 1) { if (peer._getRoomTypes().length > 1) {
peer._removeRoomType(roomType); peer._removeRoomType(roomType);
} else { }
else {
Events.fire('peer-disconnected', peerId); Events.fire('peer-disconnected', peerId);
} }
} }
_onRoomSecretRegenerated(message) { _onRoomSecretRegenerated(message) {
PersistentStorage.updateRoomSecret(message.oldRoomSecret, message.newRoomSecret).then(_ => { PersistentStorage
.updateRoomSecret(message.oldRoomSecret, message.newRoomSecret)
.then(_ => {
console.log("successfully regenerated room secret"); console.log("successfully regenerated room secret");
Events.fire("room-secrets", [message.newRoomSecret]); Events.fire("room-secrets", [message.newRoomSecret]);
}) })

View file

@ -11,28 +11,32 @@
if (currentTheme === 'dark') { if (currentTheme === 'dark') {
setModeToDark(); setModeToDark();
} else if (currentTheme === 'light') { }
else if (currentTheme === 'light') {
setModeToLight(); setModeToLight();
} }
$themeAuto.addEventListener('click', _ => { $themeAuto.addEventListener('click', _ => {
if (currentTheme) { if (currentTheme) {
setModeToAuto(); setModeToAuto();
} else { }
else {
setModeToDark(); setModeToDark();
} }
}); });
$themeLight.addEventListener('click', _ => { $themeLight.addEventListener('click', _ => {
if (currentTheme !== 'light') { if (currentTheme !== 'light') {
setModeToLight(); setModeToLight();
} else { }
else {
setModeToAuto(); setModeToAuto();
} }
}); });
$themeDark.addEventListener('click', _ => { $themeDark.addEventListener('click', _ => {
if (currentTheme !== 'dark') { if (currentTheme !== 'dark') {
setModeToDark(); setModeToDark();
} else { }
else {
setModeToLight(); setModeToLight();
} }
}); });
@ -64,7 +68,8 @@
document.body.classList.remove('light-theme'); document.body.classList.remove('light-theme');
if (prefersDarkTheme) { if (prefersDarkTheme) {
document.body.classList.add('dark-theme'); document.body.classList.add('dark-theme');
} else if (prefersLightTheme) { }
else if (prefersLightTheme) {
document.body.classList.add('light-theme'); document.body.classList.add('light-theme');
} }
localStorage.removeItem('theme'); localStorage.removeItem('theme');

View file

@ -64,9 +64,12 @@ class PeersUI {
} }
_loadSavedDisplayName() { _loadSavedDisplayName() {
this._getSavedDisplayName().then(displayName => { this._getSavedDisplayName()
.then(displayName => {
console.log("Retrieved edited display name:", displayName) console.log("Retrieved edited display name:", displayName)
if (displayName) Events.fire('self-display-name-changed', displayName); if (displayName) {
Events.fire('self-display-name-changed', displayName);
}
}); });
} }
@ -104,7 +107,8 @@ class PeersUI {
if (this.$discoveryWrapper.querySelectorAll('div:last-of-type > span[hidden]').length < 2) { if (this.$discoveryWrapper.querySelectorAll('div:last-of-type > span[hidden]').length < 2) {
this.$discoveryWrapper.classList.remove('row'); this.$discoveryWrapper.classList.remove('row');
this.$discoveryWrapper.classList.add('column'); this.$discoveryWrapper.classList.add('column');
} else { }
else {
this.$discoveryWrapper.classList.remove('column'); this.$discoveryWrapper.classList.remove('column');
this.$discoveryWrapper.classList.add('row'); this.$discoveryWrapper.classList.add('row');
} }
@ -147,7 +151,8 @@ class PeersUI {
Events.fire('self-display-name-changed', newDisplayName); Events.fire('self-display-name-changed', newDisplayName);
Events.fire('broadcast-send', {type: 'self-display-name-changed', detail: newDisplayName}); Events.fire('broadcast-send', {type: 'self-display-name-changed', detail: newDisplayName});
}); });
} else { }
else {
PersistentStorage.delete('editedDisplayName') PersistentStorage.delete('editedDisplayName')
.catch(_ => { .catch(_ => {
console.log("This browser does not support IndexedDB. Use localStorage instead.") console.log("This browser does not support IndexedDB. Use localStorage instead.")
@ -188,8 +193,12 @@ class PeersUI {
this._changePeerDisplayName(e.detail.peerId, e.detail.displayName); this._changePeerDisplayName(e.detail.peerId, e.detail.displayName);
} }
_isAnyDialogShown() {
return document.querySelectorAll('x-dialog[show]').length === 0;
}
_onKeyDown(e) { _onKeyDown(e) {
if (document.querySelectorAll('x-dialog[show]').length === 0 && window.pasteMode.activated && e.code === "Escape") { if (this._isAnyDialogShown() && window.pasteMode.activated && e.code === "Escape") {
Events.fire('deactivate-paste-mode'); Events.fire('deactivate-paste-mode');
} }
@ -245,7 +254,8 @@ class PeersUI {
_evaluateOverflowing() { _evaluateOverflowing() {
if (this.$xPeers.clientHeight < this.$xPeers.scrollHeight) { if (this.$xPeers.clientHeight < this.$xPeers.scrollHeight) {
this.$xPeers.classList.add('overflowing'); this.$xPeers.classList.add('overflowing');
} else { }
else {
this.$xPeers.classList.remove('overflowing'); this.$xPeers.classList.remove('overflowing');
} }
} }
@ -319,9 +329,11 @@ class PeersUI {
if (files.length === 1) { if (files.length === 1) {
descriptor = `<i>${files[0].name}</i>`; descriptor = `<i>${files[0].name}</i>`;
} else if (files.length > 1) { }
else if (files.length > 1) {
descriptor = `<i>${files[0].name}</i><br>${andOtherFiles}`; descriptor = `<i>${files[0].name}</i><br>${andOtherFiles}`;
} else { }
else {
descriptor = sharedText; descriptor = sharedText;
} }
@ -380,7 +392,8 @@ class PeersUI {
files: files, files: files,
to: peerId to: peerId
}); });
} else if (text.length > 0) { }
else if (text.length > 0) {
Events.fire('send-text', { Events.fire('send-text', {
text: text, text: text,
to: peerId to: peerId
@ -408,7 +421,8 @@ class PeerUI {
let input = ''; let input = '';
if (window.pasteMode.activated) { if (window.pasteMode.activated) {
title = Localization.getTranslation("peer-ui.click-to-send-paste-mode", null, {descriptor: window.pasteMode.descriptor}); title = Localization.getTranslation("peer-ui.click-to-send-paste-mode", null, {descriptor: window.pasteMode.descriptor});
} else { }
else {
title = Localization.getTranslation("peer-ui.click-to-send"); title = Localization.getTranslation("peer-ui.click-to-send");
input = '<input type="file" multiple>'; input = '<input type="file" multiple>';
} }
@ -500,7 +514,8 @@ class PeerUI {
this.$el.addEventListener('contextmenu', this._callbackContextMenu); this.$el.addEventListener('contextmenu', this._callbackContextMenu);
this.$el.addEventListener('touchstart', this._callbackTouchStart); this.$el.addEventListener('touchstart', this._callbackTouchStart);
this.$el.addEventListener('touchend', this._callbackTouchEnd); this.$el.addEventListener('touchend', this._callbackTouchEnd);
} else { }
else {
// Remove Events Normal Mode // Remove Events Normal Mode
this.$el.removeEventListener('click', this._callbackClickSleep); this.$el.removeEventListener('click', this._callbackClickSleep);
this.$el.removeEventListener('touchstart', this._callbackTouchStartSleep); this.$el.removeEventListener('touchstart', this._callbackTouchStartSleep);
@ -568,7 +583,8 @@ class PeerUI {
const $progress = this.$el.querySelector('.progress'); const $progress = this.$el.querySelector('.progress');
if (0.5 < progress && progress < 1) { if (0.5 < progress && progress < 1) {
$progress.classList.add('over50'); $progress.classList.add('over50');
} else { }
else {
$progress.classList.remove('over50'); $progress.classList.remove('over50');
} }
if (progress < 1) { if (progress < 1) {
@ -584,7 +600,8 @@ class PeerUI {
this.$el.querySelector('.status').innerText = statusName; this.$el.querySelector('.status').innerText = statusName;
this.currentStatus = status; this.currentStatus = status;
} }
} else { }
else {
this.$el.removeAttribute('status'); this.$el.removeAttribute('status');
this.$el.querySelector('.status').innerHTML = ''; this.$el.querySelector('.status').innerHTML = '';
progress = 0; progress = 0;
@ -629,7 +646,8 @@ class PeerUI {
_onTouchEnd(e) { _onTouchEnd(e) {
if (Date.now() - this._touchStart < 500) { if (Date.now() - this._touchStart < 500) {
clearTimeout(this._touchTimer); clearTimeout(this._touchTimer);
} else if (this._touchTimer) { // this was a long tap }
else if (this._touchTimer) { // this was a long tap
e.preventDefault(); e.preventDefault();
Events.fire('text-recipient', { Events.fire('text-recipient', {
peerId: this._peer.id, peerId: this._peer.id,
@ -643,7 +661,9 @@ class PeerUI {
class Dialog { class Dialog {
constructor(id) { constructor(id) {
this.$el = $(id); this.$el = $(id);
this.$el.querySelectorAll('[close]').forEach(el => el.addEventListener('click', _ => this.hide())); this.$el.querySelectorAll('[close]').forEach(el => {
el.addEventListener('click', _ => this.hide())
});
this.$autoFocus = this.$el.querySelector('[autofocus]'); this.$autoFocus = this.$el.querySelector('[autofocus]');
Events.on('peer-disconnected', e => this._onPeerDisconnected(e.detail)); Events.on('peer-disconnected', e => this._onPeerDisconnected(e.detail));
@ -701,7 +721,8 @@ class LanguageSelectDialog extends Dialog {
show() { show() {
if (Localization.isSystemLocale()) { if (Localization.isSystemLocale()) {
this.$languageButtons[0].focus(); this.$languageButtons[0].focus();
} else { }
else {
let locale = Localization.getLocale(); let locale = Localization.getLocale();
for (let i=0; i<this.$languageButtons.length; i++) { for (let i=0; i<this.$languageButtons.length; i++) {
const $btn = this.$languageButtons[i]; const $btn = this.$languageButtons[i];
@ -720,7 +741,8 @@ class LanguageSelectDialog extends Dialog {
if (languageCode) { if (languageCode) {
localStorage.setItem('language-code', languageCode); localStorage.setItem('language-code', languageCode);
} else { }
else {
localStorage.removeItem('language-code'); localStorage.removeItem('language-code');
} }
@ -747,11 +769,14 @@ class ReceiveDialog extends Dialog {
// 1024^2 = 104876; 1024^3 = 1073741824 // 1024^2 = 104876; 1024^3 = 1073741824
if (bytes >= 1073741824) { if (bytes >= 1073741824) {
return Math.round(10 * bytes / 1073741824) / 10 + ' GB'; return Math.round(10 * bytes / 1073741824) / 10 + ' GB';
} else if (bytes >= 1048576) { }
else if (bytes >= 1048576) {
return Math.round(bytes / 1048576) + ' MB'; return Math.round(bytes / 1048576) + ' MB';
} else if (bytes > 1024) { }
else if (bytes > 1024) {
return Math.round(bytes / 1024) + ' KB'; return Math.round(bytes / 1024) + ' KB';
} else { }
else {
return bytes + ' Bytes'; return bytes + ' Bytes';
} }
} }
@ -763,7 +788,8 @@ class ReceiveDialog extends Dialog {
fileOther = imagesOnly fileOther = imagesOnly
? Localization.getTranslation("dialogs.file-other-description-image") ? Localization.getTranslation("dialogs.file-other-description-image")
: Localization.getTranslation("dialogs.file-other-description-file"); : Localization.getTranslation("dialogs.file-other-description-file");
} else if (files.length >= 2) { }
else if (files.length >= 2) {
fileOther = imagesOnly fileOther = imagesOnly
? Localization.getTranslation("dialogs.file-other-description-image-plural", null, {count: files.length - 1}) ? Localization.getTranslation("dialogs.file-other-description-image-plural", null, {count: files.length - 1})
: Localization.getTranslation("dialogs.file-other-description-file-plural", null, {count: files.length - 1}); : Localization.getTranslation("dialogs.file-other-description-file-plural", null, {count: files.length - 1});
@ -847,7 +873,8 @@ class ReceiveFileDialog extends ReceiveDialog {
if (Object.keys(previewElement).indexOf(mime) === -1) { if (Object.keys(previewElement).indexOf(mime) === -1) {
resolve(false); resolve(false);
} else { }
else {
let element = document.createElement(previewElement[mime]); let element = document.createElement(previewElement[mime]);
element.controls = true; element.controls = true;
element.onload = _ => { element.onload = _ => {
@ -877,7 +904,8 @@ class ReceiveFileDialog extends ReceiveDialog {
descriptor = imagesOnly descriptor = imagesOnly
? Localization.getTranslation("dialogs.title-image") ? Localization.getTranslation("dialogs.title-image")
: Localization.getTranslation("dialogs.title-file"); : Localization.getTranslation("dialogs.title-file");
} else { }
else {
descriptor = imagesOnly descriptor = imagesOnly
? Localization.getTranslation("dialogs.title-image-plural") ? Localization.getTranslation("dialogs.title-image-plural")
: Localization.getTranslation("dialogs.title-file-plural"); : Localization.getTranslation("dialogs.title-file-plural");
@ -939,7 +967,8 @@ class ReceiveFileDialog extends ReceiveDialog {
tmpZipBtn.download = filenameDownload; tmpZipBtn.download = filenameDownload;
tmpZipBtn.href = url; tmpZipBtn.href = url;
tmpZipBtn.click(); tmpZipBtn.click();
} else { }
else {
this._downloadFilesIndividually(files); this._downloadFilesIndividually(files);
} }
@ -962,7 +991,8 @@ class ReceiveFileDialog extends ReceiveDialog {
setTimeout(() => { setTimeout(() => {
if (canShare) { if (canShare) {
this.$shareBtn.click(); this.$shareBtn.click();
} else { }
else {
this.$downloadBtn.click(); this.$downloadBtn.click();
} }
}, 500); }, 500);
@ -971,7 +1001,8 @@ class ReceiveFileDialog extends ReceiveDialog {
.then(canPreview => { .then(canPreview => {
if (canPreview) { if (canPreview) {
console.log('the file is able to preview'); console.log('the file is able to preview');
} else { }
else {
console.log('the file is not able to preview'); console.log('the file is not able to preview');
} }
}) })
@ -1134,10 +1165,12 @@ class InputKeyContainer {
if (e.key === "Backspace" && previousSibling && !e.target.value) { if (e.key === "Backspace" && previousSibling && !e.target.value) {
previousSibling.value = ''; previousSibling.value = '';
previousSibling.focus(); previousSibling.focus();
} else if (e.key === "ArrowRight" && nextSibling) { }
else if (e.key === "ArrowRight" && nextSibling) {
e.preventDefault(); e.preventDefault();
nextSibling.focus(); nextSibling.focus();
} else if (e.key === "ArrowLeft" && previousSibling) { }
else if (e.key === "ArrowLeft" && previousSibling) {
e.preventDefault(); e.preventDefault();
previousSibling.focus(); previousSibling.focus();
} }
@ -1173,7 +1206,8 @@ class InputKeyContainer {
_evaluateKeyChars() { _evaluateKeyChars() {
if (this.$inputKeyContainer.querySelectorAll('input:placeholder-shown').length > 0) { if (this.$inputKeyContainer.querySelectorAll('input:placeholder-shown').length > 0) {
this._onNotAllCharsFilled(); this._onNotAllCharsFilled();
} else { }
else {
this._onAllCharsFilled(); this._onAllCharsFilled();
const lastCharFocused = document.activeElement === this.$inputKeyChars[this.$inputKeyChars.length - 1]; const lastCharFocused = document.activeElement === this.$inputKeyChars[this.$inputKeyChars.length - 1];
@ -1243,7 +1277,10 @@ class PairDeviceDialog extends Dialog {
_onPaste(e) { _onPaste(e) {
e.preventDefault(); e.preventDefault();
let pastedKey = e.clipboardData.getData("Text").replace(/\D/g,'').substring(0, 6); let pastedKey = e.clipboardData
.getData("Text")
.replace(/\D/g,'')
.substring(0, 6);
this.inputKeyContainer._onPaste(pastedKey); this.inputKeyContainer._onPaste(pastedKey);
} }
@ -1366,7 +1403,8 @@ class PairDeviceDialog extends Dialog {
deviceName = $peer.ui._peer.name.deviceName; deviceName = $peer.ui._peer.name.deviceName;
} }
PersistentStorage.addRoomSecret(roomSecret, displayName, deviceName) PersistentStorage
.addRoomSecret(roomSecret, displayName, deviceName)
.then(_ => { .then(_ => {
Events.fire('notify-user', Localization.getTranslation("notifications.pairing-success")); Events.fire('notify-user', Localization.getTranslation("notifications.pairing-success"));
this._evaluateNumberRoomSecrets(); this._evaluateNumberRoomSecrets();
@ -1407,18 +1445,22 @@ class PairDeviceDialog extends Dialog {
} }
_onSecretRoomDeleted(roomSecret) { _onSecretRoomDeleted(roomSecret) {
PersistentStorage.deleteRoomSecret(roomSecret).then(_ => { PersistentStorage
.deleteRoomSecret(roomSecret)
.then(_ => {
this._evaluateNumberRoomSecrets(); this._evaluateNumberRoomSecrets();
}); });
} }
_evaluateNumberRoomSecrets() { _evaluateNumberRoomSecrets() {
PersistentStorage.getAllRoomSecrets() PersistentStorage
.getAllRoomSecrets()
.then(roomSecrets => { .then(roomSecrets => {
if (roomSecrets.length > 0) { if (roomSecrets.length > 0) {
this.$editPairedDevicesHeaderBtn.removeAttribute('hidden'); this.$editPairedDevicesHeaderBtn.removeAttribute('hidden');
this.$footerInstructionsPairedDevices.removeAttribute('hidden'); this.$footerInstructionsPairedDevices.removeAttribute('hidden');
} else { }
else {
this.$editPairedDevicesHeaderBtn.setAttribute('hidden', ''); this.$editPairedDevicesHeaderBtn.setAttribute('hidden', '');
this.$footerInstructionsPairedDevices.setAttribute('hidden', ''); this.$footerInstructionsPairedDevices.setAttribute('hidden', '');
} }
@ -1452,7 +1494,8 @@ class EditPairedDevicesDialog extends Dialog {
const autoAcceptString = Localization.getTranslation("dialogs.auto-accept").toLowerCase(); const autoAcceptString = Localization.getTranslation("dialogs.auto-accept").toLowerCase();
const roomSecretsEntries = await PersistentStorage.getAllRoomSecretEntries(); const roomSecretsEntries = await PersistentStorage.getAllRoomSecretEntries();
roomSecretsEntries.forEach(roomSecretsEntry => { roomSecretsEntries
.forEach(roomSecretsEntry => {
let $pairedDevice = document.createElement('div'); let $pairedDevice = document.createElement('div');
$pairedDevice.classList = ["paired-device"]; $pairedDevice.classList = ["paired-device"];
@ -1470,8 +1513,11 @@ class EditPairedDevicesDialog extends Dialog {
<button class="button" type="button">${unpairString}</button> <button class="button" type="button">${unpairString}</button>
</div>` </div>`
$pairedDevice.querySelector('input[type="checkbox"]').addEventListener('click', e => { $pairedDevice
PersistentStorage.updateRoomSecretAutoAccept(roomSecretsEntry.secret, e.target.checked).then(roomSecretsEntry => { .querySelector('input[type="checkbox"]')
.addEventListener('click', e => {
PersistentStorage.updateRoomSecretAutoAccept(roomSecretsEntry.secret, e.target.checked)
.then(roomSecretsEntry => {
Events.fire('auto-accept-updated', { Events.fire('auto-accept-updated', {
'roomSecret': roomSecretsEntry.entry.secret, 'roomSecret': roomSecretsEntry.entry.secret,
'autoAccept': e.target.checked 'autoAccept': e.target.checked
@ -1479,8 +1525,11 @@ class EditPairedDevicesDialog extends Dialog {
}); });
}); });
$pairedDevice.querySelector('button').addEventListener('click', e => { $pairedDevice
PersistentStorage.deleteRoomSecret(roomSecretsEntry.secret).then(roomSecret => { .querySelector('button')
.addEventListener('click', e => {
PersistentStorage.deleteRoomSecret(roomSecretsEntry.secret)
.then(roomSecret => {
Events.fire('room-secrets-deleted', [roomSecret]); Events.fire('room-secrets-deleted', [roomSecret]);
Events.fire('evaluate-number-room-secrets'); Events.fire('evaluate-number-room-secrets');
e.target.parentNode.parentNode.remove(); e.target.parentNode.parentNode.remove();
@ -1490,7 +1539,6 @@ class EditPairedDevicesDialog extends Dialog {
this.$pairedDevicesWrapper.html = ""; this.$pairedDevicesWrapper.html = "";
this.$pairedDevicesWrapper.appendChild($pairedDevice) this.$pairedDevicesWrapper.appendChild($pairedDevice)
}) })
} }
hide() { hide() {
@ -1505,9 +1553,12 @@ class EditPairedDevicesDialog extends Dialog {
} }
_clearRoomSecrets() { _clearRoomSecrets() {
PersistentStorage.getAllRoomSecrets() PersistentStorage
.getAllRoomSecrets()
.then(roomSecrets => { .then(roomSecrets => {
PersistentStorage.clearRoomSecrets().finally(() => { PersistentStorage
.clearRoomSecrets()
.finally(() => {
Events.fire('room-secrets-deleted', roomSecrets); Events.fire('room-secrets-deleted', roomSecrets);
Events.fire('evaluate-number-room-secrets'); Events.fire('evaluate-number-room-secrets');
Events.fire('notify-user', Localization.getTranslation("notifications.pairing-cleared")); Events.fire('notify-user', Localization.getTranslation("notifications.pairing-cleared"));
@ -1526,7 +1577,9 @@ class EditPairedDevicesDialog extends Dialog {
if (!peer || !peer._roomIds["secret"]) return; if (!peer || !peer._roomIds["secret"]) return;
PersistentStorage.updateRoomSecretNames(peer._roomIds["secret"], peer.name.displayName, peer.name.deviceName).then(roomSecretEntry => { PersistentStorage
.updateRoomSecretNames(peer._roomIds["secret"], peer.name.displayName, peer.name.deviceName)
.then(roomSecretEntry => {
console.log(`Successfully updated DisplayName and DeviceName for roomSecretEntry ${roomSecretEntry.key}`); console.log(`Successfully updated DisplayName and DeviceName for roomSecretEntry ${roomSecretEntry.key}`);
}) })
} }
@ -1591,7 +1644,8 @@ class PublicRoomDialog extends Dialog {
_onHeaderBtnClick() { _onHeaderBtnClick() {
if (this.roomId) { if (this.roomId) {
this.show(); this.show();
} else { }
else {
this._createPublicRoom(); this._createPublicRoom();
} }
} }
@ -1784,7 +1838,8 @@ class SendTextDialog extends Dialog {
if (e.code === "Escape") { if (e.code === "Escape") {
this.hide(); this.hide();
} else if (e.code === "Enter" && (e.ctrlKey || e.metaKey)) { }
else if (e.code === "Enter" && (e.ctrlKey || e.metaKey)) {
if (this._textInputEmpty()) return; if (this._textInputEmpty()) return;
this._send(); this._send();
} }
@ -1797,7 +1852,8 @@ class SendTextDialog extends Dialog {
_onChange(e) { _onChange(e) {
if (this._textInputEmpty()) { if (this._textInputEmpty()) {
this.$submit.setAttribute('disabled', ''); this.$submit.setAttribute('disabled', '');
} else { }
else {
this.$submit.removeAttribute('disabled'); this.$submit.removeAttribute('disabled');
} }
} }
@ -1855,7 +1911,8 @@ class ReceiveTextDialog extends Dialog {
if (e.code === "KeyC" && (e.ctrlKey || e.metaKey)) { if (e.code === "KeyC" && (e.ctrlKey || e.metaKey)) {
await this._onCopy() await this._onCopy()
this.hide(); this.hide();
} else if (e.code === "Escape") { }
else if (e.code === "Escape") {
this.hide(); this.hide();
} }
} }
@ -1905,7 +1962,8 @@ class ReceiveTextDialog extends Dialog {
async _onCopy() { async _onCopy() {
const sanitizedText = this.$text.innerText.replace(/\u00A0/gm, ' '); const sanitizedText = this.$text.innerText.replace(/\u00A0/gm, ' ');
navigator.clipboard.writeText(sanitizedText) navigator.clipboard
.writeText(sanitizedText)
.then(_ => { .then(_ => {
Events.fire('notify-user', Localization.getTranslation("notifications.copied-to-clipboard")); Events.fire('notify-user', Localization.getTranslation("notifications.copied-to-clipboard"));
this.hide(); this.hide();
@ -1939,7 +1997,8 @@ class Base64ZipDialog extends Dialog {
// ?base64text=paste // ?base64text=paste
// base64 encoded string is ready to be pasted from clipboard // base64 encoded string is ready to be pasted from clipboard
this.preparePasting('text'); this.preparePasting('text');
} else if (base64Text === 'hash') { }
else if (base64Text === 'hash') {
// ?base64text=hash#BASE64ENCODED // ?base64text=hash#BASE64ENCODED
// base64 encoded string is url hash which is never sent to server and faster (recommended) // base64 encoded string is url hash which is never sent to server and faster (recommended)
this.processBase64Text(base64Hash) this.processBase64Text(base64Hash)
@ -1949,7 +2008,8 @@ class Base64ZipDialog extends Dialog {
}).finally(() => { }).finally(() => {
this.hide(); this.hide();
}); });
} else { }
else {
// ?base64text=BASE64ENCODED // ?base64text=BASE64ENCODED
// base64 encoded string was part of url param (not recommended) // base64 encoded string was part of url param (not recommended)
this.processBase64Text(base64Text) this.processBase64Text(base64Text)
@ -1960,7 +2020,8 @@ class Base64ZipDialog extends Dialog {
this.hide(); this.hide();
}); });
} }
} else if (base64Zip) { }
else if (base64Zip) {
this.show(); this.show();
if (base64Zip === "hash") { if (base64Zip === "hash") {
// ?base64zip=hash#BASE64ENCODED // ?base64zip=hash#BASE64ENCODED
@ -1972,7 +2033,8 @@ class Base64ZipDialog extends Dialog {
}).finally(() => { }).finally(() => {
this.hide(); this.hide();
}); });
} else { }
else {
// ?base64zip=paste || ?base64zip=true // ?base64zip=paste || ?base64zip=true
this.preparePasting('files'); this.preparePasting('files');
} }
@ -1993,7 +2055,8 @@ class Base64ZipDialog extends Dialog {
this.$pasteBtn.innerText = Localization.getTranslation("dialogs.base64-tap-to-paste", null, {type: translateType}); this.$pasteBtn.innerText = Localization.getTranslation("dialogs.base64-tap-to-paste", null, {type: translateType});
this._clickCallback = _ => this.processClipboard(type); this._clickCallback = _ => this.processClipboard(type);
this.$pasteBtn.addEventListener('click', _ => this._clickCallback()); this.$pasteBtn.addEventListener('click', _ => this._clickCallback());
} else { }
else {
console.log("`navigator.clipboard.readText()` is not available on your browser.\nOn Firefox you can set `dom.events.asyncClipboard.readText` to true under `about:config` for convenience.") console.log("`navigator.clipboard.readText()` is not available on your browser.\nOn Firefox you can set `dom.events.asyncClipboard.readText` to true under `about:config` for convenience.")
this.$pasteBtn.setAttribute('hidden', ''); this.$pasteBtn.setAttribute('hidden', '');
this.$fallbackTextarea.setAttribute('placeholder', Localization.getTranslation("dialogs.base64-paste-to-send", null, {type: translateType})); this.$fallbackTextarea.setAttribute('placeholder', Localization.getTranslation("dialogs.base64-paste-to-send", null, {type: translateType}));
@ -2032,7 +2095,8 @@ class Base64ZipDialog extends Dialog {
try { try {
if (type === 'text') { if (type === 'text') {
await this.processBase64Text(base64); await this.processBase64Text(base64);
} else { }
else {
await this.processBase64Zip(base64); await this.processBase64Zip(base64);
} }
} catch(_) { } catch(_) {
@ -2163,7 +2227,8 @@ class Notifications {
if (/^((https?:\/\/|www)[abcdefghijklmnopqrstuvwxyz0123456789\-._~:\/?#\[\]@!$&'()*+,;=]+)$/.test(message.toLowerCase())) { if (/^((https?:\/\/|www)[abcdefghijklmnopqrstuvwxyz0123456789\-._~:\/?#\[\]@!$&'()*+,;=]+)$/.test(message.toLowerCase())) {
const notification = this._notify(Localization.getTranslation("notifications.link-received", null, {name: peerDisplayName}), message); const notification = this._notify(Localization.getTranslation("notifications.link-received", null, {name: peerDisplayName}), message);
this._bind(notification, _ => window.open(message, '_blank', "noreferrer")); this._bind(notification, _ => window.open(message, '_blank', "noreferrer"));
} else { }
else {
const notification = this._notify(Localization.getTranslation("notifications.message-received", null, {name: peerDisplayName}), message); const notification = this._notify(Localization.getTranslation("notifications.message-received", null, {name: peerDisplayName}), message);
this._bind(notification, _ => this._copyText(message, notification)); this._bind(notification, _ => this._copyText(message, notification));
} }
@ -2182,13 +2247,15 @@ class Notifications {
let title; let title;
if (files.length === 1) { if (files.length === 1) {
title = `${files[0].name}`; title = `${files[0].name}`;
} else { }
else {
let fileOther; let fileOther;
if (files.length === 2) { if (files.length === 2) {
fileOther = imagesOnly fileOther = imagesOnly
? Localization.getTranslation("dialogs.file-other-description-image") ? Localization.getTranslation("dialogs.file-other-description-image")
: Localization.getTranslation("dialogs.file-other-description-file"); : Localization.getTranslation("dialogs.file-other-description-file");
} else { }
else {
fileOther = imagesOnly fileOther = imagesOnly
? Localization.getTranslation("dialogs.file-other-description-image-plural", null, {count: files.length - 1}) ? Localization.getTranslation("dialogs.file-other-description-image-plural", null, {count: files.length - 1})
: Localization.getTranslation("dialogs.file-other-description-file-plural", null, {count: files.length - 1}); : Localization.getTranslation("dialogs.file-other-description-file-plural", null, {count: files.length - 1});
@ -2217,13 +2284,15 @@ class Notifications {
descriptor = imagesOnly descriptor = imagesOnly
? Localization.getTranslation("dialogs.title-image") ? Localization.getTranslation("dialogs.title-image")
: Localization.getTranslation("dialogs.title-file"); : Localization.getTranslation("dialogs.title-file");
} else { }
else {
descriptor = imagesOnly descriptor = imagesOnly
? Localization.getTranslation("dialogs.title-image-plural") ? Localization.getTranslation("dialogs.title-image-plural")
: Localization.getTranslation("dialogs.title-file-plural"); : Localization.getTranslation("dialogs.title-file-plural");
} }
let title = Localization.getTranslation("notifications.request-title", null, { let title = Localization
.getTranslation("notifications.request-title", null, {
name: displayName, name: displayName,
count: request.header.length, count: request.header.length,
descriptor: descriptor.toLowerCase() descriptor: descriptor.toLowerCase()
@ -2242,17 +2311,23 @@ class Notifications {
if (await navigator.clipboard.writeText(message)) { if (await navigator.clipboard.writeText(message)) {
notification.close(); notification.close();
this._notify(Localization.getTranslation("notifications.copied-text")); this._notify(Localization.getTranslation("notifications.copied-text"));
} else { }
else {
this._notify(Localization.getTranslation("notifications.copied-text-error")); this._notify(Localization.getTranslation("notifications.copied-text-error"));
} }
} }
_bind(notification, handler) { _bind(notification, handler) {
if (notification.then) { if (notification.then) {
notification.then(_ => serviceWorker.getNotifications().then(_ => { notification.then(_ => {
serviceWorker
.getNotifications()
.then(_ => {
serviceWorker.addEventListener('notificationclick', handler); serviceWorker.addEventListener('notificationclick', handler);
})); })
} else { });
}
else {
notification.onclick = handler; notification.onclick = handler;
} }
} }
@ -2291,14 +2366,17 @@ class WebShareTargetUI {
if (url) { if (url) {
shareTargetText = url; // we share only the link - no text. shareTargetText = url; // we share only the link - no text.
} else if (title && text) { }
else if (title && text) {
shareTargetText = title + '\r\n' + text; shareTargetText = title + '\r\n' + text;
} else { }
else {
shareTargetText = title + text; shareTargetText = title + text;
} }
Events.fire('activate-paste-mode', {files: [], text: shareTargetText}) Events.fire('activate-paste-mode', {files: [], text: shareTargetText})
} else if (share_target_type === "files") { }
else if (share_target_type === "files") {
let openRequest = window.indexedDB.open('pairdrop_store') let openRequest = window.indexedDB.open('pairdrop_store')
openRequest.onsuccess = e => { openRequest.onsuccess = e => {
const db = e.target.result; const db = e.target.result;
@ -2820,7 +2898,8 @@ const pairDrop = new PairDrop();
const localization = new Localization(); const localization = new Localization();
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js') navigator.serviceWorker
.register('/service-worker.js')
.then(serviceWorker => { .then(serviceWorker => {
console.log('Service Worker registered'); console.log('Service Worker registered');
window.serviceWorker = serviceWorker window.serviceWorker = serviceWorker

View file

@ -55,7 +55,8 @@ const zipper = (() => {
const blobURL = URL.createObjectURL(await zipWriter.close()); const blobURL = URL.createObjectURL(await zipWriter.close());
zipWriter = null; zipWriter = null;
return blobURL; return blobURL;
} else { }
else {
throw new Error("Zip file closed"); throw new Error("Zip file closed");
} }
}, },
@ -64,7 +65,8 @@ const zipper = (() => {
const file = new File([await zipWriter.close()], filename, {type: "application/zip"}); const file = new File([await zipWriter.close()], filename, {type: "application/zip"});
zipWriter = null; zipWriter = null;
return file; return file;
} else { }
else {
throw new Error("Zip file closed"); throw new Error("Zip file closed");
} }
}, },

View file

@ -90,13 +90,16 @@ self.addEventListener('fetch', function(event) {
const share_url = await evaluateRequestData(event.request); const share_url = await evaluateRequestData(event.request);
return Response.redirect(encodeURI(share_url), 302); return Response.redirect(encodeURI(share_url), 302);
})()); })());
} else { }
else {
// Regular requests not related to Web Share Target. // Regular requests not related to Web Share Target.
if (forceFetch) { if (forceFetch) {
event.respondWith(fromNetwork(event.request, 10000)); event.respondWith(fromNetwork(event.request, 10000));
} else { }
else {
event.respondWith( event.respondWith(
fromCache(event.request).then(rsp => { fromCache(event.request)
.then(rsp => {
// if fromCache resolves to undefined fetch from network instead // if fromCache resolves to undefined fetch from network instead
return rsp || fromNetwork(event.request, 10000); return rsp || fromNetwork(event.request, 10000);
}) })
@ -109,7 +112,8 @@ self.addEventListener('fetch', function(event) {
// on activation, we clean up the previously registered service workers // on activation, we clean up the previously registered service workers
self.addEventListener('activate', evt => { self.addEventListener('activate', evt => {
return evt.waitUntil( return evt.waitUntil(
caches.keys().then(cacheNames => { caches.keys()
.then(cacheNames => {
return Promise.all( return Promise.all(
cacheNames.map(cacheName => { cacheNames.map(cacheName => {
if (cacheName !== cacheTitle) { if (cacheName !== cacheTitle) {
@ -157,7 +161,8 @@ const evaluateRequestData = function (request) {
DBOpenRequest.onerror = _ => { DBOpenRequest.onerror = _ => {
resolve(pairDropUrl); resolve(pairDropUrl);
} }
} else { }
else {
let urlArgument = '?share-target=text'; let urlArgument = '?share-target=text';
if (title) urlArgument += `&title=${title}`; if (title) urlArgument += `&title=${title}`;