1
0
Fork 0
mirror of https://github.com/miniflux/v2.git synced 2025-08-11 17:51:01 +00:00

refactor(js): rewrite toast notification implementation

This commit is contained in:
Frédéric Guillot 2025-08-02 20:34:03 -07:00
parent e9d9256ae2
commit b505a63f3b
2 changed files with 72 additions and 34 deletions

View file

@ -199,10 +199,6 @@
<template id="icon-star">{{ icon "star" }}</template>
<template id="icon-unstar">{{ icon "unstar" }}</template>
<template id="icon-save">{{ icon "save" }}</template>
<div id="toast-wrapper" role="alert" aria-live="assertive" aria-atomic="true">
<span id="toast-msg"></span>
</div>
</body>
</html>
{{ end }}

View file

@ -143,6 +143,71 @@ function findEntry(element) {
return document.querySelector(".entry");
}
/**
* Create an icon label element with the given text.
*
* @param {string} labelText - The text to display in the icon label.
* @returns {Element} The created icon label element.
*/
function createIconLabelElement(labelText) {
const labelElement = document.createElement("span");
labelElement.classList.add("icon-label");
labelElement.textContent = labelText;
return labelElement;
}
/**
* Set the icon and label element in the parent element.
*
* @param {Element} parentElement - The parent element to insert the icon and label into.
* @param {string} iconName - The name of the icon to display.
* @param {string} labelText - The text to display in the label.
*/
function setIconAndLabelElement(parentElement, iconName, labelText) {
const iconElement = document.querySelector(`template#icon-${iconName}`);
if (iconElement) {
const iconClone = iconElement.content.cloneNode(true);
parentElement.textContent = ""; // Clear existing content
parentElement.appendChild(iconClone);
}
if (labelText) {
const labelElement = createIconLabelElement(labelText);
parentElement.appendChild(labelElement);
}
}
/**
* Show a toast notification.
*
* @param {string} iconType - The type of icon to display.
* @param {string} notificationMessage - The message to display in the toast.
* @returns {void}
*/
function showToastNotification(iconType, notificationMessage) {
const toastMsgElement = document.createElement("span");
toastMsgElement.id = "toast-msg";
setIconAndLabelElement(toastMsgElement, iconType, notificationMessage);
const toastElementWrapper = document.createElement("div");
toastElementWrapper.id = "toast-wrapper";
toastElementWrapper.setAttribute("role", "alert");
toastElementWrapper.setAttribute("aria-live", "assertive");
toastElementWrapper.setAttribute("aria-atomic", "true");
toastElementWrapper.appendChild(toastMsgElement);
toastElementWrapper.addEventListener("animationend", () => {
toastElementWrapper.remove();
});
document.body.appendChild(toastElementWrapper);
setTimeout(() => {
toastElementWrapper.classList.add("toast-animate");
}, 100);
}
/**
* Insert an icon label element into the parent element.
*
@ -492,12 +557,11 @@ function markPageAsReadAction() {
* @returns {void}
*/
function handleEntryStatus(navigationDirection, element, setToRead) {
const toasting = !element;
const currentEntry = findEntry(element);
if (currentEntry) {
if (!setToRead || currentEntry.querySelector(":is(a, button)[data-toggle-status]").dataset.value === "unread") {
toggleEntryStatus(currentEntry, toasting);
toggleEntryStatus(currentEntry, isEntryView());
}
if (isListView() && currentEntry.classList.contains('current-item')) {
switch (navigationDirection) {
@ -536,13 +600,13 @@ function toggleEntryStatus(element, toasting) {
iconElement = document.querySelector("template#icon-read");
label = link.dataset.labelRead;
if (toasting) {
showToast(link.dataset.toastUnread, iconElement);
showToastNotification("read", link.dataset.toastUnread);
}
} else {
iconElement = document.querySelector("template#icon-unread");
label = link.dataset.labelUnread;
if (toasting) {
showToast(link.dataset.toastRead, iconElement);
showToastNotification("unread", link.dataset.toastUnread);
}
}
@ -608,8 +672,8 @@ function handleSaveEntryAction(element = null) {
sendPOSTRequest(buttonElement.dataset.saveUrl).then(() => {
insertIconLabelElement(buttonElement, buttonElement.dataset.labelDone);
buttonElement.dataset.completed = "true";
if (!element) {
showToast(buttonElement.dataset.toastDone, document.querySelector("template#icon-save"));
if (isEntryView()) {
showToastNotification("save", buttonElement.dataset.toastDone);
}
});
}
@ -640,9 +704,8 @@ function handleBookmarkAction(element) {
insertIconLabelElement(buttonElement, label, false);
buttonElement.dataset.value = newStarStatus;
if (!element) {
const toastKey = isStarred ? "toastUnstar" : "toastStar";
showToast(buttonElement.dataset[toastKey], iconElement);
if (isEntryView()) {
showToastNotification(newStarStatus, buttonElement.dataset[isStarred ? "toastUnstar" : "toastStar"]);
}
});
}
@ -877,27 +940,6 @@ function handleConfirmationMessage(linkElement, callback) {
containerElement.appendChild(questionElement);
}
/**
* Show a toast notification.
*
* @param {string} toastMessage - The label to display in the toast.
* @param {Element} iconElement - The icon element to display in the toast.
* @returns {void}
*/
function showToast(toastMessage, iconElement) {
if (!toastMessage || !iconElement) {
return;
}
const toastMsgElement = document.getElementById("toast-msg");
toastMsgElement.replaceChildren(iconElement.content.cloneNode(true));
insertIconLabelElement(toastMsgElement, toastMessage);
const toastElementWrapper = document.getElementById("toast-wrapper");
toastElementWrapper.classList.remove('toast-animate');
setTimeout(() => toastElementWrapper.classList.add('toast-animate'), 100);
}
/**
* Check if the player is actually playing a media
*