Don't reference the notification levels by colour (#12138)

* Don't reference the notification levels by colour

We're about to change what colours they are so either we'd have to rename
a bunch of constants. We may as well make things not reference what colour
anything is in the actual UI. Hopefully these constants are clear enough.

 * Rename NotificationColor -> NotificationLevel
 * Red -> Highlight
 * Grey -> Notification
 * Bold -> Activity
 * Anywhere else that calls it 'color' -> 'level'

Also fixes some weird mixes of US & UK English.

It turns out this is referenced in... quite a lot of places, so this is
quite a large PR. It can't really be much smaller, sorry.

* One test rename & some hiding due to ts-ignore

* More hiding behind ts-ignore

* Damn you, @ts-ignore...

* Fix test CSS values

* Missed some colour -> level

Co-authored-by: Florian Duros <florianduros@element.io>

* Change other instances of variables renamed in suggestion

* Update new test for renames

---------

Co-authored-by: Florian Duros <florianduros@element.io>
This commit is contained in:
David Baker
2024-01-15 15:25:48 +00:00
committed by GitHub
co-authored by Florian Duros
parent 97339ee2f6
commit 9254e9562e
42 changed files with 268 additions and 265 deletions
@@ -16,7 +16,7 @@ limitations under the License.
import { Room } from "matrix-js-sdk/src/matrix";
import { NotificationColor } from "./NotificationColor";
import { NotificationLevel } from "./NotificationLevel";
import { arrayDiff } from "../../utils/arrays";
import { RoomNotificationState } from "./RoomNotificationState";
import { NotificationState, NotificationStateEvents } from "./NotificationState";
@@ -35,7 +35,7 @@ export class ListNotificationState extends NotificationState {
}
public get symbol(): string | null {
return this._color === NotificationColor.Unsent ? "!" : null;
return this._level === NotificationLevel.Unsent ? "!" : null;
}
public setRooms(rooms: Room[]): void {
@@ -86,14 +86,14 @@ export class ListNotificationState extends NotificationState {
const snapshot = this.snapshot();
if (this.byTileCount) {
this._color = NotificationColor.Red;
this._level = NotificationLevel.Highlight;
this._count = this.rooms.length;
} else {
this._count = 0;
this._color = NotificationColor.None;
this._level = NotificationLevel.None;
for (const state of Object.values(this.states)) {
this._count += state.count;
this._color = Math.max(this.color, state.color);
this._level = Math.max(this.level, state.level);
}
}
@@ -16,30 +16,30 @@ limitations under the License.
import { _t } from "../../languageHandler";
export enum NotificationColor {
export enum NotificationLevel {
Muted,
// Inverted (None -> Red) because we do integer comparisons on this
None, // nothing special
// TODO: Remove bold with notifications: https://github.com/vector-im/element-web/issues/14227
Bold, // no badge, show as unread
Grey, // unread notified messages
Red, // unread pings
Activity, // no badge, show as unread
Notification, // unread notified messages
Highlight, // unread pings
Unsent, // some messages failed to send
}
export function humanReadableNotificationColor(color: NotificationColor): string {
switch (color) {
case NotificationColor.None:
return _t("notifications|colour_none");
case NotificationColor.Bold:
return _t("notifications|colour_bold");
case NotificationColor.Grey:
return _t("notifications|colour_grey");
case NotificationColor.Red:
return _t("notifications|colour_red");
case NotificationColor.Unsent:
return _t("notifications|colour_unsent");
case NotificationColor.Muted:
return _t("notifications|colour_muted");
export function humanReadableNotificationLevel(level: NotificationLevel): string {
switch (level) {
case NotificationLevel.None:
return _t("notifications|level_none");
case NotificationLevel.Activity:
return _t("notifications|level_activity");
case NotificationLevel.Notification:
return _t("notifications|level_notification");
case NotificationLevel.Highlight:
return _t("notifications|level_highlight");
case NotificationLevel.Unsent:
return _t("notifications|level_unsent");
case NotificationLevel.Muted:
return _t("notifications|level_muted");
}
}
+14 -14
View File
@@ -16,14 +16,14 @@ limitations under the License.
import { TypedEventEmitter } from "matrix-js-sdk/src/matrix";
import { NotificationColor } from "./NotificationColor";
import { NotificationLevel } from "./NotificationLevel";
import { IDestroyable } from "../../utils/IDestroyable";
import SettingsStore from "../../settings/SettingsStore";
export interface INotificationStateSnapshotParams {
symbol: string | null;
count: number;
color: NotificationColor;
level: NotificationLevel;
muted: boolean;
knocked: boolean;
}
@@ -43,7 +43,7 @@ export abstract class NotificationState
//
protected _symbol: string | null = null;
protected _count = 0;
protected _color: NotificationColor = NotificationColor.None;
protected _level: NotificationLevel = NotificationLevel.None;
protected _muted = false;
protected _knocked = false;
@@ -66,8 +66,8 @@ export abstract class NotificationState
return this._count;
}
public get color(): NotificationColor {
return this._color;
public get level(): NotificationLevel {
return this._level;
}
public get muted(): boolean {
@@ -79,24 +79,24 @@ export abstract class NotificationState
}
public get isIdle(): boolean {
return this.color <= NotificationColor.None;
return this.level <= NotificationLevel.None;
}
public get isUnread(): boolean {
if (this.color > NotificationColor.Bold) {
if (this.level > NotificationLevel.Activity) {
return true;
} else {
const hideBold = SettingsStore.getValue("feature_hidebold");
return this.color === NotificationColor.Bold && !hideBold;
return this.level === NotificationLevel.Activity && !hideBold;
}
}
public get hasUnreadCount(): boolean {
return this.color >= NotificationColor.Grey && (!!this.count || !!this.symbol);
return this.level >= NotificationLevel.Notification && (!!this.count || !!this.symbol);
}
public get hasMentions(): boolean {
return this.color >= NotificationColor.Red;
return this.level >= NotificationLevel.Highlight;
}
protected emitIfUpdated(snapshot: NotificationStateSnapshot): void {
@@ -121,14 +121,14 @@ export abstract class NotificationState
export class NotificationStateSnapshot {
private readonly symbol: string | null;
private readonly count: number;
private readonly color: NotificationColor;
private readonly level: NotificationLevel;
private readonly muted: boolean;
private readonly knocked: boolean;
public constructor(state: INotificationStateSnapshotParams) {
this.symbol = state.symbol;
this.count = state.count;
this.color = state.color;
this.level = state.level;
this.muted = state.muted;
this.knocked = state.knocked;
}
@@ -137,14 +137,14 @@ export class NotificationStateSnapshot {
const before = {
count: this.count,
symbol: this.symbol,
color: this.color,
level: this.level,
muted: this.muted,
knocked: this.knocked,
};
const after = {
count: other.count,
symbol: other.symbol,
color: other.color,
level: other.level,
muted: other.muted,
knocked: other.knocked,
};
@@ -90,11 +90,11 @@ export class RoomNotificationState extends NotificationState implements IDestroy
private updateNotificationState(): void {
const snapshot = this.snapshot();
const { color, symbol, count } = RoomNotifs.determineUnreadState(this.room);
const { level, symbol, count } = RoomNotifs.determineUnreadState(this.room);
const muted =
RoomNotifs.getRoomNotifsState(this.room.client, this.room.roomId) === RoomNotifs.RoomNotifState.Mute;
const knocked = SettingsStore.getValue("feature_ask_to_join") && this.room.getMyMembership() === "knock";
this._color = color;
this._level = level;
this._symbol = symbol;
this._count = count;
this._muted = muted;
@@ -138,7 +138,7 @@ export class RoomNotificationStateStore extends AsyncStoreWithClient<IState> {
if (
this.globalState.symbol !== globalState.symbol ||
this.globalState.count !== globalState.count ||
this.globalState.color !== globalState.color ||
this.globalState.level !== globalState.level ||
this.globalState.numUnreadStates !== globalState.numUnreadStates ||
forceEmit
) {
@@ -16,7 +16,7 @@ limitations under the License.
import { Room } from "matrix-js-sdk/src/matrix";
import { NotificationColor } from "./NotificationColor";
import { NotificationLevel } from "./NotificationLevel";
import { arrayDiff } from "../../utils/arrays";
import { RoomNotificationState } from "./RoomNotificationState";
import { NotificationState, NotificationStateEvents } from "./NotificationState";
@@ -33,7 +33,7 @@ export class SpaceNotificationState extends NotificationState {
}
public get symbol(): string | null {
return this._color === NotificationColor.Unsent ? "!" : null;
return this._level === NotificationLevel.Unsent ? "!" : null;
}
public setRooms(rooms: Room[]): void {
@@ -56,7 +56,7 @@ export class SpaceNotificationState extends NotificationState {
}
public getFirstRoomWithNotifications(): string | undefined {
return Object.values(this.states).find((state) => state.color >= this.color)?.room.roomId;
return Object.values(this.states).find((state) => state.level >= this.level)?.room.roomId;
}
public destroy(): void {
@@ -75,16 +75,16 @@ export class SpaceNotificationState extends NotificationState {
const snapshot = this.snapshot();
this._count = 0;
this._color = NotificationColor.None;
this._level = NotificationLevel.None;
for (const [roomId, state] of Object.entries(this.states)) {
const room = this.rooms.find((r) => r.roomId === roomId);
const roomTags = room ? RoomListStore.instance.getTagsForRoom(room) : [];
// We ignore unreads in LowPriority rooms, see https://github.com/vector-im/element-web/issues/16836
if (roomTags.includes(DefaultTagID.LowPriority) && state.color === NotificationColor.Bold) continue;
if (roomTags.includes(DefaultTagID.LowPriority) && state.level === NotificationLevel.Activity) continue;
this._count += state.count;
this._color = Math.max(this.color, state.color);
this._level = Math.max(this.level, state.level);
}
// finally, publish an update if needed
@@ -14,24 +14,24 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { NotificationColor } from "./NotificationColor";
import { NotificationLevel } from "./NotificationLevel";
import { NotificationState } from "./NotificationState";
export class StaticNotificationState extends NotificationState {
public static readonly RED_EXCLAMATION = StaticNotificationState.forSymbol("!", NotificationColor.Red);
public static readonly RED_EXCLAMATION = StaticNotificationState.forSymbol("!", NotificationLevel.Highlight);
public constructor(symbol: string | null, count: number, color: NotificationColor) {
public constructor(symbol: string | null, count: number, level: NotificationLevel) {
super();
this._symbol = symbol;
this._count = count;
this._color = color;
this._level = level;
}
public static forCount(count: number, color: NotificationColor): StaticNotificationState {
return new StaticNotificationState(null, count, color);
public static forCount(count: number, level: NotificationLevel): StaticNotificationState {
return new StaticNotificationState(null, count, level);
}
public static forSymbol(symbol: string, color: NotificationColor): StaticNotificationState {
return new StaticNotificationState(symbol, 0, color);
public static forSymbol(symbol: string, level: NotificationLevel): StaticNotificationState {
return new StaticNotificationState(symbol, 0, level);
}
}
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { NotificationColor } from "./NotificationColor";
import { NotificationLevel } from "./NotificationLevel";
import { NotificationState } from "./NotificationState";
/**
@@ -30,7 +30,7 @@ export class SummarizedNotificationState extends NotificationState {
super();
this._symbol = null;
this._count = 0;
this._color = NotificationColor.None;
this._level = NotificationLevel.None;
}
public get numUnreadStates(): number {
@@ -52,8 +52,8 @@ export class SummarizedNotificationState extends NotificationState {
if (other.count) {
this._count += other.count;
}
if (other.color > this.color) {
this._color = other.color;
if (other.level > this.level) {
this._level = other.level;
}
if (other.hasUnreadCount) {
this.totalStatesWithUnread++;
@@ -22,27 +22,27 @@ import { RoomUpdateCause, TagID } from "../../models";
import { SortAlgorithm } from "../models";
import { sortRoomsWithAlgorithm } from "../tag-sorting";
import { OrderingAlgorithm } from "./OrderingAlgorithm";
import { NotificationColor } from "../../../notifications/NotificationColor";
import { NotificationLevel } from "../../../notifications/NotificationLevel";
import { RoomNotificationStateStore } from "../../../notifications/RoomNotificationStateStore";
type CategorizedRoomMap = {
[category in NotificationColor]: Room[];
[category in NotificationLevel]: Room[];
};
type CategoryIndex = Partial<{
[category in NotificationColor]: number; // integer
[category in NotificationLevel]: number; // integer
}>;
// Caution: changing this means you'll need to update a bunch of assumptions and
// comments! Check the usage of Category carefully to figure out what needs changing
// if you're going to change this array's order.
const CATEGORY_ORDER = [
NotificationColor.Unsent,
NotificationColor.Red,
NotificationColor.Grey,
NotificationColor.Bold,
NotificationColor.None, // idle
NotificationColor.Muted,
NotificationLevel.Unsent,
NotificationLevel.Highlight,
NotificationLevel.Notification,
NotificationLevel.Activity,
NotificationLevel.None, // idle
NotificationLevel.Muted,
];
/**
@@ -77,12 +77,12 @@ export class ImportanceAlgorithm extends OrderingAlgorithm {
// noinspection JSMethodCanBeStatic
private categorizeRooms(rooms: Room[]): CategorizedRoomMap {
const map: CategorizedRoomMap = {
[NotificationColor.Unsent]: [],
[NotificationColor.Red]: [],
[NotificationColor.Grey]: [],
[NotificationColor.Bold]: [],
[NotificationColor.None]: [],
[NotificationColor.Muted]: [],
[NotificationLevel.Unsent]: [],
[NotificationLevel.Highlight]: [],
[NotificationLevel.Notification]: [],
[NotificationLevel.Activity]: [],
[NotificationLevel.None]: [],
[NotificationLevel.Muted]: [],
};
for (const room of rooms) {
const category = this.getRoomCategory(room);
@@ -92,11 +92,11 @@ export class ImportanceAlgorithm extends OrderingAlgorithm {
}
// noinspection JSMethodCanBeStatic
private getRoomCategory(room: Room): NotificationColor {
private getRoomCategory(room: Room): NotificationLevel {
// It's fine for us to call this a lot because it's cached, and we shouldn't be
// wasting anything by doing so as the store holds single references
const state = RoomNotificationStateStore.instance.getRoomState(room);
return this.isMutedToBottom && state.muted ? NotificationColor.Muted : state.color;
return this.isMutedToBottom && state.muted ? NotificationLevel.Muted : state.level;
}
public setRooms(rooms: Room[]): void {
@@ -106,7 +106,7 @@ export class ImportanceAlgorithm extends OrderingAlgorithm {
// Every other sorting type affects the categories, not the whole tag.
const categorized = this.categorizeRooms(rooms);
for (const category of Object.keys(categorized)) {
const notificationColor = category as unknown as NotificationColor;
const notificationColor = category as unknown as NotificationLevel;
const roomsToOrder = categorized[notificationColor];
categorized[notificationColor] = sortRoomsWithAlgorithm(
roomsToOrder,
@@ -128,7 +128,7 @@ export class ImportanceAlgorithm extends OrderingAlgorithm {
}
}
private getCategoryIndex(category: NotificationColor): number {
private getCategoryIndex(category: NotificationLevel): number {
const categoryIndex = this.indices[category];
if (categoryIndex === undefined) {
@@ -213,7 +213,7 @@ export class ImportanceAlgorithm extends OrderingAlgorithm {
return true; // change made
}
private sortCategory(category: NotificationColor): void {
private sortCategory(category: NotificationLevel): void {
// This should be relatively quick because the room is usually inserted at the top of the
// category, and most popular sorting algorithms will deal with trying to keep the active
// room at the top/start of the category. For the few algorithms that will have to move the
@@ -231,7 +231,7 @@ export class ImportanceAlgorithm extends OrderingAlgorithm {
}
// noinspection JSMethodCanBeStatic
private getCategoryFromIndices(index: number, indices: CategoryIndex): NotificationColor {
private getCategoryFromIndices(index: number, indices: CategoryIndex): NotificationLevel {
for (let i = 0; i < CATEGORY_ORDER.length; i++) {
const category = CATEGORY_ORDER[i];
const isLast = i === CATEGORY_ORDER.length - 1;
@@ -252,8 +252,8 @@ export class ImportanceAlgorithm extends OrderingAlgorithm {
// noinspection JSMethodCanBeStatic
private moveRoomIndexes(
nRooms: number,
fromCategory: NotificationColor,
toCategory: NotificationColor,
fromCategory: NotificationLevel,
toCategory: NotificationLevel,
indices: CategoryIndex,
): void {
// We have to update the index of the category *after* the from/toCategory variables
@@ -266,7 +266,7 @@ export class ImportanceAlgorithm extends OrderingAlgorithm {
this.alterCategoryPositionBy(toCategory, +nRooms, indices);
}
private alterCategoryPositionBy(category: NotificationColor, n: number, indices: CategoryIndex): void {
private alterCategoryPositionBy(category: NotificationLevel, n: number, indices: CategoryIndex): void {
// Note: when we alter a category's index, we actually have to modify the ones following
// the target and not the target itself.