mirror of
https://github.com/schlagmichdoch/PairDrop.git
synced 2026-04-13 21:02:15 +00:00
Compare commits
2 Commits
domain_spe
...
add_turn_i
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
880e8bd195 | ||
|
|
5a56251ee3 |
@@ -737,6 +737,10 @@
|
||||
<!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.-->
|
||||
<path d="M256 0c4.6 0 9.2 1 13.4 2.9L457.7 82.8c22 9.3 38.4 31 38.3 57.2c-.5 99.2-41.3 280.7-213.6 363.2c-16.7 8-36.1 8-52.8 0C57.3 420.7 16.5 239.2 16 140c-.1-26.2 16.3-47.9 38.3-57.2L242.7 2.9C246.8 1 251.4 0 256 0zm0 66.8V444.8C394 378 431.1 230.1 432 141.4L256 66.8l0 0z"></path>
|
||||
</symbol>
|
||||
<symbol id="turn-indicator" viewBox="0 0 576 512">
|
||||
<!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->
|
||||
<path d="M0 80C0 53.5 21.5 32 48 32h96c26.5 0 48 21.5 48 48V96H384V80c0-26.5 21.5-48 48-48h96c26.5 0 48 21.5 48 48v96c0 26.5-21.5 48-48 48H432c-26.5 0-48-21.5-48-48V160H192v16c0 1.7-.1 3.4-.3 5L272 288h96c26.5 0 48 21.5 48 48v96c0 26.5-21.5 48-48 48H272c-26.5 0-48-21.5-48-48V336c0-1.7 .1-3.4 .3-5L144 224H48c-26.5 0-48-21.5-48-48V80z"></path>
|
||||
</symbol>
|
||||
</svg>
|
||||
<!-- Scripts -->
|
||||
<script src="scripts/localization.js" defer></script>
|
||||
|
||||
@@ -178,6 +178,7 @@
|
||||
"click-to-send-share-mode": "Click to send {{descriptor}}",
|
||||
"click-to-send": "Click to send files or right click to send a message",
|
||||
"connection-hash": "To verify the security of the end-to-end encryption, compare this security number on both devices",
|
||||
"turn-indicator": "Transfer over the Internet",
|
||||
"connecting": "Connecting…",
|
||||
"preparing": "Preparing…",
|
||||
"waiting": "Waiting…",
|
||||
|
||||
@@ -118,8 +118,8 @@ class PairDrop {
|
||||
this.$headerNotificationBtn.removeAttribute('hidden');
|
||||
}
|
||||
|
||||
let roomSecretsCount = await PersistentStorage.getAllRoomSecretsCount();
|
||||
if (roomSecretsCount > 0) {
|
||||
let roomSecrets = await PersistentStorage.getAllRoomSecrets();
|
||||
if (roomSecrets.length > 0) {
|
||||
this.$headerEditPairedDevicesBtn.removeAttribute('hidden');
|
||||
this.$footerPairedDevicesBadge.removeAttribute('hidden');
|
||||
}
|
||||
|
||||
@@ -8,13 +8,13 @@ class ServerConnection {
|
||||
navigator.connection.addEventListener('change', _ => this._reconnect());
|
||||
}
|
||||
|
||||
Events.on('join-ip-room', _ => this._sendJoinIpRoom());
|
||||
Events.on('room-secrets', e => this._sendRoomSecrets(e.detail));
|
||||
Events.on('room-secrets-deleted', e => this._sendRoomSecretsDeleted(e.detail));
|
||||
Events.on('regenerate-room-secret', e => this._sendRegenerateRoomSecret(e.detail));
|
||||
Events.on('pair-device-initiate', _ => this._sendPairDeviceInitiate());
|
||||
Events.on('pair-device-join', e => this._sendPairDeviceJoin(e.detail));
|
||||
Events.on('pair-device-cancel', _ => this._sendPairDeviceCancel());
|
||||
Events.on('room-secrets', e => this.send({ type: 'room-secrets', roomSecrets: e.detail }));
|
||||
Events.on('join-ip-room', _ => this.send({ type: 'join-ip-room'}));
|
||||
Events.on('room-secrets-deleted', e => this.send({ type: 'room-secrets-deleted', roomSecrets: e.detail}));
|
||||
Events.on('regenerate-room-secret', e => this.send({ type: 'regenerate-room-secret', roomSecret: e.detail}));
|
||||
Events.on('pair-device-initiate', _ => this._onPairDeviceInitiate());
|
||||
Events.on('pair-device-join', e => this._onPairDeviceJoin(e.detail));
|
||||
Events.on('pair-device-cancel', _ => this.send({ type: 'pair-device-cancel' }));
|
||||
|
||||
Events.on('create-public-room', _ => this._onCreatePublicRoom());
|
||||
Events.on('join-public-room', e => this._onJoinPublicRoom(e.detail.roomId, e.detail.createIfInvalid));
|
||||
@@ -93,23 +93,7 @@ class ServerConnection {
|
||||
}
|
||||
}
|
||||
|
||||
_sendJoinIpRoom() {
|
||||
this.send({ type: 'join-ip-room'});
|
||||
}
|
||||
|
||||
_sendRoomSecrets(roomSecrets) {
|
||||
this.send({ type: 'room-secrets', roomSecrets: roomSecrets });
|
||||
}
|
||||
|
||||
_sendRoomSecretsDeleted(roomSecrets) {
|
||||
this.send({ type: 'room-secrets-deleted', roomSecrets: roomSecrets});
|
||||
}
|
||||
|
||||
_sendRegenerateRoomSecret(roomSecret) {
|
||||
this.send({ type: 'regenerate-room-secret', roomSecret: roomSecret});
|
||||
}
|
||||
|
||||
_sendPairDeviceInitiate() {
|
||||
_onPairDeviceInitiate() {
|
||||
if (!this._isConnected()) {
|
||||
Events.fire('notify-user', Localization.getTranslation("notifications.online-requirement-pairing"));
|
||||
return;
|
||||
@@ -117,19 +101,15 @@ class ServerConnection {
|
||||
this.send({ type: 'pair-device-initiate' });
|
||||
}
|
||||
|
||||
_sendPairDeviceJoin(pairKey) {
|
||||
_onPairDeviceJoin(pairKey) {
|
||||
if (!this._isConnected()) {
|
||||
// Todo: instead use pending outbound ws queue
|
||||
setTimeout(() => this._sendPairDeviceJoin(pairKey), 1000);
|
||||
setTimeout(() => this._onPairDeviceJoin(pairKey), 1000);
|
||||
return;
|
||||
}
|
||||
this.send({ type: 'pair-device-join', pairKey: pairKey });
|
||||
}
|
||||
|
||||
_sendPairDeviceCancel() {
|
||||
this.send({ type: 'pair-device-cancel' });
|
||||
}
|
||||
|
||||
_onCreatePublicRoom() {
|
||||
if (!this._isConnected()) {
|
||||
Events.fire('notify-user', Localization.getTranslation("notifications.online-requirement-public-room"));
|
||||
@@ -1049,6 +1029,33 @@ class RTCPeer extends Peer {
|
||||
);
|
||||
}
|
||||
|
||||
async _connectionType() {
|
||||
if (!this._conn) return "";
|
||||
|
||||
const stats = await this._conn.getStats(null);
|
||||
|
||||
let id;
|
||||
stats.forEach((report) => {
|
||||
if (report.type === "candidate-pair" && report.state === "succeeded") {
|
||||
id = report.localCandidateId;
|
||||
}
|
||||
});
|
||||
|
||||
if (!id) return "";
|
||||
|
||||
let connectionType;
|
||||
stats.forEach((report) => {
|
||||
if (report.id === id) {
|
||||
connectionType = report.candidateType;
|
||||
}
|
||||
});
|
||||
return connectionType;
|
||||
}
|
||||
|
||||
async _isTurn() {
|
||||
return await this._connectionType() === "relay";
|
||||
}
|
||||
|
||||
_messageChannelOpen() {
|
||||
return this._messageChannel && this._messageChannel.readyState === 'open';
|
||||
}
|
||||
@@ -1178,13 +1185,13 @@ class RTCPeer extends Peer {
|
||||
return channel;
|
||||
}
|
||||
|
||||
_onChannelOpened(e) {
|
||||
async _onChannelOpened(e) {
|
||||
Logger.debug(`RTC: Channel ${e.target.label} opened with`, this._peerId);
|
||||
|
||||
// wait until all channels are open
|
||||
if (!this._stable()) return;
|
||||
|
||||
Events.fire('peer-connected', {peerId: this._peerId, connectionHash: this.getConnectionHash()});
|
||||
Events.fire('peer-connected', {peerId: this._peerId, connectionHash: this.getConnectionHash(), connectionType: await this._connectionType()});
|
||||
super._onPeerConnected();
|
||||
|
||||
this._sendPendingOutboundMessaged();
|
||||
|
||||
@@ -4,7 +4,7 @@ class PersistentStorage {
|
||||
PersistentStorage.logBrowserNotCapable();
|
||||
return;
|
||||
}
|
||||
const DBOpenRequest = window.indexedDB.open('pairdrop_store', 6);
|
||||
const DBOpenRequest = window.indexedDB.open('pairdrop_store', 5);
|
||||
DBOpenRequest.onerror = e => {
|
||||
PersistentStorage.logBrowserNotCapable();
|
||||
Logger.error('Error initializing database:', e);
|
||||
@@ -49,28 +49,6 @@ class PersistentStorage {
|
||||
await PersistentStorage.delete('editedDisplayName');
|
||||
}
|
||||
}
|
||||
if (e.oldVersion <= 5) {
|
||||
// migrate to v6
|
||||
let roomSecretsObjectStore5 = txn.objectStore('room_secrets');
|
||||
roomSecretsObjectStore5.createIndex('ws_domain', 'ws_domain');
|
||||
// add current ws_domain to existing peer secret entries once the config has loaded
|
||||
Events.on('config-loaded', _ => PersistentStorage.addCurrentWsDomainToAllRoomSecrets(), { once: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static getCurrentWsDomain() {
|
||||
return window._config && window._config.signalingServer
|
||||
? window._config.signalingServer
|
||||
: location.host + location.pathname;
|
||||
}
|
||||
|
||||
static async addCurrentWsDomainToAllRoomSecrets() {
|
||||
const wsServerDomain = this.getCurrentWsDomain();
|
||||
|
||||
const roomSecrets = await PersistentStorage.getAllRoomSecrets(false);
|
||||
for (let i = 0; i < roomSecrets.length; i++) {
|
||||
await PersistentStorage.updateRoomSecret(roomSecrets[i], null, null, null, null, wsServerDomain);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,8 +124,7 @@ class PersistentStorage {
|
||||
'secret': roomSecret,
|
||||
'display_name': displayName,
|
||||
'device_name': deviceName,
|
||||
'auto_accept': false,
|
||||
'ws_domain': PersistentStorage.getCurrentWsDomain()
|
||||
'auto_accept': false
|
||||
});
|
||||
objectStoreRequest.onsuccess = e => {
|
||||
Logger.debug(`Request successful. RoomSecret added: ${e.target.result}`);
|
||||
@@ -160,28 +137,22 @@ class PersistentStorage {
|
||||
})
|
||||
}
|
||||
|
||||
static async getAllRoomSecretsCount(currentWsDomainOnly = true) {
|
||||
return (await PersistentStorage.getAllRoomSecrets(currentWsDomainOnly)).length;
|
||||
}
|
||||
|
||||
static async getAllRoomSecrets(currentWsDomainOnly = true) {
|
||||
let secrets = [];
|
||||
static async getAllRoomSecrets() {
|
||||
try {
|
||||
const roomSecrets = await this.getAllRoomSecretEntries(currentWsDomainOnly);
|
||||
|
||||
secrets = roomSecrets.map(roomSecret => roomSecret.secret);
|
||||
|
||||
const roomSecrets = await this.getAllRoomSecretEntries();
|
||||
let secrets = [];
|
||||
for (let i = 0; i < roomSecrets.length; i++) {
|
||||
secrets.push(roomSecrets[i].secret);
|
||||
}
|
||||
Logger.debug(`Request successful. Retrieved ${secrets.length} room_secrets`);
|
||||
}
|
||||
catch (e) {
|
||||
console.debug(e)
|
||||
return(secrets);
|
||||
} catch (e) {
|
||||
this.logBrowserNotCapable();
|
||||
return [];
|
||||
}
|
||||
|
||||
return secrets;
|
||||
}
|
||||
|
||||
static getAllRoomSecretEntries(currentWsDomainOnly = true) {
|
||||
static getAllRoomSecretEntries() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const DBOpenRequest = window.indexedDB.open('pairdrop_store');
|
||||
DBOpenRequest.onsuccess = (e) => {
|
||||
@@ -190,19 +161,7 @@ class PersistentStorage {
|
||||
const objectStore = transaction.objectStore('room_secrets');
|
||||
const objectStoreRequest = objectStore.getAll();
|
||||
objectStoreRequest.onsuccess = e => {
|
||||
let roomSecrets = e.target.result;
|
||||
let roomSecretEntries = [];
|
||||
|
||||
for (let i = 0; i < roomSecrets.length; i++) {
|
||||
const currentWsDomainDiffers = roomSecrets[i].ws_domain !== PersistentStorage.getCurrentWsDomain();
|
||||
|
||||
// if the saved ws domain differs from the current ws domain and only peers for the current ws domain should be returned -> skip this entry
|
||||
if (currentWsDomainOnly && currentWsDomainDiffers) continue;
|
||||
|
||||
roomSecretEntries.push(roomSecrets[i]);
|
||||
}
|
||||
|
||||
resolve(roomSecretEntries);
|
||||
resolve(e.target.result);
|
||||
}
|
||||
}
|
||||
DBOpenRequest.onerror = (e) => {
|
||||
@@ -303,7 +262,7 @@ class PersistentStorage {
|
||||
return this.updateRoomSecret(roomSecret, null, null, null, autoAccept);
|
||||
}
|
||||
|
||||
static updateRoomSecret(roomSecret, updatedRoomSecret = null, updatedDisplayName = null, updatedDeviceName = null, updatedAutoAccept = null, wsDomain = null) {
|
||||
static updateRoomSecret(roomSecret, updatedRoomSecret = null, updatedDisplayName = null, updatedDeviceName = null, updatedAutoAccept = null) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const DBOpenRequest = window.indexedDB.open('pairdrop_store');
|
||||
DBOpenRequest.onsuccess = e => {
|
||||
@@ -321,8 +280,7 @@ class PersistentStorage {
|
||||
'secret': updatedRoomSecret !== null ? updatedRoomSecret : roomSecretEntry.entry.secret,
|
||||
'display_name': updatedDisplayName !== null ? updatedDisplayName : roomSecretEntry.entry.display_name,
|
||||
'device_name': updatedDeviceName !== null ? updatedDeviceName : roomSecretEntry.entry.device_name,
|
||||
'auto_accept': updatedAutoAccept !== null ? updatedAutoAccept : roomSecretEntry.entry.auto_accept,
|
||||
'ws_domain': wsDomain !== null ? wsDomain : roomSecretEntry.entry.ws_domain
|
||||
'auto_accept': updatedAutoAccept !== null ? updatedAutoAccept : roomSecretEntry.entry.auto_accept
|
||||
};
|
||||
|
||||
const objectStoreRequestUpdate = objectStore.put(updatedRoomSecretEntry, roomSecretEntry.key);
|
||||
|
||||
@@ -25,7 +25,7 @@ class PeersUI {
|
||||
this.shareMode.text = "";
|
||||
|
||||
Events.on('peer-joined', e => this._onPeerJoined(e.detail.peer, e.detail.roomType, e.detail.roomId));
|
||||
Events.on('peer-connected', e => this._onPeerConnected(e.detail.peerId, e.detail.connectionHash));
|
||||
Events.on('peer-connected', e => this._onPeerConnected(e.detail.peerId, e.detail.connectionHash, e.detail.connectionType));
|
||||
Events.on('peer-connecting', e => this._onPeerConnecting(e.detail));
|
||||
Events.on('peer-disconnected', e => this._onPeerDisconnected(e.detail));
|
||||
Events.on('peers', e => this._onPeers(e.detail));
|
||||
@@ -110,12 +110,12 @@ class PeersUI {
|
||||
this.peerUIs[peer.id] = peerUI;
|
||||
}
|
||||
|
||||
_onPeerConnected(peerId, connectionHash) {
|
||||
_onPeerConnected(peerId, connectionHash, connectionType) {
|
||||
const peerUI = this.peerUIs[peerId];
|
||||
|
||||
if (!peerUI) return;
|
||||
|
||||
peerUI._peerConnected(true, connectionHash);
|
||||
peerUI._peerConnected(true, connectionHash, connectionType);
|
||||
|
||||
this._addPeerUIIfMissing(peerUI);
|
||||
}
|
||||
@@ -459,7 +459,9 @@ class PeerUI {
|
||||
this.$displayName.textContent = this._displayName();
|
||||
this.$deviceName.textContent = this._deviceName();
|
||||
|
||||
this.updateTypesClassList();
|
||||
this._updateRoomTypeClasses();
|
||||
this._updateSameBrowserClass();
|
||||
this._updateWsPeerClass();
|
||||
|
||||
this.setStatus("connect");
|
||||
|
||||
@@ -472,14 +474,18 @@ class PeerUI {
|
||||
}
|
||||
|
||||
html() {
|
||||
let title= Localization.getTranslation("peer-ui.click-to-send");
|
||||
const titleLabelTranslation= Localization.getTranslation("peer-ui.click-to-send");
|
||||
const titleTurnTranslation= Localization.getTranslation("peer-ui.turn-indicator");
|
||||
|
||||
this.$el.innerHTML = `
|
||||
<label class="column center pointer" title="${title}">
|
||||
<label class="column center pointer" title="${titleLabelTranslation}">
|
||||
<input type="file" multiple/>
|
||||
<x-icon>
|
||||
<div class="icon-wrapper" shadow="1">
|
||||
<svg class="icon"><use xlink:href="#"/></svg>
|
||||
<div class="turn-indicator-wrapper center" title="${titleTurnTranslation}">
|
||||
<svg class="turn-indicator"><use xlink:href="#turn-indicator"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="highlight-wrapper center">
|
||||
<div class="highlight highlight-room-ip" shadow="1"></div>
|
||||
@@ -499,30 +505,49 @@ class PeerUI {
|
||||
</label>`;
|
||||
}
|
||||
|
||||
updateTypesClassList() {
|
||||
_updateRoomTypeClasses() {
|
||||
// Remove all classes
|
||||
this.$el.classList.remove('type-ip', 'type-secret', 'type-public-id', 'type-same-browser', 'ws-peer');
|
||||
this.$el.classList.remove('type-ip', 'type-secret', 'type-public-id');
|
||||
|
||||
// Add classes accordingly
|
||||
Object.keys(this._roomIds).forEach(roomType => this.$el.classList.add(`type-${roomType}`));
|
||||
}
|
||||
|
||||
_updateSameBrowserClass() {
|
||||
if (BrowserTabsConnector.peerIsSameBrowser(this._peer.id)) {
|
||||
this.$el.classList.add(`type-same-browser`);
|
||||
this.$el.classList.add('same-browser');
|
||||
}
|
||||
else {
|
||||
this.$el.classList.remove('same-browser');
|
||||
}
|
||||
}
|
||||
|
||||
_updateWsPeerClass() {
|
||||
if (!this._peer.rtcSupported || !window.isRtcSupported) {
|
||||
this.$el.classList.add('ws-peer');
|
||||
}
|
||||
else {
|
||||
this.$el.classList.remove('ws-peer');
|
||||
}
|
||||
}
|
||||
|
||||
_updateTurnClass() {
|
||||
if (this._connectionType === "relay") {
|
||||
this.$el.classList.add('turn');
|
||||
}
|
||||
else {
|
||||
this.$el.classList.remove('turn');
|
||||
}
|
||||
}
|
||||
|
||||
_addRoomId(roomType, roomId) {
|
||||
this._roomIds[roomType] = roomId;
|
||||
this.updateTypesClassList();
|
||||
this._updateRoomTypeClasses();
|
||||
}
|
||||
|
||||
_removeRoomId(roomType) {
|
||||
delete this._roomIds[roomType];
|
||||
this.updateTypesClassList();
|
||||
this._updateRoomTypeClasses();
|
||||
}
|
||||
|
||||
_onShareModeChanged(active = false, descriptor = "") {
|
||||
@@ -598,7 +623,7 @@ class PeerUI {
|
||||
});
|
||||
}
|
||||
|
||||
_peerConnected(connected = true, connectionHash = "") {
|
||||
_peerConnected(connected, connectionHash = "", connectionType = "") {
|
||||
if (connected) {
|
||||
this._connected = true;
|
||||
|
||||
@@ -607,6 +632,11 @@ class PeerUI {
|
||||
this._oldStatus = null;
|
||||
|
||||
this._connectionHash = connectionHash;
|
||||
this._connectionType = connectionType;
|
||||
|
||||
this._updateSameBrowserClass();
|
||||
this._updateWsPeerClass();
|
||||
this._updateTurnClass();
|
||||
}
|
||||
else {
|
||||
this._connected = false;
|
||||
@@ -618,6 +648,7 @@ class PeerUI {
|
||||
this.setStatus("connect");
|
||||
|
||||
this._connectionHash = "";
|
||||
this._connectionType = "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1695,7 +1726,6 @@ class PairDeviceDialog extends Dialog {
|
||||
Events.on('pair-device-join-key-invalid', _ => this._onPublicRoomJoinKeyInvalid());
|
||||
Events.on('pair-device-canceled', e => this._onPairDeviceCanceled(e.detail));
|
||||
Events.on('evaluate-number-room-secrets', _ => this._evaluateNumberRoomSecrets())
|
||||
Events.on('config-loaded', _ => this._evaluateNumberRoomSecrets())
|
||||
Events.on('secret-room-deleted', e => this._onSecretRoomDeleted(e.detail));
|
||||
this.$el.addEventListener('paste', e => this._onPaste(e));
|
||||
this.$qrCode.addEventListener('click', _ => this._copyPairUrl());
|
||||
@@ -1884,9 +1914,9 @@ class PairDeviceDialog extends Dialog {
|
||||
|
||||
_evaluateNumberRoomSecrets() {
|
||||
PersistentStorage
|
||||
.getAllRoomSecretsCount()
|
||||
.then(roomSecretsCount => {
|
||||
if (roomSecretsCount > 0) {
|
||||
.getAllRoomSecrets()
|
||||
.then(roomSecrets => {
|
||||
if (roomSecrets.length > 0) {
|
||||
this.$editPairedDevicesHeaderBtn.removeAttribute('hidden');
|
||||
this.$footerInstructionsPairedDevices.removeAttribute('hidden');
|
||||
}
|
||||
|
||||
@@ -279,6 +279,40 @@ x-peer[drop] x-icon {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
x-peer:not(.turn) .turn-indicator-wrapper {
|
||||
display: none;
|
||||
}
|
||||
|
||||
x-peer .turn-indicator-wrapper {
|
||||
z-index: 0;
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 43px;
|
||||
right: 23px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
x-peer .turn-indicator-wrapper:before {
|
||||
z-index: -1;
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: white;
|
||||
border-radius: 50%;
|
||||
border: solid 2px var(--primary-color);
|
||||
}
|
||||
|
||||
x-peer .turn-indicator {
|
||||
margin: auto;
|
||||
width: calc(var(--icon-size) / 3);
|
||||
height: calc(var(--icon-size) / 3);
|
||||
fill: var(--primary-color);
|
||||
}
|
||||
|
||||
/* Checkboxes as slider */
|
||||
|
||||
.switch {
|
||||
|
||||
Reference in New Issue
Block a user