mirror of
https://github.com/FreshRSS/FreshRSS.git
synced 2026-05-20 09:30:36 +00:00
Implement loading spinner for marking as favorite/read, read/unread (#7564)
* Implement loading spinner for marking as favorite * Ensure that the correct previous icon gets set * Remove delay * Improve compatibility with various parsers Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr> * Support multiple icons (top, bottom) * Remove preload for now * Fix CSS, remove !important * Implement read/unread and alt * Ensure correct bookmark icon gets set after error --------- Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
This commit is contained in:
@@ -78,6 +78,7 @@ echo htmlspecialchars(json_encode([
|
||||
'icons' => [
|
||||
'read' => rawurlencode(_i('read')),
|
||||
'unread' => rawurlencode(_i('unread')),
|
||||
'spinner' => '../themes/icons/spinner.svg',
|
||||
],
|
||||
'extensions' => $extData,
|
||||
], JSON_UNESCAPED_UNICODE) ?: '', ENT_NOQUOTES, 'UTF-8');
|
||||
|
||||
@@ -224,6 +224,10 @@ function send_mark_read_queue(queue, asRead, callback) {
|
||||
req.responseType = 'json';
|
||||
req.onerror = function (e) {
|
||||
for (let i = queue.length - 1; i >= 0; i--) {
|
||||
const div = document.getElementById('flux_' + queue[i]);
|
||||
div.querySelectorAll('a.read > .icon').forEach(icon => {
|
||||
icon.outerHTML = div.classList.contains('not_read') ? context.icons.unread : context.icons.read;
|
||||
});
|
||||
delete pending_entries['flux_' + queue[i]];
|
||||
}
|
||||
badAjax(this.status == 403);
|
||||
@@ -315,6 +319,12 @@ function mark_read(div, only_not_read, asBatch) {
|
||||
}
|
||||
pending_entries[div.id] = true;
|
||||
|
||||
div.querySelectorAll('a.read > .icon').forEach(icon => {
|
||||
icon.src = context.icons.spinner;
|
||||
icon.alt = '⏳';
|
||||
icon.classList.add('spinner');
|
||||
});
|
||||
|
||||
const asRead = div.classList.contains('not_read');
|
||||
const entryId = div.id.replace(/^flux_/, '');
|
||||
if (asRead && asBatch) {
|
||||
@@ -351,10 +361,26 @@ function mark_favorite(div) {
|
||||
}
|
||||
pending_entries[div.id] = true;
|
||||
|
||||
let originalIcon;
|
||||
|
||||
div.querySelectorAll('a.bookmark > .icon').forEach(icon => {
|
||||
originalIcon = {
|
||||
src: icon.getAttribute('src'),
|
||||
alt: icon.getAttribute('alt')
|
||||
};
|
||||
icon.src = context.icons.spinner;
|
||||
icon.alt = '⏳';
|
||||
icon.classList.add('spinner');
|
||||
});
|
||||
|
||||
const req = new XMLHttpRequest();
|
||||
req.open('POST', url, true);
|
||||
req.responseType = 'json';
|
||||
req.onerror = function (e) {
|
||||
div.querySelectorAll('a.bookmark > .icon').forEach(icon => {
|
||||
icon.src = originalIcon.src;
|
||||
icon.alt = originalIcon.alt;
|
||||
});
|
||||
delete pending_entries[div.id];
|
||||
badAjax(this.status == 403);
|
||||
};
|
||||
|
||||
@@ -274,6 +274,18 @@ th {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
filter: invert(1);
|
||||
}
|
||||
|
||||
a:hover > .spinner {
|
||||
filter: invert(1) brightness(2);
|
||||
}
|
||||
|
||||
.flux .item.manage .read:hover .icon.spinner {
|
||||
filter: invert(1) grayscale(0.8) brightness(1.7);
|
||||
}
|
||||
|
||||
/*=== switches */
|
||||
.switch.active {
|
||||
background-color: var(--contrast-background-color);
|
||||
|
||||
@@ -274,6 +274,18 @@ th {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
filter: invert(1);
|
||||
}
|
||||
|
||||
a:hover > .spinner {
|
||||
filter: invert(1) brightness(2);
|
||||
}
|
||||
|
||||
.flux .item.manage .read:hover .icon.spinner {
|
||||
filter: invert(1) grayscale(0.8) brightness(1.7);
|
||||
}
|
||||
|
||||
/*=== switches */
|
||||
.switch.active {
|
||||
background-color: var(--contrast-background-color);
|
||||
|
||||
@@ -118,6 +118,18 @@ p.help .icon,
|
||||
filter: brightness(.6) contrast(1.2);
|
||||
}
|
||||
|
||||
.spinner {
|
||||
filter: invert(1) brightness(.6) contrast(1.2);
|
||||
}
|
||||
|
||||
.bookmark:hover > .spinner {
|
||||
filter: invert(1) brightness(1.1);
|
||||
}
|
||||
|
||||
a:hover .icon.spinner {
|
||||
filter: invert(1) brightness(1.5);
|
||||
}
|
||||
|
||||
/*=== Forms */
|
||||
legend {
|
||||
border-bottom: 1px solid var(--dark-border-color);
|
||||
|
||||
@@ -118,6 +118,18 @@ p.help .icon,
|
||||
filter: brightness(.6) contrast(1.2);
|
||||
}
|
||||
|
||||
.spinner {
|
||||
filter: invert(1) brightness(.6) contrast(1.2);
|
||||
}
|
||||
|
||||
.bookmark:hover > .spinner {
|
||||
filter: invert(1) brightness(1.1);
|
||||
}
|
||||
|
||||
a:hover .icon.spinner {
|
||||
filter: invert(1) brightness(1.5);
|
||||
}
|
||||
|
||||
/*=== Forms */
|
||||
legend {
|
||||
border-bottom: 1px solid var(--dark-border-color);
|
||||
|
||||
@@ -1323,4 +1323,8 @@ a:hover .icon {
|
||||
:root.darkMode_auto .btn:active .icon {
|
||||
filter: brightness(1.4);
|
||||
}
|
||||
|
||||
:root.darkMode_auto .spinner {
|
||||
filter: invert(1) brightness(2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1323,4 +1323,8 @@ a:hover .icon {
|
||||
:root.darkMode_auto .btn:active .icon {
|
||||
filter: brightness(1.4);
|
||||
}
|
||||
|
||||
:root.darkMode_auto .spinner {
|
||||
filter: invert(1) brightness(2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><style>@keyframes spinner_svv2{to{transform:rotate(360deg)}}</style><path d="M10.14 1.16a11 11 0 0 0-9 8.92A1.59 1.59 0 0 0 2.46 12a1.52 1.52 0 0 0 1.65-1.3 8 8 0 0 1 6.66-6.61A1.42 1.42 0 0 0 12 2.69a1.57 1.57 0 0 0-1.86-1.53Z" style="transform-origin:center;animation:spinner_svv2 .75s infinite linear"/></svg>
|
||||
|
After Width: | Height: | Size: 375 B |
Reference in New Issue
Block a user