Skip to content

Commit b505a63

Browse files
committed
refactor(js): rewrite toast notification implementation
1 parent e9d9256 commit b505a63

File tree

2 files changed

+72
-34
lines changed

2 files changed

+72
-34
lines changed

internal/template/templates/common/layout.html

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,10 +199,6 @@ <h3 tabindex="-1" id="dialog-title">{{ t "page.keyboard_shortcuts.title" }}</h3>
199199
<template id="icon-star">{{ icon "star" }}</template>
200200
<template id="icon-unstar">{{ icon "unstar" }}</template>
201201
<template id="icon-save">{{ icon "save" }}</template>
202-
203-
<div id="toast-wrapper" role="alert" aria-live="assertive" aria-atomic="true">
204-
<span id="toast-msg"></span>
205-
</div>
206202
</body>
207203
</html>
208204
{{ end }}

internal/ui/static/js/app.js

Lines changed: 72 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,71 @@ function findEntry(element) {
143143
return document.querySelector(".entry");
144144
}
145145

146+
/**
147+
* Create an icon label element with the given text.
148+
*
149+
* @param {string} labelText - The text to display in the icon label.
150+
* @returns {Element} The created icon label element.
151+
*/
152+
function createIconLabelElement(labelText) {
153+
const labelElement = document.createElement("span");
154+
labelElement.classList.add("icon-label");
155+
labelElement.textContent = labelText;
156+
return labelElement;
157+
}
158+
159+
/**
160+
* Set the icon and label element in the parent element.
161+
*
162+
* @param {Element} parentElement - The parent element to insert the icon and label into.
163+
* @param {string} iconName - The name of the icon to display.
164+
* @param {string} labelText - The text to display in the label.
165+
*/
166+
function setIconAndLabelElement(parentElement, iconName, labelText) {
167+
const iconElement = document.querySelector(`template#icon-${iconName}`);
168+
if (iconElement) {
169+
const iconClone = iconElement.content.cloneNode(true);
170+
parentElement.textContent = ""; // Clear existing content
171+
parentElement.appendChild(iconClone);
172+
}
173+
174+
if (labelText) {
175+
const labelElement = createIconLabelElement(labelText);
176+
parentElement.appendChild(labelElement);
177+
}
178+
}
179+
180+
/**
181+
* Show a toast notification.
182+
*
183+
* @param {string} iconType - The type of icon to display.
184+
* @param {string} notificationMessage - The message to display in the toast.
185+
* @returns {void}
186+
*/
187+
function showToastNotification(iconType, notificationMessage) {
188+
const toastMsgElement = document.createElement("span");
189+
toastMsgElement.id = "toast-msg";
190+
191+
setIconAndLabelElement(toastMsgElement, iconType, notificationMessage);
192+
193+
const toastElementWrapper = document.createElement("div");
194+
toastElementWrapper.id = "toast-wrapper";
195+
toastElementWrapper.setAttribute("role", "alert");
196+
toastElementWrapper.setAttribute("aria-live", "assertive");
197+
toastElementWrapper.setAttribute("aria-atomic", "true");
198+
toastElementWrapper.appendChild(toastMsgElement);
199+
toastElementWrapper.addEventListener("animationend", () => {
200+
toastElementWrapper.remove();
201+
});
202+
203+
document.body.appendChild(toastElementWrapper);
204+
205+
setTimeout(() => {
206+
toastElementWrapper.classList.add("toast-animate");
207+
208+
}, 100);
209+
}
210+
146211
/**
147212
* Insert an icon label element into the parent element.
148213
*
@@ -492,12 +557,11 @@ function markPageAsReadAction() {
492557
* @returns {void}
493558
*/
494559
function handleEntryStatus(navigationDirection, element, setToRead) {
495-
const toasting = !element;
496560
const currentEntry = findEntry(element);
497561

498562
if (currentEntry) {
499563
if (!setToRead || currentEntry.querySelector(":is(a, button)[data-toggle-status]").dataset.value === "unread") {
500-
toggleEntryStatus(currentEntry, toasting);
564+
toggleEntryStatus(currentEntry, isEntryView());
501565
}
502566
if (isListView() && currentEntry.classList.contains('current-item')) {
503567
switch (navigationDirection) {
@@ -536,13 +600,13 @@ function toggleEntryStatus(element, toasting) {
536600
iconElement = document.querySelector("template#icon-read");
537601
label = link.dataset.labelRead;
538602
if (toasting) {
539-
showToast(link.dataset.toastUnread, iconElement);
603+
showToastNotification("read", link.dataset.toastUnread);
540604
}
541605
} else {
542606
iconElement = document.querySelector("template#icon-unread");
543607
label = link.dataset.labelUnread;
544608
if (toasting) {
545-
showToast(link.dataset.toastRead, iconElement);
609+
showToastNotification("unread", link.dataset.toastUnread);
546610
}
547611
}
548612

@@ -608,8 +672,8 @@ function handleSaveEntryAction(element = null) {
608672
sendPOSTRequest(buttonElement.dataset.saveUrl).then(() => {
609673
insertIconLabelElement(buttonElement, buttonElement.dataset.labelDone);
610674
buttonElement.dataset.completed = "true";
611-
if (!element) {
612-
showToast(buttonElement.dataset.toastDone, document.querySelector("template#icon-save"));
675+
if (isEntryView()) {
676+
showToastNotification("save", buttonElement.dataset.toastDone);
613677
}
614678
});
615679
}
@@ -640,9 +704,8 @@ function handleBookmarkAction(element) {
640704
insertIconLabelElement(buttonElement, label, false);
641705
buttonElement.dataset.value = newStarStatus;
642706

643-
if (!element) {
644-
const toastKey = isStarred ? "toastUnstar" : "toastStar";
645-
showToast(buttonElement.dataset[toastKey], iconElement);
707+
if (isEntryView()) {
708+
showToastNotification(newStarStatus, buttonElement.dataset[isStarred ? "toastUnstar" : "toastStar"]);
646709
}
647710
});
648711
}
@@ -877,27 +940,6 @@ function handleConfirmationMessage(linkElement, callback) {
877940
containerElement.appendChild(questionElement);
878941
}
879942

880-
/**
881-
* Show a toast notification.
882-
*
883-
* @param {string} toastMessage - The label to display in the toast.
884-
* @param {Element} iconElement - The icon element to display in the toast.
885-
* @returns {void}
886-
*/
887-
function showToast(toastMessage, iconElement) {
888-
if (!toastMessage || !iconElement) {
889-
return;
890-
}
891-
892-
const toastMsgElement = document.getElementById("toast-msg");
893-
toastMsgElement.replaceChildren(iconElement.content.cloneNode(true));
894-
insertIconLabelElement(toastMsgElement, toastMessage);
895-
896-
const toastElementWrapper = document.getElementById("toast-wrapper");
897-
toastElementWrapper.classList.remove('toast-animate');
898-
setTimeout(() => toastElementWrapper.classList.add('toast-animate'), 100);
899-
}
900-
901943
/**
902944
* Check if the player is actually playing a media
903945
*

0 commit comments

Comments
 (0)