[Notify] Add live in-app notifications over SSE (Iran-friendly)
CI/CD / CI · dotnet build (push) Has been cancelled
CI/CD / Deploy · hamkadr (push) Has been cancelled

Web Push is delivered by the browser vendor's push service (Chrome to Google FCM), which is filtered in Iran, so background push is unreliable. Add a Server-Sent Events channel over our own origin that always reaches users while the tab/PWA is open: NotificationHub (in-memory pub/sub), a /notifications/stream SSE endpoint (auth-gated, keep-alive pings, nginx no-buffer), and NotificationService now publishes each saved notification to the hub. Client updates the bell badge instantly, shows a toast, and fires a local OS notification via the service worker when permission is granted (no push server). Web Push stays as best-effort for closed-app reach. Verified end-to-end: login, open stream, broadcast, event delivered.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-04 15:42:16 +03:30
parent d2a7b18cb3
commit 716433ce20
7 changed files with 343 additions and 5 deletions
+17
View File
@@ -88,6 +88,23 @@ a { color: inherit; text-decoration: none; }
.nav-toggle:checked ~ .nav-burger span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
.bell-mobile { position: relative; font-size: 20px; margin-inline-start: auto; line-height: 1; }
/* ---------- Live notification toasts (SSE) ---------- */
.toast-host {
position: fixed; inset-block-end: 16px; inset-inline-start: 16px; z-index: 200;
display: flex; flex-direction: column; gap: 10px; max-width: min(360px, calc(100vw - 32px));
}
.toast {
display: flex; align-items: flex-start; gap: 10px; padding: 12px 14px;
background: var(--surface); border: 1px solid var(--line); border-inline-start: 4px solid var(--primary);
border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,.16); color: var(--text);
opacity: 0; transform: translateY(12px); transition: opacity .25s, transform .25s;
}
.toast.show { opacity: 1; transform: translateY(0); }
.toast-ico { font-size: 18px; line-height: 1.4; }
.toast-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.toast-body strong { font-size: 14px; }
.toast-body span { font-size: 13px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* ---------- Buttons ---------- */
.btn {
display: inline-flex; align-items: center; gap: 6px;