Harden settings types (#33311)
* Harden settings types * Fix types * Update apps/web/src/emojipicker/recent.ts Co-authored-by: Will Hunt <2072976+Half-Shot@users.noreply.github.com> * Fix suggestion --------- Co-authored-by: Will Hunt <2072976+Half-Shot@users.noreply.github.com>
This commit is contained in:
co-authored by
Will Hunt
parent
4bee845010
commit
5ff302539e
@@ -25,9 +25,9 @@ export interface MediaPreviewConfig extends Record<string, unknown> {
|
|||||||
/**
|
/**
|
||||||
* Media preview setting for thumbnails of media in rooms.
|
* Media preview setting for thumbnails of media in rooms.
|
||||||
*/
|
*/
|
||||||
media_previews: MediaPreviewValue;
|
media_previews?: MediaPreviewValue;
|
||||||
/**
|
/**
|
||||||
* Media preview settings for avatars of rooms we have been invited to.
|
* Media preview settings for avatars of rooms we have been invited to.
|
||||||
*/
|
*/
|
||||||
invite_avatars: MediaPreviewValue.On | MediaPreviewValue.Off;
|
invite_avatars?: MediaPreviewValue.On | MediaPreviewValue.Off;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -725,7 +725,7 @@ export default class LegacyCallHandler extends TypedEventEmitter<LegacyCallHandl
|
|||||||
);
|
);
|
||||||
|
|
||||||
finished.then(([allow]) => {
|
finished.then(([allow]) => {
|
||||||
SettingsStore.setValue("fallbackICEServerAllowed", null, SettingLevel.DEVICE, allow);
|
SettingsStore.setValue("fallbackICEServerAllowed", null, SettingLevel.DEVICE, allow ?? null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -142,6 +142,16 @@ interface EmittedEvents {
|
|||||||
[NotifierEvent.NotificationHiddenChange]: (hidden: boolean) => void;
|
[NotifierEvent.NotificationHiddenChange]: (hidden: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type representing a notification sound setting
|
||||||
|
*/
|
||||||
|
export type NotificationSound = {
|
||||||
|
url: string;
|
||||||
|
name?: string;
|
||||||
|
type?: string;
|
||||||
|
size?: number;
|
||||||
|
};
|
||||||
|
|
||||||
class NotifierClass extends TypedEventEmitter<keyof EmittedEvents, EmittedEvents> {
|
class NotifierClass extends TypedEventEmitter<keyof EmittedEvents, EmittedEvents> {
|
||||||
private notifsByRoom: Record<string, Notification[]> = {};
|
private notifsByRoom: Record<string, Notification[]> = {};
|
||||||
|
|
||||||
@@ -223,12 +233,7 @@ class NotifierClass extends TypedEventEmitter<keyof EmittedEvents, EmittedEvents
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSoundForRoom(roomId: string): {
|
public getSoundForRoom(roomId: string): NotificationSound | null {
|
||||||
url: string;
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
size: number;
|
|
||||||
} | null {
|
|
||||||
// We do no caching here because the SDK caches setting
|
// We do no caching here because the SDK caches setting
|
||||||
// and the browser will cache the sound.
|
// and the browser will cache the sound.
|
||||||
const content = SettingsStore.getValue("notificationSound", roomId);
|
const content = SettingsStore.getValue("notificationSound", roomId);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import SettingsStore from "./settings/SettingsStore";
|
|||||||
import { type ScreenName } from "./PosthogTrackers";
|
import { type ScreenName } from "./PosthogTrackers";
|
||||||
import { type ActionPayload } from "./dispatcher/payloads";
|
import { type ActionPayload } from "./dispatcher/payloads";
|
||||||
import { Action } from "./dispatcher/actions";
|
import { Action } from "./dispatcher/actions";
|
||||||
import { type SettingUpdatedPayload } from "./dispatcher/payloads/SettingUpdatedPayload";
|
import { isSettingUpdatedPayload, type SettingUpdatedPayload } from "./dispatcher/payloads/SettingUpdatedPayload";
|
||||||
import dis from "./dispatcher/dispatcher";
|
import dis from "./dispatcher/dispatcher";
|
||||||
import { Layout } from "./settings/enums/Layout";
|
import { Layout } from "./settings/enums/Layout";
|
||||||
|
|
||||||
@@ -199,8 +199,8 @@ export class PosthogAnalytics {
|
|||||||
const settingsPayload = payload as SettingUpdatedPayload;
|
const settingsPayload = payload as SettingUpdatedPayload;
|
||||||
if (["layout", "useCompactLayout"].includes(settingsPayload.settingName)) {
|
if (["layout", "useCompactLayout"].includes(settingsPayload.settingName)) {
|
||||||
this.onLayoutUpdated();
|
this.onLayoutUpdated();
|
||||||
} else if (settingsPayload.settingName === "urlPreviewsEnabled" && !settingsPayload.roomId) {
|
} else if (isSettingUpdatedPayload(settingsPayload, "urlPreviewsEnabled") && !settingsPayload.roomId) {
|
||||||
this.onUrlPreviewSettingUpdated(settingsPayload.newValue as boolean);
|
this.onUrlPreviewSettingUpdated(settingsPayload.newValue);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ export default class ManageEventIndexDialog extends React.Component<IProps, ISta
|
|||||||
|
|
||||||
private onCrawlerSleepTimeChange = (e: ChangeEvent<HTMLInputElement>): void => {
|
private onCrawlerSleepTimeChange = (e: ChangeEvent<HTMLInputElement>): void => {
|
||||||
this.setState({ crawlerSleepTime: parseInt(e.target.value, 10) });
|
this.setState({ crawlerSleepTime: parseInt(e.target.value, 10) });
|
||||||
SettingsStore.setValue("crawlerSleepTime", null, SettingLevel.DEVICE, e.target.value);
|
SettingsStore.setValue("crawlerSleepTime", null, SettingLevel.DEVICE, e.target.valueAsNumber);
|
||||||
};
|
};
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
|
|||||||
@@ -1794,11 +1794,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
SettingsStore.watchSetting(
|
SettingsStore.watchSetting(
|
||||||
"blacklistUnverifiedDevices",
|
"blacklistUnverifiedDevices",
|
||||||
null,
|
null,
|
||||||
(_settingName, _roomId, atLevel, blacklistEnabled: boolean) => {
|
(_settingName, _roomId, atLevel, blacklistEnabled) => {
|
||||||
if (atLevel != SettingLevel.DEVICE) {
|
if (atLevel != SettingLevel.DEVICE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
crypto.globalBlacklistUnverifiedDevices = blacklistEnabled;
|
crypto.globalBlacklistUnverifiedDevices = !!blacklistEnabled;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ function LayoutSelector(): JSX.Element {
|
|||||||
<Root
|
<Root
|
||||||
className="mx_LayoutSwitcher_LayoutSelector"
|
className="mx_LayoutSwitcher_LayoutSelector"
|
||||||
onChange={async (evt) => {
|
onChange={async (evt) => {
|
||||||
// We don't have any file in the form, we can cast it as string safely
|
// We don't have any file in the form, we can cast it as Layout safely
|
||||||
const newLayout = new FormData(evt.currentTarget).get("layout") as string | null;
|
const newLayout = new FormData(evt.currentTarget).get("layout") as Layout;
|
||||||
await SettingsStore.setValue("layout", null, SettingLevel.DEVICE, newLayout);
|
await SettingsStore.setValue("layout", null, SettingLevel.DEVICE, newLayout);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -211,17 +211,17 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps,
|
|||||||
|
|
||||||
private onAutocompleteDelayChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
|
private onAutocompleteDelayChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
this.setState({ autocompleteDelay: e.target.value });
|
this.setState({ autocompleteDelay: e.target.value });
|
||||||
SettingsStore.setValue("autocompleteDelay", null, SettingLevel.DEVICE, e.target.value);
|
SettingsStore.setValue("autocompleteDelay", null, SettingLevel.DEVICE, e.target.valueAsNumber);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onReadMarkerInViewThresholdMs = (e: React.ChangeEvent<HTMLInputElement>): void => {
|
private onReadMarkerInViewThresholdMs = (e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
this.setState({ readMarkerInViewThresholdMs: e.target.value });
|
this.setState({ readMarkerInViewThresholdMs: e.target.value });
|
||||||
SettingsStore.setValue("readMarkerInViewThresholdMs", null, SettingLevel.DEVICE, e.target.value);
|
SettingsStore.setValue("readMarkerInViewThresholdMs", null, SettingLevel.DEVICE, e.target.valueAsNumber);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onReadMarkerOutOfViewThresholdMs = (e: React.ChangeEvent<HTMLInputElement>): void => {
|
private onReadMarkerOutOfViewThresholdMs = (e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
this.setState({ readMarkerOutOfViewThresholdMs: e.target.value });
|
this.setState({ readMarkerOutOfViewThresholdMs: e.target.value });
|
||||||
SettingsStore.setValue("readMarkerOutOfViewThresholdMs", null, SettingLevel.DEVICE, e.target.value);
|
SettingsStore.setValue("readMarkerOutOfViewThresholdMs", null, SettingLevel.DEVICE, e.target.valueAsNumber);
|
||||||
};
|
};
|
||||||
|
|
||||||
private renderGroup(settingIds: BooleanSettingKey[], level = SettingLevel.ACCOUNT): JSX.Element {
|
private renderGroup(settingIds: BooleanSettingKey[], level = SettingLevel.ACCOUNT): JSX.Element {
|
||||||
|
|||||||
@@ -340,7 +340,7 @@ export class DeviceListener {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private onRecordClientInformationSettingChange: CallbackFn = (
|
private onRecordClientInformationSettingChange: CallbackFn<"deviceClientInformationOptIn"> = (
|
||||||
_originalSettingName,
|
_originalSettingName,
|
||||||
_roomId,
|
_roomId,
|
||||||
_level,
|
_level,
|
||||||
|
|||||||
@@ -9,14 +9,26 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
import { type ActionPayload } from "../payloads";
|
import { type ActionPayload } from "../payloads";
|
||||||
import { type Action } from "../actions";
|
import { type Action } from "../actions";
|
||||||
import { type SettingLevel } from "../../settings/SettingLevel";
|
import { type SettingLevel } from "../../settings/SettingLevel";
|
||||||
import { type SettingValueType } from "../../settings/Settings";
|
import { type SettingKey, type Settings } from "../../settings/Settings";
|
||||||
|
|
||||||
export interface SettingUpdatedPayload extends ActionPayload {
|
export interface SettingUpdatedPayload<S extends SettingKey = SettingKey> extends ActionPayload {
|
||||||
action: Action.SettingUpdated;
|
action: Action.SettingUpdated;
|
||||||
|
|
||||||
settingName: string;
|
settingName: S;
|
||||||
roomId: string | null;
|
roomId: string | null;
|
||||||
level: SettingLevel;
|
level: SettingLevel;
|
||||||
newValueAtLevel: SettingLevel;
|
newValueAtLevel: Settings[S]["default"];
|
||||||
newValue: SettingValueType;
|
newValue: Settings[S]["default"];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type guard to check if a payload is a SettingUpdatedPayload for a specific setting.
|
||||||
|
* @param payload the payload to assert
|
||||||
|
* @param settingName the setting name to check for
|
||||||
|
*/
|
||||||
|
export function isSettingUpdatedPayload<S extends SettingKey>(
|
||||||
|
payload: SettingUpdatedPayload<any>,
|
||||||
|
settingName: S,
|
||||||
|
): payload is SettingUpdatedPayload<S> {
|
||||||
|
return payload.settingName === settingName;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const STORAGE_LIMIT = 100;
|
|||||||
function migrate(): void {
|
function migrate(): void {
|
||||||
const data: ILegacyFormat = JSON.parse(window.localStorage.mx_reaction_count || "{}");
|
const data: ILegacyFormat = JSON.parse(window.localStorage.mx_reaction_count || "{}");
|
||||||
const sorted = Object.entries(data).sort(([, [count1, date1]], [, [count2, date2]]) => date2 - date1);
|
const sorted = Object.entries(data).sort(([, [count1, date1]], [, [count2, date2]]) => date2 - date1);
|
||||||
const newFormat = sorted.map(([emoji, [count, date]]) => [emoji, count]);
|
const newFormat = sorted.map<RecentEmojiData[number]>(([emoji, [count, date]]) => [emoji, count]);
|
||||||
SettingsStore.setValue(SETTING_NAME, null, SettingLevel.ACCOUNT, newFormat.slice(0, STORAGE_LIMIT));
|
SettingsStore.setValue(SETTING_NAME, null, SettingLevel.ACCOUNT, newFormat.slice(0, STORAGE_LIMIT));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ export function add(emoji: string): void {
|
|||||||
const recents = getRecentEmoji();
|
const recents = getRecentEmoji();
|
||||||
const i = recents.findIndex(([e]) => e === emoji);
|
const i = recents.findIndex(([e]) => e === emoji);
|
||||||
|
|
||||||
let newEntry;
|
let newEntry: RecentEmojiData[number];
|
||||||
if (i >= 0) {
|
if (i >= 0) {
|
||||||
// first remove the existing tuple so that we can increment it and push it to the front
|
// first remove the existing tuple so that we can increment it and push it to the front
|
||||||
[newEntry] = recents.splice(i, 1);
|
[newEntry] = recents.splice(i, 1);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { logger } from "matrix-js-sdk/src/logger";
|
|||||||
|
|
||||||
import { MatrixClientPeg } from "../MatrixClientPeg";
|
import { MatrixClientPeg } from "../MatrixClientPeg";
|
||||||
import { ALL_RULE_TYPES, BanList } from "./BanList";
|
import { ALL_RULE_TYPES, BanList } from "./BanList";
|
||||||
import SettingsStore from "../settings/SettingsStore";
|
import SettingsStore, { type CallbackFn } from "../settings/SettingsStore";
|
||||||
import { _t } from "../languageHandler";
|
import { _t } from "../languageHandler";
|
||||||
import dis from "../dispatcher/dispatcher";
|
import dis from "../dispatcher/dispatcher";
|
||||||
import { SettingLevel } from "../settings/SettingLevel";
|
import { SettingLevel } from "../settings/SettingLevel";
|
||||||
@@ -38,7 +38,7 @@ export class Mjolnir {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public start(): void {
|
public start(): void {
|
||||||
this.mjolnirWatchRef = SettingsStore.watchSetting("mjolnirRooms", null, this.onListsChanged.bind(this));
|
this.mjolnirWatchRef = SettingsStore.watchSetting("mjolnirRooms", null, this.onListsChanged);
|
||||||
|
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
dis.dispatch<DoAfterSyncPreparedPayload<ActionPayload>>({
|
dis.dispatch<DoAfterSyncPreparedPayload<ActionPayload>>({
|
||||||
@@ -130,15 +130,10 @@ export class Mjolnir {
|
|||||||
this.updateLists(this._roomIds);
|
this.updateLists(this._roomIds);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onListsChanged(
|
private onListsChanged: CallbackFn<"mjolnirRooms"> = (settingName, roomId, atLevel, newValue): void => {
|
||||||
settingName: string,
|
|
||||||
roomId: string | null,
|
|
||||||
atLevel: SettingLevel,
|
|
||||||
newValue: string[],
|
|
||||||
): void {
|
|
||||||
// We know that ban lists are only recorded at one level so we don't need to re-eval them
|
// We know that ban lists are only recorded at one level so we don't need to re-eval them
|
||||||
this.updateLists(newValue);
|
this.updateLists(newValue ?? []);
|
||||||
}
|
};
|
||||||
|
|
||||||
private updateLists(listRoomIds: string[]): void {
|
private updateLists(listRoomIds: string[]): void {
|
||||||
if (!MatrixClientPeg.get()) return;
|
if (!MatrixClientPeg.get()) return;
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ import { type ComputedInviteConfig } from "../@types/invite-rules.ts";
|
|||||||
import BlockInvitesConfigController from "./controllers/BlockInvitesConfigController.ts";
|
import BlockInvitesConfigController from "./controllers/BlockInvitesConfigController.ts";
|
||||||
import RequiresSettingsController from "./controllers/RequiresSettingsController.ts";
|
import RequiresSettingsController from "./controllers/RequiresSettingsController.ts";
|
||||||
import { type OrderedCustomSections, type CustomSectionsData } from "../stores/room-list-v3/section.ts";
|
import { type OrderedCustomSections, type CustomSectionsData } from "../stores/room-list-v3/section.ts";
|
||||||
|
import { type NotificationSound } from "../Notifier.ts";
|
||||||
|
|
||||||
export const defaultWatchManager = new WatchManager();
|
export const defaultWatchManager = new WatchManager();
|
||||||
|
|
||||||
@@ -311,15 +312,7 @@ export interface Settings {
|
|||||||
"urlPreviewsEnabled_e2ee": IBaseSetting<boolean>;
|
"urlPreviewsEnabled_e2ee": IBaseSetting<boolean>;
|
||||||
"notificationsEnabled": IBaseSetting<boolean>;
|
"notificationsEnabled": IBaseSetting<boolean>;
|
||||||
"deviceNotificationsEnabled": IBaseSetting<boolean>;
|
"deviceNotificationsEnabled": IBaseSetting<boolean>;
|
||||||
"notificationSound": IBaseSetting<
|
"notificationSound": IBaseSetting<NotificationSound | false>;
|
||||||
| {
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
size: number;
|
|
||||||
url: string;
|
|
||||||
}
|
|
||||||
| false
|
|
||||||
>;
|
|
||||||
"notificationBodyEnabled": IBaseSetting<boolean>;
|
"notificationBodyEnabled": IBaseSetting<boolean>;
|
||||||
"audioNotificationsEnabled": IBaseSetting<boolean>;
|
"audioNotificationsEnabled": IBaseSetting<boolean>;
|
||||||
"enableWidgetScreenshots": IBaseSetting<boolean>;
|
"enableWidgetScreenshots": IBaseSetting<boolean>;
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export const LEVEL_ORDER = [
|
|||||||
SettingLevel.DEFAULT,
|
SettingLevel.DEFAULT,
|
||||||
];
|
];
|
||||||
|
|
||||||
function getLevelOrder(setting: ISetting): SettingLevel[] {
|
function getLevelOrder(setting: Settings[keyof Settings]): SettingLevel[] {
|
||||||
// Settings which support only a single setting level are inherently ordered
|
// Settings which support only a single setting level are inherently ordered
|
||||||
if (setting.supportedLevelsAreOrdered || setting.supportedLevels.length === 1) {
|
if (setting.supportedLevelsAreOrdered || setting.supportedLevels.length === 1) {
|
||||||
// return a copy to prevent callers from modifying the array
|
// return a copy to prevent callers from modifying the array
|
||||||
@@ -88,12 +88,12 @@ function getLevelOrder(setting: ISetting): SettingLevel[] {
|
|||||||
return LEVEL_ORDER;
|
return LEVEL_ORDER;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CallbackFn = (
|
export type CallbackFn<S extends SettingKey> = (
|
||||||
settingName: SettingKey,
|
settingName: S,
|
||||||
roomId: string | null,
|
roomId: string | null,
|
||||||
atLevel: SettingLevel,
|
atLevel: SettingLevel,
|
||||||
newValAtLevel: any,
|
newValAtLevel: Settings[S]["default"] | null,
|
||||||
newVal: any,
|
newVal: Settings[S]["default"] | null,
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
type HandlerMap = Partial<{
|
type HandlerMap = Partial<{
|
||||||
@@ -167,7 +167,11 @@ export default class SettingsStore {
|
|||||||
* if the change in value is worthwhile enough to react upon.
|
* if the change in value is worthwhile enough to react upon.
|
||||||
* @returns {string} A reference to the watcher that was employed.
|
* @returns {string} A reference to the watcher that was employed.
|
||||||
*/
|
*/
|
||||||
public static watchSetting(settingName: SettingKey, roomId: string | null, callbackFn: CallbackFn): string {
|
public static watchSetting<S extends SettingKey>(
|
||||||
|
settingName: S,
|
||||||
|
roomId: string | null,
|
||||||
|
callbackFn: CallbackFn<S>,
|
||||||
|
): string {
|
||||||
const setting = SETTINGS[settingName];
|
const setting = SETTINGS[settingName];
|
||||||
if (!setting) throw new Error(`${settingName} is not a setting`);
|
if (!setting) throw new Error(`${settingName} is not a setting`);
|
||||||
|
|
||||||
@@ -175,7 +179,11 @@ export default class SettingsStore {
|
|||||||
|
|
||||||
const watcherId = `${new Date().getTime()}_${SettingsStore.watcherCount++}_${finalSettingName}_${roomId}`;
|
const watcherId = `${new Date().getTime()}_${SettingsStore.watcherCount++}_${finalSettingName}_${roomId}`;
|
||||||
|
|
||||||
const localizedCallback = (changedInRoomId: string | null, atLevel: SettingLevel, newValAtLevel: any): void => {
|
const localizedCallback = (
|
||||||
|
changedInRoomId: string | null,
|
||||||
|
atLevel: SettingLevel,
|
||||||
|
newValAtLevel: Settings[S]["default"],
|
||||||
|
): void => {
|
||||||
if (!SettingsStore.doesSettingSupportLevel(settingName, atLevel)) {
|
if (!SettingsStore.doesSettingSupportLevel(settingName, atLevel)) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
`Setting handler notified for an update of an invalid setting level: ` +
|
`Setting handler notified for an update of an invalid setting level: ` +
|
||||||
@@ -220,7 +228,7 @@ export default class SettingsStore {
|
|||||||
* @param {string} settingName The setting name to monitor.
|
* @param {string} settingName The setting name to monitor.
|
||||||
* @param {String} roomId The room ID to monitor for changes in. Use null for all rooms.
|
* @param {String} roomId The room ID to monitor for changes in. Use null for all rooms.
|
||||||
*/
|
*/
|
||||||
public static monitorSetting(settingName: SettingKey, roomId: string | null): void {
|
public static monitorSetting<S extends SettingKey>(settingName: S, roomId: string | null): void {
|
||||||
roomId = roomId || null; // the thing wants null specifically to work, so appease it.
|
roomId = roomId || null; // the thing wants null specifically to work, so appease it.
|
||||||
|
|
||||||
if (!this.monitors.has(settingName)) this.monitors.set(settingName, new Map());
|
if (!this.monitors.has(settingName)) this.monitors.set(settingName, new Map());
|
||||||
@@ -228,7 +236,7 @@ export default class SettingsStore {
|
|||||||
const registerWatcher = (): void => {
|
const registerWatcher = (): void => {
|
||||||
this.monitors.get(settingName)!.set(
|
this.monitors.get(settingName)!.set(
|
||||||
roomId,
|
roomId,
|
||||||
SettingsStore.watchSetting(
|
SettingsStore.watchSetting<S>(
|
||||||
settingName,
|
settingName,
|
||||||
roomId,
|
roomId,
|
||||||
(settingName, inRoomId, level, newValueAtLevel, newValue) => {
|
(settingName, inRoomId, level, newValueAtLevel, newValue) => {
|
||||||
@@ -449,11 +457,10 @@ export default class SettingsStore {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the default value of a setting.
|
* Gets the default value of a setting.
|
||||||
* @param {string} settingName The name of the setting to read the value of.
|
* @param settingName The name of the setting to read the value of.
|
||||||
* @param {String} roomId The room ID to read the setting value in, may be null.
|
* @return The default value
|
||||||
* @return {*} The default value
|
|
||||||
*/
|
*/
|
||||||
public static getDefaultValue(settingName: SettingKey): any {
|
public static getDefaultValue<S extends SettingKey>(settingName: S): Settings[S]["default"] {
|
||||||
// Verify that the setting is actually a setting
|
// Verify that the setting is actually a setting
|
||||||
if (!SETTINGS[settingName]) {
|
if (!SETTINGS[settingName]) {
|
||||||
throw new Error("Setting '" + settingName + "' does not appear to be a setting.");
|
throw new Error("Setting '" + settingName + "' does not appear to be a setting.");
|
||||||
@@ -462,13 +469,13 @@ export default class SettingsStore {
|
|||||||
return SETTINGS[settingName].default;
|
return SETTINGS[settingName].default;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static getFinalValue(
|
private static getFinalValue<S extends SettingKey>(
|
||||||
setting: ISetting,
|
setting: Settings[S],
|
||||||
level: SettingLevel,
|
level: SettingLevel,
|
||||||
roomId: string | null,
|
roomId: string | null,
|
||||||
calculatedValue: any,
|
calculatedValue: Settings[S]["default"],
|
||||||
calculatedAtLevel: SettingLevel | null,
|
calculatedAtLevel: SettingLevel | null,
|
||||||
): any {
|
): Settings[S]["default"] {
|
||||||
let resultingValue = calculatedValue;
|
let resultingValue = calculatedValue;
|
||||||
|
|
||||||
if (setting.controller) {
|
if (setting.controller) {
|
||||||
@@ -480,25 +487,22 @@ export default class SettingsStore {
|
|||||||
return resultingValue;
|
return resultingValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* eslint-disable valid-jsdoc */ //https://github.com/eslint/eslint/issues/7307
|
|
||||||
/**
|
/**
|
||||||
* Sets the value for a setting. The room ID is optional if the setting is not being
|
* Sets the value for a setting. The room ID is optional if the setting is not being
|
||||||
* set for a particular room, otherwise it should be supplied. The value may be null
|
* set for a particular room, otherwise it should be supplied. The value may be null
|
||||||
* to indicate that the level should no longer have an override.
|
* to indicate that the level should no longer have an override.
|
||||||
* @param {string} settingName The name of the setting to change.
|
* @param settingName The name of the setting to change.
|
||||||
* @param {String} roomId The room ID to change the value in, may be null.
|
* @param roomId The room ID to change the value in, may be null.
|
||||||
* @param {SettingLevel} level The level
|
* @param level The level
|
||||||
* to change the value at.
|
* to change the value at.
|
||||||
* @param {*} value The new value of the setting, may be null.
|
* @param value The new value of the setting, may be null.
|
||||||
* @return {Promise} Resolves when the setting has been changed.
|
* @return Resolves when the setting has been changed.
|
||||||
*/
|
*/
|
||||||
|
public static async setValue<S extends SettingKey>(
|
||||||
/* eslint-enable valid-jsdoc */
|
settingName: S,
|
||||||
public static async setValue(
|
|
||||||
settingName: SettingKey,
|
|
||||||
roomId: string | null,
|
roomId: string | null,
|
||||||
level: SettingLevel,
|
level: SettingLevel,
|
||||||
value: any,
|
value: Settings[S]["default"] | null,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Verify that the setting is actually a setting
|
// Verify that the setting is actually a setting
|
||||||
const setting = SETTINGS[settingName];
|
const setting = SETTINGS[settingName];
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ export default class MediaPreviewConfigController extends MatrixClientBackedCont
|
|||||||
const validMediaPreviews = Object.values(MediaPreviewValue);
|
const validMediaPreviews = Object.values(MediaPreviewValue);
|
||||||
const validInviteAvatars = [MediaPreviewValue.Off, MediaPreviewValue.On];
|
const validInviteAvatars = [MediaPreviewValue.Off, MediaPreviewValue.On];
|
||||||
return {
|
return {
|
||||||
invite_avatars: validInviteAvatars.includes(inviteAvatars) ? inviteAvatars : undefined,
|
invite_avatars: validInviteAvatars.includes(inviteAvatars!) ? inviteAvatars : undefined,
|
||||||
media_previews: validMediaPreviews.includes(mediaPreviews) ? mediaPreviews : undefined,
|
media_previews: validMediaPreviews.includes(mediaPreviews!) ? mediaPreviews : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,8 +60,8 @@ export type IRightPanelForRoomStored = {
|
|||||||
history: Array<IRightPanelCardStored>;
|
history: Array<IRightPanelCardStored>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function convertToStorePanel(cacheRoom?: IRightPanelForRoom): IRightPanelForRoomStored | undefined {
|
export function convertToStorePanel(cacheRoom?: IRightPanelForRoom): IRightPanelForRoomStored | null {
|
||||||
if (!cacheRoom) return undefined;
|
if (!cacheRoom) return null;
|
||||||
const storeHistory = [...cacheRoom.history].map((panelState) => convertCardToStore(panelState));
|
const storeHistory = [...cacheRoom.history].map((panelState) => convertCardToStore(panelState));
|
||||||
return { isOpen: cacheRoom.isOpen, history: storeHistory };
|
return { isOpen: cacheRoom.isOpen, history: storeHistory };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<EmptyObject> implem
|
|||||||
"feature_dynamic_room_predecessors",
|
"feature_dynamic_room_predecessors",
|
||||||
null,
|
null,
|
||||||
(_settingName, _roomId, _level, _newValAtLevel, newVal) => {
|
(_settingName, _roomId, _level, _newValAtLevel, newVal) => {
|
||||||
this.msc3946ProcessDynamicPredecessor = newVal;
|
this.msc3946ProcessDynamicPredecessor = !!newVal;
|
||||||
this.regenerateAllLists({ trigger: true });
|
this.regenerateAllLists({ trigger: true });
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ export class EventContentBodyViewModel
|
|||||||
"TextualBody.enableBigEmoji",
|
"TextualBody.enableBigEmoji",
|
||||||
null,
|
null,
|
||||||
(_settingName, _roomId, _level, _newValAtLevel, newVal) => {
|
(_settingName, _roomId, _level, _newValAtLevel, newVal) => {
|
||||||
this.setEnableBigEmoji(newVal);
|
this.setEnableBigEmoji(!!newVal);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
this.disposables.track(() => SettingsStore.unwatchSetting(enableBigEmojiWatcherRef));
|
this.disposables.track(() => SettingsStore.unwatchSetting(enableBigEmojiWatcherRef));
|
||||||
@@ -240,7 +240,7 @@ export class EventContentBodyViewModel
|
|||||||
"Pill.shouldShowPillAvatar",
|
"Pill.shouldShowPillAvatar",
|
||||||
null,
|
null,
|
||||||
(_settingName, _roomId, _level, _newValAtLevel, newVal) => {
|
(_settingName, _roomId, _level, _newValAtLevel, newVal) => {
|
||||||
this.setShouldShowPillAvatar(newVal);
|
this.setShouldShowPillAvatar(!!newVal);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
this.disposables.track(() => SettingsStore.unwatchSetting(shouldShowPillAvatarWatcherRef));
|
this.disposables.track(() => SettingsStore.unwatchSetting(shouldShowPillAvatarWatcherRef));
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export class RedactedBodyViewModel
|
|||||||
(_settingName, _roomId, _level, _newValAtLevel, newVal) => {
|
(_settingName, _roomId, _level, _newValAtLevel, newVal) => {
|
||||||
if (this.showTwelveHour === newVal) return;
|
if (this.showTwelveHour === newVal) return;
|
||||||
|
|
||||||
this.showTwelveHour = newVal;
|
this.showTwelveHour = !!newVal;
|
||||||
this.updateTooltip();
|
this.updateTooltip();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -296,7 +296,7 @@ export class UrlPreviewGroupViewModel
|
|||||||
null,
|
null,
|
||||||
(_setting, _roomid, _level, compactLayout) => {
|
(_setting, _roomid, _level, compactLayout) => {
|
||||||
this.snapshot.merge({
|
this.snapshot.merge({
|
||||||
compactLayout,
|
compactLayout: !!compactLayout,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export class DateSeparatorViewModel
|
|||||||
"feature_jump_to_date",
|
"feature_jump_to_date",
|
||||||
null,
|
null,
|
||||||
(_settingName, _roomId, _level, _newValAtLevel, newVal) => {
|
(_settingName, _roomId, _level, _newValAtLevel, newVal) => {
|
||||||
this.jumpToDateEnabled = newVal;
|
this.jumpToDateEnabled = !!newVal;
|
||||||
this.updateSnapshot();
|
this.updateSnapshot();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -89,7 +89,7 @@ export class DateSeparatorViewModel
|
|||||||
UIFeature.TimelineEnableRelativeDates,
|
UIFeature.TimelineEnableRelativeDates,
|
||||||
null,
|
null,
|
||||||
(_settingName, _roomId, _level, _newValAtLevel, newVal) => {
|
(_settingName, _roomId, _level, _newValAtLevel, newVal) => {
|
||||||
this.relativeDatesEnabled = newVal;
|
this.relativeDatesEnabled = !!newVal;
|
||||||
this.updateSnapshot();
|
this.updateSnapshot();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -557,7 +557,7 @@ describe("<MatrixChat />", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should not persist device language when not available", async () => {
|
it("should not persist device language when not available", async () => {
|
||||||
await SettingsStore.setValue("language", null, SettingLevel.DEVICE, undefined);
|
await SettingsStore.setValue("language", null, SettingLevel.DEVICE, null);
|
||||||
const languageBefore = SettingsStore.getValueAt(SettingLevel.DEVICE, "language", null, true, true);
|
const languageBefore = SettingsStore.getValueAt(SettingLevel.DEVICE, "language", null, true, true);
|
||||||
|
|
||||||
jest.spyOn(Lifecycle, "attemptDelegatedAuthLogin");
|
jest.spyOn(Lifecycle, "attemptDelegatedAuthLogin");
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ describe("<UserSettingsDialog />", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("watches settings", async () => {
|
it("watches settings", async () => {
|
||||||
const watchSettingCallbacks: Record<string, CallbackFn> = {};
|
const watchSettingCallbacks: Record<string, CallbackFn<any>> = {};
|
||||||
|
|
||||||
mockSettingsStore.watchSetting.mockImplementation((settingName, roomId, callback) => {
|
mockSettingsStore.watchSetting.mockImplementation((settingName, roomId, callback) => {
|
||||||
watchSettingCallbacks[settingName] = callback;
|
watchSettingCallbacks[settingName] = callback;
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ describe("ReleaseAnnouncementStore", () => {
|
|||||||
settings = {
|
settings = {
|
||||||
releaseAnnouncementData: {},
|
releaseAnnouncementData: {},
|
||||||
};
|
};
|
||||||
const watchCallbacks: Array<CallbackFn> = [];
|
const watchCallbacks: Array<CallbackFn<any>> = [];
|
||||||
|
|
||||||
mocked(SettingsStore.getValue).mockImplementation((setting: string) => {
|
mocked(SettingsStore.getValue).mockImplementation((setting: string) => {
|
||||||
return settings[setting];
|
return settings[setting];
|
||||||
|
|||||||
@@ -203,13 +203,11 @@ describe("RoomListStore", () => {
|
|||||||
let featureFlagValue = false;
|
let featureFlagValue = false;
|
||||||
jest.spyOn(SettingsStore, "getValue").mockImplementation(() => featureFlagValue);
|
jest.spyOn(SettingsStore, "getValue").mockImplementation(() => featureFlagValue);
|
||||||
|
|
||||||
let watchCallback: CallbackFn | undefined;
|
let watchCallback: CallbackFn<any> | undefined;
|
||||||
jest.spyOn(SettingsStore, "watchSetting").mockImplementation(
|
jest.spyOn(SettingsStore, "watchSetting").mockImplementation((_settingName, _roomId, callbackFn) => {
|
||||||
(_settingName: string, _roomId: string | null, callbackFn: CallbackFn) => {
|
watchCallback = callbackFn;
|
||||||
watchCallback = callbackFn;
|
return "dyn_pred_ref";
|
||||||
return "dyn_pred_ref";
|
});
|
||||||
},
|
|
||||||
);
|
|
||||||
jest.spyOn(SettingsStore, "unwatchSetting");
|
jest.spyOn(SettingsStore, "unwatchSetting");
|
||||||
|
|
||||||
const { store } = createStore();
|
const { store } = createStore();
|
||||||
|
|||||||
@@ -664,7 +664,7 @@ describe("RoomListItemViewModel", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should update sections when OrderedCustomSections setting changes", () => {
|
it("should update sections when OrderedCustomSections setting changes", () => {
|
||||||
let watchCallback: CallbackFn = () => {};
|
let watchCallback: CallbackFn<"RoomList.OrderedCustomSections"> = () => {};
|
||||||
jest.spyOn(SettingsStore, "watchSetting").mockImplementation((setting, _room, callback) => {
|
jest.spyOn(SettingsStore, "watchSetting").mockImplementation((setting, _room, callback) => {
|
||||||
if (setting === "RoomList.OrderedCustomSections") watchCallback = callback;
|
if (setting === "RoomList.OrderedCustomSections") watchCallback = callback;
|
||||||
return "watcher-id";
|
return "watcher-id";
|
||||||
|
|||||||
Reference in New Issue
Block a user