diff options
author | Alexis Hovorka <[email protected]> | 2024-02-16 23:48:02 -0700 |
---|---|---|
committer | Alexis Hovorka <[email protected]> | 2024-02-16 23:48:02 -0700 |
commit | c5aa364e0e372d0e29063a27bd17811446db8b6a (patch) | |
tree | 5c5b10471f7356b66b74277071e61e5e0447edc5 /app/public/main.js | |
parent | 773e7f747fa096031be427b22257137cf894d587 (diff) |
[feat] Flesh out basic UI
Diffstat (limited to 'app/public/main.js')
-rw-r--r-- | app/public/main.js | 119 |
1 files changed, 94 insertions, 25 deletions
diff --git a/app/public/main.js b/app/public/main.js index 5dc9531..5746142 100644 --- a/app/public/main.js +++ b/app/public/main.js @@ -28,13 +28,25 @@ function debounce(fn, delay) { //} function drawNote(note) { - const el = m(`<div> - <h3>${note.id}</h3> - <textarea>${note.content}</textarea> - </div>`); + const el = m(`<div class="card-shadow"><div class="card"> + <!--h3>${note.id}</h3--> + <textarea>${note.content.trim()}</textarea> + </div></div>`); - $("textarea", el).addEventListener("input", - debounce(() => { saveNote(note.id, $("textarea", el).value); }, 500)); + const ta = $("textarea", el) + + ta.addEventListener("input", + debounce(() => { saveNote(note.id, ta.value); }, 500)); + + function resizeTextarea() { // TODO simplify + el.style.height = (el.scrollHeight) + "px"; + ta.style.height = ""; ta.style.height = (ta.scrollHeight) + "px"; + el.style.height = ""; + } + ta.addEventListener("input", resizeTextarea); + ta.addEventListener("focus", resizeTextarea); + window.addEventListener("resize", resizeTextarea); + setTimeout(resizeTextarea); $("#notes").appendChild(el); } @@ -51,9 +63,7 @@ async function getList() { const list = await res.json(); console.log(list); - for (let i=0;i<list.length;i++) { - await getNote(list[i]); - } + return Promise.all(list.map(id => getNote(id))); } async function saveNote(id, content) { @@ -77,13 +87,12 @@ async function newNote() { drawNote(note); } -$("#new-note").addEventListener("click", newNote); +$("#add-btn").addEventListener("click", newNote); async function getUserData() { const res = await fetch(`/user`); const user = await res.json(); console.log(user); - if (user.uid) $("#uid").value = user.uid; } async function getUserSessions() { @@ -92,26 +101,75 @@ async function getUserSessions() { console.log(sessions); } -$("#login").addEventListener("click", async () => { +function showSignIn() { + const hash = location.hash.slice(2).replace(/^sign-in(\/to\/)?/,""); + history.replaceState("sign-in","","#/sign-in"+(hash?"/to/"+hash:"")); + $("#sign-in-dialog").showModal(); +} + +$("#sign-in-dialog").addEventListener("cancel", e => e.preventDefault()); +$("#sign-in-username").addEventListener("input", e => e.target.classList.remove("error")); +$("#sign-in-password").addEventListener("input", e => e.target.classList.remove("error")); +$("#sign-in-basic-go").addEventListener("click", async e => { + e.preventDefault(); + $("#sign-in-error").textContent = ""; + + const inputs = [ + $("#sign-in-username"), + $("#sign-in-password"), + $("#sign-in-keep-session"), + $("#sign-in-basic-go"), + ]; + const validatedInputs = inputs.slice(0,2); + + if (validatedInputs.map(e => { + if (!e.checkValidity()) { + e.classList.add("error"); + return true; } + }).reduce((a,e) => a||e, false)) return; + + inputs.forEach(e => e.disabled = true); + $("#sign-in-form").classList.add("loading"); + const res = await fetch("/login", { method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify({ - username: $("#username").value, - password: $("#password").value, - keepSession: $("#keep-session").checked, + username: $("#sign-in-username").value, + password: $("#sign-in-password").value, + keepSession: $("#sign-in-keep-session").checked, }) }); - // TODO check return code + inputs.forEach(e => e.disabled = false); + $("#sign-in-form").classList.remove("loading"); + if (!res.ok) { + $("#sign-in-error").textContent = res.status+" "+res.statusText; + return; + } const json = await res.json(); console.log(json); - // TODO "Must Change Password" flow + if (!json.success) { + $("#sign-in-error").textContent = json.msg; + return; + } + + if (json.mustChangePassword) { + alert("Must change password (TODO)"); + // TODO + return; + } + + const hash = location.hash.slice(9); + history.replaceState("","","#/"+hash); + // TODO load location getUserData(); getList(); + + $("#sign-in-dialog").close(); }); $("#change-password").addEventListener("click", async () => { @@ -119,8 +177,8 @@ $("#change-password").addEventListener("click", async () => { method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify({ - username: $("#username").value, - password: $("#password").value, + username: $("#username").value, // TODO + password: $("#password").value, // TODO newPassword: $("#new-password").value, keepSession: $("#keep-session").checked, }) @@ -138,7 +196,7 @@ $("#change-username").addEventListener("click", async () => { headers: {"Content-Type": "application/json"}, body: JSON.stringify({ newUsername: $("#new-username").value, - password: $("#password").value, + password: $("#password").value, // TODO }) }); @@ -148,7 +206,7 @@ $("#change-username").addEventListener("click", async () => { getUserData(); }); -$("#logout").addEventListener("click", async () => { +$("#log-out").addEventListener("click", async () => { const res = await fetch("/logout", { method: "POST", }); @@ -156,19 +214,28 @@ $("#logout").addEventListener("click", async () => { //const json = await res.json(); //console.log(json); console.log("Logged out"); - $("#uid").value = ""; + $("#sign-in-dialog").showModal(); }); -$("#logout-everywhere").addEventListener("click", async () => { +$("#log-out-everywhere").addEventListener("click", async () => { const res = await fetch("/deauth-all", { method: "POST", }); console.log("Logged out everywhere"); - $("#uid").value = ""; + showSignIn(); }); -try { getUserData(); getList(); getUserSessions(); } catch(e) {} +try { await getUserData(); await getList(); await getUserSessions(); } catch(e) { showSignIn(); } + +(() => { + let prevScroll = 0; + window.addEventListener("scroll", () => { + document.querySelector("#toolbar").classList[ + (window.scrollY && window.scrollY > prevScroll)?"add":"remove"]("hide"); + prevScroll = window.scrollY; + }); +})(); // TODO make sure to save unsynced delta even if token has expired, then sync when logged in // TODO Cookie consent on signup; "only necessary for maintaining user session and cached data" @@ -190,4 +257,6 @@ try { getUserData(); getList(); getUserSessions(); } catch(e) {} // https://github.com/cure53/DOMPurify/blob/main/src/attrs.js // https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html +// TODO note kebab menu could be a <dialog> element? + }); |