SPA privatri format
This commit is contained in:
		| @@ -1,25 +1,754 @@ | ||||
| var apx = apx || {}; | ||||
|  | ||||
| apx.privatri = {}; | ||||
| apx.privatri.loadwco = async (id, ctx) => { | ||||
|   // check if not authenticate, do nothing cause by default screensignin and wait authentification | ||||
|   //       if     authenticate, if url xhash then redirect if no url then change wco-link=screenmyworld | ||||
|   // if (dayjs(apx.data.headers.xdays).diff(dayjs(), "hours") >= 24) apx.apxauth.checkisauth(); | ||||
|   //load main.mustache of the component | ||||
|   //when wco-xxx change it run this function | ||||
|   console.log( | ||||
|     `Load wconame:privatri apx.privatri.loadwco with id:${id} and ctx: ${JSON.stringify( | ||||
|       ctx | ||||
|     )}` | ||||
|   ); | ||||
|   //refresh thread from BE | ||||
|   const threadlist = apx.privatri.getthread(apx.data.headers.xalias,1000000) | ||||
|   const tpldataname = `${apx.data.pagename}_${id}_privatri`; | ||||
|   const privatriid = document.getElementById(id); | ||||
|   privatriid.innerHTML = Mustache.render(apx.data.tpl.privatrimain, threadlist); | ||||
| apx.privatri.templates = {}; | ||||
|  | ||||
| } | ||||
| apx.privatri.loadwco = async (id, dataObj = {}, ctx = null) => { | ||||
|     // check if not authenticate, do nothing cause by default screensignin and wait authentification | ||||
|     //       if     authenticate, if url xhash then redirect if no url then change wco-link=screenmyworld | ||||
|     // if (dayjs(apx.data.headers.xdays).diff(dayjs(), "hours") >= 24) apx.apxauth.checkisauth(); | ||||
|     //load main.mustache of the component | ||||
|     //when wco-xxx change it run this function | ||||
|     console.log( | ||||
|         `Load wconame:privatri apx.privatri.loadwco with id:${id} and ctx: ${JSON.stringify( | ||||
|             ctx | ||||
|         )}` | ||||
|     ); | ||||
|      | ||||
|     const template = JSON.parse(localStorage.getItem("admin")).tpl[`privatri${id}`]; | ||||
|      | ||||
|     return Mustache.render(template, dataObj); | ||||
| }; | ||||
|  | ||||
| apx.privatri.getthread=async(alias,timestamp)=>{ | ||||
|     //store in indexdb the thread | ||||
|     return true | ||||
| } | ||||
| apx.privatri.syncronizeBackend = async (alias, lastConnection) => { | ||||
|     // const response = await fetch | ||||
|     const response = []; | ||||
|  | ||||
|     for (const message of response) { | ||||
|         await apx.indexedDB.set("privatri", "messages", message); | ||||
|     }; | ||||
| }; | ||||
|  | ||||
| apx.privatri.templates.scripts = { | ||||
|     createThread: async () => { | ||||
|         const autoDeletionBtnElArray = document.querySelectorAll("li.autoDeletionBtn"); | ||||
|          | ||||
|         autoDeletionBtnElArray.forEach(btn => { | ||||
|             btn.addEventListener("click", () => { | ||||
|                 autoDeletionBtnElArray.forEach(btn => btn.classList.remove("bg-base-200")); | ||||
|                 btn.classList.add("bg-base-200"); | ||||
|             }); | ||||
|         }); | ||||
|          | ||||
|         document.querySelector("#createThreadBtn").addEventListener("click", async () => { | ||||
|             const { publicKey, privateKey } = await apx.crypto.genKey(); | ||||
|          | ||||
|             const messageObj = await (async (publicKey) => { | ||||
|                 const uuid = crypto.randomUUID(); | ||||
|                 const timestamp = Date.now(); | ||||
|                 const alias = await apx.crypto.encryptMessage(JSON.parse(localStorage.getItem("apx")).data.headers.xalias, publicKey); | ||||
|          | ||||
|                 return { | ||||
|                     privatriid: `${uuid}_${timestamp}`, | ||||
|                     thread: uuid, | ||||
|                     timestamp: timestamp, | ||||
|                     owner: alias, | ||||
|                     title: await apx.crypto.encryptMessage(document.querySelector("#threadNameInput").value, publicKey), | ||||
|                     sender_alias: alias, | ||||
|                     publicKey: publicKey, | ||||
|                     aliases: [ | ||||
|                         alias | ||||
|                     ], | ||||
|                     message: await apx.crypto.encryptMessage(document.querySelector("#threadDescriptionInput").value, publicKey), | ||||
|                     dt_autodestruction: (() => { | ||||
|                         const selectedBtn = Array.from(autoDeletionBtnElArray).find(btn => btn.classList.contains("bg-base-200")); | ||||
|                         return parseInt(selectedBtn ? selectedBtn.getAttribute("data-auto-deletion") : 0, 10); | ||||
|                     })(), | ||||
|                     urgencydeletion: document.querySelector('input[type="checkbox"].toggle').checked | ||||
|                 }; | ||||
|             })(publicKey); | ||||
|          | ||||
|             // Faire un post sur l'endpoint /privatri | ||||
|              | ||||
|             await apx.indexedDB.set("privatri", "threads", { uuid: messageObj.thread, privateKey: privateKey }); | ||||
|             await apx.indexedDB.set("privatri", "messages", messageObj); | ||||
|         }); | ||||
|     }, | ||||
|     inviteAlias: async () => { | ||||
|         const url = "https://www.facebook.com/"; | ||||
|  | ||||
|         const bodyEl = document.querySelector("body"); | ||||
|         const qrCodeCanvasEl = document.querySelector("#qrCodeCanvas"); | ||||
|         const invitationLinkInputEl = document.querySelector("#invitationLinkInput"); | ||||
|  | ||||
|         const displayToastAlert = async (message) => { | ||||
|             return await apx.privatri.loadwco("toastAlert", { message }); | ||||
|         }; | ||||
|          | ||||
|         const qrCode = new QRCodeStyling({ | ||||
|             width: 425, | ||||
|             height: 425, | ||||
|             type: "svg", | ||||
|             data: url, | ||||
|             image: "./assets/icon.png", | ||||
|             dotsOptions: { | ||||
|                 color: "#ffffff", | ||||
|                 type: "rounded" | ||||
|             }, | ||||
|             backgroundOptions: { | ||||
|                 color: getComputedStyle(bodyEl).backgroundColor || "#000000", | ||||
|             }, | ||||
|             imageOptions: { | ||||
|                 crossOrigin: "anonymous", | ||||
|                 margin: 20 | ||||
|             } | ||||
|         }); | ||||
|          | ||||
|         qrCode.append(qrCodeCanvasEl); | ||||
|  | ||||
|         invitationLinkInputEl.value = url; | ||||
|  | ||||
|         copyBtn.addEventListener("click", async () => { | ||||
|             navigator.clipboard.writeText(invitationLinkInputEl.value); | ||||
|  | ||||
|             bodyEl.insertAdjacentHTML("beforeend", await displayToastAlert("Invitation link copied to clipboard.")); | ||||
|  | ||||
|             setTimeout(() => { | ||||
|                 bodyEl.lastElementChild.remove(); | ||||
|             }, 3000); | ||||
|         }); | ||||
|     }, | ||||
|     threadAliasList: async () => { | ||||
|         const bodyEl = document.querySelector("body"); | ||||
|         const aliasListContainerEl = document.querySelector("#aliasListContainer"); | ||||
|  | ||||
|         let aliasesArray = []; | ||||
|  | ||||
|         const newAlias = async (alias) => { | ||||
|             return await apx.privatri.loadwco("alias", { alias }); | ||||
|         }; | ||||
|  | ||||
|         const displayToastAlert = async (message) => { | ||||
|             return await apx.privatri.loadwco("toastAlert", { message }); | ||||
|         }; | ||||
|  | ||||
|         const params = new URLSearchParams(window.location.search); | ||||
|         const uuid = params.get("uuid"); | ||||
|  | ||||
|         const privateKey = (await apx.indexedDB.get("privatri", "threads", uuid)).privateKey; | ||||
|          | ||||
|         const privatriidArray = await getOldestPrivatriids("privatri", "messages"); | ||||
|          | ||||
|         privatriidArray.forEach(async privatriid => { | ||||
|             if (privatriid.split("_")[0] === uuid) { | ||||
|                 aliasesArray = (await apx.indexedDB.get("privatri", "messages", privatriid)).aliases; | ||||
|                 const ownerAlias = (await apx.indexedDB.get("privatri", "messages", privatriid)).owner; | ||||
|                  | ||||
|                 for (const alias of aliasesArray) { | ||||
|                     aliasListContainerEl.insertAdjacentHTML("beforeend", await newAlias({ | ||||
|                         decrypted: (await apx.crypto.decryptMessage(alias, privateKey)).data, | ||||
|                         crypted: alias | ||||
|                     })); | ||||
|                 }; | ||||
|  | ||||
|                 document.querySelectorAll("button.removeAliasBtn").forEach(btn => { | ||||
|                     btn.addEventListener("click", async () => { | ||||
|                         if (JSON.parse(localStorage.getItem("apx")).data.headers.xalias === (await apx.crypto.decryptMessage(ownerAlias, privateKey)).data) { | ||||
|                             if (btn.getAttribute("data-alias") !== ownerAlias) { | ||||
|                                 const alias = btn.getAttribute("data-alias"); | ||||
|                                 aliasesArray = aliasesArray.filter(a => a !== alias); | ||||
|                                 btn.parentElement.parentElement.remove(); | ||||
|                      | ||||
|                                 privatriidArray.forEach(async privatriid => { | ||||
|                                     if (privatriid.split("_")[0] === uuid) { | ||||
|                                         const messageObj = await apx.indexedDB.get("privatri", "messages", privatriid); | ||||
|                                         messageObj.aliases = aliasesArray; | ||||
|                                          | ||||
|                                         await apx.indexedDB.set("privatri", "messages", messageObj); | ||||
|                                     }; | ||||
|                                 }); | ||||
|                             } else { | ||||
|                                 bodyEl.insertAdjacentHTML("beforeend", await displayToastAlert("You cannot remove the owner of the thread.")); | ||||
|  | ||||
|                                 setTimeout(() => { | ||||
|                                     bodyEl.lastElementChild.remove(); | ||||
|                                 }, 3000); | ||||
|                             }; | ||||
|                         } else { | ||||
|                             bodyEl.insertAdjacentHTML("beforeend", await displayToastAlert("You don't have the permissions to remove this alias.")); | ||||
|  | ||||
|                             setTimeout(() => { | ||||
|                                 bodyEl.lastElementChild.remove(); | ||||
|                             }, 3000); | ||||
|                         }; | ||||
|                     }); | ||||
|                 }); | ||||
|             }; | ||||
|         }); | ||||
|     }, | ||||
|     threadSettings: async () => { | ||||
|         const bodyEl = document.querySelector("body"); | ||||
|         const threadNameInputEl = document.querySelector("#threadNameInput"); | ||||
|         const autoDeletionBtnElArray = document.querySelectorAll("li.autoDeletionBtn"); | ||||
|         const applyModificationsBtnEl = document.querySelector("#applyModificationsBtn"); | ||||
|  | ||||
|         const displayToastAlert = async (message) => { | ||||
|             return await apx.privatri.loadwco("toastAlert", { message }); | ||||
|         }; | ||||
|  | ||||
|         let messageObj = {}; | ||||
|  | ||||
|         const params = new URLSearchParams(window.location.search); | ||||
|         const uuid = params.get("uuid"); | ||||
|  | ||||
|         const privateKey = (await apx.indexedDB.get("privatri", "threads", uuid)).privateKey; | ||||
|          | ||||
|         const privatriidArray = await getOldestPrivatriids("privatri", "messages"); | ||||
|         let ownerAlias = ""; | ||||
|          | ||||
|         privatriidArray.forEach(async privatriid => { | ||||
|             if (privatriid.split("_")[0] === uuid) { | ||||
|                 messageObj = await apx.indexedDB.get("privatri", "messages", privatriid); | ||||
|                 ownerAlias = messageObj.owner; | ||||
|                  | ||||
|                 threadNameInputEl.value = (await apx.crypto.decryptMessage(messageObj.title, privateKey)).data; | ||||
|  | ||||
|                 (Array.from(autoDeletionBtnElArray).find(el => el.getAttribute("data-auto-deletion") === String(messageObj.dt_autodestruction))).classList.add("bg-base-200"); | ||||
|              | ||||
|                 if (messageObj.urgencydeletion === true) { | ||||
|                     document.querySelector('input[type="checkbox"].toggle').checked = true; | ||||
|                 }; | ||||
|             }; | ||||
|         }); | ||||
|  | ||||
|         applyModificationsBtnEl.addEventListener("click", async () => { | ||||
|             if (JSON.parse(localStorage.getItem("apx")).data.headers.xalias === (await apx.crypto.decryptMessage(ownerAlias, privateKey)).data) { | ||||
|                 messageObj.title = await apx.crypto.encryptMessage(threadNameInputEl.value, privateKey); | ||||
|                 messageObj.dt_autodestruction = (() => { | ||||
|                     const selectedBtn = Array.from(autoDeletionBtnElArray).find(btn => btn.classList.contains("bg-base-200")); | ||||
|                     return parseInt(selectedBtn ? selectedBtn.getAttribute("data-auto-deletion") : 0, 10); | ||||
|                 })(); | ||||
|                 messageObj.urgencydeletion = document.querySelector('input[type="checkbox"].toggle').checked; | ||||
|  | ||||
|                 await apx.indexedDB.set("privatri", "messages", messageObj); | ||||
|             } else { | ||||
|                 bodyEl.insertAdjacentHTML("beforeend", await displayToastAlert("You don't have the permissions to edit this thread.")); | ||||
|  | ||||
|                 setTimeout(() => { | ||||
|                     bodyEl.lastElementChild.remove(); | ||||
|                 }, 3000); | ||||
|             }; | ||||
|         }); | ||||
|  | ||||
|         autoDeletionBtnElArray.forEach(btn => { | ||||
|             btn.addEventListener("click", () => { | ||||
|                 autoDeletionBtnElArray.forEach(btn => btn.classList.remove("bg-base-200")); | ||||
|                 btn.classList.add("bg-base-200"); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| async function getOldestPrivatriids(dbName, storeName) { | ||||
|     return new Promise((resolve, reject) => { | ||||
|         const request = indexedDB.open(dbName, 1); | ||||
|  | ||||
|         request.onsuccess = (event) => { | ||||
|             const db = event.target.result; | ||||
|  | ||||
|             const transaction = db.transaction(storeName, "readonly"); | ||||
|             const store = transaction.objectStore(storeName); | ||||
|             const cursorRequest = store.openCursor(); | ||||
|             const uuidMap = {}; | ||||
|  | ||||
|             cursorRequest.onsuccess = (event) => { | ||||
|                 const cursor = event.target.result; | ||||
|                 if (cursor) { | ||||
|                     const obj = cursor.value; | ||||
|  | ||||
|                     const [uuid, timestamp] = obj.privatriid.split("_"); | ||||
|  | ||||
|                     if (!uuidMap[uuid] || Number(timestamp) < uuidMap[uuid].timestamp) { | ||||
|                         uuidMap[uuid] = { privatriid: obj.privatriid, timestamp: Number(timestamp) }; | ||||
|                     }; | ||||
|  | ||||
|                     cursor.continue(); | ||||
|                 } else { | ||||
|                     const result = Object.values(uuidMap).map(event => event.privatriid); | ||||
|                      | ||||
|                     resolve(result); | ||||
|                 }; | ||||
|             }; | ||||
|  | ||||
|             cursorRequest.onerror = (event) => reject(event); | ||||
|         }; | ||||
|  | ||||
|         request.onerror = (event) => reject(event); | ||||
|     }); | ||||
| }; | ||||
|  | ||||
| apx.ready(async () => { | ||||
|     await (async () => { | ||||
|         console.log("ok") | ||||
|         document.querySelector("body").innerHTML = await apx.privatri.loadwco("main"); | ||||
|      | ||||
|         const lastConnection = JSON.parse(localStorage.getItem("lastConnection")) || Date.now(); | ||||
|      | ||||
|         await apx.privatri.syncronizeBackend(apx.data.headers.xalias, lastConnection); | ||||
|      | ||||
|         const privatriidArray = await getOldestPrivatriids("privatri", "messages"); | ||||
|      | ||||
|         const thread = async (name, uuid) => { | ||||
|             return await apx.privatri.loadwco("threadAliasList", { uuid, name }); | ||||
|         }; | ||||
|      | ||||
|         for (const privatriid of privatriidArray) { | ||||
|             const obj = await apx.indexedDB.get("privatri", "messages", privatriid) | ||||
|              | ||||
|             const privateKey = (await apx.indexedDB.get("privatri", "threads", obj.thread)).privateKey; | ||||
|             const name = (await apx.crypto.decryptMessage(obj.title, privateKey)).data; | ||||
|      | ||||
|             threadsContainerEl.insertAdjacentHTML("beforeend", await thread(name, obj.thread)); | ||||
|         }; | ||||
|      | ||||
|         Array.from(threadsContainerEl.children).forEach(child => { | ||||
|             child.addEventListener("click", async () => { | ||||
|                 const uuid = child.getAttribute("data-uuid"); | ||||
|      | ||||
|                 const messagesObj = await new Promise((resolve, reject) => { | ||||
|                     const request = indexedDB.open("privatri", 1); | ||||
|      | ||||
|                     request.onsuccess = (event) => { | ||||
|                         const db = event.target.result; | ||||
|      | ||||
|                         const transaction = db.transaction("messages", "readonly"); | ||||
|                         const store = transaction.objectStore("messages"); | ||||
|                         const cursorRequest = store.openCursor(); | ||||
|      | ||||
|                         const result = []; | ||||
|                         cursorRequest.onsuccess = (event) => { | ||||
|                             const cursor = event.target.result; | ||||
|      | ||||
|                             if (cursor) { | ||||
|                                 const obj = cursor.value; | ||||
|                                 const keyUuid = obj.privatriid.split("_")[0]; | ||||
|      | ||||
|                                 if (keyUuid === uuid) { | ||||
|                                     result.push(obj); | ||||
|                                 }; | ||||
|                                 cursor.continue(); | ||||
|                             } else { | ||||
|                                 resolve(result); | ||||
|                             }; | ||||
|                         }; | ||||
|      | ||||
|                         cursorRequest.onerror = (event) => reject(event); | ||||
|                     }; | ||||
|      | ||||
|                     request.onerror = (event) => reject(event); | ||||
|                 }); | ||||
|      | ||||
|                 threadPageEl.setAttribute("data-uuid", uuid); | ||||
|                 threadPageEl.querySelector("#threadName").innerText = (await apx.crypto.decryptMessage(messagesObj[0].title, (await apx.indexedDB.get("privatri", "threads", uuid)).privateKey)).data; | ||||
|      | ||||
|                 messagesContainerEl.innerHTML = ""; | ||||
|      | ||||
|                 let privateKey = ""; | ||||
|      | ||||
|                 for (const message of messagesObj) { | ||||
|                     if (privateKey === "") { | ||||
|                         privateKey = (await apx.indexedDB.get("privatri", "threads", message.thread)).privateKey; | ||||
|                     }; | ||||
|      | ||||
|                     const decryptedMessage = (await apx.crypto.decryptMessage(message.message, privateKey)).data; | ||||
|      | ||||
|                     let decryptedAttachments = []; | ||||
|      | ||||
|                     if (message.attachments !== undefined) { | ||||
|                         decryptedAttachments = await (async () => { | ||||
|                             const attachmentsArray = []; | ||||
|          | ||||
|                             if (message.attachments.length > 0) { | ||||
|                                 for (const attachment of message.attachments) { | ||||
|                                     attachmentsArray.push({ | ||||
|                                         fileType: attachment.fileType, | ||||
|                                         filename: attachment.filename, | ||||
|                                         content: (await apx.crypto.decryptMessage(attachment.content, privateKey)).data | ||||
|                                     }); | ||||
|                                 }; | ||||
|                             }; | ||||
|          | ||||
|                             return attachmentsArray; | ||||
|                         })(); | ||||
|                     }; | ||||
|      | ||||
|                     messagesContainerEl.insertAdjacentHTML("beforeend", await sendNewMessage(message.timestamp, decryptedMessage, decryptedAttachments)); | ||||
|                 }; | ||||
|             }); | ||||
|         }); | ||||
|      | ||||
|         document.querySelectorAll("a").forEach(link => { | ||||
|             link.addEventListener("click", async (event) => { | ||||
|                 event.preventDefault(); | ||||
|      | ||||
|                 window.history.replaceState({}, document.title, window.location.pathname); | ||||
|      | ||||
|                 const templateName = link.getAttribute("data-template"); | ||||
|      | ||||
|                 if (templateName === "threadAliasList" || templateName === "threadSettings") { | ||||
|                     window.history.pushState({}, "", `${window.location.pathname}?uuid=${threadPageEl.getAttribute("data-uuid")}`); | ||||
|                 }; | ||||
|      | ||||
|                 bodyEl.firstElementChild.innerHTML = await apx.privatri.loadwco(templateName); | ||||
|      | ||||
|                 await apx.privatri.templates.scripts[templateName](); | ||||
|             }); | ||||
|         }); | ||||
|     })(); | ||||
|  | ||||
|     const bodyEl = document.querySelector("body"); | ||||
|     const searchInputEl = document.querySelector("#threadSearchBar"); | ||||
|     const threadFilterOptionsElArray = document.querySelector("#threadFilterOptions").querySelectorAll("li"); | ||||
|     const threadsContainerEl = document.querySelector("#threadsContainer"); | ||||
|     const threadPageEl = document.querySelector("#threadPage"); | ||||
|     const messageInputEl = document.getElementById("messageInput"); | ||||
|     const messagesContainerEl = document.querySelector("#messagesContainer"); | ||||
|     const attachmentsInputEl = document.querySelector("#attachmentsInput"); | ||||
|  | ||||
|     function formatDate(timestamp) { | ||||
|         const date = new Date(timestamp); | ||||
|         const day = String(date.getDate()).padStart(2, "0"); | ||||
|         const month = String(date.getMonth() + 1).padStart(2, "0"); | ||||
|         const year = date.getFullYear(); | ||||
|         const hours = String(date.getHours()).padStart(2, "0"); | ||||
|         const minutes = String(date.getMinutes()).padStart(2, "0"); | ||||
|  | ||||
|         return `${day}/${month}/${year} ${hours}:${minutes}`; | ||||
|     }; | ||||
|  | ||||
|     searchInputEl.addEventListener("input", () => { | ||||
|         const value = searchInputEl.value.toLowerCase(); | ||||
|  | ||||
|         Array.from(threadsContainerEl.children).forEach(child => { | ||||
|             if (child.querySelector("li > div").textContent.toLowerCase().includes(value)) { | ||||
|                 child.style.display = "block"; | ||||
|             } else { | ||||
|                 child.style.display = "none"; | ||||
|             }; | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     threadFilterOptionsElArray.forEach(option => { | ||||
|         option.addEventListener("click", async () => { | ||||
|             const filter = option.getAttribute("data-filter"); | ||||
|  | ||||
|             if (filter === "date") { | ||||
|                 Array.from(threadsContainerEl.children).sort((a, b) => { | ||||
|                     return a.querySelector("li > span").getAttribute("data-timestamp") - b.querySelector("li > span").getAttribute("data-timestamp"); | ||||
|                 }).forEach(child => { | ||||
|                     threadsContainerEl.appendChild(child); | ||||
|                 }); | ||||
|             } else if (filter === "name") { | ||||
|                 Array.from(threadsContainerEl.children).sort((a, b) => { | ||||
|                     return a.querySelector("li > div").textContent.localeCompare(b.querySelector("li > div").textContent); | ||||
|                 }).forEach(child => { | ||||
|                     threadsContainerEl.appendChild(child); | ||||
|                 }); | ||||
|             }; | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     const sendNewMessage = async (timestamp, message, attachmentsArray) => { | ||||
|         const messageTemplate = await apx.privatri.loadwco("message", { timestamp, message, date: formatDate(timestamp) }); | ||||
|  | ||||
|         const tempNode = document.createElement("div"); | ||||
|         tempNode.innerHTML = messageTemplate; | ||||
|         const messageNode = tempNode.firstElementChild; | ||||
|  | ||||
|         attachmentsArray.forEach(attachment => { | ||||
|             messageNode.querySelector("div.attachmentsContainer").insertAdjacentHTML("beforeend", `<img onclick="attachEventDisplayAttachment(this)" class="w-20 h-20 object-cover rounded" src="data:${attachment.fileType};base64,${attachment.content}" alt="${attachment.filename}"/>`); | ||||
|         }); | ||||
|          | ||||
|         return messageNode.outerHTML; | ||||
|     }; | ||||
|  | ||||
|     const editMessage = async (timestamp, message) => { | ||||
|         return await apx.privatri.loadwco("editMessage", { message, date: formatDate(timestamp) }); | ||||
|     }; | ||||
|  | ||||
|     const displayToastAlert = async (message) => { | ||||
|         return await apx.privatri.loadwco("toastAlert", { message }); | ||||
|     }; | ||||
|  | ||||
|     sendMessageBtn.addEventListener("click", async () => { | ||||
|         const message = messageInputEl.value.trim(); | ||||
|  | ||||
|         if (message !== "") { | ||||
|             const messageObj = await (async (publicKey) => { | ||||
|                 const timestamp = Date.now(); | ||||
|                 const alias = await apx.crypto.encryptMessage(JSON.parse(localStorage.getItem("apx")).data.headers.xalias, publicKey); | ||||
|          | ||||
|                 return { | ||||
|                     privatriid: `${threadPageEl.getAttribute("data-uuid")}_${timestamp}`, | ||||
|                     timestamp: timestamp, | ||||
|                     sender_alias: alias, | ||||
|                     message: await apx.crypto.encryptMessage(message, publicKey), | ||||
|                     attachments: await (async () => { | ||||
|                         const attachmentsArray = []; | ||||
|                         const localAttachmentsArray = JSON.parse(sessionStorage.getItem(`attachmentsArray_${threadPageEl.getAttribute("data-uuid")}`)) || []; | ||||
|  | ||||
|                         if (localAttachmentsArray.length > 0) { | ||||
|                             for (const attachment of localAttachmentsArray) { | ||||
|                                 attachmentsArray.push( | ||||
|                                     { | ||||
|                                         fileType: attachment.fileType, | ||||
|                                         filename: attachment.filename, | ||||
|                                         content: await apx.crypto.encryptMessage(attachment.content, publicKey) | ||||
|                                     } | ||||
|                                 ); | ||||
|                             }; | ||||
|                         }; | ||||
|  | ||||
|                         return attachmentsArray; | ||||
|                     })() | ||||
|                 }; | ||||
|             })((await apx.indexedDB.get("privatri", "messages", `${threadPageEl.getAttribute("data-uuid")}_${messagesContainerEl.firstElementChild.getAttribute("data-timestamp")}`)).publicKey); | ||||
|              | ||||
|             const newMessageHTML = await sendNewMessage(messageObj.timestamp, message, JSON.parse(sessionStorage.getItem(`attachmentsArray_${threadPageEl.getAttribute("data-uuid")}`)) || []); | ||||
|  | ||||
|             sessionStorage.removeItem(`attachmentsArray_${threadPageEl.getAttribute("data-uuid")}`); | ||||
|              | ||||
|             messagesContainerEl.insertAdjacentHTML("beforeend", newMessageHTML); | ||||
|              | ||||
|             messageInputEl.value = ""; | ||||
|  | ||||
|             // Faire un post sur l'endpoint /privatri | ||||
|              | ||||
|             await apx.indexedDB.set("privatri", "messages", messageObj); | ||||
|         }; | ||||
|     }); | ||||
|  | ||||
|     window.addEventListener("beforeunload", () => { | ||||
|         // if (apx) | ||||
|         localStorage.setItem("lastConnection", JSON.stringify(Date.now())); | ||||
|     }); | ||||
|  | ||||
|     window.attachDeleteMessageEvent = async function attachDeleteMessageEvent(btn) { | ||||
|         const messageEl = btn.parentElement.parentElement.parentElement; | ||||
|  | ||||
|         const privateKey = (await apx.indexedDB.get("privatri", "threads", threadPageEl.getAttribute("data-uuid"))).privateKey; | ||||
|  | ||||
|         const signatureMessage = `${JSON.parse(localStorage.getItem("apx")).data.headers.xalias}_${threadPageEl.getAttribute("data-uuid")}_${messageEl.getAttribute("data-timestamp")}`; | ||||
|         const signature = await apx.crypto.sign(signatureMessage, privateKey); | ||||
|          | ||||
|         let verified = false; | ||||
|  | ||||
|         try { | ||||
|             verified = await fetch("", { | ||||
|                 method: "GET", | ||||
|                 body: JSON.stringify({ | ||||
|                     message: signatureMessage, | ||||
|                     signature: signature, | ||||
|                     uuid: threadPageEl.getAttribute("data-uuid"), | ||||
|                     timestamp: messageEl.getAttribute("data-timestamp"), | ||||
|                 }) | ||||
|             }); | ||||
|         } catch (error) { | ||||
|             console.error("Error while verifying signature:", error); | ||||
|         }; | ||||
|  | ||||
|         const authorAlias = await apx.crypto.decryptMessage((await apx.indexedDB.get("privatri", "messages", `${threadPageEl.getAttribute("data-uuid")}_${messageEl.getAttribute("data-timestamp")}`)).sender_alias, privateKey).data; | ||||
|  | ||||
|         if ((JSON.parse(localStorage.getItem("apx")).data.headers.xalias === authorAlias) && (verified === true)) { | ||||
|             await apx.indexedDB.del("privatri", "messages", `${threadPageEl.getAttribute("data-uuid")}_${messageEl.getAttribute("data-timestamp")}`); | ||||
|          | ||||
|             messageEl.remove(); | ||||
|         } else { | ||||
|             bodyEl.insertAdjacentHTML("beforeend", await displayToastAlert("You don't have the permissions to delete this message.")); | ||||
|  | ||||
|             setTimeout(() => { | ||||
|                 bodyEl.lastElementChild.remove(); | ||||
|             }, 3000); | ||||
|         }; | ||||
|     }; | ||||
|  | ||||
|     window.attachEditMessageEvent = async function attachEditMessageEvent(btn) { | ||||
|         const messageEl = btn.parentElement.parentElement.parentElement; | ||||
|      | ||||
|         const authorAlias = (await apx.crypto.decryptMessage((await apx.indexedDB.get("privatri", "messages", `${threadPageEl.getAttribute("data-uuid")}_${messageEl.getAttribute("data-timestamp")}`)).sender_alias, (await apx.indexedDB.get("privatri", "threads", threadPageEl.getAttribute("data-uuid"))).privateKey)).data; | ||||
|          | ||||
|         if (JSON.parse(localStorage.getItem("apx")).data.headers.xalias === authorAlias) { | ||||
|             const messageValue = messageEl.querySelector("div.message").innerText; | ||||
|  | ||||
|             const attachmentsArray = (() => { | ||||
|                 const attachmentsArray = []; | ||||
|  | ||||
|                 messageEl.querySelector("div.attachmentsContainer").querySelectorAll("img").forEach(img => { | ||||
|                     attachmentsArray.push( | ||||
|                         { | ||||
|                             fileType: img.getAttribute("src").match(/^data:(.*);base64,/)[1], | ||||
|                             filename: img.getAttribute("alt"), | ||||
|                             content: img.getAttribute("src").split(",")[1] | ||||
|                         } | ||||
|                     ); | ||||
|                 }); | ||||
|  | ||||
|                 return attachmentsArray; | ||||
|             })(); | ||||
|  | ||||
|             messageEl.innerHTML = await editMessage(parseInt(messageEl.getAttribute("data-timestamp"), 10), messageEl.querySelector("div.message").innerText); | ||||
|  | ||||
|             messageEl.querySelector("button.cancelEditBtn").addEventListener("click", async () => { | ||||
|                 const messageObj = await apx.indexedDB.get("privatri", "messages", `${threadPageEl.getAttribute("data-uuid")}_${messageEl.getAttribute("data-timestamp")}`); | ||||
|  | ||||
|                 messageEl.innerHTML = await sendNewMessage(messageObj.timestamp, messageValue, attachmentsArray); | ||||
|             }); | ||||
|  | ||||
|             messageEl.querySelector("button.saveEditBtn").addEventListener("click", async () => { | ||||
|                 const newMessageValue = messageEl.querySelector("textarea").value.trim(); | ||||
|  | ||||
|                 if (newMessageValue !== "") { | ||||
|                     const privateKey = (await apx.indexedDB.get("privatri", "threads", threadPageEl.getAttribute("data-uuid"))).privateKey; | ||||
|  | ||||
|                     const messageObj = await apx.indexedDB.get("privatri", "messages", `${threadPageEl.getAttribute("data-uuid")}_${messageEl.getAttribute("data-timestamp")}`); | ||||
|  | ||||
|                     messageObj.message = await apx.crypto.encryptMessage(newMessageValue, privateKey), | ||||
|  | ||||
|                     await apx.indexedDB.set("privatri", "messages", messageObj); | ||||
|  | ||||
|                     messageEl.innerHTML = await sendNewMessage(messageObj.timestamp, newMessageValue, attachmentsArray); | ||||
|                 }; | ||||
|             }); | ||||
|         } else { | ||||
|             bodyEl.insertAdjacentHTML("beforeend", await displayToastAlert("You don't have the permissions to edit this message.")); | ||||
|  | ||||
|             setTimeout(() => { | ||||
|                 bodyEl.lastElementChild.remove(); | ||||
|             }, 3000); | ||||
|         }; | ||||
|     }; | ||||
|  | ||||
|     window.attachEventDisplayAttachment = function(img) { | ||||
|         let overlay = document.getElementById("imageOverlay"); | ||||
|  | ||||
|         if (overlay === null) { | ||||
|             overlay = document.createElement('div'); | ||||
|  | ||||
|             overlay.id = "imageOverlay"; | ||||
|             overlay.style.position = "fixed"; | ||||
|             overlay.style.top = 0; | ||||
|             overlay.style.left = 0; | ||||
|             overlay.style.width = "100vw"; | ||||
|             overlay.style.height = "100vh"; | ||||
|             overlay.style.background = "rgba(0, 0, 0, 0.75)"; | ||||
|             overlay.style.display = "flex"; | ||||
|             overlay.style.alignItems = "center"; | ||||
|             overlay.style.justifyContent = "center"; | ||||
|             overlay.style.zIndex = 999; | ||||
|  | ||||
|             overlay.onclick = () => { | ||||
|                 overlay.remove(); | ||||
|             }; | ||||
|  | ||||
|             document.body.appendChild(overlay); | ||||
|         } else { | ||||
|             overlay.innerHTML = ""; | ||||
|             overlay.style.display = "flex"; | ||||
|         }; | ||||
|  | ||||
|         const fullScreenImage = document.createElement("img"); | ||||
|  | ||||
|         fullScreenImage.src = img.src; | ||||
|         fullScreenImage.alt = img.alt; | ||||
|         fullScreenImage.style.maxWidth = "90vw"; | ||||
|         fullScreenImage.style.maxHeight = "90vh"; | ||||
|         overlay.appendChild(fullScreenImage); | ||||
|     }; | ||||
|  | ||||
|     document.querySelector("#attachmentsBtn").addEventListener("click", () => { | ||||
|         attachmentsInputEl.click(); | ||||
|     }); | ||||
|  | ||||
|     attachmentsInputEl.addEventListener("change", async () => { | ||||
|         const filesArray = Array.from(attachmentsInputEl.files); | ||||
|         const maxFileSize = 5 * 1024 * 1024; | ||||
|         const maxSize = 512; | ||||
|  | ||||
|         for (const file of filesArray) { | ||||
|             if (file.size <= maxFileSize) { | ||||
|                 const attachmentObj = await new Promise((resolve, reject) => { | ||||
|                     const attachmentObj = { | ||||
|                         fileType: file.type, | ||||
|                         filename: file.name, | ||||
|                         content: "" | ||||
|                     }; | ||||
|  | ||||
|                     const img = new Image(); | ||||
|                      | ||||
|                     img.onload = () => { | ||||
|                         if (img.width > maxSize || img.height > maxSize) { | ||||
|                             let width = img.width; | ||||
|                             let height = img.height; | ||||
|  | ||||
|                             if (width > maxSize) { | ||||
|                                 height *= maxSize / width; | ||||
|                                 width = maxSize; | ||||
|                             }; | ||||
|  | ||||
|                             if (height > maxSize) { | ||||
|                                 width *= maxSize / height; | ||||
|                                 height = maxSize; | ||||
|                             }; | ||||
|  | ||||
|                             const canvas = document.createElement("canvas"); | ||||
|                             canvas.width = width; | ||||
|                             canvas.height = height; | ||||
|  | ||||
|                             const ctx = canvas.getContext("2d"); | ||||
|  | ||||
|                             ctx.drawImage(img, 0, 0, width, height); | ||||
|  | ||||
|                             canvas.toBlob(blob => { | ||||
|                                 const reader = new FileReader(); | ||||
|  | ||||
|                                 reader.onload = (event) => { | ||||
|                                     attachmentObj.content = event.target.result.split(",")[1]; | ||||
|  | ||||
|                                     resolve(attachmentObj); | ||||
|                                 }; | ||||
|  | ||||
|                                 reader.onerror = reject; | ||||
|                                 reader.readAsDataURL(blob); | ||||
|                             }, "image/jpeg", 0.8); | ||||
|                         } else { | ||||
|                             const reader = new FileReader(); | ||||
|  | ||||
|                             reader.onload = (event) => { | ||||
|                                 attachmentObj.content = event.target.result.split(",")[1]; | ||||
|  | ||||
|                                 resolve(attachmentObj); | ||||
|                             }; | ||||
|  | ||||
|                             reader.onerror = reject; | ||||
|                             reader.readAsDataURL(file); | ||||
|                         }; | ||||
|                     }; | ||||
|  | ||||
|                     img.onerror = reject; | ||||
|                     img.src = URL.createObjectURL(file); | ||||
|  | ||||
|                     return attachmentObj; | ||||
|                 }); | ||||
|  | ||||
|                 if (sessionStorage.getItem(`attachmentsArray_${threadPageEl.getAttribute("data-uuid")}`) === null) { | ||||
|                     sessionStorage.setItem(`attachmentsArray_${threadPageEl.getAttribute("data-uuid")}`, JSON.stringify([attachmentObj])); | ||||
|                 } else { | ||||
|                     const attachmentsArray = JSON.parse(sessionStorage.getItem(`attachmentsArray_${threadPageEl.getAttribute("data-uuid")}`)); | ||||
|                     attachmentsArray.push(attachmentObj); | ||||
|  | ||||
|                     sessionStorage.setItem(`attachmentsArray_${threadPageEl.getAttribute("data-uuid")}`, JSON.stringify(attachmentsArray)); | ||||
|                 }; | ||||
|             }; | ||||
|         }; | ||||
|     }); | ||||
| }); | ||||
		Reference in New Issue
	
	Block a user