feat(web): add notifications history block in settings
All checks were successful
CI / test (push) Successful in 23s
All checks were successful
CI / test (push) Successful in 23s
This commit is contained in:
@@ -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<NotificationItem[]>([]);
|
||||
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) {
|
||||
<CheckboxOption checked={prefs.groupNotifications} label="Notifications for groups" onChange={(checked) => updatePrefs({ groupNotifications: checked })} />
|
||||
<CheckboxOption checked={prefs.channelNotifications} label="Notifications for channels" onChange={(checked) => updatePrefs({ channelNotifications: checked })} />
|
||||
</section>
|
||||
|
||||
<section className="rounded border border-slate-700/70 bg-slate-800/50 p-3">
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<p className="text-xs uppercase tracking-wide text-slate-400">Recent Notifications</p>
|
||||
<button
|
||||
className="rounded bg-slate-700 px-2 py-1 text-[11px]"
|
||||
onClick={async () => {
|
||||
setNotificationItemsLoading(true);
|
||||
try {
|
||||
setNotificationItems(await getNotifications(30));
|
||||
} finally {
|
||||
setNotificationItemsLoading(false);
|
||||
}
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
Refresh
|
||||
</button>
|
||||
</div>
|
||||
{notificationItemsLoading ? <p className="text-xs text-slate-400">Loading...</p> : null}
|
||||
{!notificationItemsLoading && notificationItems.length === 0 ? <p className="text-xs text-slate-400">No notifications yet</p> : null}
|
||||
<div className="space-y-2">
|
||||
{notificationItems.slice(0, 20).map((item) => (
|
||||
<div className="rounded bg-slate-900/50 px-2 py-2" key={item.id}>
|
||||
<p className="text-xs font-semibold text-slate-200">{item.event_type}</p>
|
||||
<p className="mt-0.5 line-clamp-2 text-[11px] text-slate-400">{renderNotificationPayload(item.payload)}</p>
|
||||
<p className="mt-0.5 text-[11px] text-slate-500">{new Date(item.created_at).toLocaleString()}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
) : 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 (
|
||||
<button className="flex w-full items-center justify-between px-4 py-3 text-left hover:bg-slate-800/60" onClick={onClick} type="button">
|
||||
|
||||
Reference in New Issue
Block a user