Merge branch 'implement_paste_base64_zip'

This commit is contained in:
schlagmichdoch 2023-01-19 04:42:02 +01:00
commit 000cdb2f70
7 changed files with 178 additions and 57 deletions

View file

@ -18,6 +18,9 @@ server {
location /ca.crt { location /ca.crt {
alias /etc/ssl/certs/snapdropCA.crt; alias /etc/ssl/certs/snapdropCA.crt;
} }
# To allow POST on static pages
error_page 405 =200 $uri;
} }
server { server {
@ -42,5 +45,7 @@ server {
location /ca.crt { location /ca.crt {
alias /etc/ssl/certs/snapdropCA.crt; alias /etc/ssl/certs/snapdropCA.crt;
} }
# To allow POST on static pages
error_page 405 =200 $uri;
} }

View file

@ -10,6 +10,8 @@ server {
location /ca.crt { location /ca.crt {
alias /etc/ssl/certs/snapdropCA.crt; alias /etc/ssl/certs/snapdropCA.crt;
} }
# To allow POST on static pages
error_page 405 =200 $uri;
} }
server { server {
@ -34,5 +36,7 @@ server {
location /ca.crt { location /ca.crt {
alias /etc/ssl/certs/snapdropCA.crt; alias /etc/ssl/certs/snapdropCA.crt;
} }
# To allow POST on static pages
error_page 405 =200 $uri;
} }

View file

@ -88,7 +88,7 @@
<div id="displayName" placeholder="&nbsp;"></div> <div id="displayName" placeholder="&nbsp;"></div>
<div class="font-body2">You can be discovered by everyone on this network</div> <div class="font-body2">You can be discovered by everyone on this network</div>
</footer> </footer>
<!-- JoinRoom Dialog --> <!-- Pair Device Dialog -->
<x-dialog id="pairDeviceDialog"> <x-dialog id="pairDeviceDialog">
<form action="#"> <form action="#">
<x-background class="full center text-center"> <x-background class="full center text-center">
@ -116,6 +116,7 @@
</x-background> </x-background>
</form> </form>
</x-dialog> </x-dialog>
<!-- Clear Devices Dialog -->
<x-dialog id="clearDevicesDialog"> <x-dialog id="clearDevicesDialog">
<form action="#"> <form action="#">
<x-background class="full center text-center"> <x-background class="full center text-center">
@ -192,6 +193,15 @@
</x-paper> </x-paper>
</x-background> </x-background>
</x-dialog> </x-dialog>
<!-- Receive Dialog -->
<x-dialog id="base64ZipDialog">
<x-background class="full center">
<x-paper shadow="2">
<button class="button center" id="base64ZipPasteBtn" title="Paste">Tap here to paste files</button>
<button class="button center" close>Close</button>
</x-paper>
</x-background>
</x-dialog>
<!-- Toast --> <!-- Toast -->
<div class="toast-container full center"> <div class="toast-container full center">
<x-toast class="row" shadow="1" id="toast">File Transfer Completed</x-toast> <x-toast class="row" shadow="1" id="toast">File Transfer Completed</x-toast>

View file

@ -30,12 +30,17 @@
"display": "minimal-ui", "display": "minimal-ui",
"theme_color": "#3367d6", "theme_color": "#3367d6",
"share_target": { "share_target": {
"method":"GET", "action": "/",
"action": "/?share_target", "method":"POST",
"enctype": "multipart/form-data",
"params": { "params": {
"title": "title", "title": "title",
"text": "text", "text": "text",
"url": "url" "url": "url",
"files": [{
"name": "allfiles",
"accept": ["*/*"]
}]
} }
}, },
"file_handlers": [ "file_handlers": [

View file

@ -931,7 +931,6 @@ class SendTextDialog extends Dialog {
_onRecipient(recipient) { _onRecipient(recipient) {
this._recipient = recipient; this._recipient = recipient;
this._handleShareTargetText();
this.show(); this.show();
const range = document.createRange(); const range = document.createRange();
@ -943,12 +942,6 @@ class SendTextDialog extends Dialog {
sel.addRange(range); sel.addRange(range);
} }
_handleShareTargetText() {
if (!window.shareTargetText) return;
this.$text.textContent = window.shareTargetText;
window.shareTargetText = '';
}
_send() { _send() {
Events.fire('send-text', { Events.fire('send-text', {
to: this._recipient, to: this._recipient,
@ -997,6 +990,45 @@ class ReceiveTextDialog extends Dialog {
} }
} }
class Base64ZipDialog extends Dialog {
constructor() {
super('base64ZipDialog');
const urlParams = new URL(window.location).searchParams;
const base64zip = urlParams.get('base64zip');
this.$pasteBtn = this.$el.querySelector('#base64ZipPasteBtn')
this.$pasteBtn.addEventListener('click', _ => this.processClipboard())
if (base64zip) this.show();
}
async processClipboard() {
this.$pasteBtn.pointerEvents = "none";
this.$pasteBtn.innerText = "Processing...";
try {
const base64zip = await navigator.clipboard.readText();
let bstr = atob(base64zip), n = bstr.length, u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
const zipBlob = new File([u8arr], 'archive.zip');
let files = [];
const zipEntries = await zipper.getEntries(zipBlob);
for (let i = 0; i < zipEntries.length; i++) {
let fileBlob = await zipper.getData(zipEntries[i]);
files.push(new File([fileBlob], zipEntries[i].filename));
}
Events.fire('activate-paste-mode', {files: files, text: ""})
} catch (e) {
Events.fire('notify-user', 'Clipboard content is malformed.')
} finally {
window.history.replaceState({}, "Rewrite URL", '/');
this.hide();
}
}
}
class Toast extends Dialog { class Toast extends Dialog {
constructor() { constructor() {
super('toast'); super('toast');
@ -1135,20 +1167,35 @@ class NetworkStatusUI {
class WebShareTargetUI { class WebShareTargetUI {
constructor() { constructor() {
const parsedUrl = new URL(window.location); const urlParams = new URL(window.location).searchParams;
const title = parsedUrl.searchParams.get('title'); const share_target_type = urlParams.get("share-target")
const text = parsedUrl.searchParams.get('text'); if (share_target_type) {
const url = parsedUrl.searchParams.get('url'); if (share_target_type === "text") {
const title = urlParams.get('title') || '';
const text = urlParams.get('text') || '';
const url = urlParams.get('url') || '';
let shareTargetText;
let shareTargetText = title ? title : ''; if (url) {
shareTargetText += text ? shareTargetText ? ' ' + text : text : ''; shareTargetText = url; // We share only the Link - no text. Because link-only text becomes clickable.
} else if (title && text) {
shareTargetText = title + '\r\n' + text;
} else {
shareTargetText = title + text;
}
if(url) shareTargetText = url; // We share only the Link - no text. Because link-only text becomes clickable.
if (!shareTargetText) return;
window.shareTargetText = shareTargetText;
history.pushState({}, 'URL Rewrite', '/');
console.log('Shared Target Text:', '"' + shareTargetText + '"'); console.log('Shared Target Text:', '"' + shareTargetText + '"');
Events.fire('activate-paste-mode', {files: [], text: shareTargetText})
} else if (share_target_type === "files") {
caches.match("share_target_files")
.then(files => {
console.debug(files)
Events.fire('activate-paste-mode', {files: files, text: ""})
})
caches.delete("share_target_files").then( _ => console.log("shared files deleted from cache"));
}
window.history.replaceState({}, "Rewrite URL", '/');
}
} }
} }
@ -1219,7 +1266,7 @@ class PersistentStorage {
} }
logBrowserNotCapable() { logBrowserNotCapable() {
console.log("This browser does not support IndexedDB. Paired devices will be gone after closing the browser."); console.log("This browser does not support IndexedDB. Paired devices will be gone after the browser is closed.");
} }
static set(key, value) { static set(key, value) {
@ -1382,6 +1429,7 @@ class PairDrop {
const receiveTextDialog = new ReceiveTextDialog(); const receiveTextDialog = new ReceiveTextDialog();
const pairDeviceDialog = new PairDeviceDialog(); const pairDeviceDialog = new PairDeviceDialog();
const clearDevicesDialog = new ClearDevicesDialog(); const clearDevicesDialog = new ClearDevicesDialog();
const base64ZipDialog = new Base64ZipDialog();
const toast = new Toast(); const toast = new Toast();
const notifications = new Notifications(); const notifications = new Notifications();
const networkStatusUI = new NetworkStatusUI(); const networkStatusUI = new NetworkStatusUI();

View file

@ -27,6 +27,36 @@ self.addEventListener('install', function(event) {
self.addEventListener('fetch', function(event) { self.addEventListener('fetch', function(event) {
if (event.request.method === "POST") {
// Requests related to Web Share Target.
event.respondWith(
(async () => {
const formData = await event.request.formData();
const title = formData.get("title");
const text = formData.get("text");
const url = formData.get("url");
const files = formData.get("files");
console.debug(title)
console.debug(text)
console.debug(url)
console.debug(files)
let share_url = "/";
if (files.length > 0) {
// Save to Cache?
caches.open("share_target_files")
.then(cache => {
cache.addAll(files)
console.debug("files added to cache")
});
share_url = "/?share-target=files";
} else if (title.length > 0 || text.length > 0 || url.length) {
share_url = `/?share-target=text&title=${title}&text=${text}&url=${url}`;
}
return Response.redirect(encodeURI(share_url), 303);
})()
);
} else {
// Regular requests not related to Web Share Target.
event.respondWith( event.respondWith(
caches.match(event.request) caches.match(event.request)
.then(function (response) { .then(function (response) {
@ -38,6 +68,7 @@ self.addEventListener('fetch', function(event) {
} }
) )
); );
}
}); });

View file

@ -532,10 +532,28 @@ x-dialog .row-reverse {
pointer-events: none; pointer-events: none;
} }
#base64ZipPasteBtn {
width: 100%;
height: 40vh;
border: solid 12px #438cff;
}
#base64ZipDialog button {
margin: auto;
border-radius: 8px;
}
#base64ZipDialog button[close] {
margin-top: 20px;
}
#base64ZipDialog button[close]:before {
border-radius: 8px;
}
/* Button */ /* Button */
.button { .button {
padding: 0 16px; padding: 2px 16px 0;
box-sizing: border-box; box-sizing: border-box;
min-height: 36px; min-height: 36px;
min-width: 100px; min-width: 100px;