mirror of
https://github.com/RGBCube/cinny
synced 2025-07-30 16:37:46 +00:00
Add option to change room notification settings (#2281)
This commit is contained in:
parent
074a5e855d
commit
71bfc96b5c
12 changed files with 409 additions and 164 deletions
120
src/app/components/RoomNotificationSwitcher.tsx
Normal file
120
src/app/components/RoomNotificationSwitcher.tsx
Normal file
|
@ -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<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>
|
||||||
|
);
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ import {
|
||||||
Line,
|
Line,
|
||||||
RectCords,
|
RectCords,
|
||||||
Badge,
|
Badge,
|
||||||
|
Spinner,
|
||||||
} from 'folds';
|
} from 'folds';
|
||||||
import { useFocusWithin, useHover } from 'react-aria';
|
import { useFocusWithin, useHover } from 'react-aria';
|
||||||
import FocusTrap from 'focus-trap-react';
|
import FocusTrap from 'focus-trap-react';
|
||||||
|
@ -43,13 +44,19 @@ import { useSetting } from '../../state/hooks/settings';
|
||||||
import { settingsAtom } from '../../state/settings';
|
import { settingsAtom } from '../../state/settings';
|
||||||
import { useOpenRoomSettings } from '../../state/hooks/roomSettings';
|
import { useOpenRoomSettings } from '../../state/hooks/roomSettings';
|
||||||
import { useSpaceOptionally } from '../../hooks/useSpace';
|
import { useSpaceOptionally } from '../../hooks/useSpace';
|
||||||
|
import {
|
||||||
|
getRoomNotificationModeIcon,
|
||||||
|
RoomNotificationMode,
|
||||||
|
} from '../../hooks/useRoomsNotificationPreferences';
|
||||||
|
import { RoomNotificationModeSwitcher } from '../../components/RoomNotificationSwitcher';
|
||||||
|
|
||||||
type RoomNavItemMenuProps = {
|
type RoomNavItemMenuProps = {
|
||||||
room: Room;
|
room: Room;
|
||||||
requestClose: () => void;
|
requestClose: () => void;
|
||||||
|
notificationMode?: RoomNotificationMode;
|
||||||
};
|
};
|
||||||
const RoomNavItemMenu = forwardRef<HTMLDivElement, RoomNavItemMenuProps>(
|
const RoomNavItemMenu = forwardRef<HTMLDivElement, RoomNavItemMenuProps>(
|
||||||
({ room, requestClose }, ref) => {
|
({ room, requestClose, notificationMode }, ref) => {
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const [hideActivity] = useSetting(settingsAtom, 'hideActivity');
|
const [hideActivity] = useSetting(settingsAtom, 'hideActivity');
|
||||||
const unread = useRoomUnread(room.roomId, roomToUnreadAtom);
|
const unread = useRoomUnread(room.roomId, roomToUnreadAtom);
|
||||||
|
@ -95,6 +102,27 @@ const RoomNavItemMenu = forwardRef<HTMLDivElement, RoomNavItemMenuProps>(
|
||||||
Mark as Read
|
Mark as Read
|
||||||
</Text>
|
</Text>
|
||||||
</MenuItem>
|
</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>
|
</Box>
|
||||||
<Line variant="Surface" size="300" />
|
<Line variant="Surface" size="300" />
|
||||||
<Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
|
<Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
|
||||||
|
@ -170,7 +198,7 @@ type RoomNavItemProps = {
|
||||||
room: Room;
|
room: Room;
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
linkPath: string;
|
linkPath: string;
|
||||||
muted?: boolean;
|
notificationMode?: RoomNotificationMode;
|
||||||
showAvatar?: boolean;
|
showAvatar?: boolean;
|
||||||
direct?: boolean;
|
direct?: boolean;
|
||||||
};
|
};
|
||||||
|
@ -179,7 +207,7 @@ export function RoomNavItem({
|
||||||
selected,
|
selected,
|
||||||
showAvatar,
|
showAvatar,
|
||||||
direct,
|
direct,
|
||||||
muted,
|
notificationMode,
|
||||||
linkPath,
|
linkPath,
|
||||||
}: RoomNavItemProps) {
|
}: RoomNavItemProps) {
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
|
@ -263,7 +291,9 @@ export function RoomNavItem({
|
||||||
<UnreadBadge highlight={unread.highlight > 0} count={unread.total} />
|
<UnreadBadge highlight={unread.highlight > 0} count={unread.total} />
|
||||||
</UnreadBadgeCenter>
|
</UnreadBadgeCenter>
|
||||||
)}
|
)}
|
||||||
{muted && !optionsVisible && <Icon size="50" src={Icons.BellMute} />}
|
{!optionsVisible && notificationMode !== RoomNotificationMode.Unset && (
|
||||||
|
<Icon size="50" src={getRoomNotificationModeIcon(notificationMode)} />
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</NavItemContent>
|
</NavItemContent>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
@ -287,7 +317,11 @@ export function RoomNavItem({
|
||||||
escapeDeactivates: stopPropagation,
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<RoomNavItemMenu room={room} requestClose={() => setMenuAnchor(undefined)} />
|
<RoomNavItemMenu
|
||||||
|
room={room}
|
||||||
|
requestClose={() => setMenuAnchor(undefined)}
|
||||||
|
notificationMode={notificationMode}
|
||||||
|
/>
|
||||||
</FocusTrap>
|
</FocusTrap>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
|
@ -7,6 +7,19 @@ export enum NotificationMode {
|
||||||
NotifyLoud = 'NotifyLoud',
|
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 = {
|
export type NotificationModeOptions = {
|
||||||
soundValue?: string;
|
soundValue?: string;
|
||||||
highlight?: boolean;
|
highlight?: boolean;
|
||||||
|
@ -49,18 +62,7 @@ export const useNotificationModeActions = (
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useNotificationActionsMode = (actions: PushRuleAction[]): NotificationMode => {
|
export const useNotificationActionsMode = (actions: PushRuleAction[]): NotificationMode => {
|
||||||
const mode: NotificationMode = useMemo(() => {
|
const mode: NotificationMode = useMemo(() => getNotificationMode(actions), [actions]);
|
||||||
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]);
|
|
||||||
|
|
||||||
return mode;
|
return mode;
|
||||||
};
|
};
|
||||||
|
|
169
src/app/hooks/useRoomsNotificationPreferences.ts
Normal file
169
src/app/hooks/useRoomsNotificationPreferences.ts
Normal file
|
@ -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<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,
|
||||||
|
};
|
||||||
|
};
|
|
@ -59,6 +59,7 @@ import { AuthRouteThemeManager, UnAuthRouteThemeManager } from './ThemeManager';
|
||||||
import { ReceiveSelfDeviceVerification } from '../components/DeviceVerification';
|
import { ReceiveSelfDeviceVerification } from '../components/DeviceVerification';
|
||||||
import { AutoRestoreBackupOnVerification } from '../components/BackupRestore';
|
import { AutoRestoreBackupOnVerification } from '../components/BackupRestore';
|
||||||
import { RoomSettingsRenderer } from '../features/room-settings';
|
import { RoomSettingsRenderer } from '../features/room-settings';
|
||||||
|
import { ClientRoomsNotificationPreferences } from './client/ClientRoomsNotificationPreferences';
|
||||||
|
|
||||||
export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize) => {
|
export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize) => {
|
||||||
const { hashRouter } = clientConfig;
|
const { hashRouter } = clientConfig;
|
||||||
|
@ -111,22 +112,24 @@ export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize)
|
||||||
<>
|
<>
|
||||||
<ClientRoot>
|
<ClientRoot>
|
||||||
<ClientInitStorageAtom>
|
<ClientInitStorageAtom>
|
||||||
<ClientBindAtoms>
|
<ClientRoomsNotificationPreferences>
|
||||||
<ClientNonUIFeatures>
|
<ClientBindAtoms>
|
||||||
<ClientLayout
|
<ClientNonUIFeatures>
|
||||||
nav={
|
<ClientLayout
|
||||||
<MobileFriendlyClientNav>
|
nav={
|
||||||
<SidebarNav />
|
<MobileFriendlyClientNav>
|
||||||
</MobileFriendlyClientNav>
|
<SidebarNav />
|
||||||
}
|
</MobileFriendlyClientNav>
|
||||||
>
|
}
|
||||||
<Outlet />
|
>
|
||||||
</ClientLayout>
|
<Outlet />
|
||||||
<RoomSettingsRenderer />
|
</ClientLayout>
|
||||||
<ReceiveSelfDeviceVerification />
|
<RoomSettingsRenderer />
|
||||||
<AutoRestoreBackupOnVerification />
|
<ReceiveSelfDeviceVerification />
|
||||||
</ClientNonUIFeatures>
|
<AutoRestoreBackupOnVerification />
|
||||||
</ClientBindAtoms>
|
</ClientNonUIFeatures>
|
||||||
|
</ClientBindAtoms>
|
||||||
|
</ClientRoomsNotificationPreferences>
|
||||||
</ClientInitStorageAtom>
|
</ClientInitStorageAtom>
|
||||||
</ClientRoot>
|
</ClientRoot>
|
||||||
<AuthRouteThemeManager />
|
<AuthRouteThemeManager />
|
||||||
|
|
15
src/app/pages/client/ClientRoomsNotificationPreferences.tsx
Normal file
15
src/app/pages/client/ClientRoomsNotificationPreferences.tsx
Normal file
|
@ -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 (
|
||||||
|
<RoomsNotificationPreferencesProvider value={preferences}>
|
||||||
|
{children}
|
||||||
|
</RoomsNotificationPreferencesProvider>
|
||||||
|
);
|
||||||
|
}
|
|
@ -33,7 +33,6 @@ import { getCanonicalAliasOrRoomId } from '../../../utils/matrix';
|
||||||
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
|
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
|
||||||
import { VirtualTile } from '../../../components/virtualizer';
|
import { VirtualTile } from '../../../components/virtualizer';
|
||||||
import { RoomNavCategoryButton, RoomNavItem } from '../../../features/room-nav';
|
import { RoomNavCategoryButton, RoomNavItem } from '../../../features/room-nav';
|
||||||
import { muteChangesAtom } from '../../../state/room-list/mutedRoomList';
|
|
||||||
import { makeNavCategoryId } from '../../../state/closedNavCategories';
|
import { makeNavCategoryId } from '../../../state/closedNavCategories';
|
||||||
import { roomToUnreadAtom } from '../../../state/room/roomToUnread';
|
import { roomToUnreadAtom } from '../../../state/room/roomToUnread';
|
||||||
import { useCategoryHandler } from '../../../hooks/useCategoryHandler';
|
import { useCategoryHandler } from '../../../hooks/useCategoryHandler';
|
||||||
|
@ -47,6 +46,10 @@ import { markAsRead } from '../../../../client/action/notifications';
|
||||||
import { stopPropagation } from '../../../utils/keyboard';
|
import { stopPropagation } from '../../../utils/keyboard';
|
||||||
import { useSetting } from '../../../state/hooks/settings';
|
import { useSetting } from '../../../state/hooks/settings';
|
||||||
import { settingsAtom } from '../../../state/settings';
|
import { settingsAtom } from '../../../state/settings';
|
||||||
|
import {
|
||||||
|
getRoomNotificationMode,
|
||||||
|
useRoomsNotificationPreferencesContext,
|
||||||
|
} from '../../../hooks/useRoomsNotificationPreferences';
|
||||||
|
|
||||||
type DirectMenuProps = {
|
type DirectMenuProps = {
|
||||||
requestClose: () => void;
|
requestClose: () => void;
|
||||||
|
@ -167,8 +170,7 @@ export function Direct() {
|
||||||
useNavToActivePathMapper('direct');
|
useNavToActivePathMapper('direct');
|
||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
const directs = useDirectRooms();
|
const directs = useDirectRooms();
|
||||||
const muteChanges = useAtomValue(muteChangesAtom);
|
const notificationPreferences = useRoomsNotificationPreferencesContext();
|
||||||
const mutedRooms = muteChanges.added;
|
|
||||||
const roomToUnread = useAtomValue(roomToUnreadAtom);
|
const roomToUnread = useAtomValue(roomToUnreadAtom);
|
||||||
|
|
||||||
const selectedRoomId = useSelectedRoom();
|
const selectedRoomId = useSelectedRoom();
|
||||||
|
@ -254,7 +256,10 @@ export function Direct() {
|
||||||
showAvatar
|
showAvatar
|
||||||
direct
|
direct
|
||||||
linkPath={getDirectRoomPath(getCanonicalAliasOrRoomId(mx, roomId))}
|
linkPath={getDirectRoomPath(getCanonicalAliasOrRoomId(mx, roomId))}
|
||||||
muted={mutedRooms.includes(roomId)}
|
notificationMode={getRoomNotificationMode(
|
||||||
|
notificationPreferences,
|
||||||
|
room.roomId
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</VirtualTile>
|
</VirtualTile>
|
||||||
);
|
);
|
||||||
|
|
|
@ -37,7 +37,6 @@ import { useHomeRooms } from './useHomeRooms';
|
||||||
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
||||||
import { VirtualTile } from '../../../components/virtualizer';
|
import { VirtualTile } from '../../../components/virtualizer';
|
||||||
import { RoomNavCategoryButton, RoomNavItem } from '../../../features/room-nav';
|
import { RoomNavCategoryButton, RoomNavItem } from '../../../features/room-nav';
|
||||||
import { muteChangesAtom } from '../../../state/room-list/mutedRoomList';
|
|
||||||
import { makeNavCategoryId } from '../../../state/closedNavCategories';
|
import { makeNavCategoryId } from '../../../state/closedNavCategories';
|
||||||
import { roomToUnreadAtom } from '../../../state/room/roomToUnread';
|
import { roomToUnreadAtom } from '../../../state/room/roomToUnread';
|
||||||
import { useCategoryHandler } from '../../../hooks/useCategoryHandler';
|
import { useCategoryHandler } from '../../../hooks/useCategoryHandler';
|
||||||
|
@ -50,6 +49,10 @@ import { useClosedNavCategoriesAtom } from '../../../state/hooks/closedNavCatego
|
||||||
import { stopPropagation } from '../../../utils/keyboard';
|
import { stopPropagation } from '../../../utils/keyboard';
|
||||||
import { useSetting } from '../../../state/hooks/settings';
|
import { useSetting } from '../../../state/hooks/settings';
|
||||||
import { settingsAtom } from '../../../state/settings';
|
import { settingsAtom } from '../../../state/settings';
|
||||||
|
import {
|
||||||
|
getRoomNotificationMode,
|
||||||
|
useRoomsNotificationPreferencesContext,
|
||||||
|
} from '../../../hooks/useRoomsNotificationPreferences';
|
||||||
|
|
||||||
type HomeMenuProps = {
|
type HomeMenuProps = {
|
||||||
requestClose: () => void;
|
requestClose: () => void;
|
||||||
|
@ -199,8 +202,7 @@ export function Home() {
|
||||||
useNavToActivePathMapper('home');
|
useNavToActivePathMapper('home');
|
||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
const rooms = useHomeRooms();
|
const rooms = useHomeRooms();
|
||||||
const muteChanges = useAtomValue(muteChangesAtom);
|
const notificationPreferences = useRoomsNotificationPreferencesContext();
|
||||||
const mutedRooms = muteChanges.added;
|
|
||||||
const roomToUnread = useAtomValue(roomToUnreadAtom);
|
const roomToUnread = useAtomValue(roomToUnreadAtom);
|
||||||
|
|
||||||
const selectedRoomId = useSelectedRoom();
|
const selectedRoomId = useSelectedRoom();
|
||||||
|
@ -321,7 +323,10 @@ export function Home() {
|
||||||
room={room}
|
room={room}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
linkPath={getHomeRoomPath(getCanonicalAliasOrRoomId(mx, roomId))}
|
linkPath={getHomeRoomPath(getCanonicalAliasOrRoomId(mx, roomId))}
|
||||||
muted={mutedRooms.includes(roomId)}
|
notificationMode={getRoomNotificationMode(
|
||||||
|
notificationPreferences,
|
||||||
|
room.roomId
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</VirtualTile>
|
</VirtualTile>
|
||||||
);
|
);
|
||||||
|
|
|
@ -45,7 +45,6 @@ import {
|
||||||
import { useSpace } from '../../../hooks/useSpace';
|
import { useSpace } from '../../../hooks/useSpace';
|
||||||
import { VirtualTile } from '../../../components/virtualizer';
|
import { VirtualTile } from '../../../components/virtualizer';
|
||||||
import { RoomNavCategoryButton, RoomNavItem } from '../../../features/room-nav';
|
import { RoomNavCategoryButton, RoomNavItem } from '../../../features/room-nav';
|
||||||
import { muteChangesAtom } from '../../../state/room-list/mutedRoomList';
|
|
||||||
import { makeNavCategoryId } from '../../../state/closedNavCategories';
|
import { makeNavCategoryId } from '../../../state/closedNavCategories';
|
||||||
import { roomToUnreadAtom } from '../../../state/room/roomToUnread';
|
import { roomToUnreadAtom } from '../../../state/room/roomToUnread';
|
||||||
import { useCategoryHandler } from '../../../hooks/useCategoryHandler';
|
import { useCategoryHandler } from '../../../hooks/useCategoryHandler';
|
||||||
|
@ -71,6 +70,10 @@ import { getMatrixToRoom } from '../../../plugins/matrix-to';
|
||||||
import { getViaServers } from '../../../plugins/via-servers';
|
import { getViaServers } from '../../../plugins/via-servers';
|
||||||
import { useSetting } from '../../../state/hooks/settings';
|
import { useSetting } from '../../../state/hooks/settings';
|
||||||
import { settingsAtom } from '../../../state/settings';
|
import { settingsAtom } from '../../../state/settings';
|
||||||
|
import {
|
||||||
|
getRoomNotificationMode,
|
||||||
|
useRoomsNotificationPreferencesContext,
|
||||||
|
} from '../../../hooks/useRoomsNotificationPreferences';
|
||||||
|
|
||||||
type SpaceMenuProps = {
|
type SpaceMenuProps = {
|
||||||
room: Room;
|
room: Room;
|
||||||
|
@ -269,8 +272,7 @@ export function Space() {
|
||||||
const roomToUnread = useAtomValue(roomToUnreadAtom);
|
const roomToUnread = useAtomValue(roomToUnreadAtom);
|
||||||
const allRooms = useAtomValue(allRoomsAtom);
|
const allRooms = useAtomValue(allRoomsAtom);
|
||||||
const allJoinedRooms = useMemo(() => new Set(allRooms), [allRooms]);
|
const allJoinedRooms = useMemo(() => new Set(allRooms), [allRooms]);
|
||||||
const muteChanges = useAtomValue(muteChangesAtom);
|
const notificationPreferences = useRoomsNotificationPreferencesContext();
|
||||||
const mutedRooms = muteChanges.added;
|
|
||||||
|
|
||||||
const selectedRoomId = useSelectedRoom();
|
const selectedRoomId = useSelectedRoom();
|
||||||
const lobbySelected = useSpaceLobbySelected(spaceIdOrAlias);
|
const lobbySelected = useSpaceLobbySelected(spaceIdOrAlias);
|
||||||
|
@ -404,7 +406,7 @@ export function Space() {
|
||||||
showAvatar={mDirects.has(roomId)}
|
showAvatar={mDirects.has(roomId)}
|
||||||
direct={mDirects.has(roomId)}
|
direct={mDirects.has(roomId)}
|
||||||
linkPath={getToLink(roomId)}
|
linkPath={getToLink(roomId)}
|
||||||
muted={mutedRooms.includes(roomId)}
|
notificationMode={getRoomNotificationMode(notificationPreferences, room.roomId)}
|
||||||
/>
|
/>
|
||||||
</VirtualTile>
|
</VirtualTile>
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { MatrixClient } from 'matrix-js-sdk';
|
||||||
import { allInvitesAtom, useBindAllInvitesAtom } from '../room-list/inviteList';
|
import { allInvitesAtom, useBindAllInvitesAtom } from '../room-list/inviteList';
|
||||||
import { allRoomsAtom, useBindAllRoomsAtom } from '../room-list/roomList';
|
import { allRoomsAtom, useBindAllRoomsAtom } from '../room-list/roomList';
|
||||||
import { mDirectAtom, useBindMDirectAtom } from '../mDirectList';
|
import { mDirectAtom, useBindMDirectAtom } from '../mDirectList';
|
||||||
import { muteChangesAtom, mutedRoomsAtom, useBindMutedRoomsAtom } from '../room-list/mutedRoomList';
|
|
||||||
import { roomToUnreadAtom, useBindRoomToUnreadAtom } from '../room/roomToUnread';
|
import { roomToUnreadAtom, useBindRoomToUnreadAtom } from '../room/roomToUnread';
|
||||||
import { roomToParentsAtom, useBindRoomToParentsAtom } from '../room/roomToParents';
|
import { roomToParentsAtom, useBindRoomToParentsAtom } from '../room/roomToParents';
|
||||||
import { roomIdToTypingMembersAtom, useBindRoomIdToTypingMembersAtom } from '../typingMembers';
|
import { roomIdToTypingMembersAtom, useBindRoomIdToTypingMembersAtom } from '../typingMembers';
|
||||||
|
@ -12,8 +11,7 @@ export const useBindAtoms = (mx: MatrixClient) => {
|
||||||
useBindAllInvitesAtom(mx, allInvitesAtom);
|
useBindAllInvitesAtom(mx, allInvitesAtom);
|
||||||
useBindAllRoomsAtom(mx, allRoomsAtom);
|
useBindAllRoomsAtom(mx, allRoomsAtom);
|
||||||
useBindRoomToParentsAtom(mx, roomToParentsAtom);
|
useBindRoomToParentsAtom(mx, roomToParentsAtom);
|
||||||
useBindMutedRoomsAtom(mx, mutedRoomsAtom);
|
useBindRoomToUnreadAtom(mx, roomToUnreadAtom);
|
||||||
useBindRoomToUnreadAtom(mx, roomToUnreadAtom, muteChangesAtom);
|
|
||||||
|
|
||||||
useBindRoomIdToTypingMembersAtom(mx, roomIdToTypingMembersAtom);
|
useBindRoomIdToTypingMembersAtom(mx, roomIdToTypingMembersAtom);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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<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]);
|
|
||||||
};
|
|
|
@ -1,5 +1,5 @@
|
||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
import { atom, useSetAtom, PrimitiveAtom, useAtomValue } from 'jotai';
|
import { atom, useSetAtom } from 'jotai';
|
||||||
import {
|
import {
|
||||||
IRoomTimelineData,
|
IRoomTimelineData,
|
||||||
MatrixClient,
|
MatrixClient,
|
||||||
|
@ -11,7 +11,6 @@ import {
|
||||||
import { ReceiptContent, ReceiptType } from 'matrix-js-sdk/lib/@types/read_receipts';
|
import { ReceiptContent, ReceiptType } from 'matrix-js-sdk/lib/@types/read_receipts';
|
||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
import {
|
import {
|
||||||
MuteChanges,
|
|
||||||
Membership,
|
Membership,
|
||||||
NotificationType,
|
NotificationType,
|
||||||
RoomToUnread,
|
RoomToUnread,
|
||||||
|
@ -25,11 +24,11 @@ import {
|
||||||
getUnreadInfo,
|
getUnreadInfo,
|
||||||
getUnreadInfos,
|
getUnreadInfos,
|
||||||
isNotificationEvent,
|
isNotificationEvent,
|
||||||
roomHaveUnread,
|
|
||||||
} from '../../utils/room';
|
} from '../../utils/room';
|
||||||
import { roomToParentsAtom } from './roomToParents';
|
import { roomToParentsAtom } from './roomToParents';
|
||||||
import { useStateEventCallback } from '../../hooks/useStateEventCallback';
|
import { useStateEventCallback } from '../../hooks/useStateEventCallback';
|
||||||
import { useSyncState } from '../../hooks/useSyncState';
|
import { useSyncState } from '../../hooks/useSyncState';
|
||||||
|
import { useRoomsNotificationPreferencesContext } from '../../hooks/useRoomsNotificationPreferences';
|
||||||
|
|
||||||
export type RoomToUnreadAction =
|
export type RoomToUnreadAction =
|
||||||
| {
|
| {
|
||||||
|
@ -167,13 +166,9 @@ export const roomToUnreadAtom = atom<RoomToUnread, [RoomToUnreadAction], undefin
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export const useBindRoomToUnreadAtom = (
|
export const useBindRoomToUnreadAtom = (mx: MatrixClient, unreadAtom: typeof roomToUnreadAtom) => {
|
||||||
mx: MatrixClient,
|
|
||||||
unreadAtom: typeof roomToUnreadAtom,
|
|
||||||
muteChangesAtom: PrimitiveAtom<MuteChanges>
|
|
||||||
) => {
|
|
||||||
const setUnreadAtom = useSetAtom(unreadAtom);
|
const setUnreadAtom = useSetAtom(unreadAtom);
|
||||||
const muteChanges = useAtomValue(muteChangesAtom);
|
const roomsNotificationPreferences = useRoomsNotificationPreferencesContext();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setUnreadAtom({
|
setUnreadAtom({
|
||||||
|
@ -249,16 +244,11 @@ export const useBindRoomToUnreadAtom = (
|
||||||
}, [mx, setUnreadAtom]);
|
}, [mx, setUnreadAtom]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
muteChanges.removed.forEach((roomId) => {
|
setUnreadAtom({
|
||||||
const room = mx.getRoom(roomId);
|
type: 'RESET',
|
||||||
if (!room) return;
|
unreadInfos: getUnreadInfos(mx),
|
||||||
if (!roomHaveUnread(mx, room)) return;
|
|
||||||
setUnreadAtom({ type: 'PUT', unreadInfo: getUnreadInfo(room) });
|
|
||||||
});
|
});
|
||||||
muteChanges.added.forEach((roomId) => {
|
}, [mx, setUnreadAtom, roomsNotificationPreferences]);
|
||||||
setUnreadAtom({ type: 'DELETE', roomId });
|
|
||||||
});
|
|
||||||
}, [mx, setUnreadAtom, muteChanges]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleMembershipChange = (room: Room, membership: string) => {
|
const handleMembershipChange = (room: Room, membership: string) => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue