Compare commits

...

8 Commits

Author SHA1 Message Date
schlagmichdoch fea15d3ee1 increase version to v1.4.5 2023-03-13 00:05:57 +01:00
schlagmichdoch 028752a809 fixes #76. 'File received' dialog not showing on iOS when big videos are sent. 2023-03-13 00:04:48 +01:00
schlagmichdoch 1093f4d246 log error onicecandidateerror 2023-03-10 22:21:19 +01:00
schlagmichdoch 7ddd600b0c fix display name hidden on Firefox for Android 2023-03-10 20:01:59 +01:00
schlagmichdoch 715356aafb Fix AirDrop typo 2023-03-08 11:35:37 +01:00
schlagmichdoch 490e4db380 Add information about specifying TURN servers 2023-03-07 18:25:25 +01:00
schlagmichdoch 11a988e550 increase version to v1.4.4 2023-03-06 16:05:58 +01:00
schlagmichdoch ff8f28660a prevent buttons from submitting form by adding type="button" 2023-03-06 16:03:34 +01:00
14 changed files with 125 additions and 74 deletions
+1 -1
View File
@@ -6,7 +6,7 @@
<h1>PairDrop</h1> <h1>PairDrop</h1>
<p> <p>
Local file sharing in your browser. Inspired by Apple's Airdrop. Local file sharing in your browser. Inspired by Apple's AirDrop.
<br /> <br />
<a href="https://pairdrop.net"><strong>Explore »</strong></a> <a href="https://pairdrop.net"><strong>Explore »</strong></a>
<br /> <br />
+5 -2
View File
@@ -58,8 +58,11 @@ If your devices are paired and behind a NAT, the public TURN Server from [Open R
Yes. Your files are sent using WebRTC, which encrypts them on transit. To ensure the connection is secure and there is no MITM, compare the security number shown under the device name on both devices. The security number is different for every connection. Yes. Your files are sent using WebRTC, which encrypts them on transit. To ensure the connection is secure and there is no MITM, compare the security number shown under the device name on both devices. The security number is different for every connection.
### Transferring many files with paired devices takes too long ### Transferring many files with paired devices takes too long
Naturally, if traffic needs to be routed through the turn server transfer speed decreases. Naturally, if traffic needs to be routed through the turn server because your devices are behind different NATs, transfer speed decreases.
As a workaround you can open a hotspot on one of your devices to bridge the connection which makes transfers much faster.
As the public TURN server used is not super fast, you can easily [specify to use your own TURN server](https://github.com/schlagmichdoch/PairDrop/blob/master/docs/host-your-own.md#specify-stunturn-servers) if you host your own instance.
Alternatively, you can open a hotspot on one of your devices to bridge the connection which makes transfers much faster as no TURN server is needed.
- [How to open a hotspot on Windows](https://support.microsoft.com/en-us/windows/use-your-windows-pc-as-a-mobile-hotspot-c89b0fad-72d5-41e8-f7ea-406ad9036b85#WindowsVersion=Windows_11) - [How to open a hotspot on Windows](https://support.microsoft.com/en-us/windows/use-your-windows-pc-as-a-mobile-hotspot-c89b0fad-72d5-41e8-f7ea-406ad9036b85#WindowsVersion=Windows_11)
- [How to open a hotspot on Mac](https://support.apple.com/guide/mac-help/share-internet-connection-mac-network-users-mchlp1540/mac) - [How to open a hotspot on Mac](https://support.apple.com/guide/mac-help/share-internet-connection-mac-network-users-mchlp1540/mac)
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "pairdrop", "name": "pairdrop",
"version": "1.4.3", "version": "1.4.5",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "pairdrop", "name": "pairdrop",
"version": "1.4.3", "version": "1.4.5",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"express": "^4.18.2", "express": "^4.18.2",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "pairdrop", "name": "pairdrop",
"version": "1.4.3", "version": "1.4.5",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
+3 -3
View File
@@ -122,7 +122,7 @@
<div class="font-subheading center text-center">Enter key from another device to continue.</div> <div class="font-subheading center text-center">Enter key from another device to continue.</div>
<div class="center row-reverse"> <div class="center row-reverse">
<button class="button" type="submit" disabled>Pair</button> <button class="button" type="submit" disabled>Pair</button>
<button class="button" close>Cancel</button> <button class="button" type="button" close>Cancel</button>
</div> </div>
</x-paper> </x-paper>
</x-background> </x-background>
@@ -137,7 +137,7 @@
<div class="font-subheading center text-center">Are you sure to unpair all devices?</div> <div class="font-subheading center text-center">Are you sure to unpair all devices?</div>
<div class="center row-reverse"> <div class="center row-reverse">
<button class="button" type="submit">Unpair Devices</button> <button class="button" type="submit">Unpair Devices</button>
<button class="button" close>Cancel</button> <button class="button" type="button" close>Cancel</button>
</div> </div>
</x-paper> </x-paper>
</x-background> </x-background>
@@ -209,7 +209,7 @@
<div id="text-input" class="textarea" role="textbox" autocapitalize="none" spellcheck="false" autofocus contenteditable></div> <div id="text-input" class="textarea" role="textbox" autocapitalize="none" spellcheck="false" autofocus contenteditable></div>
<div class="center row-reverse"> <div class="center row-reverse">
<button class="button" type="submit" title="STR + ENTER" disabled close>Send</button> <button class="button" type="submit" title="STR + ENTER" disabled close>Send</button>
<button class="button" title="ESCAPE" close>Cancel</button> <button class="button" type="button" title="ESCAPE" close>Cancel</button>
</div> </div>
</x-paper> </x-paper>
</x-background> </x-background>
+1
View File
@@ -524,6 +524,7 @@ class RTCPeer extends Peer {
this._peerId = peerId; this._peerId = peerId;
this._conn = new RTCPeerConnection(window.rtcConfig); this._conn = new RTCPeerConnection(window.rtcConfig);
this._conn.onicecandidate = e => this._onIceCandidate(e); this._conn.onicecandidate = e => this._onIceCandidate(e);
this._conn.onicecandidateerror = e => this._onError(e);
this._conn.onconnectionstatechange = _ => this._onConnectionStateChange(); this._conn.onconnectionstatechange = _ => this._onConnectionStateChange();
this._conn.oniceconnectionstatechange = e => this._onIceConnectionStateChange(e); this._conn.oniceconnectionstatechange = e => this._onIceConnectionStateChange(e);
} }
+45 -27
View File
@@ -632,26 +632,34 @@ class ReceiveFileDialog extends ReceiveDialog {
createPreviewElement(file) { createPreviewElement(file) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let mime = file.type.split('/')[0] try {
let previewElement = { let mime = file.type.split('/')[0]
image: 'img', let previewElement = {
audio: 'audio', image: 'img',
video: 'video' audio: 'audio',
} video: 'video'
}
if (Object.keys(previewElement).indexOf(mime) === -1) { if (Object.keys(previewElement).indexOf(mime) === -1) {
resolve(false); resolve(false);
} else { } else {
console.log('the file is able to preview'); let element = document.createElement(previewElement[mime]);
let element = document.createElement(previewElement[mime]); element.controls = true;
element.src = URL.createObjectURL(file); element.onload = _ => {
element.controls = true; this.$previewBox.appendChild(element);
element.onload = _ => { resolve(true);
this.$previewBox.appendChild(element); };
resolve(true) element.onloadeddata = _ => {
}; this.$previewBox.appendChild(element);
element.addEventListener('loadeddata', _ => resolve(true)); resolve(true);
element.onerror = _ => reject(`${mime} preview could not be loaded from type ${file.type}`); };
element.onerror = _ => {
reject(`${mime} preview could not be loaded from type ${file.type}`);
};
element.src = URL.createObjectURL(file);
}
} catch (e) {
reject(`preview could not be loaded from type ${file.type}`);
} }
}); });
} }
@@ -734,20 +742,30 @@ class ReceiveFileDialog extends ReceiveDialog {
setTimeout(_ => this.$downloadBtn.style.pointerEvents = "unset", 2000); setTimeout(_ => this.$downloadBtn.style.pointerEvents = "unset", 2000);
}; };
this.createPreviewElement(files[0]).finally(_ => { document.title = files.length === 1
document.title = files.length === 1 ? 'File received - PairDrop'
? 'File received - PairDrop' : `${files.length} Files received - PairDrop`;
: `${files.length} Files received - PairDrop`; document.changeFavicon("images/favicon-96x96-notification.png");
document.changeFavicon("images/favicon-96x96-notification.png"); Events.fire('set-progress', {peerId: peerId, progress: 1, status: 'process'})
Events.fire('set-progress', {peerId: peerId, progress: 1, status: 'process'}) this.show();
this.show();
setTimeout(_ => {
if (canShare) { if (canShare) {
this.$shareBtn.click(); this.$shareBtn.click();
} else { } else {
this.$downloadBtn.click(); this.$downloadBtn.click();
} }
}).catch(r => console.error(r)); }, 500);
this.createPreviewElement(files[0])
.then(canPreview => {
if (canPreview) {
console.log('the file is able to preview');
} else {
console.log('the file is not able to preview');
}
})
.catch(r => console.error(r));
} }
_downloadFilesIndividually(files) { _downloadFilesIndividually(files) {
+1 -1
View File
@@ -1,4 +1,4 @@
const cacheVersion = 'v1.4.3'; const cacheVersion = 'v1.4.5';
const cacheTitle = `pairdrop-cache-${cacheVersion}`; const cacheTitle = `pairdrop-cache-${cacheVersion}`;
const urlsToCache = [ const urlsToCache = [
'index.html', 'index.html',
+8 -3
View File
@@ -22,13 +22,18 @@ body {
} }
body { body {
min-height: 100vh; height: 100%;
/* mobile viewport bug fix */ /* mobile viewport bug fix */
min-height: -webkit-fill-available; min-height: -moz-available; /* WebKit-based browsers will ignore this. */
min-height: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
min-height: fill-available;
} }
html { html {
height: -webkit-fill-available; height: 100%;
min-height: -moz-available; /* WebKit-based browsers will ignore this. */
min-height: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
min-height: fill-available;
} }
.row-reverse { .row-reverse {
+3 -3
View File
@@ -125,7 +125,7 @@
<div class="font-subheading center text-center">Enter key from another device to continue.</div> <div class="font-subheading center text-center">Enter key from another device to continue.</div>
<div class="center row-reverse"> <div class="center row-reverse">
<button class="button" type="submit" disabled>Pair</button> <button class="button" type="submit" disabled>Pair</button>
<button class="button" close>Cancel</button> <button class="button" type="button" close>Cancel</button>
</div> </div>
</x-paper> </x-paper>
</x-background> </x-background>
@@ -140,7 +140,7 @@
<div class="font-subheading center text-center">Are you sure to unpair all devices?</div> <div class="font-subheading center text-center">Are you sure to unpair all devices?</div>
<div class="center row-reverse"> <div class="center row-reverse">
<button class="button" type="submit">Unpair Devices</button> <button class="button" type="submit">Unpair Devices</button>
<button class="button" close>Cancel</button> <button class="button" type="button" close>Cancel</button>
</div> </div>
</x-paper> </x-paper>
</x-background> </x-background>
@@ -212,7 +212,7 @@
<div id="text-input" class="textarea" role="textbox" autocapitalize="none" spellcheck="false" autofocus contenteditable></div> <div id="text-input" class="textarea" role="textbox" autocapitalize="none" spellcheck="false" autofocus contenteditable></div>
<div class="center row-reverse"> <div class="center row-reverse">
<button class="button" type="submit" title="STR + ENTER" disabled close>Send</button> <button class="button" type="submit" title="STR + ENTER" disabled close>Send</button>
<button class="button" title="ESCAPE" close>Cancel</button> <button class="button" type="button" title="ESCAPE" close>Cancel</button>
</div> </div>
</x-paper> </x-paper>
</x-background> </x-background>
@@ -535,6 +535,7 @@ class RTCPeer extends Peer {
this._peerId = peerId; this._peerId = peerId;
this._conn = new RTCPeerConnection(window.rtcConfig); this._conn = new RTCPeerConnection(window.rtcConfig);
this._conn.onicecandidate = e => this._onIceCandidate(e); this._conn.onicecandidate = e => this._onIceCandidate(e);
this._conn.onicecandidateerror = e => this._onError(e);
this._conn.onconnectionstatechange = _ => this._onConnectionStateChange(); this._conn.onconnectionstatechange = _ => this._onConnectionStateChange();
this._conn.oniceconnectionstatechange = e => this._onIceConnectionStateChange(e); this._conn.oniceconnectionstatechange = e => this._onIceConnectionStateChange(e);
} }
+45 -27
View File
@@ -633,26 +633,34 @@ class ReceiveFileDialog extends ReceiveDialog {
createPreviewElement(file) { createPreviewElement(file) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let mime = file.type.split('/')[0] try {
let previewElement = { let mime = file.type.split('/')[0]
image: 'img', let previewElement = {
audio: 'audio', image: 'img',
video: 'video' audio: 'audio',
} video: 'video'
}
if (Object.keys(previewElement).indexOf(mime) === -1) { if (Object.keys(previewElement).indexOf(mime) === -1) {
resolve(false); resolve(false);
} else { } else {
console.log('the file is able to preview'); let element = document.createElement(previewElement[mime]);
let element = document.createElement(previewElement[mime]); element.controls = true;
element.src = URL.createObjectURL(file); element.onload = _ => {
element.controls = true; this.$previewBox.appendChild(element);
element.onload = _ => { resolve(true);
this.$previewBox.appendChild(element); };
resolve(true) element.onloadeddata = _ => {
}; this.$previewBox.appendChild(element);
element.addEventListener('loadeddata', _ => resolve(true)); resolve(true);
element.onerror = _ => reject(`${mime} preview could not be loaded from type ${file.type}`); };
element.onerror = _ => {
reject(`${mime} preview could not be loaded from type ${file.type}`);
};
element.src = URL.createObjectURL(file);
}
} catch (e) {
reject(`preview could not be loaded from type ${file.type}`);
} }
}); });
} }
@@ -735,20 +743,30 @@ class ReceiveFileDialog extends ReceiveDialog {
setTimeout(_ => this.$downloadBtn.style.pointerEvents = "unset", 2000); setTimeout(_ => this.$downloadBtn.style.pointerEvents = "unset", 2000);
}; };
this.createPreviewElement(files[0]).finally(_ => { document.title = files.length === 1
document.title = files.length === 1 ? 'File received - PairDrop'
? 'File received - PairDrop' : `${files.length} Files received - PairDrop`;
: `${files.length} Files received - PairDrop`; document.changeFavicon("images/favicon-96x96-notification.png");
document.changeFavicon("images/favicon-96x96-notification.png"); Events.fire('set-progress', {peerId: peerId, progress: 1, status: 'process'})
Events.fire('set-progress', {peerId: peerId, progress: 1, status: 'process'}) this.show();
this.show();
setTimeout(_ => {
if (canShare) { if (canShare) {
this.$shareBtn.click(); this.$shareBtn.click();
} else { } else {
this.$downloadBtn.click(); this.$downloadBtn.click();
} }
}).catch(r => console.error(r)); }, 500);
this.createPreviewElement(files[0])
.then(canPreview => {
if (canPreview) {
console.log('the file is able to preview');
} else {
console.log('the file is not able to preview');
}
})
.catch(r => console.error(r));
} }
_downloadFilesIndividually(files) { _downloadFilesIndividually(files) {
@@ -1,4 +1,4 @@
const cacheVersion = 'v1.4.3'; const cacheVersion = 'v1.4.5';
const cacheTitle = `pairdrop-included-ws-fallback-cache-${cacheVersion}`; const cacheTitle = `pairdrop-included-ws-fallback-cache-${cacheVersion}`;
const urlsToCache = [ const urlsToCache = [
'index.html', 'index.html',
+8 -3
View File
@@ -23,13 +23,18 @@ body {
} }
body { body {
min-height: 100vh; height: 100%;
/* mobile viewport bug fix */ /* mobile viewport bug fix */
min-height: -webkit-fill-available; min-height: -moz-available; /* WebKit-based browsers will ignore this. */
min-height: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
min-height: fill-available;
} }
html { html {
height: -webkit-fill-available; height: 100%;
min-height: -moz-available; /* WebKit-based browsers will ignore this. */
min-height: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
min-height: fill-available;
} }
.row-reverse { .row-reverse {