diff --git a/cardie/static/main/editor.css b/cardie/static/main/editor.css index 4ed4315..77c6a6b 100644 --- a/cardie/static/main/editor.css +++ b/cardie/static/main/editor.css @@ -88,6 +88,7 @@ html { border: var(--default-glass-border); backdrop-filter: var(--default-glass-blur); border-radius: 20px; + transition: var(--default-transition); width: fit-content; } diff --git a/cardie/static/main/scripts/editor/editor.js b/cardie/static/main/scripts/editor/editor.js index bfccb71..70cb1e7 100644 --- a/cardie/static/main/scripts/editor/editor.js +++ b/cardie/static/main/scripts/editor/editor.js @@ -1,12 +1,14 @@ var old_card_data = JSON.stringify(editor_create_json()); var new_card_data; var qrcode; +var prevent_save = false; var layout = "left"; var font_style = "Simple"; try { - var demo_param = JSON.parse(new URL(window.location.href).searchParams.get("demo").toLowerCase()); - + var demo_param = JSON.parse( + new URL(window.location.href).searchParams.get("demo").toLowerCase() + ); } catch { var demo_param = false; } @@ -20,13 +22,15 @@ async function start_editor() { if (uuid_param == null) { let new_uuid = crypto.randomUUID(); + const response = await fetch(server_ip + "/createcard", { method: "POST", headers: { - "UUID": new_uuid, + UUID: new_uuid } }); + response.text().then(function (text) { if (text == "Done") { var refresh = window.location.protocol + "//" + window.location.host + window.location.pathname + '?uuid=' + new_uuid; @@ -37,16 +41,19 @@ async function start_editor() { } else { status_error(); - log("WARNING", "There was a problem") - create_notification("There was a problem", "There was an unknown issue", "warning"); + log("WARNING", "There was a problem"); + create_notification( + "There was a problem", + "There was an unknown issue", + "warning" + ); } }); - } else { const response = await fetch(server_ip + "/checkcard", { method: "POST", headers: { - "UUID": uuid_param, + UUID: uuid_param } }); @@ -62,11 +69,10 @@ async function start_editor() { window.location.href = server_ip; } else if (text == "No Permission") { - log("WARNING", "No Permission") + log("WARNING", "No Permission"); window.location.href = server_ip; - } else { - log("INFO", "This card exists on the server!") + log("INFO", "This card exists on the server!"); card_render_from_json(".card_card", text); editor_load_from_json(text); status_saved(); @@ -81,37 +87,63 @@ async function start_editor() { } async function save_card(card_json) { + if (prevent_save) return; status_saving(); let uuid_param = new URL(window.location.href).searchParams.get("uuid"); const response = await fetch(server_ip + "/savecard", { method: "POST", headers: { - "UUID": uuid_param, - "Data": card_json + UUID: uuid_param, + Data: card_json } }); response.text().then(function (text) { if (text == "Done") { - log("INFO", "Data has been saved") + log("INFO", "Data has been saved"); status_saved(); + prevent_save = false; return true; - } else { - log("WARNING", "There was a problem saving the card") + log("WARNING", "There was a problem saving the card"); status_error(); - create_notification("There was a problem saving your card", "There was an unknown issue", "warning"); + create_notification( + "There was a problem saving your card", + "There was an unknown issue", + "warning" + ); return false; } }); } +function checkForUnsavedChanges(newCardData, oldCardData) { + if (JSON.stringify(newCardData) !== JSON.stringify(oldCardData)) { + return true; + } + return false; +} + +window.addEventListener("beforeunload", function (event) { + // Get the current state of the card data + new_card_data = JSON.stringify(editor_create_json()); + + // Check if there are unsaved changes + if (checkForUnsavedChanges(new_card_data, old_card_data)) { + // If there are unsaved changes, prompt the user + event.preventDefault(); + event.returnValue = + "There are unsaved changes. Are you sure you want to leave?"; + prevent_save = true; + } +}); + function save_loop() { + prevent_save = false; window.dispatchEvent(new CustomEvent('getItemData')); new_card_data = JSON.stringify(editor_create_json()); - - if (new_card_data != old_card_data) { + if (checkForUnsavedChanges(new_card_data, old_card_data)) { old_card_data = new_card_data; save_card(new_card_data); card_render_from_json(".card_card", new_card_data); @@ -128,23 +160,37 @@ function demo_loop() { } } +function unsaved_loop() { + new_card_data = JSON.stringify(editor_create_json()); + + if (checkForUnsavedChanges(new_card_data, old_card_data)) { + status_unsaved(); + } +} + async function editor_demo_auth(sign_in) { const response = await fetch(server_ip + "/createtempcard", { method: "POST", headers: { - "data": JSON.stringify(editor_create_json()) + data: JSON.stringify(editor_create_json()) } }); response.text().then(function (text) { if (text == "Missing headers") { log("WARNING", text); - create_notification("There was an error creating the temporary card", "Missing headers in the request", "warning"); - + create_notification( + "There was an error creating the temporary card", + "Missing headers in the request", + "warning" + ); } else if (text == "Request is not a POST request") { log("WARNING", text); - create_notification("There was an error creating the temporary card", "Request is not a POST request", "warning"); - + create_notification( + "There was an error creating the temporary card", + "Request is not a POST request", + "warning" + ); } else if (response.status == 200) { log("INFO", `The new temporary card has been created with uuid ${text}!`); @@ -153,7 +199,6 @@ async function editor_demo_auth(sign_in) { } else { window.location.href = `${server_ip}/authentication?sign_in=false&temp_uuid=${text}&`; } - } else { log("WARNING", text); } @@ -162,28 +207,28 @@ async function editor_demo_auth(sign_in) { function setup_qrcode() { let uuid_param = new URL(window.location.href).searchParams.get("uuid"); - let url = `${server_ip}/card?uuid=${uuid_param}&` + let url = `${server_ip}/card?uuid=${uuid_param}&`; qrcode = new QRCode("qrcode", { url: url, width: 128, height: 128, - colorDark : "#000000", - colorLight : "#ffffff", - correctLevel : QRCode.CorrectLevel.H + colorDark: "#000000", + colorLight: "#ffffff", + correctLevel: QRCode.CorrectLevel.H }); - qrcode.makeCode(url) + qrcode.makeCode(url); } // Thanks to this answer https://stackoverflow.com/a/12300351 function dataURItoBlob(dataURI) { // convert base64 to raw binary data held in a string // doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this - var byteString = atob(dataURI.split(',')[1]); + var byteString = atob(dataURI.split(",")[1]); // separate out the mime component - var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0] + var mimeString = dataURI.split(",")[0].split(":")[1].split(";")[0]; // write the bytes of the string to an ArrayBuffer var ab = new ArrayBuffer(byteString.length); @@ -199,60 +244,69 @@ function dataURItoBlob(dataURI) { // write the ArrayBuffer to a blob, and you're done var blob = new Blob([ab], { type: mimeString }); return blob; - } document.querySelector("#editor_header_title_home").addEventListener("click", (event) => { window.location.replace("/home"); }); -document.querySelector("#editor_share_copylink").addEventListener("click", async (event) => { - let uuid_param = new URL(window.location.href).searchParams.get("uuid"); +document + .querySelector("#editor_share_copylink") + .addEventListener("click", async (event) => { + let uuid_param = new URL(window.location.href).searchParams.get("uuid"); - await navigator.clipboard.writeText(`${server_ip}/card?uuid=${uuid_param}&`).then(() => { - event.target.innerHTML = ` Copied!`; + await navigator.clipboard + .writeText(`${server_ip}/card?uuid=${uuid_param}&`) + .then(() => { + event.target.innerHTML = ` Copied!`; - setInterval( () => { - event.target.innerHTML = ` Copy Link`; - }, 3000); + setInterval(() => { + event.target.innerHTML = ` Copy Link`; + }, 3000); + }); }); -}); -document.querySelector("#editor_share_copyqr").addEventListener("click", async (event) => { - let uuid_param = new URL(window.location.href).searchParams.get("uuid"); - let url = `${server_ip}/card?uuid=${uuid_param}&` +document + .querySelector("#editor_share_copyqr") + .addEventListener("click", async (event) => { + let uuid_param = new URL(window.location.href).searchParams.get("uuid"); + let url = `${server_ip}/card?uuid=${uuid_param}&`; qrcode.makeCode(url); const data = [new ClipboardItem({ ["image/png"]: dataURItoBlob(document.querySelector("#qrcode > img").getAttribute("src")) })]; - await navigator.clipboard.write(data).then(() => { - event.target.innerHTML = ` Copied!`; + await navigator.clipboard.write(data).then(() => { + event.target.innerHTML = ` Copied!`; - setInterval( () => { - event.target.innerHTML = ` Copy QR Code`; - }, 3000); + setInterval(() => { + event.target.innerHTML = ` Copy QR Code`; + }, 3000); + }); }); -}); -document.querySelector("#editor_share_downloadqr").addEventListener("click", (event) => { - let uuid_param = new URL(window.location.href).searchParams.get("uuid"); - let url = `${server_ip}/card?uuid=${uuid_param}&` +document + .querySelector("#editor_share_downloadqr") + .addEventListener("click", (event) => { + let uuid_param = new URL(window.location.href).searchParams.get("uuid"); + let url = `${server_ip}/card?uuid=${uuid_param}&`; - qrcode.makeCode(url); + qrcode.makeCode(url); - var link = document.createElement("a"); - link.download = `${uuid_param}.png`; - link.href = document.querySelector("#qrcode > img").getAttribute("src"); - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - delete link; -}); + var link = document.createElement("a"); + link.download = `${uuid_param}.png`; + link.href = document.querySelector("#qrcode > img").getAttribute("src"); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + delete link; + }); -document.querySelector("#editor_demo_signin").addEventListener("click", (event) => { - editor_demo_auth(true); -}); +document + .querySelector("#editor_demo_signin") + .addEventListener("click", (event) => { + editor_demo_auth(true); + }); document.querySelector("#editor_demo_createaccount").addEventListener("click", (event) => { editor_demo_auth(false); @@ -281,6 +335,7 @@ start_editor(); if (demo_param == false) { setInterval(save_loop, 3000); + setInterval(unsaved_loop, 500); } else { setInterval(demo_loop, 1000); } diff --git a/cardie/static/main/scripts/editor/editor_information.js b/cardie/static/main/scripts/editor/editor_information.js index 7e3c38a..ade3b9f 100644 --- a/cardie/static/main/scripts/editor/editor_information.js +++ b/cardie/static/main/scripts/editor/editor_information.js @@ -107,12 +107,18 @@ function editor_load_from_json(json) { function status_saved() { document.querySelector("#editor_status").innerHTML = ' Saved'; + document.querySelector("#editor_status").style.backgroundColor = 'var(--default-glass-background)'; } function status_saving() { document.querySelector("#editor_status").innerHTML = ' Saving...'; } +function status_unsaved() { + document.querySelector("#editor_status").innerHTML = ' Unsaved Changes'; + document.querySelector("#editor_status").style.backgroundColor = 'rgba(255, 132, 52, 0.4)'; +} + function status_error() { document.querySelector("#editor_status").innerHTML = ' Error'; diff --git a/cardie/staticfiles/main/editor.css b/cardie/staticfiles/main/editor.css index 4ed4315..77c6a6b 100644 --- a/cardie/staticfiles/main/editor.css +++ b/cardie/staticfiles/main/editor.css @@ -88,6 +88,7 @@ html { border: var(--default-glass-border); backdrop-filter: var(--default-glass-blur); border-radius: 20px; + transition: var(--default-transition); width: fit-content; } diff --git a/cardie/staticfiles/main/scripts/editor/editor.js b/cardie/staticfiles/main/scripts/editor/editor.js index 7ec1504..70cb1e7 100644 --- a/cardie/staticfiles/main/scripts/editor/editor.js +++ b/cardie/staticfiles/main/scripts/editor/editor.js @@ -1,12 +1,14 @@ var old_card_data = JSON.stringify(editor_create_json()); var new_card_data; var qrcode; +var prevent_save = false; var layout = "left"; var font_style = "Simple"; try { - var demo_param = JSON.parse(new URL(window.location.href).searchParams.get("demo").toLowerCase()); - + var demo_param = JSON.parse( + new URL(window.location.href).searchParams.get("demo").toLowerCase() + ); } catch { var demo_param = false; } @@ -19,54 +21,58 @@ async function start_editor() { if (uuid_param == null) { let new_uuid = crypto.randomUUID(); - + + const response = await fetch(server_ip + "/createcard", { method: "POST", headers: { - "UUID": new_uuid, + UUID: new_uuid } }); - + + response.text().then(function (text) { if (text == "Done") { - var refresh = window.location.protocol + "//" + window.location.host + window.location.pathname + '?uuid=' + new_uuid; + var refresh = window.location.protocol + "//" + window.location.host + window.location.pathname + '?uuid=' + new_uuid; window.history.pushState({ path: refresh }, '', refresh); log("INFO", "A new card has been created on the server with uuid " + new_uuid); create_notification("New card created", "A new card has been successfully created", "check-circle"); status_saved(); - + } else { status_error(); - log("WARNING", "There was a problem") - create_notification("There was a problem", "There was an unknown issue", "warning"); + log("WARNING", "There was a problem"); + create_notification( + "There was a problem", + "There was an unknown issue", + "warning" + ); } }); - } else { const response = await fetch(server_ip + "/checkcard", { method: "POST", headers: { - "UUID": uuid_param, + UUID: uuid_param } }); - + response.text().then(function (text) { if (text == "Request is not a POST request") { status_error(); log("WARNING", "There was a problem"); create_notification("There was a problem checking the card", "There was an unknown issue", "warning"); - + } else if (text == "Card does not exist!") { log("WARNING", "Card does not exist!"); status_error(); window.location.href = server_ip; - + } else if (text == "No Permission") { - log("WARNING", "No Permission") + log("WARNING", "No Permission"); window.location.href = server_ip; - } else { - log("INFO", "This card exists on the server!") + log("INFO", "This card exists on the server!"); card_render_from_json(".card_card", text); editor_load_from_json(text); status_saved(); @@ -81,37 +87,63 @@ async function start_editor() { } async function save_card(card_json) { + if (prevent_save) return; status_saving(); let uuid_param = new URL(window.location.href).searchParams.get("uuid"); const response = await fetch(server_ip + "/savecard", { method: "POST", headers: { - "UUID": uuid_param, - "Data": card_json + UUID: uuid_param, + Data: card_json } }); response.text().then(function (text) { if (text == "Done") { - log("INFO", "Data has been saved") + log("INFO", "Data has been saved"); status_saved(); + prevent_save = false; return true; - } else { - log("WARNING", "There was a problem saving the card") + log("WARNING", "There was a problem saving the card"); status_error(); - create_notification("There was a problem saving your card", "There was an unknown issue", "warning"); + create_notification( + "There was a problem saving your card", + "There was an unknown issue", + "warning" + ); return false; } }); } +function checkForUnsavedChanges(newCardData, oldCardData) { + if (JSON.stringify(newCardData) !== JSON.stringify(oldCardData)) { + return true; + } + return false; +} + +window.addEventListener("beforeunload", function (event) { + // Get the current state of the card data + new_card_data = JSON.stringify(editor_create_json()); + + // Check if there are unsaved changes + if (checkForUnsavedChanges(new_card_data, old_card_data)) { + // If there are unsaved changes, prompt the user + event.preventDefault(); + event.returnValue = + "There are unsaved changes. Are you sure you want to leave?"; + prevent_save = true; + } +}); + function save_loop() { + prevent_save = false; window.dispatchEvent(new CustomEvent('getItemData')); new_card_data = JSON.stringify(editor_create_json()); - - if (new_card_data != old_card_data) { + if (checkForUnsavedChanges(new_card_data, old_card_data)) { old_card_data = new_card_data; save_card(new_card_data); card_render_from_json(".card_card", new_card_data); @@ -128,23 +160,37 @@ function demo_loop() { } } +function unsaved_loop() { + new_card_data = JSON.stringify(editor_create_json()); + + if (checkForUnsavedChanges(new_card_data, old_card_data)) { + status_unsaved(); + } +} + async function editor_demo_auth(sign_in) { const response = await fetch(server_ip + "/createtempcard", { method: "POST", headers: { - "data": JSON.stringify(editor_create_json()) + data: JSON.stringify(editor_create_json()) } }); response.text().then(function (text) { if (text == "Missing headers") { log("WARNING", text); - create_notification("There was an error creating the temporary card", "Missing headers in the request", "warning"); - + create_notification( + "There was an error creating the temporary card", + "Missing headers in the request", + "warning" + ); } else if (text == "Request is not a POST request") { log("WARNING", text); - create_notification("There was an error creating the temporary card", "Request is not a POST request", "warning"); - + create_notification( + "There was an error creating the temporary card", + "Request is not a POST request", + "warning" + ); } else if (response.status == 200) { log("INFO", `The new temporary card has been created with uuid ${text}!`); @@ -153,7 +199,6 @@ async function editor_demo_auth(sign_in) { } else { window.location.href = `${server_ip}/authentication?sign_in=false&temp_uuid=${text}&`; } - } else { log("WARNING", text); } @@ -162,28 +207,28 @@ async function editor_demo_auth(sign_in) { function setup_qrcode() { let uuid_param = new URL(window.location.href).searchParams.get("uuid"); - let url = `${server_ip}/card?uuid=${uuid_param}&` + let url = `${server_ip}/card?uuid=${uuid_param}&`; qrcode = new QRCode("qrcode", { url: url, width: 128, height: 128, - colorDark : "#000000", - colorLight : "#ffffff", - correctLevel : QRCode.CorrectLevel.H + colorDark: "#000000", + colorLight: "#ffffff", + correctLevel: QRCode.CorrectLevel.H }); - qrcode.makeCode(url) + qrcode.makeCode(url); } // Thanks to this answer https://stackoverflow.com/a/12300351 function dataURItoBlob(dataURI) { // convert base64 to raw binary data held in a string // doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this - var byteString = atob(dataURI.split(',')[1]); + var byteString = atob(dataURI.split(",")[1]); // separate out the mime component - var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0] + var mimeString = dataURI.split(",")[0].split(":")[1].split(";")[0]; // write the bytes of the string to an ArrayBuffer var ab = new ArrayBuffer(byteString.length); @@ -199,60 +244,69 @@ function dataURItoBlob(dataURI) { // write the ArrayBuffer to a blob, and you're done var blob = new Blob([ab], { type: mimeString }); return blob; - } document.querySelector("#editor_header_title_home").addEventListener("click", (event) => { - window.location.href = server_ip + "/home"; + window.location.replace("/home"); }); -document.querySelector("#editor_share_copylink").addEventListener("click", async (event) => { - let uuid_param = new URL(window.location.href).searchParams.get("uuid"); +document + .querySelector("#editor_share_copylink") + .addEventListener("click", async (event) => { + let uuid_param = new URL(window.location.href).searchParams.get("uuid"); - await navigator.clipboard.writeText(`${server_ip}/card?uuid=${uuid_param}&`).then(() => { - event.target.innerHTML = ` Copied!`; + await navigator.clipboard + .writeText(`${server_ip}/card?uuid=${uuid_param}&`) + .then(() => { + event.target.innerHTML = ` Copied!`; - setInterval( () => { - event.target.innerHTML = ` Copy Link`; - }, 3000); + setInterval(() => { + event.target.innerHTML = ` Copy Link`; + }, 3000); + }); }); -}); -document.querySelector("#editor_share_copyqr").addEventListener("click", async (event) => { - let uuid_param = new URL(window.location.href).searchParams.get("uuid"); - let url = `${server_ip}/card?uuid=${uuid_param}&` +document + .querySelector("#editor_share_copyqr") + .addEventListener("click", async (event) => { + let uuid_param = new URL(window.location.href).searchParams.get("uuid"); + let url = `${server_ip}/card?uuid=${uuid_param}&`; qrcode.makeCode(url); - + const data = [new ClipboardItem({ ["image/png"]: dataURItoBlob(document.querySelector("#qrcode > img").getAttribute("src")) })]; - await navigator.clipboard.write(data).then(() => { - event.target.innerHTML = ` Copied!`; + await navigator.clipboard.write(data).then(() => { + event.target.innerHTML = ` Copied!`; - setInterval( () => { - event.target.innerHTML = ` Copy QR Code`; - }, 3000); + setInterval(() => { + event.target.innerHTML = ` Copy QR Code`; + }, 3000); + }); }); -}); -document.querySelector("#editor_share_downloadqr").addEventListener("click", (event) => { - let uuid_param = new URL(window.location.href).searchParams.get("uuid"); - let url = `${server_ip}/card?uuid=${uuid_param}&` +document + .querySelector("#editor_share_downloadqr") + .addEventListener("click", (event) => { + let uuid_param = new URL(window.location.href).searchParams.get("uuid"); + let url = `${server_ip}/card?uuid=${uuid_param}&`; - qrcode.makeCode(url); + qrcode.makeCode(url); - var link = document.createElement("a"); - link.download = `${uuid_param}.png`; - link.href = document.querySelector("#qrcode > img").getAttribute("src"); - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - delete link; -}); + var link = document.createElement("a"); + link.download = `${uuid_param}.png`; + link.href = document.querySelector("#qrcode > img").getAttribute("src"); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + delete link; + }); -document.querySelector("#editor_demo_signin").addEventListener("click", (event) => { - editor_demo_auth(true); -}); +document + .querySelector("#editor_demo_signin") + .addEventListener("click", (event) => { + editor_demo_auth(true); + }); document.querySelector("#editor_demo_createaccount").addEventListener("click", (event) => { editor_demo_auth(false); @@ -281,6 +335,7 @@ start_editor(); if (demo_param == false) { setInterval(save_loop, 3000); + setInterval(unsaved_loop, 500); } else { setInterval(demo_loop, 1000); } diff --git a/cardie/staticfiles/main/scripts/editor/editor_information.js b/cardie/staticfiles/main/scripts/editor/editor_information.js index 7e3c38a..ade3b9f 100644 --- a/cardie/staticfiles/main/scripts/editor/editor_information.js +++ b/cardie/staticfiles/main/scripts/editor/editor_information.js @@ -107,12 +107,18 @@ function editor_load_from_json(json) { function status_saved() { document.querySelector("#editor_status").innerHTML = ' Saved'; + document.querySelector("#editor_status").style.backgroundColor = 'var(--default-glass-background)'; } function status_saving() { document.querySelector("#editor_status").innerHTML = ' Saving...'; } +function status_unsaved() { + document.querySelector("#editor_status").innerHTML = ' Unsaved Changes'; + document.querySelector("#editor_status").style.backgroundColor = 'rgba(255, 132, 52, 0.4)'; +} + function status_error() { document.querySelector("#editor_status").innerHTML = ' Error';