Add thread notification with server assistance (MSC3773) (#9400)

Co-authored-by: Janne Mareike Koschinski <janne@kuschku.de>
This commit is contained in:
Germain
2022-10-24 07:50:21 +01:00
committed by GitHub
co-authored by Janne Mareike Koschinski
parent d4f1c573ad
commit 9eb4f8d723
22 changed files with 1014 additions and 142 deletions
@@ -17,6 +17,7 @@ limitations under the License.
import { MatrixEvent, MatrixEventEvent } from "matrix-js-sdk/src/models/event";
import { NotificationCountType, Room, RoomEvent } from "matrix-js-sdk/src/models/room";
import { ClientEvent } from "matrix-js-sdk/src/client";
import { Feature, ServerSupport } from "matrix-js-sdk/src/feature";
import { NotificationColor } from "./NotificationColor";
import { IDestroyable } from "../../utils/IDestroyable";
@@ -32,15 +33,16 @@ import { ThreadsRoomNotificationState } from "./ThreadsRoomNotificationState";
export class RoomNotificationState extends NotificationState implements IDestroyable {
constructor(public readonly room: Room, private readonly threadsState?: ThreadsRoomNotificationState) {
super();
this.room.on(RoomEvent.Receipt, this.handleReadReceipt); // for unread indicators
this.room.on(RoomEvent.MyMembership, this.handleMembershipUpdate); // for redness on invites
this.room.on(RoomEvent.LocalEchoUpdated, this.handleLocalEchoUpdated); // for redness on unsent messages
const cli = this.room.client;
this.room.on(RoomEvent.Receipt, this.handleReadReceipt);
this.room.on(RoomEvent.MyMembership, this.handleMembershipUpdate);
this.room.on(RoomEvent.LocalEchoUpdated, this.handleLocalEchoUpdated);
this.room.on(RoomEvent.UnreadNotifications, this.handleNotificationCountUpdate); // for server-sent counts
if (threadsState) {
threadsState.on(NotificationStateEvents.Update, this.handleThreadsUpdate);
if (cli.canSupport.get(Feature.ThreadUnreadNotifications) === ServerSupport.Unsupported) {
this.threadsState?.on(NotificationStateEvents.Update, this.handleThreadsUpdate);
}
MatrixClientPeg.get().on(MatrixEventEvent.Decrypted, this.onEventDecrypted); // for local count calculation
MatrixClientPeg.get().on(ClientEvent.AccountData, this.handleAccountDataUpdate); // for push rules
cli.on(MatrixEventEvent.Decrypted, this.onEventDecrypted);
cli.on(ClientEvent.AccountData, this.handleAccountDataUpdate);
this.updateNotificationState();
}
@@ -50,17 +52,17 @@ export class RoomNotificationState extends NotificationState implements IDestroy
public destroy(): void {
super.destroy();
const cli = this.room.client;
this.room.removeListener(RoomEvent.Receipt, this.handleReadReceipt);
this.room.removeListener(RoomEvent.MyMembership, this.handleMembershipUpdate);
this.room.removeListener(RoomEvent.LocalEchoUpdated, this.handleLocalEchoUpdated);
this.room.removeListener(RoomEvent.UnreadNotifications, this.handleNotificationCountUpdate);
if (this.threadsState) {
if (cli.canSupport.get(Feature.ThreadUnreadNotifications) === ServerSupport.Unsupported) {
this.room.removeListener(RoomEvent.UnreadNotifications, this.handleNotificationCountUpdate);
} else if (this.threadsState) {
this.threadsState.removeListener(NotificationStateEvents.Update, this.handleThreadsUpdate);
}
if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener(MatrixEventEvent.Decrypted, this.onEventDecrypted);
MatrixClientPeg.get().removeListener(ClientEvent.AccountData, this.handleAccountDataUpdate);
}
cli.removeListener(MatrixEventEvent.Decrypted, this.onEventDecrypted);
cli.removeListener(ClientEvent.AccountData, this.handleAccountDataUpdate);
}
private handleThreadsUpdate = () => {
@@ -17,6 +17,7 @@ limitations under the License.
import { Room } from "matrix-js-sdk/src/models/room";
import { ISyncStateData, SyncState } from "matrix-js-sdk/src/sync";
import { ClientEvent } from "matrix-js-sdk/src/client";
import { Feature, ServerSupport } from "matrix-js-sdk/src/feature";
import { ActionPayload } from "../../dispatcher/payloads";
import { AsyncStoreWithClient } from "../AsyncStoreWithClient";
@@ -39,9 +40,9 @@ export class RoomNotificationStateStore extends AsyncStoreWithClient<IState> {
instance.start();
return instance;
})();
private roomMap = new Map<Room, RoomNotificationState>();
private roomThreadsMap = new Map<Room, ThreadsRoomNotificationState>();
private roomThreadsMap: Map<Room, ThreadsRoomNotificationState> = new Map<Room, ThreadsRoomNotificationState>();
private listMap = new Map<TagID, ListNotificationState>();
private _globalState = new SummarizedNotificationState();
@@ -86,18 +87,25 @@ export class RoomNotificationStateStore extends AsyncStoreWithClient<IState> {
*/
public getRoomState(room: Room): RoomNotificationState {
if (!this.roomMap.has(room)) {
// Not very elegant, but that way we ensure that we start tracking
// threads notification at the same time at rooms.
// There are multiple entry points, and it's unclear which one gets
// called first
const threadState = new ThreadsRoomNotificationState(room);
this.roomThreadsMap.set(room, threadState);
let threadState;
if (room.client.canSupport.get(Feature.ThreadUnreadNotifications) === ServerSupport.Unsupported) {
// Not very elegant, but that way we ensure that we start tracking
// threads notification at the same time at rooms.
// There are multiple entry points, and it's unclear which one gets
// called first
const threadState = new ThreadsRoomNotificationState(room);
this.roomThreadsMap.set(room, threadState);
}
this.roomMap.set(room, new RoomNotificationState(room, threadState));
}
return this.roomMap.get(room);
}
public getThreadsRoomState(room: Room): ThreadsRoomNotificationState {
public getThreadsRoomState(room: Room): ThreadsRoomNotificationState | null {
if (room.client.canSupport.get(Feature.ThreadUnreadNotifications) !== ServerSupport.Unsupported) {
return null;
}
if (!this.roomThreadsMap.has(room)) {
this.roomThreadsMap.set(room, new ThreadsRoomNotificationState(room));
}