From 74430b1a8f92d1cb53e0053af7324a28bfffe63e Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 24 Dec 2021 11:19:02 +0000 Subject: [PATCH] feat(mobx): migrate legacy data --- src/mobx/State.ts | 41 ++++++++++++++------- src/mobx/legacy/redux.ts | 77 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 13 deletions(-) diff --git a/src/mobx/State.ts b/src/mobx/State.ts index 19ec3ba3..5c4a6efe 100644 --- a/src/mobx/State.ts +++ b/src/mobx/State.ts @@ -4,6 +4,8 @@ import localforage from "localforage"; import { makeAutoObservable, reaction } from "mobx"; import { Client } from "revolt.js"; +import { legacyMigrateForwards, LegacyState } from "./legacy/redux"; + import Persistent from "./interfaces/Persistent"; import Syncable from "./interfaces/Syncable"; import Auth from "./stores/Auth"; @@ -89,10 +91,23 @@ export default class State { } } + /** + * Temporarily ignore updates to a key. + * @param key Key to ignore + */ setDisabled(key: string) { this.disabled.add(key); } + /** + * Save to disk. + */ + async save() { + for (const [id, store] of this.persistent) { + await localforage.setItem(id, store.toJSON()); + } + } + /** * Register reaction listeners for persistent data stores. * @returns Function to dispose of listeners @@ -177,6 +192,20 @@ export default class State { * Load data stores from local storage. */ async hydrate() { + // Migrate legacy Redux store. + let legacy = await localforage.getItem("state"); + if (legacy) { + if (typeof legacy === "string") { + legacy = JSON.parse(legacy); + } + + legacyMigrateForwards(legacy as Partial, this); + await localforage.removeItem("state"); + await this.save(); + return; + } + + // Load MobX store. const sync = (await localforage.getItem("sync")) as DataSync; if (sync) { const { revision } = sync; @@ -205,15 +234,3 @@ export async function hydrateState() { export function useApplicationState() { return state; } - -/** - * - * Redux hydration: - * localForage.getItem("state").then((s) => { - if (s !== null) { - dispatch({ type: "__INIT", state: s as State }); - } - - state.hydrate().then(() => setLoaded(true)); - }); - */ diff --git a/src/mobx/legacy/redux.ts b/src/mobx/legacy/redux.ts index 63727668..1069494e 100644 --- a/src/mobx/legacy/redux.ts +++ b/src/mobx/legacy/redux.ts @@ -1,8 +1,15 @@ +import { runInAction } from "mobx"; import { Session } from "revolt-api/types/Auth"; import { Language } from "../../context/Locale"; -import { Fonts, MonospaceFonts, Overrides } from "../../context/Theme"; +import { + Fonts, + MonospaceFonts, + Overrides, + ThemeOptions, +} from "../../context/Theme"; +import State from "../State"; import { Data as DataAuth } from "../stores/Auth"; import { Data as DataLocaleOptions } from "../stores/LocaleOptions"; import { Data as DataNotificationOptions } from "../stores/NotificationOptions"; @@ -62,6 +69,11 @@ export interface LegacyAuthState { active?: string; } +export interface LegacySettings { + theme?: LegacyThemeOptions; + appearance?: LegacyAppearanceOptions; +} + export function legacyMigrateAuth(auth: LegacyAuthState): DataAuth { return { current: auth.active, @@ -100,6 +112,20 @@ export function legacyMigrateAppearance( }; } +/** + * Remove trolling from an object + * @param inp Object to remove trolling from + * @returns Object without trolling + */ +function detroll(inp: object): ISettings { + const obj: object = {}; + Object.keys(inp) + .filter((x) => typeof (inp as any)[x] !== "undefined") + .map((x) => ((obj as any)[x] = (inp as any)[x])); + + return obj as unknown as ISettings; +} + export function legacyMigrateNotification( channel: LegacyNotifications, ): DataNotificationOptions { @@ -116,3 +142,52 @@ export function legacyMigrateSync(sync: LegacySyncOptions): DataSync { }, }; } + +export type LegacyState = { + locale: Language; + auth: LegacyAuthState; + settings: LegacySettings; + sync: LegacySyncOptions; + notifications: LegacyNotifications; +}; + +export function legacyMigrateForwards( + data: Partial, + target: State, +) { + runInAction(() => { + if ("sync" in data) { + target.sync.hydrate(legacyMigrateSync(data.sync!)); + } + + if ("locale" in data) { + target.locale.hydrate(legacyMigrateLocale(data.locale!)); + } + + if ("auth" in data) { + target.auth.hydrate(legacyMigrateAuth(data.auth!)); + } + + if ("settings" in data) { + if (data!.settings!.theme) { + target.settings.hydrate( + detroll(legacyMigrateTheme(data.settings!.theme!)), + ); + } + + if (data!.settings!.appearance) { + target.settings.hydrate( + detroll( + legacyMigrateAppearance(data.settings!.appearance!), + ), + ); + } + } + + if ("notifications" in data) { + target.notifications.hydrate( + legacyMigrateNotification(data.notifications!), + ); + } + }); +}