type: cons.notifs.MUTE,
}];
-function getNotifType(roomId) {
- const mx = initMatrix.matrixClient;
- const pushRule = mx.getRoomPushRule('global', roomId);
-
- if (typeof pushRule === 'undefined') {
- const overridePushRules = mx.getAccountData('m.push_rules')?.getContent()?.global?.override;
- if (typeof overridePushRules === 'undefined') return 0;
-
- const isMuteOverride = overridePushRules.find((rule) => (
- rule.rule_id === roomId
- && rule.actions[0] === 'dont_notify'
- && rule.conditions[0].kind === 'event_match'
- ));
-
- return isMuteOverride ? cons.notifs.MUTE : cons.notifs.DEFAULT;
- }
- if (pushRule.actions[0] === 'notify') return cons.notifs.ALL_MESSAGES;
- return cons.notifs.MENTIONS_AND_KEYWORDS;
-}
-
function setRoomNotifType(roomId, newType) {
const mx = initMatrix.matrixClient;
+ const { notifications } = initMatrix;
const roomPushRule = mx.getRoomPushRule('global', roomId);
const promises = [];
return promises;
}
- const oldState = getNotifType(roomId);
+ const oldState = notifications.getNotiType(roomId);
if (oldState === cons.notifs.MUTE) {
promises.push(mx.deletePushRule('global', 'override', roomId));
}
}
function useNotifications(roomId) {
- const [activeType, setActiveType] = useState(getNotifType(roomId));
- useEffect(() => setActiveType(getNotifType(roomId)), [roomId]);
+ const { notifications } = initMatrix;
+ const [activeType, setActiveType] = useState(notifications.getNotiType(roomId));
+ useEffect(() => setActiveType(notifications.getNotiType(roomId)), [roomId]);
const setNotification = useCallback((item) => {
if (item.type === activeType.type) return;
import { blurOnBubbling } from '../../atoms/button/script';
function RoomSelectorWrapper({
- isSelected, isUnread, onClick,
+ isSelected, isMuted, isUnread, onClick,
content, options, onContextMenu,
}) {
- let myClass = isUnread ? ' room-selector--unread' : '';
- myClass += isSelected ? ' room-selector--selected' : '';
+ const classes = ['room-selector'];
+ if (isMuted) classes.push('room-selector--muted');
+ if (isUnread) classes.push('room-selector--unread');
+ if (isSelected) classes.push('room-selector--selected');
+
return (
- <div className={`room-selector${myClass}`}>
+ <div className={classes.join(' ')}>
<button
className="room-selector__content"
type="button"
);
}
RoomSelectorWrapper.defaultProps = {
+ isMuted: false,
options: null,
onContextMenu: null,
};
RoomSelectorWrapper.propTypes = {
isSelected: PropTypes.bool.isRequired,
+ isMuted: PropTypes.bool,
isUnread: PropTypes.bool.isRequired,
onClick: PropTypes.func.isRequired,
content: PropTypes.node.isRequired,
function RoomSelector({
name, parentName, roomId, imageSrc, iconSrc,
- isSelected, isUnread, notificationCount, isAlert,
+ isSelected, isMuted, isUnread, notificationCount, isAlert,
options, onClick, onContextMenu,
}) {
return (
<RoomSelectorWrapper
isSelected={isSelected}
+ isMuted={isMuted}
isUnread={isUnread}
content={(
<>
isSelected: false,
imageSrc: null,
iconSrc: null,
+ isMuted: false,
options: null,
onContextMenu: null,
};
imageSrc: PropTypes.string,
iconSrc: PropTypes.string,
isSelected: PropTypes.bool,
+ isMuted: PropTypes.bool,
isUnread: PropTypes.bool.isRequired,
notificationCount: PropTypes.oneOfType([
PropTypes.string,
border-radius: var(--bo-radius);
cursor: pointer;
+ &--muted {
+ opacity: 0.6;
+ }
+
&--unread {
.room-selector__content > .text {
color: var(--tc-surface-high);
navigation.on(cons.events.navigation.ROOM_SELECTED, selectorChanged);
notifications.on(cons.events.notifications.NOTI_CHANGED, notiChanged);
+ notifications.on(cons.events.notifications.MUTE_TOGGLED, notiChanged);
return () => {
navigation.removeListener(cons.events.navigation.ROOM_SELECTED, selectorChanged);
notifications.removeListener(cons.events.notifications.NOTI_CHANGED, notiChanged);
+ notifications.removeListener(cons.events.notifications.MUTE_TOGGLED, notiChanged);
};
}, []);
navigation.on(cons.events.navigation.ROOM_SELECTED, selectorChanged);
notifications.on(cons.events.notifications.NOTI_CHANGED, notiChanged);
+ notifications.on(cons.events.notifications.MUTE_TOGGLED, notiChanged);
return () => {
navigation.removeListener(cons.events.navigation.ROOM_SELECTED, selectorChanged);
notifications.removeListener(cons.events.notifications.NOTI_CHANGED, notiChanged);
+ notifications.removeListener(cons.events.notifications.MUTE_TOGGLED, notiChanged);
};
}, []);
import './RoomsCategory.scss';
import initMatrix from '../../../client/initMatrix';
-import { selectSpace, selectRoom,openReusableContextMenu } from '../../../client/action/navigation';
+import { selectSpace, selectRoom, openReusableContextMenu } from '../../../client/action/navigation';
import { getEventCords } from '../../../util/common';
import Text from '../../atoms/text/Text';
import PropTypes from 'prop-types';
import initMatrix from '../../../client/initMatrix';
+import cons from '../../../client/state/cons';
import navigation from '../../../client/state/navigation';
import { openReusableContextMenu } from '../../../client/action/navigation';
import { getEventCords, abbreviateNumber } from '../../../util/common';
const mx = initMatrix.matrixClient;
const noti = initMatrix.notifications;
const room = mx.getRoom(roomId);
+
let imageSrc = room.getAvatarFallbackMember()?.getAvatarUrl(mx.baseUrl, 24, 24, 'crop') || null;
if (imageSrc === null) imageSrc = room.getAvatarUrl(mx.baseUrl, 24, 24, 'crop') || null;
+ const isMuted = noti.getNotiType(roomId) === cons.notifs.MUTE;
+
const [, forceUpdate] = useForceUpdate();
useEffect(() => {
imageSrc={isDM ? imageSrc : null}
iconSrc={isDM ? null : joinRuleToIconSrc(room.getJoinRule(), room.isSpaceRoom())}
isSelected={navigation.selectedRoomId === roomId}
- isUnread={noti.hasNoti(roomId)}
+ isMuted={isMuted}
+ isUnread={!isMuted && noti.hasNoti(roomId)}
notificationCount={abbreviateNumber(noti.getTotalNoti(roomId))}
isAlert={noti.getHighlightNoti(roomId) !== 0}
onClick={onClick}
return true;
}
+function isMutedRule(rule) {
+ return rule.actions[0] === 'dont_notify' && rule.conditions[0].kind === 'event_match';
+}
+
+function findMutedRule(overrideRules, roomId) {
+ return overrideRules.find((rule) => (
+ rule.rule_id === roomId
+ && isMutedRule(rule)
+ ));
+}
+
class Notifications extends EventEmitter {
constructor(roomList) {
super();
_initNoti() {
const addNoti = (roomId) => {
const room = this.matrixClient.getRoom(roomId);
+ if (this.getNotiType(room.roomId) === cons.notifs.MUTE) return;
if (this.doesRoomHaveUnread(room) === false) return;
+
const total = room.getUnreadNotificationCount('total');
const highlight = room.getUnreadNotificationCount('highlight');
this._setNoti(room.roomId, total ?? 0, highlight ?? 0);
return true;
}
+ getNotiType(roomId) {
+ const mx = this.matrixClient;
+ const pushRule = mx.getRoomPushRule('global', roomId);
+
+ if (pushRule === undefined) {
+ const overrideRules = mx.getAccountData('m.push_rules')?.getContent()?.global?.override;
+ if (overrideRules === undefined) return cons.notifs.DEFAULT;
+
+ const isMuted = findMutedRule(overrideRules, roomId);
+
+ return isMuted ? cons.notifs.MUTE : cons.notifs.DEFAULT;
+ }
+ if (pushRule.actions[0] === 'notify') return cons.notifs.ALL_MESSAGES;
+ return cons.notifs.MENTIONS_AND_KEYWORDS;
+ }
+
getNoti(roomId) {
return this.roomIdToNoti.get(roomId) || { total: 0, highlight: 0, from: null };
}
this.matrixClient.on('Room.timeline', (mEvent, room) => {
if (room.isSpaceRoom()) return;
if (!isNotifEvent(mEvent)) return;
+
const liveEvents = room.getLiveTimeline().getEvents();
const lastTimelineEvent = liveEvents[liveEvents.length - 1];
const total = room.getUnreadNotificationCount('total');
const highlight = room.getUnreadNotificationCount('highlight');
+ if (this.getNotiType(room.roomId) === cons.notifs.MUTE) {
+ this.deleteNoti(room.roomId, total ?? 0, highlight ?? 0);
+ return;
+ }
+
this._setNoti(room.roomId, total ?? 0, highlight ?? 0);
if (this.matrixClient.getSyncState() === 'SYNCING') {
}
});
+ this.matrixClient.on('accountData', (mEvent, oldMEvent) => {
+ if (mEvent.getType() === 'm.push_rules') {
+ const override = mEvent?.getContent()?.global?.override;
+ const oldOverride = oldMEvent?.getContent()?.global?.override;
+ if (!override || !oldOverride) return;
+
+ const isMuteToggled = (rule, otherOverride) => {
+ const roomId = rule.rule_id;
+ const room = this.matrixClient.getRoom(roomId);
+ if (room === null) return false;
+ if (room.isSpaceRoom()) return false;
+
+ 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));
+
+ mutedRules.forEach((rule) => {
+ this.emit(cons.events.notifications.MUTE_TOGGLED, rule.rule_id, true);
+ this.deleteNoti(rule.rule_id);
+ });
+ unMutedRules.forEach((rule) => {
+ this.emit(cons.events.notifications.MUTE_TOGGLED, rule.rule_id, false);
+ const room = this.matrixClient.getRoom(rule.rule_id);
+ if (!this.doesRoomHaveUnread(room)) return;
+ const total = room.getUnreadNotificationCount('total');
+ const highlight = room.getUnreadNotificationCount('highlight');
+ this._setNoti(room.roomId, total ?? 0, highlight ?? 0);
+ });
+ }
+ });
+
this.matrixClient.on('Room.receipt', (mEvent, room) => {
if (mEvent.getType() === 'm.receipt') {
if (room.isSpaceRoom()) return;
notifications: {
NOTI_CHANGED: 'NOTI_CHANGED',
FULL_READ: 'FULL_READ',
+ MUTE_TOGGLED: 'MUTE_TOGGLED',
},
roomTimeline: {
READY: 'READY',