2021-12-12 23:55:58 +00:00
|
|
|
import { action, computed, makeAutoObservable, ObservableMap } from "mobx";
|
|
|
|
|
|
|
|
import { mapToRecord } from "../../lib/conversion";
|
|
|
|
|
2021-12-24 02:05:18 +00:00
|
|
|
import {
|
|
|
|
LegacyAppearanceOptions,
|
|
|
|
legacyMigrateAppearance,
|
|
|
|
legacyMigrateTheme,
|
|
|
|
LegacyTheme,
|
|
|
|
LegacyThemeOptions,
|
|
|
|
} from "../legacy/redux";
|
|
|
|
|
2021-12-15 18:23:05 +00:00
|
|
|
import { Fonts, MonospaceFonts, Overrides } from "../../context/Theme";
|
2021-12-12 23:55:58 +00:00
|
|
|
|
2021-12-16 22:05:31 +00:00
|
|
|
import { EmojiPack } from "../../components/common/Emoji";
|
|
|
|
|
2021-12-24 02:05:18 +00:00
|
|
|
import { MIGRATIONS } from "../State";
|
2021-12-12 23:55:58 +00:00
|
|
|
import Persistent from "../interfaces/Persistent";
|
|
|
|
import Store from "../interfaces/Store";
|
2021-12-24 02:05:18 +00:00
|
|
|
import Syncable from "../interfaces/Syncable";
|
2021-12-16 22:05:31 +00:00
|
|
|
import SAudio, { SoundOptions } from "./helpers/SAudio";
|
2021-12-17 10:20:55 +00:00
|
|
|
import SSecurity from "./helpers/SSecurity";
|
2021-12-13 17:27:06 +00:00
|
|
|
import STheme from "./helpers/STheme";
|
2021-12-12 23:55:58 +00:00
|
|
|
|
2021-12-21 12:31:14 +00:00
|
|
|
export interface ISettings {
|
2021-12-12 23:55:58 +00:00
|
|
|
"notifications:desktop": boolean;
|
|
|
|
"notifications:sounds": SoundOptions;
|
|
|
|
|
|
|
|
"appearance:emoji": EmojiPack;
|
|
|
|
"appearance:ligatures": boolean;
|
2021-12-24 12:41:07 +00:00
|
|
|
"appearance:seasonal": boolean;
|
2021-12-12 23:55:58 +00:00
|
|
|
|
2021-12-13 17:27:06 +00:00
|
|
|
"appearance:theme:base": "dark" | "light";
|
|
|
|
"appearance:theme:overrides": Partial<Overrides>;
|
|
|
|
"appearance:theme:light": boolean;
|
|
|
|
"appearance:theme:font": Fonts;
|
|
|
|
"appearance:theme:monoFont": MonospaceFonts;
|
|
|
|
"appearance:theme:css": string;
|
2021-12-17 10:20:55 +00:00
|
|
|
|
|
|
|
"security:trustedOrigins": string[];
|
2021-12-13 17:27:06 +00:00
|
|
|
}
|
2021-12-12 23:55:58 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Manages user settings.
|
|
|
|
*/
|
2021-12-24 02:05:18 +00:00
|
|
|
export default class Settings
|
|
|
|
implements Store, Persistent<ISettings>, Syncable
|
|
|
|
{
|
2021-12-12 23:55:58 +00:00
|
|
|
private data: ObservableMap<string, unknown>;
|
|
|
|
|
2021-12-13 17:27:06 +00:00
|
|
|
theme: STheme;
|
2021-12-16 22:05:31 +00:00
|
|
|
sounds: SAudio;
|
2021-12-17 10:20:55 +00:00
|
|
|
security: SSecurity;
|
2021-12-13 17:27:06 +00:00
|
|
|
|
2021-12-12 23:55:58 +00:00
|
|
|
/**
|
2021-12-13 17:27:06 +00:00
|
|
|
* Construct new Settings store.
|
2021-12-12 23:55:58 +00:00
|
|
|
*/
|
|
|
|
constructor() {
|
|
|
|
this.data = new ObservableMap();
|
|
|
|
makeAutoObservable(this);
|
2021-12-13 17:27:06 +00:00
|
|
|
|
|
|
|
this.theme = new STheme(this);
|
2021-12-16 22:05:31 +00:00
|
|
|
this.sounds = new SAudio(this);
|
2021-12-17 10:20:55 +00:00
|
|
|
this.security = new SSecurity(this);
|
2021-12-12 23:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
get id() {
|
2021-12-13 17:27:06 +00:00
|
|
|
return "settings";
|
2021-12-12 23:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
toJSON() {
|
|
|
|
return JSON.parse(JSON.stringify(mapToRecord(this.data)));
|
|
|
|
}
|
|
|
|
|
|
|
|
@action hydrate(data: ISettings) {
|
2021-12-21 12:31:14 +00:00
|
|
|
Object.keys(data).forEach(
|
|
|
|
(key) =>
|
2021-12-26 15:03:44 +00:00
|
|
|
typeof (data as any)[key] !== "undefined" &&
|
|
|
|
this.data.set(key, (data as any)[key]),
|
2021-12-12 23:55:58 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-12-13 17:27:06 +00:00
|
|
|
/**
|
|
|
|
* Set a settings key.
|
|
|
|
* @param key Colon-divided key
|
|
|
|
* @param value Value
|
|
|
|
*/
|
2021-12-12 23:55:58 +00:00
|
|
|
@action set<T extends keyof ISettings>(key: T, value: ISettings[T]) {
|
2021-12-13 17:27:06 +00:00
|
|
|
this.data.set(key, value);
|
2021-12-12 23:55:58 +00:00
|
|
|
}
|
|
|
|
|
2021-12-13 17:27:06 +00:00
|
|
|
/**
|
|
|
|
* Get a settings key.
|
|
|
|
* @param key Colon-divided key
|
2021-12-23 21:43:11 +00:00
|
|
|
* @param defaultValue Default value if not present
|
2021-12-13 17:27:06 +00:00
|
|
|
* @returns Value at key
|
|
|
|
*/
|
2021-12-23 21:43:11 +00:00
|
|
|
@computed get<T extends keyof ISettings>(
|
|
|
|
key: T,
|
|
|
|
defaultValue?: ISettings[T],
|
|
|
|
) {
|
|
|
|
return (this.data.get(key) as ISettings[T] | undefined) ?? defaultValue;
|
2021-12-12 23:55:58 +00:00
|
|
|
}
|
|
|
|
|
2021-12-13 17:27:30 +00:00
|
|
|
@action remove<T extends keyof ISettings>(key: T) {
|
|
|
|
this.data.delete(key);
|
|
|
|
}
|
|
|
|
|
2021-12-13 17:27:06 +00:00
|
|
|
/**
|
|
|
|
* Set a value in settings without type-checking.
|
|
|
|
* @param key Colon-divided key
|
|
|
|
* @param value Value
|
|
|
|
*/
|
2021-12-12 23:55:58 +00:00
|
|
|
@action setUnchecked(key: string, value: unknown) {
|
2021-12-13 17:27:06 +00:00
|
|
|
this.data.set(key, value);
|
2021-12-12 23:55:58 +00:00
|
|
|
}
|
|
|
|
|
2021-12-13 17:27:06 +00:00
|
|
|
/**
|
|
|
|
* Get a settings key with unknown type.
|
|
|
|
* @param key Colon-divided key
|
|
|
|
* @returns Value at key
|
|
|
|
*/
|
2021-12-12 23:55:58 +00:00
|
|
|
@computed getUnchecked(key: string) {
|
|
|
|
return this.data.get(key);
|
|
|
|
}
|
2021-12-24 02:05:18 +00:00
|
|
|
|
|
|
|
@action apply(
|
|
|
|
key: "appearance" | "theme",
|
|
|
|
data: unknown,
|
|
|
|
revision: number,
|
|
|
|
) {
|
|
|
|
if (revision < MIGRATIONS.REDUX) {
|
|
|
|
if (key === "appearance") {
|
|
|
|
data = legacyMigrateAppearance(data as LegacyAppearanceOptions);
|
|
|
|
} else {
|
|
|
|
data = legacyMigrateTheme(data as LegacyThemeOptions);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (key === "appearance") {
|
|
|
|
this.remove("appearance:emoji");
|
2021-12-24 12:41:07 +00:00
|
|
|
this.remove("appearance:seasonal");
|
2021-12-24 02:05:18 +00:00
|
|
|
} else {
|
|
|
|
this.remove("appearance:ligatures");
|
|
|
|
this.remove("appearance:theme:base");
|
|
|
|
this.remove("appearance:theme:css");
|
|
|
|
this.remove("appearance:theme:font");
|
|
|
|
this.remove("appearance:theme:light");
|
|
|
|
this.remove("appearance:theme:monoFont");
|
|
|
|
this.remove("appearance:theme:overrides");
|
|
|
|
}
|
|
|
|
|
|
|
|
this.hydrate(data as ISettings);
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed private pullKeys(keys: (keyof ISettings)[]) {
|
|
|
|
const obj: Partial<ISettings> = {};
|
|
|
|
keys.forEach((key) => {
|
|
|
|
let value = this.get(key);
|
|
|
|
if (!value) return;
|
|
|
|
(obj as any)[key] = value;
|
|
|
|
});
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed toSyncable() {
|
|
|
|
const data: Record<"appearance" | "theme", Partial<ISettings>> = {
|
2021-12-24 12:41:07 +00:00
|
|
|
appearance: this.pullKeys([
|
|
|
|
"appearance:emoji",
|
|
|
|
"appearance:seasonal",
|
|
|
|
]),
|
2021-12-24 02:05:18 +00:00
|
|
|
theme: this.pullKeys([
|
|
|
|
"appearance:ligatures",
|
|
|
|
"appearance:theme:base",
|
|
|
|
"appearance:theme:css",
|
|
|
|
"appearance:theme:font",
|
|
|
|
"appearance:theme:light",
|
|
|
|
"appearance:theme:monoFont",
|
|
|
|
"appearance:theme:overrides",
|
|
|
|
]),
|
|
|
|
};
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
2021-12-12 23:55:58 +00:00
|
|
|
}
|