From 392a1b88583f3658fb4780e4b2a02faee698e095 Mon Sep 17 00:00:00 2001 From: Victor Wads Date: Fri, 3 Jun 2022 00:39:27 -0300 Subject: [PATCH 01/16] Add events "enum" --- client/scripts/network.js | 72 ++++++++++++++++++++++++++------------- client/scripts/ui.js | 48 +++++++++++++------------- 2 files changed, 73 insertions(+), 47 deletions(-) diff --git a/client/scripts/network.js b/client/scripts/network.js index eb599014..cd5c5e4a 100644 --- a/client/scripts/network.js +++ b/client/scripts/network.js @@ -26,23 +26,23 @@ class ServerConnection { msg = JSON.parse(msg); console.log('WS:', msg); switch (msg.type) { - case 'peers': - Events.fire('peers', msg.peers); + case Events.PEERS: + Events.fire(Events.PEERS, msg.peers); break; - case 'peer-joined': - Events.fire('peer-joined', msg.peer); + case Events.PEER_JOINED: + Events.fire(Events.PEER_JOINED, msg.peer); break; - case 'peer-left': - Events.fire('peer-left', msg.peerId); + case Events.PEER_LEFT: + Events.fire(Events.PEER_LEFT, msg.peerId); break; - case 'signal': - Events.fire('signal', msg); + case Events.SIGNAL: + Events.fire(Events.SIGNAL, msg); break; - case 'ping': - this.send({ type: 'pong' }); + case Events.DISPLAY_NAME: + Events.fire(Events.DISPLAY_NAME, msg); break; - case 'display-name': - Events.fire('display-name', msg); + case Events.PING: + this.send({ type: Events.PONG }); break; default: console.error('WS: unkown message type', msg); @@ -70,7 +70,7 @@ class ServerConnection { _onDisconnect() { console.log('WS: server disconnected'); - Events.fire('notify-user', 'Connection lost. Retry in 5 seconds...'); + Events.fire(Events.NOTIFY_USER, 'Connection lost. Retry in 5 seconds...'); clearTimeout(this._reconnectTimer); this._reconnectTimer = setTimeout(_ => this._connect(), 5000); } @@ -199,11 +199,11 @@ class Peer { } _onDownloadProgress(progress) { - Events.fire('file-progress', { sender: this._peerId, progress: progress }); + Events.fire(Events.FILE_PROGRESS, { sender: this._peerId, progress: progress }); } _onFileReceived(proxyFile) { - Events.fire('file-received', proxyFile); + Events.fire(Events.FILE_RECEIVED, proxyFile); this.sendJSON({ type: 'transfer-complete' }); } @@ -212,7 +212,7 @@ class Peer { this._reader = null; this._busy = false; this._dequeueFile(); - Events.fire('notify-user', 'File transfer completed.'); + Events.fire(Events.NOTIFY_USER, 'File transfer completed.'); } sendText(text) { @@ -222,7 +222,7 @@ class Peer { _onTextReceived(message) { const escaped = decodeURIComponent(escape(atob(message.text))); - Events.fire('text-received', { text: escaped, sender: this._peerId }); + Events.fire(Events.TEXT_RECEIVED, { text: escaped, sender: this._peerId }); } } @@ -364,11 +364,11 @@ class PeersManager { constructor(serverConnection) { this.peers = {}; this._server = serverConnection; - Events.on('signal', e => this._onMessage(e.detail)); - Events.on('peers', e => this._onPeers(e.detail)); - Events.on('files-selected', e => this._onFilesSelected(e.detail)); - Events.on('send-text', e => this._onSendText(e.detail)); - Events.on('peer-left', e => this._onPeerLeft(e.detail)); + Events.on(Events.SIGNAL, e => this._onMessage(e.detail)); + Events.on(Events.PEERS, e => this._onPeers(e.detail)); + Events.on(Events.FILES_SELECTED, e => this._onFilesSelected(e.detail)); + Events.on(Events.SEND_TEXT, e => this._onSendText(e.detail)); + Events.on(Events.PEER_LEFT, e => this._onPeerLeft(e.detail)); } _onMessage(message) { @@ -431,7 +431,7 @@ class FileChunker { this._onChunk = onChunk; this._onPartitionEnd = onPartitionEnd; this._reader = new FileReader(); - this._reader.addEventListener('load', e => this._onChunkRead(e.target.result)); + this._reader.addEventListener(Events.LOAD, e => this._onChunkRead(e.target.result)); } nextPartition() { @@ -506,12 +506,38 @@ class FileDigester { class Events { static fire(type, detail) { + console.warn('Events.fire: ', type) window.dispatchEvent(new CustomEvent(type, { detail: detail })); } static on(type, callback) { + console.warn('Events.on: ', type) return window.addEventListener(type, callback, false); } + + static LOAD = 'load' + + static PEERS = 'peers' + static PEER_JOINED = 'peer-joined' + static PEER_LEFT = 'peer-left' + static PING = 'ping' + static PONG = 'pong' + + static SIGNAL = 'signal' + static DISPLAY_NAME = 'display-name' + static NOTIFY_USER = 'notify-user' + + static FILES_SELECTED = 'files-selected' + static FILE_PROGRESS = 'file-progress' + static FILE_RECEIVED = 'file-received' + static FILE_REQUEST = 'file-request' + static FILE_ACCEPT = 'file-accept' + static FILE_DENY = 'file-deny' + + static PASTE = 'paste' + static SEND_TEXT = 'send-text' + static TEXT_RECEIVED = 'text-received' + static TEXT_RECIPIENT = 'text-recipient' } diff --git a/client/scripts/ui.js b/client/scripts/ui.js index 0c159841..f69e9f84 100644 --- a/client/scripts/ui.js +++ b/client/scripts/ui.js @@ -6,7 +6,7 @@ window.isProductionEnvironment = !window.location.host.startsWith('localhost'); window.iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; // set display name -Events.on('display-name', e => { +Events.on(Events.DISPLAY_NAME, e => { const me = e.detail.message; const $displayName = $('displayName') $displayName.textContent = 'You are known as ' + me.displayName; @@ -16,11 +16,11 @@ Events.on('display-name', e => { class PeersUI { constructor() { - Events.on('peer-joined', e => this._onPeerJoined(e.detail)); - Events.on('peer-left', e => this._onPeerLeft(e.detail)); - Events.on('peers', e => this._onPeers(e.detail)); - Events.on('file-progress', e => this._onFileProgress(e.detail)); - Events.on('paste', e => this._onPaste(e)); + Events.on(Events.PEER_JOINED, e => this._onPeerJoined(e.detail)); + Events.on(Events.PEER_LEFT, e => this._onPeerLeft(e.detail)); + Events.on(Events.PEERS, e => this._onPeers(e.detail)); + Events.on(Events.FILE_PROGRESS, e => this._onFileProgress(e.detail)); + Events.on(Events.PASTE, e => this._onPaste(e)); } _onPeerJoined(peer) { @@ -62,7 +62,7 @@ class PeersUI { // "image data has been pasted, click the client to which to send it" // not implemented if (files.length > 0 && peers.length === 1) { - Events.fire('files-selected', { + Events.fire(Events.FILES_SELECTED, { files: files, to: $$('x-peer').id }); @@ -143,7 +143,7 @@ class PeerUI { _onFilesSelected(e) { const $input = e.target; const files = $input.files; - Events.fire('files-selected', { + Events.fire(Events.FILES_SELECTED, { files: files, to: this._peer.id }); @@ -170,7 +170,7 @@ class PeerUI { _onDrop(e) { e.preventDefault(); const files = e.dataTransfer.files; - Events.fire('files-selected', { + Events.fire(Events.FILES_SELECTED, { files: files, to: this._peer.id }); @@ -187,7 +187,7 @@ class PeerUI { _onRightClick(e) { e.preventDefault(); - Events.fire('text-recipient', this._peer.id); + Events.fire(Events.TEXT_RECIPIENT, this._peer.id); } _onTouchStart(e) { @@ -200,7 +200,7 @@ class PeerUI { clearTimeout(this._touchTimer); } else { // this was a long tap if (e) e.preventDefault(); - Events.fire('text-recipient', this._peer.id); + Events.fire(Events.TEXT_RECIPIENT, this._peer.id); } } } @@ -229,7 +229,7 @@ class ReceiveDialog extends Dialog { constructor() { super('receiveDialog'); - Events.on('file-received', e => { + Events.on(Events.FILE_RECEIVED, e => { this._nextFile(e.detail); window.blop.play(); }); @@ -313,7 +313,7 @@ class ReceiveDialog extends Dialog { class SendTextDialog extends Dialog { constructor() { super('sendTextDialog'); - Events.on('text-recipient', e => this._onRecipient(e.detail)) + Events.on(Events.TEXT_RECIPIENT, e => this._onRecipient(e.detail)) this.$text = this.$el.querySelector('#textInput'); const button = this.$el.querySelector('form'); button.addEventListener('submit', e => this._send(e)); @@ -341,7 +341,7 @@ class SendTextDialog extends Dialog { _send(e) { e.preventDefault(); - Events.fire('send-text', { + Events.fire(Events.SEND_TEXT, { to: this._recipient, text: this.$text.innerText }); @@ -351,7 +351,7 @@ class SendTextDialog extends Dialog { class ReceiveTextDialog extends Dialog { constructor() { super('receiveTextDialog'); - Events.on('text-received', e => this._onText(e.detail)) + Events.on(Events.TEXT_RECEIVED, e => this._onText(e.detail)) this.$text = this.$el.querySelector('#text'); const $copy = this.$el.querySelector('#copy'); copy.addEventListener('click', _ => this._onCopy()); @@ -375,14 +375,14 @@ class ReceiveTextDialog extends Dialog { async _onCopy() { await navigator.clipboard.writeText(this.$text.textContent); - Events.fire('notify-user', 'Copied to clipboard'); + Events.fire(Events.NOTIFY_USER, 'Copied to clipboard'); } } class Toast extends Dialog { constructor() { super('toast'); - Events.on('notify-user', e => this._onNotfiy(e.detail)); + Events.on(Events.NOTIFY_USER, e => this._onNotfiy(e.detail)); } _onNotfiy(message) { @@ -405,14 +405,14 @@ class Notifications { this.$button.removeAttribute('hidden'); this.$button.addEventListener('click', e => this._requestPermission()); } - Events.on('text-received', e => this._messageNotification(e.detail.text)); - Events.on('file-received', e => this._downloadNotification(e.detail.name)); + Events.on(Events.TEXT_RECEIVED, e => this._messageNotification(e.detail.text)); + Events.on(Events.FILE_RECEIVED, e => this._downloadNotification(e.detail.name)); } _requestPermission() { Notification.requestPermission(permission => { if (permission !== 'granted') { - Events.fire('notify-user', Notifications.PERMISSION_ERROR || 'Error'); + Events.fire(Events.NOTIFY_USER, Notifications.PERMISSION_ERROR || 'Error'); return; } this._notify('Even more snappy sharing!'); @@ -490,11 +490,11 @@ class NetworkStatusUI { } _showOfflineMessage() { - Events.fire('notify-user', 'You are offline'); + Events.fire(Events.NOTIFY_USER, 'You are offline'); } _showOnlineMessage() { - Events.fire('notify-user', 'You are back online'); + Events.fire(Events.NOTIFY_USER, 'You are back online'); } } @@ -523,7 +523,7 @@ class Snapdrop { const server = new ServerConnection(); const peers = new PeersManager(server); const peersUI = new PeersUI(); - Events.on('load', e => { + Events.on(Events.LOAD, e => { const receiveDialog = new ReceiveDialog(); const sendTextDialog = new SendTextDialog(); const receiveTextDialog = new ReceiveTextDialog(); @@ -560,7 +560,7 @@ window.addEventListener('beforeinstallprompt', e => { }); // Background Animation -Events.on('load', () => { +Events.on(Events.LOAD, () => { let c = document.createElement('canvas'); document.body.appendChild(c); let style = c.style; From 00226357aac2d5c672f4d19de5f270144ae3488e Mon Sep 17 00:00:00 2001 From: Victor Wads Date: Fri, 3 Jun 2022 00:40:25 -0300 Subject: [PATCH 02/16] Replace buttons Ids with classes --- client/index.html | 6 +++--- client/scripts/ui.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/index.html b/client/index.html index e4752dbe..6fef6cc1 100644 --- a/client/index.html +++ b/client/index.html @@ -78,14 +78,14 @@

Open Snapdrop on other devices to send files

File Received

-
Filename
-
+
Filename
+
- +
Save diff --git a/client/scripts/ui.js b/client/scripts/ui.js index f69e9f84..0528a5eb 100644 --- a/client/scripts/ui.js +++ b/client/scripts/ui.js @@ -272,8 +272,8 @@ class ReceiveDialog extends Dialog { this.$el.querySelector("#img-preview").src = url; } - this.$el.querySelector('#fileName').textContent = file.name; - this.$el.querySelector('#fileSize').textContent = this._formatFileSize(file.size); + this.$el.querySelector('.fileName').textContent = file.name; + this.$el.querySelector('.fileSize').textContent = this._formatFileSize(file.size); this.show(); if (window.isDownloadSupported) return; @@ -305,7 +305,7 @@ class ReceiveDialog extends Dialog { _autoDownload(){ - return !this.$el.querySelector('#autoDownload').checked + return !this.$el.querySelector('.autoDownload').checked } } From 9d1653daedc28bab1e2915d8afc4546495418061 Mon Sep 17 00:00:00 2001 From: Victor Wads Date: Fri, 3 Jun 2022 01:54:44 -0300 Subject: [PATCH 03/16] Add file accept request Dialog --- client/index.html | 21 ++++++++++++++ client/scripts/ui.js | 69 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 77 insertions(+), 13 deletions(-) diff --git a/client/index.html b/client/index.html index 6fef6cc1..c13df563 100644 --- a/client/index.html +++ b/client/index.html @@ -73,6 +73,27 @@

Open Snapdrop on other devices to send files

You can be discovered by everyone on this network
+ + + + +

File Request

+
Filename
+
+ +
+ + +
+
+ + +
+
+
+
diff --git a/client/scripts/ui.js b/client/scripts/ui.js index 0528a5eb..71e2be2d 100644 --- a/client/scripts/ui.js +++ b/client/scripts/ui.js @@ -13,6 +13,21 @@ Events.on(Events.DISPLAY_NAME, e => { $displayName.title = me.deviceName; }); +Number.prototype.bytesToHumanFileSize = function () { + let bytes = this; + if (bytes >= 1e12) { + return (Math.round(bytes / 1e11) / 10) + ' TB'; + } else if (bytes >= 1e9) { + return (Math.round(bytes / 1e8) / 10) + ' GB'; + } else if (bytes >= 1e6) { + return (Math.round(bytes / 1e5) / 10) + ' MB'; + } else if (bytes > 1000) { + return Math.round(bytes / 1000) + ' KB'; + } else { + return bytes + ' Bytes'; + } +} + class PeersUI { constructor() { @@ -225,6 +240,45 @@ class Dialog { } } +class RequestDialog extends Dialog { + + constructor() { + super('requestDialog'); + Events.on(Events.FILE_REQUEST, e => { + window.blop.play(); + this.lastDetail = e.detail + if(this._autoDownload()) { + this._accept(); + } else { + this._ask(e.detail.file); + } + }); + this.$el.querySelector('.accept').addEventListener('click', () => this._accept()) + this.$el.querySelector('.deny').addEventListener('click', () => this._deny()) + } + + _ask(file) { + this.$el.querySelector('.fileName').textContent = file.name; + this.$el.querySelector('.fileSize').textContent = file.size.bytesToHumanFileSize(); + this.show() + } + + _accept() { + this.lastDetail && this.lastDetail.accept(); + this.hide() + } + + _deny() { + this.lastDetail && this.lastDetail.deny(); + this.hide() + } + + _autoDownload(){ + return !this.$el.querySelector('.autoDownload').checked + } + +} + class ReceiveDialog extends Dialog { constructor() { @@ -273,7 +327,7 @@ class ReceiveDialog extends Dialog { } this.$el.querySelector('.fileName').textContent = file.name; - this.$el.querySelector('.fileSize').textContent = this._formatFileSize(file.size); + this.$el.querySelector('.fileSize').textContent = file.size.bytesToHumanFileSize(); this.show(); if (window.isDownloadSupported) return; @@ -284,18 +338,6 @@ class ReceiveDialog extends Dialog { reader.readAsDataURL(file.blob); } - _formatFileSize(bytes) { - if (bytes >= 1e9) { - return (Math.round(bytes / 1e8) / 10) + ' GB'; - } else if (bytes >= 1e6) { - return (Math.round(bytes / 1e5) / 10) + ' MB'; - } else if (bytes > 1000) { - return Math.round(bytes / 1000) + ' KB'; - } else { - return bytes + ' Bytes'; - } - } - hide() { this.$el.querySelector('.preview').style.visibility = 'hidden'; this.$el.querySelector("#img-preview").src = ""; @@ -525,6 +567,7 @@ class Snapdrop { const peersUI = new PeersUI(); Events.on(Events.LOAD, e => { const receiveDialog = new ReceiveDialog(); + const requestDialog = new RequestDialog(); const sendTextDialog = new SendTextDialog(); const receiveTextDialog = new ReceiveTextDialog(); const toast = new Toast(); From 8a30df1fc8b33eb3896c07927fc44d341332e6c0 Mon Sep 17 00:00:00 2001 From: Victor Wads Date: Fri, 3 Jun 2022 01:55:36 -0300 Subject: [PATCH 04/16] Add request, accept, deny and MultiTransferfile --- client/scripts/network.js | 125 ++++++++++++++++++++++++++++---------- 1 file changed, 92 insertions(+), 33 deletions(-) diff --git a/client/scripts/network.js b/client/scripts/network.js index cd5c5e4a..7fe90ad0 100644 --- a/client/scripts/network.js +++ b/client/scripts/network.js @@ -91,11 +91,38 @@ class ServerConnection { class Peer { + // return uuid of form xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx + static uuid() { + let uuid = '', + ii; + for (ii = 0; ii < 32; ii += 1) { + switch (ii) { + case 8: + case 20: + uuid += '-'; + uuid += (Math.random() * 16 | 0).toString(16); + break; + case 12: + uuid += '-'; + uuid += '4'; + break; + case 16: + uuid += '-'; + uuid += (Math.random() * 4 | 8).toString(16); + break; + default: + uuid += (Math.random() * 16 | 0).toString(16); + } + } + return uuid; + }; + constructor(serverConnection, peerId) { this._server = serverConnection; this._peerId = peerId; this._filesQueue = []; this._busy = false; + this._files = {} } sendJSON(message) { @@ -114,6 +141,7 @@ class Peer { if (!this._filesQueue.length) return; this._busy = true; const file = this._filesQueue.shift(); + file.uuid = Peer.uuid(); this._sendFile(file); } @@ -122,29 +150,42 @@ class Peer { type: 'header', name: file.name, mime: file.type, - size: file.size + size: file.size, + uuid: file.uuid, }); - this._chunker = new FileChunker(file, - chunk => this._send(chunk), - offset => this._onPartitionEnd(offset)); - this._chunker.nextPartition(); + this._files[file.uuid] = { + header: file, + chunker: new FileChunker( + file, + chunk => { + let uuidBytes = new TextEncoder().encode(file.uuid) + let fileBytes = new Uint8Array(chunk); + let bytes = new Uint8Array(uuidBytes.byteLength + fileBytes.byteLength); + bytes.set(uuidBytes); + bytes.set(fileBytes, uuidBytes.byteLength); + this._send(bytes) + }, + offset => this._onPartitionEnd(file.uuid, offset) + ) + } } - _onPartitionEnd(offset) { - this.sendJSON({ type: 'partition', offset: offset }); + _onPartitionEnd(uuid, offset) { + this.sendJSON({ type: 'partition', uuid, offset }); } - _onReceivedPartitionEnd(offset) { - this.sendJSON({ type: 'partition-received', offset: offset }); + _onReceivedPartitionEnd(uuid, offset) { + this.sendJSON({ type: 'partition-received', uuid, offset }); } - _sendNextPartition() { - if (!this._chunker || this._chunker.isFileEnd()) return; - this._chunker.nextPartition(); + _sendNextPartition(uuid) { + let chunker = this._files[uuid].chunker + if (!chunker || chunker.isFileEnd()) return; + chunker.nextPartition(); } - _sendProgress(progress) { - this.sendJSON({ type: 'progress', progress: progress }); + _sendProgress(uuid, progress) { + this.sendJSON({ type: 'progress', uuid, progress }); } _onMessage(message) { @@ -159,10 +200,14 @@ class Peer { this._onFileHeader(message); break; case 'partition': - this._onReceivedPartitionEnd(message); + this._onReceivedPartitionEnd(message.uuid, message.offset); + break; + case Events.FILE_DENY: + this._dequeueFile(); break; + case Events.FILE_ACCEPT: case 'partition-received': - this._sendNextPartition(); + this._sendNextPartition(message.uuid); break; case 'progress': this._onDownloadProgress(message.progress); @@ -177,34 +222,48 @@ class Peer { } _onFileHeader(header) { - this._lastProgress = 0; - this._digester = new FileDigester({ - name: header.name, - mime: header.mime, - size: header.size - }, file => this._onFileReceived(file)); + this._files[header.uuid] = { + header, + lastProgress: 0, + digester: new FileDigester({ + name: header.name, + mime: header.mime, + size: header.size, + uuid: header.uuid + }, file => this._onFileReceived(header.uuid, file)) + } + Events.fire(Events.FILE_REQUEST, { + file: header, + accept: () => this.sendJSON({type: Events.FILE_ACCEPT, uuid: header.uuid}), + deny: () => this.sendJSON({type: Events.FILE_DENY, uuid: header.uuid}) + }) } - _onChunkReceived(chunk) { - if(!chunk.byteLength) return; + _onChunkReceived(data) { + if(!data.byteLength) return; + + let uuid = new TextDecoder().decode(data.slice(0,36)); + var chunk = data.slice(36); - this._digester.unchunk(chunk); - const progress = this._digester.progress; + let file = this._files[uuid]; + file.digester.unchunk(chunk); + + const progress = file.digester.progress; this._onDownloadProgress(progress); // occasionally notify sender about our progress - if (progress - this._lastProgress < 0.01) return; - this._lastProgress = progress; - this._sendProgress(progress); + if (progress - file.lastProgress < 0.01) return; + file.lastProgress = progress; + this._sendProgress(file.header.uuid, progress); } _onDownloadProgress(progress) { Events.fire(Events.FILE_PROGRESS, { sender: this._peerId, progress: progress }); } - _onFileReceived(proxyFile) { + _onFileReceived(uuid, proxyFile) { Events.fire(Events.FILE_RECEIVED, proxyFile); - this.sendJSON({ type: 'transfer-complete' }); + this.sendJSON({ type: 'transfer-complete', uuid }); } _onTransferCompleted() { @@ -423,7 +482,7 @@ class WSPeer { class FileChunker { constructor(file, onChunk, onPartitionEnd) { - this._chunkSize = 64000; // 64 KB + this._chunkSize = 128000; // 128 KB this._maxPartitionSize = 1e6; // 1 MB this._offset = 0; this._partitionSize = 0; @@ -546,4 +605,4 @@ RTCPeer.config = { 'iceServers': [{ urls: 'stun:stun.l.google.com:19302' }] -} +} \ No newline at end of file From f38cd36b1c61b9f408623bb27fe50e7cece9d202 Mon Sep 17 00:00:00 2001 From: Victor Wads Date: Fri, 3 Jun 2022 02:50:25 -0300 Subject: [PATCH 05/16] Add simultaneos files transference --- client/scripts/network.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/client/scripts/network.js b/client/scripts/network.js index 7fe90ad0..5fabf3f0 100644 --- a/client/scripts/network.js +++ b/client/scripts/network.js @@ -137,6 +137,8 @@ class Peer { this._dequeueFile(); } + static MAX_SIMULTANEOUS_REQUEST = 5 + _dequeueFile() { if (!this._filesQueue.length) return; this._busy = true; @@ -153,6 +155,7 @@ class Peer { size: file.size, uuid: file.uuid, }); + this._requestingPermission++ this._files[file.uuid] = { header: file, chunker: new FileChunker( @@ -168,6 +171,9 @@ class Peer { offset => this._onPartitionEnd(file.uuid, offset) ) } + if(this._requestingPermission < Peer.MAX_SIMULTANEOUS_REQUEST){ + this._dequeueFile() + } } _onPartitionEnd(uuid, offset) { @@ -203,9 +209,13 @@ class Peer { this._onReceivedPartitionEnd(message.uuid, message.offset); break; case Events.FILE_DENY: + this._requestingPermission--; this._dequeueFile(); break; case Events.FILE_ACCEPT: + this._requestingPermission--; + this._sendNextPartition(message.uuid); + break case 'partition-received': this._sendNextPartition(message.uuid); break; @@ -565,12 +575,10 @@ class FileDigester { class Events { static fire(type, detail) { - console.warn('Events.fire: ', type) window.dispatchEvent(new CustomEvent(type, { detail: detail })); } static on(type, callback) { - console.warn('Events.on: ', type) return window.addEventListener(type, callback, false); } From cf85386aa7d94136f64de3221e079a947e77477c Mon Sep 17 00:00:00 2001 From: Victor Wads Date: Fri, 3 Jun 2022 02:52:03 -0300 Subject: [PATCH 06/16] Made pear show all downloading files progress --- client/scripts/network.js | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/client/scripts/network.js b/client/scripts/network.js index 5fabf3f0..bf80e5bf 100644 --- a/client/scripts/network.js +++ b/client/scripts/network.js @@ -123,6 +123,9 @@ class Peer { this._filesQueue = []; this._busy = false; this._files = {} + this._requestingPermission = 0 + this._totalBytesAccepted = 0; + this._totalBytesReceived = 0; } sendJSON(message) { @@ -190,8 +193,8 @@ class Peer { chunker.nextPartition(); } - _sendProgress(uuid, progress) { - this.sendJSON({ type: 'progress', uuid, progress }); + _sendProgress(uuid, progress, total) { + this.sendJSON({ type: 'progress', uuid, progress, total }); } _onMessage(message) { @@ -244,7 +247,10 @@ class Peer { } Events.fire(Events.FILE_REQUEST, { file: header, - accept: () => this.sendJSON({type: Events.FILE_ACCEPT, uuid: header.uuid}), + accept: () => { + this._totalBytesAccepted += header.size + this.sendJSON({type: Events.FILE_ACCEPT, uuid: header.uuid}) + }, deny: () => this.sendJSON({type: Events.FILE_DENY, uuid: header.uuid}) }) } @@ -258,17 +264,30 @@ class Peer { let file = this._files[uuid]; file.digester.unchunk(chunk); - const progress = file.digester.progress; - this._onDownloadProgress(progress); + this._totalBytesReceived += chunk.byteLength + const totalProgress = this._totalBytesReceived / this._totalBytesAccepted + const fileProgress = file.digester.progress; + this._onDownloadProgress(totalProgress, uuid, fileProgress); // occasionally notify sender about our progress - if (progress - file.lastProgress < 0.01) return; - file.lastProgress = progress; - this._sendProgress(file.header.uuid, progress); + if (fileProgress - file.lastProgress < 0.05) return; + file.lastProgress = fileProgress; + + this._sendProgress(file.header.uuid, totalProgress, file.digester.progress); + + console.clear() + console.warn( + Object.keys(this._files).map(key => { + let file = this._files[key] + let prog = file.digester.progress + return file.header.name + ' - progress: ' + Math.round(prog * 100) + '% - ' + + ((file.header.size*prog).bytesToHumanFileSize()) + '/' + (file.header.size.bytesToHumanFileSize()) + }).join('\n') + ) } - _onDownloadProgress(progress) { - Events.fire(Events.FILE_PROGRESS, { sender: this._peerId, progress: progress }); + _onDownloadProgress(totalProgress, uuid, fileProgress) { + Events.fire(Events.FILE_PROGRESS, { sender: this._peerId, uuid, progress: totalProgress, fileProgress }); } _onFileReceived(uuid, proxyFile) { From 83a64ffe3e6cfc6df158915c8f8417df34211131 Mon Sep 17 00:00:00 2001 From: Victor Wads Date: Fri, 3 Jun 2022 03:32:21 -0300 Subject: [PATCH 07/16] Base file list view --- client/index.html | 8 ++++++ client/scripts/network.js | 10 -------- client/scripts/ui.js | 52 +++++++++++++++++++++++++++++++++++++++ client/styles.css | 32 +++++++++++++++++++++++- 4 files changed, 91 insertions(+), 11 deletions(-) diff --git a/client/index.html b/client/index.html index c13df563..6b14685d 100644 --- a/client/index.html +++ b/client/index.html @@ -73,6 +73,14 @@

Open Snapdrop on other devices to send files

You can be discovered by everyone on this network
+ +
+
+ Toggle +
    +
+
+
diff --git a/client/scripts/network.js b/client/scripts/network.js index bf80e5bf..a169078b 100644 --- a/client/scripts/network.js +++ b/client/scripts/network.js @@ -274,16 +274,6 @@ class Peer { file.lastProgress = fileProgress; this._sendProgress(file.header.uuid, totalProgress, file.digester.progress); - - console.clear() - console.warn( - Object.keys(this._files).map(key => { - let file = this._files[key] - let prog = file.digester.progress - return file.header.name + ' - progress: ' + Math.round(prog * 100) + '% - ' + - ((file.header.size*prog).bytesToHumanFileSize()) + '/' + (file.header.size.bytesToHumanFileSize()) - }).join('\n') - ) } _onDownloadProgress(totalProgress, uuid, fileProgress) { diff --git a/client/scripts/ui.js b/client/scripts/ui.js index 71e2be2d..ca0a29bb 100644 --- a/client/scripts/ui.js +++ b/client/scripts/ui.js @@ -559,6 +559,57 @@ class WebShareTargetUI { } } +class FilesListView { + constructor() { + this.$el = $('filesListView'); + this.$list = this.$el.querySelector('ul'); + this.$el.querySelectorAll('[toggle]').forEach(el => el.addEventListener('click', e => this.toggle())) + this.$autoFocus = this.$el.querySelector('[autofocus]'); + this._visible = false + this._files = {} + + Events.on(Events.FILE_REQUEST, ({detail}) => { + this._files[detail.file.uuid] = new FilesListItemView(detail.file).root(this.$list) + }) + Events.on(Events.FILE_PROGRESS, ({detail}) => { + this._files[detail.uuid].setProgress(detail.fileProgress) + }) + } + + toggle() { + this._visible = !this._visible + if (this._visible) { + this.$el.setAttribute('show', 1); + } else { + this.$el.removeAttribute('show'); + } + } +} + +class FilesListItemView { + constructor(file) { + this._file = file + this.$el = document.createElement('li') + this.$el.innerHTML = ` +
${file.name}
+
${file.size.bytesToHumanFileSize()}
+
+
${(0).bytesToHumanFileSize()} / ${file.size.bytesToHumanFileSize()}
+ ` + this.$progress = this.$el.querySelector('progress') + this.$progressLabel = this.$el.querySelector('.progressLabel') + } + + setProgress(value) { + this.$progress.value = value + } + + root(root) { + root.appendChild(this.$el) + return this + } + +} class Snapdrop { constructor() { @@ -566,6 +617,7 @@ class Snapdrop { const peers = new PeersManager(server); const peersUI = new PeersUI(); Events.on(Events.LOAD, e => { + const filesListView = new FilesListView(); const receiveDialog = new ReceiveDialog(); const requestDialog = new RequestDialog(); const sendTextDialog = new SendTextDialog(); diff --git a/client/styles.css b/client/styles.css index 0a9aca98..ff6ea360 100644 --- a/client/styles.css +++ b/client/styles.css @@ -322,6 +322,37 @@ footer .font-body2 { } } +/* + Files Listing +*/ + +#filesListView { + box-sizing: border-box; + border: 1px solid black; + border-radius: 20px; + position: fixed; + left: 1vw; + top: 1vh; + width: 80px; + height: 20px; + max-width: 400px; + max-height: 700px; + background-color: rgba(255, 255, 255, 0.3); + transition-duration: 0.3s; + transition-property: width, height; + overflow-x: hidden; + overflow-y: scroll; +} + +#filesListView[show] { + width: 98vw; + height: 98vh; +} + +#filesListView > div { + width: 98vw; +} + /* Dialog */ @@ -754,4 +785,3 @@ x-dialog x-paper { overflow: hidden; } } - From 4905e42ba40ad110d80e99d8c2eaa6a55a51a352 Mon Sep 17 00:00:00 2001 From: Victor Wads Date: Fri, 3 Jun 2022 04:03:36 -0300 Subject: [PATCH 08/16] Fix sender ui progress --- client/scripts/network.js | 15 +++++++++------ client/scripts/ui.js | 21 ++++++++++++--------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/client/scripts/network.js b/client/scripts/network.js index a169078b..20aa5643 100644 --- a/client/scripts/network.js +++ b/client/scripts/network.js @@ -193,8 +193,8 @@ class Peer { chunker.nextPartition(); } - _sendProgress(uuid, progress, total) { - this.sendJSON({ type: 'progress', uuid, progress, total }); + _sendProgress(totalProgress, uuid, fileProgress, ) { + this.sendJSON({ type: 'progress', uuid, totalProgress, fileProgress }); } _onMessage(message) { @@ -217,13 +217,15 @@ class Peer { break; case Events.FILE_ACCEPT: this._requestingPermission--; + Events.fire(Events.FILE_ACCEPT, this._files[message.uuid].header) this._sendNextPartition(message.uuid); break case 'partition-received': this._sendNextPartition(message.uuid); break; case 'progress': - this._onDownloadProgress(message.progress); + const {totalProgress, uuid, fileProgress} = message + this._onDownloadProgress(totalProgress, uuid, fileProgress); break; case 'transfer-complete': this._onTransferCompleted(); @@ -273,11 +275,11 @@ class Peer { if (fileProgress - file.lastProgress < 0.05) return; file.lastProgress = fileProgress; - this._sendProgress(file.header.uuid, totalProgress, file.digester.progress); + this._sendProgress(totalProgress, file.header.uuid, file.digester.progress); } _onDownloadProgress(totalProgress, uuid, fileProgress) { - Events.fire(Events.FILE_PROGRESS, { sender: this._peerId, uuid, progress: totalProgress, fileProgress }); + Events.fire(Events.FILE_PROGRESS, { sender: this._peerId, uuid, totalProgress, fileProgress }); } _onFileReceived(uuid, proxyFile) { @@ -286,7 +288,8 @@ class Peer { } _onTransferCompleted() { - this._onDownloadProgress(1); + // TODO + // this._onDownloadProgress(null,null,1); this._reader = null; this._busy = false; this._dequeueFile(); diff --git a/client/scripts/ui.js b/client/scripts/ui.js index ca0a29bb..e23020d7 100644 --- a/client/scripts/ui.js +++ b/client/scripts/ui.js @@ -568,12 +568,13 @@ class FilesListView { this._visible = false this._files = {} - Events.on(Events.FILE_REQUEST, ({detail}) => { - this._files[detail.file.uuid] = new FilesListItemView(detail.file).root(this.$list) - }) - Events.on(Events.FILE_PROGRESS, ({detail}) => { - this._files[detail.uuid].setProgress(detail.fileProgress) - }) + Events.on(Events.FILE_ACCEPT, ({detail}) => this._addFile(detail)) + Events.on(Events.FILE_REQUEST, ({detail}) => this._addFile(detail.file)) + Events.on(Events.FILE_PROGRESS, ({detail}) => this._files[detail.uuid].setProgress(detail.fileProgress)) + } + + _addFile(file) { + this._files[file.uuid] = new FilesListItemView(file).root(this.$list) } toggle() { @@ -592,9 +593,10 @@ class FilesListItemView { this.$el = document.createElement('li') this.$el.innerHTML = `
${file.name}
-
${file.size.bytesToHumanFileSize()}
-
-
${(0).bytesToHumanFileSize()} / ${file.size.bytesToHumanFileSize()}
+
+ ${(0).bytesToHumanFileSize()} / ${file.size.bytesToHumanFileSize()} + +
` this.$progress = this.$el.querySelector('progress') this.$progressLabel = this.$el.querySelector('.progressLabel') @@ -602,6 +604,7 @@ class FilesListItemView { setProgress(value) { this.$progress.value = value + this.$progressLabel.innerHTML = (this._file.size * value).bytesToHumanFileSize() } root(root) { From ac2907d176cdcb49e8cf93336e23df653f6840cc Mon Sep 17 00:00:00 2001 From: Victor Wads Date: Fri, 3 Jun 2022 04:44:58 -0300 Subject: [PATCH 09/16] Request Multi Prompt --- client/scripts/ui.js | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/client/scripts/ui.js b/client/scripts/ui.js index e23020d7..1f30f676 100644 --- a/client/scripts/ui.js +++ b/client/scripts/ui.js @@ -246,31 +246,47 @@ class RequestDialog extends Dialog { super('requestDialog'); Events.on(Events.FILE_REQUEST, e => { window.blop.play(); - this.lastDetail = e.detail - if(this._autoDownload()) { - this._accept(); - } else { - this._ask(e.detail.file); - } + this._acceptQueue.push(e.detail) + this._proccess() }); + this._acceptQueue = [] + this._showing = false this.$el.querySelector('.accept').addEventListener('click', () => this._accept()) this.$el.querySelector('.deny').addEventListener('click', () => this._deny()) } + _proccess () { + if(this._showing || this._acceptQueue.length == 0) return; + this.lastDetail = this._acceptQueue.shift() + if(this._autoDownload()) { + this._accept(); + } else { + this._ask(this.lastDetail.file); + } + } + _ask(file) { this.$el.querySelector('.fileName').textContent = file.name; this.$el.querySelector('.fileSize').textContent = file.size.bytesToHumanFileSize(); this.show() + this._showing = true } _accept() { this.lastDetail && this.lastDetail.accept(); - this.hide() + this._hide() } _deny() { this.lastDetail && this.lastDetail.deny(); + this._hide() + } + + _hide() { + this._showing = false + this.lastDetail = null this.hide() + this._proccess() } _autoDownload(){ From 501a32bafcb6828784ba17e4e2cac9d9d6593e05 Mon Sep 17 00:00:00 2001 From: Victor Wads Date: Fri, 3 Jun 2022 04:45:35 -0300 Subject: [PATCH 10/16] Add sender simultaneos limit --- client/scripts/network.js | 43 ++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/client/scripts/network.js b/client/scripts/network.js index 20aa5643..121e5898 100644 --- a/client/scripts/network.js +++ b/client/scripts/network.js @@ -121,9 +121,8 @@ class Peer { this._server = serverConnection; this._peerId = peerId; this._filesQueue = []; - this._busy = false; this._files = {} - this._requestingPermission = 0 + this._sendingSimultaneos = 0 this._totalBytesAccepted = 0; this._totalBytesReceived = 0; } @@ -134,23 +133,12 @@ class Peer { sendFiles(files) { for (let i = 0; i < files.length; i++) { - this._filesQueue.push(files[i]); + this._sendFile(files[i]) } - if (this._busy) return; - this._dequeueFile(); - } - - static MAX_SIMULTANEOUS_REQUEST = 5 - - _dequeueFile() { - if (!this._filesQueue.length) return; - this._busy = true; - const file = this._filesQueue.shift(); - file.uuid = Peer.uuid(); - this._sendFile(file); } _sendFile(file) { + file.uuid = Peer.uuid(); this.sendJSON({ type: 'header', name: file.name, @@ -158,7 +146,6 @@ class Peer { size: file.size, uuid: file.uuid, }); - this._requestingPermission++ this._files[file.uuid] = { header: file, chunker: new FileChunker( @@ -174,9 +161,18 @@ class Peer { offset => this._onPartitionEnd(file.uuid, offset) ) } - if(this._requestingPermission < Peer.MAX_SIMULTANEOUS_REQUEST){ - this._dequeueFile() - } + } + + static MAX_SIMULTANEOUS_REQUEST = 3 + + _dequeueFile() { + if ( + this._sendingSimultaneos >= Peer.MAX_SIMULTANEOUS_REQUEST | + !this._filesQueue.length + ) return; + this._sendingSimultaneos++ + const uuid = this._filesQueue.shift(); + this._sendNextPartition(uuid); } _onPartitionEnd(uuid, offset) { @@ -212,13 +208,12 @@ class Peer { this._onReceivedPartitionEnd(message.uuid, message.offset); break; case Events.FILE_DENY: - this._requestingPermission--; this._dequeueFile(); break; case Events.FILE_ACCEPT: - this._requestingPermission--; Events.fire(Events.FILE_ACCEPT, this._files[message.uuid].header) - this._sendNextPartition(message.uuid); + this._filesQueue.push(message.uuid); + this._dequeueFile(); break case 'partition-received': this._sendNextPartition(message.uuid); @@ -288,10 +283,8 @@ class Peer { } _onTransferCompleted() { - // TODO - // this._onDownloadProgress(null,null,1); + this._sendingSimultaneos-- this._reader = null; - this._busy = false; this._dequeueFile(); Events.fire(Events.NOTIFY_USER, 'File transfer completed.'); } From f927ba5640ea93e5d21069fed3954d4965b3a2f3 Mon Sep 17 00:00:00 2001 From: Victor Wads Date: Fri, 3 Jun 2022 05:47:35 -0300 Subject: [PATCH 11/16] Add Files Preview --- client/index.html | 2 +- client/scripts/network.js | 2 +- client/scripts/ui.js | 32 +++++++++++---- client/styles.css | 86 ++++++++++++++++++++++++++++++++++----- 4 files changed, 102 insertions(+), 20 deletions(-) diff --git a/client/index.html b/client/index.html index 6b14685d..b9807c48 100644 --- a/client/index.html +++ b/client/index.html @@ -76,7 +76,7 @@

Open Snapdrop on other devices to send files

- Toggle + Files
diff --git a/client/scripts/network.js b/client/scripts/network.js index 121e5898..a749c248 100644 --- a/client/scripts/network.js +++ b/client/scripts/network.js @@ -278,7 +278,7 @@ class Peer { } _onFileReceived(uuid, proxyFile) { - Events.fire(Events.FILE_RECEIVED, proxyFile); + Events.fire(Events.FILE_RECEIVED, { file: proxyFile, uuid }); this.sendJSON({ type: 'transfer-complete', uuid }); } diff --git a/client/scripts/ui.js b/client/scripts/ui.js index 1f30f676..e9e5c527 100644 --- a/client/scripts/ui.js +++ b/client/scripts/ui.js @@ -300,7 +300,7 @@ class ReceiveDialog extends Dialog { constructor() { super('receiveDialog'); Events.on(Events.FILE_RECEIVED, e => { - this._nextFile(e.detail); + this._nextFile(e.detail.file); window.blop.play(); }); this._filesQueue = []; @@ -464,7 +464,7 @@ class Notifications { this.$button.addEventListener('click', e => this._requestPermission()); } Events.on(Events.TEXT_RECEIVED, e => this._messageNotification(e.detail.text)); - Events.on(Events.FILE_RECEIVED, e => this._downloadNotification(e.detail.name)); + Events.on(Events.FILE_RECEIVED, e => this._downloadNotification(e.detail.file.name)); } _requestPermission() { @@ -587,6 +587,8 @@ class FilesListView { Events.on(Events.FILE_ACCEPT, ({detail}) => this._addFile(detail)) Events.on(Events.FILE_REQUEST, ({detail}) => this._addFile(detail.file)) Events.on(Events.FILE_PROGRESS, ({detail}) => this._files[detail.uuid].setProgress(detail.fileProgress)) + Events.on(Events.FILE_RECEIVED, ({detail}) => this._files[detail.uuid].preview(detail.file) ); + } _addFile(file) { @@ -601,6 +603,7 @@ class FilesListView { this.$el.removeAttribute('show'); } } + } class FilesListItemView { @@ -608,14 +611,20 @@ class FilesListItemView { this._file = file this.$el = document.createElement('li') this.$el.innerHTML = ` -
${file.name}
-
- ${(0).bytesToHumanFileSize()} / ${file.size.bytesToHumanFileSize()} - +
+
+
+ ${file.name} +
+ ${(0).bytesToHumanFileSize()} / ${file.size.bytesToHumanFileSize()} +
+
+
` this.$progress = this.$el.querySelector('progress') - this.$progressLabel = this.$el.querySelector('.progressLabel') + this.$progressLabel = this.$el.querySelector('.progressLabel') + this.$preview = this.$el.querySelector('.preview') } setProgress(value) { @@ -628,6 +637,15 @@ class FilesListItemView { return this } + preview(file) { + const url = URL.createObjectURL(file.blob); + if(file.mime.split('/')[0] === 'image'){ + console.log('the file is image'); + this.$preview.style.backgroundImage = `url(${url})`; + this.$preview.setAttribute('preview','loaded') + } + } + } class Snapdrop { diff --git a/client/styles.css b/client/styles.css index ff6ea360..e8d082a8 100644 --- a/client/styles.css +++ b/client/styles.css @@ -328,29 +328,93 @@ footer .font-body2 { #filesListView { box-sizing: border-box; - border: 1px solid black; border-radius: 20px; position: fixed; - left: 1vw; - top: 1vh; - width: 80px; - height: 20px; - max-width: 400px; - max-height: 700px; - background-color: rgba(255, 255, 255, 0.3); + left: 2vw; + top: 2.5vh; + width: 90px; + height: 45px; + max-width: 600px; + padding: 10px; + background-color: var(--bg-color-secondary); transition-duration: 0.3s; - transition-property: width, height; + transition-property: width, height, overflow-y; overflow-x: hidden; - overflow-y: scroll; + overflow-y: hidden; + z-index: 100; +} + +#filesListView [toggle] { + display: block; + padding-bottom: 15px; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + text-align: center; + cursor: pointer; } #filesListView[show] { width: 98vw; - height: 98vh; + height: 95vh; + overflow-y: scroll; } #filesListView > div { width: 98vw; + max-width: 100%; +} + +#filesListView ul { + margin: 0; + padding: 0; + list-style: none; +} + +#filesListView li { + display: flex; + margin: 10px 0; + padding: 10px 12px; + background-color: rgba(255, 255, 255, 0.08); + border-radius: 8px; +} + +#filesListView li .preview { + width: 0px; + border-radius: 8px; + background-size: cover; + transition-duration: 0.3s; + transition-property: width, opacity, margin; + opacity: 0; + margin-right: 15px; +} + +#filesListView li .preview[preview] { + width: 80px; + opacity: 1; +} + +#filesListView li .content { + flex: 1; +} + +#filesListView li .content > div { + display: flex; + align-items: flex-end; +} + +#filesListView li div > span { + display: block; + flex: 1; +} + +#filesListView .details { + text-align: right; + font-size: 0.7em; +} + +#filesListView progress { + display: block; + box-sizing: border-box; + width: 100%; } From 48f2c31ec529ce8d77cc1c84a3cf79450937ef81 Mon Sep 17 00:00:00 2001 From: Victor Wads Date: Fri, 3 Jun 2022 05:57:34 -0300 Subject: [PATCH 12/16] Add Video/Audio Preview --- client/scripts/ui.js | 12 ++++++++++-- client/styles.css | 8 +++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/client/scripts/ui.js b/client/scripts/ui.js index e9e5c527..36d47bb3 100644 --- a/client/scripts/ui.js +++ b/client/scripts/ui.js @@ -611,7 +611,6 @@ class FilesListItemView { this._file = file this.$el = document.createElement('li') this.$el.innerHTML = ` -
${file.name} @@ -621,6 +620,7 @@ class FilesListItemView {
+
` this.$progress = this.$el.querySelector('progress') this.$progressLabel = this.$el.querySelector('.progressLabel') @@ -639,10 +639,18 @@ class FilesListItemView { preview(file) { const url = URL.createObjectURL(file.blob); - if(file.mime.split('/')[0] === 'image'){ + let mine = file.mime.split('/')[0] + if(mine === 'image'){ console.log('the file is image'); this.$preview.style.backgroundImage = `url(${url})`; this.$preview.setAttribute('preview','loaded') + } else if (mine === 'video' || mine === 'audio') { + let video = document.createElement(mine) + this.$preview.appendChild(video) + video.src = url; + video.controls = true + video.load(); + this.$preview.setAttribute('preview','loaded') } } diff --git a/client/styles.css b/client/styles.css index e8d082a8..232d7d76 100644 --- a/client/styles.css +++ b/client/styles.css @@ -384,7 +384,13 @@ footer .font-body2 { transition-duration: 0.3s; transition-property: width, opacity, margin; opacity: 0; - margin-right: 15px; + margin-left: 15px; + overflow: hidden; +} + +#filesListView li .preview > * { + width: 100%; + height: 100%; } #filesListView li .preview[preview] { From eab6ca156e4545980ee0ecd0136bb7402e417dff Mon Sep 17 00:00:00 2001 From: Victor Wads Date: Sun, 5 Jun 2022 14:23:17 -0300 Subject: [PATCH 13/16] Minor change --- client/scripts/network.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/scripts/network.js b/client/scripts/network.js index a749c248..7e216fc4 100644 --- a/client/scripts/network.js +++ b/client/scripts/network.js @@ -163,7 +163,7 @@ class Peer { } } - static MAX_SIMULTANEOUS_REQUEST = 3 + static MAX_SIMULTANEOUS_REQUEST = 5 _dequeueFile() { if ( @@ -413,7 +413,7 @@ class RTCPeer extends Peer { } _sendSignal(signal) { - signal.type = 'signal'; + signal.type = Events.SIGNAL; signal.to = this._peerId; this._server.send(signal); } From 7645becb416fd50726ce9986b8708e359eb1816d Mon Sep 17 00:00:00 2001 From: Victor Wads Date: Sun, 5 Jun 2022 14:32:43 -0300 Subject: [PATCH 14/16] Adds Video and Audio preview --- client/index.html | 4 +--- client/scripts/ui.js | 26 ++++++++++++++++++++------ client/styles.css | 5 +++-- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/client/index.html b/client/index.html index e4752dbe..bac11567 100644 --- a/client/index.html +++ b/client/index.html @@ -80,9 +80,7 @@

Open Snapdrop on other devices to send files

File Received

Filename
- +
diff --git a/client/scripts/ui.js b/client/scripts/ui.js index 0c159841..96ba911d 100644 --- a/client/scripts/ui.js +++ b/client/scripts/ui.js @@ -234,6 +234,7 @@ class ReceiveDialog extends Dialog { window.blop.play(); }); this._filesQueue = []; + this.$previewBox = this.$el.querySelector('.preview') } _nextFile(nextFile) { @@ -266,10 +267,23 @@ class ReceiveDialog extends Dialog { $a.click() return } - if(file.mime.split('/')[0] === 'image'){ - console.log('the file is image'); - this.$el.querySelector('.preview').style.visibility = 'inherit'; - this.$el.querySelector("#img-preview").src = url; + + let mine = file.mime.split('/')[0] + let previewElement = { + image: 'img', + audio: 'audio', + video: 'video' + } + + if(Object.keys(previewElement).indexOf(mine) !== -1){ + console.log('the file is able to preview'); + let element = document.createElement(previewElement[mine]); + element.src = url; + element.controls = true; + element.classList = 'element-preview' + + this.$previewBox.style.visibility = 'inherit'; + this.$previewBox.appendChild(element) } this.$el.querySelector('#fileName').textContent = file.name; @@ -297,8 +311,8 @@ class ReceiveDialog extends Dialog { } hide() { - this.$el.querySelector('.preview').style.visibility = 'hidden'; - this.$el.querySelector("#img-preview").src = ""; + this.$previewBox.style.visibility = 'hidden'; + this.$previewBox.innerHTML = ''; super.hide(); this._dequeueFile(); } diff --git a/client/styles.css b/client/styles.css index 0a9aca98..c3e573d7 100644 --- a/client/styles.css +++ b/client/styles.css @@ -717,8 +717,9 @@ x-dialog x-paper { color: var(--text-color); background-color: var(--bg-color-secondary); } -/* Image Preview */ -#img-preview{ +/* Image/Video/Audio Preview */ +.element-preview { + max-width: 100%; max-height: 50vh; margin: auto; display: block; From 3bcf0135ea88deede71f204bd7cb672e73cb1eee Mon Sep 17 00:00:00 2001 From: Victor Wads Date: Sun, 5 Jun 2022 22:47:19 -0300 Subject: [PATCH 15/16] Refactor Previews --- client/scripts/ui.js | 71 ++++++++++++++++++++++++-------------------- client/styles.css | 26 ++++++++-------- 2 files changed, 53 insertions(+), 44 deletions(-) diff --git a/client/scripts/ui.js b/client/scripts/ui.js index 49caa0e0..234dca58 100644 --- a/client/scripts/ui.js +++ b/client/scripts/ui.js @@ -28,6 +28,36 @@ Number.prototype.bytesToHumanFileSize = function () { } } +class PreviewView { + + static Elements = { + image: 'img', + audio: 'audio', + video: 'video' + } + + static Keys = Object.keys(PreviewView.Elements) + + constructor(file, parent) { + let mine = file.mime.split('/')[0] + this.url = URL.createObjectURL(file.blob); + this.isPlayable = PreviewView.Keys.indexOf(mine) !== -1 + this.$parent = parent + + if(this.isPlayable){ + console.log('the file is able to preview'); + let element = document.createElement(PreviewView.Elements[mine]); + element.src = this.url; + element.controls = true; + element.classList = 'element-preview' + + this.$parent.appendChild(element) + } + + } + +} + class PeersUI { constructor() { @@ -328,9 +358,10 @@ class ReceiveDialog extends Dialog { } _displayFile(file) { + const preview = new PreviewView(file, this.$previewBox); + const $a = this.$el.querySelector('#download'); - const url = URL.createObjectURL(file.blob); - $a.href = url; + $a.href = preview.url; $a.download = file.name; if(this._autoDownload()){ @@ -338,22 +369,8 @@ class ReceiveDialog extends Dialog { return } - let mine = file.mime.split('/')[0] - let previewElement = { - image: 'img', - audio: 'audio', - video: 'video' - } - - if(Object.keys(previewElement).indexOf(mine) !== -1){ - console.log('the file is able to preview'); - let element = document.createElement(previewElement[mine]); - element.src = url; - element.controls = true; - element.classList = 'element-preview' - + if(preview.isPlayable) { this.$previewBox.style.visibility = 'inherit'; - this.$previewBox.appendChild(element) } this.$el.querySelector('.fileName').textContent = file.name; @@ -627,7 +644,7 @@ class FilesListItemView { this.$el.innerHTML = `
- ${file.name} + ${file.name}
${(0).bytesToHumanFileSize()} / ${file.size.bytesToHumanFileSize()}
@@ -638,7 +655,7 @@ class FilesListItemView { ` this.$progress = this.$el.querySelector('progress') this.$progressLabel = this.$el.querySelector('.progressLabel') - this.$preview = this.$el.querySelector('.preview') + this.$previewBox = this.$el.querySelector('.preview') } setProgress(value) { @@ -652,19 +669,9 @@ class FilesListItemView { } preview(file) { - const url = URL.createObjectURL(file.blob); - let mine = file.mime.split('/')[0] - if(mine === 'image'){ - console.log('the file is image'); - this.$preview.style.backgroundImage = `url(${url})`; - this.$preview.setAttribute('preview','loaded') - } else if (mine === 'video' || mine === 'audio') { - let video = document.createElement(mine) - this.$preview.appendChild(video) - video.src = url; - video.controls = true - video.load(); - this.$preview.setAttribute('preview','loaded') + let preview = new PreviewView(file, this.$previewBox); + if(preview.isPlayable) { + this.$previewBox.setAttribute('preview','loaded') } } diff --git a/client/styles.css b/client/styles.css index c73feefe..679bbdaa 100644 --- a/client/styles.css +++ b/client/styles.css @@ -341,7 +341,7 @@ footer .font-body2 { transition-property: width, height, overflow-y; overflow-x: hidden; overflow-y: hidden; - z-index: 100; + z-index: 15; } #filesListView [toggle] { @@ -356,6 +356,7 @@ footer .font-body2 { width: 98vw; height: 95vh; overflow-y: scroll; + z-index: 3; } #filesListView > div { @@ -371,6 +372,7 @@ footer .font-body2 { #filesListView li { display: flex; + align-items: center; margin: 10px 0; padding: 10px 12px; background-color: rgba(255, 255, 255, 0.08); @@ -378,23 +380,27 @@ footer .font-body2 { } #filesListView li .preview { - width: 0px; + display: flex; + align-items: end; + max-width: 0px; + max-height: 100px; border-radius: 8px; - background-size: cover; transition-duration: 0.3s; - transition-property: width, opacity, margin; + transition-property: max-width, opacity, margin; opacity: 0; - margin-left: 15px; + margin-left: 0; overflow: hidden; } #filesListView li .preview > * { - width: 100%; - height: 100%; + min-width: 100%; + min-height: 100%; } #filesListView li .preview[preview] { - width: 80px; + margin-left: 15px; + max-width: 100px; + max-height: 100px; opacity: 1; } @@ -407,10 +413,6 @@ footer .font-body2 { align-items: flex-end; } -#filesListView li div > span { - display: block; - flex: 1; -} #filesListView .details { text-align: right; From 357f1eb2e04411ee671656ae88d22e0446db91b8 Mon Sep 17 00:00:00 2001 From: Victor Wads Date: Sun, 5 Jun 2022 22:48:07 -0300 Subject: [PATCH 16/16] Add Settings icon --- client/index.html | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/client/index.html b/client/index.html index 05ffe687..347cd631 100644 --- a/client/index.html +++ b/client/index.html @@ -38,11 +38,16 @@
- + + + + + + @@ -235,9 +240,12 @@

Snapdrop

- + + + + - +