aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Inverle <inverle@proton.me> 2025-05-10 21:06:58 +0200
committerGravatar GitHub <noreply@github.com> 2025-05-10 21:06:58 +0200
commit84d4aeb9e613a70b53d87c62d07bb4c4635290c3 (patch)
tree31c5144a7b40df5cefceb732abd7fe318dddc6d1
parent5f74634c8324affda1ceec0b81fc50021775c6e3 (diff)
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>
-rw-r--r--app/views/helpers/javascript_vars.phtml1
-rw-r--r--p/scripts/main.js26
-rw-r--r--p/themes/Alternative-Dark/adark.css12
-rw-r--r--p/themes/Alternative-Dark/adark.rtl.css12
-rw-r--r--p/themes/Dark/dark.css12
-rw-r--r--p/themes/Dark/dark.rtl.css12
-rw-r--r--p/themes/Origine/origine.css4
-rw-r--r--p/themes/Origine/origine.rtl.css4
-rw-r--r--p/themes/icons/spinner.svg1
9 files changed, 84 insertions, 0 deletions
diff --git a/app/views/helpers/javascript_vars.phtml b/app/views/helpers/javascript_vars.phtml
index cadaf9331..159d7ba9b 100644
--- a/app/views/helpers/javascript_vars.phtml
+++ b/app/views/helpers/javascript_vars.phtml
@@ -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');
diff --git a/p/scripts/main.js b/p/scripts/main.js
index 73ac27ddd..c0e31ef98 100644
--- a/p/scripts/main.js
+++ b/p/scripts/main.js
@@ -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);
};
diff --git a/p/themes/Alternative-Dark/adark.css b/p/themes/Alternative-Dark/adark.css
index fa0b45dec..7b8af5f41 100644
--- a/p/themes/Alternative-Dark/adark.css
+++ b/p/themes/Alternative-Dark/adark.css
@@ -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);
diff --git a/p/themes/Alternative-Dark/adark.rtl.css b/p/themes/Alternative-Dark/adark.rtl.css
index 5cabd1f24..147d05fcf 100644
--- a/p/themes/Alternative-Dark/adark.rtl.css
+++ b/p/themes/Alternative-Dark/adark.rtl.css
@@ -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);
diff --git a/p/themes/Dark/dark.css b/p/themes/Dark/dark.css
index 412ff9cd4..b2ddf8f2b 100644
--- a/p/themes/Dark/dark.css
+++ b/p/themes/Dark/dark.css
@@ -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);
diff --git a/p/themes/Dark/dark.rtl.css b/p/themes/Dark/dark.rtl.css
index aead74393..4656ca241 100644
--- a/p/themes/Dark/dark.rtl.css
+++ b/p/themes/Dark/dark.rtl.css
@@ -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);
diff --git a/p/themes/Origine/origine.css b/p/themes/Origine/origine.css
index 8f405ea19..5c658345c 100644
--- a/p/themes/Origine/origine.css
+++ b/p/themes/Origine/origine.css
@@ -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);
+ }
}
diff --git a/p/themes/Origine/origine.rtl.css b/p/themes/Origine/origine.rtl.css
index 4f5f3bc45..417a9003c 100644
--- a/p/themes/Origine/origine.rtl.css
+++ b/p/themes/Origine/origine.rtl.css
@@ -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);
+ }
}
diff --git a/p/themes/icons/spinner.svg b/p/themes/icons/spinner.svg
new file mode 100644
index 000000000..772d0c39f
--- /dev/null
+++ b/p/themes/icons/spinner.svg
@@ -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> \ No newline at end of file