feat(phase7): per-channel notification settings and read receipts

Phase 7.1 — Per-channel notification settings
- New notification_settings table (user_id, channel_id, level)
- GET/PUT/DELETE handlers under /channels/{channelID}/notifications
- Push dispatch and @mention loops filtered by notification level
- Frontend: bell icon per channel cycling all/mentions/none

Phase 7.4 — Read receipts
- New read_states table (user_id, channel_id, last_read_message_id)
- PUT /channels/{channelID}/read and GET /users/me/read-states
- Auto-mark-read when messages load in ChatArea
- Unread dot for channels never opened

Also: favicon/icon refresh in index.html
This commit is contained in:
2026-06-30 12:39:14 -04:00
parent ab3e6053c8
commit cc6ed741f0
15 changed files with 389 additions and 16 deletions
+10
View File
@@ -6,6 +6,7 @@ import { useMemberStore } from "../stores/member.ts";
import { useThreadStore } from "../stores/thread.ts";
import { GiphyPicker, type Gif } from "./GiphyPicker";
import { MentionDropdown } from "./MentionDropdown";
import { useReadStatesStore } from "../stores/readStates.ts";
import { MessageSearch } from "./MessageSearch";
import { ThreadListPanel } from "./ThreadListPanel.tsx";
import { ThreadPanel } from "./ThreadPanel.tsx";
@@ -140,6 +141,7 @@ export function ChatArea() {
const activeChannel = channels.find((c) => c.id === activeChannelId);
const members = activeServerId ? membersByServer[activeServerId] || [] : [];
const memberUsernames = useMemo(() => new Set(members.map((m) => m.username)), [members]);
const markRead = useReadStatesStore((s) => s.markRead);
useEffect(() => {
if (activeChannelId) {
@@ -148,6 +150,14 @@ export function ChatArea() {
}
}, [activeChannelId, fetchMessages]);
useEffect(() => {
if (!activeChannelId || messages.length === 0 || isLoading) return;
const lastMsg = messages[messages.length - 1];
if (lastMsg?.id) {
markRead(activeChannelId, lastMsg.id);
}
}, [activeChannelId, messages.length, isLoading]);
useEffect(() => {
bottomRef.current?.scrollIntoView({ behavior: "auto" });
}, [messages]);