$(document).ready(async () => { console.log("Loading stored notifications..."); await loadStoredToasts(); console.log("Showing stored notifications..."); await showStoredToasts(); }); /** * Gets all toasts stored in local storage * * @returns {Object} Object of toasts in storage */ function getStoredToasts() { if (localStorage.getItem("toasts")) { const storedToasts = JSON.parse(localStorage.getItem("toasts")); console.log("LOADED STORED TOASTS: ", storedToasts); navbarUpdateNotificationBellCount(storedToasts); return storedToasts; } else return false } /** * Adds a toast to storage, will not allow duplicates * * @param {Date} time The date object from when the toast was created * @param {*} message The message of the toast */ function addToastToStorage(time, message) { var toasts = [{ 'time': time, 'message': message }] var storedToasts = getStoredToasts(); console.log("Adding new notification to storage: ", toasts); if (storedToasts) { toasts = toasts.concat(storedToasts); console.log("Combined new and stored notifications: ", toasts); toasts = toasts.filter((value, index, self) => index === self.findIndex((t) => ( t.time === value.time && t.message === value.message )) ) } console.log("Deduped stored notifications: ", toasts); localStorage.setItem("toasts", JSON.stringify(toasts)); navbarUpdateNotificationBellCount(toasts); } /** * Removes a toast from the local storage * * @param {Date} time The date object from when the toast was created * @param {*} message The message of the toast */ function removeToastFromStorage(time, message) { const toastToRemove = { 'time': time, 'message': message } console.log("Toast to remove: ", toastToRemove); var toasts = getStoredToasts(); console.log("Stored toasts: ", toasts); if (toasts.indexOf(toastToRemove)) toasts.splice(toasts.indexOf(toastToRemove) - 1, 1) console.log("Toasts with selected toast removed: ", toasts); localStorage.setItem("toasts", JSON.stringify(toasts)); navbarUpdateNotificationBellCount(toasts); } /** * Shows all stored toasts */ function showStoredToasts() { const storedToasts = getStoredToasts(); if (!storedToasts) return console.log("Loaded stored notifications to show: ", storedToasts); for (const toast of storedToasts) { const toastId = `${toast.time}-toast`; console.log("Showing stored toast: ", toast, toastId); const toastElement = bootstrap.Toast.getOrCreateInstance(document.getElementById(toastId)); toastElement.show(); } } /** * Loads all toasts stored in the local storage into the DOM of the webpage */ function loadStoredToasts() { const storedToasts = getStoredToasts(); if (!storedToasts) return console.log("Loaded stored notifications: ", storedToasts); for (const toast of storedToasts) { createToast(toast.message, { time: toast.time }) } } /** * Will update the count of notifications on the bell icon in the navbar * * @param {Array} storedToasts An array of stored toasts to be counted and updated in the navbar */ function navbarUpdateNotificationBellCount(storedToasts) { const notificationBellIcon = document.getElementById("navbar-notification-bell"); var notificationBellCount = document.getElementById("notification-bell-icon-count"); if (!notificationBellCount) { notificationBellCount = document.createElement('span'); notificationBellCount.id = "notification-bell-icon-count"; notificationBellCount.classList.add('badge'); notificationBellCount.classList.add('text-bg-secondary'); notificationBellCount.appendChild(document.createTextNode(storedToasts.length)); } else notificationBellCount.innerHTML = storedToasts.length; notificationBellIcon.appendChild(notificationBellCount); } /** * Remove a frequency input from the DOM * * @param {string} system The system name to add the frequency to * @param {string} inputId [OPTIONAL] The ID of input, this can be anything unique to this input. If this is not provided the number of frequencies will be used as the ID */ function addFrequencyInput(system, inputId = null) { if (!inputId) inputId = $(`[id^="${system}_systemFreqRow_"]`).length; // Create new input var icon = document.createElement('i'); icon.classList.add('bi'); icon.classList.add('bi-x-circle'); icon.classList.add('text-black'); var remove = document.createElement('a'); remove.classList.add('align-middle'); remove.classList.add('float-left'); remove.href = '#' remove.onclick = () => { removeFrequencyInput(`${system}_systemFreqRow_${inputId}`) } remove.appendChild(icon); var childColRemoveIcon = document.createElement('div'); childColRemoveIcon.classList.add('col-2'); childColRemoveIcon.appendChild(remove); var input = document.createElement('input'); input.classList.add('form-control'); input.id = `${system}_systemFreq_${inputId}`; input.type = 'text'; var childColInput = document.createElement('div'); childColInput.classList.add('col-10'); childColInput.appendChild(input); var childRow = document.createElement('div'); childRow.classList.add("row"); childRow.classList.add("px-1"); childRow.appendChild(childColInput); childRow.appendChild(childColRemoveIcon); var colParent = document.createElement('div'); colParent.classList.add("col-md-6"); colParent.classList.add("mb-1"); colParent.id = `${system}_systemFreqRow_${inputId}` colParent.appendChild(childRow); document.getElementById(`frequencyRow_${system.replaceAll(" ", "_")}`).appendChild(colParent); } /** * Add a toast element to the DOM * * @param {*} notificationMessage The message of the notification * @param {Date} param1.time The date object for when the toast was created, blank if creating new * @param {boolean} param1.showNow Show the toast now or just store it * @returns */ function createToast(notificationMessage, { time = undefined, showNow = false } = {}) { if (!time) time = new Date(Date.now()); else time = new Date(Date.parse(time)); const toastTitle = document.createElement('strong'); toastTitle.classList.add('me-auto'); toastTitle.appendChild(document.createTextNode("Server Notification")); const toastTime = document.createElement('small'); toastTime.appendChild(document.createTextNode(time.toLocaleString())); const toastClose = document.createElement('button'); toastClose.type = 'button'; toastClose.classList.add('btn-close'); toastClose.ariaLabel = 'Close'; toastClose.setAttribute('data-bs-dismiss', 'toast'); toastClose.onclick = () => { removeToastFromStorage(time.toISOString(), notificationMessage); }; const toastHeader = document.createElement('div'); toastHeader.classList.add('toast-header'); toastHeader.appendChild(toastTitle); toastHeader.appendChild(toastTime); toastHeader.appendChild(toastClose); const toastMessage = document.createElement('p'); toastMessage.classList.add("px-2"); toastMessage.appendChild(document.createTextNode(notificationMessage)); const toastBody = document.createElement('div'); toastBody.classList.add('toast-body'); toastBody.appendChild(toastMessage); const wrapperDiv = document.createElement('div'); wrapperDiv.classList.add('toast'); //wrapperDiv.classList.add('position-fixed'); wrapperDiv.id = `${time.toISOString()}-toast`; wrapperDiv.role = 'alert'; wrapperDiv.ariaLive = 'assertive'; wrapperDiv.ariaAtomic = true; wrapperDiv.setAttribute('data-bs-delay', "7500"); wrapperDiv.setAttribute('data-bs-animation', true); wrapperDiv.appendChild(toastHeader); wrapperDiv.appendChild(toastMessage); document.getElementById("toastZone").appendChild(wrapperDiv); addToastToStorage(time.toISOString(), notificationMessage); if (showNow) { const toastElement = bootstrap.Toast.getOrCreateInstance(document.getElementById(`${time.toISOString()}-toast`)); toastElement.show(); } return; } function sendNodeHeartbeat(nodeId) { const Http = new XMLHttpRequest(); const url = '/nodes/nodeCheckIn/' + nodeId; Http.open("GET", url); Http.send(); Http.onloadend = (e) => { console.log(Http.responseText) createToast(Http.responseText, { showNow: true }); } } function joinServer() { const preset = document.getElementById("selectRadioPreset").value; const nodeId = document.getElementById("nodeId").value; const clientId = document.getElementById("inputDiscordClientId").value; const channelId = document.getElementById("inputDiscordChannelId").value; const reqBody = { 'preset': preset, 'nodeId': nodeId, 'clientId': clientId, 'channelId': channelId }; console.log(reqBody); const Http = new XMLHttpRequest(); const url = '/admin/join'; Http.open("POST", url); Http.setRequestHeader("Content-Type", "application/json"); Http.send(JSON.stringify(reqBody)); Http.onloadend = (e) => { const responseObject = JSON.parse(Http.responseText) console.log(Http.status); console.log(responseObject); createToast(`${responseObject.name} will join shortly`, { showNow: true }); location.reload(); } } function leaveServer() { const nodeId = document.getElementById("nodeId").value; const reqBody = { 'nodeId': nodeId }; const Http = new XMLHttpRequest(); const url = '/admin/leave'; Http.open("POST", url); Http.setRequestHeader("Content-Type", "application/json"); Http.send(JSON.stringify(reqBody)); Http.onloadend = (e) => { const responseObject = JSON.parse(Http.responseText) console.log(Http.status); console.log(responseObject); createToast(`${responseObject} is leaving`, { showNow: true }); } } function saveNodeDetails() { const nodeId = document.getElementById("nodeId").value; const nodeName = document.getElementById("inputNodeName").value; const nodeIp = document.getElementById("inputNodeIp").value; const nodePort = document.getElementById("inputOrgName").value; const nodeLocation = document.getElementById("inputNodeLocation").value; const reqBody = { 'id': nodeId, 'name': nodeName, 'ip': nodeIp, 'port': nodePort, 'location': nodeLocation } console.log("Request Body: ", reqBody); const Http = new XMLHttpRequest(); const url = '/nodes/' + nodeId; Http.open("PUT", url); Http.setRequestHeader("Content-Type", "application/json"); Http.send(JSON.stringify(reqBody)); Http.onloadend = (e) => { const responseObject = JSON.parse(Http.responseText); console.log(Http.status); console.log(responseObject); createToast(`Node Updated!`); location.reload(); } } function addNewSystem() { const nodeId = document.getElementById("nodeId").value; const systemName = document.getElementById(`New System_systemName`).value; const systemMode = document.getElementById(`New System_systemMode`).value; const inputSystemFreqs = $(`[id^="New System_systemFreq_"]`); let systemFreqs = []; for (const inputFreq of inputSystemFreqs) { systemFreqs.push(inputFreq.value); } const reqBody = { 'systemName': systemName, 'mode': systemMode, 'frequencies': systemFreqs } console.log("Request Body: ", reqBody); const Http = new XMLHttpRequest(); const url = '/nodes/' + nodeId + "/systems"; Http.open("POST", url); Http.setRequestHeader("Content-Type", "application/json"); Http.send(JSON.stringify(reqBody)); Http.onloadend = (e) => { const responseObject = Http.responseText console.log(Http.status); console.log(responseObject); createToast(`${systemName} Added!`); location.reload(); } } function updateSystem(systemName) { const nodeId = document.getElementById("nodeId").value; const systemMode = document.getElementById(`${systemName}_systemMode`).value; const inputSystemFreqs = $(`[id^="${systemName}_systemFreq_"]`); let systemFreqs = []; for (const inputFreq of inputSystemFreqs) { systemFreqs.push(inputFreq.value); } const reqBody = { 'systemName': systemName, 'mode': systemMode, 'frequencies': systemFreqs } console.log("Request Body: ", reqBody); const Http = new XMLHttpRequest(); const url = '/nodes/' + nodeId + "/systems"; Http.open("PUT", url); Http.setRequestHeader("Content-Type", "application/json"); Http.send(JSON.stringify(reqBody)); Http.onloadend = (e) => { const responseObject = Http.responseText; console.log(Http.status); console.log(responseObject); createToast(`${systemName} Updated!`); location.reload(); } } function removeSystem(systemName) { const nodeId = document.getElementById("nodeId").value; const reqBody = { 'systemName': systemName, } console.log("Request Body: ", reqBody); const Http = new XMLHttpRequest(); const url = '/nodes/' + nodeId + "/systems"; Http.open("DELETE", url); Http.setRequestHeader("Content-Type", "application/json"); Http.send(JSON.stringify(reqBody)); Http.onloadend = (e) => { const responseObject = Http.responseText; console.log(Http.status); console.log(responseObject); createToast(`${systemName} Removed!`); location.reload(); } } function requestNodeUpdate() { } function removeFrequencyInput(elementId) { const element = document.getElementById(elementId); element.remove(); }