summaryrefslogtreecommitdiff
path: root/app/public/main.js
diff options
context:
space:
mode:
authorAlexis Hovorka <[email protected]>2024-02-16 23:48:02 -0700
committerAlexis Hovorka <[email protected]>2024-02-16 23:48:02 -0700
commitc5aa364e0e372d0e29063a27bd17811446db8b6a (patch)
tree5c5b10471f7356b66b74277071e61e5e0447edc5 /app/public/main.js
parent773e7f747fa096031be427b22257137cf894d587 (diff)
[feat] Flesh out basic UI
Diffstat (limited to 'app/public/main.js')
-rw-r--r--app/public/main.js119
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?
+
});