222 lines
6.3 KiB
JavaScript
222 lines
6.3 KiB
JavaScript
import { api_url } from '/js/settings.js';
|
|
import * as api from '/js/api.js';
|
|
|
|
function create_notification_element_from_notification_object(notification) {
|
|
|
|
const e_container = document.createElement('div');
|
|
e_container.classList = `status status-${notification.type}`;
|
|
e_container.setAttribute('notification_id', "" + notification.id);
|
|
e_container.setAttribute('notification_hash', "" + notification.hash);
|
|
|
|
const e_title_span = document.createElement('span');
|
|
e_title_span.innerText = notification.title;
|
|
|
|
const e_title = document.createElement('div');
|
|
e_title.classList = 'status-title';
|
|
e_title.appendChild(e_title_span)
|
|
|
|
const e_progress = document.createElement('progress');
|
|
if ('progress' in notification) {
|
|
if(notification.progress !== null) {
|
|
e_progress.setAttribute('value', notification.progress);
|
|
}
|
|
} else e_progress.style.display = 'none';
|
|
|
|
const e_text = document.createElement('div');
|
|
e_text.classList = 'status-text';
|
|
e_text.innerHTML = notification.text;
|
|
|
|
const e_delete_button = document.createElement('button');
|
|
e_delete_button.classList = 'dismiss';
|
|
e_delete_button.innerText = "Dismiss";
|
|
e_delete_button.addEventListener('click', () => {
|
|
api.notifications.remove(notification.id);
|
|
})
|
|
|
|
e_container.appendChild(e_title);
|
|
e_container.appendChild(e_progress);
|
|
e_container.appendChild(e_text);
|
|
// e_container.appendChild(e_delete_button);
|
|
|
|
return e_container;
|
|
}
|
|
|
|
function begin_status_update_loop() {
|
|
const statuses_container = document.getElementById('statuses');
|
|
const clear_button = document.getElementById('clear-notifications-button');
|
|
const notif_header = document.getElementById('notif-header');
|
|
|
|
clear_button.addEventListener('click', () => {
|
|
api.notifications.remove_all();
|
|
})
|
|
|
|
function get_current_shown_status_ids() {
|
|
let arr = [];
|
|
for(const element of statuses_container.children) {
|
|
arr.push(element.getAttribute("notification_id"))
|
|
}
|
|
return arr;
|
|
}
|
|
|
|
async function check_statuses() {
|
|
const active = await api.notifications.get_active();
|
|
const failed = await api.notifications.get_failed();
|
|
const succeeded = await api.notifications.get_succeeded();
|
|
|
|
notif_header.textContent = `${active.length} Active | ${failed.length} Failed | ${succeeded.length} Succeeded`;
|
|
|
|
let req = await fetch(await api_url('notifications'));
|
|
let json = await req.json();
|
|
|
|
const current_status_ids = get_current_shown_status_ids();
|
|
const new_status_ids = json.map(v => v.id);
|
|
|
|
const combined_ids = Array.from(new Set([
|
|
...current_status_ids,
|
|
...new_status_ids
|
|
]));
|
|
|
|
const first = new Map(combined_ids.map(notif_id => [
|
|
notif_id,
|
|
document.querySelector(`[notification_id="${notif_id}"]`)?.getBoundingClientRect() ?? null
|
|
]));
|
|
|
|
console.log(first);
|
|
|
|
for(const status_id of combined_ids) {
|
|
const in_current = current_status_ids.includes(status_id);
|
|
const in_new = new_status_ids.includes(status_id);
|
|
|
|
if (in_current && !in_new) {
|
|
const status_element = statuses_container.querySelector(`[notification_id="${status_id}"]`);
|
|
status_element.outerHTML = "";
|
|
} else if (!in_current && in_new) {
|
|
const status_object = json.find(status => status.id === status_id);
|
|
const new_element = create_notification_element_from_notification_object(status_object)
|
|
statuses_container.appendChild(new_element);
|
|
} else if (in_current && in_new) {
|
|
const status_object = json.find(status => status.id === status_id);
|
|
const current_element = statuses_container.querySelector(`[notification_id="${status_id}"]`);
|
|
const new_element = create_notification_element_from_notification_object(status_object);
|
|
current_element.replaceWith(new_element);
|
|
}
|
|
}
|
|
|
|
new_status_ids.forEach((id, index) => {
|
|
const el = document.querySelector(`[notification_id="${id}"]`);
|
|
if (el) el.style.order = index;
|
|
});
|
|
|
|
|
|
for(const [id, old_rect] of first.entries()) {
|
|
if(!old_rect) continue;
|
|
|
|
const el = document.querySelector(`[notification_id="${id}"]`);
|
|
if(!el) continue;
|
|
|
|
const current_rect = el.getBoundingClientRect();
|
|
const dy = old_rect.top - current_rect.top;
|
|
|
|
el.style.transition = 'none';
|
|
el.style.transform = `translate(0px, ${dy}px)`;
|
|
|
|
requestAnimationFrame(() => {
|
|
el.style.transition = 'transform 300ms ease';
|
|
el.style.transform = '';
|
|
});
|
|
}
|
|
// notification_els.forEach(el => {
|
|
// const prev = first.get(el);
|
|
// const next = el.getBoundingClientRect();
|
|
//
|
|
// const dx = prev.left - next.left;
|
|
// const dy = prev.top - next.top;
|
|
//
|
|
// if (dx === 0 && dy === 0) return;
|
|
//
|
|
// el.style.transition = 'none';
|
|
// el.style.transform = `translate(${dx}px, ${dy}px)`;
|
|
//
|
|
// });
|
|
|
|
setTimeout(check_statuses, 500);
|
|
}
|
|
check_statuses();
|
|
}
|
|
|
|
|
|
export function enable() {
|
|
begin_status_update_loop();
|
|
}
|
|
|
|
|
|
|
|
function alignElements(liveEl, templateEl) {
|
|
// If node types differ, replace entirely
|
|
if (liveEl.nodeType !== templateEl.nodeType) {
|
|
liveEl.replaceWith(templateEl.cloneNode(true));
|
|
return;
|
|
}
|
|
|
|
// If element tag names differ, replace
|
|
if (liveEl.nodeType === Node.ELEMENT_NODE &&
|
|
liveEl.tagName !== templateEl.tagName) {
|
|
liveEl.replaceWith(templateEl.cloneNode(true));
|
|
return;
|
|
}
|
|
|
|
// Text node
|
|
if (liveEl.nodeType === Node.TEXT_NODE) {
|
|
if (liveEl.textContent !== templateEl.textContent) {
|
|
liveEl.textContent = templateEl.textContent;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Element node — sync attributes
|
|
syncAttributes(liveEl, templateEl);
|
|
|
|
// Sync children
|
|
syncChildren(liveEl, templateEl);
|
|
}
|
|
|
|
function syncAttributes(liveEl, templateEl) {
|
|
// Remove old attributes
|
|
for (const attr of [...liveEl.attributes]) {
|
|
if (!templateEl.hasAttribute(attr.name)) {
|
|
liveEl.removeAttribute(attr.name);
|
|
}
|
|
}
|
|
|
|
// Add / update attributes
|
|
for (const attr of [...templateEl.attributes]) {
|
|
if (liveEl.getAttribute(attr.name) !== attr.value) {
|
|
liveEl.setAttribute(attr.name, attr.value);
|
|
}
|
|
}
|
|
}
|
|
|
|
function syncChildren(liveEl, templateEl) {
|
|
const liveChildren = [...liveEl.childNodes];
|
|
const templateChildren = [...templateEl.childNodes];
|
|
|
|
const max = Math.max(liveChildren.length, templateChildren.length);
|
|
|
|
for (let i = 0; i < max; i++) {
|
|
const liveChild = liveChildren[i];
|
|
const templateChild = templateChildren[i];
|
|
|
|
if (!liveChild && templateChild) {
|
|
liveEl.appendChild(templateChild.cloneNode(true));
|
|
continue;
|
|
}
|
|
|
|
if (liveChild && !templateChild) {
|
|
liveChild.remove();
|
|
continue;
|
|
}
|
|
|
|
alignElements(liveChild, templateChild);
|
|
}
|
|
}
|