Add in-app + real-time notifications (SignalR/mock, Iran-friendly)
- AppNotification + OnlineService.onNotification (hub event + mock periodic) — no FCM/APNs (blocked in Iran); uses the existing realtime channel - notification-store + pushNotification(); 🔔 bell with unread badge in TopBar, notifications screen, global toaster (plays notify sfx) - Wired events: daily reward, post-match achievements, friend requests - Closed-app push (Pushe/Najva/Chabok) noted as a later step (needs provider keys) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
Unsubscribe,
|
||||
} from "./service";
|
||||
import {
|
||||
AppNotification,
|
||||
AuthSession,
|
||||
ChatMessage,
|
||||
Conversation,
|
||||
@@ -48,6 +49,8 @@ export class SignalrService implements OnlineService {
|
||||
private mmCbs = new Set<(s: MatchmakingState) => void>();
|
||||
private stateCbs = new Set<(s: ServerGameState) => void>();
|
||||
private reactionCbs = new Set<(seat: number, reaction: string) => void>();
|
||||
private notifCbs = new Set<(n: AppNotification) => void>();
|
||||
private mockNotifUnsub?: () => void;
|
||||
|
||||
constructor() {
|
||||
if (typeof window !== "undefined") {
|
||||
@@ -89,6 +92,8 @@ export class SignalrService implements OnlineService {
|
||||
conn.on("state", (s: ServerGameState) => this.stateCbs.forEach((cb) => cb(s)));
|
||||
conn.on("reaction", (r: { seat: number; reaction: string }) =>
|
||||
this.reactionCbs.forEach((cb) => cb(r.seat, r.reaction)));
|
||||
conn.on("notification", (n: AppNotification) =>
|
||||
this.notifCbs.forEach((cb) => cb(n)));
|
||||
|
||||
this.conn = conn;
|
||||
try {
|
||||
@@ -262,6 +267,14 @@ export class SignalrService implements OnlineService {
|
||||
markRead(id: string) { return this.mock.markRead(id); }
|
||||
onChat(cb: (id: string, m: ChatMessage[]) => void) { return this.mock.onChat(cb); }
|
||||
|
||||
onNotification(cb: (n: AppNotification) => void): Unsubscribe {
|
||||
this.notifCbs.add(cb);
|
||||
// also forward the mock's periodic notifications for liveliness
|
||||
if (!this.mockNotifUnsub)
|
||||
this.mockNotifUnsub = this.mock.onNotification((n) => this.notifCbs.forEach((c) => c(n)));
|
||||
return () => this.notifCbs.delete(cb);
|
||||
}
|
||||
|
||||
async getOnlineCount(): Promise<number> {
|
||||
try {
|
||||
const res = await fetch(`${SERVER}/api/stats/online`);
|
||||
|
||||
Reference in New Issue
Block a user