mirror of
https://github.com/revoltchat/revite.git
synced 2024-11-22 15:10:57 -05:00
feat(mobx): start work on settings store
This commit is contained in:
parent
fef2c5997f
commit
26a34032f9
3 changed files with 111 additions and 22 deletions
|
@ -4,11 +4,11 @@ import { createGlobalStyle } from "styled-components";
|
||||||
import { createContext } from "preact";
|
import { createContext } from "preact";
|
||||||
import { useEffect } from "preact/hooks";
|
import { useEffect } from "preact/hooks";
|
||||||
|
|
||||||
|
import { useApplicationState } from "../mobx/State";
|
||||||
|
import { getState } from "../redux";
|
||||||
import { connectState } from "../redux/connector";
|
import { connectState } from "../redux/connector";
|
||||||
|
|
||||||
import { Children } from "../types/Preact";
|
import { Children } from "../types/Preact";
|
||||||
import { fetchManifest, fetchTheme } from "../pages/settings/panes/ThemeShop";
|
|
||||||
import { getState } from "../redux";
|
|
||||||
|
|
||||||
export type Variables =
|
export type Variables =
|
||||||
| "accent"
|
| "accent"
|
||||||
|
@ -57,6 +57,7 @@ export type Fonts =
|
||||||
| "Raleway"
|
| "Raleway"
|
||||||
| "Ubuntu"
|
| "Ubuntu"
|
||||||
| "Comic Neue";
|
| "Comic Neue";
|
||||||
|
|
||||||
export type MonospaceFonts =
|
export type MonospaceFonts =
|
||||||
| "Fira Code"
|
| "Fira Code"
|
||||||
| "Roboto Mono"
|
| "Roboto Mono"
|
||||||
|
@ -285,23 +286,23 @@ export const PRESETS: Record<string, Theme> = {
|
||||||
// todo: store used themes locally
|
// todo: store used themes locally
|
||||||
export function getBaseTheme(name: string): Theme {
|
export function getBaseTheme(name: string): Theme {
|
||||||
if (name in PRESETS) {
|
if (name in PRESETS) {
|
||||||
return PRESETS[name]
|
return PRESETS[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: properly initialize `themes` in state instead of letting it be undefined
|
// TODO: properly initialize `themes` in state instead of letting it be undefined
|
||||||
const themes = getState().themes ?? {}
|
const themes = getState().themes ?? {};
|
||||||
|
|
||||||
if (name in themes) {
|
if (name in themes) {
|
||||||
const { theme } = themes[name];
|
const { theme } = themes[name];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...PRESETS[theme.light ? 'light' : 'dark'],
|
...PRESETS[theme.light ? "light" : "dark"],
|
||||||
...theme
|
...theme,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// how did we get here
|
// how did we get here
|
||||||
return PRESETS['dark']
|
return PRESETS["dark"];
|
||||||
}
|
}
|
||||||
|
|
||||||
const keys = Object.keys(PRESETS.dark);
|
const keys = Object.keys(PRESETS.dark);
|
||||||
|
@ -315,21 +316,22 @@ export const generateVariables = (theme: Theme) => {
|
||||||
return (Object.keys(theme) as Variables[]).map((key) => {
|
return (Object.keys(theme) as Variables[]).map((key) => {
|
||||||
if (!keys.includes(key)) return;
|
if (!keys.includes(key)) return;
|
||||||
return `--${key}: ${theme[key]};`;
|
return `--${key}: ${theme[key]};`;
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
// Load the default default them and apply extras later
|
// Load the default default them and apply extras later
|
||||||
export const ThemeContext = createContext<Theme>(PRESETS["dark"]);
|
export const ThemeContext = createContext<Theme>(PRESETS["dark"]);
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children: Children;
|
children: Children;
|
||||||
options?: ThemeOptions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function Theme({ children, options }: Props) {
|
export default function Theme({ children }: Props) {
|
||||||
|
const settings = useApplicationState().settings;
|
||||||
|
|
||||||
const theme: Theme = {
|
const theme: Theme = {
|
||||||
...getBaseTheme(options?.base ?? 'dark'),
|
...getBaseTheme(settings.get("appearance:theme:base") ?? "dark"),
|
||||||
...options?.custom,
|
...settings.get("appearance:theme:custom"),
|
||||||
};
|
};
|
||||||
|
|
||||||
const root = document.documentElement.style;
|
const root = document.documentElement.style;
|
||||||
|
@ -346,8 +348,11 @@ function Theme({ children, options }: Props) {
|
||||||
}, [root, theme.monospaceFont]);
|
}, [root, theme.monospaceFont]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
root.setProperty("--ligatures", options?.ligatures ? "normal" : "none");
|
root.setProperty(
|
||||||
}, [root, options?.ligatures]);
|
"--ligatures",
|
||||||
|
settings.get("appearance:ligatures") ? "normal" : "none",
|
||||||
|
);
|
||||||
|
}, [root, settings.get("appearance:ligatures")]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const resize = () =>
|
const resize = () =>
|
||||||
|
@ -371,9 +376,3 @@ function Theme({ children, options }: Props) {
|
||||||
</ThemeContext.Provider>
|
</ThemeContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connectState<{ children: Children }>(Theme, (state) => {
|
|
||||||
return {
|
|
||||||
options: state.settings.theme,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ import LocaleOptions from "./stores/LocaleOptions";
|
||||||
import MessageQueue from "./stores/MessageQueue";
|
import MessageQueue from "./stores/MessageQueue";
|
||||||
import NotificationOptions from "./stores/NotificationOptions";
|
import NotificationOptions from "./stores/NotificationOptions";
|
||||||
import ServerConfig from "./stores/ServerConfig";
|
import ServerConfig from "./stores/ServerConfig";
|
||||||
|
import Settings from "./stores/Settings";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles global application state.
|
* Handles global application state.
|
||||||
|
@ -26,6 +27,7 @@ export default class State {
|
||||||
config: ServerConfig;
|
config: ServerConfig;
|
||||||
notifications: NotificationOptions;
|
notifications: NotificationOptions;
|
||||||
queue: MessageQueue;
|
queue: MessageQueue;
|
||||||
|
settings: Settings;
|
||||||
|
|
||||||
private persistent: [string, Persistent<unknown>][] = [];
|
private persistent: [string, Persistent<unknown>][] = [];
|
||||||
|
|
||||||
|
@ -41,6 +43,7 @@ export default class State {
|
||||||
this.config = new ServerConfig();
|
this.config = new ServerConfig();
|
||||||
this.notifications = new NotificationOptions();
|
this.notifications = new NotificationOptions();
|
||||||
this.queue = new MessageQueue();
|
this.queue = new MessageQueue();
|
||||||
|
this.settings = new Settings();
|
||||||
|
|
||||||
makeAutoObservable(this);
|
makeAutoObservable(this);
|
||||||
this.registerListeners = this.registerListeners.bind(this);
|
this.registerListeners = this.registerListeners.bind(this);
|
||||||
|
|
87
src/mobx/stores/Settings.ts
Normal file
87
src/mobx/stores/Settings.ts
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import { action, computed, makeAutoObservable, ObservableMap } from "mobx";
|
||||||
|
|
||||||
|
import { mapToRecord } from "../../lib/conversion";
|
||||||
|
|
||||||
|
import { Theme } from "../../context/Theme";
|
||||||
|
|
||||||
|
import { Sounds } from "../../assets/sounds/Audio";
|
||||||
|
import Persistent from "../interfaces/Persistent";
|
||||||
|
import Store from "../interfaces/Store";
|
||||||
|
|
||||||
|
export type SoundOptions = {
|
||||||
|
[key in Sounds]?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EmojiPack = "mutant" | "twemoji" | "noto" | "openmoji";
|
||||||
|
|
||||||
|
interface ISettings {
|
||||||
|
"notifications:desktop": boolean;
|
||||||
|
"notifications:sounds": SoundOptions;
|
||||||
|
|
||||||
|
"appearance:emoji": EmojiPack;
|
||||||
|
"appearance:ligatures": boolean;
|
||||||
|
"appearance:theme:base": string;
|
||||||
|
"appearance:theme:custom": Partial<Theme>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*const Schema: {
|
||||||
|
[key in keyof ISettings]:
|
||||||
|
| "string"
|
||||||
|
| "number"
|
||||||
|
| "boolean"
|
||||||
|
| "object"
|
||||||
|
| "function";
|
||||||
|
} = {
|
||||||
|
"notifications:desktop": "boolean",
|
||||||
|
"notifications:sounds": "object",
|
||||||
|
|
||||||
|
"appearance:emoji": "string",
|
||||||
|
"appearance:ligatures": "boolean",
|
||||||
|
"appearance:theme:base": "string",
|
||||||
|
"appearance:theme:custom": "object",
|
||||||
|
};*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages user settings.
|
||||||
|
*/
|
||||||
|
export default class Settings implements Store, Persistent<ISettings> {
|
||||||
|
private data: ObservableMap<string, unknown>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct new Layout store.
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
this.data = new ObservableMap();
|
||||||
|
makeAutoObservable(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
get id() {
|
||||||
|
return "layout";
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return JSON.parse(JSON.stringify(mapToRecord(this.data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@action hydrate(data: ISettings) {
|
||||||
|
Object.keys(data).forEach((key) =>
|
||||||
|
this.data.set(key, (data as any)[key]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action set<T extends keyof ISettings>(key: T, value: ISettings[T]) {
|
||||||
|
return this.data.set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed get<T extends keyof ISettings>(key: T) {
|
||||||
|
return this.data.get(key) as ISettings[T] | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setUnchecked(key: string, value: unknown) {
|
||||||
|
return this.data.set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed getUnchecked(key: string) {
|
||||||
|
return this.data.get(key);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue