Add: webdav sync (#1363)

This commit is contained in:
eight 2021-12-12 08:05:58 +08:00 committed by GitHub
parent 3ea7e45624
commit f6e6a138db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 90 additions and 12 deletions

View File

@ -1200,6 +1200,15 @@
} }
} }
}, },
"optionsSyncUsername": {
"message": "Username"
},
"optionsSyncPassword": {
"message": "Password"
},
"optionsSyncUrl": {
"message": "URL"
},
"optionsSyncStatusRelogin": { "optionsSyncStatusRelogin": {
"message": "Session expired, please login again." "message": "Session expired, please login again."
}, },

View File

@ -1,5 +1,5 @@
/* global API msg */// msg.js /* global API msg */// msg.js
/* global chromeLocal */// storage-util.js /* global chromeLocal chromeSync */// storage-util.js
/* global compareRevision */// common.js /* global compareRevision */// common.js
/* global iconMan */ /* global iconMan */
/* global prefs */ /* global prefs */
@ -18,6 +18,7 @@ const syncMan = (() => {
disconnecting: 'disconnecting', disconnecting: 'disconnecting',
}); });
const STORAGE_KEY = 'sync/state/'; const STORAGE_KEY = 'sync/state/';
const NO_LOGIN = ['webdav'];
const status = /** @namespace SyncManager.Status */ { const status = /** @namespace SyncManager.Status */ {
STATES, STATES,
state: STATES.disconnected, state: STATES.disconnected,
@ -85,19 +86,29 @@ const syncMan = (() => {
return ctrl.put(...args); return ctrl.put(...args);
}, },
async setDriveOptions(driveName, options) {
const key = `secure/sync/driveOptions/${driveName}`;
await chromeSync.setValue(key, options);
},
async getDriveOptions(driveName) {
const key = `secure/sync/driveOptions/${driveName}`;
return await chromeSync.getValue(key) || {};
},
async start(name, fromPref = false) { async start(name, fromPref = false) {
if (ready.then) await ready; if (ready.then) await ready;
if (!ctrl) await initController(); if (!ctrl) await initController();
if (currentDrive) return; if (currentDrive) return;
currentDrive = getDrive(name); currentDrive = await getDrive(name);
ctrl.use(currentDrive); ctrl.use(currentDrive);
status.state = STATES.connecting; status.state = STATES.connecting;
status.currentDriveName = currentDrive.name; status.currentDriveName = currentDrive.name;
emitStatusChange(); emitStatusChange();
if (fromPref) { if (fromPref || NO_LOGIN.includes(currentDrive.name)) {
status.login = true; status.login = true;
} else { } else {
try { try {
@ -240,11 +251,11 @@ const syncMan = (() => {
} }
} }
function getDrive(name) { async function getDrive(name) {
if (name === 'dropbox' || name === 'google' || name === 'onedrive') { if (name === 'dropbox' || name === 'google' || name === 'onedrive' || name === 'webdav') {
return dbToCloud.drive[name]({ const options = await syncMan.getDriveOptions(name);
getAccessToken: () => tokenMan.getToken(name), options.getAccessToken = () => tokenMan.getToken(name);
}); return dbToCloud.drive[name](options);
} }
throw new Error(`unknown cloud name: ${name}`); throw new Error(`unknown cloud name: ${name}`);
} }

View File

@ -199,10 +199,27 @@
<option value="dropbox">Dropbox</option> <option value="dropbox">Dropbox</option>
<option value="google">Google Drive</option> <option value="google">Google Drive</option>
<option value="onedrive">OneDrive</option> <option value="onedrive">OneDrive</option>
<option value="webdav">WebDAV</option>
</select> </select>
<svg class="svg-icon select-arrow"><use xlink:href="#svg-icon-select-arrow"/></svg> <svg class="svg-icon select-arrow"><use xlink:href="#svg-icon-select-arrow"/></svg>
</div> </div>
</div> </div>
<fieldset class="drive-options">
<div class="webdav-options" data-drive="webdav">
<label class="url">
<span i18n-text="optionsSyncUrl"></span>
<input type="text" data-option="url">
</label>
<label class="username">
<span i18n-text="optionsSyncUsername"></span>
<input type="text" data-option="username">
</label>
<label class="password">
<span i18n-text="optionsSyncPassword"></span>
<input type="password" data-option="password">
</label>
</div>
</fieldset>
<div class="actions"> <div class="actions">
<button type="button" class="connect" i18n-text="optionsSyncConnect"></button> <button type="button" class="connect" i18n-text="optionsSyncConnect"></button>
<button type="button" class="disconnect" i18n-text="optionsSyncDisconnect"></button> <button type="button" class="disconnect" i18n-text="optionsSyncDisconnect"></button>

View File

@ -163,7 +163,7 @@ label > :first-child {
} }
label:not([disabled]), label:not([disabled]),
label:not([disabled]) :not([type="number"]) { label:not([disabled]) :not([type="number"]):not([type="text"]):not([type="password"]) {
cursor: pointer; cursor: pointer;
} }
@ -437,7 +437,25 @@ input[type="radio"].radio:checked::after {
.sync-status::first-letter { .sync-status::first-letter {
text-transform: uppercase; text-transform: uppercase;
} }
.sync-options .drive-options {
margin: 0;
padding: 0;
border: 0;
}
.drive-options > :not([hidden]) {
display: table;
width: 100%;
}
.drive-options > * > label {
display: table-row;
}
.drive-options > * > label > * {
display: table-cell;
}
.drive-options > * input {
width: 100%;
box-sizing: border-box;
}
.sync-options .actions button { .sync-options .actions button {
margin-top: .5em; margin-top: .5em;
} }

View File

@ -96,6 +96,7 @@ document.onclick = e => {
const elSyncNow = $('.sync-options .sync-now'); const elSyncNow = $('.sync-options .sync-now');
const elStatus = $('.sync-options .sync-status'); const elStatus = $('.sync-options .sync-status');
const elLogin = $('.sync-options .sync-login'); const elLogin = $('.sync-options .sync-login');
const elDriveOptions = $('.sync-options .drive-options');
/** @type {Sync.Status} */ /** @type {Sync.Status} */
let status = {}; let status = {};
msg.onExtension(e => { msg.onExtension(e => {
@ -108,7 +109,10 @@ document.onclick = e => {
elCloud.on('change', updateButtons); elCloud.on('change', updateButtons);
for (const [btn, fn] of [ for (const [btn, fn] of [
[elStart, () => API.sync.start(elCloud.value)], [elStart, async () => {
await API.sync.setDriveOptions(elCloud.value, getDriveOptions());
await API.sync.start(elCloud.value);
}],
[elStop, API.sync.stop], [elStop, API.sync.stop],
[elSyncNow, API.sync.syncNow], [elSyncNow, API.sync.syncNow],
[elLogin, async () => { [elLogin, async () => {
@ -123,12 +127,26 @@ document.onclick = e => {
}); });
} }
function getDriveOptions() {
const result = {};
for (const el of $$(`[data-drive=${elCloud.value}] [data-option]`)) {
result[el.dataset.option] = el.value;
}
return result;
}
function setDriveOptions(options) {
for (const el of $$(`[data-drive=${elCloud.value}] [data-option]`)) {
el.value = options[el.dataset.option] || '';
}
}
function setStatus(newStatus) { function setStatus(newStatus) {
status = newStatus; status = newStatus;
updateButtons(); updateButtons();
} }
function updateButtons() { async function updateButtons() {
const {state, STATES} = status; const {state, STATES} = status;
const isConnected = state === STATES.connected; const isConnected = state === STATES.connected;
const isDisconnected = state === STATES.disconnected; const isDisconnected = state === STATES.disconnected;
@ -137,6 +155,7 @@ document.onclick = e => {
} }
for (const [el, enable] of [ for (const [el, enable] of [
[elCloud, isDisconnected], [elCloud, isDisconnected],
[elDriveOptions, isDisconnected],
[elStart, isDisconnected && elCloud.value !== 'none'], [elStart, isDisconnected && elCloud.value !== 'none'],
[elStop, isConnected && !status.syncing], [elStop, isConnected && !status.syncing],
[elSyncNow, isConnected && !status.syncing && status.login], [elSyncNow, isConnected && !status.syncing && status.login],
@ -145,6 +164,10 @@ document.onclick = e => {
} }
elStatus.textContent = getStatusText(); elStatus.textContent = getStatusText();
elLogin.hidden = !isConnected || status.login; elLogin.hidden = !isConnected || status.login;
for (const el of elDriveOptions.children) {
el.hidden = el.dataset.drive !== elCloud.value;
}
setDriveOptions(await API.sync.getDriveOptions(elCloud.value));
} }
function getStatusText() { function getStatusText() {