You've already forked matrix-react-sdk
mirror of
https://github.com/matrix-org/matrix-react-sdk.git
synced 2025-07-28 15:22:05 +03:00
Add labs flag for Threads Activity Centre (#12137)
* Add `Thread Activity centre` labs flag * Rename translation string * Update supportedLevels * Fix labs subsection test * Update Threads Activity Centre label * Make threads activity centre labs flag split out unread counts Just shows notif & unread counts for main thread if the TAC is enabled. * Fix tests * Simpler fix * Pass in & cache the status of the TAC labs flag * Pass includeThreads as setting to doesRoomHaveUnreadMessages too * Fix tests --------- Co-authored-by: David Baker <dbkr@users.noreply.github.com>
This commit is contained in:
@ -80,10 +80,19 @@ export function setRoomNotifsState(client: MatrixClient, roomId: string, newStat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUnreadNotificationCount(room: Room, type: NotificationCountType, threadId?: string): number {
|
export function getUnreadNotificationCount(
|
||||||
|
room: Room,
|
||||||
|
type: NotificationCountType,
|
||||||
|
includeThreads: boolean,
|
||||||
|
threadId?: string,
|
||||||
|
): number {
|
||||||
|
const getCountShownForRoom = (r: Room, type: NotificationCountType): number => {
|
||||||
|
return includeThreads ? r.getUnreadNotificationCount(type) : r.getRoomUnreadNotificationCount(type);
|
||||||
|
};
|
||||||
|
|
||||||
let notificationCount = !!threadId
|
let notificationCount = !!threadId
|
||||||
? room.getThreadUnreadNotificationCount(threadId, type)
|
? room.getThreadUnreadNotificationCount(threadId, type)
|
||||||
: room.getUnreadNotificationCount(type);
|
: getCountShownForRoom(room, type);
|
||||||
|
|
||||||
// Check notification counts in the old room just in case there's some lost
|
// Check notification counts in the old room just in case there's some lost
|
||||||
// there. We only go one level down to avoid performance issues, and theory
|
// there. We only go one level down to avoid performance issues, and theory
|
||||||
@ -99,7 +108,7 @@ export function getUnreadNotificationCount(room: Room, type: NotificationCountTy
|
|||||||
// notifying the user for unread messages because they would have extreme
|
// notifying the user for unread messages because they would have extreme
|
||||||
// difficulty changing their notification preferences away from "All Messages"
|
// difficulty changing their notification preferences away from "All Messages"
|
||||||
// and "Noisy".
|
// and "Noisy".
|
||||||
notificationCount += oldRoom.getUnreadNotificationCount(NotificationCountType.Highlight);
|
notificationCount += getCountShownForRoom(oldRoom, NotificationCountType.Highlight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,9 +233,18 @@ function isMuteRule(rule: IPushRule): boolean {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an object giving information about the unread state of a room or thread
|
||||||
|
* @param room The room to query, or the room the thread is in
|
||||||
|
* @param threadId The thread to check the unread state of, or undefined to query the main thread
|
||||||
|
* @param includeThreads If threadId is undefined, true to include threads other than the main thread, or
|
||||||
|
* false to exclude them. Ignored if threadId is specified.
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
export function determineUnreadState(
|
export function determineUnreadState(
|
||||||
room?: Room,
|
room?: Room,
|
||||||
threadId?: string,
|
threadId?: string,
|
||||||
|
includeThreads?: boolean,
|
||||||
): { level: NotificationLevel; symbol: string | null; count: number } {
|
): { level: NotificationLevel; symbol: string | null; count: number } {
|
||||||
if (!room) {
|
if (!room) {
|
||||||
return { symbol: null, count: 0, level: NotificationLevel.None };
|
return { symbol: null, count: 0, level: NotificationLevel.None };
|
||||||
@ -248,8 +266,13 @@ export function determineUnreadState(
|
|||||||
return { symbol: null, count: 0, level: NotificationLevel.None };
|
return { symbol: null, count: 0, level: NotificationLevel.None };
|
||||||
}
|
}
|
||||||
|
|
||||||
const redNotifs = getUnreadNotificationCount(room, NotificationCountType.Highlight, threadId);
|
const redNotifs = getUnreadNotificationCount(
|
||||||
const greyNotifs = getUnreadNotificationCount(room, NotificationCountType.Total, threadId);
|
room,
|
||||||
|
NotificationCountType.Highlight,
|
||||||
|
includeThreads ?? false,
|
||||||
|
threadId,
|
||||||
|
);
|
||||||
|
const greyNotifs = getUnreadNotificationCount(room, NotificationCountType.Total, includeThreads ?? false, threadId);
|
||||||
|
|
||||||
const trueCount = greyNotifs || redNotifs;
|
const trueCount = greyNotifs || redNotifs;
|
||||||
if (redNotifs > 0) {
|
if (redNotifs > 0) {
|
||||||
@ -269,7 +292,7 @@ export function determineUnreadState(
|
|||||||
}
|
}
|
||||||
// If the thread does not exist, assume it contains no unreads
|
// If the thread does not exist, assume it contains no unreads
|
||||||
} else {
|
} else {
|
||||||
hasUnread = doesRoomHaveUnreadMessages(room);
|
hasUnread = doesRoomHaveUnreadMessages(room, includeThreads ?? false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -50,14 +50,19 @@ export function eventTriggersUnreadCount(client: MatrixClient, ev: MatrixEvent):
|
|||||||
return haveRendererForEvent(ev, client, false /* hidden messages should never trigger unread counts anyways */);
|
return haveRendererForEvent(ev, client, false /* hidden messages should never trigger unread counts anyways */);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doesRoomHaveUnreadMessages(room: Room): boolean {
|
export function doesRoomHaveUnreadMessages(room: Room, includeThreads: boolean): boolean {
|
||||||
if (SettingsStore.getValue("feature_sliding_sync")) {
|
if (SettingsStore.getValue("feature_sliding_sync")) {
|
||||||
// TODO: https://github.com/vector-im/element-web/issues/23207
|
// TODO: https://github.com/vector-im/element-web/issues/23207
|
||||||
// Sliding Sync doesn't support unread indicator dots (yet...)
|
// Sliding Sync doesn't support unread indicator dots (yet...)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const withTimeline of [room, ...room.getThreads()]) {
|
const toCheck: Array<Room | Thread> = [room];
|
||||||
|
if (includeThreads) {
|
||||||
|
toCheck.push(...room.getThreads());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const withTimeline of toCheck) {
|
||||||
if (doesTimelineHaveUnreadMessages(room, withTimeline.timeline)) {
|
if (doesTimelineHaveUnreadMessages(room, withTimeline.timeline)) {
|
||||||
// We found an unread, so the room is unread
|
// We found an unread, so the room is unread
|
||||||
return true;
|
return true;
|
||||||
|
@ -15,7 +15,7 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { NotificationCountType, Room, Thread, ReceiptType } from "matrix-js-sdk/src/matrix";
|
import { NotificationCountType, Room, Thread, ReceiptType } from "matrix-js-sdk/src/matrix";
|
||||||
import React, { useContext } from "react";
|
import React, { useContext, useMemo } from "react";
|
||||||
import { ReadReceipt } from "matrix-js-sdk/src/models/read-receipt";
|
import { ReadReceipt } from "matrix-js-sdk/src/models/read-receipt";
|
||||||
|
|
||||||
import MatrixClientContext from "../../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../../contexts/MatrixClientContext";
|
||||||
@ -25,6 +25,7 @@ import { determineUnreadState } from "../../../../RoomNotifs";
|
|||||||
import { humanReadableNotificationLevel } from "../../../../stores/notifications/NotificationLevel";
|
import { humanReadableNotificationLevel } from "../../../../stores/notifications/NotificationLevel";
|
||||||
import { doesRoomOrThreadHaveUnreadMessages } from "../../../../Unread";
|
import { doesRoomOrThreadHaveUnreadMessages } from "../../../../Unread";
|
||||||
import BaseTool, { DevtoolsContext, IDevtoolsProps } from "./BaseTool";
|
import BaseTool, { DevtoolsContext, IDevtoolsProps } from "./BaseTool";
|
||||||
|
import SettingsStore from "../../../../settings/SettingsStore";
|
||||||
|
|
||||||
function UserReadUpTo({ target }: { target: ReadReceipt<any, any> }): JSX.Element {
|
function UserReadUpTo({ target }: { target: ReadReceipt<any, any> }): JSX.Element {
|
||||||
const cli = useContext(MatrixClientContext);
|
const cli = useContext(MatrixClientContext);
|
||||||
@ -65,10 +66,12 @@ function UserReadUpTo({ target }: { target: ReadReceipt<any, any> }): JSX.Elemen
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function RoomNotifications({ onBack }: IDevtoolsProps): JSX.Element {
|
export default function RoomNotifications({ onBack }: IDevtoolsProps): JSX.Element {
|
||||||
|
const tacEnabled = useMemo(() => SettingsStore.getValue("threadsActivityCentre"), []);
|
||||||
|
|
||||||
const { room } = useContext(DevtoolsContext);
|
const { room } = useContext(DevtoolsContext);
|
||||||
const cli = useContext(MatrixClientContext);
|
const cli = useContext(MatrixClientContext);
|
||||||
|
|
||||||
const { level, count } = determineUnreadState(room);
|
const { level, count } = determineUnreadState(room, undefined, !tacEnabled);
|
||||||
const [notificationState] = useNotificationState(room);
|
const [notificationState] = useNotificationState(room);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -15,12 +15,13 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { RoomEvent } from "matrix-js-sdk/src/matrix";
|
import { RoomEvent } from "matrix-js-sdk/src/matrix";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
|
||||||
import type { NotificationCount, Room } from "matrix-js-sdk/src/matrix";
|
import type { NotificationCount, Room } from "matrix-js-sdk/src/matrix";
|
||||||
import { determineUnreadState } from "../RoomNotifs";
|
import { determineUnreadState } from "../RoomNotifs";
|
||||||
import { NotificationLevel } from "../stores/notifications/NotificationLevel";
|
import { NotificationLevel } from "../stores/notifications/NotificationLevel";
|
||||||
import { useEventEmitter } from "./useEventEmitter";
|
import { useEventEmitter } from "./useEventEmitter";
|
||||||
|
import SettingsStore from "../settings/SettingsStore";
|
||||||
|
|
||||||
export const useUnreadNotifications = (
|
export const useUnreadNotifications = (
|
||||||
room?: Room,
|
room?: Room,
|
||||||
@ -30,6 +31,8 @@ export const useUnreadNotifications = (
|
|||||||
count: number;
|
count: number;
|
||||||
level: NotificationLevel;
|
level: NotificationLevel;
|
||||||
} => {
|
} => {
|
||||||
|
const tacEnabled = useMemo(() => SettingsStore.getValue("threadsActivityCentre"), []);
|
||||||
|
|
||||||
const [symbol, setSymbol] = useState<string | null>(null);
|
const [symbol, setSymbol] = useState<string | null>(null);
|
||||||
const [count, setCount] = useState<number>(0);
|
const [count, setCount] = useState<number>(0);
|
||||||
const [level, setLevel] = useState<NotificationLevel>(NotificationLevel.None);
|
const [level, setLevel] = useState<NotificationLevel>(NotificationLevel.None);
|
||||||
@ -50,11 +53,11 @@ export const useUnreadNotifications = (
|
|||||||
useEventEmitter(room, RoomEvent.MyMembership, () => updateNotificationState());
|
useEventEmitter(room, RoomEvent.MyMembership, () => updateNotificationState());
|
||||||
|
|
||||||
const updateNotificationState = useCallback(() => {
|
const updateNotificationState = useCallback(() => {
|
||||||
const { symbol, count, level } = determineUnreadState(room, threadId);
|
const { symbol, count, level } = determineUnreadState(room, threadId, !tacEnabled);
|
||||||
setSymbol(symbol);
|
setSymbol(symbol);
|
||||||
setCount(count);
|
setCount(count);
|
||||||
setLevel(level);
|
setLevel(level);
|
||||||
}, [room, threadId]);
|
}, [room, threadId, tacEnabled]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
updateNotificationState();
|
updateNotificationState();
|
||||||
|
@ -1416,6 +1416,7 @@
|
|||||||
"group_rooms": "Rooms",
|
"group_rooms": "Rooms",
|
||||||
"group_spaces": "Spaces",
|
"group_spaces": "Spaces",
|
||||||
"group_themes": "Themes",
|
"group_themes": "Themes",
|
||||||
|
"group_threads": "Threads",
|
||||||
"group_voip": "Voice & Video",
|
"group_voip": "Voice & Video",
|
||||||
"group_widgets": "Widgets",
|
"group_widgets": "Widgets",
|
||||||
"hidebold": "Hide notification dot (only display counters badges)",
|
"hidebold": "Hide notification dot (only display counters badges)",
|
||||||
@ -1459,6 +1460,7 @@
|
|||||||
"sliding_sync_server_no_support": "Your server lacks native support",
|
"sliding_sync_server_no_support": "Your server lacks native support",
|
||||||
"sliding_sync_server_specify_proxy": "Your server lacks native support, you must specify a proxy",
|
"sliding_sync_server_specify_proxy": "Your server lacks native support, you must specify a proxy",
|
||||||
"sliding_sync_server_support": "Your server has native support",
|
"sliding_sync_server_support": "Your server has native support",
|
||||||
|
"threads_activity_centre": "Threads Activity Centre (in development). Currently this just removes thread notification counts from the count total in the room list",
|
||||||
"under_active_development": "Under active development.",
|
"under_active_development": "Under active development.",
|
||||||
"unrealiable_e2e": "Unreliable in encrypted rooms",
|
"unrealiable_e2e": "Unreliable in encrypted rooms",
|
||||||
"video_rooms": "Video rooms",
|
"video_rooms": "Video rooms",
|
||||||
|
@ -81,6 +81,7 @@ export enum LabGroup {
|
|||||||
Spaces,
|
Spaces,
|
||||||
Widgets,
|
Widgets,
|
||||||
Rooms,
|
Rooms,
|
||||||
|
Threads,
|
||||||
VoiceAndVideo,
|
VoiceAndVideo,
|
||||||
Moderation,
|
Moderation,
|
||||||
Analytics,
|
Analytics,
|
||||||
@ -104,6 +105,7 @@ export const labGroupNames: Record<LabGroup, TranslationKey> = {
|
|||||||
[LabGroup.Spaces]: _td("labs|group_spaces"),
|
[LabGroup.Spaces]: _td("labs|group_spaces"),
|
||||||
[LabGroup.Widgets]: _td("labs|group_widgets"),
|
[LabGroup.Widgets]: _td("labs|group_widgets"),
|
||||||
[LabGroup.Rooms]: _td("labs|group_rooms"),
|
[LabGroup.Rooms]: _td("labs|group_rooms"),
|
||||||
|
[LabGroup.Threads]: _td("labs|group_threads"),
|
||||||
[LabGroup.VoiceAndVideo]: _td("labs|group_voip"),
|
[LabGroup.VoiceAndVideo]: _td("labs|group_voip"),
|
||||||
[LabGroup.Moderation]: _td("labs|group_moderation"),
|
[LabGroup.Moderation]: _td("labs|group_moderation"),
|
||||||
[LabGroup.Analytics]: _td("common|analytics"),
|
[LabGroup.Analytics]: _td("common|analytics"),
|
||||||
@ -1113,6 +1115,14 @@ export const SETTINGS: { [setting: string]: ISetting } = {
|
|||||||
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
|
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
|
||||||
default: [],
|
default: [],
|
||||||
},
|
},
|
||||||
|
"threadsActivityCentre": {
|
||||||
|
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||||
|
labsGroup: LabGroup.Threads,
|
||||||
|
controller: new ReloadOnChangeController(),
|
||||||
|
displayName: _td("labs|threads_activity_centre"),
|
||||||
|
default: false,
|
||||||
|
isFeature: true,
|
||||||
|
},
|
||||||
[UIFeature.RoomHistorySettings]: {
|
[UIFeature.RoomHistorySettings]: {
|
||||||
supportedLevels: LEVELS_UI_FEATURE,
|
supportedLevels: LEVELS_UI_FEATURE,
|
||||||
default: true,
|
default: true,
|
||||||
|
@ -25,7 +25,10 @@ import { NotificationState } from "./NotificationState";
|
|||||||
import SettingsStore from "../../settings/SettingsStore";
|
import SettingsStore from "../../settings/SettingsStore";
|
||||||
|
|
||||||
export class RoomNotificationState extends NotificationState implements IDestroyable {
|
export class RoomNotificationState extends NotificationState implements IDestroyable {
|
||||||
public constructor(public readonly room: Room) {
|
public constructor(
|
||||||
|
public readonly room: Room,
|
||||||
|
private includeThreads: boolean,
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
const cli = this.room.client;
|
const cli = this.room.client;
|
||||||
this.room.on(RoomEvent.Receipt, this.handleReadReceipt);
|
this.room.on(RoomEvent.Receipt, this.handleReadReceipt);
|
||||||
@ -90,7 +93,7 @@ export class RoomNotificationState extends NotificationState implements IDestroy
|
|||||||
private updateNotificationState(): void {
|
private updateNotificationState(): void {
|
||||||
const snapshot = this.snapshot();
|
const snapshot = this.snapshot();
|
||||||
|
|
||||||
const { level, symbol, count } = RoomNotifs.determineUnreadState(this.room);
|
const { level, symbol, count } = RoomNotifs.determineUnreadState(this.room, undefined, this.includeThreads);
|
||||||
const muted =
|
const muted =
|
||||||
RoomNotifs.getRoomNotifsState(this.room.client, this.room.roomId) === RoomNotifs.RoomNotifState.Mute;
|
RoomNotifs.getRoomNotifsState(this.room.client, this.room.roomId) === RoomNotifs.RoomNotifState.Mute;
|
||||||
const knocked = SettingsStore.getValue("feature_ask_to_join") && this.room.getMyMembership() === "knock";
|
const knocked = SettingsStore.getValue("feature_ask_to_join") && this.room.getMyMembership() === "knock";
|
||||||
|
@ -42,6 +42,8 @@ export class RoomNotificationStateStore extends AsyncStoreWithClient<IState> {
|
|||||||
private listMap = new Map<TagID, ListNotificationState>();
|
private listMap = new Map<TagID, ListNotificationState>();
|
||||||
private _globalState = new SummarizedNotificationState();
|
private _globalState = new SummarizedNotificationState();
|
||||||
|
|
||||||
|
private tacEnabled = SettingsStore.getValue("threadsActivityCentre");
|
||||||
|
|
||||||
private constructor(dispatcher = defaultDispatcher) {
|
private constructor(dispatcher = defaultDispatcher) {
|
||||||
super(dispatcher, {});
|
super(dispatcher, {});
|
||||||
SettingsStore.watchSetting("feature_dynamic_room_predecessors", null, () => {
|
SettingsStore.watchSetting("feature_dynamic_room_predecessors", null, () => {
|
||||||
@ -97,7 +99,7 @@ export class RoomNotificationStateStore extends AsyncStoreWithClient<IState> {
|
|||||||
*/
|
*/
|
||||||
public getRoomState(room: Room): RoomNotificationState {
|
public getRoomState(room: Room): RoomNotificationState {
|
||||||
if (!this.roomMap.has(room)) {
|
if (!this.roomMap.has(room)) {
|
||||||
this.roomMap.set(room, new RoomNotificationState(room));
|
this.roomMap.set(room, new RoomNotificationState(room, !this.tacEnabled));
|
||||||
}
|
}
|
||||||
return this.roomMap.get(room)!;
|
return this.roomMap.get(room)!;
|
||||||
}
|
}
|
||||||
|
@ -109,16 +109,16 @@ describe("RoomNotifs test", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("counts room notification type", () => {
|
it("counts room notification type", () => {
|
||||||
expect(getUnreadNotificationCount(room, NotificationCountType.Total)).toBe(0);
|
expect(getUnreadNotificationCount(room, NotificationCountType.Total, false)).toBe(0);
|
||||||
expect(getUnreadNotificationCount(room, NotificationCountType.Highlight)).toBe(0);
|
expect(getUnreadNotificationCount(room, NotificationCountType.Highlight, false)).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("counts notifications type", () => {
|
it("counts notifications type", () => {
|
||||||
room.setUnreadNotificationCount(NotificationCountType.Total, 2);
|
room.setUnreadNotificationCount(NotificationCountType.Total, 2);
|
||||||
room.setUnreadNotificationCount(NotificationCountType.Highlight, 1);
|
room.setUnreadNotificationCount(NotificationCountType.Highlight, 1);
|
||||||
|
|
||||||
expect(getUnreadNotificationCount(room, NotificationCountType.Total)).toBe(2);
|
expect(getUnreadNotificationCount(room, NotificationCountType.Total, false)).toBe(2);
|
||||||
expect(getUnreadNotificationCount(room, NotificationCountType.Highlight)).toBe(1);
|
expect(getUnreadNotificationCount(room, NotificationCountType.Highlight, false)).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when there is a room predecessor", () => {
|
describe("when there is a room predecessor", () => {
|
||||||
@ -156,8 +156,8 @@ describe("RoomNotifs test", () => {
|
|||||||
it("and there is a predecessor in the create event, it should count predecessor highlight", () => {
|
it("and there is a predecessor in the create event, it should count predecessor highlight", () => {
|
||||||
room.addLiveEvents([mkCreateEvent(OLD_ROOM_ID)]);
|
room.addLiveEvents([mkCreateEvent(OLD_ROOM_ID)]);
|
||||||
|
|
||||||
expect(getUnreadNotificationCount(room, NotificationCountType.Total)).toBe(8);
|
expect(getUnreadNotificationCount(room, NotificationCountType.Total, false)).toBe(8);
|
||||||
expect(getUnreadNotificationCount(room, NotificationCountType.Highlight)).toBe(7);
|
expect(getUnreadNotificationCount(room, NotificationCountType.Highlight, false)).toBe(7);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -167,8 +167,8 @@ describe("RoomNotifs test", () => {
|
|||||||
room.addLiveEvents([mkCreateEvent(OLD_ROOM_ID)]);
|
room.addLiveEvents([mkCreateEvent(OLD_ROOM_ID)]);
|
||||||
upsertRoomStateEvents(room, [mkPredecessorEvent(OLD_ROOM_ID)]);
|
upsertRoomStateEvents(room, [mkPredecessorEvent(OLD_ROOM_ID)]);
|
||||||
|
|
||||||
expect(getUnreadNotificationCount(room, NotificationCountType.Total)).toBe(8);
|
expect(getUnreadNotificationCount(room, NotificationCountType.Total, false)).toBe(8);
|
||||||
expect(getUnreadNotificationCount(room, NotificationCountType.Highlight)).toBe(7);
|
expect(getUnreadNotificationCount(room, NotificationCountType.Highlight, false)).toBe(7);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -195,8 +195,8 @@ describe("RoomNotifs test", () => {
|
|||||||
room.addLiveEvents([mkCreateEvent()]);
|
room.addLiveEvents([mkCreateEvent()]);
|
||||||
upsertRoomStateEvents(room, [mkPredecessorEvent(OLD_ROOM_ID)]);
|
upsertRoomStateEvents(room, [mkPredecessorEvent(OLD_ROOM_ID)]);
|
||||||
|
|
||||||
expect(getUnreadNotificationCount(room, NotificationCountType.Total)).toBe(2);
|
expect(getUnreadNotificationCount(room, NotificationCountType.Total, false)).toBe(2);
|
||||||
expect(getUnreadNotificationCount(room, NotificationCountType.Highlight)).toBe(1);
|
expect(getUnreadNotificationCount(room, NotificationCountType.Highlight, false)).toBe(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -214,31 +214,31 @@ describe("RoomNotifs test", () => {
|
|||||||
room.addLiveEvents([mkCreateEvent()]);
|
room.addLiveEvents([mkCreateEvent()]);
|
||||||
upsertRoomStateEvents(room, [mkPredecessorEvent(OLD_ROOM_ID)]);
|
upsertRoomStateEvents(room, [mkPredecessorEvent(OLD_ROOM_ID)]);
|
||||||
|
|
||||||
expect(getUnreadNotificationCount(room, NotificationCountType.Total)).toBe(8);
|
expect(getUnreadNotificationCount(room, NotificationCountType.Total, false)).toBe(8);
|
||||||
expect(getUnreadNotificationCount(room, NotificationCountType.Highlight)).toBe(7);
|
expect(getUnreadNotificationCount(room, NotificationCountType.Highlight, false)).toBe(7);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("and there is an unknown room in the predecessor event, it should not count predecessor highlight", () => {
|
it("and there is an unknown room in the predecessor event, it should not count predecessor highlight", () => {
|
||||||
room.addLiveEvents([mkCreateEvent()]);
|
room.addLiveEvents([mkCreateEvent()]);
|
||||||
upsertRoomStateEvents(room, [mkPredecessorEvent("!unknon:example.com")]);
|
upsertRoomStateEvents(room, [mkPredecessorEvent("!unknon:example.com")]);
|
||||||
|
|
||||||
expect(getUnreadNotificationCount(room, NotificationCountType.Total)).toBe(2);
|
expect(getUnreadNotificationCount(room, NotificationCountType.Total, false)).toBe(2);
|
||||||
expect(getUnreadNotificationCount(room, NotificationCountType.Highlight)).toBe(1);
|
expect(getUnreadNotificationCount(room, NotificationCountType.Highlight, false)).toBe(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("counts thread notification type", () => {
|
it("counts thread notification type", () => {
|
||||||
expect(getUnreadNotificationCount(room, NotificationCountType.Total, THREAD_ID)).toBe(0);
|
expect(getUnreadNotificationCount(room, NotificationCountType.Total, false, THREAD_ID)).toBe(0);
|
||||||
expect(getUnreadNotificationCount(room, NotificationCountType.Highlight, THREAD_ID)).toBe(0);
|
expect(getUnreadNotificationCount(room, NotificationCountType.Highlight, false, THREAD_ID)).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("counts thread notifications type", () => {
|
it("counts thread notifications type", () => {
|
||||||
room.setThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Total, 2);
|
room.setThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Total, 2);
|
||||||
room.setThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Highlight, 1);
|
room.setThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Highlight, 1);
|
||||||
|
|
||||||
expect(getUnreadNotificationCount(room, NotificationCountType.Total, THREAD_ID)).toBe(2);
|
expect(getUnreadNotificationCount(room, NotificationCountType.Total, false, THREAD_ID)).toBe(2);
|
||||||
expect(getUnreadNotificationCount(room, NotificationCountType.Highlight, THREAD_ID)).toBe(1);
|
expect(getUnreadNotificationCount(room, NotificationCountType.Highlight, false, THREAD_ID)).toBe(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@ describe("Unread", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("returns true for a room with no receipts", () => {
|
it("returns true for a room with no receipts", () => {
|
||||||
expect(doesRoomHaveUnreadMessages(room)).toBe(true);
|
expect(doesRoomHaveUnreadMessages(room, false)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns false for a room when the latest event was sent by the current user", () => {
|
it("returns false for a room when the latest event was sent by the current user", () => {
|
||||||
@ -166,7 +166,7 @@ describe("Unread", () => {
|
|||||||
// Only for timeline events.
|
// Only for timeline events.
|
||||||
room.addLiveEvents([event]);
|
room.addLiveEvents([event]);
|
||||||
|
|
||||||
expect(doesRoomHaveUnreadMessages(room)).toBe(false);
|
expect(doesRoomHaveUnreadMessages(room, false)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns false for a room when the read receipt is at the latest event", () => {
|
it("returns false for a room when the read receipt is at the latest event", () => {
|
||||||
@ -183,7 +183,7 @@ describe("Unread", () => {
|
|||||||
});
|
});
|
||||||
room.addReceipt(receipt);
|
room.addReceipt(receipt);
|
||||||
|
|
||||||
expect(doesRoomHaveUnreadMessages(room)).toBe(false);
|
expect(doesRoomHaveUnreadMessages(room, false)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns true for a room when the read receipt is earlier than the latest event", () => {
|
it("returns true for a room when the read receipt is earlier than the latest event", () => {
|
||||||
@ -210,7 +210,7 @@ describe("Unread", () => {
|
|||||||
// Only for timeline events.
|
// Only for timeline events.
|
||||||
room.addLiveEvents([event2]);
|
room.addLiveEvents([event2]);
|
||||||
|
|
||||||
expect(doesRoomHaveUnreadMessages(room)).toBe(true);
|
expect(doesRoomHaveUnreadMessages(room, false)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns true for a room with an unread message in a thread", async () => {
|
it("returns true for a room with an unread message in a thread", async () => {
|
||||||
@ -247,7 +247,7 @@ describe("Unread", () => {
|
|||||||
// Create a thread as a different user.
|
// Create a thread as a different user.
|
||||||
await populateThread({ room, client, authorId: myId, participantUserIds: [aliceId] });
|
await populateThread({ room, client, authorId: myId, participantUserIds: [aliceId] });
|
||||||
|
|
||||||
expect(doesRoomHaveUnreadMessages(room)).toBe(true);
|
expect(doesRoomHaveUnreadMessages(room, true)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns false for a room when the latest thread event was sent by the current user", async () => {
|
it("returns false for a room when the latest thread event was sent by the current user", async () => {
|
||||||
@ -268,7 +268,7 @@ describe("Unread", () => {
|
|||||||
// Create a thread as the current user.
|
// Create a thread as the current user.
|
||||||
await populateThread({ room, client, authorId: myId, participantUserIds: [myId] });
|
await populateThread({ room, client, authorId: myId, participantUserIds: [myId] });
|
||||||
|
|
||||||
expect(doesRoomHaveUnreadMessages(room)).toBe(false);
|
expect(doesRoomHaveUnreadMessages(room, true)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns false for a room with read thread messages", async () => {
|
it("returns false for a room with read thread messages", async () => {
|
||||||
@ -308,7 +308,7 @@ describe("Unread", () => {
|
|||||||
});
|
});
|
||||||
room.addReceipt(receipt);
|
room.addReceipt(receipt);
|
||||||
|
|
||||||
expect(doesRoomHaveUnreadMessages(room)).toBe(false);
|
expect(doesRoomHaveUnreadMessages(room, true)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns true for a room when read receipt is not on the latest thread messages", async () => {
|
it("returns true for a room when read receipt is not on the latest thread messages", async () => {
|
||||||
@ -348,7 +348,7 @@ describe("Unread", () => {
|
|||||||
});
|
});
|
||||||
room.addReceipt(receipt);
|
room.addReceipt(receipt);
|
||||||
|
|
||||||
expect(doesRoomHaveUnreadMessages(room)).toBe(true);
|
expect(doesRoomHaveUnreadMessages(room, true)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns true when the event for a thread receipt can't be found", async () => {
|
it("returns true when the event for a thread receipt can't be found", async () => {
|
||||||
@ -394,7 +394,7 @@ describe("Unread", () => {
|
|||||||
});
|
});
|
||||||
room.addReceipt(receipt);
|
room.addReceipt(receipt);
|
||||||
|
|
||||||
expect(doesRoomHaveUnreadMessages(room)).toBe(true);
|
expect(doesRoomHaveUnreadMessages(room, true)).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -412,7 +412,7 @@ describe("Unread", () => {
|
|||||||
// Only for timeline events.
|
// Only for timeline events.
|
||||||
room.addLiveEvents([redactedEvent]);
|
room.addLiveEvents([redactedEvent]);
|
||||||
|
|
||||||
expect(doesRoomHaveUnreadMessages(room)).toBe(true);
|
expect(doesRoomHaveUnreadMessages(room, true)).toBe(true);
|
||||||
expect(logger.warn).toHaveBeenCalledWith(
|
expect(logger.warn).toHaveBeenCalledWith(
|
||||||
"Falling back to unread room because of no read receipt or counting message found",
|
"Falling back to unread room because of no read receipt or counting message found",
|
||||||
{
|
{
|
||||||
|
@ -812,8 +812,11 @@ function createRoom(info: IRoomCreationInfo) {
|
|||||||
const userId = client.getUserId()!;
|
const userId = client.getUserId()!;
|
||||||
if (info.isDm) {
|
if (info.isDm) {
|
||||||
client.getAccountData = (eventType) => {
|
client.getAccountData = (eventType) => {
|
||||||
expect(eventType).toEqual("m.direct");
|
if (eventType === "m.direct") {
|
||||||
return mkDirectEvent(roomId, userId, info.userIds);
|
return mkDirectEvent(roomId, userId, info.userIds);
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ describe("<VideoRoomChatButton />", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const mockRoomNotificationState = (room: Room, level: NotificationLevel): RoomNotificationState => {
|
const mockRoomNotificationState = (room: Room, level: NotificationLevel): RoomNotificationState => {
|
||||||
const roomNotificationState = new RoomNotificationState(room);
|
const roomNotificationState = new RoomNotificationState(room, false);
|
||||||
|
|
||||||
// @ts-ignore ugly mocking
|
// @ts-ignore ugly mocking
|
||||||
roomNotificationState._level = level;
|
roomNotificationState._level = level;
|
||||||
|
@ -60,7 +60,7 @@ describe("<LabsUserSettingsTab />", () => {
|
|||||||
// non-beta labs section
|
// non-beta labs section
|
||||||
expect(screen.getByText("Early previews")).toBeInTheDocument();
|
expect(screen.getByText("Early previews")).toBeInTheDocument();
|
||||||
const labsSections = container.getElementsByClassName("mx_SettingsSubsection");
|
const labsSections = container.getElementsByClassName("mx_SettingsSubsection");
|
||||||
expect(labsSections).toHaveLength(9);
|
expect(labsSections).toHaveLength(10);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Rust crypto setting", () => {
|
describe("Rust crypto setting", () => {
|
||||||
|
@ -125,6 +125,7 @@ describe("RoomNotificationStateStore", function () {
|
|||||||
ret.getPendingEvents = jest.fn().mockReturnValue([]);
|
ret.getPendingEvents = jest.fn().mockReturnValue([]);
|
||||||
ret.isSpaceRoom = jest.fn().mockReturnValue(false);
|
ret.isSpaceRoom = jest.fn().mockReturnValue(false);
|
||||||
ret.getUnreadNotificationCount = jest.fn().mockReturnValue(numUnreads);
|
ret.getUnreadNotificationCount = jest.fn().mockReturnValue(numUnreads);
|
||||||
|
ret.getRoomUnreadNotificationCount = jest.fn().mockReturnValue(numUnreads);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -81,7 +81,7 @@ describe("RoomNotificationState", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
it("Updates on event decryption", () => {
|
it("Updates on event decryption", () => {
|
||||||
const roomNotifState = new RoomNotificationState(room);
|
const roomNotifState = new RoomNotificationState(room, true);
|
||||||
const listener = jest.fn();
|
const listener = jest.fn();
|
||||||
roomNotifState.addListener(NotificationStateEvents.Update, listener);
|
roomNotifState.addListener(NotificationStateEvents.Update, listener);
|
||||||
const testEvent = {
|
const testEvent = {
|
||||||
@ -93,12 +93,12 @@ describe("RoomNotificationState", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("removes listeners", () => {
|
it("removes listeners", () => {
|
||||||
const roomNotifState = new RoomNotificationState(room);
|
const roomNotifState = new RoomNotificationState(room, false);
|
||||||
expect(() => roomNotifState.destroy()).not.toThrow();
|
expect(() => roomNotifState.destroy()).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("suggests an 'unread' ! if there are unsent messages", () => {
|
it("suggests an 'unread' ! if there are unsent messages", () => {
|
||||||
const roomNotifState = new RoomNotificationState(room);
|
const roomNotifState = new RoomNotificationState(room, false);
|
||||||
|
|
||||||
const event = mkEvent({
|
const event = mkEvent({
|
||||||
event: true,
|
event: true,
|
||||||
@ -115,7 +115,7 @@ describe("RoomNotificationState", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("suggests nothing if the room is muted", () => {
|
it("suggests nothing if the room is muted", () => {
|
||||||
const roomNotifState = new RoomNotificationState(room);
|
const roomNotifState = new RoomNotificationState(room, false);
|
||||||
|
|
||||||
muteRoom(room);
|
muteRoom(room);
|
||||||
setUnreads(room, 1234, 0);
|
setUnreads(room, 1234, 0);
|
||||||
@ -127,7 +127,7 @@ describe("RoomNotificationState", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("suggests a red ! if the user has been invited to a room", () => {
|
it("suggests a red ! if the user has been invited to a room", () => {
|
||||||
const roomNotifState = new RoomNotificationState(room);
|
const roomNotifState = new RoomNotificationState(room, false);
|
||||||
|
|
||||||
room.updateMyMembership("invite"); // emit
|
room.updateMyMembership("invite"); // emit
|
||||||
|
|
||||||
@ -137,7 +137,7 @@ describe("RoomNotificationState", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("returns a proper count and color for regular unreads", () => {
|
it("returns a proper count and color for regular unreads", () => {
|
||||||
const roomNotifState = new RoomNotificationState(room);
|
const roomNotifState = new RoomNotificationState(room, false);
|
||||||
|
|
||||||
setUnreads(room, 4321, 0);
|
setUnreads(room, 4321, 0);
|
||||||
room.updateMyMembership("join"); // emit
|
room.updateMyMembership("join"); // emit
|
||||||
@ -148,7 +148,7 @@ describe("RoomNotificationState", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("returns a proper count and color for highlights", () => {
|
it("returns a proper count and color for highlights", () => {
|
||||||
const roomNotifState = new RoomNotificationState(room);
|
const roomNotifState = new RoomNotificationState(room, false);
|
||||||
|
|
||||||
setUnreads(room, 0, 69);
|
setUnreads(room, 0, 69);
|
||||||
room.updateMyMembership("join"); // emit
|
room.updateMyMembership("join"); // emit
|
||||||
@ -159,7 +159,7 @@ describe("RoomNotificationState", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("includes threads", async () => {
|
it("includes threads", async () => {
|
||||||
const roomNotifState = new RoomNotificationState(room);
|
const roomNotifState = new RoomNotificationState(room, true);
|
||||||
|
|
||||||
room.timeline.push(
|
room.timeline.push(
|
||||||
new MatrixEvent({
|
new MatrixEvent({
|
||||||
|
Reference in New Issue
Block a user