WebDAV-/Nextcloud-Sync mit Einstellungsseite (v1.1.0)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
bec85f7e50
commit
bb7ee831a8
@@ -1,9 +1,11 @@
|
||||
const $ = (id) => document.getElementById(id);
|
||||
const store = chrome.storage.sync;
|
||||
|
||||
let links = [];
|
||||
let deleted = [];
|
||||
let editing = false;
|
||||
let editId = null;
|
||||
let syncOn = false;
|
||||
let syncState = ""; // "", "busy", "ok", "err"
|
||||
|
||||
const uid = () => Math.random().toString(36).slice(2, 10);
|
||||
|
||||
@@ -14,11 +16,43 @@ function normalizeUrl(raw) {
|
||||
}
|
||||
|
||||
async function load() {
|
||||
const data = await store.get("links");
|
||||
links = data.links || [];
|
||||
const s = await LabSync.readLocal();
|
||||
links = s.links;
|
||||
deleted = s.deleted;
|
||||
// Einträge aus Version 1.0 haben noch keinen Zeitstempel
|
||||
if (links.some((l) => !l.updated)) {
|
||||
links = links.map((l) => ({ ...l, updated: l.updated || Date.now() }));
|
||||
await save(false);
|
||||
}
|
||||
render();
|
||||
syncOn = !!(await LabSync.getConfig())?.url;
|
||||
render();
|
||||
if (syncOn) runSync();
|
||||
}
|
||||
|
||||
async function save(sync = true) {
|
||||
await LabSync.writeLocal({ links, deleted, modified: Date.now() });
|
||||
if (sync && syncOn) runSync();
|
||||
}
|
||||
|
||||
async function runSync() {
|
||||
setSync("busy");
|
||||
try {
|
||||
await LabSync.sync();
|
||||
const s = await LabSync.readLocal();
|
||||
links = s.links;
|
||||
deleted = s.deleted;
|
||||
setSync("ok");
|
||||
} catch (e) {
|
||||
setSync("err", e.message);
|
||||
}
|
||||
render();
|
||||
}
|
||||
const save = () => store.set({ links });
|
||||
|
||||
function setSync(state, msg = "") {
|
||||
syncState = state;
|
||||
$("syncBtn").title = msg || "Mit WebDAV synchronisieren";
|
||||
}
|
||||
|
||||
function render() {
|
||||
const q = $("search").value.trim().toLowerCase();
|
||||
@@ -57,6 +91,8 @@ function render() {
|
||||
document.body.classList.toggle("editing", editing);
|
||||
for (const id of ["add", "export", "import"]) $(id).hidden = !editing;
|
||||
$("editToggle").textContent = editing ? "[FERTIG]" : "[EDIT]";
|
||||
$("syncBtn").hidden = !syncOn;
|
||||
$("syncBtn").className = syncState;
|
||||
}
|
||||
|
||||
function btn(label, fn) {
|
||||
@@ -77,6 +113,7 @@ async function move(id, dir) {
|
||||
|
||||
async function remove(id) {
|
||||
links = links.filter((l) => l.id !== id);
|
||||
deleted = [...deleted.filter((t) => t.id !== id), { id, updated: Date.now() }];
|
||||
await save();
|
||||
render();
|
||||
}
|
||||
@@ -99,7 +136,7 @@ $("form").addEventListener("submit", async (e) => {
|
||||
e.preventDefault();
|
||||
const url = normalizeUrl($("fUrl").value);
|
||||
if (!url) { $("fUrl").focus(); return; }
|
||||
const entry = { name: $("fName").value.trim(), url, icon: $("fIcon").value.trim() };
|
||||
const entry = { name: $("fName").value.trim(), url, icon: $("fIcon").value.trim(), updated: Date.now() };
|
||||
if (editId === "new") links.push({ id: uid(), ...entry });
|
||||
else Object.assign(links.find((l) => l.id === editId), entry);
|
||||
await save();
|
||||
@@ -114,6 +151,8 @@ $("editToggle").addEventListener("click", () => {
|
||||
if (!editing) { editId = null; $("form").hidden = true; }
|
||||
render();
|
||||
});
|
||||
$("syncBtn").addEventListener("click", runSync);
|
||||
$("settings").addEventListener("click", () => chrome.runtime.openOptionsPage());
|
||||
|
||||
// Enter im Suchfeld öffnet den ersten Treffer
|
||||
$("search").addEventListener("keydown", (e) => {
|
||||
@@ -132,9 +171,12 @@ $("file").addEventListener("change", async (e) => {
|
||||
if (!file) return;
|
||||
try {
|
||||
const arr = JSON.parse(await file.text());
|
||||
links = arr
|
||||
const now = Date.now();
|
||||
const imported = arr
|
||||
.filter((x) => x && x.name && x.url)
|
||||
.map((x) => ({ id: uid(), name: String(x.name), url: normalizeUrl(String(x.url)) || String(x.url), icon: x.icon || "" }));
|
||||
.map((x) => ({ id: uid(), name: String(x.name), url: normalizeUrl(String(x.url)) || String(x.url), icon: x.icon || "", updated: now }));
|
||||
deleted = [...deleted, ...links.map((l) => ({ id: l.id, updated: now }))];
|
||||
links = imported;
|
||||
await save();
|
||||
render();
|
||||
} catch { alert("Ungültige JSON-Datei."); }
|
||||
|
||||
Reference in New Issue
Block a user