mirror of
				https://github.com/cinnyapp/cinny.git
				synced 2025-11-04 06:20:28 +03: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,
 | 
			
		||||
  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<HTMLDivElement, RoomNavItemMenuProps>(
 | 
			
		||||
  ({ 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<HTMLDivElement, RoomNavItemMenuProps>(
 | 
			
		|||
              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 }}>
 | 
			
		||||
| 
						 | 
				
			
			@ -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({
 | 
			
		|||
                <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>
 | 
			
		||||
| 
						 | 
				
			
			@ -287,7 +317,11 @@ export function RoomNavItem({
 | 
			
		|||
                  escapeDeactivates: stopPropagation,
 | 
			
		||||
                }}
 | 
			
		||||
              >
 | 
			
		||||
                <RoomNavItemMenu room={room} requestClose={() => setMenuAnchor(undefined)} />
 | 
			
		||||
                <RoomNavItemMenu
 | 
			
		||||
                  room={room}
 | 
			
		||||
                  requestClose={() => setMenuAnchor(undefined)}
 | 
			
		||||
                  notificationMode={notificationMode}
 | 
			
		||||
                />
 | 
			
		||||
              </FocusTrap>
 | 
			
		||||
            }
 | 
			
		||||
          >
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										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 { 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)
 | 
			
		|||
          <>
 | 
			
		||||
            <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 />
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										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 { 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<HTMLDivElement>(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
 | 
			
		||||
                        )}
 | 
			
		||||
                      />
 | 
			
		||||
                    </VirtualTile>
 | 
			
		||||
                  );
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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<HTMLDivElement>(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
 | 
			
		||||
                        )}
 | 
			
		||||
                      />
 | 
			
		||||
                    </VirtualTile>
 | 
			
		||||
                  );
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)}
 | 
			
		||||
                  />
 | 
			
		||||
                </VirtualTile>
 | 
			
		||||
              );
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 { 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<RoomToUnread, [RoomToUnreadAction], undefin
 | 
			
		|||
  }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
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({
 | 
			
		||||
| 
						 | 
				
			
			@ -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) => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue