mirror of
https://github.com/schlagmichdoch/PairDrop.git
synced 2025-04-20 15:06:15 -04:00
merge master into branch
This commit is contained in:
commit
de76da52fe
12 changed files with 952 additions and 499 deletions
2
.github/workflows/github-image.yml
vendored
2
.github/workflows/github-image.yml
vendored
|
@ -16,7 +16,7 @@ on:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: ghcr.io
|
REGISTRY: ghcr.io
|
||||||
IMAGE_NAME: ${{ github.repository | downcase }}
|
IMAGE_NAME: ${{ github.repository }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-push-image:
|
build-and-push-image:
|
||||||
|
|
|
@ -1,32 +1,35 @@
|
||||||
# Deployment Notes
|
# Deployment Notes
|
||||||
The easiest way to get PairDrop up and running is by using Docker.
|
The easiest way to get PairDrop up and running is by using Docker.
|
||||||
|
|
||||||
## Deployment with Docker from Docker Hub
|
## Deployment with Docker
|
||||||
|
|
||||||
|
### Docker Image from Docker Hub
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -d --restart=unless-stopped --name=pairdrop -p 127.0.0.1:3000:3000 lscr.io/linuxserver/pairdrop
|
docker run -d --restart=unless-stopped --name=pairdrop -p 127.0.0.1:3000:3000 lscr.io/linuxserver/pairdrop
|
||||||
```
|
```
|
||||||
|
|
||||||
> You must use a server proxy to set the X-Forwarded-For to prevent all clients from discovering each other (See [#HTTP-Server](#http-server)).
|
> You must use a server proxy to set the X-Forwarded-For to prevent all clients from discovering each other (See [#HTTP-Server](#http-server)).
|
||||||
>
|
>
|
||||||
> To prevent bypassing the proxy and reach the docker container directly, `127.0.0.1` is specified in the run command.
|
> To prevent bypassing the proxy by reaching the docker container directly, `127.0.0.1` is specified in the run command.
|
||||||
|
|
||||||
### Options / Flags
|
#### Options / Flags
|
||||||
Set options by using the following flags in the `docker run` command:
|
Set options by using the following flags in the `docker run` command:
|
||||||
|
|
||||||
#### Port
|
##### Port
|
||||||
```bash
|
```bash
|
||||||
-p 127.0.0.1:8080:3000
|
-p 127.0.0.1:8080:3000
|
||||||
```
|
```
|
||||||
> Specify the port used by the docker image
|
> Specify the port used by the docker image
|
||||||
> - 3000 -> `-p 127.0.0.1:3000:3000`
|
> - 3000 -> `-p 127.0.0.1:3000:3000`
|
||||||
> - 8080 -> `-p 127.0.0.1:8080:3000`
|
> - 8080 -> `-p 127.0.0.1:8080:3000`
|
||||||
#### Rate limiting requests
|
##### Rate limiting requests
|
||||||
```
|
```
|
||||||
-e RATE_LIMIT=true
|
-e RATE_LIMIT=true
|
||||||
```
|
```
|
||||||
> Limits clients to 100 requests per 5 min
|
> Limits clients to 1000 requests per 5 min
|
||||||
|
|
||||||
#### Websocket Fallback (for VPN)
|
##### Websocket Fallback (for VPN)
|
||||||
```bash
|
```bash
|
||||||
-e WS_FALLBACK=true
|
-e WS_FALLBACK=true
|
||||||
```
|
```
|
||||||
|
@ -69,8 +72,18 @@ Set options by using the following flags in the `docker run` command:
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
## Deployment with Docker with self-built image
|
### Docker Image from GHCR
|
||||||
### Build the image
|
```bash
|
||||||
|
docker run -d --restart=unless-stopped --name=pairdrop -p 127.0.0.1:3000:3000 ghcr.io/schlagmichdoch/pairdrop npm run start:prod
|
||||||
|
```
|
||||||
|
> You must use a server proxy to set the X-Forwarded-For to prevent all clients from discovering each other (See [#HTTP-Server](#http-server)).
|
||||||
|
>
|
||||||
|
> To prevent bypassing the proxy by reaching the docker container directly, `127.0.0.1` is specified in the run command.
|
||||||
|
>
|
||||||
|
> To specify options replace `npm run start:prod` according to [the documentation below.](#options--flags-1)
|
||||||
|
|
||||||
|
### Docker Image self-built
|
||||||
|
#### Build the image
|
||||||
```bash
|
```bash
|
||||||
docker build --pull . -f Dockerfile -t pairdrop
|
docker build --pull . -f Dockerfile -t pairdrop
|
||||||
```
|
```
|
||||||
|
@ -78,15 +91,45 @@ docker build --pull . -f Dockerfile -t pairdrop
|
||||||
>
|
>
|
||||||
> `--pull` ensures always the latest node image is used.
|
> `--pull` ensures always the latest node image is used.
|
||||||
|
|
||||||
### Run the image
|
#### Run the image
|
||||||
```bash
|
```bash
|
||||||
docker run -d --restart=unless-stopped --name=pairdrop -p 127.0.0.1:3000:3000 -it pairdrop npm run start:prod
|
docker run -d --restart=unless-stopped --name=pairdrop -p 127.0.0.1:3000:3000 -it pairdrop npm run start:prod
|
||||||
```
|
```
|
||||||
> You must use a server proxy to set the X-Forwarded-For to prevent all clients from discovering each other (See [#HTTP-Server](#http-server)).
|
> You must use a server proxy to set the X-Forwarded-For to prevent all clients from discovering each other (See [#HTTP-Server](#http-server)).
|
||||||
>
|
>
|
||||||
> To prevent bypassing the proxy and reach the docker container directly, `127.0.0.1` is specified in the run command.
|
> To prevent bypassing the proxy by reaching the docker container directly, `127.0.0.1` is specified in the run command.
|
||||||
>
|
>
|
||||||
> To specify options replace `npm run start:prod` according to [the documentation above.](#options--flags)
|
> To specify options replace `npm run start:prod` according to [the documentation below.](#options--flags-1)
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## Deployment with Docker Compose
|
||||||
|
Here's an example docker-compose file:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: "2"
|
||||||
|
services:
|
||||||
|
pairdrop:
|
||||||
|
image: lscr.io/linuxserver/pairdrop:latest
|
||||||
|
container_name: pairdrop
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- PUID=1000 # UID to run the application as
|
||||||
|
- PGID=1000 # GID to run the application as
|
||||||
|
- WS_FALLBACK=false # Set to true to enable websocket fallback if the peer to peer WebRTC connection is not available to the client.
|
||||||
|
- RATE_LIMIT=false # Set to true to limit clients to 1000 requests per 5 min.
|
||||||
|
- TZ=Etc/UTC # Time Zone
|
||||||
|
ports:
|
||||||
|
- 127.0.0.1:3000:3000 # Web UI
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the compose file with `docker compose up -d`.
|
||||||
|
|
||||||
|
> You must use a server proxy to set the X-Forwarded-For to prevent all clients from discovering each other (See [#HTTP-Server](#http-server)).
|
||||||
|
>
|
||||||
|
> To prevent bypassing the proxy by reaching the docker container directly, `127.0.0.1` is specified in the run command.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
## Deployment with node
|
## Deployment with node
|
||||||
|
|
||||||
|
@ -169,7 +212,7 @@ npm start -- --localhost-only
|
||||||
>
|
>
|
||||||
> You must use a server proxy to set the X-Forwarded-For to prevent all clients from discovering each other (See [#HTTP-Server](#http-server)).
|
> You must use a server proxy to set the X-Forwarded-For to prevent all clients from discovering each other (See [#HTTP-Server](#http-server)).
|
||||||
>
|
>
|
||||||
> Use this when deploying PairDrop with node to prevent bypassing the proxy and reach the docker container directly.
|
> Use this when deploying PairDrop with node to prevent bypassing the proxy by reaching the docker container directly.
|
||||||
|
|
||||||
#### Automatic restart on error
|
#### Automatic restart on error
|
||||||
```bash
|
```bash
|
||||||
|
@ -183,7 +226,7 @@ npm start -- --auto-restart
|
||||||
```bash
|
```bash
|
||||||
npm start -- --rate-limit
|
npm start -- --rate-limit
|
||||||
```
|
```
|
||||||
> Limits clients to 100 requests per 5 min
|
> Limits clients to 1000 requests per 5 min
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
@ -218,7 +261,7 @@ When running PairDrop, the `X-Forwarded-For` header has to be set by a proxy. Ot
|
||||||
|
|
||||||
### Using nginx
|
### Using nginx
|
||||||
#### Allow http and https requests
|
#### Allow http and https requests
|
||||||
```nginx configuration
|
```
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
|
|
||||||
|
@ -251,7 +294,7 @@ server {
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Automatic http to https redirect:
|
#### Automatic http to https redirect:
|
||||||
```nginx configuration
|
```
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
|
|
||||||
|
|
4
package-lock.json
generated
4
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "pairdrop",
|
"name": "pairdrop",
|
||||||
"version": "1.1.1",
|
"version": "1.1.3",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "pairdrop",
|
"name": "pairdrop",
|
||||||
"version": "1.1.1",
|
"version": "1.1.3",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "pairdrop",
|
"name": "pairdrop",
|
||||||
"version": "1.1.1",
|
"version": "1.1.3",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -69,45 +69,49 @@
|
||||||
<use xlink:href="#clear-pair-devices-icon" />
|
<use xlink:href="#clear-pair-devices-icon" />
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
|
<a id="cancel-paste-mode-btn" class="button" close hidden>Done</a>
|
||||||
</header>
|
</header>
|
||||||
<a id="cancelPasteModeBtn" class="button" close hidden>Done</a>
|
<!-- Center -->
|
||||||
|
<div id="center">
|
||||||
<!-- Peers -->
|
<!-- Peers -->
|
||||||
|
<div class="x-peers-filler"></div>
|
||||||
<x-peers class="center"></x-peers>
|
<x-peers class="center"></x-peers>
|
||||||
<x-no-peers>
|
<x-no-peers>
|
||||||
<h2>Open PairDrop on other devices to send files</h2>
|
<h2>Open PairDrop on other devices to send files</h2>
|
||||||
<div>Pair devices to be discoverable on other networks</div>
|
<div>Pair devices to be discoverable on other networks</div>
|
||||||
</x-no-peers>
|
</x-no-peers>
|
||||||
<x-instructions desktop="Click to send files or right click to send a message" mobile="Tap to send files or long tap to send a message">
|
<x-instructions desktop="Click to send files or right click to send a message" mobile="Tap to send files or long tap to send a message">
|
||||||
<p id="pasteFilename"></p>
|
<p id="paste-filename"></p>
|
||||||
</x-instructions>
|
</x-instructions>
|
||||||
|
</div>
|
||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
<footer class="column">
|
<footer class="column">
|
||||||
<svg class="icon logo">
|
<svg class="icon logo">
|
||||||
<use xlink:href="#wifi-tethering" />
|
<use xlink:href="#wifi-tethering" />
|
||||||
</svg>
|
</svg>
|
||||||
<div id="displayName" placeholder=" "></div>
|
<div id="display-name" placeholder=" "></div>
|
||||||
<div class="font-body2">
|
<div class="font-body2">
|
||||||
You can be discovered by everyone <span id="on-this-network">on this network</span>
|
You can be discovered by everyone <span id="on-this-network">on this network</span>
|
||||||
<span id="and-by-paired-devices" hidden> and by <span id="paired-devices">paired devices</span></span>
|
<span id="and-by-paired-devices" hidden> and by <span id="paired-devices">paired devices</span></span>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
<!-- Pair Device Dialog -->
|
<!-- Pair Device Dialog -->
|
||||||
<x-dialog id="pairDeviceDialog">
|
<x-dialog id="pair-device-dialog">
|
||||||
<form action="#">
|
<form action="#">
|
||||||
<x-background class="full center text-center">
|
<x-background class="full center text-center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
<h2 class="center">Pair Devices</h2>
|
<h2 class="center">Pair Devices</h2>
|
||||||
<div class="center" id="roomKeyQrCode"></div>
|
<div id="room-key-qr-code" class="center"></div>
|
||||||
<h1 class="center" id="roomKey">000 000</h1>
|
<h1 id="room-key" class="center">000 000</h1>
|
||||||
<div id="pairInstructions" class="font-subheading center text-center">Input this key on another device<br>or scan the QR-Code.</div>
|
<div id="pair-instructions" class="font-subheading center text-center">Input this key on another device<br>or scan the QR-Code.</div>
|
||||||
<hr>
|
<hr>
|
||||||
<div id="keyInputContainer">
|
<div id="key-input-container">
|
||||||
<input type="tel" id="char0" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" autofocus contenteditable placeholder="" disabled>
|
<input id="char0" type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" autofocus contenteditable placeholder="" disabled>
|
||||||
<input type="tel" id="char1" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
<input id="char1" type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||||
<input type="tel" id="char2" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
<input id="char2" type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||||
<input type="tel" id="char3" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
<input id="char3" type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||||
<input type="tel" id="char4" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
<input id="char4" type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||||
<input type="tel" id="char5" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
<input id="char5" type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||||
</div>
|
</div>
|
||||||
<div class="font-subheading center text-center">Enter key from another device to continue.</div>
|
<div class="font-subheading center text-center">Enter key from another device to continue.</div>
|
||||||
<div class="row-reverse space-between">
|
<div class="row-reverse space-between">
|
||||||
|
@ -120,7 +124,7 @@
|
||||||
</form>
|
</form>
|
||||||
</x-dialog>
|
</x-dialog>
|
||||||
<!-- Clear Devices Dialog -->
|
<!-- Clear Devices Dialog -->
|
||||||
<x-dialog id="clearDevicesDialog">
|
<x-dialog id="clear-devices-dialog">
|
||||||
<form action="#">
|
<form action="#">
|
||||||
<x-background class="full center text-center">
|
<x-background class="full center text-center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
|
@ -135,43 +139,43 @@
|
||||||
</form>
|
</form>
|
||||||
</x-dialog>
|
</x-dialog>
|
||||||
<!-- Receive Request Dialog -->
|
<!-- Receive Request Dialog -->
|
||||||
<x-dialog id="receiveRequestDialog">
|
<x-dialog id="receive-request-dialog">
|
||||||
<x-background class="full center">
|
<x-background class="full center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
<h2 class="center">PairDrop</h2>
|
<h2 class="center">PairDrop</h2>
|
||||||
<div class="text-center file-description">
|
<div class="text-center file-description">
|
||||||
<div>
|
<div>
|
||||||
<span id="requestingPeerDisplayName"></span>
|
<span id="requesting-peer-display-name"></span>
|
||||||
<span>would like to share</span>
|
<span>would like to share</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="row" id="fileName">
|
<div id="file-name" class="row" >
|
||||||
<span id="fileStem"></span>
|
<span id="file-stem"></span>
|
||||||
<span id="fileExtension"></span>
|
<span id="file-extension"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<span id="fileOther"></span>
|
<span id="file-other"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="font-body2 text-center file-size"></div>
|
<div class="font-body2 text-center file-size"></div>
|
||||||
<div class="center file-preview"></div>
|
<div class="center file-preview"></div>
|
||||||
<div class="row-reverse space-between">
|
<div class="row-reverse space-between">
|
||||||
<button class="button" id="acceptRequest" title="ENTER" autofocus>Accept</button>
|
<button id="accept-request" class="button" title="ENTER" autofocus>Accept</button>
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
<button class="button" id="declineRequest" title="ESCAPE">Decline</button>
|
<button id="decline-request" class="button" title="ESCAPE">Decline</button>
|
||||||
</div>
|
</div>
|
||||||
</x-paper>
|
</x-paper>
|
||||||
</x-background>
|
</x-background>
|
||||||
</x-dialog>
|
</x-dialog>
|
||||||
<!-- Receive File Dialog -->
|
<!-- Receive File Dialog -->
|
||||||
<x-dialog id="receiveFileDialog">
|
<x-dialog id="receive-file-dialog">
|
||||||
<x-background class="full center">
|
<x-background class="full center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
<h2 class="center" id="receiveTitle"></h2>
|
<h2 id="receive-title" class="center"></h2>
|
||||||
<div class="text-center file-description"></div>
|
<div class="text-center file-description"></div>
|
||||||
<div class="font-body2 text-center file-size"></div>
|
<div class="font-body2 text-center file-size"></div>
|
||||||
<div class="center file-preview"></div>
|
<div class="center file-preview"></div>
|
||||||
<div class="row-reverse space-between">
|
<div class="row-reverse space-between">
|
||||||
<a class="button" id="shareOrDownload" autofocus></a>
|
<a id="share-or-download" class="button" autofocus></a>
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
<button class="button" close>Close</button>
|
<button class="button" close>Close</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -179,12 +183,16 @@
|
||||||
</x-background>
|
</x-background>
|
||||||
</x-dialog>
|
</x-dialog>
|
||||||
<!-- Send Text Dialog -->
|
<!-- Send Text Dialog -->
|
||||||
<x-dialog id="sendTextDialog">
|
<x-dialog id="send-text-dialog">
|
||||||
<form action="#">
|
<form action="#">
|
||||||
<x-background class="full center">
|
<x-background class="full center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
<h2>PairDrop - Send a Message</h2>
|
<h2 class="text-center">PairDrop</h2>
|
||||||
<div id="textInput" class="textarea" role="textbox" placeholder="Send a message" autocapitalize="none" spellcheck="false" autofocus contenteditable></div>
|
<div class="text-center">
|
||||||
|
<span>Send a Message to</span>
|
||||||
|
<span id="text-send-peer-display-name"></span>
|
||||||
|
</div>
|
||||||
|
<div id="text-input" class="textarea" role="textbox" autocapitalize="none" spellcheck="false" autofocus contenteditable></div>
|
||||||
<div class="row-reverse">
|
<div class="row-reverse">
|
||||||
<button class="button" type="submit" title="STR + ENTER" disabled close>Send</button>
|
<button class="button" type="submit" title="STR + ENTER" disabled close>Send</button>
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
|
@ -195,36 +203,36 @@
|
||||||
</form>
|
</form>
|
||||||
</x-dialog>
|
</x-dialog>
|
||||||
<!-- Receive Text Dialog -->
|
<!-- Receive Text Dialog -->
|
||||||
<x-dialog id="receiveTextDialog">
|
<x-dialog id="receive-text-dialog">
|
||||||
<x-background class="full center">
|
<x-background class="full center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
<h2>PairDrop - Message Received</h2>
|
<h2>PairDrop - Message Received</h2>
|
||||||
<div id="receiveTextDescriptionContainer">
|
<div id="receive-text-description-container">
|
||||||
<span id="receiveTextPeerDisplayName"></span>
|
<span id="receive-text-peer-display-name"></span>
|
||||||
<span>sent the following message:</span>
|
<span>sent the following message:</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="row-separator"></div>
|
<div class="row-separator"></div>
|
||||||
<div id="text"></div>
|
<div id="text"></div>
|
||||||
<div class="row-reverse">
|
<div class="row-reverse">
|
||||||
<button class="button" id="copy" title="CTRL/⌘ + C">Copy</button>
|
<button id="copy" class="button" title="CTRL/⌘ + C">Copy</button>
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
<button class="button" id="close" title="ESCAPE">Close</button>
|
<button id="close" class="button" title="ESCAPE">Close</button>
|
||||||
</div>
|
</div>
|
||||||
</x-paper>
|
</x-paper>
|
||||||
</x-background>
|
</x-background>
|
||||||
</x-dialog>
|
</x-dialog>
|
||||||
<!-- base64PasteDialog Dialog -->
|
<!-- base64 Paste Dialog -->
|
||||||
<x-dialog id="base64PasteDialog">
|
<x-dialog id="base64-paste-dialog">
|
||||||
<x-background class="full center">
|
<x-background class="full center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
<button class="button center" id="base64PasteBtn" title="Paste">Tap here to paste files</button>
|
<button class="button center" id="base64-paste-btn" title="Paste">Tap here to paste files</button>
|
||||||
<button class="button center" close>Close</button>
|
<button class="button center" close>Close</button>
|
||||||
</x-paper>
|
</x-paper>
|
||||||
</x-background>
|
</x-background>
|
||||||
</x-dialog>
|
</x-dialog>
|
||||||
<!-- Toast -->
|
<!-- Toast -->
|
||||||
<div class="toast-container full center">
|
<div class="toast-container full center">
|
||||||
<x-toast class="row" shadow="1" id="toast"></x-toast>
|
<x-toast id="toast" class="row" shadow="1"></x-toast>
|
||||||
</div>
|
</div>
|
||||||
<!-- About Page -->
|
<!-- About Page -->
|
||||||
<x-about id="about" class="full center column">
|
<x-about id="about" class="full center column">
|
||||||
|
|
|
@ -10,7 +10,7 @@ window.pasteMode.activated = false;
|
||||||
// set display name
|
// set display name
|
||||||
Events.on('display-name', e => {
|
Events.on('display-name', e => {
|
||||||
const me = e.detail.message;
|
const me = e.detail.message;
|
||||||
const $displayName = $('displayName')
|
const $displayName = $('display-name')
|
||||||
$displayName.textContent = 'You are known as ' + me.displayName;
|
$displayName.textContent = 'You are known as ' + me.displayName;
|
||||||
$displayName.title = me.deviceName;
|
$displayName.title = me.deviceName;
|
||||||
});
|
});
|
||||||
|
@ -28,7 +28,7 @@ class PeersUI {
|
||||||
Events.on('activate-paste-mode', e => this._activatePasteMode(e.detail.files, e.detail.text));
|
Events.on('activate-paste-mode', e => this._activatePasteMode(e.detail.files, e.detail.text));
|
||||||
this.peers = {};
|
this.peers = {};
|
||||||
|
|
||||||
this.$cancelPasteModeBtn = $('cancelPasteModeBtn');
|
this.$cancelPasteModeBtn = $('cancel-paste-mode-btn');
|
||||||
this.$cancelPasteModeBtn.addEventListener('click', _ => this._cancelPasteMode());
|
this.$cancelPasteModeBtn.addEventListener('click', _ => this._cancelPasteMode());
|
||||||
|
|
||||||
Events.on('dragover', e => this._onDragOver(e));
|
Events.on('dragover', e => this._onDragOver(e));
|
||||||
|
@ -38,8 +38,12 @@ class PeersUI {
|
||||||
Events.on('drop', e => this._onDrop(e));
|
Events.on('drop', e => this._onDrop(e));
|
||||||
Events.on('keydown', e => this._onKeyDown(e));
|
Events.on('keydown', e => this._onKeyDown(e));
|
||||||
|
|
||||||
|
this.$xPeers = $$('x-peers');
|
||||||
this.$xNoPeers = $$('x-no-peers');
|
this.$xNoPeers = $$('x-no-peers');
|
||||||
this.$xInstructions = $$('x-instructions');
|
this.$xInstructions = $$('x-instructions');
|
||||||
|
|
||||||
|
Events.on('peer-added', _ => this.evaluateOverflowing());
|
||||||
|
Events.on('bg-resize', _ => this.evaluateOverflowing());
|
||||||
}
|
}
|
||||||
|
|
||||||
_onKeyDown(e) {
|
_onKeyDown(e) {
|
||||||
|
@ -53,11 +57,11 @@ class PeersUI {
|
||||||
}
|
}
|
||||||
|
|
||||||
_joinPeer(peer, roomType, roomSecret) {
|
_joinPeer(peer, roomType, roomSecret) {
|
||||||
peer.roomType = roomType;
|
peer.roomTypes = [roomType];
|
||||||
peer.roomSecret = roomSecret;
|
peer.roomSecret = roomSecret;
|
||||||
if (this.peers[peer.id]) {
|
if (this.peers[peer.id]) {
|
||||||
this.peers[peer.id].roomType = peer.roomType;
|
if (!this.peers[peer.id].roomTypes.includes(roomType)) this.peers[peer.id].roomTypes.push(roomType);
|
||||||
this._redrawPeer(peer);
|
this._redrawPeer(this.peers[peer.id]);
|
||||||
return; // peer already exists
|
return; // peer already exists
|
||||||
}
|
}
|
||||||
this.peers[peer.id] = peer;
|
this.peers[peer.id] = peer;
|
||||||
|
@ -72,7 +76,15 @@ class PeersUI {
|
||||||
const peerNode = $(peer.id);
|
const peerNode = $(peer.id);
|
||||||
if (!peerNode) return;
|
if (!peerNode) return;
|
||||||
peerNode.classList.remove('type-ip', 'type-secret');
|
peerNode.classList.remove('type-ip', 'type-secret');
|
||||||
peerNode.classList.add(`type-${peer.roomType}`)
|
peer.roomTypes.forEach(roomType => peerNode.classList.add(`type-${roomType}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
evaluateOverflowing() {
|
||||||
|
if (this.$xPeers.clientHeight < this.$xPeers.scrollHeight) {
|
||||||
|
this.$xPeers.classList.add('overflowing');
|
||||||
|
} else {
|
||||||
|
this.$xPeers.classList.remove('overflowing');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onPeers(msg) {
|
_onPeers(msg) {
|
||||||
|
@ -83,6 +95,7 @@ class PeersUI {
|
||||||
const $peer = $(peerId);
|
const $peer = $(peerId);
|
||||||
if (!$peer) return;
|
if (!$peer) return;
|
||||||
$peer.remove();
|
$peer.remove();
|
||||||
|
this.evaluateOverflowing();
|
||||||
if ($$('x-peers:empty')) setTimeout(_ => window.animateBackground(true), 1750); // Start animation again
|
if ($$('x-peers:empty')) setTimeout(_ => window.animateBackground(true), 1750); // Start animation again
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,6 +226,18 @@ class PeersUI {
|
||||||
|
|
||||||
class PeerUI {
|
class PeerUI {
|
||||||
|
|
||||||
|
constructor(peer, connectionHash) {
|
||||||
|
this._peer = peer;
|
||||||
|
this._connectionHash = connectionHash;
|
||||||
|
this._initDom();
|
||||||
|
this._bindListeners();
|
||||||
|
|
||||||
|
$$('x-peers').appendChild(this.$el)
|
||||||
|
Events.fire('peer-added');
|
||||||
|
this.$xInstructions = $$('x-instructions');
|
||||||
|
setTimeout(_ => window.animateBackground(false), 1750); // Stop animation
|
||||||
|
}
|
||||||
|
|
||||||
html() {
|
html() {
|
||||||
let title;
|
let title;
|
||||||
let input = '';
|
let input = '';
|
||||||
|
@ -225,17 +250,24 @@ class PeerUI {
|
||||||
this.$el.innerHTML = `
|
this.$el.innerHTML = `
|
||||||
<label class="column center" title="${title}">
|
<label class="column center" title="${title}">
|
||||||
${input}
|
${input}
|
||||||
<x-icon shadow="1">
|
<x-icon>
|
||||||
|
<div class="icon-wrapper" shadow="1">
|
||||||
<svg class="icon"><use xlink:href="#"/></svg>
|
<svg class="icon"><use xlink:href="#"/></svg>
|
||||||
|
</div>
|
||||||
|
<div class="highlight-wrapper center">
|
||||||
|
<div class="highlight" shadow="1"></div>
|
||||||
|
</div>
|
||||||
</x-icon>
|
</x-icon>
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div class="circle"></div>
|
<div class="circle"></div>
|
||||||
<div class="circle right"></div>
|
<div class="circle right"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="device-descriptor">
|
||||||
<div class="name font-subheading"></div>
|
<div class="name font-subheading"></div>
|
||||||
<div class="device-name font-body2"></div>
|
<div class="device-name font-body2"></div>
|
||||||
<div class="status font-body2"></div>
|
<div class="status font-body2"></div>
|
||||||
<span class="connection-hash font-body2" title="To verify the security of the end-to-end encryption, compare this security number on both devices"></span>
|
<span class="connection-hash font-body2" title="To verify the security of the end-to-end encryption, compare this security number on both devices"></span>
|
||||||
|
</div>
|
||||||
</label>`;
|
</label>`;
|
||||||
|
|
||||||
this.$el.querySelector('svg use').setAttribute('xlink:href', this._icon());
|
this.$el.querySelector('svg use').setAttribute('xlink:href', this._icon());
|
||||||
|
@ -245,23 +277,12 @@ class PeerUI {
|
||||||
this._connectionHash.substring(0, 4) + " " + this._connectionHash.substring(4, 8) + " " + this._connectionHash.substring(8, 12) + " " + this._connectionHash.substring(12, 16);
|
this._connectionHash.substring(0, 4) + " " + this._connectionHash.substring(4, 8) + " " + this._connectionHash.substring(8, 12) + " " + this._connectionHash.substring(12, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(peer, connectionHash) {
|
|
||||||
this._peer = peer;
|
|
||||||
this._roomType = peer.roomType;
|
|
||||||
this._roomSecret = peer.roomSecret;
|
|
||||||
this._connectionHash = connectionHash;
|
|
||||||
this._initDom();
|
|
||||||
this._bindListeners();
|
|
||||||
$$('x-peers').appendChild(this.$el);
|
|
||||||
this.$xInstructions = $$('x-instructions');
|
|
||||||
setTimeout(_ => window.animateBackground(false), 1750); // Stop animation
|
|
||||||
}
|
|
||||||
|
|
||||||
_initDom() {
|
_initDom() {
|
||||||
this.$el = document.createElement('x-peer');
|
this.$el = document.createElement('x-peer');
|
||||||
this.$el.id = this._peer.id;
|
this.$el.id = this._peer.id;
|
||||||
this.$el.ui = this;
|
this.$el.ui = this;
|
||||||
this.$el.classList.add(`type-${this._roomType}`);
|
this._peer.roomTypes.forEach(roomType => this.$el.classList.add(`type-${roomType}`));
|
||||||
|
this.$el.classList.add('center');
|
||||||
this.html();
|
this.html();
|
||||||
|
|
||||||
this._callbackInput = e => this._onFilesSelected(e)
|
this._callbackInput = e => this._onFilesSelected(e)
|
||||||
|
@ -272,7 +293,7 @@ class PeerUI {
|
||||||
this._callbackDragLeave = e => this._onDragEnd(e)
|
this._callbackDragLeave = e => this._onDragEnd(e)
|
||||||
this._callbackDragOver = e => this._onDragOver(e)
|
this._callbackDragOver = e => this._onDragOver(e)
|
||||||
this._callbackContextMenu = e => this._onRightClick(e)
|
this._callbackContextMenu = e => this._onRightClick(e)
|
||||||
this._callbackTouchStart = _ => this._onTouchStart()
|
this._callbackTouchStart = e => this._onTouchStart(e)
|
||||||
this._callbackTouchEnd = e => this._onTouchEnd(e)
|
this._callbackTouchEnd = e => this._onTouchEnd(e)
|
||||||
this._callbackPointerDown = e => this._onPointerDown(e)
|
this._callbackPointerDown = e => this._onPointerDown(e)
|
||||||
// PasteMode
|
// PasteMode
|
||||||
|
@ -393,21 +414,28 @@ class PeerUI {
|
||||||
|
|
||||||
_onRightClick(e) {
|
_onRightClick(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
Events.fire('text-recipient', this._peer.id);
|
Events.fire('text-recipient', {
|
||||||
|
peerId: this._peer.id,
|
||||||
|
deviceName: e.target.closest('x-peer').querySelector('.name').innerText
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_onTouchStart() {
|
_onTouchStart(e) {
|
||||||
this._touchStart = Date.now();
|
this._touchStart = Date.now();
|
||||||
this._touchTimer = setTimeout(_ => this._onTouchEnd(), 610);
|
this._touchTimer = setTimeout(_ => this._onTouchEnd(e), 610);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onTouchEnd(e) {
|
_onTouchEnd(e) {
|
||||||
if (Date.now() - this._touchStart < 500) {
|
if (Date.now() - this._touchStart < 500) {
|
||||||
clearTimeout(this._touchTimer);
|
clearTimeout(this._touchTimer);
|
||||||
} else { // this was a long tap
|
} else if (this._touchTimer) { // this was a long tap
|
||||||
if (e) e.preventDefault();
|
e.preventDefault();
|
||||||
Events.fire('text-recipient', this._peer.id);
|
Events.fire('text-recipient', {
|
||||||
|
peerId: this._peer.id,
|
||||||
|
deviceName: e.target.closest('x-peer').querySelector('.name').innerText
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
this._touchTimer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -469,10 +497,10 @@ class ReceiveDialog extends Dialog {
|
||||||
class ReceiveFileDialog extends ReceiveDialog {
|
class ReceiveFileDialog extends ReceiveDialog {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('receiveFileDialog');
|
super('receive-file-dialog');
|
||||||
|
|
||||||
this.$shareOrDownloadBtn = this.$el.querySelector('#shareOrDownload');
|
this.$shareOrDownloadBtn = this.$el.querySelector('#share-or-download');
|
||||||
this.$receiveTitleNode = this.$el.querySelector('#receiveTitle')
|
this.$receiveTitleNode = this.$el.querySelector('#receive-title')
|
||||||
|
|
||||||
Events.on('files-received', e => this._onFilesReceived(e.detail.sender, e.detail.files, e.detail.request));
|
Events.on('files-received', e => this._onFilesReceived(e.detail.sender, e.detail.files, e.detail.request));
|
||||||
this._filesQueue = [];
|
this._filesQueue = [];
|
||||||
|
@ -631,15 +659,15 @@ class ReceiveFileDialog extends ReceiveDialog {
|
||||||
class ReceiveRequestDialog extends ReceiveDialog {
|
class ReceiveRequestDialog extends ReceiveDialog {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('receiveRequestDialog');
|
super('receive-request-dialog');
|
||||||
|
|
||||||
this.$requestingPeerDisplayNameNode = this.$el.querySelector('#requestingPeerDisplayName');
|
this.$requestingPeerDisplayNameNode = this.$el.querySelector('#requesting-peer-display-name');
|
||||||
this.$fileStemNode = this.$el.querySelector('#fileStem');
|
this.$fileStemNode = this.$el.querySelector('#file-stem');
|
||||||
this.$fileExtensionNode = this.$el.querySelector('#fileExtension');
|
this.$fileExtensionNode = this.$el.querySelector('#file-extension');
|
||||||
this.$fileOtherNode = this.$el.querySelector('#fileOther');
|
this.$fileOtherNode = this.$el.querySelector('#file-other');
|
||||||
|
|
||||||
this.$acceptRequestBtn = this.$el.querySelector('#acceptRequest');
|
this.$acceptRequestBtn = this.$el.querySelector('#accept-request');
|
||||||
this.$declineRequestBtn = this.$el.querySelector('#declineRequest');
|
this.$declineRequestBtn = this.$el.querySelector('#decline-request');
|
||||||
this.$acceptRequestBtn.addEventListener('click', _ => this._respondToFileTransferRequest(true));
|
this.$acceptRequestBtn.addEventListener('click', _ => this._respondToFileTransferRequest(true));
|
||||||
this.$declineRequestBtn.addEventListener('click', _ => this._respondToFileTransferRequest(false));
|
this.$declineRequestBtn.addEventListener('click', _ => this._respondToFileTransferRequest(false));
|
||||||
|
|
||||||
|
@ -720,12 +748,12 @@ class ReceiveRequestDialog extends ReceiveDialog {
|
||||||
|
|
||||||
class PairDeviceDialog extends Dialog {
|
class PairDeviceDialog extends Dialog {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('pairDeviceDialog');
|
super('pair-device-dialog');
|
||||||
$('pair-device').addEventListener('click', _ => this._pairDeviceInitiate());
|
$('pair-device').addEventListener('click', _ => this._pairDeviceInitiate());
|
||||||
this.$inputRoomKeyChars = this.$el.querySelectorAll('#keyInputContainer>input');
|
this.$inputRoomKeyChars = this.$el.querySelectorAll('#key-input-container>input');
|
||||||
this.$submitBtn = this.$el.querySelector('button[type="submit"]');
|
this.$submitBtn = this.$el.querySelector('button[type="submit"]');
|
||||||
this.$roomKey = this.$el.querySelector('#roomKey');
|
this.$roomKey = this.$el.querySelector('#room-key');
|
||||||
this.$qrCode = this.$el.querySelector('#roomKeyQrCode');
|
this.$qrCode = this.$el.querySelector('#room-key-qr-code');
|
||||||
this.$clearSecretsBtn = $('clear-pair-devices');
|
this.$clearSecretsBtn = $('clear-pair-devices');
|
||||||
this.$footerInstructionsPairedDevices = $('and-by-paired-devices');
|
this.$footerInstructionsPairedDevices = $('and-by-paired-devices');
|
||||||
let createJoinForm = this.$el.querySelector('form');
|
let createJoinForm = this.$el.querySelector('form');
|
||||||
|
@ -799,7 +827,7 @@ class PairDeviceDialog extends Dialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
evaluateRoomKeyChars() {
|
evaluateRoomKeyChars() {
|
||||||
if (this.$el.querySelectorAll('#keyInputContainer>input:placeholder-shown').length > 0) {
|
if (this.$el.querySelectorAll('#key-input-container>input:placeholder-shown').length > 0) {
|
||||||
this.$submitBtn.setAttribute("disabled", "");
|
this.$submitBtn.setAttribute("disabled", "");
|
||||||
} else {
|
} else {
|
||||||
this.inputRoomKey = "";
|
this.inputRoomKey = "";
|
||||||
|
@ -843,7 +871,7 @@ class PairDeviceDialog extends Dialog {
|
||||||
height: 150,
|
height: 150,
|
||||||
padding: 0,
|
padding: 0,
|
||||||
background: "transparent",
|
background: "transparent",
|
||||||
color: getComputedStyle(document.body).getPropertyValue('--text-color'),
|
color: `rgb(var(--text-color))`,
|
||||||
ecl: "L",
|
ecl: "L",
|
||||||
join: true
|
join: true
|
||||||
});
|
});
|
||||||
|
@ -935,13 +963,14 @@ class PairDeviceDialog extends Dialog {
|
||||||
this.$clearSecretsBtn.setAttribute('hidden', '');
|
this.$clearSecretsBtn.setAttribute('hidden', '');
|
||||||
this.$footerInstructionsPairedDevices.setAttribute('hidden', '');
|
this.$footerInstructionsPairedDevices.setAttribute('hidden', '');
|
||||||
}
|
}
|
||||||
|
Events.fire('bg-resize');
|
||||||
}).catch(_ => PersistentStorage.logBrowserNotCapable());
|
}).catch(_ => PersistentStorage.logBrowserNotCapable());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClearDevicesDialog extends Dialog {
|
class ClearDevicesDialog extends Dialog {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('clearDevicesDialog');
|
super('clear-devices-dialog');
|
||||||
$('clear-pair-devices').addEventListener('click', _ => this._onClearPairDevices());
|
$('clear-pair-devices').addEventListener('click', _ => this._onClearPairDevices());
|
||||||
let clearDevicesForm = this.$el.querySelector('form');
|
let clearDevicesForm = this.$el.querySelector('form');
|
||||||
clearDevicesForm.addEventListener('submit', _ => this._onSubmit());
|
clearDevicesForm.addEventListener('submit', _ => this._onSubmit());
|
||||||
|
@ -959,9 +988,10 @@ class ClearDevicesDialog extends Dialog {
|
||||||
|
|
||||||
class SendTextDialog extends Dialog {
|
class SendTextDialog extends Dialog {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('sendTextDialog');
|
super('send-text-dialog');
|
||||||
Events.on('text-recipient', e => this._onRecipient(e.detail));
|
Events.on('text-recipient', e => this._onRecipient(e.detail.peerId, e.detail.deviceName));
|
||||||
this.$text = this.$el.querySelector('#textInput');
|
this.$text = this.$el.querySelector('#text-input');
|
||||||
|
this.$peerDisplayName = this.$el.querySelector('#text-send-peer-display-name');
|
||||||
this.$form = this.$el.querySelector('form');
|
this.$form = this.$el.querySelector('form');
|
||||||
this.$submit = this.$el.querySelector('button[type="submit"]');
|
this.$submit = this.$el.querySelector('button[type="submit"]');
|
||||||
this.$form.addEventListener('submit', _ => this._send());
|
this.$form.addEventListener('submit', _ => this._send());
|
||||||
|
@ -992,8 +1022,9 @@ class SendTextDialog extends Dialog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onRecipient(peerId) {
|
_onRecipient(peerId, deviceName) {
|
||||||
this.correspondingPeerId = peerId;
|
this.correspondingPeerId = peerId;
|
||||||
|
this.$peerDisplayName.innerText = deviceName;
|
||||||
this.show();
|
this.show();
|
||||||
|
|
||||||
const range = document.createRange();
|
const range = document.createRange();
|
||||||
|
@ -1017,7 +1048,7 @@ class SendTextDialog extends Dialog {
|
||||||
|
|
||||||
class ReceiveTextDialog extends Dialog {
|
class ReceiveTextDialog extends Dialog {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('receiveTextDialog');
|
super('receive-text-dialog');
|
||||||
Events.on('text-received', e => this._onText(e.detail.text, e.detail.peerId));
|
Events.on('text-received', e => this._onText(e.detail.text, e.detail.peerId));
|
||||||
this.$text = this.$el.querySelector('#text');
|
this.$text = this.$el.querySelector('#text');
|
||||||
this.$copy = this.$el.querySelector('#copy');
|
this.$copy = this.$el.querySelector('#copy');
|
||||||
|
@ -1028,7 +1059,7 @@ class ReceiveTextDialog extends Dialog {
|
||||||
|
|
||||||
Events.on("keydown", e => this._onKeyDown(e));
|
Events.on("keydown", e => this._onKeyDown(e));
|
||||||
|
|
||||||
this.$receiveTextPeerDisplayNameNode = this.$el.querySelector('#receiveTextPeerDisplayName');
|
this.$receiveTextPeerDisplayNameNode = this.$el.querySelector('#receive-text-peer-display-name');
|
||||||
this._receiveTextQueue = [];
|
this._receiveTextQueue = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1089,13 +1120,13 @@ class ReceiveTextDialog extends Dialog {
|
||||||
class Base64ZipDialog extends Dialog {
|
class Base64ZipDialog extends Dialog {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('base64PasteDialog');
|
super('base64-paste-dialog');
|
||||||
const urlParams = new URL(window.location).searchParams;
|
const urlParams = new URL(window.location).searchParams;
|
||||||
const base64Text = urlParams.get('base64text');
|
const base64Text = urlParams.get('base64text');
|
||||||
const base64Zip = urlParams.get('base64zip');
|
const base64Zip = urlParams.get('base64zip');
|
||||||
const base64Hash = window.location.hash.substring(1);
|
const base64Hash = window.location.hash.substring(1);
|
||||||
|
|
||||||
this.$pasteBtn = this.$el.querySelector('#base64PasteBtn');
|
this.$pasteBtn = this.$el.querySelector('#base64-paste-btn');
|
||||||
|
|
||||||
if (base64Text) {
|
if (base64Text) {
|
||||||
this.show();
|
this.show();
|
||||||
|
@ -1246,6 +1277,7 @@ class Notifications {
|
||||||
this.$button.removeAttribute('hidden');
|
this.$button.removeAttribute('hidden');
|
||||||
this.$button.addEventListener('click', _ => this._requestPermission());
|
this.$button.addEventListener('click', _ => this._requestPermission());
|
||||||
}
|
}
|
||||||
|
// Todo: fix Notifications
|
||||||
Events.on('text-received', e => this._messageNotification(e.detail.text, e.detail.peerId));
|
Events.on('text-received', e => this._messageNotification(e.detail.text, e.detail.peerId));
|
||||||
Events.on('files-received', e => this._downloadNotification(e.detail.files));
|
Events.on('files-received', e => this._downloadNotification(e.detail.files));
|
||||||
}
|
}
|
||||||
|
@ -1321,7 +1353,7 @@ class Notifications {
|
||||||
}
|
}
|
||||||
|
|
||||||
_download(notification) {
|
_download(notification) {
|
||||||
$('shareOrDownload').click();
|
$('share-or-download').click();
|
||||||
notification.close();
|
notification.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1714,19 +1746,15 @@ Events.on('load', () => {
|
||||||
h = window.innerHeight;
|
h = window.innerHeight;
|
||||||
c.width = w;
|
c.width = w;
|
||||||
c.height = h;
|
c.height = h;
|
||||||
offset = h > 800
|
offset = $$('footer').offsetHeight - 32;
|
||||||
? 116
|
if (h > 800) offset += 16;
|
||||||
: h > 380
|
|
||||||
? 100
|
|
||||||
: 65;
|
|
||||||
|
|
||||||
if (w < 420) offset += 20;
|
|
||||||
x0 = w / 2;
|
x0 = w / 2;
|
||||||
y0 = h - offset;
|
y0 = h - offset;
|
||||||
dw = Math.max(w, h, 1000) / 13;
|
dw = Math.max(w, h, 1000) / 13;
|
||||||
drawCircles();
|
drawCircles();
|
||||||
}
|
}
|
||||||
window.onresize = init;
|
Events.on('bg-resize', _ => init());
|
||||||
|
window.onresize = _ => Events.fire('bg-resize');
|
||||||
|
|
||||||
function drawCircle(radius) {
|
function drawCircle(radius) {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
|
@ -1791,9 +1819,3 @@ Notifications permission has been blocked
|
||||||
as the user has dismissed the permission prompt several times.
|
as the user has dismissed the permission prompt several times.
|
||||||
This can be reset in Page Info
|
This can be reset in Page Info
|
||||||
which can be accessed by clicking the lock icon next to the URL.`;
|
which can be accessed by clicking the lock icon next to the URL.`;
|
||||||
|
|
||||||
document.body.onclick = _ => { // safari hack to fix audio
|
|
||||||
document.body.onclick = null;
|
|
||||||
if (!(/.*Version.*Safari.*/.test(navigator.userAgent))) return;
|
|
||||||
blop.play();
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const cacheVersion = 'v1.1.1';
|
const cacheVersion = 'v1.1.3';
|
||||||
const cacheTitle = `pairdrop-cache-${cacheVersion}`;
|
const cacheTitle = `pairdrop-cache-${cacheVersion}`;
|
||||||
const urlsToCache = [
|
const urlsToCache = [
|
||||||
'index.html',
|
'index.html',
|
||||||
|
|
|
@ -10,28 +10,25 @@
|
||||||
|
|
||||||
/* Layout */
|
/* Layout */
|
||||||
|
|
||||||
html {
|
|
||||||
min-height: 100%;
|
|
||||||
height: -webkit-fill-available;
|
|
||||||
}
|
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100vw;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overscroll-behavior-y: none;
|
overscroll-behavior: none;
|
||||||
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
min-height: 100%;
|
min-height: 100vh;
|
||||||
|
/* mobile viewport bug fix */
|
||||||
min-height: -webkit-fill-available;
|
min-height: -webkit-fill-available;
|
||||||
flex-grow: 1;
|
}
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
html {
|
||||||
overflow-y: hidden;
|
height: -webkit-fill-available;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-reverse {
|
.row-reverse {
|
||||||
|
@ -73,10 +70,7 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
position: absolute;
|
position: relative;
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 56px;
|
height: 56px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
|
@ -119,9 +113,9 @@ h3 {
|
||||||
}
|
}
|
||||||
|
|
||||||
.font-subheading {
|
.font-subheading {
|
||||||
font-size: 16px;
|
font-size: 14px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 24px;
|
line-height: 18px;
|
||||||
word-break: normal;
|
word-break: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,20 +193,151 @@ body>header a {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#center {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
flex-grow: 1;
|
||||||
|
--footer-height: 132px;
|
||||||
|
max-height: calc(100vh - 56px - var(--footer-height));
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: scroll;
|
||||||
|
overscroll-behavior-x: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 425px) {
|
||||||
|
header:has(#clear-pair-devices:not([hidden]))~#center {
|
||||||
|
--footer-height: 150px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Peers List */
|
/* Peers List */
|
||||||
|
|
||||||
|
#x-peers-filler {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
x-peers {
|
x-peers {
|
||||||
width: 100%;
|
position: relative;
|
||||||
overflow: hidden;
|
display: flex;
|
||||||
flex-flow: row wrap;
|
flex-flow: row wrap;
|
||||||
|
flex-grow: 1;
|
||||||
|
align-items: start !important;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
transition: color 300ms;
|
transition: --bg-color 0.5s ease;
|
||||||
|
overflow-y: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overscroll-behavior-x: none;
|
||||||
|
scrollbar-width: none;
|
||||||
|
|
||||||
|
--peers-per-row: 6; /* default if browser does not support :has selector */
|
||||||
|
--x-peers-width: min(100vw, calc(var(--peers-per-row) * (var(--peer-width) + 25px) - 16px));
|
||||||
|
width: var(--x-peers-width);
|
||||||
|
margin-right: 20px;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers.overflowing {
|
||||||
|
background: /* Shadow covers */ linear-gradient(rgb(var(--bg-color)) 30%, rgba(var(--bg-color), 0)),
|
||||||
|
linear-gradient(rgba(var(--bg-color), 0), rgb(var(--bg-color)) 70%) 0 100%,
|
||||||
|
/* Shadows */ radial-gradient(farthest-side at 50% 0, rgba(var(--text-color), .2), rgba(var(--text-color), 0)),
|
||||||
|
radial-gradient(farthest-side at 50% 100%, rgba(var(--text-color), .2), rgba(var(--text-color), 0)) 0 100%;
|
||||||
|
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 100% 40px, 100% 40px, 100% 14px, 100% 14px;
|
||||||
|
|
||||||
|
/* Opera doesn't support this in the shorthand */
|
||||||
|
background-attachment: local, local, scroll, scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer) {
|
||||||
|
--peers-per-row: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-height: 505px) and (max-height: 649px) and (max-width: 426px),
|
||||||
|
screen and (min-height: 486px) and (max-height: 631px) and (min-width: 426px) {
|
||||||
|
x-peers:has(> x-peer) {
|
||||||
|
--peers-per-row: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(7)) {
|
||||||
|
--peers-per-row: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(10)) {
|
||||||
|
--peers-per-row: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(13)) {
|
||||||
|
--peers-per-row: 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(16)) {
|
||||||
|
--peers-per-row: 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(19)) {
|
||||||
|
--peers-per-row: 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(22)) {
|
||||||
|
--peers-per-row: 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(25)) {
|
||||||
|
--peers-per-row: 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-height: 649px) and (max-width: 425px),
|
||||||
|
screen and (min-height: 631px) and (min-width: 426px) {
|
||||||
|
x-peers:has(> x-peer) {
|
||||||
|
--peers-per-row: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(10)) {
|
||||||
|
--peers-per-row: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(13)) {
|
||||||
|
--peers-per-row: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(16)) {
|
||||||
|
--peers-per-row: 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(19)) {
|
||||||
|
--peers-per-row: 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(22)) {
|
||||||
|
--peers-per-row: 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(25)) {
|
||||||
|
--peers-per-row: 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(28)) {
|
||||||
|
--peers-per-row: 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Empty Peers List */
|
/* Empty Peers List */
|
||||||
|
|
||||||
x-no-peers {
|
x-no-peers {
|
||||||
height: 114px;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
/* prevent flickering on load */
|
/* prevent flickering on load */
|
||||||
|
@ -254,25 +379,19 @@ x-no-peers[drop-bg] * {
|
||||||
x-peer {
|
x-peer {
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
padding: 8px;
|
||||||
|
align-content: start;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-peer label {
|
x-peer label {
|
||||||
width: var(--peer-width);
|
width: var(--peer-width);
|
||||||
padding: 8px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
touch-action: manipulation;
|
touch-action: manipulation;
|
||||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-peer .name {
|
|
||||||
width: var(--peer-width);
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="file"] {
|
input[type="file"] {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -280,21 +399,45 @@ input[type="file"] {
|
||||||
|
|
||||||
x-peer x-icon {
|
x-peer x-icon {
|
||||||
--icon-size: 40px;
|
--icon-size: 40px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
transition: transform 150ms;
|
||||||
|
will-change: transform;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peer .icon-wrapper {
|
||||||
width: var(--icon-size);
|
width: var(--icon-size);
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: var(--primary-color);
|
background: var(--primary-color);
|
||||||
color: white;
|
color: white;
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-bottom: 8px;
|
|
||||||
transition: transform 150ms;
|
|
||||||
will-change: transform;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
x-peer:not(.type-ip) x-icon {
|
x-peer:not(.type-ip).type-secret .icon-wrapper {
|
||||||
background: var(--paired-device-color);
|
background: var(--paired-device-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
x-peer x-icon > .highlight-wrapper {
|
||||||
|
align-self: center;
|
||||||
|
align-items: center;
|
||||||
|
margin: 7px auto 0;
|
||||||
|
height: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peer x-icon > .highlight-wrapper > .highlight {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peer.type-secret x-icon > .highlight-wrapper > .highlight {
|
||||||
|
background-color: var(--paired-device-color);
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
x-peer:not([status]):hover x-icon,
|
x-peer:not([status]):hover x-icon,
|
||||||
x-peer:not([status]):focus x-icon {
|
x-peer:not([status]):focus x-icon {
|
||||||
transform: scale(1.05);
|
transform: scale(1.05);
|
||||||
|
@ -306,6 +449,18 @@ x-peer[status] x-icon {
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.device-descriptor {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.status,
|
.status,
|
||||||
.device-name,
|
.device-name,
|
||||||
.connection-hash {
|
.connection-hash {
|
||||||
|
@ -371,10 +526,9 @@ x-peer[drop] x-icon {
|
||||||
/* Footer */
|
/* Footer */
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
position: absolute;
|
position: relative;
|
||||||
bottom: 0;
|
margin-top: auto;
|
||||||
left: 0;
|
z-index: 2;
|
||||||
right: 0;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 0 16px 0;
|
padding: 0 0 16px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -385,6 +539,7 @@ footer .logo {
|
||||||
--icon-size: 80px;
|
--icon-size: 80px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
|
margin-top: -10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
footer .font-body2 {
|
footer .font-body2 {
|
||||||
|
@ -425,11 +580,14 @@ x-dialog x-paper {
|
||||||
will-change: transform;
|
will-change: transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pairDeviceDialog x-paper {
|
#pair-device-dialog x-paper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: max(50%, 350px);
|
top: max(50%, 350px);
|
||||||
height: 650px;
|
height: 650px;
|
||||||
margin-top: -325px;
|
margin-top: -325px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-dialog:not([show]) {
|
x-dialog:not([show]) {
|
||||||
|
@ -461,13 +619,13 @@ x-dialog .font-subheading {
|
||||||
|
|
||||||
/* PairDevicesDialog */
|
/* PairDevicesDialog */
|
||||||
|
|
||||||
#keyInputContainer {
|
#key-input-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#keyInputContainer>input {
|
#key-input-container>input {
|
||||||
width: 45px;
|
width: 45px;
|
||||||
height: 45px;
|
height: 45px;
|
||||||
font-size: 30px;
|
font-size: 30px;
|
||||||
|
@ -483,15 +641,15 @@ x-dialog .font-subheading {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#keyInputContainer>input + * {
|
#key-input-container>input + * {
|
||||||
margin-left: 6px;
|
margin-left: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#keyInputContainer>input:nth-of-type(4) {
|
#key-input-container>input:nth-of-type(4) {
|
||||||
margin-left: 18px;
|
margin-left: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#roomKey {
|
#room-key {
|
||||||
font-size: 50px;
|
font-size: 50px;
|
||||||
letter-spacing: min(calc((100vw - 80px - 99px) / 100 * 7), 23px);
|
letter-spacing: min(calc((100vw - 80px - 99px) / 100 * 7), 23px);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
@ -499,19 +657,20 @@ x-dialog .font-subheading {
|
||||||
margin: 15px -15px;
|
margin: 15px -15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#roomKeyQrCode {
|
#room-key-qr-code {
|
||||||
padding: inherit;
|
padding: inherit;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
width: 150px;
|
width: 150px;
|
||||||
height: 150px;
|
height: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pairDeviceDialog hr {
|
#pair-device-dialog hr {
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
margin-bottom: 40px;
|
margin-bottom: 40px;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pairDeviceDialog x-background {
|
#pair-device-dialog x-background {
|
||||||
padding: 16px!important;
|
padding: 16px!important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -526,13 +685,13 @@ x-dialog h2 {
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
#receiveRequestDialog h2,
|
#receive-request-dialog h2,
|
||||||
#receiveFileDialog h2 {
|
#receive-file-dialog h2 {
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-dialog .row-reverse {
|
x-dialog .row-reverse {
|
||||||
margin: 40px -24px auto;
|
margin: 40px -24px 0;
|
||||||
border-top: solid 2.5px var(--border-color);
|
border-top: solid 2.5px var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -556,11 +715,11 @@ x-dialog .row-reverse {
|
||||||
word-break: normal;
|
word-break: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
#fileName {
|
#file-name {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
#fileStem {
|
#file-stem {
|
||||||
max-width: 80%;
|
max-width: 80%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
@ -574,13 +733,13 @@ x-dialog .row-reverse {
|
||||||
|
|
||||||
/* Send Text Dialog */
|
/* Send Text Dialog */
|
||||||
|
|
||||||
#textInput {
|
#text-input {
|
||||||
min-height: 120px;
|
min-height: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Receive Text Dialog */
|
/* Receive Text Dialog */
|
||||||
|
|
||||||
#receiveTextDialog #text {
|
#receive-text-dialog #text {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
|
@ -593,15 +752,15 @@ x-dialog .row-reverse {
|
||||||
margin-top:36px;
|
margin-top:36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#receiveTextDialog #text a {
|
#receive-text-dialog #text a {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#receiveTextDialog #text a:hover {
|
#receive-text-dialog #text a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
#receiveTextDialog h3 {
|
#receive-text-dialog h3 {
|
||||||
/* Select the received text when double-clicking the dialog */
|
/* Select the received text when double-clicking the dialog */
|
||||||
user-select: none;
|
user-select: none;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
@ -612,26 +771,26 @@ x-dialog .row-reverse {
|
||||||
margin: auto -25px;
|
margin: auto -25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#receiveTextDescriptionContainer {
|
#receive-text-description-container {
|
||||||
margin-bottom: 25px;
|
margin-bottom: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#base64PasteBtn {
|
#base64-paste-btn {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 40vh;
|
height: 40vh;
|
||||||
border: solid 12px #438cff;
|
border: solid 12px #438cff;
|
||||||
}
|
}
|
||||||
|
|
||||||
#base64PasteDialog button {
|
#base64-paste-dialog button {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#base64PasteDialog button[close] {
|
#base64-paste-dialog button[close] {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#base64PasteDialog button[close]:before {
|
#base64-paste-dialog button[close]:before {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -689,16 +848,18 @@ x-dialog .row-reverse {
|
||||||
opacity: 0.1;
|
opacity: 0.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#cancelPasteModeBtn {
|
#cancel-paste-mode-btn {
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
margin-top: 0;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100vw;
|
||||||
height: 56px;
|
height: 56px;
|
||||||
border-bottom: solid 2.5px var(--border-color);
|
background-color: var(--primary-color);
|
||||||
|
color: rgb(238, 238, 238);
|
||||||
}
|
}
|
||||||
|
|
||||||
.button:focus:before,
|
.button:focus:before,
|
||||||
|
@ -809,7 +970,7 @@ button::-moz-focus-inner {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
height: 80px;
|
height: 80px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: -8px;
|
||||||
clip: rect(0px, 80px, 80px, 40px);
|
clip: rect(0px, 80px, 80px, 40px);
|
||||||
--progress: rotate(0deg);
|
--progress: rotate(0deg);
|
||||||
transition: transform 200ms;
|
transition: transform 200ms;
|
||||||
|
@ -876,13 +1037,16 @@ x-toast:not([show]):not(:hover) {
|
||||||
/* Instructions */
|
/* Instructions */
|
||||||
|
|
||||||
x-instructions {
|
x-instructions {
|
||||||
position: absolute;
|
position: relative;
|
||||||
top: 120px;
|
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
transition: opacity 300ms;
|
transition: opacity 300ms;
|
||||||
z-index: -1;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 80%;
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-instructions:not([drop-peer]):not([drop-bg]):before {
|
x-instructions:not([drop-peer]):not([drop-bg]):before {
|
||||||
|
@ -899,88 +1063,84 @@ x-instructions[drop-bg]:not([drop-peer]):before {
|
||||||
|
|
||||||
x-instructions p {
|
x-instructions p {
|
||||||
display: none;
|
display: none;
|
||||||
margin: 0 auto auto;
|
|
||||||
max-width: 80%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
x-peers:empty~x-instructions {
|
x-peers:empty~x-instructions {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (hover: none) and (pointer: coarse) {
|
||||||
|
x-peer {
|
||||||
|
transform: scale(0.95);
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#websocket-fallback {
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: 5px;
|
||||||
|
padding: 5px;
|
||||||
|
text-align: center;
|
||||||
|
opacity: 0.5;
|
||||||
|
transition: opacity 300ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
#websocket-fallback>span {
|
||||||
|
margin: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#websocket-fallback > span > span {
|
||||||
|
border-bottom: solid 4px var(--ws-peer-color);
|
||||||
|
}
|
||||||
|
|
||||||
/* Responsive Styles */
|
/* Responsive Styles */
|
||||||
|
|
||||||
@media (min-height: 800px) {
|
@media screen and (min-height: 800px) {
|
||||||
footer {
|
footer {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-height: 800px),
|
@media (hover: hover) and (pointer: fine) {
|
||||||
screen and (min-width: 1100px) {
|
|
||||||
x-instructions:not([drop-peer]):not([drop-bg]):before {
|
x-instructions:not([drop-peer]):not([drop-bg]):before {
|
||||||
content: attr(desktop);
|
content: attr(desktop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-height: 420px) {
|
|
||||||
x-instructions {
|
|
||||||
top: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer .logo {
|
|
||||||
--icon-size: 40px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
iOS specific styles
|
|
||||||
*/
|
|
||||||
@supports (-webkit-overflow-scrolling: touch) {
|
|
||||||
|
|
||||||
|
|
||||||
html {
|
|
||||||
position: fixed;
|
|
||||||
}
|
|
||||||
|
|
||||||
x-instructions:not([drop-peer]):not([drop-bg]):before {
|
|
||||||
content: attr(mobile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Color Themes
|
Color Themes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Default colors */
|
/* Default colors */
|
||||||
body {
|
body {
|
||||||
--text-color: #333;
|
--text-color: 51,51,51;
|
||||||
--bg-color: #fff;
|
--bg-color: 250,250,250; /*rgb code*/
|
||||||
|
--bg-color-test: 18,18,18;
|
||||||
--bg-color-secondary: #f1f3f4;
|
--bg-color-secondary: #f1f3f4;
|
||||||
--border-color: #e7e8e8;
|
--border-color: #e7e8e8;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dark theme colors */
|
/* Dark theme colors */
|
||||||
body.dark-theme {
|
body.dark-theme {
|
||||||
--text-color: #eee;
|
--text-color: 238,238,238;
|
||||||
--bg-color: #121212;
|
--bg-color: 18,18,18; /*rgb code*/
|
||||||
--bg-color-secondary: #333;
|
--bg-color-secondary: #333;
|
||||||
--border-color: #252525;
|
--border-color: #252525;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Colored Elements */
|
/* Colored Elements */
|
||||||
body {
|
body {
|
||||||
color: var(--text-color);
|
color: rgb(var(--text-color));
|
||||||
background-color: var(--bg-color);
|
background-color: rgb(var(--bg-color));
|
||||||
transition: background-color 0.5s ease;
|
transition: background-color 0.5s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-dialog x-paper {
|
x-dialog x-paper {
|
||||||
background-color: var(--bg-color);
|
background-color: rgb(var(--bg-color));
|
||||||
}
|
}
|
||||||
|
|
||||||
.textarea {
|
.textarea {
|
||||||
color: var(--text-color) !important;
|
color: rgb(var(--text-color)) !important;
|
||||||
background-color: var(--bg-color-secondary) !important;
|
background-color: var(--bg-color-secondary) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1018,16 +1178,16 @@ x-dialog x-paper {
|
||||||
|
|
||||||
/* defaults to dark theme */
|
/* defaults to dark theme */
|
||||||
body {
|
body {
|
||||||
--text-color: #eee;
|
--text-color: 238,238,238;
|
||||||
--bg-color: #121212;
|
--bg-color: 18,18,18; /*rgb code*/
|
||||||
--bg-color-secondary: #333;
|
--bg-color-secondary: #333;
|
||||||
--border-color: #252525;
|
--border-color: #252525;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Override dark mode with light mode styles if the user decides to swap */
|
/* Override dark mode with light mode styles if the user decides to swap */
|
||||||
body.light-theme {
|
body.light-theme {
|
||||||
--text-color: #333;
|
--text-color: 51,51,51;
|
||||||
--bg-color: #fafafa;
|
--bg-color: 250,250,250; /*rgb code*/
|
||||||
--bg-color-secondary: #f1f3f4;
|
--bg-color-secondary: #f1f3f4;
|
||||||
--border-color: #e7e8e8;
|
--border-color: #e7e8e8;
|
||||||
}
|
}
|
||||||
|
@ -1045,6 +1205,15 @@ x-dialog x-paper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
iOS specific styles
|
||||||
|
*/
|
||||||
|
@supports (-webkit-overflow-scrolling: touch) {
|
||||||
|
html {
|
||||||
|
min-height: -webkit-fill-available;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* webkit scrollbar style*/
|
/* webkit scrollbar style*/
|
||||||
|
|
||||||
::-webkit-scrollbar{
|
::-webkit-scrollbar{
|
||||||
|
|
|
@ -69,48 +69,52 @@
|
||||||
<use xlink:href="#clear-pair-devices-icon" />
|
<use xlink:href="#clear-pair-devices-icon" />
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
|
<a id="cancel-paste-mode-btn" class="button" close hidden>Done</a>
|
||||||
</header>
|
</header>
|
||||||
<a id="cancelPasteModeBtn" class="button" close hidden>Done</a>
|
<!-- Center -->
|
||||||
|
<div id="center">
|
||||||
<!-- Peers -->
|
<!-- Peers -->
|
||||||
|
<div class="x-peers-filler"></div>
|
||||||
<x-peers class="center"></x-peers>
|
<x-peers class="center"></x-peers>
|
||||||
<x-no-peers>
|
<x-no-peers>
|
||||||
<h2>Open PairDrop on other devices to send files</h2>
|
<h2>Open PairDrop on other devices to send files</h2>
|
||||||
<div>Pair devices to be discoverable on other networks</div>
|
<div>Pair devices to be discoverable on other networks</div>
|
||||||
<br>
|
|
||||||
<div>A <span class="websocket-fallback">websocket fallback</span> is implemented on this instance. Use only if you trust the server!</div>
|
|
||||||
</x-no-peers>
|
</x-no-peers>
|
||||||
<x-instructions desktop="Click to send files or right click to send a message" mobile="Tap to send files or long tap to send a message">
|
<x-instructions desktop="Click to send files or right click to send a message" mobile="Tap to send files or long tap to send a message">
|
||||||
<div>A <span class="websocket-fallback">websocket fallback</span> is implemented on this instance. Use only if you trust the server!</div>
|
<p id="paste-filename"></p>
|
||||||
<p id="pasteFilename"></p>
|
|
||||||
</x-instructions>
|
</x-instructions>
|
||||||
|
</div>
|
||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
<footer class="column">
|
<footer class="column">
|
||||||
<svg class="icon logo">
|
<svg class="icon logo">
|
||||||
<use xlink:href="#wifi-tethering" />
|
<use xlink:href="#wifi-tethering" />
|
||||||
</svg>
|
</svg>
|
||||||
<div id="displayName" placeholder=" "></div>
|
<div id="display-name" placeholder=" "></div>
|
||||||
<div class="font-body2">
|
<div class="font-body2">
|
||||||
You can be discovered by everyone <span id="on-this-network">on this network</span>
|
You can be discovered by everyone <span id="on-this-network">on this network</span>
|
||||||
<span id="and-by-paired-devices" hidden> and by <span id="paired-devices">paired devices</span></span>
|
<span id="and-by-paired-devices" hidden> and by <span id="paired-devices">paired devices</span></span>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="websocket-fallback">
|
||||||
|
<span>Traffic is <span>routed through the server</span> if WebRTC is not available.</span>
|
||||||
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
<!-- Pair Device Dialog -->
|
<!-- Pair Device Dialog -->
|
||||||
<x-dialog id="pairDeviceDialog">
|
<x-dialog id="pair-device-dialog">
|
||||||
<form action="#">
|
<form action="#">
|
||||||
<x-background class="full center text-center">
|
<x-background class="full center text-center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
<h2 class="center">Pair Devices</h2>
|
<h2 class="center">Pair Devices</h2>
|
||||||
<div class="center" id="roomKeyQrCode"></div>
|
<div id="room-key-qr-code" class="center"></div>
|
||||||
<h1 class="center" id="roomKey">000 000</h1>
|
<h1 id="room-key" class="center">000 000</h1>
|
||||||
<div id="pairInstructions" class="font-subheading center text-center">Input this key on another device<br>or scan the QR-Code.</div>
|
<div id="pair-instructions" class="font-subheading center text-center">Input this key on another device<br>or scan the QR-Code.</div>
|
||||||
<hr>
|
<hr>
|
||||||
<div id="keyInputContainer">
|
<div id="key-input-container">
|
||||||
<input type="tel" id="char0" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" autofocus contenteditable placeholder="" disabled>
|
<input id="char0" type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" autofocus contenteditable placeholder="" disabled>
|
||||||
<input type="tel" id="char1" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
<input id="char1" type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||||
<input type="tel" id="char2" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
<input id="char2" type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||||
<input type="tel" id="char3" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
<input id="char3" type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||||
<input type="tel" id="char4" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
<input id="char4" type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||||
<input type="tel" id="char5" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
<input id="char5" type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||||
</div>
|
</div>
|
||||||
<div class="font-subheading center text-center">Enter key from another device to continue.</div>
|
<div class="font-subheading center text-center">Enter key from another device to continue.</div>
|
||||||
<div class="row-reverse space-between">
|
<div class="row-reverse space-between">
|
||||||
|
@ -123,7 +127,7 @@
|
||||||
</form>
|
</form>
|
||||||
</x-dialog>
|
</x-dialog>
|
||||||
<!-- Clear Devices Dialog -->
|
<!-- Clear Devices Dialog -->
|
||||||
<x-dialog id="clearDevicesDialog">
|
<x-dialog id="clear-devices-dialog">
|
||||||
<form action="#">
|
<form action="#">
|
||||||
<x-background class="full center text-center">
|
<x-background class="full center text-center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
|
@ -138,43 +142,43 @@
|
||||||
</form>
|
</form>
|
||||||
</x-dialog>
|
</x-dialog>
|
||||||
<!-- Receive Request Dialog -->
|
<!-- Receive Request Dialog -->
|
||||||
<x-dialog id="receiveRequestDialog">
|
<x-dialog id="receive-request-dialog">
|
||||||
<x-background class="full center">
|
<x-background class="full center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
<h2 class="center">PairDrop</h2>
|
<h2 class="center">PairDrop</h2>
|
||||||
<div class="text-center file-description">
|
<div class="text-center file-description">
|
||||||
<div>
|
<div>
|
||||||
<span id="requestingPeerDisplayName"></span>
|
<span id="requesting-peer-display-name"></span>
|
||||||
<span>would like to share</span>
|
<span>would like to share</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="row" id="fileName">
|
<div id="file-name" class="row" >
|
||||||
<span id="fileStem"></span>
|
<span id="file-stem"></span>
|
||||||
<span id="fileExtension"></span>
|
<span id="file-extension"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<span id="fileOther"></span>
|
<span id="file-other"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="font-body2 text-center file-size"></div>
|
<div class="font-body2 text-center file-size"></div>
|
||||||
<div class="center file-preview"></div>
|
<div class="center file-preview"></div>
|
||||||
<div class="row-reverse space-between">
|
<div class="row-reverse space-between">
|
||||||
<button class="button" id="acceptRequest" title="ENTER" autofocus>Accept</button>
|
<button id="accept-request" class="button" title="ENTER" autofocus>Accept</button>
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
<button class="button" id="declineRequest" title="ESCAPE">Decline</button>
|
<button id="decline-request" class="button" title="ESCAPE">Decline</button>
|
||||||
</div>
|
</div>
|
||||||
</x-paper>
|
</x-paper>
|
||||||
</x-background>
|
</x-background>
|
||||||
</x-dialog>
|
</x-dialog>
|
||||||
<!-- Receive File Dialog -->
|
<!-- Receive File Dialog -->
|
||||||
<x-dialog id="receiveFileDialog">
|
<x-dialog id="receive-file-dialog">
|
||||||
<x-background class="full center">
|
<x-background class="full center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
<h2 class="center" id="receiveTitle"></h2>
|
<h2 id="receive-title" class="center"></h2>
|
||||||
<div class="text-center file-description"></div>
|
<div class="text-center file-description"></div>
|
||||||
<div class="font-body2 text-center file-size"></div>
|
<div class="font-body2 text-center file-size"></div>
|
||||||
<div class="center file-preview"></div>
|
<div class="center file-preview"></div>
|
||||||
<div class="row-reverse space-between">
|
<div class="row-reverse space-between">
|
||||||
<a class="button" id="shareOrDownload" autofocus></a>
|
<a id="share-or-download" class="button" autofocus></a>
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
<button class="button" close>Close</button>
|
<button class="button" close>Close</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -182,12 +186,16 @@
|
||||||
</x-background>
|
</x-background>
|
||||||
</x-dialog>
|
</x-dialog>
|
||||||
<!-- Send Text Dialog -->
|
<!-- Send Text Dialog -->
|
||||||
<x-dialog id="sendTextDialog">
|
<x-dialog id="send-text-dialog">
|
||||||
<form action="#">
|
<form action="#">
|
||||||
<x-background class="full center">
|
<x-background class="full center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
<h2>PairDrop - Send a Message</h2>
|
<h2 class="text-center">PairDrop</h2>
|
||||||
<div id="textInput" class="textarea" role="textbox" placeholder="Send a message" autocapitalize="none" spellcheck="false" autofocus contenteditable></div>
|
<div class="text-center">
|
||||||
|
<span>Send a Message to</span>
|
||||||
|
<span id="text-send-peer-display-name"></span>
|
||||||
|
</div>
|
||||||
|
<div id="text-input" class="textarea" role="textbox" autocapitalize="none" spellcheck="false" autofocus contenteditable></div>
|
||||||
<div class="row-reverse">
|
<div class="row-reverse">
|
||||||
<button class="button" type="submit" title="STR + ENTER" disabled close>Send</button>
|
<button class="button" type="submit" title="STR + ENTER" disabled close>Send</button>
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
|
@ -198,36 +206,36 @@
|
||||||
</form>
|
</form>
|
||||||
</x-dialog>
|
</x-dialog>
|
||||||
<!-- Receive Text Dialog -->
|
<!-- Receive Text Dialog -->
|
||||||
<x-dialog id="receiveTextDialog">
|
<x-dialog id="receive-text-dialog">
|
||||||
<x-background class="full center">
|
<x-background class="full center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
<h2>PairDrop - Message Received</h2>
|
<h2>PairDrop - Message Received</h2>
|
||||||
<div id="receiveTextDescriptionContainer">
|
<div id="receive-text-description-container">
|
||||||
<span id="receiveTextPeerDisplayName"></span>
|
<span id="receive-text-peer-display-name"></span>
|
||||||
<span>sent the following message:</span>
|
<span>sent the following message:</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="row-separator"></div>
|
<div class="row-separator"></div>
|
||||||
<div id="text"></div>
|
<div id="text"></div>
|
||||||
<div class="row-reverse">
|
<div class="row-reverse">
|
||||||
<button class="button" id="copy" title="CTRL/⌘ + C">Copy</button>
|
<button id="copy" class="button" title="CTRL/⌘ + C">Copy</button>
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
<button class="button" id="close" title="ESCAPE">Close</button>
|
<button id="close" class="button" title="ESCAPE">Close</button>
|
||||||
</div>
|
</div>
|
||||||
</x-paper>
|
</x-paper>
|
||||||
</x-background>
|
</x-background>
|
||||||
</x-dialog>
|
</x-dialog>
|
||||||
<!-- base64PasteDialog Dialog -->
|
<!-- base64 Paste Dialog -->
|
||||||
<x-dialog id="base64PasteDialog">
|
<x-dialog id="base64-paste-dialog">
|
||||||
<x-background class="full center">
|
<x-background class="full center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
<button class="button center" id="base64PasteBtn" title="Paste">Tap here to paste files</button>
|
<button class="button center" id="base64-paste-btn" title="Paste">Tap here to paste files</button>
|
||||||
<button class="button center" close>Close</button>
|
<button class="button center" close>Close</button>
|
||||||
</x-paper>
|
</x-paper>
|
||||||
</x-background>
|
</x-background>
|
||||||
</x-dialog>
|
</x-dialog>
|
||||||
<!-- Toast -->
|
<!-- Toast -->
|
||||||
<div class="toast-container full center">
|
<div class="toast-container full center">
|
||||||
<x-toast class="row" shadow="1" id="toast"></x-toast>
|
<x-toast id="toast" class="row" shadow="1"></x-toast>
|
||||||
</div>
|
</div>
|
||||||
<!-- About Page -->
|
<!-- About Page -->
|
||||||
<x-about id="about" class="full center column">
|
<x-about id="about" class="full center column">
|
||||||
|
|
|
@ -10,7 +10,7 @@ window.pasteMode.activated = false;
|
||||||
// set display name
|
// set display name
|
||||||
Events.on('display-name', e => {
|
Events.on('display-name', e => {
|
||||||
const me = e.detail.message;
|
const me = e.detail.message;
|
||||||
const $displayName = $('displayName')
|
const $displayName = $('display-name')
|
||||||
$displayName.textContent = 'You are known as ' + me.displayName;
|
$displayName.textContent = 'You are known as ' + me.displayName;
|
||||||
$displayName.title = me.deviceName;
|
$displayName.title = me.deviceName;
|
||||||
});
|
});
|
||||||
|
@ -28,7 +28,7 @@ class PeersUI {
|
||||||
Events.on('activate-paste-mode', e => this._activatePasteMode(e.detail.files, e.detail.text));
|
Events.on('activate-paste-mode', e => this._activatePasteMode(e.detail.files, e.detail.text));
|
||||||
this.peers = {};
|
this.peers = {};
|
||||||
|
|
||||||
this.$cancelPasteModeBtn = $('cancelPasteModeBtn');
|
this.$cancelPasteModeBtn = $('cancel-paste-mode-btn');
|
||||||
this.$cancelPasteModeBtn.addEventListener('click', _ => this._cancelPasteMode());
|
this.$cancelPasteModeBtn.addEventListener('click', _ => this._cancelPasteMode());
|
||||||
|
|
||||||
Events.on('dragover', e => this._onDragOver(e));
|
Events.on('dragover', e => this._onDragOver(e));
|
||||||
|
@ -38,8 +38,12 @@ class PeersUI {
|
||||||
Events.on('drop', e => this._onDrop(e));
|
Events.on('drop', e => this._onDrop(e));
|
||||||
Events.on('keydown', e => this._onKeyDown(e));
|
Events.on('keydown', e => this._onKeyDown(e));
|
||||||
|
|
||||||
|
this.$xPeers = $$('x-peers');
|
||||||
this.$xNoPeers = $$('x-no-peers');
|
this.$xNoPeers = $$('x-no-peers');
|
||||||
this.$xInstructions = $$('x-instructions');
|
this.$xInstructions = $$('x-instructions');
|
||||||
|
|
||||||
|
Events.on('peer-added', _ => this.evaluateOverflowing());
|
||||||
|
Events.on('bg-resize', _ => this.evaluateOverflowing());
|
||||||
}
|
}
|
||||||
|
|
||||||
_onKeyDown(e) {
|
_onKeyDown(e) {
|
||||||
|
@ -53,11 +57,11 @@ class PeersUI {
|
||||||
}
|
}
|
||||||
|
|
||||||
_joinPeer(peer, roomType, roomSecret) {
|
_joinPeer(peer, roomType, roomSecret) {
|
||||||
peer.roomType = roomType;
|
peer.roomTypes = [roomType];
|
||||||
peer.roomSecret = roomSecret;
|
peer.roomSecret = roomSecret;
|
||||||
if (this.peers[peer.id]) {
|
if (this.peers[peer.id]) {
|
||||||
this.peers[peer.id].roomType = peer.roomType;
|
if (!this.peers[peer.id].roomTypes.includes(roomType)) this.peers[peer.id].roomTypes.push(roomType);
|
||||||
this._redrawPeer(peer);
|
this._redrawPeer(this.peers[peer.id]);
|
||||||
return; // peer already exists
|
return; // peer already exists
|
||||||
}
|
}
|
||||||
this.peers[peer.id] = peer;
|
this.peers[peer.id] = peer;
|
||||||
|
@ -72,7 +76,15 @@ class PeersUI {
|
||||||
const peerNode = $(peer.id);
|
const peerNode = $(peer.id);
|
||||||
if (!peerNode) return;
|
if (!peerNode) return;
|
||||||
peerNode.classList.remove('type-ip', 'type-secret');
|
peerNode.classList.remove('type-ip', 'type-secret');
|
||||||
peerNode.classList.add(`type-${peer.roomType}`)
|
peer.roomTypes.forEach(roomType => peerNode.classList.add(`type-${roomType}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
evaluateOverflowing() {
|
||||||
|
if (this.$xPeers.clientHeight < this.$xPeers.scrollHeight) {
|
||||||
|
this.$xPeers.classList.add('overflowing');
|
||||||
|
} else {
|
||||||
|
this.$xPeers.classList.remove('overflowing');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onPeers(msg) {
|
_onPeers(msg) {
|
||||||
|
@ -83,6 +95,7 @@ class PeersUI {
|
||||||
const $peer = $(peerId);
|
const $peer = $(peerId);
|
||||||
if (!$peer) return;
|
if (!$peer) return;
|
||||||
$peer.remove();
|
$peer.remove();
|
||||||
|
this.evaluateOverflowing();
|
||||||
if ($$('x-peers:empty')) setTimeout(_ => window.animateBackground(true), 1750); // Start animation again
|
if ($$('x-peers:empty')) setTimeout(_ => window.animateBackground(true), 1750); // Start animation again
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,6 +226,18 @@ class PeersUI {
|
||||||
|
|
||||||
class PeerUI {
|
class PeerUI {
|
||||||
|
|
||||||
|
constructor(peer, connectionHash) {
|
||||||
|
this._peer = peer;
|
||||||
|
this._connectionHash = connectionHash;
|
||||||
|
this._initDom();
|
||||||
|
this._bindListeners();
|
||||||
|
|
||||||
|
$$('x-peers').appendChild(this.$el)
|
||||||
|
Events.fire('peer-added');
|
||||||
|
this.$xInstructions = $$('x-instructions');
|
||||||
|
setTimeout(_ => window.animateBackground(false), 1750); // Stop animation
|
||||||
|
}
|
||||||
|
|
||||||
html() {
|
html() {
|
||||||
let title;
|
let title;
|
||||||
let input = '';
|
let input = '';
|
||||||
|
@ -225,17 +250,24 @@ class PeerUI {
|
||||||
this.$el.innerHTML = `
|
this.$el.innerHTML = `
|
||||||
<label class="column center" title="${title}">
|
<label class="column center" title="${title}">
|
||||||
${input}
|
${input}
|
||||||
<x-icon shadow="1">
|
<x-icon>
|
||||||
|
<div class="icon-wrapper" shadow="1">
|
||||||
<svg class="icon"><use xlink:href="#"/></svg>
|
<svg class="icon"><use xlink:href="#"/></svg>
|
||||||
|
</div>
|
||||||
|
<div class="highlight-wrapper center">
|
||||||
|
<div class="highlight" shadow="1"></div>
|
||||||
|
</div>
|
||||||
</x-icon>
|
</x-icon>
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div class="circle"></div>
|
<div class="circle"></div>
|
||||||
<div class="circle right"></div>
|
<div class="circle right"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="device-descriptor">
|
||||||
<div class="name font-subheading"></div>
|
<div class="name font-subheading"></div>
|
||||||
<div class="device-name font-body2"></div>
|
<div class="device-name font-body2"></div>
|
||||||
<div class="status font-body2"></div>
|
<div class="status font-body2"></div>
|
||||||
<span class="connection-hash font-body2" title="To verify the security of the end-to-end encryption, compare this security number on both devices"></span>
|
<span class="connection-hash font-body2" title="To verify the security of the end-to-end encryption, compare this security number on both devices"></span>
|
||||||
|
</div>
|
||||||
</label>`;
|
</label>`;
|
||||||
|
|
||||||
this.$el.querySelector('svg use').setAttribute('xlink:href', this._icon());
|
this.$el.querySelector('svg use').setAttribute('xlink:href', this._icon());
|
||||||
|
@ -245,23 +277,12 @@ class PeerUI {
|
||||||
this._connectionHash.substring(0, 4) + " " + this._connectionHash.substring(4, 8) + " " + this._connectionHash.substring(8, 12) + " " + this._connectionHash.substring(12, 16);
|
this._connectionHash.substring(0, 4) + " " + this._connectionHash.substring(4, 8) + " " + this._connectionHash.substring(8, 12) + " " + this._connectionHash.substring(12, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(peer, connectionHash) {
|
|
||||||
this._peer = peer;
|
|
||||||
this._roomType = peer.roomType;
|
|
||||||
this._roomSecret = peer.roomSecret;
|
|
||||||
this._connectionHash = connectionHash;
|
|
||||||
this._initDom();
|
|
||||||
this._bindListeners();
|
|
||||||
$$('x-peers').appendChild(this.$el);
|
|
||||||
this.$xInstructions = $$('x-instructions');
|
|
||||||
setTimeout(_ => window.animateBackground(false), 1750); // Stop animation
|
|
||||||
}
|
|
||||||
|
|
||||||
_initDom() {
|
_initDom() {
|
||||||
this.$el = document.createElement('x-peer');
|
this.$el = document.createElement('x-peer');
|
||||||
this.$el.id = this._peer.id;
|
this.$el.id = this._peer.id;
|
||||||
this.$el.ui = this;
|
this.$el.ui = this;
|
||||||
this.$el.classList.add(`type-${this._roomType}`);
|
this._peer.roomTypes.forEach(roomType => this.$el.classList.add(`type-${roomType}`));
|
||||||
|
this.$el.classList.add('center');
|
||||||
if (!this._peer.rtcSupported || !window.isRtcSupported) this.$el.classList.add('ws-peer')
|
if (!this._peer.rtcSupported || !window.isRtcSupported) this.$el.classList.add('ws-peer')
|
||||||
this.html();
|
this.html();
|
||||||
|
|
||||||
|
@ -273,7 +294,7 @@ class PeerUI {
|
||||||
this._callbackDragLeave = e => this._onDragEnd(e)
|
this._callbackDragLeave = e => this._onDragEnd(e)
|
||||||
this._callbackDragOver = e => this._onDragOver(e)
|
this._callbackDragOver = e => this._onDragOver(e)
|
||||||
this._callbackContextMenu = e => this._onRightClick(e)
|
this._callbackContextMenu = e => this._onRightClick(e)
|
||||||
this._callbackTouchStart = _ => this._onTouchStart()
|
this._callbackTouchStart = e => this._onTouchStart(e)
|
||||||
this._callbackTouchEnd = e => this._onTouchEnd(e)
|
this._callbackTouchEnd = e => this._onTouchEnd(e)
|
||||||
this._callbackPointerDown = e => this._onPointerDown(e)
|
this._callbackPointerDown = e => this._onPointerDown(e)
|
||||||
// PasteMode
|
// PasteMode
|
||||||
|
@ -394,21 +415,28 @@ class PeerUI {
|
||||||
|
|
||||||
_onRightClick(e) {
|
_onRightClick(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
Events.fire('text-recipient', this._peer.id);
|
Events.fire('text-recipient', {
|
||||||
|
peerId: this._peer.id,
|
||||||
|
deviceName: e.target.closest('x-peer').querySelector('.name').innerText
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_onTouchStart() {
|
_onTouchStart(e) {
|
||||||
this._touchStart = Date.now();
|
this._touchStart = Date.now();
|
||||||
this._touchTimer = setTimeout(_ => this._onTouchEnd(), 610);
|
this._touchTimer = setTimeout(_ => this._onTouchEnd(e), 610);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onTouchEnd(e) {
|
_onTouchEnd(e) {
|
||||||
if (Date.now() - this._touchStart < 500) {
|
if (Date.now() - this._touchStart < 500) {
|
||||||
clearTimeout(this._touchTimer);
|
clearTimeout(this._touchTimer);
|
||||||
} else { // this was a long tap
|
} else if (this._touchTimer) { // this was a long tap
|
||||||
if (e) e.preventDefault();
|
e.preventDefault();
|
||||||
Events.fire('text-recipient', this._peer.id);
|
Events.fire('text-recipient', {
|
||||||
|
peerId: this._peer.id,
|
||||||
|
deviceName: e.target.closest('x-peer').querySelector('.name').innerText
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
this._touchTimer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -470,10 +498,10 @@ class ReceiveDialog extends Dialog {
|
||||||
class ReceiveFileDialog extends ReceiveDialog {
|
class ReceiveFileDialog extends ReceiveDialog {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('receiveFileDialog');
|
super('receive-file-dialog');
|
||||||
|
|
||||||
this.$shareOrDownloadBtn = this.$el.querySelector('#shareOrDownload');
|
this.$shareOrDownloadBtn = this.$el.querySelector('#share-or-download');
|
||||||
this.$receiveTitleNode = this.$el.querySelector('#receiveTitle')
|
this.$receiveTitleNode = this.$el.querySelector('#receive-title')
|
||||||
|
|
||||||
Events.on('files-received', e => this._onFilesReceived(e.detail.sender, e.detail.files, e.detail.request));
|
Events.on('files-received', e => this._onFilesReceived(e.detail.sender, e.detail.files, e.detail.request));
|
||||||
this._filesQueue = [];
|
this._filesQueue = [];
|
||||||
|
@ -632,15 +660,15 @@ class ReceiveFileDialog extends ReceiveDialog {
|
||||||
class ReceiveRequestDialog extends ReceiveDialog {
|
class ReceiveRequestDialog extends ReceiveDialog {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('receiveRequestDialog');
|
super('receive-request-dialog');
|
||||||
|
|
||||||
this.$requestingPeerDisplayNameNode = this.$el.querySelector('#requestingPeerDisplayName');
|
this.$requestingPeerDisplayNameNode = this.$el.querySelector('#requesting-peer-display-name');
|
||||||
this.$fileStemNode = this.$el.querySelector('#fileStem');
|
this.$fileStemNode = this.$el.querySelector('#file-stem');
|
||||||
this.$fileExtensionNode = this.$el.querySelector('#fileExtension');
|
this.$fileExtensionNode = this.$el.querySelector('#file-extension');
|
||||||
this.$fileOtherNode = this.$el.querySelector('#fileOther');
|
this.$fileOtherNode = this.$el.querySelector('#file-other');
|
||||||
|
|
||||||
this.$acceptRequestBtn = this.$el.querySelector('#acceptRequest');
|
this.$acceptRequestBtn = this.$el.querySelector('#accept-request');
|
||||||
this.$declineRequestBtn = this.$el.querySelector('#declineRequest');
|
this.$declineRequestBtn = this.$el.querySelector('#decline-request');
|
||||||
this.$acceptRequestBtn.addEventListener('click', _ => this._respondToFileTransferRequest(true));
|
this.$acceptRequestBtn.addEventListener('click', _ => this._respondToFileTransferRequest(true));
|
||||||
this.$declineRequestBtn.addEventListener('click', _ => this._respondToFileTransferRequest(false));
|
this.$declineRequestBtn.addEventListener('click', _ => this._respondToFileTransferRequest(false));
|
||||||
|
|
||||||
|
@ -721,12 +749,12 @@ class ReceiveRequestDialog extends ReceiveDialog {
|
||||||
|
|
||||||
class PairDeviceDialog extends Dialog {
|
class PairDeviceDialog extends Dialog {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('pairDeviceDialog');
|
super('pair-device-dialog');
|
||||||
$('pair-device').addEventListener('click', _ => this._pairDeviceInitiate());
|
$('pair-device').addEventListener('click', _ => this._pairDeviceInitiate());
|
||||||
this.$inputRoomKeyChars = this.$el.querySelectorAll('#keyInputContainer>input');
|
this.$inputRoomKeyChars = this.$el.querySelectorAll('#key-input-container>input');
|
||||||
this.$submitBtn = this.$el.querySelector('button[type="submit"]');
|
this.$submitBtn = this.$el.querySelector('button[type="submit"]');
|
||||||
this.$roomKey = this.$el.querySelector('#roomKey');
|
this.$roomKey = this.$el.querySelector('#room-key');
|
||||||
this.$qrCode = this.$el.querySelector('#roomKeyQrCode');
|
this.$qrCode = this.$el.querySelector('#room-key-qr-code');
|
||||||
this.$clearSecretsBtn = $('clear-pair-devices');
|
this.$clearSecretsBtn = $('clear-pair-devices');
|
||||||
this.$footerInstructionsPairedDevices = $('and-by-paired-devices');
|
this.$footerInstructionsPairedDevices = $('and-by-paired-devices');
|
||||||
let createJoinForm = this.$el.querySelector('form');
|
let createJoinForm = this.$el.querySelector('form');
|
||||||
|
@ -800,7 +828,7 @@ class PairDeviceDialog extends Dialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
evaluateRoomKeyChars() {
|
evaluateRoomKeyChars() {
|
||||||
if (this.$el.querySelectorAll('#keyInputContainer>input:placeholder-shown').length > 0) {
|
if (this.$el.querySelectorAll('#key-input-container>input:placeholder-shown').length > 0) {
|
||||||
this.$submitBtn.setAttribute("disabled", "");
|
this.$submitBtn.setAttribute("disabled", "");
|
||||||
} else {
|
} else {
|
||||||
this.inputRoomKey = "";
|
this.inputRoomKey = "";
|
||||||
|
@ -844,7 +872,7 @@ class PairDeviceDialog extends Dialog {
|
||||||
height: 150,
|
height: 150,
|
||||||
padding: 0,
|
padding: 0,
|
||||||
background: "transparent",
|
background: "transparent",
|
||||||
color: getComputedStyle(document.body).getPropertyValue('--text-color'),
|
color: `rgb(var(--text-color))`,
|
||||||
ecl: "L",
|
ecl: "L",
|
||||||
join: true
|
join: true
|
||||||
});
|
});
|
||||||
|
@ -936,13 +964,14 @@ class PairDeviceDialog extends Dialog {
|
||||||
this.$clearSecretsBtn.setAttribute('hidden', '');
|
this.$clearSecretsBtn.setAttribute('hidden', '');
|
||||||
this.$footerInstructionsPairedDevices.setAttribute('hidden', '');
|
this.$footerInstructionsPairedDevices.setAttribute('hidden', '');
|
||||||
}
|
}
|
||||||
|
Events.fire('bg-resize');
|
||||||
}).catch(_ => PersistentStorage.logBrowserNotCapable());
|
}).catch(_ => PersistentStorage.logBrowserNotCapable());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClearDevicesDialog extends Dialog {
|
class ClearDevicesDialog extends Dialog {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('clearDevicesDialog');
|
super('clear-devices-dialog');
|
||||||
$('clear-pair-devices').addEventListener('click', _ => this._onClearPairDevices());
|
$('clear-pair-devices').addEventListener('click', _ => this._onClearPairDevices());
|
||||||
let clearDevicesForm = this.$el.querySelector('form');
|
let clearDevicesForm = this.$el.querySelector('form');
|
||||||
clearDevicesForm.addEventListener('submit', _ => this._onSubmit());
|
clearDevicesForm.addEventListener('submit', _ => this._onSubmit());
|
||||||
|
@ -960,9 +989,10 @@ class ClearDevicesDialog extends Dialog {
|
||||||
|
|
||||||
class SendTextDialog extends Dialog {
|
class SendTextDialog extends Dialog {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('sendTextDialog');
|
super('send-text-dialog');
|
||||||
Events.on('text-recipient', e => this._onRecipient(e.detail));
|
Events.on('text-recipient', e => this._onRecipient(e.detail.peerId, e.detail.deviceName));
|
||||||
this.$text = this.$el.querySelector('#textInput');
|
this.$text = this.$el.querySelector('#text-input');
|
||||||
|
this.$peerDisplayName = this.$el.querySelector('#text-send-peer-display-name');
|
||||||
this.$form = this.$el.querySelector('form');
|
this.$form = this.$el.querySelector('form');
|
||||||
this.$submit = this.$el.querySelector('button[type="submit"]');
|
this.$submit = this.$el.querySelector('button[type="submit"]');
|
||||||
this.$form.addEventListener('submit', _ => this._send());
|
this.$form.addEventListener('submit', _ => this._send());
|
||||||
|
@ -993,8 +1023,9 @@ class SendTextDialog extends Dialog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onRecipient(peerId) {
|
_onRecipient(peerId, deviceName) {
|
||||||
this.correspondingPeerId = peerId;
|
this.correspondingPeerId = peerId;
|
||||||
|
this.$peerDisplayName.innerText = deviceName;
|
||||||
this.show();
|
this.show();
|
||||||
|
|
||||||
const range = document.createRange();
|
const range = document.createRange();
|
||||||
|
@ -1018,7 +1049,7 @@ class SendTextDialog extends Dialog {
|
||||||
|
|
||||||
class ReceiveTextDialog extends Dialog {
|
class ReceiveTextDialog extends Dialog {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('receiveTextDialog');
|
super('receive-text-dialog');
|
||||||
Events.on('text-received', e => this._onText(e.detail.text, e.detail.peerId));
|
Events.on('text-received', e => this._onText(e.detail.text, e.detail.peerId));
|
||||||
this.$text = this.$el.querySelector('#text');
|
this.$text = this.$el.querySelector('#text');
|
||||||
this.$copy = this.$el.querySelector('#copy');
|
this.$copy = this.$el.querySelector('#copy');
|
||||||
|
@ -1029,7 +1060,7 @@ class ReceiveTextDialog extends Dialog {
|
||||||
|
|
||||||
Events.on("keydown", e => this._onKeyDown(e));
|
Events.on("keydown", e => this._onKeyDown(e));
|
||||||
|
|
||||||
this.$receiveTextPeerDisplayNameNode = this.$el.querySelector('#receiveTextPeerDisplayName');
|
this.$receiveTextPeerDisplayNameNode = this.$el.querySelector('#receive-text-peer-display-name');
|
||||||
this._receiveTextQueue = [];
|
this._receiveTextQueue = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1090,13 +1121,13 @@ class ReceiveTextDialog extends Dialog {
|
||||||
class Base64ZipDialog extends Dialog {
|
class Base64ZipDialog extends Dialog {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('base64PasteDialog');
|
super('base64-paste-dialog');
|
||||||
const urlParams = new URL(window.location).searchParams;
|
const urlParams = new URL(window.location).searchParams;
|
||||||
const base64Text = urlParams.get('base64text');
|
const base64Text = urlParams.get('base64text');
|
||||||
const base64Zip = urlParams.get('base64zip');
|
const base64Zip = urlParams.get('base64zip');
|
||||||
const base64Hash = window.location.hash.substring(1);
|
const base64Hash = window.location.hash.substring(1);
|
||||||
|
|
||||||
this.$pasteBtn = this.$el.querySelector('#base64PasteBtn');
|
this.$pasteBtn = this.$el.querySelector('#base64-paste-btn');
|
||||||
|
|
||||||
if (base64Text) {
|
if (base64Text) {
|
||||||
this.show();
|
this.show();
|
||||||
|
@ -1247,6 +1278,7 @@ class Notifications {
|
||||||
this.$button.removeAttribute('hidden');
|
this.$button.removeAttribute('hidden');
|
||||||
this.$button.addEventListener('click', _ => this._requestPermission());
|
this.$button.addEventListener('click', _ => this._requestPermission());
|
||||||
}
|
}
|
||||||
|
// Todo: fix Notifications
|
||||||
Events.on('text-received', e => this._messageNotification(e.detail.text, e.detail.peerId));
|
Events.on('text-received', e => this._messageNotification(e.detail.text, e.detail.peerId));
|
||||||
Events.on('files-received', e => this._downloadNotification(e.detail.files));
|
Events.on('files-received', e => this._downloadNotification(e.detail.files));
|
||||||
}
|
}
|
||||||
|
@ -1322,7 +1354,7 @@ class Notifications {
|
||||||
}
|
}
|
||||||
|
|
||||||
_download(notification) {
|
_download(notification) {
|
||||||
$('shareOrDownload').click();
|
$('share-or-download').click();
|
||||||
notification.close();
|
notification.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1715,19 +1747,14 @@ Events.on('load', () => {
|
||||||
h = window.innerHeight;
|
h = window.innerHeight;
|
||||||
c.width = w;
|
c.width = w;
|
||||||
c.height = h;
|
c.height = h;
|
||||||
offset = h > 800
|
offset = $$('footer').offsetHeight - 32;
|
||||||
? 116
|
|
||||||
: h > 380
|
|
||||||
? 100
|
|
||||||
: 65;
|
|
||||||
|
|
||||||
if (w < 420) offset += 20;
|
|
||||||
x0 = w / 2;
|
x0 = w / 2;
|
||||||
y0 = h - offset;
|
y0 = h - offset;
|
||||||
dw = Math.max(w, h, 1000) / 13;
|
dw = Math.max(w, h, 1000) / 13;
|
||||||
drawCircles();
|
drawCircles();
|
||||||
}
|
}
|
||||||
window.onresize = init;
|
Events.on('bg-resize', _ => init());
|
||||||
|
window.onresize = _ => Events.fire('bg-resize');
|
||||||
|
|
||||||
function drawCircle(radius) {
|
function drawCircle(radius) {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
|
@ -1792,9 +1819,3 @@ Notifications permission has been blocked
|
||||||
as the user has dismissed the permission prompt several times.
|
as the user has dismissed the permission prompt several times.
|
||||||
This can be reset in Page Info
|
This can be reset in Page Info
|
||||||
which can be accessed by clicking the lock icon next to the URL.`;
|
which can be accessed by clicking the lock icon next to the URL.`;
|
||||||
|
|
||||||
document.body.onclick = _ => { // safari hack to fix audio
|
|
||||||
document.body.onclick = null;
|
|
||||||
if (!(/.*Version.*Safari.*/.test(navigator.userAgent))) return;
|
|
||||||
blop.play();
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const cacheVersion = 'v1.1.1';
|
const cacheVersion = 'v1.1.3';
|
||||||
const cacheTitle = `pairdrop-included-ws-fallback-cache-${cacheVersion}`;
|
const cacheTitle = `pairdrop-included-ws-fallback-cache-${cacheVersion}`;
|
||||||
const urlsToCache = [
|
const urlsToCache = [
|
||||||
'index.html',
|
'index.html',
|
||||||
|
|
|
@ -11,28 +11,25 @@
|
||||||
|
|
||||||
/* Layout */
|
/* Layout */
|
||||||
|
|
||||||
html {
|
|
||||||
min-height: 100%;
|
|
||||||
height: -webkit-fill-available;
|
|
||||||
}
|
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100vw;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overscroll-behavior-y: none;
|
overscroll-behavior: none;
|
||||||
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
min-height: 100%;
|
min-height: 100vh;
|
||||||
|
/* mobile viewport bug fix */
|
||||||
min-height: -webkit-fill-available;
|
min-height: -webkit-fill-available;
|
||||||
flex-grow: 1;
|
}
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
html {
|
||||||
overflow-y: hidden;
|
height: -webkit-fill-available;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-reverse {
|
.row-reverse {
|
||||||
|
@ -74,10 +71,7 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
position: absolute;
|
position: relative;
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 56px;
|
height: 56px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
|
@ -120,9 +114,9 @@ h3 {
|
||||||
}
|
}
|
||||||
|
|
||||||
.font-subheading {
|
.font-subheading {
|
||||||
font-size: 16px;
|
font-size: 14px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 24px;
|
line-height: 18px;
|
||||||
word-break: normal;
|
word-break: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,20 +194,160 @@ body>header a {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#center {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
flex-grow: 1;
|
||||||
|
--footer-height: 146px;
|
||||||
|
max-height: calc(100vh - 56px - var(--footer-height));
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: scroll;
|
||||||
|
overscroll-behavior-x: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 402px) and (max-width: 425px) {
|
||||||
|
header:has(#clear-pair-devices:not([hidden]))~#center {
|
||||||
|
--footer-height: 164px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 402px) {
|
||||||
|
#center {
|
||||||
|
--footer-height: 184px;
|
||||||
|
}
|
||||||
|
}
|
||||||
/* Peers List */
|
/* Peers List */
|
||||||
|
|
||||||
|
#x-peers-filler {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
x-peers {
|
x-peers {
|
||||||
width: 100%;
|
position: relative;
|
||||||
overflow: hidden;
|
display: flex;
|
||||||
flex-flow: row wrap;
|
flex-flow: row wrap;
|
||||||
|
flex-grow: 1;
|
||||||
|
align-items: start !important;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
transition: color 300ms;
|
transition: --bg-color 0.5s ease;
|
||||||
|
overflow-y: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overscroll-behavior-x: none;
|
||||||
|
scrollbar-width: none;
|
||||||
|
|
||||||
|
--peers-per-row: 6; /* default if browser does not support :has selector */
|
||||||
|
--x-peers-width: min(100vw, calc(var(--peers-per-row) * (var(--peer-width) + 25px) - 16px));
|
||||||
|
width: var(--x-peers-width);
|
||||||
|
margin-right: 20px;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers.overflowing {
|
||||||
|
background: /* Shadow covers */ linear-gradient(rgb(var(--bg-color)) 30%, rgba(var(--bg-color), 0)),
|
||||||
|
linear-gradient(rgba(var(--bg-color), 0), rgb(var(--bg-color)) 70%) 0 100%,
|
||||||
|
/* Shadows */ radial-gradient(farthest-side at 50% 0, rgba(var(--text-color), .2), rgba(var(--text-color), 0)),
|
||||||
|
radial-gradient(farthest-side at 50% 100%, rgba(var(--text-color), .2), rgba(var(--text-color), 0)) 0 100%;
|
||||||
|
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 100% 40px, 100% 40px, 100% 14px, 100% 14px;
|
||||||
|
|
||||||
|
/* Opera doesn't support this in the shorthand */
|
||||||
|
background-attachment: local, local, scroll, scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer) {
|
||||||
|
--peers-per-row: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* peers-per-row if height is too small for 2 rows */
|
||||||
|
@media screen and (min-height: 538px) and (max-height: 683px) and (max-width: 402px),
|
||||||
|
screen and (min-height: 517px) and (max-height: 664px) and (max-width: 426px),
|
||||||
|
screen and (min-height: 501px) and (max-height: 647px) and (min-width: 426px) {
|
||||||
|
x-peers:has(> x-peer) {
|
||||||
|
--peers-per-row: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(7)) {
|
||||||
|
--peers-per-row: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(10)) {
|
||||||
|
--peers-per-row: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(13)) {
|
||||||
|
--peers-per-row: 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(16)) {
|
||||||
|
--peers-per-row: 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(19)) {
|
||||||
|
--peers-per-row: 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(22)) {
|
||||||
|
--peers-per-row: 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(25)) {
|
||||||
|
--peers-per-row: 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* peers-per-row if height is too small for 3 rows */
|
||||||
|
@media screen and (min-height: 683px) and (max-width: 402px),
|
||||||
|
screen and (min-height: 664px) and (max-width: 426px),
|
||||||
|
screen and (min-height: 647px) and (min-width: 426px) {
|
||||||
|
x-peers:has(> x-peer) {
|
||||||
|
--peers-per-row: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(10)) {
|
||||||
|
--peers-per-row: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(13)) {
|
||||||
|
--peers-per-row: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(16)) {
|
||||||
|
--peers-per-row: 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(19)) {
|
||||||
|
--peers-per-row: 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(22)) {
|
||||||
|
--peers-per-row: 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(25)) {
|
||||||
|
--peers-per-row: 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peers:has(> x-peer:nth-of-type(28)) {
|
||||||
|
--peers-per-row: 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Empty Peers List */
|
/* Empty Peers List */
|
||||||
|
|
||||||
x-no-peers {
|
x-no-peers {
|
||||||
height: 114px;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
/* prevent flickering on load */
|
/* prevent flickering on load */
|
||||||
|
@ -255,25 +389,19 @@ x-no-peers[drop-bg] * {
|
||||||
x-peer {
|
x-peer {
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
padding: 8px;
|
||||||
|
align-content: start;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-peer label {
|
x-peer label {
|
||||||
width: var(--peer-width);
|
width: var(--peer-width);
|
||||||
padding: 8px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
touch-action: manipulation;
|
touch-action: manipulation;
|
||||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-peer .name {
|
|
||||||
width: var(--peer-width);
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="file"] {
|
input[type="file"] {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -281,27 +409,43 @@ input[type="file"] {
|
||||||
|
|
||||||
x-peer x-icon {
|
x-peer x-icon {
|
||||||
--icon-size: 40px;
|
--icon-size: 40px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
transition: transform 150ms;
|
||||||
|
will-change: transform;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peer .icon-wrapper {
|
||||||
width: var(--icon-size);
|
width: var(--icon-size);
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: var(--primary-color);
|
background: var(--primary-color);
|
||||||
color: white;
|
color: white;
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-bottom: 8px;
|
|
||||||
transition: transform 150ms;
|
|
||||||
will-change: transform;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
x-peer:not(.type-ip) x-icon {
|
x-peer:not(.type-ip).type-secret .icon-wrapper {
|
||||||
background: var(--paired-device-color);
|
background: var(--paired-device-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
x-peer.ws-peer x-icon {
|
x-peer x-icon > .highlight-wrapper {
|
||||||
border: solid 4px var(--ws-peer-color);
|
align-self: center;
|
||||||
|
align-items: center;
|
||||||
|
margin: 7px auto 0;
|
||||||
|
height: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-peer.ws-peer .progress {
|
x-peer x-icon > .highlight-wrapper > .highlight {
|
||||||
margin-top: 4px;
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peer.type-secret x-icon > .highlight-wrapper > .highlight {
|
||||||
|
background-color: var(--paired-device-color);
|
||||||
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-peer:not([status]):hover x-icon,
|
x-peer:not([status]):hover x-icon,
|
||||||
|
@ -315,6 +459,35 @@ x-peer[status] x-icon {
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
x-peer.ws-peer {
|
||||||
|
margin-top: -1.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peer.ws-peer .progress {
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peer.ws-peer .icon-wrapper{
|
||||||
|
border: solid 3px var(--ws-peer-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
x-peer.ws-peer .highlight-wrapper {
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-descriptor {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.status,
|
.status,
|
||||||
.device-name,
|
.device-name,
|
||||||
.connection-hash {
|
.connection-hash {
|
||||||
|
@ -380,12 +553,10 @@ x-peer[drop] x-icon {
|
||||||
/* Footer */
|
/* Footer */
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
position: absolute;
|
position: relative;
|
||||||
bottom: 0;
|
margin-top: auto;
|
||||||
left: 0;
|
z-index: 2;
|
||||||
right: 0;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 0 16px 0;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
transition: color 300ms;
|
transition: color 300ms;
|
||||||
}
|
}
|
||||||
|
@ -394,6 +565,7 @@ footer .logo {
|
||||||
--icon-size: 80px;
|
--icon-size: 80px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
|
margin-top: -10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
footer .font-body2 {
|
footer .font-body2 {
|
||||||
|
@ -434,11 +606,14 @@ x-dialog x-paper {
|
||||||
will-change: transform;
|
will-change: transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pairDeviceDialog x-paper {
|
#pair-device-dialog x-paper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: max(50%, 350px);
|
top: max(50%, 350px);
|
||||||
height: 650px;
|
height: 650px;
|
||||||
margin-top: -325px;
|
margin-top: -325px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-dialog:not([show]) {
|
x-dialog:not([show]) {
|
||||||
|
@ -470,13 +645,13 @@ x-dialog .font-subheading {
|
||||||
|
|
||||||
/* PairDevicesDialog */
|
/* PairDevicesDialog */
|
||||||
|
|
||||||
#keyInputContainer {
|
#key-input-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#keyInputContainer>input {
|
#key-input-container>input {
|
||||||
width: 45px;
|
width: 45px;
|
||||||
height: 45px;
|
height: 45px;
|
||||||
font-size: 30px;
|
font-size: 30px;
|
||||||
|
@ -492,15 +667,15 @@ x-dialog .font-subheading {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#keyInputContainer>input + * {
|
#key-input-container>input + * {
|
||||||
margin-left: 6px;
|
margin-left: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#keyInputContainer>input:nth-of-type(4) {
|
#key-input-container>input:nth-of-type(4) {
|
||||||
margin-left: 18px;
|
margin-left: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#roomKey {
|
#room-key {
|
||||||
font-size: 50px;
|
font-size: 50px;
|
||||||
letter-spacing: min(calc((100vw - 80px - 99px) / 100 * 7), 23px);
|
letter-spacing: min(calc((100vw - 80px - 99px) / 100 * 7), 23px);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
@ -508,19 +683,20 @@ x-dialog .font-subheading {
|
||||||
margin: 15px -15px;
|
margin: 15px -15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#roomKeyQrCode {
|
#room-key-qr-code {
|
||||||
padding: inherit;
|
padding: inherit;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
width: 150px;
|
width: 150px;
|
||||||
height: 150px;
|
height: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pairDeviceDialog hr {
|
#pair-device-dialog hr {
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
margin-bottom: 40px;
|
margin-bottom: 40px;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pairDeviceDialog x-background {
|
#pair-device-dialog x-background {
|
||||||
padding: 16px!important;
|
padding: 16px!important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,13 +711,13 @@ x-dialog h2 {
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
#receiveRequestDialog h2,
|
#receive-request-dialog h2,
|
||||||
#receiveFileDialog h2 {
|
#receive-file-dialog h2 {
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-dialog .row-reverse {
|
x-dialog .row-reverse {
|
||||||
margin: 40px -24px auto;
|
margin: 40px -24px 0;
|
||||||
border-top: solid 2.5px var(--border-color);
|
border-top: solid 2.5px var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -565,11 +741,11 @@ x-dialog .row-reverse {
|
||||||
word-break: normal;
|
word-break: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
#fileName {
|
#file-name {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
#fileStem {
|
#file-stem {
|
||||||
max-width: 80%;
|
max-width: 80%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
@ -583,13 +759,13 @@ x-dialog .row-reverse {
|
||||||
|
|
||||||
/* Send Text Dialog */
|
/* Send Text Dialog */
|
||||||
|
|
||||||
#textInput {
|
#text-input {
|
||||||
min-height: 120px;
|
min-height: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Receive Text Dialog */
|
/* Receive Text Dialog */
|
||||||
|
|
||||||
#receiveTextDialog #text {
|
#receive-text-dialog #text {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
|
@ -602,15 +778,15 @@ x-dialog .row-reverse {
|
||||||
margin-top:36px;
|
margin-top:36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#receiveTextDialog #text a {
|
#receive-text-dialog #text a {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#receiveTextDialog #text a:hover {
|
#receive-text-dialog #text a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
#receiveTextDialog h3 {
|
#receive-text-dialog h3 {
|
||||||
/* Select the received text when double-clicking the dialog */
|
/* Select the received text when double-clicking the dialog */
|
||||||
user-select: none;
|
user-select: none;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
@ -621,26 +797,26 @@ x-dialog .row-reverse {
|
||||||
margin: auto -25px;
|
margin: auto -25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#receiveTextDescriptionContainer {
|
#receive-text-description-container {
|
||||||
margin-bottom: 25px;
|
margin-bottom: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#base64PasteBtn {
|
#base64-paste-btn {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 40vh;
|
height: 40vh;
|
||||||
border: solid 12px #438cff;
|
border: solid 12px #438cff;
|
||||||
}
|
}
|
||||||
|
|
||||||
#base64PasteDialog button {
|
#base64-paste-dialog button {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#base64PasteDialog button[close] {
|
#base64-paste-dialog button[close] {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#base64PasteDialog button[close]:before {
|
#base64-paste-dialog button[close]:before {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -698,16 +874,18 @@ x-dialog .row-reverse {
|
||||||
opacity: 0.1;
|
opacity: 0.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#cancelPasteModeBtn {
|
#cancel-paste-mode-btn {
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
margin-top: 0;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100vw;
|
||||||
height: 56px;
|
height: 56px;
|
||||||
border-bottom: solid 2.5px var(--border-color);
|
background-color: var(--primary-color);
|
||||||
|
color: rgb(238, 238, 238);
|
||||||
}
|
}
|
||||||
|
|
||||||
.button:focus:before,
|
.button:focus:before,
|
||||||
|
@ -818,7 +996,7 @@ button::-moz-focus-inner {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
height: 80px;
|
height: 80px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: -8px;
|
||||||
clip: rect(0px, 80px, 80px, 40px);
|
clip: rect(0px, 80px, 80px, 40px);
|
||||||
--progress: rotate(0deg);
|
--progress: rotate(0deg);
|
||||||
transition: transform 200ms;
|
transition: transform 200ms;
|
||||||
|
@ -885,13 +1063,16 @@ x-toast:not([show]):not(:hover) {
|
||||||
/* Instructions */
|
/* Instructions */
|
||||||
|
|
||||||
x-instructions {
|
x-instructions {
|
||||||
position: absolute;
|
position: relative;
|
||||||
top: 120px;
|
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
transition: opacity 300ms;
|
transition: opacity 300ms;
|
||||||
z-index: -1;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 80%;
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-instructions:not([drop-peer]):not([drop-bg]):before {
|
x-instructions:not([drop-peer]):not([drop-bg]):before {
|
||||||
|
@ -908,92 +1089,84 @@ x-instructions[drop-bg]:not([drop-peer]):before {
|
||||||
|
|
||||||
x-instructions p {
|
x-instructions p {
|
||||||
display: none;
|
display: none;
|
||||||
margin: 0 auto auto;
|
|
||||||
max-width: 80%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
x-peers:empty~x-instructions {
|
x-peers:empty~x-instructions {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.websocket-fallback {
|
@media (hover: none) and (pointer: coarse) {
|
||||||
|
x-peer {
|
||||||
|
transform: scale(0.95);
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#websocket-fallback {
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: 5px;
|
||||||
|
padding: 5px;
|
||||||
|
text-align: center;
|
||||||
|
opacity: 0.5;
|
||||||
|
transition: opacity 300ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
#websocket-fallback>span {
|
||||||
|
margin: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#websocket-fallback > span > span {
|
||||||
border-bottom: solid 4px var(--ws-peer-color);
|
border-bottom: solid 4px var(--ws-peer-color);
|
||||||
padding-bottom: 1px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Responsive Styles */
|
/* Responsive Styles */
|
||||||
|
|
||||||
@media (min-height: 800px) {
|
@media screen and (min-height: 800px) {
|
||||||
footer {
|
#websocket-fallback {
|
||||||
margin-bottom: 16px;
|
padding-bottom: 15px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-height: 800px),
|
@media (hover: hover) and (pointer: fine) {
|
||||||
screen and (min-width: 1100px) {
|
|
||||||
x-instructions:not([drop-peer]):not([drop-bg]):before {
|
x-instructions:not([drop-peer]):not([drop-bg]):before {
|
||||||
content: attr(desktop);
|
content: attr(desktop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-height: 420px) {
|
|
||||||
x-instructions {
|
|
||||||
top: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer .logo {
|
|
||||||
--icon-size: 40px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
iOS specific styles
|
|
||||||
*/
|
|
||||||
@supports (-webkit-overflow-scrolling: touch) {
|
|
||||||
|
|
||||||
|
|
||||||
html {
|
|
||||||
position: fixed;
|
|
||||||
}
|
|
||||||
|
|
||||||
x-instructions:not([drop-peer]):not([drop-bg]):before {
|
|
||||||
content: attr(mobile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Color Themes
|
Color Themes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Default colors */
|
/* Default colors */
|
||||||
body {
|
body {
|
||||||
--text-color: #333;
|
--text-color: 51,51,51;
|
||||||
--bg-color: #fff;
|
--bg-color: 250,250,250; /*rgb code*/
|
||||||
|
--bg-color-test: 18,18,18;
|
||||||
--bg-color-secondary: #f1f3f4;
|
--bg-color-secondary: #f1f3f4;
|
||||||
--border-color: #e7e8e8;
|
--border-color: #e7e8e8;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dark theme colors */
|
/* Dark theme colors */
|
||||||
body.dark-theme {
|
body.dark-theme {
|
||||||
--text-color: #eee;
|
--text-color: 238,238,238;
|
||||||
--bg-color: #121212;
|
--bg-color: 18,18,18; /*rgb code*/
|
||||||
--bg-color-secondary: #333;
|
--bg-color-secondary: #333;
|
||||||
--border-color: #252525;
|
--border-color: #252525;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Colored Elements */
|
/* Colored Elements */
|
||||||
body {
|
body {
|
||||||
color: var(--text-color);
|
color: rgb(var(--text-color));
|
||||||
background-color: var(--bg-color);
|
background-color: rgb(var(--bg-color));
|
||||||
transition: background-color 0.5s ease;
|
transition: background-color 0.5s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-dialog x-paper {
|
x-dialog x-paper {
|
||||||
background-color: var(--bg-color);
|
background-color: rgb(var(--bg-color));
|
||||||
}
|
}
|
||||||
|
|
||||||
.textarea {
|
.textarea {
|
||||||
color: var(--text-color) !important;
|
color: rgb(var(--text-color)) !important;
|
||||||
background-color: var(--bg-color-secondary) !important;
|
background-color: var(--bg-color-secondary) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1031,16 +1204,16 @@ x-dialog x-paper {
|
||||||
|
|
||||||
/* defaults to dark theme */
|
/* defaults to dark theme */
|
||||||
body {
|
body {
|
||||||
--text-color: #eee;
|
--text-color: 238,238,238;
|
||||||
--bg-color: #121212;
|
--bg-color: 18,18,18; /*rgb code*/
|
||||||
--bg-color-secondary: #333;
|
--bg-color-secondary: #333;
|
||||||
--border-color: #252525;
|
--border-color: #252525;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Override dark mode with light mode styles if the user decides to swap */
|
/* Override dark mode with light mode styles if the user decides to swap */
|
||||||
body.light-theme {
|
body.light-theme {
|
||||||
--text-color: #333;
|
--text-color: 51,51,51;
|
||||||
--bg-color: #fafafa;
|
--bg-color: 250,250,250; /*rgb code*/
|
||||||
--bg-color-secondary: #f1f3f4;
|
--bg-color-secondary: #f1f3f4;
|
||||||
--border-color: #e7e8e8;
|
--border-color: #e7e8e8;
|
||||||
}
|
}
|
||||||
|
@ -1058,6 +1231,15 @@ x-dialog x-paper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
iOS specific styles
|
||||||
|
*/
|
||||||
|
@supports (-webkit-overflow-scrolling: touch) {
|
||||||
|
html {
|
||||||
|
min-height: -webkit-fill-available;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* webkit scrollbar style*/
|
/* webkit scrollbar style*/
|
||||||
|
|
||||||
::-webkit-scrollbar{
|
::-webkit-scrollbar{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue