--- /dev/null
+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<RoomNotificationMode, string> =>
+ useMemo(
+ () => ({
+ [RoomNotificationMode.Unset]: 'Default',
+ [RoomNotificationMode.AllMessages]: 'All Messages',
+ [RoomNotificationMode.SpecialMessages]: 'Mention & Keywords',
+ [RoomNotificationMode.Mute]: 'Mute',
+ }),
+ []
+ );
+
+type NotificationModeSwitcherProps = {
+ roomId: string;
+ value?: RoomNotificationMode;
+ children: (
+ handleOpen: MouseEventHandler<HTMLButtonElement>,
+ 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<RectCords>();
+
+ const handleOpenMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
+ setMenuCords(evt.currentTarget.getBoundingClientRect());
+ };
+
+ const handleClose = () => {
+ setMenuCords(undefined);
+ };
+
+ const handleSelect = (mode: RoomNotificationMode) => {
+ if (changing) return;
+ setMode(mode, value);
+ handleClose();
+ };
+
+ return (
+ <PopOut
+ anchor={menuCords}
+ offset={5}
+ position="Right"
+ align="Start"
+ content={
+ <FocusTrap
+ focusTrapOptions={{
+ initialFocus: false,
+ onDeactivate: handleClose,
+ clickOutsideDeactivates: true,
+ isKeyForward: (evt: KeyboardEvent) =>
+ evt.key === 'ArrowDown' || evt.key === 'ArrowRight',
+ isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp' || evt.key === 'ArrowLeft',
+ escapeDeactivates: stopPropagation,
+ }}
+ >
+ <Menu>
+ <Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
+ {modes.map((mode) => (
+ <MenuItem
+ key={mode}
+ size="300"
+ variant="Surface"
+ aria-pressed={mode === value}
+ radii="300"
+ disabled={changing}
+ onClick={() => handleSelect(mode)}
+ before={
+ <Icon
+ size="100"
+ src={getRoomNotificationModeIcon(mode)}
+ filled={mode === value}
+ />
+ }
+ >
+ <Text size="T300">
+ {mode === value ? <b>{modeToStr[mode]}</b> : modeToStr[mode]}
+ </Text>
+ </MenuItem>
+ ))}
+ </Box>
+ </Menu>
+ </FocusTrap>
+ }
+ >
+ {children(handleOpenMenu, !!menuCords, changing)}
+ </PopOut>
+ );
+}
Line,
RectCords,
Badge,
+ Spinner,
} from 'folds';
import { useFocusWithin, useHover } from 'react-aria';
import FocusTrap from 'focus-trap-react';
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<HTMLDivElement, RoomNavItemMenuProps>(
- ({ room, requestClose }, ref) => {
+ ({ room, requestClose, notificationMode }, ref) => {
const mx = useMatrixClient();
const [hideActivity] = useSetting(settingsAtom, 'hideActivity');
const unread = useRoomUnread(room.roomId, roomToUnreadAtom);
Mark as Read
</Text>
</MenuItem>
+ <RoomNotificationModeSwitcher roomId={room.roomId} value={notificationMode}>
+ {(handleOpen, opened, changing) => (
+ <MenuItem
+ size="300"
+ after={
+ changing ? (
+ <Spinner size="100" variant="Secondary" />
+ ) : (
+ <Icon size="100" src={getRoomNotificationModeIcon(notificationMode)} />
+ )
+ }
+ radii="300"
+ aria-pressed={opened}
+ onClick={handleOpen}
+ >
+ <Text style={{ flexGrow: 1 }} as="span" size="T300" truncate>
+ Notifications
+ </Text>
+ </MenuItem>
+ )}
+ </RoomNotificationModeSwitcher>
</Box>
<Line variant="Surface" size="300" />
<Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
room: Room;
selected: boolean;
linkPath: string;
- muted?: boolean;
+ notificationMode?: RoomNotificationMode;
showAvatar?: boolean;
direct?: boolean;
};
selected,
showAvatar,
direct,
- muted,
+ notificationMode,
linkPath,
}: RoomNavItemProps) {
const mx = useMatrixClient();
<UnreadBadge highlight={unread.highlight > 0} count={unread.total} />
</UnreadBadgeCenter>
)}
- {muted && !optionsVisible && <Icon size="50" src={Icons.BellMute} />}
+ {!optionsVisible && notificationMode !== RoomNotificationMode.Unset && (
+ <Icon size="50" src={getRoomNotificationModeIcon(notificationMode)} />
+ )}
</Box>
</NavItemContent>
</NavLink>
escapeDeactivates: stopPropagation,
}}
>
- <RoomNavItemMenu room={room} requestClose={() => setMenuAnchor(undefined)} />
+ <RoomNavItemMenu
+ room={room}
+ requestClose={() => setMenuAnchor(undefined)}
+ notificationMode={notificationMode}
+ />
</FocusTrap>
}
>
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;
};
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;
};
--- /dev/null
+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<string>;
+ specialMessages: Set<string>;
+ allMessages: Set<string>;
+};
+
+const RoomsNotificationPreferencesContext = createContext<RoomsNotificationPreferences | null>(
+ 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<IPushRules>();
+
+ 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<void> => {
+ // 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,
+ };
+};
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;
<>
<ClientRoot>
<ClientInitStorageAtom>
- <ClientBindAtoms>
- <ClientNonUIFeatures>
- <ClientLayout
- nav={
- <MobileFriendlyClientNav>
- <SidebarNav />
- </MobileFriendlyClientNav>
- }
- >
- <Outlet />
- </ClientLayout>
- <RoomSettingsRenderer />
- <ReceiveSelfDeviceVerification />
- <AutoRestoreBackupOnVerification />
- </ClientNonUIFeatures>
- </ClientBindAtoms>
+ <ClientRoomsNotificationPreferences>
+ <ClientBindAtoms>
+ <ClientNonUIFeatures>
+ <ClientLayout
+ nav={
+ <MobileFriendlyClientNav>
+ <SidebarNav />
+ </MobileFriendlyClientNav>
+ }
+ >
+ <Outlet />
+ </ClientLayout>
+ <RoomSettingsRenderer />
+ <ReceiveSelfDeviceVerification />
+ <AutoRestoreBackupOnVerification />
+ </ClientNonUIFeatures>
+ </ClientBindAtoms>
+ </ClientRoomsNotificationPreferences>
</ClientInitStorageAtom>
</ClientRoot>
<AuthRouteThemeManager />
--- /dev/null
+import React, { ReactNode } from 'react';
+import {
+ RoomsNotificationPreferencesProvider,
+ useRoomsNotificationPreferences,
+} from '../../hooks/useRoomsNotificationPreferences';
+
+export function ClientRoomsNotificationPreferences({ children }: { children: ReactNode }) {
+ const preferences = useRoomsNotificationPreferences();
+
+ return (
+ <RoomsNotificationPreferencesProvider value={preferences}>
+ {children}
+ </RoomsNotificationPreferencesProvider>
+ );
+}
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';
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;
useNavToActivePathMapper('direct');
const scrollRef = useRef<HTMLDivElement>(null);
const directs = useDirectRooms();
- const muteChanges = useAtomValue(muteChangesAtom);
- const mutedRooms = muteChanges.added;
+ const notificationPreferences = useRoomsNotificationPreferencesContext();
const roomToUnread = useAtomValue(roomToUnreadAtom);
const selectedRoomId = useSelectedRoom();
showAvatar
direct
linkPath={getDirectRoomPath(getCanonicalAliasOrRoomId(mx, roomId))}
- muted={mutedRooms.includes(roomId)}
+ notificationMode={getRoomNotificationMode(
+ notificationPreferences,
+ room.roomId
+ )}
/>
</VirtualTile>
);
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';
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;
useNavToActivePathMapper('home');
const scrollRef = useRef<HTMLDivElement>(null);
const rooms = useHomeRooms();
- const muteChanges = useAtomValue(muteChangesAtom);
- const mutedRooms = muteChanges.added;
+ const notificationPreferences = useRoomsNotificationPreferencesContext();
const roomToUnread = useAtomValue(roomToUnreadAtom);
const selectedRoomId = useSelectedRoom();
room={room}
selected={selected}
linkPath={getHomeRoomPath(getCanonicalAliasOrRoomId(mx, roomId))}
- muted={mutedRooms.includes(roomId)}
+ notificationMode={getRoomNotificationMode(
+ notificationPreferences,
+ room.roomId
+ )}
/>
</VirtualTile>
);
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';
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;
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);
showAvatar={mDirects.has(roomId)}
direct={mDirects.has(roomId)}
linkPath={getToLink(roomId)}
- muted={mutedRooms.includes(roomId)}
+ notificationMode={getRoomNotificationMode(notificationPreferences, room.roomId)}
/>
</VirtualTile>
);
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';
useBindAllInvitesAtom(mx, allInvitesAtom);
useBindAllRoomsAtom(mx, allRoomsAtom);
useBindRoomToParentsAtom(mx, roomToParentsAtom);
- useBindMutedRoomsAtom(mx, mutedRoomsAtom);
- useBindRoomToUnreadAtom(mx, roomToUnreadAtom, muteChangesAtom);
+ useBindRoomToUnreadAtom(mx, roomToUnreadAtom);
useBindRoomIdToTypingMembersAtom(mx, roomIdToTypingMembersAtom);
};
+++ /dev/null
-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<MuteChanges>({
- added: [],
- removed: [],
-});
-
-const baseMutedRoomsAtom = atom(new Set<string>());
-export const mutedRoomsAtom = atom<Set<string>, [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<IPushRules>()
- ?.global?.override;
- if (overrideRules) {
- const mutedRooms = overrideRules.reduce<string[]>((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]);
-};
import produce from 'immer';
-import { atom, useSetAtom, PrimitiveAtom, useAtomValue } from 'jotai';
+import { atom, useSetAtom } from 'jotai';
import {
IRoomTimelineData,
MatrixClient,
import { ReceiptContent, ReceiptType } from 'matrix-js-sdk/lib/@types/read_receipts';
import { useCallback, useEffect } from 'react';
import {
- MuteChanges,
Membership,
NotificationType,
RoomToUnread,
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 =
| {
}
);
-export const useBindRoomToUnreadAtom = (
- mx: MatrixClient,
- unreadAtom: typeof roomToUnreadAtom,
- muteChangesAtom: PrimitiveAtom<MuteChanges>
-) => {
+export const useBindRoomToUnreadAtom = (mx: MatrixClient, unreadAtom: typeof roomToUnreadAtom) => {
const setUnreadAtom = useSetAtom(unreadAtom);
- const muteChanges = useAtomValue(muteChangesAtom);
+ const roomsNotificationPreferences = useRoomsNotificationPreferencesContext();
useEffect(() => {
setUnreadAtom({
}, [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) });
- });
- muteChanges.added.forEach((roomId) => {
- setUnreadAtom({ type: 'DELETE', roomId });
+ setUnreadAtom({
+ type: 'RESET',
+ unreadInfos: getUnreadInfos(mx),
});
- }, [mx, setUnreadAtom, muteChanges]);
+ }, [mx, setUnreadAtom, roomsNotificationPreferences]);
useEffect(() => {
const handleMembershipChange = (room: Room, membership: string) => {