From cf51a1b4d9f599fa3f40651dd2b86571637526f6 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Sat, 25 Apr 2026 13:38:04 +0200 Subject: [PATCH] bug-fixes --- .../controllers/comment_reply_controller.js | 33 +++- assets/styles/app.css | 14 +- assets/styles/article.css | 32 ++++ assets/styles/layout.css | 76 +++++++- src/Command/PrewarmCommand.php | 103 ++++++++++- src/Controller/CommentReplyController.php | 7 +- src/Controller/FeaturedAuthorsController.php | 22 +-- src/Service/ArticleCommentThreadLoader.php | 4 +- src/Service/CommentReplyService.php | 26 ++- src/Service/FeaturedAuthorSync.php | 89 ++++++++-- src/Service/MagazineContentService.php | 168 ++++++++++++++++++ src/Service/NostrClient.php | 32 +++- templates/pages/featured_authors.html.twig | 45 ++--- 13 files changed, 563 insertions(+), 88 deletions(-) diff --git a/assets/controllers/comment_reply_controller.js b/assets/controllers/comment_reply_controller.js index c75a580..a6dbde8 100644 --- a/assets/controllers/comment_reply_controller.js +++ b/assets/controllers/comment_reply_controller.js @@ -109,10 +109,23 @@ export default class extends Controller { } const data = await res.json().catch(() => ({})); if (!res.ok) { - this.setHint(data.error || `HTTP ${res.status}`); + const msg = data.error || `HTTP ${res.status}`; + this.setHint(msg); + this.showToast(msg, 'error'); return; } - this.setHint('Published.'); + const okRelaysRaw = Number(data.ok_relays); + const totalRelaysRaw = Number(data.total_relays); + const okRelays = Number.isFinite(okRelaysRaw) ? okRelaysRaw : null; + const totalRelays = Number.isFinite(totalRelaysRaw) ? totalRelaysRaw : null; + const successMsg = + okRelays !== null && totalRelays !== null + ? `Published to ${okRelays}/${totalRelays} relays.` + : 'Published.'; + this.setHint(successMsg); + this.showToast(successMsg, 'success'); + // Keep form content until the success toast is visible. + await new Promise((r) => window.setTimeout(r, 180)); if (ta) { ta.value = ''; } @@ -203,4 +216,20 @@ export default class extends Controller { this.hintTarget.textContent = msg; } } + + showToast(message, tone = 'success') { + const el = document.createElement('div'); + el.className = `reply-toast reply-toast--${tone === 'error' ? 'error' : 'success'}`; + el.setAttribute('role', 'status'); + el.setAttribute('aria-live', 'polite'); + el.textContent = message; + document.body.appendChild(el); + window.setTimeout(() => { + el.classList.add('reply-toast--visible'); + }, 10); + window.setTimeout(() => { + el.classList.remove('reply-toast--visible'); + window.setTimeout(() => el.remove(), 220); + }, 2600); + } } diff --git a/assets/styles/app.css b/assets/styles/app.css index aab3be9..268b65c 100644 --- a/assets/styles/app.css +++ b/assets/styles/app.css @@ -957,16 +957,17 @@ a:focus-visible { .pager { margin-top: 1.25rem; - display: flex; - justify-content: center; width: 100%; + box-sizing: border-box; } .pager__inner { - width: min(100%, 36rem); + width: 100%; + max-width: 100%; + box-sizing: border-box; display: flex; align-items: center; - justify-content: center; + justify-content: space-between; gap: 0.75rem; padding: 0.5rem 0.75rem; border: 1px solid var(--color-border); @@ -974,7 +975,8 @@ a:focus-visible { } .pager__status { - min-width: 8rem; + flex: 1 1 auto; + min-width: 0; text-align: center; } @@ -990,14 +992,12 @@ a:focus-visible { @media (max-width: 640px) { .pager__inner { - width: 100%; gap: 0.5rem; } .pager__btn { min-width: 5.25rem; } .pager__status { - min-width: 7rem; font-size: 0.92rem; } } diff --git a/assets/styles/article.css b/assets/styles/article.css index 6a65b48..c6b15a2 100644 --- a/assets/styles/article.css +++ b/assets/styles/article.css @@ -338,3 +338,35 @@ blockquote p { font-size: 0.9rem; margin: 0.5rem 0 0; } + +.reply-toast { + position: fixed; + left: 50%; + bottom: 1.2rem; + transform: translateX(-50%) translateY(8px); + z-index: 1200; + min-width: 16rem; + max-width: min(92vw, 32rem); + padding: 0.55rem 0.85rem; + border: 1px solid var(--color-border); + color: var(--color-text); + background: var(--color-bg); + opacity: 0; + transition: opacity 0.2s ease, transform 0.2s ease; + box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18); +} + +.reply-toast--visible { + opacity: 1; + transform: translateX(-50%) translateY(0); +} + +.reply-toast--success { + border-color: #2f7a4b; + background: #e7f5eb; +} + +.reply-toast--error { + border-color: #a12b2b; + background: #fdecec; +} diff --git a/assets/styles/layout.css b/assets/styles/layout.css index 454bbbf..0c57460 100644 --- a/assets/styles/layout.css +++ b/assets/styles/layout.css @@ -22,7 +22,8 @@ flex-grow: 1; } -nav { +/* Only the app chrome sidebar — not