mirror of
https://github.com/schlagmichdoch/PairDrop.git
synced 2025-04-25 17:26:18 -04:00
implement temporary public rooms, tidy up index.js, rework UI dialogs and change colors slightly
This commit is contained in:
parent
bd7b3c6d28
commit
8d2584fa69
6 changed files with 1237 additions and 496 deletions
|
@ -78,7 +78,7 @@
|
|||
<use xlink:href="#homescreen" />
|
||||
</svg>
|
||||
</div>
|
||||
<div id="pair-device" class="icon-button" data-i18n-key="header.pair-device" data-i18n-attrs="title" title="Pair Device" hidden>
|
||||
<div id="pair-device" class="icon-button" data-i18n-key="header.pair-device" data-i18n-attrs="title" title="Pair Your Devices Permanently">
|
||||
<svg class="icon">
|
||||
<use xlink:href="#pair-device-icon" />
|
||||
</svg>
|
||||
|
@ -88,6 +88,11 @@
|
|||
<use xlink:href="#edit-pair-devices-icon" />
|
||||
</svg>
|
||||
</div>
|
||||
<div id="join-public-room" class="icon-button" data-i18n-key="header.join-public-room" data-i18n-attrs="title" title="Join Public Room Temporarily">
|
||||
<svg class="icon">
|
||||
<use xlink:href="#public-room-icon" />
|
||||
</svg>
|
||||
</div>
|
||||
<div id="cancel-paste-mode" class="button" data-i18n-key="header.cancel-paste-mode" data-i18n-attrs="text" hidden>Done</div>
|
||||
</header>
|
||||
<!-- Center -->
|
||||
|
@ -97,7 +102,7 @@
|
|||
<x-peers class="center"></x-peers>
|
||||
<x-no-peers data-i18n-key="instructions.no-peers" data-i18n-attrs="data-drop-bg" data-drop-bg="Release to select recipient">
|
||||
<h2 data-i18n-key="instructions.no-peers-title" data-i18n-attrs="text">Open PairDrop on other devices to send files</h2>
|
||||
<div data-i18n-key="instructions.no-peers-subtitle" data-i18n-attrs="text">Pair devices to be discoverable on other networks</div>
|
||||
<div data-i18n-key="instructions.no-peers-subtitle" data-i18n-attrs="text">Pair devices or enter a public room to be discoverable on other networks</div>
|
||||
</x-no-peers>
|
||||
<x-instructions data-i18n-key="instructions.x-instructions" data-i18n-attrs="desktop mobile data-drop-peer data-drop-bg"
|
||||
desktop="Click to send files or right click to send a message"
|
||||
|
@ -112,21 +117,25 @@
|
|||
<svg class="icon logo">
|
||||
<use xlink:href="#wifi-tethering" />
|
||||
</svg>
|
||||
<div>
|
||||
<span data-i18n-key="footer.known-as" data-i18n-attrs="text">You are known as:</span>
|
||||
<div id="display-name" data-i18n-key="footer.display-name" data-i18n-attrs="data-placeholder title" placeholder="Loading..." data-placeholder="Loading..." title="Edit your device name permanently" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable></div>
|
||||
<svg id="edit-pen" class="icon">
|
||||
<use xlink:href="#edit-pen-icon" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="font-body2">
|
||||
<div>
|
||||
<span data-i18n-key="footer.discovery-everyone" data-i18n-attrs="text">You can be discovered by everyone</span>
|
||||
<span id="on-this-network" data-i18n-key="footer.on-this-network" data-i18n-attrs="text">on this network</span>
|
||||
<div class="column">
|
||||
<div class="known-as-wrapper">
|
||||
<span data-i18n-key="footer.known-as" data-i18n-attrs="text">You are known as:</span>
|
||||
<div id="display-name" class="badge" data-i18n-key="footer.display-name" data-i18n-attrs="data-placeholder title"
|
||||
placeholder="Loading..." data-placeholder="Loading..." title="Edit your device name permanently"
|
||||
autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable></div>
|
||||
<svg id="edit-pen" class="icon">
|
||||
<use xlink:href="#edit-pen-icon" />
|
||||
</svg>
|
||||
</div>
|
||||
<div id="and-by-paired-devices" hidden>
|
||||
<span id="and-by" data-i18n-key="footer.and-by" data-i18n-attrs="text">and by</span>
|
||||
<span id="paired-devices" data-i18n-key="footer.paired-devices" data-i18n-attrs="text">paired devices</span>
|
||||
<div class="discovery-wrapper row">
|
||||
<div class="row center">
|
||||
<span data-i18n-key="footer.discovery" data-i18n-attrs="text">You can be discovered:</span>
|
||||
</div>
|
||||
<div class="row center">
|
||||
<span class="badge badge-room-ip" data-i18n-key="footer.on-this-network" data-i18n-attrs="text title">on this network</span>
|
||||
<span class="badge badge-room-secret pointer" data-i18n-key="footer.paired-devices" data-i18n-attrs="text title" hidden>paired devices</span>
|
||||
<span class="badge badge-room-public-id pointer" data-i18n-key="footer.public-room-devices" data-i18n-attrs="text title" hidden>in room IAIAI</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
@ -134,8 +143,9 @@
|
|||
<x-dialog id="language-select-dialog">
|
||||
<x-background class="full center">
|
||||
<x-paper shadow="2">
|
||||
<h2 class="center" data-i18n-key="dialogs.language-selector-title" data-i18n-attrs="text">Select Language</h2>
|
||||
<hr>
|
||||
<div class="row center">
|
||||
<h2 class="center" data-i18n-key="dialogs.language-selector-title" data-i18n-attrs="text">Select Language</h2>
|
||||
</div>
|
||||
<div class="language-buttons">
|
||||
<button class="button fw" data-i18n-key="dialogs.system-language" data-i18n-attrs="text">System Language</button>
|
||||
<button class="button fw" value="en">English</button>
|
||||
|
@ -154,24 +164,39 @@
|
|||
<form action="#">
|
||||
<x-background class="full center text-center">
|
||||
<x-paper shadow="2">
|
||||
<h2 class="center" data-i18n-key="dialogs.pair-devices-title" data-i18n-attrs="text">Pair Devices</h2>
|
||||
<div id="room-key-qr-code" class="center"></div>
|
||||
<h1 id="room-key" class="center">000 000</h1>
|
||||
<div id="pair-instructions" class="center text-center">
|
||||
<span class="font-subheading" data-i18n-key="dialogs.input-key-on-this-device" data-i18n-attrs="text">Input this key on another device</span>
|
||||
<span class="font-subheading" data-i18n-key="dialogs.scan-qr-code" data-i18n-attrs="text">or scan the QR-Code.</span>
|
||||
<div class="row center">
|
||||
<h2 class="center" data-i18n-key="dialogs.pair-devices-title" data-i18n-attrs="text">Pair Devices</h2>
|
||||
</div>
|
||||
<hr>
|
||||
<div id="key-input-container">
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-1" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" autofocus contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-2" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-3" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-4" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-5" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-6" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<div class="row center">
|
||||
<div class="column">
|
||||
<div class="center key-qr-code"></div>
|
||||
<h1 class="center key">000 000</h1>
|
||||
<p class="center text-center key-instructions">
|
||||
<span class="font-subheading" data-i18n-key="dialogs.input-key-on-this-device" data-i18n-attrs="text">Input this key on another device</span>
|
||||
<span class="font-subheading" data-i18n-key="dialogs.scan-qr-code" data-i18n-attrs="text">or scan the QR-Code.</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="font-subheading center text-center" data-i18n-key="dialogs.enter-key-from-another-device" data-i18n-attrs="text">Enter key from another device to continue.</div>
|
||||
<div class="center row-reverse button-row">
|
||||
<div class="hr-note">
|
||||
<hr>
|
||||
<div>
|
||||
<span data-i18n-key="dialogs.hr-or" data-i18n-attrs="text">OR</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row center">
|
||||
<div class="column">
|
||||
<div class="input-key-container six-chars">
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-char-1" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" autofocus contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-char-2" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-char-3" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-char-4" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-char-5" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-char-6" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
</div>
|
||||
<p class="font-subheading center text-center" data-i18n-key="dialogs.enter-key-from-another-device" data-i18n-attrs="text">Enter key from another device here.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="button-row row-reverse">
|
||||
<button class="button" type="submit" data-i18n-key="dialogs.pair" data-i18n-attrs="text" disabled>Pair</button>
|
||||
<button class="button" type="button" data-i18n-key="dialogs.cancel" data-i18n-attrs="text" close>Cancel</button>
|
||||
</div>
|
||||
|
@ -184,7 +209,9 @@
|
|||
<form action="#">
|
||||
<x-background class="full center text-center">
|
||||
<x-paper shadow="2">
|
||||
<h2 class="center" data-i18n-key="dialogs.edit-paired-devices-title" data-i18n-attrs="text">Edit Paired Devices</h2>
|
||||
<div class="row center">
|
||||
<h2 class="center" data-i18n-key="dialogs.edit-paired-devices-title" data-i18n-attrs="text">Edit Paired Devices</h2>
|
||||
</div>
|
||||
<div class="paired-devices-wrapper" data-i18n-key="dialogs.paired-devices-wrapper" data-i18n-attrs="data-empty" data-empty="No paired devices."></div>
|
||||
<div class="font-subheading center">
|
||||
<p>
|
||||
|
@ -204,26 +231,79 @@
|
|||
</x-background>
|
||||
</form>
|
||||
</x-dialog>
|
||||
<!-- Public Room Dialog -->
|
||||
<x-dialog id="public-room-dialog">
|
||||
<form action="#">
|
||||
<x-background class="full center text-center">
|
||||
<x-paper shadow="2">
|
||||
<div class="row center">
|
||||
<div class="column">
|
||||
<h2 class="center">Temporary Public Room</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row center">
|
||||
<div class="column">
|
||||
<div class="center key-qr-code"></div>
|
||||
<h1 class="center key">IOX9P</h1>
|
||||
<p class="center text-center key-instructions">
|
||||
<span class="font-subheading" data-i18n-key="dialogs.input-room-id-on-another-device" data-i18n-attrs="text">Input this room id on another device</span>
|
||||
<span class="font-subheading" data-i18n-key="dialogs.scan-qr-code" data-i18n-attrs="text">or scan the QR-Code.</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hr-note">
|
||||
<hr>
|
||||
<div>
|
||||
<span data-i18n-key="dialogs.hr-or" data-i18n-attrs="text">OR</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row center">
|
||||
<div class="column">
|
||||
<div class="input-key-container">
|
||||
<input type="text" class="textarea center" aria-label="room-id-char-1" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" autofocus contenteditable placeholder="" disabled>
|
||||
<input type="text" class="textarea center" aria-label="room-id-char-2" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="text" class="textarea center" aria-label="room-id-char-3" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="text" class="textarea center" aria-label="room-id-char-4" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="text" class="textarea center" aria-label="room-id-char-5" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
</div>
|
||||
<p class="font-subheading center text-center">Enter room id from another device to join.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="center row-reverse button-row">
|
||||
<button class="button" type="submit" disabled>Join</button>
|
||||
<button class="button" type="button" close>Close</button>
|
||||
<button class="button leave-room" type="button">Leave</button>
|
||||
</div>
|
||||
</x-paper>
|
||||
</x-background>
|
||||
</form>
|
||||
</x-dialog>
|
||||
<!-- Receive Request Dialog -->
|
||||
<x-dialog id="receive-request-dialog">
|
||||
<x-background class="full center">
|
||||
<x-paper shadow="2">
|
||||
<h2 class="center"></h2>
|
||||
<div class="center column file-description">
|
||||
<div>
|
||||
<span class="display-name"></span>
|
||||
<span data-i18n-key="dialogs.would-like-to-share" data-i18n-attrs="text">would like to share</span>
|
||||
<div class="row center">
|
||||
<div class="column">
|
||||
<h2 class="center"></h2>
|
||||
</div>
|
||||
<div class="row file-name" >
|
||||
<span class="file-stem"></span>
|
||||
<span class="file-extension"></span>
|
||||
</div>
|
||||
<div class="row center">
|
||||
<div class="column center file-description">
|
||||
<div>
|
||||
<span class="display-name badge"></span>
|
||||
<span data-i18n-key="dialogs.would-like-to-share" data-i18n-attrs="text">would like to share</span>
|
||||
</div>
|
||||
<div class="row file-name" >
|
||||
<span class="file-stem"></span>
|
||||
<span class="file-extension"></span>
|
||||
</div>
|
||||
<div class="row file-other">
|
||||
</div>
|
||||
<div class="row font-body2 file-size"></div>
|
||||
</div>
|
||||
<div class="row file-other">
|
||||
</div>
|
||||
<div class="row font-body2 file-size"></div>
|
||||
</div>
|
||||
<div class="center file-preview"></div>
|
||||
<div class="center row-reverse button-row">
|
||||
<div class="row-reverse center button-row">
|
||||
<button id="accept-request" class="button" title="ENTER" data-i18n-key="dialogs.accept" data-i18n-attrs="text" autofocus>Accept</button>
|
||||
<button id="decline-request" class="button" title="ESCAPE" data-i18n-key="dialogs.decline" data-i18n-attrs="text">Decline</button>
|
||||
</div>
|
||||
|
@ -234,21 +314,28 @@
|
|||
<x-dialog id="receive-file-dialog">
|
||||
<x-background class="full center">
|
||||
<x-paper shadow="2">
|
||||
<h2 class="center"></h2>
|
||||
<div class="center column file-description">
|
||||
<div>
|
||||
<span class="display-name"></span>
|
||||
<span data-i18n-key="dialogs.has-sent" data-i18n-attrs="text">has sent</span>
|
||||
<div class="row center">
|
||||
<div class="column">
|
||||
<h2 class="center"></h2>
|
||||
</div>
|
||||
<div class="row file-name" >
|
||||
<span class="file-stem"></span>
|
||||
<span class="file-extension"></span>
|
||||
</div>
|
||||
<div class="row center">
|
||||
<div class="column center file-description">
|
||||
<div>
|
||||
<span class="display-name badge"></span>
|
||||
<span data-i18n-key="dialogs.has-sent" data-i18n-attrs="text">has sent</span>
|
||||
</div>
|
||||
<div class="row file-name" >
|
||||
<span class="file-stem"></span>
|
||||
<span class="file-extension"></span>
|
||||
</div>
|
||||
<div class="row file-other">
|
||||
</div>
|
||||
<div class="row font-body2 file-size"></div>
|
||||
</div>
|
||||
<div class="row file-other"></div>
|
||||
<div class="row font-body2 file-size"></div>
|
||||
</div>
|
||||
<div class="center file-preview"></div>
|
||||
<div class="center row-reverse button-row">
|
||||
<div class="row-reverse center button-row">
|
||||
<button id="share-btn" class="button" data-i18n-key="dialogs.share" data-i18n-attrs="text" autofocus hidden>Share</button>
|
||||
<button id="download-btn" class="button" data-i18n-key="dialogs.download" data-i18n-attrs="text" autofocus>Download</button>
|
||||
<button class="button" data-i18n-key="dialogs.close" data-i18n-attrs="text" close>Close</button>
|
||||
|
@ -261,14 +348,25 @@
|
|||
<form action="#">
|
||||
<x-background class="full center">
|
||||
<x-paper shadow="2">
|
||||
<h2 class="text-center" data-i18n-key="dialogs.send-message-title" data-i18n-attrs="text">Send Message</h2>
|
||||
<div class="dialog-subheader text-center">
|
||||
<span data-i18n-key="dialogs.send-message-to" data-i18n-attrs="text">Send a Message to</span>
|
||||
<span class="display-name"></span>
|
||||
<div class="row center">
|
||||
<div class="column">
|
||||
<h2 class="center" data-i18n-key="dialogs.send-message-title" data-i18n-attrs="text">Send Message</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-separator"></div>
|
||||
<div id="text-input" title="Message" class="textarea" role="textbox" autocapitalize="none" spellcheck="false" autofocus contenteditable></div>
|
||||
<div class="center row-reverse button-row">
|
||||
<div class="row center display-name-wrapper">
|
||||
<div class="column">
|
||||
<div class="text-center">
|
||||
<span data-i18n-key="dialogs.send-message-to" data-i18n-attrs="text">Send a Message to</span>
|
||||
<span class="display-name badge"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="column fw">
|
||||
<div id="text-input" title="Message" class="textarea" role="textbox" autocapitalize="none" spellcheck="false" autofocus contenteditable></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="button-row row-reverse">
|
||||
<button class="button" type="submit" title="CTRL/⌘ + ENTER" data-i18n-key="dialogs.send" data-i18n-attrs="text" disabled>Send</button>
|
||||
<button class="button" type="button" title="ESCAPE" data-i18n-key="dialogs.cancel" data-i18n-attrs="text" close>Cancel</button>
|
||||
</div>
|
||||
|
@ -280,14 +378,21 @@
|
|||
<x-dialog id="receive-text-dialog">
|
||||
<x-background class="full center">
|
||||
<x-paper shadow="2">
|
||||
<h2 class="text-center" data-i18n-key="dialogs.receive-text-title" data-i18n-attrs="text">Message Received</h2>
|
||||
<div class="text-center dialog-subheader">
|
||||
<span class="display-name"></span>
|
||||
<span data-i18n-key="dialogs.has-sent" data-i18n-attrs="text">has sent:</span>
|
||||
<div class="row center">
|
||||
<h2 class="text-center" data-i18n-key="dialogs.receive-text-title" data-i18n-attrs="text">Message Received</h2>
|
||||
</div>
|
||||
<div class="row-separator"></div>
|
||||
<div id="text"></div>
|
||||
<div class="center row-reverse button-row">
|
||||
<div class="row center">
|
||||
<div class="text-center">
|
||||
<span class="display-name badge"></span>
|
||||
<span data-i18n-key="dialogs.has-sent" data-i18n-attrs="text">has sent:</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row center">
|
||||
<div class="column fw">
|
||||
<div id="text" class="textarea fw"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-reverse center button-row">
|
||||
<button id="copy" class="button" title="CTRL/⌘ + C" data-i18n-key="dialogs.copy" data-i18n-attrs="text">Copy</button>
|
||||
<button id="close" class="button" title="ESCAPE" data-i18n-key="dialogs.close" data-i18n-attrs="text">Close</button>
|
||||
</div>
|
||||
|
@ -306,7 +411,7 @@
|
|||
</x-dialog>
|
||||
<!-- Toast -->
|
||||
<div class="toast-container full center">
|
||||
<x-toast id="toast" class="row" shadow="1"></x-toast>
|
||||
<x-toast id="toast" class="row center" shadow="1"></x-toast>
|
||||
</div>
|
||||
<!-- About Page -->
|
||||
<x-about id="about" class="full center column">
|
||||
|
@ -416,6 +521,10 @@
|
|||
<!--! Font Awesome Pro 6.3.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
|
||||
<path d="M362.7 19.3L314.3 67.7 444.3 197.7l48.4-48.4c25-25 25-65.5 0-90.5L453.3 19.3c-25-25-65.5-25-90.5 0zm-71 71L58.6 323.5c-10.4 10.4-18 23.3-22.2 37.4L1 481.2C-1.5 489.7 .8 498.8 7 505s15.3 8.5 23.7 6.1l120.3-35.4c14.1-4.2 27-11.8 37.4-22.2L421.7 220.3 291.7 90.3z"/>
|
||||
</symbol>
|
||||
<symbol id="public-room-icon" viewBox="0 0 640 512">
|
||||
<!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
|
||||
<path d="M0 24C0 10.7 10.7 0 24 0H616c13.3 0 24 10.7 24 24s-10.7 24-24 24H24C10.7 48 0 37.3 0 24zM0 488c0-13.3 10.7-24 24-24H616c13.3 0 24 10.7 24 24s-10.7 24-24 24H24c-13.3 0-24-10.7-24-24zM83.2 160a64 64 0 1 1 128 0 64 64 0 1 1 -128 0zM32 320c0-35.3 28.7-64 64-64h96c12.2 0 23.7 3.4 33.4 9.4c-37.2 15.1-65.6 47.2-75.8 86.6H64c-17.7 0-32-14.3-32-32zm461.6 32c-10.3-40.1-39.6-72.6-77.7-87.4c9.4-5.5 20.4-8.6 32.1-8.6h96c35.3 0 64 28.7 64 64c0 17.7-14.3 32-32 32H493.6zM391.2 290.4c32.1 7.4 58.1 30.9 68.9 61.6c3.5 10 5.5 20.8 5.5 32c0 17.7-14.3 32-32 32h-224c-17.7 0-32-14.3-32-32c0-11.2 1.9-22 5.5-32c10.5-29.7 35.3-52.8 66.1-60.9c7.8-2.1 16-3.1 24.5-3.1h96c7.4 0 14.7 .8 21.6 2.4zm44-130.4a64 64 0 1 1 128 0 64 64 0 1 1 -128 0zM321.6 96a80 80 0 1 1 0 160 80 80 0 1 1 0-160z"/>
|
||||
</symbol>
|
||||
<symbol id="icon-language-selector" viewBox="0 0 640 512">
|
||||
<!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
|
||||
<path d="M0 128C0 92.7 28.7 64 64 64H256h48 16H576c35.3 0 64 28.7 64 64V384c0 35.3-28.7 64-64 64H320 304 256 64c-35.3 0-64-28.7-64-64V128zm320 0V384H576V128H320zM178.3 175.9c-3.2-7.2-10.4-11.9-18.3-11.9s-15.1 4.7-18.3 11.9l-64 144c-4.5 10.1 .1 21.9 10.2 26.4s21.9-.1 26.4-10.2l8.9-20.1h73.6l8.9 20.1c4.5 10.1 16.3 14.6 26.4 10.2s14.6-16.3 10.2-26.4l-64-144zM160 233.2L179 276H141l19-42.8zM448 164c11 0 20 9 20 20v4h44 16c11 0 20 9 20 20s-9 20-20 20h-2l-1.6 4.5c-8.9 24.4-22.4 46.6-39.6 65.4c.9 .6 1.8 1.1 2.7 1.6l18.9 11.3c9.5 5.7 12.5 18 6.9 27.4s-18 12.5-27.4 6.9l-18.9-11.3c-4.5-2.7-8.8-5.5-13.1-8.5c-10.6 7.5-21.9 14-34 19.4l-3.6 1.6c-10.1 4.5-21.9-.1-26.4-10.2s.1-21.9 10.2-26.4l3.6-1.6c6.4-2.9 12.6-6.1 18.5-9.8l-12.2-12.2c-7.8-7.8-7.8-20.5 0-28.3s20.5-7.8 28.3 0l14.6 14.6 .5 .5c12.4-13.1 22.5-28.3 29.8-45H448 376c-11 0-20-9-20-20s9-20 20-20h52v-4c0-11 9-20 20-20z"/>
|
||||
|
|
|
@ -8,14 +8,15 @@
|
|||
"theme-dark_title": "Always Use Dark-Theme",
|
||||
"notification_title": "Enable Notifications",
|
||||
"install_title": "Install PairDrop",
|
||||
"pair-device_title": "Pair Device",
|
||||
"pair-device_title": "Pair Your Devices Permanently",
|
||||
"edit-paired-devices_title": "Edit Paired Devices",
|
||||
"join-public-room_title": "Join Public Room Temporarily",
|
||||
"cancel-paste-mode": "Done"
|
||||
},
|
||||
"instructions": {
|
||||
"no-peers_data-drop-bg": "Release to select recipient",
|
||||
"no-peers-title": "Open PairDrop on other devices to send files",
|
||||
"no-peers-subtitle": "Pair devices to be discoverable on other networks",
|
||||
"no-peers-subtitle": "Pair devices or enter a public room to be discoverable on other networks",
|
||||
"x-instructions_desktop": "Click to send files or right click to send a message",
|
||||
"x-instructions_mobile": "Tap to send files or long tap to send a message",
|
||||
"x-instructions_data-drop-peer": "Release to send to peer",
|
||||
|
@ -27,10 +28,13 @@
|
|||
"known-as": "You are known as:",
|
||||
"display-name_data-placeholder": "Loading…",
|
||||
"display-name_title": "Edit your device name permanently",
|
||||
"discovery-everyone": "You can be discovered by everyone",
|
||||
"discovery": "You can be discovered:",
|
||||
"on-this-network": "on this network",
|
||||
"and-by": "and by",
|
||||
"paired-devices": "paired devices",
|
||||
"on-this-network_title": "You can be discovered by everyone on this network.",
|
||||
"paired-devices": "by paired devices",
|
||||
"paired-devices_title": "You can be discovered by paired devices at all times independent of the network.",
|
||||
"public-room-devices": "in room {{roomId}}",
|
||||
"public-room-devices_title": "You can be discovered by devices in this public room independent of the network.",
|
||||
"traffic": "Traffic is",
|
||||
"routed": "routed through the server",
|
||||
"webrtc": "if WebRTC is not available."
|
||||
|
@ -39,10 +43,12 @@
|
|||
"activate-paste-mode-base": "Open PairDrop on other devices to send",
|
||||
"activate-paste-mode-and-other-files": "and {{count}} other files",
|
||||
"activate-paste-mode-activate-paste-mode-shared-text": "shared text",
|
||||
"pair-devices-title": "Pair Devices",
|
||||
"pair-devices-title": "Pair Devices Permanently",
|
||||
"input-key-on-this-device": "Input this key on another device",
|
||||
"scan-qr-code": "or scan the QR-code.",
|
||||
"enter-key-from-another-device": "Enter key from another device to continue.",
|
||||
"enter-key-from-another-device": "Enter key from another device here.",
|
||||
"input-room-id-on-another-device": "Input this room id on another device",
|
||||
"hr-or": "OR",
|
||||
"pair": "Pair",
|
||||
"cancel": "Cancel",
|
||||
"edit-paired-devices-title": "Edit Paired Devices",
|
||||
|
@ -96,10 +102,13 @@
|
|||
"pairing-tabs-error": "Pairing two web browser tabs is impossible.",
|
||||
"pairing-success": "Devices paired.",
|
||||
"pairing-not-persistent": "Paired devices are not persistent.",
|
||||
"pairing-key-invalid": "Invalid key",
|
||||
"pairing-key-invalid": "Invalid key.",
|
||||
"pairing-key-invalidated": "Key {{key}} invalidated.",
|
||||
"pairing-cleared": "All Devices unpaired.",
|
||||
"copied-to-clipboard": "Copied to clipboard",
|
||||
"public-room-id-invalid": "Invalid room id.",
|
||||
"public-room-left": "Left public room {{publicRoomId}}.",
|
||||
"copied-to-clipboard": "Copied to clipboard.",
|
||||
"copied-to-clipboard-error": "Copying not possible. Copy manually.",
|
||||
"copied-to-clipboard-error": "Copying not possible. Copy manually.",
|
||||
"text-content-incorrect": "Text content is incorrect.",
|
||||
"file-content-incorrect": "File content is incorrect.",
|
||||
|
@ -115,7 +124,8 @@
|
|||
"offline": "You are offline",
|
||||
"online": "You are back online",
|
||||
"connected": "Connected.",
|
||||
"online-requirement": "You need to be online to pair devices.",
|
||||
"online-requirement-pairing": "You need to be online to pair devices.",
|
||||
"online-requirement-public-room": "You need to be online to create a public room.",
|
||||
"connecting": "Connecting…",
|
||||
"files-incorrect": "Files are incorrect.",
|
||||
"file-transfer-completed": "File transfer completed.",
|
||||
|
|
|
@ -23,10 +23,14 @@ class ServerConnection {
|
|||
Events.on('join-ip-room', e => 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('resend-peers', _ => this.send({ type: 'resend-peers'}));
|
||||
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));
|
||||
Events.on('leave-public-room', _ => this._onLeavePublicRoom());
|
||||
|
||||
Events.on('offline', _ => clearTimeout(this._reconnectTimer));
|
||||
Events.on('online', _ => this._connect());
|
||||
}
|
||||
|
@ -51,18 +55,42 @@ class ServerConnection {
|
|||
|
||||
_onPairDeviceInitiate() {
|
||||
if (!this._isConnected()) {
|
||||
Events.fire('notify-user', Localization.getTranslation("notifications.online-requirement"));
|
||||
Events.fire('notify-user', Localization.getTranslation("notifications.online-requirement-pairing"));
|
||||
return;
|
||||
}
|
||||
this.send({ type: 'pair-device-initiate' })
|
||||
this.send({ type: 'pair-device-initiate' });
|
||||
}
|
||||
|
||||
_onPairDeviceJoin(roomKey) {
|
||||
_onPairDeviceJoin(pairKey) {
|
||||
if (!this._isConnected()) {
|
||||
setTimeout(_ => this._onPairDeviceJoin(roomKey), 1000);
|
||||
setTimeout(_ => this._onPairDeviceJoin(pairKey), 1000);
|
||||
return;
|
||||
}
|
||||
this.send({ type: 'pair-device-join', roomKey: roomKey })
|
||||
this.send({ type: 'pair-device-join', pairKey: pairKey });
|
||||
}
|
||||
|
||||
_onCreatePublicRoom() {
|
||||
if (!this._isConnected()) {
|
||||
Events.fire('notify-user', Localization.getTranslation("notifications.online-requirement-public-room"));
|
||||
return;
|
||||
}
|
||||
this.send({ type: 'create-public-room' });
|
||||
}
|
||||
|
||||
_onJoinPublicRoom(roomId, createIfInvalid) {
|
||||
if (!this._isConnected()) {
|
||||
setTimeout(_ => this._onJoinPublicRoom(roomId), 1000);
|
||||
return;
|
||||
}
|
||||
this.send({ type: 'join-public-room', publicRoomId: roomId, createIfInvalid: createIfInvalid });
|
||||
}
|
||||
|
||||
_onLeavePublicRoom() {
|
||||
if (!this._isConnected()) {
|
||||
setTimeout(_ => this._onLeavePublicRoom(), 1000);
|
||||
return;
|
||||
}
|
||||
this.send({ type: 'leave-public-room' });
|
||||
}
|
||||
|
||||
_setRtcConfig(config) {
|
||||
|
@ -104,9 +132,9 @@ class ServerConnection {
|
|||
Events.fire('pair-device-join-key-invalid');
|
||||
break;
|
||||
case 'pair-device-canceled':
|
||||
Events.fire('pair-device-canceled', msg.roomKey);
|
||||
Events.fire('pair-device-canceled', msg.pairKey);
|
||||
break;
|
||||
case 'pair-device-join-key-rate-limit':
|
||||
case 'join-key-rate-limit':
|
||||
Events.fire('notify-user', Localization.getTranslation("notifications.rate-limit-join-key"));
|
||||
break;
|
||||
case 'secret-room-deleted':
|
||||
|
@ -115,6 +143,15 @@ class ServerConnection {
|
|||
case 'room-secret-regenerated':
|
||||
Events.fire('room-secret-regenerated', msg);
|
||||
break;
|
||||
case 'public-room-id-invalid':
|
||||
Events.fire('public-room-id-invalid', msg.publicRoomId);
|
||||
break;
|
||||
case 'public-room-created':
|
||||
Events.fire('public-room-created', msg.roomId);
|
||||
break;
|
||||
case 'public-room-left':
|
||||
Events.fire('public-room-left');
|
||||
break;
|
||||
default:
|
||||
console.error('WS receive: unknown message type', msg);
|
||||
}
|
||||
|
@ -132,8 +169,8 @@ class ServerConnection {
|
|||
|
||||
_onDisplayName(msg) {
|
||||
// Add peerId and peerIdHash to sessionStorage to authenticate as the same device on page reload
|
||||
sessionStorage.setItem("peerId", msg.message.peerId);
|
||||
sessionStorage.setItem("peerIdHash", msg.message.peerIdHash);
|
||||
sessionStorage.setItem('peer_id', msg.message.peerId);
|
||||
sessionStorage.setItem('peer_id_hash', msg.message.peerIdHash);
|
||||
|
||||
// Add peerId to localStorage to mark it for other PairDrop tabs on the same browser
|
||||
BrowserTabsConnector.addPeerIdToLocalStorage().then(peerId => {
|
||||
|
@ -155,8 +192,8 @@ class ServerConnection {
|
|||
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 = sessionStorage.getItem("peerId");
|
||||
const peerIdHash = sessionStorage.getItem("peerIdHash");
|
||||
const peerId = sessionStorage.getItem('peer_id');
|
||||
const peerIdHash = sessionStorage.getItem('peer_id_hash');
|
||||
if (peerId && peerIdHash) {
|
||||
ws_url.searchParams.append('peer_id', peerId);
|
||||
ws_url.searchParams.append('peer_id_hash', peerIdHash);
|
||||
|
@ -167,7 +204,7 @@ class ServerConnection {
|
|||
_disconnect() {
|
||||
this.send({ type: 'disconnect' });
|
||||
|
||||
const peerId = sessionStorage.getItem("peerId");
|
||||
const peerId = sessionStorage.getItem('peer_id');
|
||||
BrowserTabsConnector.removePeerIdFromLocalStorage(peerId).then(_ => {
|
||||
console.log("successfully removed peerId from localStorage");
|
||||
});
|
||||
|
@ -215,12 +252,13 @@ class ServerConnection {
|
|||
|
||||
class Peer {
|
||||
|
||||
constructor(serverConnection, isCaller, peerId, roomType, roomSecret) {
|
||||
constructor(serverConnection, isCaller, peerId, roomType, roomId) {
|
||||
this._server = serverConnection;
|
||||
this._isCaller = isCaller;
|
||||
this._peerId = peerId;
|
||||
this._roomType = roomType;
|
||||
this._updateRoomSecret(roomSecret);
|
||||
|
||||
this._roomIds = {};
|
||||
this._updateRoomIds(roomType, roomId);
|
||||
|
||||
this._filesQueue = [];
|
||||
this._busy = false;
|
||||
|
@ -241,34 +279,58 @@ class Peer {
|
|||
return BrowserTabsConnector.peerIsSameBrowser(this._peerId);
|
||||
}
|
||||
|
||||
_updateRoomSecret(roomSecret) {
|
||||
_isPaired() {
|
||||
return !!this._roomIds['secret'];
|
||||
}
|
||||
|
||||
_getPairSecret() {
|
||||
return this._roomIds['secret'];
|
||||
}
|
||||
|
||||
_getRoomTypes() {
|
||||
return Object.keys(this._roomIds);
|
||||
}
|
||||
|
||||
_updateRoomIds(roomType, roomId) {
|
||||
// if peer is another browser tab, peer is not identifiable with roomSecret as browser tabs share all roomSecrets
|
||||
// -> do not delete duplicates and do not regenerate room secrets
|
||||
if (!this._isSameBrowser() && this._roomSecret && this._roomSecret !== roomSecret) {
|
||||
// remove old roomSecrets to prevent multiple pairings with same peer
|
||||
PersistentStorage.deleteRoomSecret(this._roomSecret).then(deletedRoomSecret => {
|
||||
if (deletedRoomSecret) console.log("Successfully deleted duplicate room secret with same peer: ", deletedRoomSecret);
|
||||
})
|
||||
if (!this._isSameBrowser() && roomType === "secret" && this._isPaired() && this._getPairSecret() !== roomId) {
|
||||
// multiple roomSecrets with same peer -> delete old roomSecret
|
||||
PersistentStorage.deleteRoomSecret(this._getPairSecret())
|
||||
.then(deletedRoomSecret => {
|
||||
if (deletedRoomSecret) console.log("Successfully deleted duplicate room secret with same peer: ", deletedRoomSecret);
|
||||
});
|
||||
}
|
||||
|
||||
this._roomSecret = roomSecret;
|
||||
this._roomIds[roomType] = roomId;
|
||||
|
||||
if (!this._isSameBrowser() && this._roomSecret && this._roomSecret.length !== 256 && this._isCaller) {
|
||||
// increase security by increasing roomSecret length
|
||||
if (!this._isSameBrowser() && roomType === "secret" && this._isPaired() && this._getPairSecret().length !== 256 && this._isCaller) {
|
||||
// increase security by initiating the increase of the roomSecret length from 64 chars (<v1.7.0) to 256 chars (v1.7.0+)
|
||||
console.log('RoomSecret is regenerated to increase security')
|
||||
Events.fire('regenerate-room-secret', this._roomSecret);
|
||||
Events.fire('regenerate-room-secret', this._getPairSecret());
|
||||
}
|
||||
}
|
||||
|
||||
_removeRoomType(roomType) {
|
||||
delete this._roomIds[roomType];
|
||||
|
||||
Events.fire('room-type-removed', {
|
||||
peerId: this._peerId,
|
||||
roomType: roomType
|
||||
});
|
||||
}
|
||||
|
||||
_evaluateAutoAccept() {
|
||||
if (!this._roomSecret) {
|
||||
if (!this._isPaired()) {
|
||||
this._setAutoAccept(false);
|
||||
return;
|
||||
}
|
||||
|
||||
PersistentStorage.getRoomSecretEntry(this._roomSecret)
|
||||
PersistentStorage.getRoomSecretEntry(this._getPairSecret())
|
||||
.then(roomSecretEntry => {
|
||||
const autoAccept = roomSecretEntry ? roomSecretEntry.entry.auto_accept : false;
|
||||
const autoAccept = roomSecretEntry
|
||||
? roomSecretEntry.entry.auto_accept
|
||||
: false;
|
||||
this._setAutoAccept(autoAccept);
|
||||
})
|
||||
.catch(_ => {
|
||||
|
@ -277,7 +339,9 @@ class Peer {
|
|||
}
|
||||
|
||||
_setAutoAccept(autoAccept) {
|
||||
this._autoAccept = autoAccept;
|
||||
this._autoAccept = !this._isSameBrowser()
|
||||
? autoAccept
|
||||
: false;
|
||||
}
|
||||
|
||||
getResizedImageDataUrl(file, width = undefined, height = undefined, quality = 0.7) {
|
||||
|
@ -536,7 +600,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, imagesOnly: this._requestAccepted.imagesOnly, totalSize: this._requestAccepted.totalSize});
|
||||
Events.fire('files-received', {peerId: this._peerId, files: this._filesReceived, imagesOnly: this._requestAccepted.imagesOnly, totalSize: this._requestAccepted.totalSize});
|
||||
this._filesReceived = [];
|
||||
this._requestAccepted = null;
|
||||
}
|
||||
|
@ -599,8 +663,8 @@ class Peer {
|
|||
|
||||
class RTCPeer extends Peer {
|
||||
|
||||
constructor(serverConnection, isCaller, peerId, roomType, roomSecret) {
|
||||
super(serverConnection, isCaller, peerId, roomType, roomSecret);
|
||||
constructor(serverConnection, isCaller, peerId, roomType, roomId) {
|
||||
super(serverConnection, isCaller, peerId, roomType, roomId);
|
||||
this.rtcSupported = true;
|
||||
if (!this._isCaller) return; // we will listen for a caller
|
||||
this._connect();
|
||||
|
@ -626,13 +690,17 @@ class RTCPeer extends Peer {
|
|||
|
||||
_openChannel() {
|
||||
if (!this._conn) return;
|
||||
|
||||
const channel = this._conn.createDataChannel('data-channel', {
|
||||
ordered: true,
|
||||
reliable: true // Obsolete. See https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel/reliable
|
||||
});
|
||||
channel.onopen = e => this._onChannelOpened(e);
|
||||
channel.onerror = e => this._onError(e);
|
||||
this._conn.createOffer().then(d => this._onDescription(d)).catch(e => this._onError(e));
|
||||
|
||||
this._conn.createOffer()
|
||||
.then(d => this._onDescription(d))
|
||||
.catch(e => this._onError(e));
|
||||
}
|
||||
|
||||
_onDescription(description) {
|
||||
|
@ -772,8 +840,8 @@ class RTCPeer extends Peer {
|
|||
_sendSignal(signal) {
|
||||
signal.type = 'signal';
|
||||
signal.to = this._peerId;
|
||||
signal.roomType = this._roomType;
|
||||
signal.roomSecret = this._roomSecret;
|
||||
signal.roomType = this._getRoomTypes()[0];
|
||||
signal.roomId = this._roomIds[this._getRoomTypes()[0]];
|
||||
this._server.send(signal);
|
||||
}
|
||||
|
||||
|
@ -815,7 +883,14 @@ class PeersManager {
|
|||
Events.on('peer-joined', e => this._onPeerJoined(e.detail));
|
||||
Events.on('peer-connected', e => this._onPeerConnected(e.detail.peerId));
|
||||
Events.on('peer-disconnected', e => this._onPeerDisconnected(e.detail));
|
||||
|
||||
// this device closes connection
|
||||
Events.on('room-secrets-deleted', e => this._onRoomSecretsDeleted(e.detail));
|
||||
Events.on('leave-public-room', e => this._onLeavePublicRoom(e.detail));
|
||||
|
||||
// peer closes connection
|
||||
Events.on('secret-room-deleted', e => this._onSecretRoomDeleted(e.detail));
|
||||
|
||||
Events.on('room-secret-regenerated', e => this._onRoomSecretRegenerated(e.detail));
|
||||
Events.on('display-name', e => this._onDisplayName(e.detail.message.displayName));
|
||||
Events.on('self-display-name-changed', e => this._notifyPeersDisplayNameChanged(e.detail));
|
||||
|
@ -828,47 +903,43 @@ class PeersManager {
|
|||
this.peers[peerId].onServerMessage(message);
|
||||
}
|
||||
|
||||
_refreshPeer(peer, roomType, roomSecret) {
|
||||
_refreshPeer(peer, roomType, roomId) {
|
||||
if (!peer) return false;
|
||||
|
||||
const roomTypeIsSecret = roomType === "secret";
|
||||
const roomSecretsDiffer = peer._roomSecret !== roomSecret;
|
||||
const roomTypesDiffer = Object.keys(peer._roomIds)[0] !== roomType;
|
||||
const roomIdsDiffer = peer._roomIds[roomType] !== roomId;
|
||||
|
||||
// if roomSecrets differs peer is already connected -> abort but update roomSecret and reevaluate auto accept
|
||||
if (roomTypeIsSecret && roomSecretsDiffer) {
|
||||
peer._updateRoomSecret(roomSecret);
|
||||
// if roomType or roomId for roomType differs peer is already connected
|
||||
// -> only update roomSecret and reevaluate auto accept
|
||||
if (roomTypesDiffer || roomIdsDiffer) {
|
||||
peer._updateRoomIds(roomType, roomId);
|
||||
peer._evaluateAutoAccept();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const roomTypesDiffer = peer._roomType !== roomType;
|
||||
|
||||
// if roomTypes differ peer is already connected -> abort
|
||||
if (roomTypesDiffer) return true;
|
||||
|
||||
peer.refresh();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_createOrRefreshPeer(isCaller, peerId, roomType, roomSecret) {
|
||||
_createOrRefreshPeer(isCaller, peerId, roomType, roomId) {
|
||||
const peer = this.peers[peerId];
|
||||
if (peer) {
|
||||
this._refreshPeer(peer, roomType, roomSecret);
|
||||
this._refreshPeer(peer, roomType, roomId);
|
||||
return;
|
||||
}
|
||||
|
||||
this.peers[peerId] = new RTCPeer(this._server, isCaller, peerId, roomType, roomSecret);
|
||||
this.peers[peerId] = new RTCPeer(this._server, isCaller, peerId, roomType, roomId);
|
||||
}
|
||||
|
||||
_onPeerJoined(message) {
|
||||
this._createOrRefreshPeer(false, message.peer.id, message.roomType, message.roomSecret);
|
||||
this._createOrRefreshPeer(false, message.peer.id, message.roomType, message.roomId);
|
||||
}
|
||||
|
||||
_onPeers(message) {
|
||||
message.peers.forEach(peer => {
|
||||
this._createOrRefreshPeer(true, peer.id, message.roomType, message.roomSecret);
|
||||
this._createOrRefreshPeer(true, peer.id, message.roomType, message.roomId);
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -899,7 +970,7 @@ class PeersManager {
|
|||
_onPeerLeft(message) {
|
||||
if (message.disconnect === true) {
|
||||
// if user actively disconnected from PairDrop server, disconnect all peer to peer connections immediately
|
||||
Events.fire('peer-disconnected', message.peerId);
|
||||
this._disconnectOrRemoveRoomTypeByPeerId(message.peerId, message.roomType);
|
||||
|
||||
// If no peers are connected anymore, we can safely assume that no other tab on the same browser is connected:
|
||||
// Tidy up peerIds in localStorage
|
||||
|
@ -923,14 +994,42 @@ class PeersManager {
|
|||
if (peer._channel) peer._channel.onclose = null;
|
||||
peer._conn.close();
|
||||
peer._busy = false;
|
||||
peer._roomIds = {};
|
||||
}
|
||||
|
||||
_onRoomSecretsDeleted(roomSecrets) {
|
||||
for (let i=0; i<roomSecrets.length; i++) {
|
||||
this._disconnectOrRemoveRoomTypeByRoomId('secret', roomSecrets[i]);
|
||||
}
|
||||
}
|
||||
|
||||
_onLeavePublicRoom(publicRoomId) {
|
||||
this._disconnectOrRemoveRoomTypeByRoomId('public-id', publicRoomId);
|
||||
}
|
||||
|
||||
_onSecretRoomDeleted(roomSecret) {
|
||||
for (const peerId in this.peers) {
|
||||
const peer = this.peers[peerId];
|
||||
if (peer._roomType === 'secret' && peer._roomSecret === roomSecret) {
|
||||
this._onPeerDisconnected(peerId);
|
||||
}
|
||||
this._disconnectOrRemoveRoomTypeByRoomId('secret', roomSecret);
|
||||
}
|
||||
|
||||
_disconnectOrRemoveRoomTypeByRoomId(roomType, roomId) {
|
||||
const peerIds = this._getPeerIdsFromRoomId(roomId);
|
||||
|
||||
if (!peerIds.length) return;
|
||||
|
||||
for (let i=0; i<peerIds.length; i++) {
|
||||
this._disconnectOrRemoveRoomTypeByPeerId(peerIds[i], roomType);
|
||||
}
|
||||
}
|
||||
|
||||
_disconnectOrRemoveRoomTypeByPeerId(peerId, roomType) {
|
||||
const peer = this.peers[peerId];
|
||||
|
||||
if (!peer) return;
|
||||
|
||||
if (peer._getRoomTypes().length > 1) {
|
||||
peer._removeRoomType(roomType);
|
||||
} else {
|
||||
Events.fire('peer-disconnected', peerId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -961,20 +1060,26 @@ class PeersManager {
|
|||
}
|
||||
|
||||
_onAutoAcceptUpdated(roomSecret, autoAccept) {
|
||||
const peerId = this._getPeerIdFromRoomSecret(roomSecret);
|
||||
const peerId = this._getPeerIdsFromRoomId(roomSecret)[0];
|
||||
|
||||
if (!peerId) return;
|
||||
|
||||
this.peers[peerId]._setAutoAccept(autoAccept);
|
||||
}
|
||||
|
||||
_getPeerIdFromRoomSecret(roomSecret) {
|
||||
_getPeerIdsFromRoomId(roomId) {
|
||||
if (!roomId) return [];
|
||||
|
||||
let peerIds = []
|
||||
for (const peerId in this.peers) {
|
||||
const peer = this.peers[peerId];
|
||||
// peer must have same roomSecret and not be on the same browser.
|
||||
if (peer._roomSecret === roomSecret && !peer._isSameBrowser()) {
|
||||
return peer._peerId;
|
||||
|
||||
// peer must have same roomId.
|
||||
if (Object.values(peer._roomIds).includes(roomId)) {
|
||||
peerIds.push(peer._peerId);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return peerIds;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1060,7 +1165,7 @@ class FileDigester {
|
|||
}
|
||||
|
||||
class Events {
|
||||
static fire(type, detail) {
|
||||
static fire(type, detail = {}) {
|
||||
window.dispatchEvent(new CustomEvent(type, { detail: detail }));
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4,6 +4,8 @@
|
|||
--icon-size: 24px;
|
||||
--primary-color: #4285f4;
|
||||
--paired-device-color: #00a69c;
|
||||
--public-room-color: #db8500;
|
||||
--accent-color: var(--primary-color);
|
||||
--peer-width: 120px;
|
||||
color-scheme: light dark;
|
||||
}
|
||||
|
@ -56,7 +58,6 @@ html {
|
|||
|
||||
.row {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
|
@ -83,6 +84,10 @@ html {
|
|||
bottom: 0;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
header {
|
||||
position: absolute;
|
||||
align-items: baseline;
|
||||
|
@ -220,10 +225,6 @@ a,
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
hr {
|
||||
color: white;
|
||||
}
|
||||
|
||||
input {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -289,11 +290,6 @@ x-noscript {
|
|||
overscroll-behavior-x: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 425px) {
|
||||
header:has(#edit-pair-devices:not([hidden]))~#center {
|
||||
--footer-height: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Peers List */
|
||||
|
||||
|
@ -466,7 +462,6 @@ x-peer {
|
|||
|
||||
x-peer label {
|
||||
width: var(--peer-width);
|
||||
cursor: pointer;
|
||||
touch-action: manipulation;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
position: relative;
|
||||
|
@ -495,10 +490,14 @@ x-peer .icon-wrapper {
|
|||
display: flex;
|
||||
}
|
||||
|
||||
x-peer:not(.type-ip).type-secret .icon-wrapper {
|
||||
x-peer.type-secret .icon-wrapper {
|
||||
background: var(--paired-device-color);
|
||||
}
|
||||
|
||||
x-peer:not(.type-ip):not(.type-secret).type-public-id .icon-wrapper {
|
||||
background: var(--public-room-color);
|
||||
}
|
||||
|
||||
x-peer x-icon > .highlight-wrapper {
|
||||
align-self: center;
|
||||
align-items: center;
|
||||
|
@ -507,17 +506,29 @@ x-peer x-icon > .highlight-wrapper {
|
|||
}
|
||||
|
||||
x-peer x-icon > .highlight-wrapper > .highlight {
|
||||
width: 6px;
|
||||
width: 15px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
border-radius: 4px;
|
||||
margin-left: 1px;
|
||||
margin-right: 1px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
x-peer.type-secret x-icon > .highlight-wrapper > .highlight {
|
||||
x-peer.type-ip x-icon > .highlight-wrapper > .highlight.highlight-room-ip {
|
||||
background-color: var(--primary-color);
|
||||
display: inline;
|
||||
}
|
||||
|
||||
x-peer.type-secret x-icon > .highlight-wrapper > .highlight.highlight-room-secret {
|
||||
background-color: var(--paired-device-color);
|
||||
display: inline;
|
||||
}
|
||||
|
||||
x-peer.type-public-id x-icon > .highlight-wrapper > .highlight.highlight-room-public-id {
|
||||
background-color: var(--public-room-color);
|
||||
display: inline;
|
||||
}
|
||||
|
||||
x-peer:not([status]):hover x-icon,
|
||||
x-peer:not([status]):focus x-icon {
|
||||
transform: scale(1.05);
|
||||
|
@ -591,12 +602,11 @@ x-peer[drop] x-icon {
|
|||
|
||||
footer {
|
||||
position: relative;
|
||||
margin-top: auto;
|
||||
z-index: 2;
|
||||
align-items: center;
|
||||
padding: 0 0 16px 0;
|
||||
text-align: center;
|
||||
cursor: default;
|
||||
margin: auto 5px 5px;
|
||||
}
|
||||
|
||||
footer .logo {
|
||||
|
@ -606,45 +616,72 @@ footer .logo {
|
|||
margin-top: -10px;
|
||||
}
|
||||
|
||||
footer .font-body2 {
|
||||
color: var(--primary-color);
|
||||
margin: auto 18px;
|
||||
.discovery-wrapper {
|
||||
font-size: 12px;
|
||||
max-width: 350px;
|
||||
margin: 10px auto auto;
|
||||
border: 3px solid var(--border-color);
|
||||
border-radius: 0.5rem;
|
||||
padding: 2px;
|
||||
background-color: rgb(var(--bg-color));
|
||||
transition: background-color 0.5s ease;
|
||||
}
|
||||
|
||||
#on-this-network {
|
||||
border-bottom: solid 4px var(--primary-color);
|
||||
padding-bottom: 1px;
|
||||
word-break: keep-all;
|
||||
/*You can be discovered wrapper*/
|
||||
.discovery-wrapper > div:first-of-type {
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
#paired-devices {
|
||||
border-bottom: solid 4px var(--paired-device-color);
|
||||
padding-bottom: 1px;
|
||||
|
||||
.discovery-wrapper .badge {
|
||||
word-break: keep-all;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
border-radius: 0.3rem/0.3rem;
|
||||
padding-right: 0.3rem;
|
||||
padding-left: 0.3em;
|
||||
background-color: var(--badge-color);
|
||||
color: white;
|
||||
transition: background-color 0.5s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.badge-room-ip {
|
||||
background-color: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.badge-room-secret {
|
||||
background-color: var(--paired-device-color);
|
||||
border-color: var(--paired-device-color);
|
||||
}
|
||||
|
||||
.badge-room-public-id {
|
||||
background-color: var(--public-room-color);
|
||||
border-color: var(--public-room-color);
|
||||
}
|
||||
|
||||
#display-name {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
border: none;
|
||||
outline: none;
|
||||
max-width: 15em;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
cursor: text;
|
||||
margin-left: -1rem;
|
||||
margin-bottom: -6px;
|
||||
padding-right: 0.3rem;
|
||||
padding-left: 0.3em;
|
||||
padding-bottom: 0.1rem;
|
||||
border-radius: 1.3rem/30%;
|
||||
border-right: solid 1rem transparent;
|
||||
border-left: solid 1rem transparent;
|
||||
background-clip: padding-box;
|
||||
background-color: rgba(var(--text-color), 43%);
|
||||
color: white;
|
||||
transition: background-color 0.5s ease;
|
||||
overflow: hidden;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#edit-pen {
|
||||
|
@ -653,7 +690,6 @@ footer .font-body2 {
|
|||
margin-left: -1rem;
|
||||
margin-bottom: -2px;
|
||||
position: relative;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
/* Dialog */
|
||||
|
@ -671,7 +707,6 @@ x-dialog x-paper {
|
|||
z-index: 3;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 16px 24px;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
overflow: hidden;
|
||||
|
@ -680,7 +715,27 @@ x-dialog x-paper {
|
|||
will-change: transform;
|
||||
}
|
||||
|
||||
#pair-device-dialog x-paper {
|
||||
x-paper > .row:first-of-type {
|
||||
background-color: var(--accent-color);
|
||||
border-bottom: solid 4px var(--border-color);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
x-paper > .row:first-of-type h2 {
|
||||
color: white;
|
||||
}
|
||||
|
||||
#pair-device-dialog,
|
||||
#edit-paired-devices-dialog {
|
||||
--accent-color: var(--paired-device-color);
|
||||
}
|
||||
|
||||
#public-room-dialog {
|
||||
--accent-color: var(--public-room-color);
|
||||
}
|
||||
|
||||
#pair-device-dialog x-paper,
|
||||
#public-room-dialog x-paper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
|
@ -695,6 +750,12 @@ x-dialog x-paper {
|
|||
background: var(--paired-device-color);
|
||||
}
|
||||
|
||||
#public-room-dialog ::-moz-selection,
|
||||
#public-room-dialog ::selection {
|
||||
color: black;
|
||||
background: var(--public-room-color);
|
||||
}
|
||||
|
||||
x-dialog:not([show]) {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
@ -714,18 +775,19 @@ x-dialog a {
|
|||
|
||||
/* Pair Devices Dialog */
|
||||
|
||||
#key-input-container {
|
||||
.input-key-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#key-input-container > input {
|
||||
.input-key-container > input {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
font-size: 30px;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
display: -webkit-box !important;
|
||||
display: -webkit-flex !important;
|
||||
display: -moz-flex !important;
|
||||
|
@ -736,15 +798,15 @@ x-dialog a {
|
|||
justify-content: center;
|
||||
}
|
||||
|
||||
#key-input-container > input + * {
|
||||
.input-key-container > input + * {
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
#key-input-container > input:nth-of-type(4) {
|
||||
.input-key-container.six-chars > input:nth-of-type(4) {
|
||||
margin-left: 5%;
|
||||
}
|
||||
|
||||
#room-key {
|
||||
.key {
|
||||
-webkit-user-select: text;
|
||||
-moz-user-select: text;
|
||||
user-select: text;
|
||||
|
@ -755,17 +817,48 @@ x-dialog a {
|
|||
margin: 15px -15px;
|
||||
}
|
||||
|
||||
#room-key-qr-code {
|
||||
.key-qr-code {
|
||||
margin: 16px;
|
||||
}
|
||||
|
||||
#pair-instructions {
|
||||
.key-instructions {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
x-dialog h2 {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
x-dialog hr {
|
||||
margin: 20px -24px 20px -24px;
|
||||
border: solid 1.25px var(--border-color);
|
||||
height: 3px;
|
||||
border: none;
|
||||
width: 100%;
|
||||
background-color: var(--border-color);
|
||||
}
|
||||
|
||||
.hr-note {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.hr-note hr {
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
|
||||
.hr-note > div {
|
||||
height: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
|
||||
|
||||
.hr-note > div > span {
|
||||
padding: 3px 10px;
|
||||
border-radius: 10px;
|
||||
color: rgb(var(--text-color));
|
||||
background-color: rgb(var(--bg-color));
|
||||
border: var(--border-color) solid 3px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
#pair-device-dialog x-background {
|
||||
|
@ -859,22 +952,17 @@ x-dialog hr {
|
|||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.paired-device > .auto-accept {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Receive Dialog */
|
||||
|
||||
x-dialog .row {
|
||||
margin-top: 24px;
|
||||
margin-bottom: 8px;
|
||||
x-paper > .row {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* button row*/
|
||||
x-paper > .button-row {
|
||||
margin: 25px -24px -15px;
|
||||
border-top: solid 2.5px var(--border-color);
|
||||
border-top: solid 3px var(--border-color);
|
||||
height: 50px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
x-paper > .button-row > .button {
|
||||
|
@ -882,16 +970,16 @@ x-paper > .button-row > .button {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
x-paper > .button-row > .button:not(:first-child) {
|
||||
border-right: solid 1.5px var(--border-color);
|
||||
}
|
||||
|
||||
x-paper > .button-row > .button:not(:last-child) {
|
||||
border-left: solid 2.5px var(--border-color);
|
||||
border-left: solid 1.5px var(--border-color);
|
||||
}
|
||||
|
||||
.file-description {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.file-description .row {
|
||||
margin: 0
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.file-description span {
|
||||
|
@ -902,23 +990,29 @@ x-paper > .button-row > .button:not(:last-child) {
|
|||
.file-name {
|
||||
font-style: italic;
|
||||
max-width: 100%;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.file-stem {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
padding-right: 1px;
|
||||
}
|
||||
|
||||
/* Send Text Dialog */
|
||||
/* Todo: add pair underline to send / receive dialogs displayName */
|
||||
x-dialog .dialog-subheader {
|
||||
margin-bottom: 25px;
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
#send-text-dialog .display-name-wrapper {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
#text-input {
|
||||
min-height: 200px;
|
||||
margin: 14px auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Receive Text Dialog */
|
||||
|
@ -933,7 +1027,6 @@ x-dialog .dialog-subheader {
|
|||
-moz-user-select: text;
|
||||
user-select: text;
|
||||
white-space: pre-wrap;
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
#receive-text-dialog #text a {
|
||||
|
@ -1008,12 +1101,13 @@ x-dialog .dialog-subheader {
|
|||
cursor: pointer;
|
||||
user-select: none;
|
||||
background: inherit;
|
||||
color: var(--primary-color);
|
||||
color: var(--accent-color);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.button[disabled] {
|
||||
color: #5B5B66;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1094,8 +1188,7 @@ button::-moz-focus-inner {
|
|||
border: none;
|
||||
outline: none;
|
||||
padding: 16px 24px;
|
||||
border-radius: 16px;
|
||||
margin: 10px 0;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-family: inherit;
|
||||
background: #f1f3f4;
|
||||
|
@ -1305,14 +1398,6 @@ x-peers:empty~x-instructions {
|
|||
}
|
||||
|
||||
/* Responsive Styles */
|
||||
@media screen and (max-width: 360px) {
|
||||
x-dialog x-paper {
|
||||
padding: 15px;
|
||||
}
|
||||
x-paper > .button-row {
|
||||
margin: auto -15px -15px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-height: 800px) {
|
||||
footer {
|
||||
|
@ -1335,8 +1420,9 @@ body {
|
|||
--text-color: 51,51,51;
|
||||
--bg-color: 250,250,250; /*rgb code*/
|
||||
--bg-color-test: 18,18,18;
|
||||
--bg-color-secondary: #f1f3f4;
|
||||
--border-color: #e7e8e8;
|
||||
--bg-color-secondary: #e4e4e4;
|
||||
--border-color: rgb(169, 169, 169);
|
||||
--badge-color: #a5a5a5;
|
||||
}
|
||||
|
||||
/* Dark theme colors */
|
||||
|
@ -1344,7 +1430,8 @@ body.dark-theme {
|
|||
--text-color: 238,238,238;
|
||||
--bg-color: 18,18,18; /*rgb code*/
|
||||
--bg-color-secondary: #333;
|
||||
--border-color: #252525;
|
||||
--border-color: rgb(238,238,238);
|
||||
--badge-color: #717171;
|
||||
}
|
||||
|
||||
/* Colored Elements */
|
||||
|
@ -1378,7 +1465,7 @@ x-dialog x-paper {
|
|||
|
||||
/* Image/Video/Audio Preview */
|
||||
.file-preview {
|
||||
margin: 10px -24px 40px -24px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.file-preview:empty {
|
||||
|
@ -1402,15 +1489,17 @@ x-dialog x-paper {
|
|||
--text-color: 238,238,238;
|
||||
--bg-color: 18,18,18; /*rgb code*/
|
||||
--bg-color-secondary: #333;
|
||||
--border-color: #252525;
|
||||
--border-color: rgb(238,238,238);
|
||||
--badge-color: #717171;
|
||||
}
|
||||
|
||||
/* Override dark mode with light mode styles if the user decides to swap */
|
||||
body.light-theme {
|
||||
--text-color: 51,51,51;
|
||||
--bg-color: 250,250,250; /*rgb code*/
|
||||
--bg-color-secondary: #f1f3f4;
|
||||
--border-color: #e7e8e8;
|
||||
--bg-color-secondary: #e4e4e4;
|
||||
--border-color: rgb(169, 169, 169);
|
||||
--badge-color: #a5a5a5;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue