diff --git a/src/app/components/RoomNotificationSwitcher.tsx b/src/app/components/RoomNotificationSwitcher.tsx new file mode 100644 index 0000000..7ce9156 --- /dev/null +++ b/src/app/components/RoomNotificationSwitcher.tsx @@ -0,0 +1,120 @@ +import { Box, config, Icon, Menu, MenuItem, PopOut, RectCords, Text } from 'folds'; +import React, { MouseEventHandler, ReactNode, useMemo, useState } from 'react'; +import FocusTrap from 'focus-trap-react'; +import { stopPropagation } from '../utils/keyboard'; +import { + getRoomNotificationModeIcon, + RoomNotificationMode, + useSetRoomNotificationPreference, +} from '../hooks/useRoomsNotificationPreferences'; +import { AsyncStatus } from '../hooks/useAsyncCallback'; + +const useRoomNotificationModes = (): RoomNotificationMode[] => + useMemo( + () => [ + RoomNotificationMode.Unset, + RoomNotificationMode.AllMessages, + RoomNotificationMode.SpecialMessages, + RoomNotificationMode.Mute, + ], + [] + ); + +const useRoomNotificationModeStr = (): Record => + useMemo( + () => ({ + [RoomNotificationMode.Unset]: 'Default', + [RoomNotificationMode.AllMessages]: 'All Messages', + [RoomNotificationMode.SpecialMessages]: 'Mention & Keywords', + [RoomNotificationMode.Mute]: 'Mute', + }), + [] + ); + +type NotificationModeSwitcherProps = { + roomId: string; + value?: RoomNotificationMode; + children: ( + handleOpen: MouseEventHandler, + opened: boolean, + changing: boolean + ) => ReactNode; +}; +export function RoomNotificationModeSwitcher({ + roomId, + value = RoomNotificationMode.Unset, + children, +}: NotificationModeSwitcherProps) { + const modes = useRoomNotificationModes(); + const modeToStr = useRoomNotificationModeStr(); + + const { modeState, setMode } = useSetRoomNotificationPreference(roomId); + const changing = modeState.status === AsyncStatus.Loading; + + const [menuCords, setMenuCords] = useState(); + + const handleOpenMenu: MouseEventHandler = (evt) => { + setMenuCords(evt.currentTarget.getBoundingClientRect()); + }; + + const handleClose = () => { + setMenuCords(undefined); + }; + + const handleSelect = (mode: RoomNotificationMode) => { + if (changing) return; + setMode(mode, value); + handleClose(); + }; + + return ( + + evt.key === 'ArrowDown' || evt.key === 'ArrowRight', + isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp' || evt.key === 'ArrowLeft', + escapeDeactivates: stopPropagation, + }} + > + + + {modes.map((mode) => ( + handleSelect(mode)} + before={ + + } + > + + {mode === value ? {modeToStr[mode]} : modeToStr[mode]} + + + ))} + + + + } + > + {children(handleOpenMenu, !!menuCords, changing)} + + ); +} diff --git a/src/app/features/room-nav/RoomNavItem.tsx b/src/app/features/room-nav/RoomNavItem.tsx index 27f7283..bdb8141 100644 --- a/src/app/features/room-nav/RoomNavItem.tsx +++ b/src/app/features/room-nav/RoomNavItem.tsx @@ -15,6 +15,7 @@ import { Line, RectCords, Badge, + Spinner, } from 'folds'; import { useFocusWithin, useHover } from 'react-aria'; import FocusTrap from 'focus-trap-react'; @@ -43,13 +44,19 @@ import { useSetting } from '../../state/hooks/settings'; import { settingsAtom } from '../../state/settings'; import { useOpenRoomSettings } from '../../state/hooks/roomSettings'; import { useSpaceOptionally } from '../../hooks/useSpace'; +import { + getRoomNotificationModeIcon, + RoomNotificationMode, +} from '../../hooks/useRoomsNotificationPreferences'; +import { RoomNotificationModeSwitcher } from '../../components/RoomNotificationSwitcher'; type RoomNavItemMenuProps = { room: Room; requestClose: () => void; + notificationMode?: RoomNotificationMode; }; const RoomNavItemMenu = forwardRef( - ({ room, requestClose }, ref) => { + ({ room, requestClose, notificationMode }, ref) => { const mx = useMatrixClient(); const [hideActivity] = useSetting(settingsAtom, 'hideActivity'); const unread = useRoomUnread(room.roomId, roomToUnreadAtom); @@ -95,6 +102,27 @@ const RoomNavItemMenu = forwardRef( Mark as Read + + {(handleOpen, opened, changing) => ( + + ) : ( + + ) + } + radii="300" + aria-pressed={opened} + onClick={handleOpen} + > + + Notifications + + + )} + @@ -170,7 +198,7 @@ type RoomNavItemProps = { room: Room; selected: boolean; linkPath: string; - muted?: boolean; + notificationMode?: RoomNotificationMode; showAvatar?: boolean; direct?: boolean; }; @@ -179,7 +207,7 @@ export function RoomNavItem({ selected, showAvatar, direct, - muted, + notificationMode, linkPath, }: RoomNavItemProps) { const mx = useMatrixClient(); @@ -263,7 +291,9 @@ export function RoomNavItem({ 0} count={unread.total} /> )} - {muted && !optionsVisible && } + {!optionsVisible && notificationMode !== RoomNotificationMode.Unset && ( + + )} @@ -287,7 +317,11 @@ export function RoomNavItem({ escapeDeactivates: stopPropagation, }} > - setMenuAnchor(undefined)} /> + setMenuAnchor(undefined)} + notificationMode={notificationMode} + /> } > diff --git a/src/app/hooks/useNotificationMode.ts b/src/app/hooks/useNotificationMode.ts index 1c2267e..df90f64 100644 --- a/src/app/hooks/useNotificationMode.ts +++ b/src/app/hooks/useNotificationMode.ts @@ -7,6 +7,19 @@ export enum NotificationMode { NotifyLoud = 'NotifyLoud', } +export const getNotificationMode = (actions: PushRuleAction[]): NotificationMode => { + const soundTweak = actions.find( + (action) => typeof action === 'object' && action.set_tweak === TweakName.Sound + ); + const notify = actions.find( + (action) => typeof action === 'string' && action === PushRuleActionName.Notify + ); + + if (notify && soundTweak) return NotificationMode.NotifyLoud; + if (notify) return NotificationMode.Notify; + return NotificationMode.OFF; +}; + export type NotificationModeOptions = { soundValue?: string; highlight?: boolean; @@ -49,18 +62,7 @@ export const useNotificationModeActions = ( }; export const useNotificationActionsMode = (actions: PushRuleAction[]): NotificationMode => { - const mode: NotificationMode = useMemo(() => { - const soundTweak = actions.find( - (action) => typeof action === 'object' && action.set_tweak === TweakName.Sound - ); - const notify = actions.find( - (action) => typeof action === 'string' && action === PushRuleActionName.Notify - ); - - if (notify && soundTweak) return NotificationMode.NotifyLoud; - if (notify) return NotificationMode.Notify; - return NotificationMode.OFF; - }, [actions]); + const mode: NotificationMode = useMemo(() => getNotificationMode(actions), [actions]); return mode; }; diff --git a/src/app/hooks/useRoomsNotificationPreferences.ts b/src/app/hooks/useRoomsNotificationPreferences.ts new file mode 100644 index 0000000..ceea95f --- /dev/null +++ b/src/app/hooks/useRoomsNotificationPreferences.ts @@ -0,0 +1,169 @@ +import { createContext, useCallback, useContext, useMemo } from 'react'; +import { ConditionKind, IPushRules, MatrixClient, PushRuleKind } from 'matrix-js-sdk'; +import { Icons, IconSrc } from 'folds'; +import { AccountDataEvent } from '../../types/matrix/accountData'; +import { useAccountData } from './useAccountData'; +import { isRoomId } from '../utils/matrix'; +import { + getNotificationMode, + getNotificationModeActions, + NotificationMode, +} from './useNotificationMode'; +import { useAsyncCallback } from './useAsyncCallback'; +import { useMatrixClient } from './useMatrixClient'; + +export type RoomsNotificationPreferences = { + mute: Set; + specialMessages: Set; + allMessages: Set; +}; + +const RoomsNotificationPreferencesContext = createContext( + null +); +export const RoomsNotificationPreferencesProvider = RoomsNotificationPreferencesContext.Provider; + +export const useRoomsNotificationPreferencesContext = (): RoomsNotificationPreferences => { + const preferences = useContext(RoomsNotificationPreferencesContext); + + if (!preferences) { + throw new Error('No RoomsNotificationPreferences provided!'); + } + + return preferences; +}; + +export const useRoomsNotificationPreferences = (): RoomsNotificationPreferences => { + const pushRules = useAccountData(AccountDataEvent.PushRules)?.getContent(); + + const preferences: RoomsNotificationPreferences = useMemo(() => { + const global = pushRules?.global; + const room = global?.room ?? []; + const override = global?.override ?? []; + + const pref: RoomsNotificationPreferences = { + mute: new Set(), + specialMessages: new Set(), + allMessages: new Set(), + }; + + override.forEach((rule) => { + if (isRoomId(rule.rule_id) && getNotificationMode(rule.actions) === NotificationMode.OFF) { + pref.mute.add(rule.rule_id); + } + }); + room.forEach((rule) => { + if (getNotificationMode(rule.actions) === NotificationMode.OFF) { + pref.specialMessages.add(rule.rule_id); + } + }); + room.forEach((rule) => { + if (getNotificationMode(rule.actions) !== NotificationMode.OFF) { + pref.allMessages.add(rule.rule_id); + } + }); + + return pref; + }, [pushRules]); + + return preferences; +}; + +export enum RoomNotificationMode { + Unset = 'Unset', + Mute = 'Mute', + SpecialMessages = 'SpecialMessages', + AllMessages = 'AllMessages', +} + +export const getRoomNotificationMode = ( + preferences: RoomsNotificationPreferences, + roomId: string +): RoomNotificationMode => { + if (preferences.mute.has(roomId)) { + return RoomNotificationMode.Mute; + } + if (preferences.specialMessages.has(roomId)) { + return RoomNotificationMode.SpecialMessages; + } + if (preferences.allMessages.has(roomId)) { + return RoomNotificationMode.AllMessages; + } + + return RoomNotificationMode.Unset; +}; + +export const useRoomNotificationPreference = ( + preferences: RoomsNotificationPreferences, + roomId: string +): RoomNotificationMode => + useMemo(() => getRoomNotificationMode(preferences, roomId), [preferences, roomId]); + +export const getRoomNotificationModeIcon = (mode?: RoomNotificationMode): IconSrc => { + if (mode === RoomNotificationMode.Mute) return Icons.BellMute; + if (mode === RoomNotificationMode.SpecialMessages) return Icons.BellPing; + if (mode === RoomNotificationMode.AllMessages) return Icons.BellRing; + + return Icons.Bell; +}; + +export const setRoomNotificationPreference = async ( + mx: MatrixClient, + roomId: string, + mode: RoomNotificationMode, + previousMode: RoomNotificationMode +): Promise => { + // remove the old preference + if ( + previousMode === RoomNotificationMode.AllMessages || + previousMode === RoomNotificationMode.SpecialMessages + ) { + await mx.deletePushRule('global', PushRuleKind.RoomSpecific, roomId); + } + if (previousMode === RoomNotificationMode.Mute) { + await mx.deletePushRule('global', PushRuleKind.Override, roomId); + } + + // set new preference + if (mode === RoomNotificationMode.Unset) { + return; + } + + if (mode === RoomNotificationMode.Mute) { + await mx.addPushRule('global', PushRuleKind.Override, roomId, { + conditions: [ + { + kind: ConditionKind.EventMatch, + key: 'room_id', + pattern: roomId, + }, + ], + actions: getNotificationModeActions(NotificationMode.OFF), + }); + return; + } + + await mx.addPushRule('global', PushRuleKind.RoomSpecific, roomId, { + actions: + mode === RoomNotificationMode.AllMessages + ? getNotificationModeActions(NotificationMode.NotifyLoud) + : getNotificationModeActions(NotificationMode.OFF), + }); +}; + +export const useSetRoomNotificationPreference = (roomId: string) => { + const mx = useMatrixClient(); + + const [modeState, setMode] = useAsyncCallback( + useCallback( + (mode: RoomNotificationMode, previousMode: RoomNotificationMode) => + setRoomNotificationPreference(mx, roomId, mode, previousMode), + [mx, roomId] + ) + ); + + return { + modeState, + setMode, + }; +}; diff --git a/src/app/pages/Router.tsx b/src/app/pages/Router.tsx index ee934e5..76f0460 100644 --- a/src/app/pages/Router.tsx +++ b/src/app/pages/Router.tsx @@ -59,6 +59,7 @@ import { AuthRouteThemeManager, UnAuthRouteThemeManager } from './ThemeManager'; import { ReceiveSelfDeviceVerification } from '../components/DeviceVerification'; import { AutoRestoreBackupOnVerification } from '../components/BackupRestore'; import { RoomSettingsRenderer } from '../features/room-settings'; +import { ClientRoomsNotificationPreferences } from './client/ClientRoomsNotificationPreferences'; export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize) => { const { hashRouter } = clientConfig; @@ -111,22 +112,24 @@ export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize) <> - - - - - - } - > - - - - - - - + + + + + + + } + > + + + + + + + + diff --git a/src/app/pages/client/ClientRoomsNotificationPreferences.tsx b/src/app/pages/client/ClientRoomsNotificationPreferences.tsx new file mode 100644 index 0000000..1ab0213 --- /dev/null +++ b/src/app/pages/client/ClientRoomsNotificationPreferences.tsx @@ -0,0 +1,15 @@ +import React, { ReactNode } from 'react'; +import { + RoomsNotificationPreferencesProvider, + useRoomsNotificationPreferences, +} from '../../hooks/useRoomsNotificationPreferences'; + +export function ClientRoomsNotificationPreferences({ children }: { children: ReactNode }) { + const preferences = useRoomsNotificationPreferences(); + + return ( + + {children} + + ); +} diff --git a/src/app/pages/client/direct/Direct.tsx b/src/app/pages/client/direct/Direct.tsx index 5e79921..b6a8de1 100644 --- a/src/app/pages/client/direct/Direct.tsx +++ b/src/app/pages/client/direct/Direct.tsx @@ -33,7 +33,6 @@ import { getCanonicalAliasOrRoomId } from '../../../utils/matrix'; import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom'; import { VirtualTile } from '../../../components/virtualizer'; import { RoomNavCategoryButton, RoomNavItem } from '../../../features/room-nav'; -import { muteChangesAtom } from '../../../state/room-list/mutedRoomList'; import { makeNavCategoryId } from '../../../state/closedNavCategories'; import { roomToUnreadAtom } from '../../../state/room/roomToUnread'; import { useCategoryHandler } from '../../../hooks/useCategoryHandler'; @@ -47,6 +46,10 @@ import { markAsRead } from '../../../../client/action/notifications'; import { stopPropagation } from '../../../utils/keyboard'; import { useSetting } from '../../../state/hooks/settings'; import { settingsAtom } from '../../../state/settings'; +import { + getRoomNotificationMode, + useRoomsNotificationPreferencesContext, +} from '../../../hooks/useRoomsNotificationPreferences'; type DirectMenuProps = { requestClose: () => void; @@ -167,8 +170,7 @@ export function Direct() { useNavToActivePathMapper('direct'); const scrollRef = useRef(null); const directs = useDirectRooms(); - const muteChanges = useAtomValue(muteChangesAtom); - const mutedRooms = muteChanges.added; + const notificationPreferences = useRoomsNotificationPreferencesContext(); const roomToUnread = useAtomValue(roomToUnreadAtom); const selectedRoomId = useSelectedRoom(); @@ -254,7 +256,10 @@ export function Direct() { showAvatar direct linkPath={getDirectRoomPath(getCanonicalAliasOrRoomId(mx, roomId))} - muted={mutedRooms.includes(roomId)} + notificationMode={getRoomNotificationMode( + notificationPreferences, + room.roomId + )} /> ); diff --git a/src/app/pages/client/home/Home.tsx b/src/app/pages/client/home/Home.tsx index fa5e68a..af4164f 100644 --- a/src/app/pages/client/home/Home.tsx +++ b/src/app/pages/client/home/Home.tsx @@ -37,7 +37,6 @@ import { useHomeRooms } from './useHomeRooms'; import { useMatrixClient } from '../../../hooks/useMatrixClient'; import { VirtualTile } from '../../../components/virtualizer'; import { RoomNavCategoryButton, RoomNavItem } from '../../../features/room-nav'; -import { muteChangesAtom } from '../../../state/room-list/mutedRoomList'; import { makeNavCategoryId } from '../../../state/closedNavCategories'; import { roomToUnreadAtom } from '../../../state/room/roomToUnread'; import { useCategoryHandler } from '../../../hooks/useCategoryHandler'; @@ -50,6 +49,10 @@ import { useClosedNavCategoriesAtom } from '../../../state/hooks/closedNavCatego import { stopPropagation } from '../../../utils/keyboard'; import { useSetting } from '../../../state/hooks/settings'; import { settingsAtom } from '../../../state/settings'; +import { + getRoomNotificationMode, + useRoomsNotificationPreferencesContext, +} from '../../../hooks/useRoomsNotificationPreferences'; type HomeMenuProps = { requestClose: () => void; @@ -199,8 +202,7 @@ export function Home() { useNavToActivePathMapper('home'); const scrollRef = useRef(null); const rooms = useHomeRooms(); - const muteChanges = useAtomValue(muteChangesAtom); - const mutedRooms = muteChanges.added; + const notificationPreferences = useRoomsNotificationPreferencesContext(); const roomToUnread = useAtomValue(roomToUnreadAtom); const selectedRoomId = useSelectedRoom(); @@ -321,7 +323,10 @@ export function Home() { room={room} selected={selected} linkPath={getHomeRoomPath(getCanonicalAliasOrRoomId(mx, roomId))} - muted={mutedRooms.includes(roomId)} + notificationMode={getRoomNotificationMode( + notificationPreferences, + room.roomId + )} /> ); diff --git a/src/app/pages/client/space/Space.tsx b/src/app/pages/client/space/Space.tsx index 1714d8e..737663a 100644 --- a/src/app/pages/client/space/Space.tsx +++ b/src/app/pages/client/space/Space.tsx @@ -45,7 +45,6 @@ import { import { useSpace } from '../../../hooks/useSpace'; import { VirtualTile } from '../../../components/virtualizer'; import { RoomNavCategoryButton, RoomNavItem } from '../../../features/room-nav'; -import { muteChangesAtom } from '../../../state/room-list/mutedRoomList'; import { makeNavCategoryId } from '../../../state/closedNavCategories'; import { roomToUnreadAtom } from '../../../state/room/roomToUnread'; import { useCategoryHandler } from '../../../hooks/useCategoryHandler'; @@ -71,6 +70,10 @@ import { getMatrixToRoom } from '../../../plugins/matrix-to'; import { getViaServers } from '../../../plugins/via-servers'; import { useSetting } from '../../../state/hooks/settings'; import { settingsAtom } from '../../../state/settings'; +import { + getRoomNotificationMode, + useRoomsNotificationPreferencesContext, +} from '../../../hooks/useRoomsNotificationPreferences'; type SpaceMenuProps = { room: Room; @@ -269,8 +272,7 @@ export function Space() { const roomToUnread = useAtomValue(roomToUnreadAtom); const allRooms = useAtomValue(allRoomsAtom); const allJoinedRooms = useMemo(() => new Set(allRooms), [allRooms]); - const muteChanges = useAtomValue(muteChangesAtom); - const mutedRooms = muteChanges.added; + const notificationPreferences = useRoomsNotificationPreferencesContext(); const selectedRoomId = useSelectedRoom(); const lobbySelected = useSpaceLobbySelected(spaceIdOrAlias); @@ -404,7 +406,7 @@ export function Space() { showAvatar={mDirects.has(roomId)} direct={mDirects.has(roomId)} linkPath={getToLink(roomId)} - muted={mutedRooms.includes(roomId)} + notificationMode={getRoomNotificationMode(notificationPreferences, room.roomId)} /> ); diff --git a/src/app/state/hooks/useBindAtoms.ts b/src/app/state/hooks/useBindAtoms.ts index 136c833..d4572ff 100644 --- a/src/app/state/hooks/useBindAtoms.ts +++ b/src/app/state/hooks/useBindAtoms.ts @@ -2,7 +2,6 @@ import { MatrixClient } from 'matrix-js-sdk'; import { allInvitesAtom, useBindAllInvitesAtom } from '../room-list/inviteList'; import { allRoomsAtom, useBindAllRoomsAtom } from '../room-list/roomList'; import { mDirectAtom, useBindMDirectAtom } from '../mDirectList'; -import { muteChangesAtom, mutedRoomsAtom, useBindMutedRoomsAtom } from '../room-list/mutedRoomList'; import { roomToUnreadAtom, useBindRoomToUnreadAtom } from '../room/roomToUnread'; import { roomToParentsAtom, useBindRoomToParentsAtom } from '../room/roomToParents'; import { roomIdToTypingMembersAtom, useBindRoomIdToTypingMembersAtom } from '../typingMembers'; @@ -12,8 +11,7 @@ export const useBindAtoms = (mx: MatrixClient) => { useBindAllInvitesAtom(mx, allInvitesAtom); useBindAllRoomsAtom(mx, allRoomsAtom); useBindRoomToParentsAtom(mx, roomToParentsAtom); - useBindMutedRoomsAtom(mx, mutedRoomsAtom); - useBindRoomToUnreadAtom(mx, roomToUnreadAtom, muteChangesAtom); + useBindRoomToUnreadAtom(mx, roomToUnreadAtom); useBindRoomIdToTypingMembersAtom(mx, roomIdToTypingMembersAtom); }; diff --git a/src/app/state/room-list/mutedRoomList.ts b/src/app/state/room-list/mutedRoomList.ts deleted file mode 100644 index cb56ec0..0000000 --- a/src/app/state/room-list/mutedRoomList.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { atom, useSetAtom } from 'jotai'; -import { ClientEvent, IPushRule, IPushRules, MatrixClient, MatrixEvent } from 'matrix-js-sdk'; -import { useEffect } from 'react'; -import { MuteChanges } from '../../../types/matrix/room'; -import { findMutedRule, isMutedRule } from '../../utils/room'; - -export type MutedRoomsUpdate = - | { - type: 'INITIALIZE'; - addRooms: string[]; - } - | { - type: 'UPDATE'; - addRooms: string[]; - removeRooms: string[]; - }; - -export const muteChangesAtom = atom({ - added: [], - removed: [], -}); - -const baseMutedRoomsAtom = atom(new Set()); -export const mutedRoomsAtom = atom, [MutedRoomsUpdate], undefined>( - (get) => get(baseMutedRoomsAtom), - (get, set, action) => { - const mutedRooms = new Set([...get(mutedRoomsAtom)]); - if (action.type === 'INITIALIZE') { - set(baseMutedRoomsAtom, new Set([...action.addRooms])); - set(muteChangesAtom, { - added: [...action.addRooms], - removed: [], - }); - return; - } - if (action.type === 'UPDATE') { - action.removeRooms.forEach((roomId) => mutedRooms.delete(roomId)); - action.addRooms.forEach((roomId) => mutedRooms.add(roomId)); - set(baseMutedRoomsAtom, mutedRooms); - set(muteChangesAtom, { - added: [...action.addRooms], - removed: [...action.removeRooms], - }); - } - } -); - -export const useBindMutedRoomsAtom = (mx: MatrixClient, mutedAtom: typeof mutedRoomsAtom) => { - const setMuted = useSetAtom(mutedAtom); - - useEffect(() => { - const overrideRules = mx.getAccountData('m.push_rules')?.getContent() - ?.global?.override; - if (overrideRules) { - const mutedRooms = overrideRules.reduce((rooms, rule) => { - if (isMutedRule(rule)) rooms.push(rule.rule_id); - return rooms; - }, []); - setMuted({ - type: 'INITIALIZE', - addRooms: mutedRooms, - }); - } - }, [mx, setMuted]); - - useEffect(() => { - const handlePushRules = (mEvent: MatrixEvent, oldMEvent?: MatrixEvent) => { - if (mEvent.getType() === 'm.push_rules') { - const override = mEvent?.getContent()?.global?.override as IPushRule[] | undefined; - const oldOverride = oldMEvent?.getContent()?.global?.override as IPushRule[] | undefined; - if (!override || !oldOverride) return; - - const isMuteToggled = (rule: IPushRule, otherOverride: IPushRule[]) => { - const roomId = rule.rule_id; - - const isMuted = isMutedRule(rule); - if (!isMuted) return false; - const isOtherMuted = findMutedRule(otherOverride, roomId); - if (isOtherMuted) return false; - return true; - }; - - const mutedRules = override.filter((rule) => isMuteToggled(rule, oldOverride)); - const unMutedRules = oldOverride.filter((rule) => isMuteToggled(rule, override)); - - setMuted({ - type: 'UPDATE', - addRooms: mutedRules.map((rule) => rule.rule_id), - removeRooms: unMutedRules.map((rule) => rule.rule_id), - }); - } - }; - mx.on(ClientEvent.AccountData, handlePushRules); - return () => { - mx.removeListener(ClientEvent.AccountData, handlePushRules); - }; - }, [mx, setMuted]); -}; diff --git a/src/app/state/room/roomToUnread.ts b/src/app/state/room/roomToUnread.ts index bf99fe3..f7ef1b7 100644 --- a/src/app/state/room/roomToUnread.ts +++ b/src/app/state/room/roomToUnread.ts @@ -1,5 +1,5 @@ import produce from 'immer'; -import { atom, useSetAtom, PrimitiveAtom, useAtomValue } from 'jotai'; +import { atom, useSetAtom } from 'jotai'; import { IRoomTimelineData, MatrixClient, @@ -11,7 +11,6 @@ import { import { ReceiptContent, ReceiptType } from 'matrix-js-sdk/lib/@types/read_receipts'; import { useCallback, useEffect } from 'react'; import { - MuteChanges, Membership, NotificationType, RoomToUnread, @@ -25,11 +24,11 @@ import { getUnreadInfo, getUnreadInfos, isNotificationEvent, - roomHaveUnread, } from '../../utils/room'; import { roomToParentsAtom } from './roomToParents'; import { useStateEventCallback } from '../../hooks/useStateEventCallback'; import { useSyncState } from '../../hooks/useSyncState'; +import { useRoomsNotificationPreferencesContext } from '../../hooks/useRoomsNotificationPreferences'; export type RoomToUnreadAction = | { @@ -167,13 +166,9 @@ export const roomToUnreadAtom = atom -) => { +export const useBindRoomToUnreadAtom = (mx: MatrixClient, unreadAtom: typeof roomToUnreadAtom) => { const setUnreadAtom = useSetAtom(unreadAtom); - const muteChanges = useAtomValue(muteChangesAtom); + const roomsNotificationPreferences = useRoomsNotificationPreferencesContext(); useEffect(() => { setUnreadAtom({ @@ -249,16 +244,11 @@ export const useBindRoomToUnreadAtom = ( }, [mx, setUnreadAtom]); useEffect(() => { - muteChanges.removed.forEach((roomId) => { - const room = mx.getRoom(roomId); - if (!room) return; - if (!roomHaveUnread(mx, room)) return; - setUnreadAtom({ type: 'PUT', unreadInfo: getUnreadInfo(room) }); + setUnreadAtom({ + type: 'RESET', + unreadInfos: getUnreadInfos(mx), }); - muteChanges.added.forEach((roomId) => { - setUnreadAtom({ type: 'DELETE', roomId }); - }); - }, [mx, setUnreadAtom, muteChanges]); + }, [mx, setUnreadAtom, roomsNotificationPreferences]); useEffect(() => { const handleMembershipChange = (room: Room, membership: string) => {