From cf1a77ae76d50b4d1b98519fa4ce5008d1da2072 Mon Sep 17 00:00:00 2001 From: benya Date: Sun, 8 Mar 2026 13:33:50 +0300 Subject: [PATCH] feat(web): add notifications history block in settings --- web/src/components/SettingsPanel.tsx | 70 ++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/web/src/components/SettingsPanel.tsx b/web/src/components/SettingsPanel.tsx index 457fde2..2840634 100644 --- a/web/src/components/SettingsPanel.tsx +++ b/web/src/components/SettingsPanel.tsx @@ -3,6 +3,7 @@ import { createPortal } from "react-dom"; import QRCode from "qrcode"; import { listBlockedUsers, updateMyProfile } from "../api/users"; import { disableTwoFactor, enableTwoFactor, listSessions, revokeAllSessions, revokeSession, setupTwoFactor } from "../api/auth"; +import { getNotifications, type NotificationItem } from "../api/notifications"; import type { AuthSession, AuthUser } from "../chat/types"; import { useAuthStore } from "../store/authStore"; import { AppPreferences, getAppPreferences, updateAppPreferences } from "../utils/preferences"; @@ -32,6 +33,8 @@ export function SettingsPanel({ open, onClose }: Props) { const [privacyLastSeen, setPrivacyLastSeen] = useState<"everyone" | "contacts" | "nobody">("everyone"); const [privacyAvatar, setPrivacyAvatar] = useState<"everyone" | "contacts" | "nobody">("everyone"); const [privacyGroupInvites, setPrivacyGroupInvites] = useState<"everyone" | "contacts">("everyone"); + const [notificationItems, setNotificationItems] = useState([]); + const [notificationItemsLoading, setNotificationItemsLoading] = useState(false); const [profileDraft, setProfileDraft] = useState({ name: "", username: "", @@ -107,6 +110,33 @@ export function SettingsPanel({ open, onClose }: Props) { }; }, [open, page]); + useEffect(() => { + if (!open || page !== "notifications") { + return; + } + let cancelled = false; + setNotificationItemsLoading(true); + void (async () => { + try { + const rows = await getNotifications(30); + if (!cancelled) { + setNotificationItems(rows); + } + } catch { + if (!cancelled) { + setNotificationItems([]); + } + } finally { + if (!cancelled) { + setNotificationItemsLoading(false); + } + } + })(); + return () => { + cancelled = true; + }; + }, [open, page]); + useEffect(() => { if (!open || page !== "privacy") { return; @@ -292,6 +322,37 @@ export function SettingsPanel({ open, onClose }: Props) { updatePrefs({ groupNotifications: checked })} /> updatePrefs({ channelNotifications: checked })} /> + +
+
+

Recent Notifications

+ +
+ {notificationItemsLoading ?

Loading...

: null} + {!notificationItemsLoading && notificationItems.length === 0 ?

No notifications yet

: null} +
+ {notificationItems.slice(0, 20).map((item) => ( +
+

{item.event_type}

+

{renderNotificationPayload(item.payload)}

+

{new Date(item.created_at).toLocaleString()}

+
+ ))} +
+
) : null} @@ -495,6 +556,15 @@ export function SettingsPanel({ open, onClose }: Props) { ); } +function renderNotificationPayload(payload: string): string { + try { + const parsed = JSON.parse(payload) as { text?: string; title?: string; body?: string; chat_id?: number }; + return parsed.text || parsed.body || parsed.title || (parsed.chat_id ? `Chat #${parsed.chat_id}` : payload); + } catch { + return payload; + } +} + function SettingsRow({ label, value, onClick }: { label: string; value: string; onClick: () => void }) { return (