From: Amazingca Date: Thu, 2 Jul 2026 07:00:32 +0000 (-0600) Subject: Add sync server X-Git-Url: http://git.amazingca.dev/?a=commitdiff_plain;h=9803843f6062ef16f4ce52e2c6d16b897411821d;p=autumn%2Ftodo Add sync server --- diff --git a/app/settings.html b/app/settings.html index 49c3ad1..267a1ed 100644 --- a/app/settings.html +++ b/app/settings.html @@ -36,6 +36,23 @@ +

sync server

+
+
+

server url

+ +
+
+

passphrase

+ +
+ +
+
+

override css

diff --git a/app/settings.js b/app/settings.js index e487fae..8f3b8f1 100644 --- a/app/settings.js +++ b/app/settings.js @@ -8,6 +8,12 @@ var theme = { css: "", showCompleted: false }; +var server = { + address: "", + passphrase: "" +}; + +const wsOptions = document.getElementById("ws-options"); if (localStorage.getItem("todos")) { @@ -24,9 +30,19 @@ if (localStorage.getItem("theme")) { theme = JSON.parse(localStorage.getItem("theme")); } +const serverAddressElement = document.getElementById("serverAddress"); + +if (localStorage.getItem("server")) { + + server = JSON.parse(localStorage.getItem("server")); + + serverAddressElement.value = server.address; + wsOptions.classList.remove("hidden"); +} + const exportDataText = document.getElementById("exportData_text"); -document.getElementById("exportData").addEventListener("click", async (event) => { +const encodeApplicationData = () => { const dataExport = { todos: todos, @@ -34,7 +50,12 @@ document.getElementById("exportData").addEventListener("click", async (event) => theme: theme }; - const exportURL = window.location.href.replace("settings", "").replace(".html", "") + "index.html#" + btoa(encodeURIComponent(JSON.stringify(dataExport))); + return btoa(encodeURIComponent(JSON.stringify(dataExport))); +} + +document.getElementById("exportData").addEventListener("click", async (event) => { + + const exportURL = window.location.href.replace("settings", "").replace(".html", "") + "index.html#" + encodeApplicationData(); if (!window.navigator.userAgent.includes("Firefox") && !/(Linux|Windows|Mac)/g.test(window.navigator.platform)) { @@ -64,9 +85,15 @@ document.getElementById("exportData").addEventListener("click", async (event) => } }); -const backupData = () => { +const backupData = (full = false) => { + + if (full == true) { + localStorage.setItem("todos", JSON.stringify(todos)); + localStorage.setItem("archived", JSON.stringify(archived)); + } localStorage.setItem("theme", JSON.stringify(theme)); + localStorage.setItem("server", JSON.stringify(server)); } const override_css = document.getElementById("override_css"); @@ -172,3 +199,109 @@ const updateTheme = (force = false) => { } updateTheme(); + +const checkConnection = document.getElementById("checkConnection"); + +checkConnection.addEventListener("click", async () => { + + const serverAddress = serverAddressElement.value; + const serverPassphrase = document.getElementById("serverPassphrase").value; + + const reply = await fetch(serverAddress + "/auth", { + headers: { + "Authorization": `Basic ${serverPassphrase}` + } + }); + + if (reply.status == 200) { + + server = { + address: serverAddress, + passphrase: serverPassphrase + }; + + backupData(); + + checkConnection.innerHTML = "Successfully connected!"; + + setTimeout(() => { + checkConnection.innerHTML = "Save"; + }, 2000); + + wsOptions.classList.remove("hidden"); + } else { + + checkConnection.innerHTML = "Server or credentials provided not valid!"; + + setTimeout(() => { + checkConnection.innerHTML = "Save"; + }, 2000); + + wsOptions.classList.add("hidden"); + } +}); + +const pushToServer = document.getElementById("pushToServer"); +const pullFromServer = document.getElementById("pullFromServer"); + +pushToServer.addEventListener("click", async () => { + + const req = await fetch(server.address + "/push", { + method: "POST", + headers: { + "Authorization": `Basic ${server.passphrase}`, + "Data": JSON.stringify({ + todos: todos, + archived: archived, + theme: theme + }) + } + }); + + if (req.status == 200) { + + pushToServer.innerHTML = "Success!"; + + setTimeout(() => { + pushToServer.innerHTML = "Push"; + }, 2000); + } else { + + window.alert(JSON.stringify(req)); + } +}); + +pullFromServer.addEventListener("click", async () => { + + const req = await fetch(server.address + "/pull", { + headers: { + "Authorization": `Basic ${server.passphrase}` + } + }); + + if (req.status == 200) { + + try { + + ({todos, archived, theme} = JSON.parse(await req.text())); // using parenthesis around this is required because we're doing object literal destructuring without a declaration + + backupData(true); + updateHeaders(); + updateTheme(); + } catch (e) { + + console.error(e); + + window.alert("The data pulled from the server was not valid!"); + } + + pullFromServer.innerHTML = "Success!"; + + setTimeout(() => { + pullFromServer.innerHTML = "Pull"; + }, 2000); + } else { + + window.alert(JSON.stringify(req)); + } +}) diff --git a/app/style.css b/app/style.css index 51c0768..7ed7934 100644 --- a/app/style.css +++ b/app/style.css @@ -266,6 +266,53 @@ div:has(> #createTodo) { } } +.form { + display: flex; + flex-direction: column; + gap: 0.5rem; + & input[type="text"], & input[type="password"] { + border: unset; + border-radius: var(--border-radius-inner); + background-color: var(--background-primary); + font-family: "Maple Mono"; + padding: 0.25rem; + &:focus-visible { + outline: unset; + } + } + & > div { + background-color: var(--background-secondary); + border-radius: var(--border-radius-inner); + padding: 0.5rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + p { + margin: 0; + } + } + & > button { + border-radius: var(--border-radius-inner); + } + & > :first-child { + border-top-left-radius: var(--border-radius-outer); + border-top-right-radius: var(--border-radius-outer); + } + & > :last-child { + border-bottom-left-radius: var(--border-radius-outer); + border-bottom-right-radius: var(--border-radius-outer); + } +} + +#ws-options { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + & > button { + flex: 1; + } +} + #override_css { border-radius: 0.5rem; resize: vertical;