From 20f31cd15e41b62046cef6110ba30850d595e93e Mon Sep 17 00:00:00 2001 From: benya Date: Sun, 8 Mar 2026 20:40:44 +0300 Subject: [PATCH] fix(notifications): sync mute state in chat store immediately --- docs/core-checklist-status.md | 2 +- web/src/components/ChatInfoPanel.tsx | 2 ++ web/src/store/chatStore.ts | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/core-checklist-status.md b/docs/core-checklist-status.md index bf30abd..64a670b 100644 --- a/docs/core-checklist-status.md +++ b/docs/core-checklist-status.md @@ -34,7 +34,7 @@ Legend: 25. Admin Rights - `PARTIAL` (delete/pin/edit info + explicit ban API for groups/channels; integration tests cover channel member read-only, channel admin full-delete, channel message delete-for-all permissions, group profile edit permissions, and owner-only role management rules; remaining UX moderation tools limited) 26. Channels - `PARTIAL` (create/post/edit/delete/subscribe/unsubscribe; UX edge-cases still polishing) 27. Channel Types - `DONE` (public/private) -28. Notifications - `PARTIAL` (browser notifications + mute/settings; chat mute is now propagated in chat list payload and honored by web realtime notifications with mention override; no mobile push infra) +28. Notifications - `PARTIAL` (browser notifications + mute/settings; chat mute is propagated in chat list payload, honored by web realtime notifications with mention override, and mute toggle now syncs instantly in chat store; no mobile push infra) 29. Archive - `DONE` 30. Blacklist - `DONE` 31. Privacy - `PARTIAL` (avatar/last-seen/group-invites + PM policy `everyone|contacts|nobody`; group-invite `nobody` is available in API and web settings; integration tests cover PM policy matrix (`everyone/contacts/nobody`), group-invite policy matrix (`everyone/contacts/nobody`), and private chat counterpart visibility for `nobody/contacts`, remaining UX/matrix hardening) diff --git a/web/src/components/ChatInfoPanel.tsx b/web/src/components/ChatInfoPanel.tsx index 9b36001..2fbf363 100644 --- a/web/src/components/ChatInfoPanel.tsx +++ b/web/src/components/ChatInfoPanel.tsx @@ -34,6 +34,7 @@ interface Props { export function ChatInfoPanel({ chatId, open, onClose }: Props) { const me = useAuthStore((s) => s.me); const loadChats = useChatStore((s) => s.loadChats); + const updateChatMuted = useChatStore((s) => s.updateChatMuted); const setActiveChatId = useChatStore((s) => s.setActiveChatId); const setFocusedMessage = useChatStore((s) => s.setFocusedMessage); const showToast = useUiStore((s) => s.showToast); @@ -306,6 +307,7 @@ export function ChatInfoPanel({ chatId, open, onClose }: Props) { try { const updated = await updateChatNotificationSettings(chatId, !muted); setMuted(updated.muted); + updateChatMuted(chatId, updated.muted); } catch { setError("Failed to update notifications"); } finally { diff --git a/web/src/store/chatStore.ts b/web/src/store/chatStore.ts index 5376235..03f6474 100644 --- a/web/src/store/chatStore.ts +++ b/web/src/store/chatStore.ts @@ -99,6 +99,7 @@ interface ChatState { setReplyToMessage: (chatId: number, message: Message | null) => void; setEditingMessage: (chatId: number, message: Message | null) => void; updateChatPinnedMessage: (chatId: number, pinnedMessageId: number | null) => void; + updateChatMuted: (chatId: number, muted: boolean) => void; applyPresenceEvent: (chatId: number, userId: number, isOnline: boolean, lastSeenAt?: string) => void; removeChat: (chatId: number) => void; setDraft: (chatId: number, text: string) => void; @@ -416,6 +417,10 @@ export const useChatStore = create((set, get) => ({ set((state) => ({ chats: state.chats.map((chat) => (chat.id === chatId ? { ...chat, pinned_message_id: pinnedMessageId } : chat)) })), + updateChatMuted: (chatId, muted) => + set((state) => ({ + chats: state.chats.map((chat) => (chat.id === chatId ? { ...chat, muted } : chat)) + })), applyPresenceEvent: (chatId, userId, isOnline, lastSeenAt) => set((state) => ({ chats: state.chats.map((chat) => {