+
diff --git a/public_included_ws_fallback/scripts/network.js b/public_included_ws_fallback/scripts/network.js
index bf277d3..85199d4 100644
--- a/public_included_ws_fallback/scripts/network.js
+++ b/public_included_ws_fallback/scripts/network.js
@@ -35,6 +35,7 @@ class ServerConnection {
_onOpen() {
console.log('WS: server connected');
Events.fire('ws-connected');
+ if (this._isReconnect) Events.fire('notify-user', 'Connected.');
}
_sendRoomSecrets(roomSecrets) {
@@ -137,6 +138,8 @@ class ServerConnection {
this._socket.onclose = null;
this._socket.close();
this._socket = null;
+ Events.fire('ws-disconnected');
+ this._isReconnect = true;
}
}
@@ -146,10 +149,11 @@ class ServerConnection {
_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() {
@@ -315,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();
@@ -342,10 +346,10 @@ class Peer {
this._onMessageTransferCompleted();
break;
case 'text':
- this._onTextReceived(message);
+ this._onTextReceived(messageJSON);
break;
case 'display-name-changed':
- this._onDisplayNameChanged(message);
+ this._onDisplayNameChanged(messageJSON);
break;
}
}
@@ -439,7 +443,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;
}
@@ -495,17 +499,11 @@ 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);
}
- _onMessage(message) {
- if (typeof message !== 'string') {
- console.log('RTC:', JSON.parse(message));
- }
- super._onMessage(message);
- }
-
_connect(peerId, isCaller) {
if (!this._conn || this._conn.signalingState === "closed") this._openConnection(peerId, isCaller);
@@ -578,6 +576,13 @@ class RTCPeer extends Peer {
Events.fire('peer-connected', {peerId: this._peerId, connectionHash: this.getConnectionHash()});
}
+ _onMessage(message) {
+ if (typeof message === 'string') {
+ console.log('RTC:', JSON.parse(message));
+ }
+ super._onMessage(message);
+ }
+
getConnectionHash() {
const localDescriptionLines = this._conn.localDescription.sdp.split("\r\n");
const remoteDescriptionLines = this._conn.remoteDescription.sdp.split("\r\n");
@@ -692,6 +697,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._isCaller = true;
this._sendSignal();
@@ -712,15 +718,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) {
this._peerId = message.sender.id;
Events.fire('peer-connected', {peerId: message.sender.id, connectionHash: this.getConnectionHash()})
- if (this._isCaller) return;
- this._sendSignal();
+ if (message.connected) return;
+ this._sendSignal(true);
}
getConnectionHash() {
@@ -745,6 +751,7 @@ class PeersManager {
Events.on('secret-room-deleted', e => this._onSecretRoomDeleted(e.detail));
Events.on('display-name', e => this._onDisplayName(e.detail.message.displayName));
Events.on('self-display-name-changed', e => this._notifyPeersDisplayNameChanged(e.detail));
+ Events.on('ws-disconnected', _ => this._onWsDisconnected());
Events.on('ws-relay', e => this._onWsRelay(e.detail));
}
@@ -764,7 +771,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) {
@@ -808,9 +815,9 @@ 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);
@@ -821,6 +828,15 @@ class PeersManager {
this._notifyPeerDisplayNameChanged(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];
diff --git a/public_included_ws_fallback/scripts/ui.js b/public_included_ws_fallback/scripts/ui.js
index f8040bf..0699043 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);
@@ -28,7 +27,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));
@@ -549,10 +548,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) {
@@ -568,6 +571,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 {
@@ -575,24 +598,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() {
@@ -624,7 +648,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)
@@ -635,30 +658,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'
})
}
@@ -686,47 +711,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 = `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'})
- 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));
@@ -774,32 +805,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 = 'PairDrop - File Transfer Requested';
+ 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();
}
@@ -834,7 +851,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)));
@@ -913,7 +930,7 @@ class PairDeviceDialog extends Dialog {
})
this.$submitBtn.removeAttribute("disabled");
if (document.activeElement === this.$inputRoomKeyChars[5]) {
- this._onSubmit();
+ this._pairDeviceJoin(this.inputRoomKey);
}
}
}
@@ -963,7 +980,8 @@ class PairDeviceDialog extends Dialog {
return url.href;
}
- _onSubmit() {
+ _onSubmit(e) {
+ e.preventDefault();
this._pairDeviceJoin(this.inputRoomKey);
}
@@ -1050,14 +1068,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();
}
@@ -1068,10 +1091,10 @@ 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('#send-text-dialog .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', _ => 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));
}
@@ -1113,6 +1136,11 @@ class SendTextDialog extends Dialog {
sel.addRange(range);
}
+ _onSubmit(e) {
+ e.preventDefault();
+ this._send();
+ }
+
_send() {
Events.fire('send-text', {
to: this.correspondingPeerId,
@@ -1136,7 +1164,7 @@ class ReceiveTextDialog extends Dialog {
Events.on("keydown", e => this._onKeyDown(e));
- this.$receiveTextPeerDisplayNameNode = this.$el.querySelector('#receive-text-dialog .display-name');
+ this.$displayNameNode = this.$el.querySelector('.display-name');
this._receiveTextQueue = [];
}
@@ -1154,6 +1182,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();
}
@@ -1165,23 +1194,35 @@ 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');
- $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');
+ }
}
- 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');
@@ -1254,7 +1295,7 @@ class Base64ZipDialog extends Dialog {
}
_setPasteBtnToProcessing() {
- this.$pasteBtn.pointerEvents = "none";
+ this.$pasteBtn.style.pointerEvents = "none";
this.$pasteBtn.innerText = "Processing...";
}
@@ -1399,7 +1440,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 {
@@ -1460,7 +1501,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();
}
@@ -1471,17 +1512,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;
}
}
@@ -1837,8 +1877,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/service-worker.js b/public_included_ws_fallback/service-worker.js
index 9e968ec..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.1.3';
+const cacheVersion = 'v1.2.2';
const cacheTitle = `pairdrop-included-ws-fallback-cache-${cacheVersion}`;
const urlsToCache = [
'index.html',
diff --git a/public_included_ws_fallback/styles.css b/public_included_ws_fallback/styles.css
index de2d6f9..4b3cc83 100644
--- a/public_included_ws_fallback/styles.css
+++ b/public_included_ws_fallback/styles.css
@@ -613,7 +613,7 @@ x-dialog x-background {
z-index: 10;
transition: opacity 300ms;
will-change: opacity;
- padding: 35px;
+ padding: 15px;
overflow: overlay;
}
@@ -624,19 +624,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]) {
@@ -651,12 +652,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);
@@ -695,7 +690,7 @@ x-dialog .font-subheading {
}
#key-input-container>input:nth-of-type(4) {
- margin-left: 18px;
+ margin-left: 5%;
}
#room-key {
@@ -707,16 +702,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 {
@@ -730,29 +720,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 {
@@ -764,26 +749,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 */
@@ -791,14 +776,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 {
@@ -817,11 +802,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 {
@@ -849,7 +830,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;
@@ -860,6 +840,7 @@ x-dialog .row-reverse {
user-select: none;
background: inherit;
color: var(--primary-color);
+ overflow: hidden;
}
.button[disabled] {
@@ -897,7 +878,7 @@ x-dialog .row-reverse {
opacity: 0.1;
}
-#cancel-paste-mode-btn {
+#cancel-paste-mode {
z-index: 2;
margin: 0;
padding: 0;
@@ -924,7 +905,6 @@ button::-moz-focus-inner {
/* Icon Button */
-
.icon-button {
width: 40px;
height: 40px;
@@ -934,10 +914,7 @@ button::-moz-focus-inner {
border-radius: 50%;
}
-
-
/* Text Input */
-
.textarea {
box-sizing: border-box;
border: none;
@@ -951,9 +928,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;
}
@@ -1143,6 +1119,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 {
@@ -1215,7 +1199,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;