From 7220e85422509fb79d787fed66fc0624aa1e0fb6 Mon Sep 17 00:00:00 2001 From: schlagmichdoch Date: Thu, 2 Mar 2023 15:30:25 +0100 Subject: [PATCH 01/14] document/tab title: Show number of received messages and move '- PairDrop' to the end --- public/scripts/ui.js | 16 +++++++++++++--- public_included_ws_fallback/scripts/ui.js | 16 +++++++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/public/scripts/ui.js b/public/scripts/ui.js index 25733b8..303e610 100644 --- a/public/scripts/ui.js +++ b/public/scripts/ui.js @@ -639,7 +639,9 @@ class ReceiveFileDialog extends ReceiveDialog { this.$shareOrDownloadBtn.addEventListener("click", this.continueCallback); this.createPreviewElement(files[0]).finally(_ => { - document.title = `PairDrop - ${files.length} Files received`; + document.title = files.length === 1 + ? 'File received - PairDrop' + : `(${files.length}) Files received - PairDrop`; document.changeFavicon("images/favicon-96x96-notification.png"); this.show(); Events.fire('set-progress', {peerId: peerId, progress: 1, status: 'process'}) @@ -722,7 +724,7 @@ class ReceiveRequestDialog extends ReceiveDialog { this.$previewBox.appendChild(element) } - document.title = 'PairDrop - File Transfer Requested'; + document.title = 'File Transfer Requested - PairDrop'; document.changeFavicon("images/favicon-96x96-notification.png"); this.show(); } @@ -1077,6 +1079,7 @@ class ReceiveTextDialog extends Dialog { _onText(text, peerId) { window.blop.play(); this._receiveTextQueue.push({text: text, peerId: peerId}); + this._setDocumentTitleMessages(); if (this.$el.attributes["show"]) return; this._dequeueRequests(); } @@ -1100,11 +1103,18 @@ class ReceiveTextDialog extends Dialog { } else { this.$text.textContent = text; } - document.title = 'PairDrop - Message Received'; + this._setDocumentTitleMessages(); + document.changeFavicon("images/favicon-96x96-notification.png"); this.show(); } + _setDocumentTitleMessages() { + document.title = !this._receiveTextQueue.length + ? 'Message Received - PairDrop' + : `(${this._receiveTextQueue.length + 1}) Messages Received - PairDrop`; + } + async _onCopy() { await navigator.clipboard.writeText(this.$text.textContent); Events.fire('notify-user', 'Copied to clipboard'); diff --git a/public_included_ws_fallback/scripts/ui.js b/public_included_ws_fallback/scripts/ui.js index dadfb02..ca37ac5 100644 --- a/public_included_ws_fallback/scripts/ui.js +++ b/public_included_ws_fallback/scripts/ui.js @@ -640,7 +640,9 @@ class ReceiveFileDialog extends ReceiveDialog { this.$shareOrDownloadBtn.addEventListener("click", this.continueCallback); this.createPreviewElement(files[0]).finally(_ => { - document.title = `PairDrop - ${files.length} Files received`; + document.title = files.length === 1 + ? 'File received - PairDrop' + : `(${files.length}) Files received - PairDrop`; document.changeFavicon("images/favicon-96x96-notification.png"); this.show(); Events.fire('set-progress', {peerId: peerId, progress: 1, status: 'process'}) @@ -723,7 +725,7 @@ class ReceiveRequestDialog extends ReceiveDialog { this.$previewBox.appendChild(element) } - document.title = 'PairDrop - File Transfer Requested'; + document.title = 'File Transfer Requested - PairDrop'; document.changeFavicon("images/favicon-96x96-notification.png"); this.show(); } @@ -1078,6 +1080,7 @@ class ReceiveTextDialog extends Dialog { _onText(text, peerId) { window.blop.play(); this._receiveTextQueue.push({text: text, peerId: peerId}); + this._setDocumentTitleMessages(); if (this.$el.attributes["show"]) return; this._dequeueRequests(); } @@ -1101,11 +1104,18 @@ class ReceiveTextDialog extends Dialog { } else { this.$text.textContent = text; } - document.title = 'PairDrop - Message Received'; + this._setDocumentTitleMessages(); + document.changeFavicon("images/favicon-96x96-notification.png"); this.show(); } + _setDocumentTitleMessages() { + document.title = !this._receiveTextQueue.length + ? 'Message Received - PairDrop' + : `(${this._receiveTextQueue.length + 1}) Messages Received - PairDrop`; + } + async _onCopy() { await navigator.clipboard.writeText(this.$text.textContent); Events.fire('notify-user', 'Copied to clipboard'); From a1fdd8162920a50b8c8ecec23f8d87ace897291d Mon Sep 17 00:00:00 2001 From: schlagmichdoch Date: Thu, 2 Mar 2023 15:33:30 +0100 Subject: [PATCH 02/14] increase version to v1.2.0 --- package-lock.json | 4 ++-- package.json | 2 +- public/service-worker.js | 2 +- public_included_ws_fallback/service-worker.js | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index c9ddb2a..6e94449 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pairdrop", - "version": "1.1.3", + "version": "1.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "pairdrop", - "version": "1.1.3", + "version": "1.2.0", "license": "ISC", "dependencies": { "express": "^4.18.2", diff --git a/package.json b/package.json index 30444eb..e5c00ab 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pairdrop", - "version": "1.1.3", + "version": "1.2.0", "description": "", "main": "index.js", "scripts": { diff --git a/public/service-worker.js b/public/service-worker.js index 1f1adc6..d8a145a 100644 --- a/public/service-worker.js +++ b/public/service-worker.js @@ -1,4 +1,4 @@ -const cacheVersion = 'v1.1.3'; +const cacheVersion = 'v1.2.0'; const cacheTitle = `pairdrop-cache-${cacheVersion}`; const urlsToCache = [ 'index.html', diff --git a/public_included_ws_fallback/service-worker.js b/public_included_ws_fallback/service-worker.js index 9e968ec..1fb70bc 100644 --- a/public_included_ws_fallback/service-worker.js +++ b/public_included_ws_fallback/service-worker.js @@ -1,4 +1,4 @@ -const cacheVersion = 'v1.1.3'; +const cacheVersion = 'v1.2.0'; const cacheTitle = `pairdrop-included-ws-fallback-cache-${cacheVersion}`; const urlsToCache = [ 'index.html', From 545cdc2459862895e2e2a22746d80fc3956efd30 Mon Sep 17 00:00:00 2001 From: schlagmichdoch Date: Thu, 2 Mar 2023 16:30:47 +0100 Subject: [PATCH 03/14] Fix browser reloading when first message is sent by preventing event default on submit --- public/scripts/ui.js | 23 +++++++++++++++++------ public_included_ws_fallback/scripts/ui.js | 23 +++++++++++++++++------ 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/public/scripts/ui.js b/public/scripts/ui.js index 303e610..9b22ca1 100644 --- a/public/scripts/ui.js +++ b/public/scripts/ui.js @@ -759,7 +759,7 @@ class PairDeviceDialog extends Dialog { this.$clearSecretsBtn = $('clear-pair-devices'); this.$footerInstructionsPairedDevices = $('and-by-paired-devices'); let createJoinForm = this.$el.querySelector('form'); - createJoinForm.addEventListener('submit', _ => this._onSubmit()); + createJoinForm.addEventListener('submit', e => this._onSubmit(e)); this.$el.querySelector('[close]').addEventListener('click', _ => this._pairDeviceCancel()) this.$inputRoomKeyChars.forEach(el => el.addEventListener('input', e => this._onCharsInput(e))); @@ -838,7 +838,7 @@ class PairDeviceDialog extends Dialog { }) this.$submitBtn.removeAttribute("disabled"); if (document.activeElement === this.$inputRoomKeyChars[5]) { - this._onSubmit(); + this._pairDeviceJoin(this.inputRoomKey); } } } @@ -888,7 +888,8 @@ class PairDeviceDialog extends Dialog { return url.href; } - _onSubmit() { + _onSubmit(e) { + e.preventDefault(); this._pairDeviceJoin(this.inputRoomKey); } @@ -975,14 +976,19 @@ class ClearDevicesDialog extends Dialog { super('clear-devices-dialog'); $('clear-pair-devices').addEventListener('click', _ => this._onClearPairDevices()); let clearDevicesForm = this.$el.querySelector('form'); - clearDevicesForm.addEventListener('submit', _ => this._onSubmit()); + clearDevicesForm.addEventListener('submit', e => this._onSubmit(e)); } _onClearPairDevices() { this.show(); } - _onSubmit() { + _onSubmit(e) { + e.preventDefault(); + this._clearRoomSecrets(); + } + + _clearRoomSecrets() { Events.fire('clear-room-secrets'); this.hide(); } @@ -996,7 +1002,7 @@ class SendTextDialog extends Dialog { this.$peerDisplayName = this.$el.querySelector('#text-send-peer-display-name'); this.$form = this.$el.querySelector('form'); this.$submit = this.$el.querySelector('button[type="submit"]'); - this.$form.addEventListener('submit', _ => this._send()); + this.$form.addEventListener('submit', e => this._onSubmit(e)); this.$text.addEventListener('input', e => this._onChange(e)); Events.on("keydown", e => this._onKeyDown(e)); } @@ -1038,6 +1044,11 @@ class SendTextDialog extends Dialog { sel.addRange(range); } + _onSubmit(e) { + e.preventDefault(); + this._send(); + } + _send() { Events.fire('send-text', { to: this.correspondingPeerId, diff --git a/public_included_ws_fallback/scripts/ui.js b/public_included_ws_fallback/scripts/ui.js index ca37ac5..8fca2ba 100644 --- a/public_included_ws_fallback/scripts/ui.js +++ b/public_included_ws_fallback/scripts/ui.js @@ -760,7 +760,7 @@ class PairDeviceDialog extends Dialog { this.$clearSecretsBtn = $('clear-pair-devices'); this.$footerInstructionsPairedDevices = $('and-by-paired-devices'); let createJoinForm = this.$el.querySelector('form'); - createJoinForm.addEventListener('submit', _ => this._onSubmit()); + createJoinForm.addEventListener('submit', e => this._onSubmit(e)); this.$el.querySelector('[close]').addEventListener('click', _ => this._pairDeviceCancel()) this.$inputRoomKeyChars.forEach(el => el.addEventListener('input', e => this._onCharsInput(e))); @@ -839,7 +839,7 @@ class PairDeviceDialog extends Dialog { }) this.$submitBtn.removeAttribute("disabled"); if (document.activeElement === this.$inputRoomKeyChars[5]) { - this._onSubmit(); + this._pairDeviceJoin(this.inputRoomKey); } } } @@ -889,7 +889,8 @@ class PairDeviceDialog extends Dialog { return url.href; } - _onSubmit() { + _onSubmit(e) { + e.preventDefault(); this._pairDeviceJoin(this.inputRoomKey); } @@ -976,14 +977,19 @@ class ClearDevicesDialog extends Dialog { super('clear-devices-dialog'); $('clear-pair-devices').addEventListener('click', _ => this._onClearPairDevices()); let clearDevicesForm = this.$el.querySelector('form'); - clearDevicesForm.addEventListener('submit', _ => this._onSubmit()); + clearDevicesForm.addEventListener('submit', e => this._onSubmit(e)); } _onClearPairDevices() { this.show(); } - _onSubmit() { + _onSubmit(e) { + e.preventDefault(); + this._clearRoomSecrets(); + } + + _clearRoomSecrets() { Events.fire('clear-room-secrets'); this.hide(); } @@ -997,7 +1003,7 @@ class SendTextDialog extends Dialog { this.$peerDisplayName = this.$el.querySelector('#text-send-peer-display-name'); this.$form = this.$el.querySelector('form'); this.$submit = this.$el.querySelector('button[type="submit"]'); - this.$form.addEventListener('submit', _ => this._send()); + this.$form.addEventListener('submit', e => this._onSubmit(e)); this.$text.addEventListener('input', e => this._onChange(e)); Events.on("keydown", e => this._onKeyDown(e)); } @@ -1039,6 +1045,11 @@ class SendTextDialog extends Dialog { sel.addRange(range); } + _onSubmit(e) { + e.preventDefault(); + this._send(); + } + _send() { Events.fire('send-text', { to: this.correspondingPeerId, From 3a2d8c75f7de1bbfd380af7b4115a5ff21b8b906 Mon Sep 17 00:00:00 2001 From: schlagmichdoch Date: Fri, 3 Mar 2023 12:01:43 +0100 Subject: [PATCH 04/14] - restructure and unify dialogs to use less space on mobile and be clearer - give user option both options "share" and "download" on mobile - add fallback if zipper fails that downloads files individually - fix dequeuing of message queue not possible if sending peer has left --- public/index.html | 88 ++++---- public/scripts/network.js | 2 +- public/scripts/ui.js | 200 ++++++++++-------- public/styles.css | 112 +++++----- public_included_ws_fallback/index.html | 88 ++++---- .../scripts/network.js | 2 +- public_included_ws_fallback/scripts/ui.js | 200 ++++++++++-------- public_included_ws_fallback/styles.css | 112 +++++----- 8 files changed, 410 insertions(+), 394 deletions(-) diff --git a/public/index.html b/public/index.html index 59a257a..4171562 100644 --- a/public/index.html +++ b/public/index.html @@ -69,7 +69,7 @@ - +
@@ -106,18 +106,17 @@
Input this key on another device
or scan the QR-Code.

- - - - - - + + + + + +
Enter key from another device to continue.
-
+
-
- Cancel +
@@ -130,9 +129,9 @@

Unpair Devices

Are you sure to unpair all devices?
-
+
- Cancel +
@@ -142,25 +141,23 @@ -

PairDrop

-
+

+
- + would like to share
-
- - +
+ +
-
- +
+
-
-
+
-
@@ -170,13 +167,23 @@ -

-
-
+

+
+
+ + has sent +
+
+ + +
+
+
+
-
- -
+
+ +
@@ -187,16 +194,16 @@
-

PairDrop

-
+

Send Message

+
Send a Message to - +
+
-
+
-
- Cancel +
@@ -206,16 +213,15 @@ -

PairDrop - Message Received

-
- - sent the following message: +

Message Received

+
+ + has sent:
-
+
-
diff --git a/public/scripts/network.js b/public/scripts/network.js index be1389f..e2ad3cc 100644 --- a/public/scripts/network.js +++ b/public/scripts/network.js @@ -441,7 +441,7 @@ class Peer { if (!this._requestAccepted.header.length) { this._busy = false; Events.fire('set-progress', {peerId: this._peerId, progress: 0, status: 'process'}); - Events.fire('files-received', {sender: this._peerId, files: this._filesReceived, request: this._requestAccepted}); + Events.fire('files-received', {sender: this._peerId, files: this._filesReceived, imagesOnly: this._requestAccepted.imagesOnly, totalSize: this._requestAccepted.totalSize}); this._filesReceived = []; this._requestAccepted = null; } diff --git a/public/scripts/ui.js b/public/scripts/ui.js index 9b22ca1..4f2c167 100644 --- a/public/scripts/ui.js +++ b/public/scripts/ui.js @@ -28,7 +28,7 @@ class PeersUI { Events.on('activate-paste-mode', e => this._activatePasteMode(e.detail.files, e.detail.text)); this.peers = {}; - this.$cancelPasteModeBtn = $('cancel-paste-mode-btn'); + this.$cancelPasteModeBtn = $('cancel-paste-mode'); this.$cancelPasteModeBtn.addEventListener('click', _ => this._cancelPasteMode()); Events.on('dragover', e => this._onDragOver(e)); @@ -473,10 +473,14 @@ class Dialog { class ReceiveDialog extends Dialog { constructor(id) { super(id); - - this.$fileDescriptionNode = this.$el.querySelector('.file-description'); - this.$fileSizeNode = this.$el.querySelector('.file-size'); - this.$previewBox = this.$el.querySelector('.file-preview') + this.$fileDescription = this.$el.querySelector('.file-description'); + this.$displayName = this.$el.querySelector('.display-name'); + this.$fileStem = this.$el.querySelector('.file-stem'); + this.$fileExtension = this.$el.querySelector('.file-extension'); + this.$fileOther = this.$el.querySelector('.file-other'); + this.$fileSize = this.$el.querySelector('.file-size'); + this.$previewBox = this.$el.querySelector('.file-preview'); + this.$receiveTitle = this.$el.querySelector('h2:first-of-type'); } _formatFileSize(bytes) { @@ -492,6 +496,26 @@ class ReceiveDialog extends Dialog { return bytes + ' Bytes'; } } + + _parseFileData(displayName, files, imagesOnly, totalSize) { + if (files.length > 1) { + let fileOtherText = ` and ${files.length - 1} other `; + if (files.length === 2) { + fileOtherText += imagesOnly ? 'image' : 'file'; + } else { + fileOtherText += imagesOnly ? 'images' : 'files'; + } + this.$fileOther.innerText = fileOtherText; + } + + const fileName = files[0].name; + const fileNameSplit = fileName.split('.'); + const fileExtension = '.' + fileNameSplit[fileNameSplit.length - 1]; + this.$fileStem.innerText = fileName.substring(0, fileName.length - fileExtension.length); + this.$fileExtension.innerText = fileExtension; + this.$displayName.innerText = displayName; + this.$fileSize.innerText = this._formatFileSize(totalSize); + } } class ReceiveFileDialog extends ReceiveDialog { @@ -499,24 +523,25 @@ class ReceiveFileDialog extends ReceiveDialog { constructor() { super('receive-file-dialog'); - this.$shareOrDownloadBtn = this.$el.querySelector('#share-or-download'); - this.$receiveTitleNode = this.$el.querySelector('#receive-title') + this.$downloadBtn = this.$el.querySelector('#download-btn'); + this.$shareBtn = this.$el.querySelector('#share-btn'); - Events.on('files-received', e => this._onFilesReceived(e.detail.sender, e.detail.files, e.detail.request)); + Events.on('files-received', e => this._onFilesReceived(e.detail.sender, e.detail.files, e.detail.imagesOnly, e.detail.totalSize)); this._filesQueue = []; } - _onFilesReceived(sender, files, request) { - this._nextFiles(sender, files, request); + _onFilesReceived(sender, files, imagesOnly, totalSize) { + const displayName = $(sender).ui._displayName() + this._filesQueue.push({peer: sender, displayName: displayName, files: files, imagesOnly: imagesOnly, totalSize: totalSize}); + this._nextFiles(); window.blop.play(); } - _nextFiles(sender, nextFiles, nextRequest) { - if (nextFiles) this._filesQueue.push({peerId: sender, files: nextFiles, request: nextRequest}); + _nextFiles() { if (this._busy) return; this._busy = true; - const {peerId, files, request} = this._filesQueue.shift(); - this._displayFiles(peerId, files, request); + const {peer, displayName, files, imagesOnly, totalSize} = this._filesQueue.shift(); + this._displayFiles(peer, displayName, files, imagesOnly, totalSize); } _dequeueFile() { @@ -547,7 +572,6 @@ class ReceiveFileDialog extends ReceiveDialog { let element = document.createElement(previewElement[mime]); element.src = URL.createObjectURL(file); element.controls = true; - element.classList.add('element-preview'); element.onload = _ => { this.$previewBox.appendChild(element); resolve(true) @@ -558,30 +582,32 @@ class ReceiveFileDialog extends ReceiveDialog { }); } - async _displayFiles(peerId, files, request) { - if (this.continueCallback) this.$shareOrDownloadBtn.removeEventListener("click", this.continueCallback); - - let url; - let title; - let filenameDownload; - - let descriptor = request.imagesOnly ? "Image" : "File"; - - let size = this._formatFileSize(request.totalSize); - let description = files[0].name; - - let shareInsteadOfDownload = (window.iOS || window.android) && !!navigator.share && navigator.canShare({files}); + async _displayFiles(peerId, displayName, files, imagesOnly, totalSize) { + this._parseFileData(displayName, files, imagesOnly, totalSize); + let descriptor, url, filenameDownload; if (files.length === 1) { - url = URL.createObjectURL(files[0]) - title = `PairDrop - ${descriptor} Received` - filenameDownload = files[0].name; + descriptor = imagesOnly ? 'Image' : 'File'; } else { - title = `PairDrop - ${files.length} ${descriptor}s Received` - description += ` and ${files.length-1} other ${descriptor.toLowerCase()}`; - if(files.length>2) description += "s"; + descriptor = imagesOnly ? 'Images' : 'Files'; + } + this.$receiveTitle.innerText = `${descriptor} Received`; - if(!shareInsteadOfDownload) { + const canShare = (window.iOS || window.android) && !!navigator.share && navigator.canShare({files}); + if (canShare) { + this.$shareBtn.removeAttribute('hidden'); + this.$shareBtn.onclick = _ => { + navigator.share({files: files}) + .catch(err => { + console.error(err); + }); + } + } + + let downloadZipped = false; + if (files.length > 1) { + downloadZipped = true; + try { let bytesCompleted = 0; zipper.createNewZipWriter(); for (let i=0; i { Events.fire('set-progress', { peerId: peerId, - progress: (bytesCompleted + progress) / request.totalSize, + progress: (bytesCompleted + progress) / totalSize, status: 'process' }) } @@ -609,49 +635,58 @@ class ReceiveFileDialog extends ReceiveDialog { let minutes = now.getMinutes().toString(); minutes = minutes.length < 2 ? "0" + minutes : minutes; filenameDownload = `PairDrop_files_${year+month+date}_${hours+minutes}.zip`; + } catch (e) { + console.error(e); + downloadZipped = false; } } - this.$receiveTitleNode.textContent = title; - this.$fileDescriptionNode.textContent = description; - this.$fileSizeNode.textContent = size; - - if (shareInsteadOfDownload) { - this.$shareOrDownloadBtn.innerText = "Share"; - this.continue = _ => { - navigator.share({files: files}) - .catch(err => console.error(err)); + this.$downloadBtn.innerText = "Download"; + this.$downloadBtn.onclick = _ => { + if (downloadZipped) { + let tmpZipBtn = document.createElement("a"); + tmpZipBtn.download = filenameDownload; + tmpZipBtn.href = url; + tmpZipBtn.click(); + } else { + this._downloadFilesIndividually(files); } - this.continueCallback = _ => this.continue(); - } else { - this.$shareOrDownloadBtn.innerText = "Download again"; - this.continue = _ => { - let tmpBtn = document.createElement("a"); - tmpBtn.download = filenameDownload; - tmpBtn.href = url; - tmpBtn.click(); - }; - this.continueCallback = _ => { - this.continue(); - this.hide(); - }; - } - this.$shareOrDownloadBtn.addEventListener("click", this.continueCallback); + + if (!canShare) { + this.$downloadBtn.innerText = "Download again"; + } + Events.fire('notify-user', `${descriptor} downloaded successfully`); + this.$downloadBtn.style.pointerEvents = "none"; + setTimeout(_ => this.$downloadBtn.style.pointerEvents = "unset", 2000); + }; this.createPreviewElement(files[0]).finally(_ => { document.title = files.length === 1 ? 'File received - PairDrop' : `(${files.length}) Files received - PairDrop`; document.changeFavicon("images/favicon-96x96-notification.png"); - this.show(); Events.fire('set-progress', {peerId: peerId, progress: 1, status: 'process'}) - this.continue(); + this.show(); + + if (canShare) { + this.$shareBtn.click(); + } else { + this.$downloadBtn.click(); + } }).catch(r => console.error(r)); } + _downloadFilesIndividually(files) { + let tmpBtn = document.createElement("a"); + for (let i=0; i this._respondToFileTransferRequest(true)); @@ -699,32 +729,18 @@ class ReceiveRequestDialog extends ReceiveDialog { _showRequestDialog(request, peerId) { this.correspondingPeerId = peerId; - this.$requestingPeerDisplayNameNode.innerText = $(peerId).ui._displayName(); - - const fileName = request.header[0].name; - const fileNameSplit = fileName.split('.'); - const fileExtension = '.' + fileNameSplit[fileNameSplit.length - 1]; - this.$fileStemNode.innerText = fileName.substring(0, fileName.length - fileExtension.length); - this.$fileExtensionNode.innerText = fileExtension - - if (request.header.length >= 2) { - let fileOtherText = ` and ${request.header.length - 1} other `; - fileOtherText += request.imagesOnly ? 'image' : 'file'; - if (request.header.length > 2) fileOtherText += "s"; - this.$fileOtherNode.innerText = fileOtherText; - } - - this.$fileSizeNode.innerText = this._formatFileSize(request.totalSize); + const displayName = $(peerId).ui._displayName(); + this._parseFileData(displayName, request.header, request.imagesOnly, request.totalSize); if (request.thumbnailDataUrl?.substring(0, 22) === "data:image/jpeg;base64") { let element = document.createElement('img'); element.src = request.thumbnailDataUrl; - element.classList.add('element-preview'); - this.$previewBox.appendChild(element) } - document.title = 'File Transfer Requested - PairDrop'; + this.$receiveTitle.innerText = `${request.imagesOnly ? 'Image' : 'File'} Transfer Request` + + document.title = `${request.imagesOnly ? 'Image' : 'File'} Transfer Requested - PairDrop`; document.changeFavicon("images/favicon-96x96-notification.png"); this.show(); } @@ -999,7 +1015,7 @@ class SendTextDialog extends Dialog { super('send-text-dialog'); Events.on('text-recipient', e => this._onRecipient(e.detail.peerId, e.detail.deviceName)); this.$text = this.$el.querySelector('#text-input'); - this.$peerDisplayName = this.$el.querySelector('#text-send-peer-display-name'); + this.$peerDisplayName = this.$el.querySelector('.display-name'); this.$form = this.$el.querySelector('form'); this.$submit = this.$el.querySelector('button[type="submit"]'); this.$form.addEventListener('submit', e => this._onSubmit(e)); @@ -1072,7 +1088,7 @@ class ReceiveTextDialog extends Dialog { Events.on("keydown", e => this._onKeyDown(e)); - this.$receiveTextPeerDisplayNameNode = this.$el.querySelector('#receive-text-peer-display-name'); + this.$displayNameNode = this.$el.querySelector('.display-name'); this._receiveTextQueue = []; } @@ -1102,7 +1118,7 @@ class ReceiveTextDialog extends Dialog { } _showReceiveTextDialog(text, peerId) { - this.$receiveTextPeerDisplayNameNode.innerText = $(peerId).ui._displayName(); + this.$displayNameNode.innerText = $(peerId).ui._displayName(); if (isURL(text)) { const $a = document.createElement('a'); @@ -1198,7 +1214,7 @@ class Base64ZipDialog extends Dialog { } _setPasteBtnToProcessing() { - this.$pasteBtn.pointerEvents = "none"; + this.$pasteBtn.style.pointerEvents = "none"; this.$pasteBtn.innerText = "Processing..."; } diff --git a/public/styles.css b/public/styles.css index d3c05ac..c88d9d5 100644 --- a/public/styles.css +++ b/public/styles.css @@ -564,7 +564,7 @@ x-dialog x-background { z-index: 10; transition: opacity 300ms; will-change: opacity; - padding: 35px; + padding: 15px; overflow: overlay; } @@ -575,19 +575,20 @@ x-dialog x-paper { padding: 16px 24px; width: 100%; max-width: 400px; + overflow: hidden; box-sizing: border-box; transition: transform 300ms; will-change: transform; } #pair-device-dialog x-paper { - position: absolute; - top: max(50%, 350px); - height: 650px; - margin-top: -325px; display: flex; flex-direction: column; - justify-content: space-between; + position: absolute; + top: max(50%, 350px); + margin-top: -328.5px; + width: calc(100vw - 20px); + height: 625px; } x-dialog:not([show]) { @@ -602,12 +603,6 @@ x-dialog:not([show]) x-background { opacity: 0; } -x-dialog .row-reverse>.button { - margin-top: 0; - margin-bottom: -16px; - width: 50%; - height: 50px; -} x-dialog a { color: var(--primary-color); @@ -646,7 +641,7 @@ x-dialog .font-subheading { } #key-input-container>input:nth-of-type(4) { - margin-left: 18px; + margin-left: 5%; } #room-key { @@ -658,16 +653,11 @@ x-dialog .font-subheading { } #room-key-qr-code { - padding: inherit; - margin: auto; - width: 150px; - height: 150px; + margin: 16px; } #pair-device-dialog hr { - margin-top: 40px; - margin-bottom: 40px; - width: 100%; + margin: 40px -24px; } #pair-device-dialog x-background { @@ -681,29 +671,24 @@ x-dialog .row { margin-bottom: 8px; } -x-dialog h2 { - margin-top: 1rem; -} - -#receive-request-dialog h2, -#receive-file-dialog h2 { - margin-bottom: 0.5rem; -} - -x-dialog .row-reverse { - margin: 40px -24px 0; +/* button row*/ +x-paper > div:last-child { + margin: auto -24px -15px; border-top: solid 2.5px var(--border-color); + height: 50px; } -.separator { - border: solid 1.25px var(--border-color); - margin-bottom: -16px; +x-paper > div:last-child > .button { + height: 100%; + width: 50%; +} + +x-paper > div:last-child > .button:not(:last-child) { + border-left: solid 2.5px var(--border-color); } .file-description { - word-break: break-word; - width: 80%; - margin: auto; + margin-bottom: 25px; } .file-description .row { @@ -715,26 +700,26 @@ x-dialog .row-reverse { word-break: normal; } -#file-name { +.file-name { font-style: italic; + max-width: 100%; } -#file-stem { - max-width: 80%; +.file-stem { overflow: hidden; text-overflow: ellipsis; - word-break: break-all; - max-height: 20px; -} - -.file-size{ - margin-bottom: 30px; + white-space: nowrap; } /* Send Text Dialog */ +x-dialog .dialog-subheader { + margin-bottom: 25px; +} + #text-input { - min-height: 120px; + min-height: 200px; + margin: 14px auto; } /* Receive Text Dialog */ @@ -742,14 +727,14 @@ x-dialog .row-reverse { #receive-text-dialog #text { width: 100%; word-break: break-all; - max-height: 300px; + max-height: calc(100vh - 393px); overflow-x: hidden; overflow-y: auto; -webkit-user-select: all; -moz-user-select: all; user-select: all; white-space: pre-wrap; - margin-top:36px; + padding: 15px 0; } #receive-text-dialog #text a { @@ -768,11 +753,7 @@ x-dialog .row-reverse { .row-separator { border-bottom: solid 2.5px var(--border-color); - margin: auto -25px; -} - -#receive-text-description-container { - margin-bottom: 25px; + margin: auto -24px; } #base64-paste-btn { @@ -800,7 +781,6 @@ x-dialog .row-reverse { padding: 2px 16px 0; box-sizing: border-box; min-height: 36px; - min-width: 100px; font-size: 14px; line-height: 24px; font-weight: 700; @@ -811,6 +791,7 @@ x-dialog .row-reverse { user-select: none; background: inherit; color: var(--primary-color); + overflow: hidden; } .button[disabled] { @@ -848,7 +829,7 @@ x-dialog .row-reverse { opacity: 0.1; } -#cancel-paste-mode-btn { +#cancel-paste-mode { z-index: 2; margin: 0; padding: 0; @@ -875,7 +856,6 @@ button::-moz-focus-inner { /* Icon Button */ - .icon-button { width: 40px; height: 40px; @@ -885,10 +865,7 @@ button::-moz-focus-inner { border-radius: 50%; } - - /* Text Input */ - .textarea { box-sizing: border-box; border: none; @@ -902,9 +879,8 @@ button::-moz-focus-inner { display: block; overflow: auto; resize: none; - min-height: 40px; line-height: 16px; - max-height: 300px; + max-height: calc(100vh - 254px); white-space: pre; } @@ -1094,6 +1070,14 @@ x-peers:empty~x-instructions { } /* Responsive Styles */ +@media screen and (max-width: 360px) { + x-dialog x-paper { + padding: 15px; + } + x-paper > div:last-child { + margin: auto -15px -15px; + } +} @media screen and (min-height: 800px) { footer { @@ -1166,7 +1150,9 @@ x-dialog x-paper { display: none; } -.element-preview { +.file-preview > img, +.file-preview > audio, +.file-preview > video { max-width: 100%; max-height: 40vh; margin: auto; diff --git a/public_included_ws_fallback/index.html b/public_included_ws_fallback/index.html index 8227434..7591cf1 100644 --- a/public_included_ws_fallback/index.html +++ b/public_included_ws_fallback/index.html @@ -69,7 +69,7 @@ - +
@@ -109,18 +109,17 @@
Input this key on another device
or scan the QR-Code.

- - - - - - + + + + + +
Enter key from another device to continue.
-
+
-
- Cancel +
@@ -133,9 +132,9 @@

Unpair Devices

Are you sure to unpair all devices?
-
+
- Cancel +
@@ -145,25 +144,23 @@ -

PairDrop

-
+

+
- + would like to share
-
- - +
+ +
-
- +
+
-
-
+
-
@@ -173,13 +170,23 @@ -

-
-
+

+
+
+ + has sent +
+
+ + +
+
+
+
-
- -
+
+ +
@@ -190,16 +197,16 @@ -

PairDrop

-
+

Send Message

+
Send a Message to - +
+
-
+
-
- Cancel +
@@ -209,16 +216,15 @@ -

PairDrop - Message Received

-
- - sent the following message: +

Message Received

+
+ + has sent:
-
+
-
diff --git a/public_included_ws_fallback/scripts/network.js b/public_included_ws_fallback/scripts/network.js index f739465..c5f3b2f 100644 --- a/public_included_ws_fallback/scripts/network.js +++ b/public_included_ws_fallback/scripts/network.js @@ -451,7 +451,7 @@ class Peer { if (!this._requestAccepted.header.length) { this._busy = false; Events.fire('set-progress', {peerId: this._peerId, progress: 0, status: 'process'}); - Events.fire('files-received', {sender: this._peerId, files: this._filesReceived, request: this._requestAccepted}); + Events.fire('files-received', {sender: this._peerId, files: this._filesReceived, imagesOnly: this._requestAccepted.imagesOnly, totalSize: this._requestAccepted.totalSize}); this._filesReceived = []; this._requestAccepted = null; } diff --git a/public_included_ws_fallback/scripts/ui.js b/public_included_ws_fallback/scripts/ui.js index 8fca2ba..0df8e81 100644 --- a/public_included_ws_fallback/scripts/ui.js +++ b/public_included_ws_fallback/scripts/ui.js @@ -28,7 +28,7 @@ class PeersUI { Events.on('activate-paste-mode', e => this._activatePasteMode(e.detail.files, e.detail.text)); this.peers = {}; - this.$cancelPasteModeBtn = $('cancel-paste-mode-btn'); + this.$cancelPasteModeBtn = $('cancel-paste-mode'); this.$cancelPasteModeBtn.addEventListener('click', _ => this._cancelPasteMode()); Events.on('dragover', e => this._onDragOver(e)); @@ -474,10 +474,14 @@ class Dialog { class ReceiveDialog extends Dialog { constructor(id) { super(id); - - this.$fileDescriptionNode = this.$el.querySelector('.file-description'); - this.$fileSizeNode = this.$el.querySelector('.file-size'); - this.$previewBox = this.$el.querySelector('.file-preview') + this.$fileDescription = this.$el.querySelector('.file-description'); + this.$displayName = this.$el.querySelector('.display-name'); + this.$fileStem = this.$el.querySelector('.file-stem'); + this.$fileExtension = this.$el.querySelector('.file-extension'); + this.$fileOther = this.$el.querySelector('.file-other'); + this.$fileSize = this.$el.querySelector('.file-size'); + this.$previewBox = this.$el.querySelector('.file-preview'); + this.$receiveTitle = this.$el.querySelector('h2:first-of-type'); } _formatFileSize(bytes) { @@ -493,6 +497,26 @@ class ReceiveDialog extends Dialog { return bytes + ' Bytes'; } } + + _parseFileData(displayName, files, imagesOnly, totalSize) { + if (files.length > 1) { + let fileOtherText = ` and ${files.length - 1} other `; + if (files.length === 2) { + fileOtherText += imagesOnly ? 'image' : 'file'; + } else { + fileOtherText += imagesOnly ? 'images' : 'files'; + } + this.$fileOther.innerText = fileOtherText; + } + + const fileName = files[0].name; + const fileNameSplit = fileName.split('.'); + const fileExtension = '.' + fileNameSplit[fileNameSplit.length - 1]; + this.$fileStem.innerText = fileName.substring(0, fileName.length - fileExtension.length); + this.$fileExtension.innerText = fileExtension; + this.$displayName.innerText = displayName; + this.$fileSize.innerText = this._formatFileSize(totalSize); + } } class ReceiveFileDialog extends ReceiveDialog { @@ -500,24 +524,25 @@ class ReceiveFileDialog extends ReceiveDialog { constructor() { super('receive-file-dialog'); - this.$shareOrDownloadBtn = this.$el.querySelector('#share-or-download'); - this.$receiveTitleNode = this.$el.querySelector('#receive-title') + this.$downloadBtn = this.$el.querySelector('#download-btn'); + this.$shareBtn = this.$el.querySelector('#share-btn'); - Events.on('files-received', e => this._onFilesReceived(e.detail.sender, e.detail.files, e.detail.request)); + Events.on('files-received', e => this._onFilesReceived(e.detail.sender, e.detail.files, e.detail.imagesOnly, e.detail.totalSize)); this._filesQueue = []; } - _onFilesReceived(sender, files, request) { - this._nextFiles(sender, files, request); + _onFilesReceived(sender, files, imagesOnly, totalSize) { + const displayName = $(sender).ui._displayName() + this._filesQueue.push({peer: sender, displayName: displayName, files: files, imagesOnly: imagesOnly, totalSize: totalSize}); + this._nextFiles(); window.blop.play(); } - _nextFiles(sender, nextFiles, nextRequest) { - if (nextFiles) this._filesQueue.push({peerId: sender, files: nextFiles, request: nextRequest}); + _nextFiles() { if (this._busy) return; this._busy = true; - const {peerId, files, request} = this._filesQueue.shift(); - this._displayFiles(peerId, files, request); + const {peer, displayName, files, imagesOnly, totalSize} = this._filesQueue.shift(); + this._displayFiles(peer, displayName, files, imagesOnly, totalSize); } _dequeueFile() { @@ -548,7 +573,6 @@ class ReceiveFileDialog extends ReceiveDialog { let element = document.createElement(previewElement[mime]); element.src = URL.createObjectURL(file); element.controls = true; - element.classList.add('element-preview'); element.onload = _ => { this.$previewBox.appendChild(element); resolve(true) @@ -559,30 +583,32 @@ class ReceiveFileDialog extends ReceiveDialog { }); } - async _displayFiles(peerId, files, request) { - if (this.continueCallback) this.$shareOrDownloadBtn.removeEventListener("click", this.continueCallback); - - let url; - let title; - let filenameDownload; - - let descriptor = request.imagesOnly ? "Image" : "File"; - - let size = this._formatFileSize(request.totalSize); - let description = files[0].name; - - let shareInsteadOfDownload = (window.iOS || window.android) && !!navigator.share && navigator.canShare({files}); + async _displayFiles(peerId, displayName, files, imagesOnly, totalSize) { + this._parseFileData(displayName, files, imagesOnly, totalSize); + let descriptor, url, filenameDownload; if (files.length === 1) { - url = URL.createObjectURL(files[0]) - title = `PairDrop - ${descriptor} Received` - filenameDownload = files[0].name; + descriptor = imagesOnly ? 'Image' : 'File'; } else { - title = `PairDrop - ${files.length} ${descriptor}s Received` - description += ` and ${files.length-1} other ${descriptor.toLowerCase()}`; - if(files.length>2) description += "s"; + descriptor = imagesOnly ? 'Images' : 'Files'; + } + this.$receiveTitle.innerText = `${descriptor} Received`; - if(!shareInsteadOfDownload) { + const canShare = (window.iOS || window.android) && !!navigator.share && navigator.canShare({files}); + if (canShare) { + this.$shareBtn.removeAttribute('hidden'); + this.$shareBtn.onclick = _ => { + navigator.share({files: files}) + .catch(err => { + console.error(err); + }); + } + } + + let downloadZipped = false; + if (files.length > 1) { + downloadZipped = true; + try { let bytesCompleted = 0; zipper.createNewZipWriter(); for (let i=0; i { Events.fire('set-progress', { peerId: peerId, - progress: (bytesCompleted + progress) / request.totalSize, + progress: (bytesCompleted + progress) / totalSize, status: 'process' }) } @@ -610,49 +636,58 @@ class ReceiveFileDialog extends ReceiveDialog { let minutes = now.getMinutes().toString(); minutes = minutes.length < 2 ? "0" + minutes : minutes; filenameDownload = `PairDrop_files_${year+month+date}_${hours+minutes}.zip`; + } catch (e) { + console.error(e); + downloadZipped = false; } } - this.$receiveTitleNode.textContent = title; - this.$fileDescriptionNode.textContent = description; - this.$fileSizeNode.textContent = size; - - if (shareInsteadOfDownload) { - this.$shareOrDownloadBtn.innerText = "Share"; - this.continue = _ => { - navigator.share({files: files}) - .catch(err => console.error(err)); + this.$downloadBtn.innerText = "Download"; + this.$downloadBtn.onclick = _ => { + if (downloadZipped) { + let tmpZipBtn = document.createElement("a"); + tmpZipBtn.download = filenameDownload; + tmpZipBtn.href = url; + tmpZipBtn.click(); + } else { + this._downloadFilesIndividually(files); } - this.continueCallback = _ => this.continue(); - } else { - this.$shareOrDownloadBtn.innerText = "Download again"; - this.continue = _ => { - let tmpBtn = document.createElement("a"); - tmpBtn.download = filenameDownload; - tmpBtn.href = url; - tmpBtn.click(); - }; - this.continueCallback = _ => { - this.continue(); - this.hide(); - }; - } - this.$shareOrDownloadBtn.addEventListener("click", this.continueCallback); + + if (!canShare) { + this.$downloadBtn.innerText = "Download again"; + } + Events.fire('notify-user', `${descriptor} downloaded successfully`); + this.$downloadBtn.style.pointerEvents = "none"; + setTimeout(_ => this.$downloadBtn.style.pointerEvents = "unset", 2000); + }; this.createPreviewElement(files[0]).finally(_ => { document.title = files.length === 1 ? 'File received - PairDrop' : `(${files.length}) Files received - PairDrop`; document.changeFavicon("images/favicon-96x96-notification.png"); - this.show(); Events.fire('set-progress', {peerId: peerId, progress: 1, status: 'process'}) - this.continue(); + this.show(); + + if (canShare) { + this.$shareBtn.click(); + } else { + this.$downloadBtn.click(); + } }).catch(r => console.error(r)); } + _downloadFilesIndividually(files) { + let tmpBtn = document.createElement("a"); + for (let i=0; i this._respondToFileTransferRequest(true)); @@ -700,32 +730,18 @@ class ReceiveRequestDialog extends ReceiveDialog { _showRequestDialog(request, peerId) { this.correspondingPeerId = peerId; - this.$requestingPeerDisplayNameNode.innerText = $(peerId).ui._displayName(); - - const fileName = request.header[0].name; - const fileNameSplit = fileName.split('.'); - const fileExtension = '.' + fileNameSplit[fileNameSplit.length - 1]; - this.$fileStemNode.innerText = fileName.substring(0, fileName.length - fileExtension.length); - this.$fileExtensionNode.innerText = fileExtension - - if (request.header.length >= 2) { - let fileOtherText = ` and ${request.header.length - 1} other `; - fileOtherText += request.imagesOnly ? 'image' : 'file'; - if (request.header.length > 2) fileOtherText += "s"; - this.$fileOtherNode.innerText = fileOtherText; - } - - this.$fileSizeNode.innerText = this._formatFileSize(request.totalSize); + const displayName = $(peerId).ui._displayName(); + this._parseFileData(displayName, request.header, request.imagesOnly, request.totalSize); if (request.thumbnailDataUrl?.substring(0, 22) === "data:image/jpeg;base64") { let element = document.createElement('img'); element.src = request.thumbnailDataUrl; - element.classList.add('element-preview'); - this.$previewBox.appendChild(element) } - document.title = 'File Transfer Requested - PairDrop'; + this.$receiveTitle.innerText = `${request.imagesOnly ? 'Image' : 'File'} Transfer Request` + + document.title = `${request.imagesOnly ? 'Image' : 'File'} Transfer Requested - PairDrop`; document.changeFavicon("images/favicon-96x96-notification.png"); this.show(); } @@ -1000,7 +1016,7 @@ class SendTextDialog extends Dialog { super('send-text-dialog'); Events.on('text-recipient', e => this._onRecipient(e.detail.peerId, e.detail.deviceName)); this.$text = this.$el.querySelector('#text-input'); - this.$peerDisplayName = this.$el.querySelector('#text-send-peer-display-name'); + this.$peerDisplayName = this.$el.querySelector('.display-name'); this.$form = this.$el.querySelector('form'); this.$submit = this.$el.querySelector('button[type="submit"]'); this.$form.addEventListener('submit', e => this._onSubmit(e)); @@ -1073,7 +1089,7 @@ class ReceiveTextDialog extends Dialog { Events.on("keydown", e => this._onKeyDown(e)); - this.$receiveTextPeerDisplayNameNode = this.$el.querySelector('#receive-text-peer-display-name'); + this.$displayNameNode = this.$el.querySelector('.display-name'); this._receiveTextQueue = []; } @@ -1103,7 +1119,7 @@ class ReceiveTextDialog extends Dialog { } _showReceiveTextDialog(text, peerId) { - this.$receiveTextPeerDisplayNameNode.innerText = $(peerId).ui._displayName(); + this.$displayNameNode.innerText = $(peerId).ui._displayName(); if (isURL(text)) { const $a = document.createElement('a'); @@ -1199,7 +1215,7 @@ class Base64ZipDialog extends Dialog { } _setPasteBtnToProcessing() { - this.$pasteBtn.pointerEvents = "none"; + this.$pasteBtn.style.pointerEvents = "none"; this.$pasteBtn.innerText = "Processing..."; } diff --git a/public_included_ws_fallback/styles.css b/public_included_ws_fallback/styles.css index f153398..0026356 100644 --- a/public_included_ws_fallback/styles.css +++ b/public_included_ws_fallback/styles.css @@ -590,7 +590,7 @@ x-dialog x-background { z-index: 10; transition: opacity 300ms; will-change: opacity; - padding: 35px; + padding: 15px; overflow: overlay; } @@ -601,19 +601,20 @@ x-dialog x-paper { padding: 16px 24px; width: 100%; max-width: 400px; + overflow: hidden; box-sizing: border-box; transition: transform 300ms; will-change: transform; } #pair-device-dialog x-paper { - position: absolute; - top: max(50%, 350px); - height: 650px; - margin-top: -325px; display: flex; flex-direction: column; - justify-content: space-between; + position: absolute; + top: max(50%, 350px); + margin-top: -328.5px; + width: calc(100vw - 20px); + height: 625px; } x-dialog:not([show]) { @@ -628,12 +629,6 @@ x-dialog:not([show]) x-background { opacity: 0; } -x-dialog .row-reverse>.button { - margin-top: 0; - margin-bottom: -16px; - width: 50%; - height: 50px; -} x-dialog a { color: var(--primary-color); @@ -672,7 +667,7 @@ x-dialog .font-subheading { } #key-input-container>input:nth-of-type(4) { - margin-left: 18px; + margin-left: 5%; } #room-key { @@ -684,16 +679,11 @@ x-dialog .font-subheading { } #room-key-qr-code { - padding: inherit; - margin: auto; - width: 150px; - height: 150px; + margin: 16px; } #pair-device-dialog hr { - margin-top: 40px; - margin-bottom: 40px; - width: 100%; + margin: 40px -24px; } #pair-device-dialog x-background { @@ -707,29 +697,24 @@ x-dialog .row { margin-bottom: 8px; } -x-dialog h2 { - margin-top: 1rem; -} - -#receive-request-dialog h2, -#receive-file-dialog h2 { - margin-bottom: 0.5rem; -} - -x-dialog .row-reverse { - margin: 40px -24px 0; +/* button row*/ +x-paper > div:last-child { + margin: auto -24px -15px; border-top: solid 2.5px var(--border-color); + height: 50px; } -.separator { - border: solid 1.25px var(--border-color); - margin-bottom: -16px; +x-paper > div:last-child > .button { + height: 100%; + width: 50%; +} + +x-paper > div:last-child > .button:not(:last-child) { + border-left: solid 2.5px var(--border-color); } .file-description { - word-break: break-word; - width: 80%; - margin: auto; + margin-bottom: 25px; } .file-description .row { @@ -741,26 +726,26 @@ x-dialog .row-reverse { word-break: normal; } -#file-name { +.file-name { font-style: italic; + max-width: 100%; } -#file-stem { - max-width: 80%; +.file-stem { overflow: hidden; text-overflow: ellipsis; - word-break: break-all; - max-height: 20px; -} - -.file-size{ - margin-bottom: 30px; + white-space: nowrap; } /* Send Text Dialog */ +x-dialog .dialog-subheader { + margin-bottom: 25px; +} + #text-input { - min-height: 120px; + min-height: 200px; + margin: 14px auto; } /* Receive Text Dialog */ @@ -768,14 +753,14 @@ x-dialog .row-reverse { #receive-text-dialog #text { width: 100%; word-break: break-all; - max-height: 300px; + max-height: calc(100vh - 393px); overflow-x: hidden; overflow-y: auto; -webkit-user-select: all; -moz-user-select: all; user-select: all; white-space: pre-wrap; - margin-top:36px; + padding: 15px 0; } #receive-text-dialog #text a { @@ -794,11 +779,7 @@ x-dialog .row-reverse { .row-separator { border-bottom: solid 2.5px var(--border-color); - margin: auto -25px; -} - -#receive-text-description-container { - margin-bottom: 25px; + margin: auto -24px; } #base64-paste-btn { @@ -826,7 +807,6 @@ x-dialog .row-reverse { padding: 2px 16px 0; box-sizing: border-box; min-height: 36px; - min-width: 100px; font-size: 14px; line-height: 24px; font-weight: 700; @@ -837,6 +817,7 @@ x-dialog .row-reverse { user-select: none; background: inherit; color: var(--primary-color); + overflow: hidden; } .button[disabled] { @@ -874,7 +855,7 @@ x-dialog .row-reverse { opacity: 0.1; } -#cancel-paste-mode-btn { +#cancel-paste-mode { z-index: 2; margin: 0; padding: 0; @@ -901,7 +882,6 @@ button::-moz-focus-inner { /* Icon Button */ - .icon-button { width: 40px; height: 40px; @@ -911,10 +891,7 @@ button::-moz-focus-inner { border-radius: 50%; } - - /* Text Input */ - .textarea { box-sizing: border-box; border: none; @@ -928,9 +905,8 @@ button::-moz-focus-inner { display: block; overflow: auto; resize: none; - min-height: 40px; line-height: 16px; - max-height: 300px; + max-height: calc(100vh - 254px); white-space: pre; } @@ -1120,6 +1096,14 @@ x-peers:empty~x-instructions { } /* Responsive Styles */ +@media screen and (max-width: 360px) { + x-dialog x-paper { + padding: 15px; + } + x-paper > div:last-child { + margin: auto -15px -15px; + } +} @media screen and (min-height: 800px) { #websocket-fallback { @@ -1192,7 +1176,9 @@ x-dialog x-paper { display: none; } -.element-preview { +.file-preview > img, +.file-preview > audio, +.file-preview > video { max-width: 100%; max-height: 40vh; margin: auto; From d244f5fa475cf0b4ff07926299ac9185f9fb7697 Mon Sep 17 00:00:00 2001 From: schlagmichdoch Date: Fri, 3 Mar 2023 12:03:20 +0100 Subject: [PATCH 05/14] fix circles position on ios safari are shifted by url bar --- public/scripts/ui.js | 4 ++-- public_included_ws_fallback/scripts/ui.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/scripts/ui.js b/public/scripts/ui.js index 4f2c167..c48ac70 100644 --- a/public/scripts/ui.js +++ b/public/scripts/ui.js @@ -1779,8 +1779,8 @@ Events.on('load', () => { let x0, y0, w, h, dw, offset; function init() { - w = window.innerWidth; - h = window.innerHeight; + w = document.documentElement.clientWidth; + h = document.documentElement.clientHeight; c.width = w; c.height = h; offset = $$('footer').offsetHeight - 32; diff --git a/public_included_ws_fallback/scripts/ui.js b/public_included_ws_fallback/scripts/ui.js index 0df8e81..76148c2 100644 --- a/public_included_ws_fallback/scripts/ui.js +++ b/public_included_ws_fallback/scripts/ui.js @@ -1780,8 +1780,8 @@ Events.on('load', () => { let x0, y0, w, h, dw, offset; function init() { - w = window.innerWidth; - h = window.innerHeight; + w = document.documentElement.clientWidth; + h = document.documentElement.clientHeight; c.width = w; c.height = h; offset = $$('footer').offsetHeight - 32; From f9f1abef7a570c1967df151478008897ed9edf00 Mon Sep 17 00:00:00 2001 From: schlagmichdoch Date: Fri, 3 Mar 2023 12:28:50 +0100 Subject: [PATCH 06/14] Replace all urls in received messages with links. Center the message if it does not include any whitespace. --- public/scripts/ui.js | 26 +++++++++++++---------- public_included_ws_fallback/scripts/ui.js | 26 +++++++++++++---------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/public/scripts/ui.js b/public/scripts/ui.js index c48ac70..1eff336 100644 --- a/public/scripts/ui.js +++ b/public/scripts/ui.js @@ -1,6 +1,5 @@ const $ = query => document.getElementById(query); const $$ = query => document.body.querySelector(query); -const isURL = text => /^(https?:\/\/|www)[^\s]+$/g.test(text.toLowerCase()); window.isProductionEnvironment = !window.location.host.startsWith('localhost'); window.iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; window.android = /android/i.test(navigator.userAgent); @@ -1120,16 +1119,21 @@ class ReceiveTextDialog extends Dialog { _showReceiveTextDialog(text, peerId) { this.$displayNameNode.innerText = $(peerId).ui._displayName(); - if (isURL(text)) { - const $a = document.createElement('a'); - $a.href = text; - $a.target = '_blank'; - $a.textContent = text; - this.$text.innerHTML = ''; - this.$text.appendChild($a); - } else { - this.$text.textContent = text; + this.$text.innerText = text; + this.$text.classList.remove('text-center'); + + // Beautify text if text is short + if (text.length < 2000) { + // replace urls with actual links + this.$text.innerHTML = this.$text.innerHTML.replace(/((https?:\/\/|www)[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\-._~:\/?#\[\]@!$&'()*+,;=]+)/g, url => { + return `${url}`; + }); + + if (!/\s/.test(text)) { + this.$text.classList.add('text-center'); + } } + this._setDocumentTitleMessages(); document.changeFavicon("images/favicon-96x96-notification.png"); @@ -1359,7 +1363,7 @@ class Notifications { _messageNotification(message, peerId) { if (document.visibilityState !== 'visible') { const peerDisplayName = $(peerId).ui._displayName(); - if (isURL(message)) { + if (/^((https?:\/\/|www)[abcdefghijklmnopqrstuvwxyz0123456789\-._~:\/?#\[\]@!$&'()*+,;=]+)$/.test(message.toLowerCase())) { const notification = this._notify(`Link received by ${peerDisplayName} - Click to open`, message); this._bind(notification, _ => window.open(message, '_blank', null, true)); } else { diff --git a/public_included_ws_fallback/scripts/ui.js b/public_included_ws_fallback/scripts/ui.js index 76148c2..3270f2d 100644 --- a/public_included_ws_fallback/scripts/ui.js +++ b/public_included_ws_fallback/scripts/ui.js @@ -1,6 +1,5 @@ const $ = query => document.getElementById(query); const $$ = query => document.body.querySelector(query); -const isURL = text => /^(https?:\/\/|www)[^\s]+$/g.test(text.toLowerCase()); window.isProductionEnvironment = !window.location.host.startsWith('localhost'); window.iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; window.android = /android/i.test(navigator.userAgent); @@ -1121,16 +1120,21 @@ class ReceiveTextDialog extends Dialog { _showReceiveTextDialog(text, peerId) { this.$displayNameNode.innerText = $(peerId).ui._displayName(); - if (isURL(text)) { - const $a = document.createElement('a'); - $a.href = text; - $a.target = '_blank'; - $a.textContent = text; - this.$text.innerHTML = ''; - this.$text.appendChild($a); - } else { - this.$text.textContent = text; + this.$text.innerText = text; + this.$text.classList.remove('text-center'); + + // Beautify text if text is short + if (text.length < 2000) { + // replace urls with actual links + this.$text.innerHTML = this.$text.innerHTML.replace(/((https?:\/\/|www)[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\-._~:\/?#\[\]@!$&'()*+,;=]+)/g, url => { + return `${url}`; + }); + + if (!/\s/.test(text)) { + this.$text.classList.add('text-center'); + } } + this._setDocumentTitleMessages(); document.changeFavicon("images/favicon-96x96-notification.png"); @@ -1360,7 +1364,7 @@ class Notifications { _messageNotification(message, peerId) { if (document.visibilityState !== 'visible') { const peerDisplayName = $(peerId).ui._displayName(); - if (isURL(message)) { + if (/^((https?:\/\/|www)[abcdefghijklmnopqrstuvwxyz0123456789\-._~:\/?#\[\]@!$&'()*+,;=]+)$/.test(message.toLowerCase())) { const notification = this._notify(`Link received by ${peerDisplayName} - Click to open`, message); this._bind(notification, _ => window.open(message, '_blank', null, true)); } else { From d6ef5887ddf616ce69980b6ac256cf32ef294202 Mon Sep 17 00:00:00 2001 From: schlagmichdoch Date: Fri, 3 Mar 2023 12:38:34 +0100 Subject: [PATCH 07/14] move logging of rtc message from class Peer class to overwritten method in class RTCPeer --- public/scripts/network.js | 9 ++++++++- public_included_ws_fallback/scripts/network.js | 13 ++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/public/scripts/network.js b/public/scripts/network.js index e2ad3cc..a905331 100644 --- a/public/scripts/network.js +++ b/public/scripts/network.js @@ -320,7 +320,6 @@ class Peer { return; } message = JSON.parse(message); - console.log('RTC:', message); switch (message.type) { case 'request': this._onFilesTransferRequest(message); @@ -568,6 +567,14 @@ class RTCPeer extends Peer { this._channel = channel; } + _onMessage(message) { + if (typeof message === 'string') { + message = JSON.parse(message); + console.log('RTC:', message); + } + super._onMessage(message); + } + getConnectionHash() { const localDescriptionLines = this._conn.localDescription.sdp.split("\r\n"); const remoteDescriptionLines = this._conn.remoteDescription.sdp.split("\r\n"); diff --git a/public_included_ws_fallback/scripts/network.js b/public_included_ws_fallback/scripts/network.js index c5f3b2f..1397a1a 100644 --- a/public_included_ws_fallback/scripts/network.js +++ b/public_included_ws_fallback/scripts/network.js @@ -324,13 +324,12 @@ class Peer { this.sendJSON({ type: 'progress', progress: progress }); } - _onMessage(message, logMessage = true) { + _onMessage(message) { if (typeof message !== 'string') { this._onChunkReceived(message); return; } message = JSON.parse(message); - if (logMessage) console.log('RTC:', message); switch (message.type) { case 'request': this._onFilesTransferRequest(message); @@ -578,6 +577,14 @@ class RTCPeer extends Peer { this._channel = channel; } + _onMessage(message) { + if (typeof message === 'string') { + message = JSON.parse(message); + console.log('RTC:', message); + } + super._onMessage(message); + } + getConnectionHash() { const localDescriptionLines = this._conn.localDescription.sdp.split("\r\n"); const remoteDescriptionLines = this._conn.remoteDescription.sdp.split("\r\n"); @@ -749,7 +756,7 @@ class PeersManager { _onWsRelay(message) { const messageJSON = JSON.parse(message) if (messageJSON.type === 'ws-chunk') message = base64ToArrayBuffer(messageJSON.chunk); - this.peers[messageJSON.sender.id]._onMessage(message, false) + this.peers[messageJSON.sender.id]._onMessage(message) } _onPeers(msg) { From bbb8c1b10f3a79fbfe5f07cdb4f8186eafa2f854 Mon Sep 17 00:00:00 2001 From: schlagmichdoch Date: Fri, 3 Mar 2023 12:40:41 +0100 Subject: [PATCH 08/14] ws-fallback: prevent signaling from stopping on reconnect. Do not stop to signal until both devices have sent event "peer-connected" --- public_included_ws_fallback/scripts/network.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public_included_ws_fallback/scripts/network.js b/public_included_ws_fallback/scripts/network.js index 1397a1a..e65f270 100644 --- a/public_included_ws_fallback/scripts/network.js +++ b/public_included_ws_fallback/scripts/network.js @@ -707,15 +707,15 @@ class WSPeer extends Peer { this._server.send(message); } - _sendSignal() { - this.sendJSON({type: 'signal'}); + _sendSignal(connected = false) { + this.sendJSON({type: 'signal', connected: connected}); } onServerMessage(message) { Events.fire('peer-connected', {peerId: message.sender.id, connectionHash: this.getConnectionHash()}) - if (this._peerId) return; + if (message.connected) return; this._peerId = message.sender.id; - this._sendSignal(); + this._sendSignal(true); } getConnectionHash() { From cf715b28729388a1b4af9f4400ef01804d663699 Mon Sep 17 00:00:00 2001 From: schlagmichdoch Date: Fri, 3 Mar 2023 12:54:21 +0100 Subject: [PATCH 09/14] stability on reconnect: prevent "peer-left" signal after "peer-joined" by leaving rooms first before reentering them, clear _keepAlive timeout before joining ip room and not manually terminating sockets --- index.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index 31fbca9..824bc13 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ const process = require('process') const crypto = require('crypto') const {spawn} = require('child_process') +const WebSocket = require('ws'); // Handle SIGINT process.on('SIGINT', () => { @@ -99,7 +100,6 @@ const { uniqueNamesGenerator, animals, colors } = require('unique-names-generato class PairDropServer { constructor() { - const WebSocket = require('ws'); this._wss = new WebSocket.Server({ server }); this._wss.on('connection', (socket, request) => this._onConnection(new Peer(socket, request))); @@ -110,10 +110,10 @@ class PairDropServer { } _onConnection(peer) { - this._joinRoom(peer); peer.socket.on('message', message => this._onMessage(peer, message)); peer.socket.onerror = e => console.error(e); this._keepAlive(peer); + this._joinRoom(peer); // send displayName this._send(peer, { @@ -317,6 +317,10 @@ class PairDropServer { _joinRoom(peer, roomType = 'ip', roomSecret = '') { const room = roomType === 'ip' ? peer.ip : roomSecret; + if (this._rooms[room] && this._rooms[room][peer.id]) { + this._leaveRoom(peer, roomType, roomSecret); + } + // if room doesn't exist, create it if (!this._rooms[room]) { this._rooms[room] = {}; @@ -341,10 +345,6 @@ class PairDropServer { // delete the peer delete this._rooms[room][peer.id]; - if (roomType === 'ip') { - peer.socket.terminate(); - } - //if room is empty, delete the room if (!Object.keys(this._rooms[room]).length) { delete this._rooms[room]; From 39ca5b2d21760cd5b4468a8a3df4ad568551188f Mon Sep 17 00:00:00 2001 From: schlagmichdoch Date: Fri, 3 Mar 2023 13:09:59 +0100 Subject: [PATCH 10/14] ws-fallback: remove all WSPeers when server connection disconnects + fix onPeerLeft --- public/scripts/network.js | 1 + public_included_ws_fallback/scripts/network.js | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/public/scripts/network.js b/public/scripts/network.js index a905331..998290e 100644 --- a/public/scripts/network.js +++ b/public/scripts/network.js @@ -491,6 +491,7 @@ class RTCPeer extends Peer { constructor(serverConnection, peerId, roomType, roomSecret) { super(serverConnection, peerId, roomType, roomSecret); + this.rtcSupported = true; if (!peerId) return; // we will listen for a caller this._connect(peerId, true); } diff --git a/public_included_ws_fallback/scripts/network.js b/public_included_ws_fallback/scripts/network.js index e65f270..b606c7a 100644 --- a/public_included_ws_fallback/scripts/network.js +++ b/public_included_ws_fallback/scripts/network.js @@ -501,6 +501,7 @@ class RTCPeer extends Peer { constructor(serverConnection, peerId, roomType, roomSecret) { super(serverConnection, peerId, roomType, roomSecret); + this.rtcSupported = true; if (!peerId) return; // we will listen for a caller this._connect(peerId, true); } @@ -689,6 +690,7 @@ class WSPeer extends Peer { constructor(serverConnection, peerId, roomType, roomSecret) { super(serverConnection, peerId, roomType, roomSecret); + this.rtcSupported = false; if (!peerId) return; // we will listen for a caller this._sendSignal(); } @@ -737,6 +739,7 @@ class PeersManager { Events.on('peer-left', e => this._onPeerLeft(e.detail)); Events.on('peer-disconnected', e => this._onPeerDisconnected(e.detail)); Events.on('secret-room-deleted', e => this._onSecretRoomDeleted(e.detail)); + Events.on('ws-disconnected', _ => this._onWsDisconnected()); Events.on('ws-relay', e => this._onWsRelay(e.detail)); } @@ -804,15 +807,24 @@ class PeersManager { } _onPeerLeft(msg) { - if (this.peers[msg.peerId] && !this.peers[msg.peerId].rtcSupported) { - console.log('WSPeer left:', msg.peerId) - Events.fire('peer-disconnected', msg.peerId) + if (this.peers[msg.peerId] && (!this.peers[msg.peerId].rtcSupported || !window.isRtcSupported)) { + console.log('WSPeer left:', msg.peerId); + Events.fire('peer-disconnected', msg.peerId); } else if (msg.disconnect === true) { // if user actively disconnected from PairDrop server, disconnect all peer to peer connections immediately Events.fire('peer-disconnected', msg.peerId); } } + _onWsDisconnected() { + for (const peerId in this.peers) { + console.debug(this.peers[peerId].rtcSupported); + if (this.peers[peerId] && (!this.peers[peerId].rtcSupported || !window.isRtcSupported)) { + Events.fire('peer-disconnected', peerId); + } + } + } + _onPeerDisconnected(peerId) { const peer = this.peers[peerId]; delete this.peers[peerId]; From a9d7960a59044e435e86b3466fc6a2ecbed33f61 Mon Sep 17 00:00:00 2001 From: schlagmichdoch Date: Fri, 3 Mar 2023 13:12:06 +0100 Subject: [PATCH 11/14] increase version to v1.2.1 --- package-lock.json | 4 ++-- package.json | 2 +- public/service-worker.js | 2 +- public_included_ws_fallback/service-worker.js | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6e94449..33f7c1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pairdrop", - "version": "1.2.0", + "version": "1.2.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "pairdrop", - "version": "1.2.0", + "version": "1.2.1", "license": "ISC", "dependencies": { "express": "^4.18.2", diff --git a/package.json b/package.json index e5c00ab..140d658 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pairdrop", - "version": "1.2.0", + "version": "1.2.1", "description": "", "main": "index.js", "scripts": { diff --git a/public/service-worker.js b/public/service-worker.js index d8a145a..ce7ff2e 100644 --- a/public/service-worker.js +++ b/public/service-worker.js @@ -1,4 +1,4 @@ -const cacheVersion = 'v1.2.0'; +const cacheVersion = 'v1.2.1'; const cacheTitle = `pairdrop-cache-${cacheVersion}`; const urlsToCache = [ 'index.html', diff --git a/public_included_ws_fallback/service-worker.js b/public_included_ws_fallback/service-worker.js index 1fb70bc..d3d8e04 100644 --- a/public_included_ws_fallback/service-worker.js +++ b/public_included_ws_fallback/service-worker.js @@ -1,4 +1,4 @@ -const cacheVersion = 'v1.2.0'; +const cacheVersion = 'v1.2.1'; const cacheTitle = `pairdrop-included-ws-fallback-cache-${cacheVersion}`; const urlsToCache = [ 'index.html', From bfb5aa8546023e185b2093186239ca51f8893507 Mon Sep 17 00:00:00 2001 From: schlagmichdoch Date: Fri, 3 Mar 2023 16:36:55 +0100 Subject: [PATCH 12/14] fix overwrite method _onMessage of class RTCPeer --- public/scripts/network.js | 19 +++++++++---------- .../scripts/network.js | 19 +++++++++---------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/public/scripts/network.js b/public/scripts/network.js index 998290e..8e27f21 100644 --- a/public/scripts/network.js +++ b/public/scripts/network.js @@ -319,25 +319,25 @@ class Peer { this._onChunkReceived(message); return; } - message = JSON.parse(message); - switch (message.type) { + const messageJSON = JSON.parse(message); + switch (messageJSON.type) { case 'request': - this._onFilesTransferRequest(message); + this._onFilesTransferRequest(messageJSON); break; case 'header': - this._onFilesHeader(message); + this._onFilesHeader(messageJSON); break; case 'partition': - this._onReceivedPartitionEnd(message); + this._onReceivedPartitionEnd(messageJSON); break; case 'partition-received': this._sendNextPartition(); break; case 'progress': - this._onDownloadProgress(message.progress); + this._onDownloadProgress(messageJSON.progress); break; case 'files-transfer-response': - this._onFileTransferRequestResponded(message); + this._onFileTransferRequestResponded(messageJSON); break; case 'file-transfer-complete': this._onFileTransferCompleted(); @@ -346,7 +346,7 @@ class Peer { this._onMessageTransferCompleted(); break; case 'text': - this._onTextReceived(message); + this._onTextReceived(messageJSON); break; } } @@ -570,8 +570,7 @@ class RTCPeer extends Peer { _onMessage(message) { if (typeof message === 'string') { - message = JSON.parse(message); - console.log('RTC:', message); + console.log('RTC:', JSON.parse(message)); } super._onMessage(message); } diff --git a/public_included_ws_fallback/scripts/network.js b/public_included_ws_fallback/scripts/network.js index b606c7a..bcd7df6 100644 --- a/public_included_ws_fallback/scripts/network.js +++ b/public_included_ws_fallback/scripts/network.js @@ -329,25 +329,25 @@ class Peer { this._onChunkReceived(message); return; } - message = JSON.parse(message); - switch (message.type) { + const messageJSON = JSON.parse(message); + switch (messageJSON.type) { case 'request': - this._onFilesTransferRequest(message); + this._onFilesTransferRequest(messageJSON); break; case 'header': - this._onFilesHeader(message); + this._onFilesHeader(messageJSON); break; case 'partition': - this._onReceivedPartitionEnd(message); + this._onReceivedPartitionEnd(messageJSON); break; case 'partition-received': this._sendNextPartition(); break; case 'progress': - this._onDownloadProgress(message.progress); + this._onDownloadProgress(messageJSON.progress); break; case 'files-transfer-response': - this._onFileTransferRequestResponded(message); + this._onFileTransferRequestResponded(messageJSON); break; case 'file-transfer-complete': this._onFileTransferCompleted(); @@ -356,7 +356,7 @@ class Peer { this._onMessageTransferCompleted(); break; case 'text': - this._onTextReceived(message); + this._onTextReceived(messageJSON); break; } } @@ -580,8 +580,7 @@ class RTCPeer extends Peer { _onMessage(message) { if (typeof message === 'string') { - message = JSON.parse(message); - console.log('RTC:', message); + console.log('RTC:', JSON.parse(message)); } super._onMessage(message); } From bb0493d0717148d0bde0299599ace6ad2e6c2175 Mon Sep 17 00:00:00 2001 From: schlagmichdoch Date: Fri, 3 Mar 2023 17:03:10 +0100 Subject: [PATCH 13/14] Make user notifications and document titles more concise. --- public/scripts/network.js | 5 ++++- public/scripts/ui.js | 17 ++++++++--------- public_included_ws_fallback/scripts/network.js | 5 ++++- public_included_ws_fallback/scripts/ui.js | 17 ++++++++--------- 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/public/scripts/network.js b/public/scripts/network.js index 8e27f21..41916a8 100644 --- a/public/scripts/network.js +++ b/public/scripts/network.js @@ -36,6 +36,7 @@ class ServerConnection { _onOpen() { console.log('WS: server connected'); Events.fire('ws-connected'); + if (this._isReconnect) Events.fire('notify-user', 'Connected.'); } _sendRoomSecrets(roomSecrets) { @@ -145,15 +146,17 @@ class ServerConnection { this._socket.close(); this._socket = null; Events.fire('ws-disconnected'); + this._isReconnect = true; } } _onDisconnect() { console.log('WS: server disconnected'); - Events.fire('notify-user', 'No server connection. Retry in 5s...'); + Events.fire('notify-user', 'Connecting..'); clearTimeout(this._reconnectTimer); this._reconnectTimer = setTimeout(_ => this._connect(), 5000); Events.fire('ws-disconnected'); + this._isReconnect = true; } _onVisibilityChange() { diff --git a/public/scripts/ui.js b/public/scripts/ui.js index 1eff336..cc0a476 100644 --- a/public/scripts/ui.js +++ b/public/scripts/ui.js @@ -662,7 +662,7 @@ class ReceiveFileDialog extends ReceiveDialog { this.createPreviewElement(files[0]).finally(_ => { document.title = files.length === 1 ? 'File received - PairDrop' - : `(${files.length}) Files received - PairDrop`; + : `${files.length} Files received - PairDrop`; document.changeFavicon("images/favicon-96x96-notification.png"); Events.fire('set-progress', {peerId: peerId, progress: 1, status: 'process'}) this.show(); @@ -1143,7 +1143,7 @@ class ReceiveTextDialog extends Dialog { _setDocumentTitleMessages() { document.title = !this._receiveTextQueue.length ? 'Message Received - PairDrop' - : `(${this._receiveTextQueue.length + 1}) Messages Received - PairDrop`; + : `${this._receiveTextQueue.length + 1} Messages Received - PairDrop`; } async _onCopy() { @@ -1424,7 +1424,7 @@ class NetworkStatusUI { constructor() { Events.on('offline', _ => this._showOfflineMessage()); Events.on('online', _ => this._showOnlineMessage()); - Events.on('ws-connected', _ => this._showOnlineMessage()); + Events.on('ws-connected', _ => this._onWsConnected()); Events.on('ws-disconnected', _ => this._onWsDisconnected()); if (!navigator.onLine) this._showOfflineMessage(); } @@ -1435,17 +1435,16 @@ class NetworkStatusUI { } _showOnlineMessage() { - window.animateBackground(true); - if (!this.firstConnect) { - this.firstConnect = true; - return; - } Events.fire('notify-user', 'You are back online'); + window.animateBackground(true); + } + + _onWsConnected() { + window.animateBackground(true); } _onWsDisconnected() { window.animateBackground(false); - if (!this.firstConnect) this.firstConnect = true; } } diff --git a/public_included_ws_fallback/scripts/network.js b/public_included_ws_fallback/scripts/network.js index bcd7df6..2e0f1ef 100644 --- a/public_included_ws_fallback/scripts/network.js +++ b/public_included_ws_fallback/scripts/network.js @@ -34,6 +34,7 @@ class ServerConnection { _onOpen() { console.log('WS: server connected'); Events.fire('ws-connected'); + if (this._isReconnect) Events.fire('notify-user', 'Connected.'); } _sendRoomSecrets(roomSecrets) { @@ -155,15 +156,17 @@ class ServerConnection { this._socket.close(); this._socket = null; Events.fire('ws-disconnected'); + this._isReconnect = true; } } _onDisconnect() { console.log('WS: server disconnected'); - Events.fire('notify-user', 'No server connection. Retry in 5s...'); + Events.fire('notify-user', 'Connecting..'); clearTimeout(this._reconnectTimer); this._reconnectTimer = setTimeout(_ => this._connect(), 5000); Events.fire('ws-disconnected'); + this._isReconnect = true; } _onVisibilityChange() { diff --git a/public_included_ws_fallback/scripts/ui.js b/public_included_ws_fallback/scripts/ui.js index 3270f2d..82b0eab 100644 --- a/public_included_ws_fallback/scripts/ui.js +++ b/public_included_ws_fallback/scripts/ui.js @@ -663,7 +663,7 @@ class ReceiveFileDialog extends ReceiveDialog { this.createPreviewElement(files[0]).finally(_ => { document.title = files.length === 1 ? 'File received - PairDrop' - : `(${files.length}) Files received - PairDrop`; + : `${files.length} Files received - PairDrop`; document.changeFavicon("images/favicon-96x96-notification.png"); Events.fire('set-progress', {peerId: peerId, progress: 1, status: 'process'}) this.show(); @@ -1144,7 +1144,7 @@ class ReceiveTextDialog extends Dialog { _setDocumentTitleMessages() { document.title = !this._receiveTextQueue.length ? 'Message Received - PairDrop' - : `(${this._receiveTextQueue.length + 1}) Messages Received - PairDrop`; + : `${this._receiveTextQueue.length + 1} Messages Received - PairDrop`; } async _onCopy() { @@ -1425,7 +1425,7 @@ class NetworkStatusUI { constructor() { Events.on('offline', _ => this._showOfflineMessage()); Events.on('online', _ => this._showOnlineMessage()); - Events.on('ws-connected', _ => this._showOnlineMessage()); + Events.on('ws-connected', _ => this._onWsConnected()); Events.on('ws-disconnected', _ => this._onWsDisconnected()); if (!navigator.onLine) this._showOfflineMessage(); } @@ -1436,17 +1436,16 @@ class NetworkStatusUI { } _showOnlineMessage() { - window.animateBackground(true); - if (!this.firstConnect) { - this.firstConnect = true; - return; - } Events.fire('notify-user', 'You are back online'); + window.animateBackground(true); + } + + _onWsConnected() { + window.animateBackground(true); } _onWsDisconnected() { window.animateBackground(false); - if (!this.firstConnect) this.firstConnect = true; } } From 1e35bab327417fb6fccf1d799cbcd43ffbe97a77 Mon Sep 17 00:00:00 2001 From: schlagmichdoch Date: Fri, 3 Mar 2023 17:07:02 +0100 Subject: [PATCH 14/14] increase version to v1.2.2 --- package-lock.json | 4 ++-- package.json | 2 +- public/service-worker.js | 2 +- public_included_ws_fallback/service-worker.js | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 33f7c1d..c5060cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pairdrop", - "version": "1.2.1", + "version": "1.2.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "pairdrop", - "version": "1.2.1", + "version": "1.2.2", "license": "ISC", "dependencies": { "express": "^4.18.2", diff --git a/package.json b/package.json index 140d658..d2baecf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pairdrop", - "version": "1.2.1", + "version": "1.2.2", "description": "", "main": "index.js", "scripts": { diff --git a/public/service-worker.js b/public/service-worker.js index ce7ff2e..b76115f 100644 --- a/public/service-worker.js +++ b/public/service-worker.js @@ -1,4 +1,4 @@ -const cacheVersion = 'v1.2.1'; +const cacheVersion = 'v1.2.2'; const cacheTitle = `pairdrop-cache-${cacheVersion}`; const urlsToCache = [ 'index.html', diff --git a/public_included_ws_fallback/service-worker.js b/public_included_ws_fallback/service-worker.js index d3d8e04..c384efd 100644 --- a/public_included_ws_fallback/service-worker.js +++ b/public_included_ws_fallback/service-worker.js @@ -1,4 +1,4 @@ -const cacheVersion = 'v1.2.1'; +const cacheVersion = 'v1.2.2'; const cacheTitle = `pairdrop-included-ws-fallback-cache-${cacheVersion}`; const urlsToCache = [ 'index.html',