From 68578d262042a1cd5d89e1861224addb9832fd5b Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Mon, 20 Dec 2021 12:01:45 +0000 Subject: [PATCH] feat(mobx): start work on migrations --- src/components/settings/AppearanceShims.tsx | 12 +-- .../settings/appearance/EmojiSelector.tsx | 2 +- src/mobx/interfaces/Migrate.ts | 11 +++ src/mobx/stores/Cache.ts | 76 ------------------- src/mobx/stores/Experiments.ts | 1 - src/mobx/stores/Sync.ts | 70 +++++++++++++++++ src/mobx/stores/helpers/STheme.ts | 15 +++- src/pages/settings/panes/ThemeShop.tsx | 17 +---- 8 files changed, 105 insertions(+), 99 deletions(-) create mode 100644 src/mobx/interfaces/Migrate.ts delete mode 100644 src/mobx/stores/Cache.ts create mode 100644 src/mobx/stores/Sync.ts diff --git a/src/components/settings/AppearanceShims.tsx b/src/components/settings/AppearanceShims.tsx index 017c132a..e465462e 100644 --- a/src/components/settings/AppearanceShims.tsx +++ b/src/components/settings/AppearanceShims.tsx @@ -34,7 +34,13 @@ import { ThemeBaseSelector } from "./appearance/ThemeBaseSelector"; export const ThemeBaseSelectorShim = observer(() => { const theme = useApplicationState().settings.theme; return ( - + { + theme.setBase(base); + theme.reset(); + }} + /> ); }); @@ -97,10 +103,6 @@ export const ThemeCustomCSSShim = observer(() => { ); }); -export const ThemeImporterShim = observer(() => { - return ; -}); - /** * Component providing a way to switch between compact and normal message view. */ diff --git a/src/components/settings/appearance/EmojiSelector.tsx b/src/components/settings/appearance/EmojiSelector.tsx index ac6d623c..4b54cf76 100644 --- a/src/components/settings/appearance/EmojiSelector.tsx +++ b/src/components/settings/appearance/EmojiSelector.tsx @@ -92,7 +92,7 @@ export function EmojiSelector({ value, setValue }: Props) {
setValue("mutant")} - data-active={value === "mutant"}> + data-active={!value || value === "mutant"}> extends Store { + /** + * Migrate this data store. + */ + migrate(key: K, data: Record, rev: number): void; +} diff --git a/src/mobx/stores/Cache.ts b/src/mobx/stores/Cache.ts deleted file mode 100644 index b8030c58..00000000 --- a/src/mobx/stores/Cache.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { action, computed, makeAutoObservable, ObservableMap } from "mobx"; - -import { mapToRecord } from "../../lib/conversion"; - -import { StoredTheme } from "../../redux/reducers/themes"; - -import Persistent from "../interfaces/Persistent"; -import Store from "../interfaces/Store"; - -interface Data { - themes: Record; -} - -/** - * Cache data store for temporary, long-lived data. - */ -export default class Cache implements Store, Persistent { - private themes: ObservableMap; - - /** - * Construct new Cache store. - */ - constructor() { - this.themes = new ObservableMap(); - makeAutoObservable(this); - } - - get id() { - return "draft"; - } - - toJSON() { - return { - themes: JSON.parse(JSON.stringify(mapToRecord(this.themes))), - }; - } - - @action hydrate(data: Data) { - Object.keys(data.themes).forEach((key) => - this.themes.set(key, data.themes[key]), - ); - } - - /** - * Cache a given theme. - * @param theme Theme - */ - @action cacheTheme(theme: StoredTheme) { - this.themes.set(theme.slug, theme); - } - - /** - * Remove a cached theme. - * @param slug String - */ - @action removeTheme(slug: string) { - this.themes.delete(slug); - } - - /** - * Get a cached theme by its slug. - * @param slug Theme slug - * @returns Theme, if found - */ - @computed getTheme(slug: string) { - return this.themes.get(slug); - } - - /** - * Get all cached themes. - * @returns Themes - */ - @computed getThemes() { - return [...this.themes.values()]; - } -} diff --git a/src/mobx/stores/Experiments.ts b/src/mobx/stores/Experiments.ts index 8e564cc0..37d5a4e7 100644 --- a/src/mobx/stores/Experiments.ts +++ b/src/mobx/stores/Experiments.ts @@ -50,7 +50,6 @@ export default class Experiments implements Store, Persistent { */ constructor() { this.enabled = new ObservableSet(); - makeAutoObservable(this); } diff --git a/src/mobx/stores/Sync.ts b/src/mobx/stores/Sync.ts new file mode 100644 index 00000000..d18a21fd --- /dev/null +++ b/src/mobx/stores/Sync.ts @@ -0,0 +1,70 @@ +import { + action, + computed, + makeAutoObservable, + ObservableMap, + ObservableSet, +} from "mobx"; +import { Client } from "revolt.js"; + +import { mapToRecord } from "../../lib/conversion"; + +import Persistent from "../interfaces/Persistent"; +import Store from "../interfaces/Store"; + +export type SyncKeys = "theme" | "appearance" | "locale" | "notifications"; + +export const SYNC_KEYS: SyncKeys[] = [ + "theme", + "appearance", + "locale", + "notifications", +]; + +interface Data { + disabled: SyncKeys[]; +} + +/** + * Handles syncing settings data. + */ +export default class Sync implements Store, Persistent { + private disabled: ObservableSet; + + /** + * Construct new Sync store. + */ + constructor() { + this.disabled = new ObservableSet(); + makeAutoObservable(this); + this.isEnabled = this.isEnabled.bind(this); + } + + get id() { + return "sync"; + } + + toJSON() { + return { + enabled: [...this.disabled], + }; + } + + @action hydrate(data: Data) { + if (data.disabled) { + for (const key of data.disabled) { + this.disabled.add(key as SyncKeys); + } + } + } + + @computed isEnabled(key: SyncKeys) { + return !this.disabled.has(key); + } + + async pull(client: Client) { + const data = await client.syncFetchSettings( + SYNC_KEYS.filter(this.isEnabled), + ); + } +} diff --git a/src/mobx/stores/helpers/STheme.ts b/src/mobx/stores/helpers/STheme.ts index 17c69551..93257b7e 100644 --- a/src/mobx/stores/helpers/STheme.ts +++ b/src/mobx/stores/helpers/STheme.ts @@ -41,7 +41,9 @@ export default class STheme { ); } - @action hydrate(data: Partial) { + @action hydrate(data: Partial, resetCSS = false) { + if (resetCSS) this.setCSS(); + for (const key of Object.keys(data)) { const value = data[key as keyof Theme] as string; switch (key) { @@ -137,8 +139,8 @@ export default class STheme { ); } - @action setCSS(value: string) { - if (value.length > 0) { + @action setCSS(value?: string) { + if (value && value.length > 0) { this.settings.set("appearance:theme:css", value); } else { this.settings.remove("appearance:theme:css"); @@ -153,6 +155,13 @@ export default class STheme { return this.settings.get("appearance:theme:css"); } + @computed isModified() { + return ( + Object.keys(this.settings.get("appearance:theme:overrides") ?? {}) + .length > 0 + ); + } + @action setBase(base?: "light" | "dark") { if (base) { this.settings.set("appearance:theme:base", base); diff --git a/src/pages/settings/panes/ThemeShop.tsx b/src/pages/settings/panes/ThemeShop.tsx index 47a4db79..11965e64 100644 --- a/src/pages/settings/panes/ThemeShop.tsx +++ b/src/pages/settings/panes/ThemeShop.tsx @@ -2,6 +2,7 @@ import styled from "styled-components"; import { useEffect, useState } from "preact/hooks"; +import { useApplicationState } from "../../../mobx/State"; import { dispatch } from "../../../redux"; import { Theme, generateVariables, ThemeOptions } from "../../../context/Theme"; @@ -149,6 +150,8 @@ export function ThemeShop() { >(null); const [themeData, setThemeData] = useState>({}); + const themes = useApplicationState().settings.theme; + async function fetchThemeList() { const manifest = await fetchManifest(); setThemeList( @@ -190,19 +193,7 @@ export function ThemeShop() {