mirror of
https://github.com/schlagmichdoch/PairDrop.git
synced 2025-04-23 16:26:17 -04:00
- Enable renaming of own display name permanently via UI
- Make peerId completely ephemeral - Stabilize RTCConnection by closing connections cleanly
This commit is contained in:
parent
a3b348d9b6
commit
d56ee87437
9 changed files with 377 additions and 115 deletions
|
@ -8,6 +8,7 @@ class ServerConnection {
|
|||
constructor() {
|
||||
this._connect();
|
||||
Events.on('pagehide', _ => this._disconnect());
|
||||
Events.on('beforeunload', _ => this._onBeforeUnload());
|
||||
document.addEventListener('visibilitychange', _ => this._onVisibilityChange());
|
||||
if (navigator.connection) navigator.connection.addEventListener('change', _ => this._reconnect());
|
||||
Events.on('room-secrets', e => this._sendRoomSecrets(e.detail));
|
||||
|
@ -21,10 +22,10 @@ class ServerConnection {
|
|||
Events.on('online', _ => this._connect());
|
||||
}
|
||||
|
||||
async _connect() {
|
||||
_connect() {
|
||||
clearTimeout(this._reconnectTimer);
|
||||
if (this._isConnected() || this._isConnecting()) return;
|
||||
const ws = new WebSocket(await this._endpoint());
|
||||
const ws = new WebSocket(this._endpoint());
|
||||
ws.binaryType = 'arraybuffer';
|
||||
ws.onopen = _ => this._onOpen();
|
||||
ws.onmessage = e => this._onMessage(e.data);
|
||||
|
@ -109,45 +110,29 @@ class ServerConnection {
|
|||
}
|
||||
|
||||
_onDisplayName(msg) {
|
||||
sessionStorage.setItem("peerId", msg.message.peerId);
|
||||
PersistentStorage.get('peerId').then(peerId => {
|
||||
if (!peerId) {
|
||||
// save peerId to indexedDB to retrieve after PWA is installed
|
||||
PersistentStorage.set('peerId', msg.message.peerId).then(peerId => {
|
||||
console.log(`peerId saved to indexedDB: ${peerId}`);
|
||||
});
|
||||
}
|
||||
}).catch(_ => _ => PersistentStorage.logBrowserNotCapable())
|
||||
Events.fire('display-name', msg);
|
||||
}
|
||||
|
||||
async _endpoint() {
|
||||
_endpoint() {
|
||||
// hack to detect if deployment or development environment
|
||||
const protocol = location.protocol.startsWith('https') ? 'wss' : 'ws';
|
||||
const webrtc = window.isRtcSupported ? '/webrtc' : '/fallback';
|
||||
let ws_url = new URL(protocol + '://' + location.host + location.pathname + 'server' + webrtc);
|
||||
const peerId = await this._peerId();
|
||||
if (peerId) ws_url.searchParams.append('peer_id', peerId)
|
||||
return ws_url.toString();
|
||||
}
|
||||
|
||||
async _peerId() {
|
||||
// make peerId persistent when pwa is installed
|
||||
return window.matchMedia('(display-mode: minimal-ui)').matches
|
||||
? await PersistentStorage.get('peerId')
|
||||
: sessionStorage.getItem("peerId");
|
||||
}
|
||||
|
||||
_disconnect() {
|
||||
this.send({ type: 'disconnect' });
|
||||
_onBeforeUnload() {
|
||||
if (this._socket) {
|
||||
this._socket.onclose = null;
|
||||
this._socket.close();
|
||||
this._socket = null;
|
||||
Events.fire('ws-disconnected');
|
||||
}
|
||||
}
|
||||
|
||||
_disconnect() {
|
||||
this.send({ type: 'disconnect' });
|
||||
}
|
||||
|
||||
_onDisconnect() {
|
||||
console.log('WS: server disconnected');
|
||||
Events.fire('notify-user', 'No server connection. Retry in 5s...');
|
||||
|
@ -320,7 +305,6 @@ class Peer {
|
|||
return;
|
||||
}
|
||||
message = JSON.parse(message);
|
||||
console.log('RTC:', message);
|
||||
switch (message.type) {
|
||||
case 'request':
|
||||
this._onFilesTransferRequest(message);
|
||||
|
@ -349,6 +333,9 @@ class Peer {
|
|||
case 'text':
|
||||
this._onTextReceived(message);
|
||||
break;
|
||||
case 'display-name-changed':
|
||||
this._onDisplayNameChanged(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -486,6 +473,11 @@ class Peer {
|
|||
Events.fire('text-received', { text: escaped, peerId: this._peerId });
|
||||
this.sendJSON({ type: 'message-transfer-complete' });
|
||||
}
|
||||
|
||||
_onDisplayNameChanged(message) {
|
||||
if (!message.displayName) return;
|
||||
Events.fire('peer-display-name-changed', {peerId: this._peerId, displayName: message.displayName});
|
||||
}
|
||||
}
|
||||
|
||||
class RTCPeer extends Peer {
|
||||
|
@ -496,6 +488,13 @@ class RTCPeer extends Peer {
|
|||
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);
|
||||
|
||||
|
@ -558,14 +557,14 @@ class RTCPeer extends Peer {
|
|||
|
||||
_onChannelOpened(event) {
|
||||
console.log('RTC: channel opened with', this._peerId);
|
||||
Events.fire('peer-connected', {peerId: this._peerId, connectionHash: this.getConnectionHash()});
|
||||
const channel = event.channel || event.target;
|
||||
channel.binaryType = 'arraybuffer';
|
||||
channel.onmessage = e => this._onMessage(e.data);
|
||||
channel.onclose = _ => this._onChannelClosed();
|
||||
Events.on('beforeunload', e => this._onBeforeUnload(e));
|
||||
Events.on('pagehide', _ => this._closeChannel());
|
||||
channel.onclose = e => this._onChannelClosed(e);
|
||||
this._channel = channel;
|
||||
Events.on('beforeunload', e => this._onBeforeUnload(e));
|
||||
Events.on('pagehide', _ => this._onPageHide());
|
||||
Events.fire('peer-connected', {peerId: this._peerId, connectionHash: this.getConnectionHash()});
|
||||
}
|
||||
|
||||
getConnectionHash() {
|
||||
|
@ -598,13 +597,21 @@ class RTCPeer extends Peer {
|
|||
if (this._busy) {
|
||||
e.preventDefault();
|
||||
return "There are unfinished transfers. Are you sure you want to close?";
|
||||
} else {
|
||||
this._disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
_closeChannel() {
|
||||
if (this._channel) this._channel.onclose = null;
|
||||
if (this._conn) this._conn.close();
|
||||
this._conn = null;
|
||||
_onPageHide() {
|
||||
this._disconnect();
|
||||
}
|
||||
|
||||
_disconnect() {
|
||||
if (this._conn && this._channel) {
|
||||
this._channel.onclose = null;
|
||||
this._channel.close();
|
||||
}
|
||||
Events.fire('peer-disconnected', this._peerId);
|
||||
}
|
||||
|
||||
_onChannelClosed() {
|
||||
|
@ -618,9 +625,11 @@ class RTCPeer extends Peer {
|
|||
console.log('RTC: state changed:', this._conn.connectionState);
|
||||
switch (this._conn.connectionState) {
|
||||
case 'disconnected':
|
||||
Events.fire('peer-disconnected', this._peerId);
|
||||
this._onError('rtc connection disconnected');
|
||||
break;
|
||||
case 'failed':
|
||||
Events.fire('peer-disconnected', this._peerId);
|
||||
this._onError('rtc connection failed');
|
||||
break;
|
||||
}
|
||||
|
@ -679,8 +688,11 @@ class PeersManager {
|
|||
Events.on('respond-to-files-transfer-request', e => this._onRespondToFileTransferRequest(e.detail))
|
||||
Events.on('send-text', e => this._onSendText(e.detail));
|
||||
Events.on('peer-left', e => this._onPeerLeft(e.detail));
|
||||
Events.on('peer-connected', e => this._onPeerConnected(e.detail.peerId));
|
||||
Events.on('peer-disconnected', e => this._onPeerDisconnected(e.detail));
|
||||
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));
|
||||
}
|
||||
|
||||
_onMessage(message) {
|
||||
|
@ -704,10 +716,6 @@ class PeersManager {
|
|||
})
|
||||
}
|
||||
|
||||
sendTo(peerId, message) {
|
||||
this.peers[peerId].send(message);
|
||||
}
|
||||
|
||||
_onRespondToFileTransferRequest(detail) {
|
||||
this.peers[detail.to]._respondToFileTransferRequest(detail.accepted);
|
||||
}
|
||||
|
@ -739,6 +747,10 @@ class PeersManager {
|
|||
}
|
||||
}
|
||||
|
||||
_onPeerConnected(peerId) {
|
||||
this._notifyPeerDisplayNameChanged(peerId);
|
||||
}
|
||||
|
||||
_onPeerDisconnected(peerId) {
|
||||
const peer = this.peers[peerId];
|
||||
delete this.peers[peerId];
|
||||
|
@ -756,6 +768,23 @@ class PeersManager {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
_notifyPeersDisplayNameChanged(newDisplayName) {
|
||||
this._displayName = newDisplayName ? newDisplayName : this._originalDisplayName;
|
||||
for (const peerId in this.peers) {
|
||||
this._notifyPeerDisplayNameChanged(peerId);
|
||||
}
|
||||
}
|
||||
|
||||
_notifyPeerDisplayNameChanged(peerId) {
|
||||
const peer = this.peers[peerId];
|
||||
if (!peer || (peer._conn && (peer._conn.signalingState !== "stable" || !peer._channel || peer._channel.readyState !== "open"))) return;
|
||||
this.peers[peerId].sendJSON({type: 'display-name-changed', displayName: this._displayName});
|
||||
}
|
||||
|
||||
_onDisplayName(displayName) {
|
||||
this._originalDisplayName = displayName;
|
||||
}
|
||||
}
|
||||
|
||||
class FileChunker {
|
||||
|
|
|
@ -10,8 +10,8 @@ window.pasteMode.activated = false;
|
|||
// set display name
|
||||
Events.on('display-name', e => {
|
||||
const me = e.detail.message;
|
||||
const $displayName = $('display-name')
|
||||
$displayName.textContent = 'You are known as ' + me.displayName;
|
||||
const $displayName = $('display-name');
|
||||
$displayName.setAttribute('placeholder', me.displayName);
|
||||
$displayName.title = me.deviceName;
|
||||
});
|
||||
|
||||
|
@ -44,6 +44,61 @@ class PeersUI {
|
|||
|
||||
Events.on('peer-added', _ => this.evaluateOverflowing());
|
||||
Events.on('bg-resize', _ => this.evaluateOverflowing());
|
||||
|
||||
this.$displayName = $('display-name');
|
||||
|
||||
this.$displayName.addEventListener('keydown', e => this._onKeyDownDisplayName(e));
|
||||
this.$displayName.addEventListener('keyup', e => this._onKeyUpDisplayName(e));
|
||||
this.$displayName.addEventListener('blur', e => this._saveDisplayName(e.target.innerText));
|
||||
|
||||
Events.on('self-display-name-changed', e => this._insertDisplayName(e.detail));
|
||||
Events.on('peer-display-name-changed', e => this._changePeerDisplayName(e.detail.peerId, e.detail.displayName));
|
||||
|
||||
// Load saved display name
|
||||
PersistentStorage.get('editedDisplayName').then(displayName => {
|
||||
console.log("Retrieved edited display name:", displayName)
|
||||
if (displayName) Events.fire('self-display-name-changed', displayName);
|
||||
});
|
||||
}
|
||||
|
||||
_insertDisplayName(displayName) {
|
||||
this.$displayName.textContent = displayName;
|
||||
}
|
||||
|
||||
_onKeyDownDisplayName(e) {
|
||||
if (e.key === "Enter" || e.key === "Escape") {
|
||||
e.preventDefault();
|
||||
e.target.blur();
|
||||
}
|
||||
}
|
||||
|
||||
_onKeyUpDisplayName(e) {
|
||||
if (/(\n|\r|\r\n)/.test(e.target.innerText)) e.target.innerText = e.target.innerText.replace(/(\n|\r|\r\n)/, '');
|
||||
}
|
||||
|
||||
async _saveDisplayName(newDisplayName) {
|
||||
const savedDisplayName = await PersistentStorage.get('editedDisplayName') ?? "";
|
||||
if (newDisplayName === savedDisplayName) return;
|
||||
|
||||
if (newDisplayName) {
|
||||
PersistentStorage.set('editedDisplayName', newDisplayName).then(_ => {
|
||||
Events.fire('notify-user', `Display name is set permanently.`);
|
||||
Events.fire('self-display-name-changed', newDisplayName);
|
||||
Events.fire('broadcast-send', {type: 'self-display-name-changed', detail: newDisplayName});
|
||||
});
|
||||
} else {
|
||||
PersistentStorage.delete('editedDisplayName').then(_ => {
|
||||
Events.fire('notify-user', 'Display name is randomly generated again.');
|
||||
Events.fire('self-display-name-changed', '');
|
||||
Events.fire('broadcast-send', {type: 'self-display-name-changed', detail: ''});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_changePeerDisplayName(peerId, displayName) {
|
||||
this.peers[peerId].name.displayName = displayName;
|
||||
const peerIdNode = $(peerId);
|
||||
if (peerIdNode && displayName) peerIdNode.querySelector('.name').textContent = displayName;
|
||||
}
|
||||
|
||||
_onKeyDown(e) {
|
||||
|
@ -520,6 +575,7 @@ class ReceiveFileDialog extends ReceiveDialog {
|
|||
}
|
||||
|
||||
_dequeueFile() {
|
||||
// Todo: change count in document.title and move '- PairDrop' to back
|
||||
if (!this._filesQueue.length) { // nothing to do
|
||||
this._busy = false;
|
||||
return;
|
||||
|
@ -661,7 +717,7 @@ class ReceiveRequestDialog extends ReceiveDialog {
|
|||
constructor() {
|
||||
super('receive-request-dialog');
|
||||
|
||||
this.$requestingPeerDisplayNameNode = this.$el.querySelector('#requesting-peer-display-name');
|
||||
this.$requestingPeerDisplayNameNode = this.$el.querySelector('#receive-request-dialog .display-name');
|
||||
this.$fileStemNode = this.$el.querySelector('#file-stem');
|
||||
this.$fileExtensionNode = this.$el.querySelector('#file-extension');
|
||||
this.$fileOtherNode = this.$el.querySelector('#file-other');
|
||||
|
@ -991,7 +1047,7 @@ class SendTextDialog extends Dialog {
|
|||
super('send-text-dialog');
|
||||
Events.on('text-recipient', e => this._onRecipient(e.detail.peerId, e.detail.deviceName));
|
||||
this.$text = this.$el.querySelector('#text-input');
|
||||
this.$peerDisplayName = this.$el.querySelector('#text-send-peer-display-name');
|
||||
this.$peerDisplayName = this.$el.querySelector('#send-text-dialog .display-name');
|
||||
this.$form = this.$el.querySelector('form');
|
||||
this.$submit = this.$el.querySelector('button[type="submit"]');
|
||||
this.$form.addEventListener('submit', _ => this._send());
|
||||
|
@ -1059,7 +1115,7 @@ class ReceiveTextDialog extends Dialog {
|
|||
|
||||
Events.on("keydown", e => this._onKeyDown(e));
|
||||
|
||||
this.$receiveTextPeerDisplayNameNode = this.$el.querySelector('#receive-text-peer-display-name');
|
||||
this.$receiveTextPeerDisplayNameNode = this.$el.querySelector('#receive-text-dialog .display-name');
|
||||
this._receiveTextQueue = [];
|
||||
}
|
||||
|
||||
|
@ -1683,6 +1739,23 @@ class PersistentStorage {
|
|||
}
|
||||
}
|
||||
|
||||
class Broadcast {
|
||||
constructor() {
|
||||
this.bc = new BroadcastChannel('pairdrop');
|
||||
this.bc.addEventListener('message', e => this._onMessage(e));
|
||||
Events.on('broadcast-send', e => this._broadcastMessage(e.detail));
|
||||
}
|
||||
|
||||
_broadcastMessage(message) {
|
||||
this.bc.postMessage(message);
|
||||
}
|
||||
|
||||
_onMessage(e) {
|
||||
console.log('Broadcast message received:', e.data)
|
||||
Events.fire(e.data.type, e.data.detail);
|
||||
}
|
||||
}
|
||||
|
||||
class PairDrop {
|
||||
constructor() {
|
||||
Events.on('load', _ => {
|
||||
|
@ -1702,6 +1775,7 @@ class PairDrop {
|
|||
const webShareTargetUI = new WebShareTargetUI();
|
||||
const webFileHandlersUI = new WebFileHandlersUI();
|
||||
const noSleepUI = new NoSleepUI();
|
||||
const broadCast = new Broadcast();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue