From 3a501003e246dbf01f1f220ab6aa9f4df2885374 Mon Sep 17 00:00:00 2001 From: Germain Date: Tue, 6 Dec 2022 09:59:17 +0000 Subject: [PATCH] Add setting to hide bold notifications (#9705) --- .../StatelessNotificationBadge.tsx | 7 +++- src/i18n/strings/en_EN.json | 1 + src/settings/Settings.tsx | 9 ++++- .../notifications/ListNotificationState.ts | 2 +- src/stores/notifications/NotificationState.ts | 33 +++++++++++++++---- .../notifications/RoomNotificationState.ts | 4 +-- .../notifications/SpaceNotificationState.ts | 2 +- .../notifications/StaticNotificationState.ts | 2 +- .../NotificationBadge-test.tsx | 15 +++++++++ .../views/spaces/QuickThemeSwitcher-test.tsx | 1 + test/stores/TypingStore-test.ts | 1 + test/utils/MultiInviter-test.ts | 1 + 12 files changed, 65 insertions(+), 13 deletions(-) diff --git a/src/components/views/rooms/NotificationBadge/StatelessNotificationBadge.tsx b/src/components/views/rooms/NotificationBadge/StatelessNotificationBadge.tsx index e9e97475f7..ebefca56d5 100644 --- a/src/components/views/rooms/NotificationBadge/StatelessNotificationBadge.tsx +++ b/src/components/views/rooms/NotificationBadge/StatelessNotificationBadge.tsx @@ -20,6 +20,7 @@ import classNames from "classnames"; import { formatCount } from "../../../../utils/FormattingUtils"; import AccessibleButton from "../../elements/AccessibleButton"; import { NotificationColor } from "../../../../stores/notifications/NotificationColor"; +import { useSettingValue } from "../../../../hooks/useSettings"; interface Props { symbol: string | null; @@ -37,8 +38,12 @@ export function StatelessNotificationBadge({ count, color, ...props }: Props) { + const hideBold = useSettingValue("feature_hidebold"); + // Don't show a badge if we don't need to - if (color === NotificationColor.None) return null; + if (color === NotificationColor.None || (hideBold && color == NotificationColor.Bold)) { + return null; + } const hasUnreadCount = color >= NotificationColor.Grey && (!!count || !!symbol); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index e2daf50263..b76586eabb 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -960,6 +960,7 @@ "Show stickers button": "Show stickers button", "Show polls button": "Show polls button", "Insert a trailing colon after user mentions at the start of a message": "Insert a trailing colon after user mentions at the start of a message", + "Hide notification dot (only display counters badges)": "Hide notification dot (only display counters badges)", "Use a more compact 'Modern' layout": "Use a more compact 'Modern' layout", "Show a placeholder for removed messages": "Show a placeholder for removed messages", "Show join/leave messages (invites/removes/bans unaffected)": "Show join/leave messages (invites/removes/bans unaffected)", diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index c6472868b7..110a520f84 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -556,11 +556,18 @@ export const SETTINGS: {[setting: string]: ISetting} = { supportedLevels: LEVELS_ROOM_OR_ACCOUNT, default: false, }, + "feature_hidebold": { + isFeature: true, + supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, + displayName: _td("Hide notification dot (only display counters badges)"), + labsGroup: LabGroup.Rooms, + default: false, + }, "useCompactLayout": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, displayName: _td("Use a more compact 'Modern' layout"), default: false, - controller: new IncompatibleController("layout", false, v => v !== Layout.Group), + controller: new IncompatibleController("layout", false, (v: Layout) => v !== Layout.Group), }, "showRedactions": { supportedLevels: LEVELS_ROOM_SETTINGS_WITH_ROOM, diff --git a/src/stores/notifications/ListNotificationState.ts b/src/stores/notifications/ListNotificationState.ts index 8ff1824bd6..37235b0dd6 100644 --- a/src/stores/notifications/ListNotificationState.ts +++ b/src/stores/notifications/ListNotificationState.ts @@ -31,7 +31,7 @@ export class ListNotificationState extends NotificationState { super(); } - public get symbol(): string { + public get symbol(): string | null { return this._color === NotificationColor.Unsent ? "!" : null; } diff --git a/src/stores/notifications/NotificationState.ts b/src/stores/notifications/NotificationState.ts index 60f50fad8c..c963d9c1a0 100644 --- a/src/stores/notifications/NotificationState.ts +++ b/src/stores/notifications/NotificationState.ts @@ -18,6 +18,7 @@ import { TypedEventEmitter } from "matrix-js-sdk/src/models/typed-event-emitter" import { NotificationColor } from "./NotificationColor"; import { IDestroyable } from "../../utils/IDestroyable"; +import SettingsStore from "../../settings/SettingsStore"; export interface INotificationStateSnapshotParams { symbol: string | null; @@ -37,11 +38,22 @@ export abstract class NotificationState extends TypedEventEmitter implements INotificationStateSnapshotParams, IDestroyable { // - protected _symbol: string | null; - protected _count: number; - protected _color: NotificationColor; + protected _symbol: string | null = null; + protected _count = 0; + protected _color: NotificationColor = NotificationColor.None; - public get symbol(): string { + private watcherReferences: string[] = []; + + constructor() { + super(); + this.watcherReferences.push( + SettingsStore.watchSetting("feature_hidebold", null, () => { + this.emit(NotificationStateEvents.Update); + }), + ); + } + + public get symbol(): string | null { return this._symbol; } @@ -58,7 +70,12 @@ export abstract class NotificationState } public get isUnread(): boolean { - return this.color >= NotificationColor.Bold; + if (this.color > NotificationColor.Bold) { + return true; + } else { + const hideBold = SettingsStore.getValue("feature_hidebold"); + return this.color === NotificationColor.Bold && !hideBold; + } } public get hasUnreadCount(): boolean { @@ -81,11 +98,15 @@ export abstract class NotificationState public destroy(): void { this.removeAllListeners(NotificationStateEvents.Update); + for (const watcherReference of this.watcherReferences) { + SettingsStore.unwatchSetting(watcherReference); + } + this.watcherReferences = []; } } export class NotificationStateSnapshot { - private readonly symbol: string; + private readonly symbol: string | null; private readonly count: number; private readonly color: NotificationColor; diff --git a/src/stores/notifications/RoomNotificationState.ts b/src/stores/notifications/RoomNotificationState.ts index dca3e290e3..559ae55de1 100644 --- a/src/stores/notifications/RoomNotificationState.ts +++ b/src/stores/notifications/RoomNotificationState.ts @@ -98,8 +98,8 @@ export class RoomNotificationState extends NotificationState implements IDestroy this.updateNotificationState(); }; - private handleRoomEventUpdate = (event: MatrixEvent, room: Room | null) => { - if (room?.roomId !== this.room.roomId) return; // ignore - not for us or notifications timeline + private handleRoomEventUpdate = (event: MatrixEvent) => { + if (event?.getRoomId() !== this.room.roomId) return; // ignore - not for us or notifications timeline this.updateNotificationState(); }; diff --git a/src/stores/notifications/SpaceNotificationState.ts b/src/stores/notifications/SpaceNotificationState.ts index 241530f77f..0df920b566 100644 --- a/src/stores/notifications/SpaceNotificationState.ts +++ b/src/stores/notifications/SpaceNotificationState.ts @@ -32,7 +32,7 @@ export class SpaceNotificationState extends NotificationState { super(); } - public get symbol(): string { + public get symbol(): string | null { return this._color === NotificationColor.Unsent ? "!" : null; } diff --git a/src/stores/notifications/StaticNotificationState.ts b/src/stores/notifications/StaticNotificationState.ts index b18aa78e0f..fce8bee217 100644 --- a/src/stores/notifications/StaticNotificationState.ts +++ b/src/stores/notifications/StaticNotificationState.ts @@ -20,7 +20,7 @@ import { NotificationState } from "./NotificationState"; export class StaticNotificationState extends NotificationState { public static readonly RED_EXCLAMATION = StaticNotificationState.forSymbol("!", NotificationColor.Red); - constructor(symbol: string, count: number, color: NotificationColor) { + constructor(symbol: string | null, count: number, color: NotificationColor) { super(); this._symbol = symbol; this._count = count; diff --git a/test/components/views/rooms/NotificationBadge/NotificationBadge-test.tsx b/test/components/views/rooms/NotificationBadge/NotificationBadge-test.tsx index 95d598a704..e0c503d6c5 100644 --- a/test/components/views/rooms/NotificationBadge/NotificationBadge-test.tsx +++ b/test/components/views/rooms/NotificationBadge/NotificationBadge-test.tsx @@ -20,6 +20,7 @@ import React from "react"; import { StatelessNotificationBadge, } from "../../../../../src/components/views/rooms/NotificationBadge/StatelessNotificationBadge"; +import SettingsStore from "../../../../../src/settings/SettingsStore"; import { NotificationColor } from "../../../../../src/stores/notifications/NotificationColor"; describe("NotificationBadge", () => { @@ -45,5 +46,19 @@ describe("NotificationBadge", () => { fireEvent.mouseLeave(container.firstChild); expect(cb).toHaveBeenCalledTimes(3); }); + + it("hides the bold icon when the settings is set", () => { + jest.spyOn(SettingsStore, "getValue").mockImplementation((name: string) => { + return name === "feature_hidebold"; + }); + + const { container } = render(); + + expect(container.firstChild).toBeNull(); + }); }); }); diff --git a/test/components/views/spaces/QuickThemeSwitcher-test.tsx b/test/components/views/spaces/QuickThemeSwitcher-test.tsx index 4efa1473b2..28a0e3e954 100644 --- a/test/components/views/spaces/QuickThemeSwitcher-test.tsx +++ b/test/components/views/spaces/QuickThemeSwitcher-test.tsx @@ -38,6 +38,7 @@ jest.mock('../../../../src/settings/SettingsStore', () => ({ setValue: jest.fn(), getValue: jest.fn(), monitorSetting: jest.fn(), + watchSetting: jest.fn(), })); jest.mock('../../../../src/dispatcher/dispatcher', () => ({ diff --git a/test/stores/TypingStore-test.ts b/test/stores/TypingStore-test.ts index a5b4437f14..b6b5c388f8 100644 --- a/test/stores/TypingStore-test.ts +++ b/test/stores/TypingStore-test.ts @@ -25,6 +25,7 @@ import { TestSdkContext } from "../TestSdkContext"; jest.mock("../../src/settings/SettingsStore", () => ({ getValue: jest.fn(), monitorSetting: jest.fn(), + watchSetting: jest.fn(), })); describe("TypingStore", () => { diff --git a/test/utils/MultiInviter-test.ts b/test/utils/MultiInviter-test.ts index 83b71232fc..49c2ebbeaf 100644 --- a/test/utils/MultiInviter-test.ts +++ b/test/utils/MultiInviter-test.ts @@ -42,6 +42,7 @@ jest.mock('../../src/Modal', () => ({ jest.mock('../../src/settings/SettingsStore', () => ({ getValue: jest.fn(), monitorSetting: jest.fn(), + watchSetting: jest.fn(), })); const mockPromptBeforeInviteUnknownUsers = (value: boolean) => {