dropbox - load script dynamically

This commit is contained in:
Azgaar 2022-02-08 00:47:17 +03:00
parent 8e480be704
commit aee78071c6
5 changed files with 100 additions and 88 deletions

View file

@ -7,35 +7,34 @@
</head> </head>
<body> <body>
<script> <script>
/* // open this page in a new window without query parameter to start auth
open this page in a new window without query parameter to start auth // setDropBoxToken(token) will be called on the opener window
window.opener.setDropBoxToken(token) will be called on the opener
window.
*/
const REDIRECT_URI = window.location.origin + window.location.pathname; const REDIRECT_URI = window.location.origin + window.location.pathname;
const dbxAuth = new Dropbox.DropboxAuth({clientId: "pdr9ae64ip0qno4"}); const auth = new Dropbox.DropboxAuth({clientId: "pdr9ae64ip0qno4"});
const spObj = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);
const searchParams = Object.fromEntries(spObj.entries()); const code = params.get("code");
const error = params.get("error");
if (searchParams.code) getToken(); if (code) getToken();
else doAuth(); // start authentication else if (error) window.opener.Cloud.providers.dropbox.returnError(params.get("error_description"));
else startAuth();
function doAuth() { function startAuth() {
dbxAuth auth
.getAuthenticationUrl(REDIRECT_URI, undefined, "code", "offline", undefined, undefined, true) .getAuthenticationUrl(REDIRECT_URI, undefined, "code", "offline", undefined, undefined, true)
.then(authUrl => { .then(authUrl => {
window.sessionStorage.clear(); window.sessionStorage.clear();
window.sessionStorage.setItem("codeVerifier", dbxAuth.codeVerifier); window.sessionStorage.setItem("codeVerifier", auth.codeVerifier);
window.location.href = authUrl; window.location.href = authUrl;
}) })
.catch(error => console.error(error)); .catch(error => console.error(error));
} }
function getToken() { function getToken() {
dbxAuth.setCodeVerifier(window.sessionStorage.getItem("codeVerifier")); auth.setCodeVerifier(window.sessionStorage.getItem("codeVerifier"));
dbxAuth auth
.getAccessTokenFromCode(REDIRECT_URI, searchParams.code) .getAccessTokenFromCode(REDIRECT_URI, code)
.then(resp => { .then(resp => {
const token = resp.result.access_token; const token = resp.result.access_token;
window.opener.Cloud.providers.dropbox.setDropBoxToken(token); window.opener.Cloud.providers.dropbox.setDropBoxToken(token);

View file

@ -3586,12 +3586,15 @@
<button onclick="quickLoad()" data-tip="Load map from browser storage (if saved before)">storage</button> <button onclick="quickLoad()" data-tip="Load map from browser storage (if saved before)">storage</button>
</div> </div>
<div id="loadFromDropbox"> <div id="loadFromDropbox">
<p style="margin-bottom: .3em">From your Dropbox account</p> <p style="margin-bottom: .3em">
<select id="loadFromDropboxSelect" style="width: 22em"></select> From your Dropbox account
<button id="dropboxConnectButton" onclick="connectToDropbox()" data-tip="Connect your Dropbox account to be able to load maps from it">Connect</button>
</p>
<div id="loadFromDropboxButtons" style="margin-bottom: .3em"> <select id="loadFromDropboxSelect" style="width: 22em"></select>
<button onclick="loadFromDropbox()" data-tip="Load .map file from your Dropbox">Open</button> <div id="loadFromDropboxButtons" style="margin-bottom: .6em">
<button onclick="createSharableDropboxLink()" data-tip="Select file and create a link to share with your friends">Create link</button> <button onclick="loadFromDropbox()" data-tip="Load .map file from your Dropbox">Load</button>
<button onclick="createSharableDropboxLink()" data-tip="Select file and create a link to share with your friends">Share</button>
</div> </div>
<div style="margin-top: .3em"> <div style="margin-top: .3em">
@ -4547,7 +4550,6 @@
<script defer src="libs/jquery.ui.touch-punch.min.js"></script> <script defer src="libs/jquery.ui.touch-punch.min.js"></script>
<script defer src="libs/pell.min.js"></script> <script defer src="libs/pell.min.js"></script>
<script defer src="libs/jszip.min.js"></script> <script defer src="libs/jszip.min.js"></script>
<script defer src="https://unpkg.com/dropbox@10.8.0/dist/Dropbox-sdk.min.js"></script>
<script defer src="modules/io/save.js"></script> <script defer src="modules/io/save.js"></script>
<script defer src="modules/io/load.js"></script> <script defer src="modules/io/load.js"></script>

View file

@ -213,7 +213,9 @@ function checkLoadParameters() {
const pattern = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/; const pattern = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
const valid = pattern.test(maplink); const valid = pattern.test(maplink);
if (valid) { if (valid) {
loadMapFromURL(maplink, 1); setTimeout(() => {
loadMapFromURL(maplink, 1);
}, 1000);
return; return;
} else showUploadErrorMessage("Map link is not a valid URL", maplink); } else showUploadErrorMessage("Map link is not a valid URL", maplink);
} }

View file

@ -12,7 +12,6 @@ async load(filename): load filename from provider
async list(): list available filenames at provider async list(): list available filenames at provider
async getLink(filePath): get shareable link for file async getLink(filePath): get shareable link for file
restore(): restore access tokens from storage if possible restore(): restore access tokens from storage if possible
*/ */
window.Cloud = (function () { window.Cloud = (function () {
@ -32,38 +31,40 @@ window.Cloud = (function () {
token: null, // Access token token: null, // Access token
api: null, api: null,
restore() {
this.token = getToken(this.name);
if (this.token) this.connect(this.token);
},
async call(name, param) { async call(name, param) {
try { try {
if (!this.api) await this.initialize();
return await this.api[name](param); return await this.api[name](param);
} catch (e) { } catch (e) {
if (e.name !== "DropboxResponseError") throw e; if (e.name !== "DropboxResponseError") throw e;
// retry with auth await this.auth(); // retry with auth
await this.auth();
return await this.api[name](param); return await this.api[name](param);
} }
}, },
connect(token) { initialize() {
const clientId = this.clientId; const token = getToken(this.name);
const auth = new Dropbox.DropboxAuth({clientId}); if (token) {
return this.connect(token);
} else {
return this.auth();
}
},
async connect(token) {
await import("https://unpkg.com/dropbox@10.8.0/dist/Dropbox-sdk.min.js");
const auth = new Dropbox.DropboxAuth({clientId: this.clientId});
auth.setAccessToken(token); auth.setAccessToken(token);
this.api = new Dropbox.Dropbox({auth}); this.api = new Dropbox.Dropbox({auth});
}, },
async save(fileName, contents) { async save(fileName, contents) {
if (!this.api) await this.auth(); const resp = await this.call("filesUpload", {path: "/" + fileName, contents});
const resp = this.call("filesUpload", {path: "/" + fileName, contents});
DEBUG && console.log("Dropbox response:", resp); DEBUG && console.log("Dropbox response:", resp);
return true; return true;
}, },
async load(path) { async load(path) {
if (!this.api) await this.auth();
const resp = await this.call("filesDownload", {path}); const resp = await this.call("filesDownload", {path});
const blob = resp.result.fileBlob; const blob = resp.result.fileBlob;
if (!blob) throw new Error("Invalid response from dropbox."); if (!blob) throw new Error("Invalid response from dropbox.");
@ -71,22 +72,23 @@ window.Cloud = (function () {
}, },
async list() { async list() {
if (!this.api) return null;
const resp = await this.call("filesListFolder", {path: ""}); const resp = await this.call("filesListFolder", {path: ""});
return resp.result.entries.map(e => ({name: e.name, path: e.path_lower})); return resp.result.entries.map(e => ({name: e.name, path: e.path_lower}));
}, },
auth() { auth() {
const url = window.location.origin + window.location.pathname + "dropbox.html"; const width = 640;
this.authWindow = window.open(url, "auth", "width=640,height=480"); const height = 480;
// child window expected to call const left = window.innerWidth / 2 - width / 2;
// window.opener.Cloud.providers.dropbox.setDropBoxToken (see below) const top = window.innerHeight / 2 - height / 2.5;
this.authWindow = window.open("./dropbox.html", "auth", `width=640, height=${height}, top=${top}, left=${left}}`);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const watchDog = () => { const watchDog = setTimeout(() => {
this.authWindow.close(); this.authWindow.close();
reject(new Error("Timeout. No auth for dropbox.")); reject(new Error("Timeout. No auth for Dropbox"));
}; }, 120 * 1000);
setTimeout(watchDog, 120 * 1000);
window.addEventListener("dropboxauth", e => { window.addEventListener("dropboxauth", e => {
clearTimeout(watchDog); clearTimeout(watchDog);
resolve(); resolve();
@ -94,46 +96,34 @@ window.Cloud = (function () {
}); });
}, },
// Callback function for auth window. // Callback function for auth window
setDropBoxToken(token) { async setDropBoxToken(token) {
DEBUG && console.log("Access token:", token); DEBUG && console.log("Access token:", token);
setToken(this.name, token); setToken(this.name, token);
this.connect(token); await this.connect(token);
this.authWindow.close(); this.authWindow.close();
window.dispatchEvent(new Event("dropboxauth")); window.dispatchEvent(new Event("dropboxauth"));
}, },
returnError(errorDescription) {
console.error(errorDescription);
tip(errorDescription.replaceAll("+", " "), true, "error", 4000);
this.authWindow.close();
},
async getLink(path) { async getLink(path) {
if (!this.api) await this.auth(); // return existitng shared link
let resp; const sharedLinks = await this.call("sharingListSharedLinks", {path});
if (sharedLinks.result.links.length) return resp.result.links[0].url;
// already exists? // create new shared link
resp = await this.call("sharingListSharedLinks", {path}); const settings = {require_password: false, audience: "public", access: "viewer", requested_visibility: "public", allow_download: true};
if (resp.result.links.length) return resp.result.links[0].url; const resp = await this.call("sharingCreateSharedLinkWithSettings", {path, settings});
// create new
resp = await this.call("sharingCreateSharedLinkWithSettings", {
path,
settings: {
require_password: false,
audience: "public",
access: "viewer",
requested_visibility: "public",
allow_download: true
}
});
DEBUG && console.log("Dropbox link object:", resp.result); DEBUG && console.log("Dropbox link object:", resp.result);
return resp.result.url; return resp.result.url;
} }
}; };
// register providers here: const providers = {dropbox: DBP};
const providers = {
dropbox: DBP
};
// restore all providers at startup
for (const p of Object.values(providers)) p.restore();
return {providers}; return {providers};
})(); })();

View file

@ -737,24 +737,43 @@ async function showLoadPane() {
} }
}); });
const loadFromDropboxButtons = document.getElementById("loadFromDropboxButtons"); // already connected to Dropbox: list saved maps
const fileSelect = document.getElementById("loadFromDropboxSelect"); if (Cloud.providers.dropbox.api) {
const files = await Cloud.providers.dropbox.list(); document.getElementById("dropboxConnectButton").style.display = "none";
document.getElementById("loadFromDropboxSelect").style.display = "block";
const loadFromDropboxButtons = document.getElementById("loadFromDropboxButtons");
const fileSelect = document.getElementById("loadFromDropboxSelect");
fileSelect.innerHTML = `<option value="" disabled selected>Loading...</option>`;
const files = await Cloud.providers.dropbox.list();
if (!files) {
loadFromDropboxButtons.style.display = "none";
fileSelect.innerHTML = `<option value="" disabled selected>Save files to Dropbox first</option>`;
return;
}
loadFromDropboxButtons.style.display = "block";
fileSelect.innerHTML = "";
files.forEach(file => {
const opt = document.createElement("option");
opt.innerText = file.name;
opt.value = file.path;
fileSelect.appendChild(opt);
});
if (!files) {
loadFromDropboxButtons.style.display = "none";
fileSelect.innerHTML = `<option value="" disabled selected>Save files to Dropbox first</option>`;
return; return;
} }
loadFromDropboxButtons.style.display = "block"; // not connected to Dropbox: show connect button
fileSelect.innerHTML = ""; document.getElementById("dropboxConnectButton").style.display = "inline-block";
files.forEach(file => { document.getElementById("loadFromDropboxButtons").style.display = "none";
const opt = document.createElement("option"); document.getElementById("loadFromDropboxSelect").style.display = "none";
opt.innerText = file.name; }
opt.value = file.path;
fileSelect.appendChild(opt); async function connectToDropbox() {
}); await Cloud.providers.dropbox.initialize();
if (Cloud.providers.dropbox.api) showLoadPane();
} }
function loadURL() { function loadURL() {