From 858457e2ebd29e8bad1fe52cb7a57db4acd28b0a Mon Sep 17 00:00:00 2001 From: Der_Googler <54764558+DerGoogler@users.noreply.github.com> Date: Sun, 25 Feb 2024 19:12:28 +0100 Subject: [PATCH] add config to modconf --- .../activitys/ModConfPlaygroundActivity.tsx | 2 +- .../src/components/ConfigureView/index.tsx | 1 + Website/src/components/ConfigureView/libs.ts | 4 +- Website/src/hooks/useModFS.tsx | 2 +- Website/src/hooks/useNativeFileStorage.tsx | 97 +++++++++++++++---- 5 files changed, 85 insertions(+), 21 deletions(-) diff --git a/Website/src/activitys/ModConfPlaygroundActivity.tsx b/Website/src/activitys/ModConfPlaygroundActivity.tsx index e669bd84..fa74cf3d 100644 --- a/Website/src/activitys/ModConfPlaygroundActivity.tsx +++ b/Website/src/activitys/ModConfPlaygroundActivity.tsx @@ -117,7 +117,7 @@ const ModConfPlaygroundActivity = () => { const { context, extra } = useActivity(); const { strings } = useStrings(); const { modFS } = useModFS(); - const [description, setDescription] = useNativeFileStorage(modFS("MODCONF_PLAYGROUND"), extra.defaultText || "", { json: false }); + const [description, setDescription] = useNativeFileStorage(modFS("MODCONF_PLAYGROUND"), extra.defaultText || ""); const [errBoundKey, setErrBoundKey] = React.useState(0); const isLargeScreen = useMediaQuery("(min-width:600px)"); diff --git a/Website/src/components/ConfigureView/index.tsx b/Website/src/components/ConfigureView/index.tsx index 66f080aa..a53733e6 100644 --- a/Website/src/components/ConfigureView/index.tsx +++ b/Website/src/components/ConfigureView/index.tsx @@ -131,6 +131,7 @@ export const ConfigureView = React.forwardRef `${format("MODULECWD")}/${path}`, diff --git a/Website/src/components/ConfigureView/libs.ts b/Website/src/components/ConfigureView/libs.ts index 63fd169a..b009f37c 100644 --- a/Website/src/components/ConfigureView/libs.ts +++ b/Website/src/components/ConfigureView/libs.ts @@ -19,7 +19,7 @@ import { Ansi } from "@Components/Ansi"; import { os } from "@Native/Os"; import { BuildConfig } from "@Native/BuildConfig"; import { useSettings } from "@Hooks/useSettings"; -import { useNativeFileStorage } from "@Hooks/useNativeFileStorage"; +import { ConfigProvider, useConfig, useNativeFileStorage } from "@Hooks/useNativeFileStorage"; import { useModFS } from "@Hooks/useModFS"; import PicturePreviewActivity from "@Activitys/PicturePreviewActivity"; @@ -76,6 +76,7 @@ export const libraries = [ { name: "@mmrl/hooks", __esModule: { + useConfig: useConfig, useModFS: useModFS, useActivity: useActivity, useNativeProperties: useNativeProperties, @@ -89,6 +90,7 @@ export const libraries = [ { name: "@mmrl/providers", __esModule: { + ConfigProvider: ConfigProvider, StringsProvider: StringsProvider, }, }, diff --git a/Website/src/hooks/useModFS.tsx b/Website/src/hooks/useModFS.tsx index d68461a1..640be2f9 100644 --- a/Website/src/hooks/useModFS.tsx +++ b/Website/src/hooks/useModFS.tsx @@ -116,7 +116,7 @@ export const useModFS = () => { }; export const ModFSProvider = (props: React.PropsWithChildren) => { - const [modFS, setModFS] = useNativeFileStorage("/data/adb/mmrl/modfs.v7.json", INITIAL_MOD_CONF); + const [modFS, setModFS] = useNativeFileStorage("/data/adb/mmrl/modfs.v7.json", INITIAL_MOD_CONF, { loader: JSON }); const contextValue = React.useMemo( () => ({ diff --git a/Website/src/hooks/useNativeFileStorage.tsx b/Website/src/hooks/useNativeFileStorage.tsx index 8595dbc2..99ec7a5a 100644 --- a/Website/src/hooks/useNativeFileStorage.tsx +++ b/Website/src/hooks/useNativeFileStorage.tsx @@ -1,26 +1,35 @@ -import { useCallback, useEffect, useState } from "react"; +import { SetStateAction, useCallback, useEffect, useState } from "react"; import { useLog } from "./native/useLog"; import { SuFile } from "@Native/SuFile"; -import { SetValue, parseJSON } from "./useNativeStorage"; +import INI from "ini"; +import YMAL from "yaml"; import { os } from "@Native/Os"; +import { SetValue } from "./useNativeStorage"; +import React from "react"; -export function useNativeFileStorage(key: string, initialValue: T, opt: { json: boolean } = { json: true }): [T, SetValue] { - const log = useLog("useNativeStorage"); +type Loader = typeof JSON | typeof INI | typeof YMAL | null; - const { json } = opt; +export function useNativeFileStorage( + key: string, + initialValue: T, + opt: { loader: Loader } = { loader: null } +): [T, SetValue] { + const { loader } = opt; const file = new SuFile(key); const readValue = useCallback((): T => { - if (typeof window === "undefined") { - return initialValue; - } - try { - return file.exist() ? (json ? (parseJSON(file.read()) as T) : (file.read() as T)) : initialValue; + if (file.exist()) { + if (loader) { + return loader.parse(file.read()); + } else { + return file.read() as T; + } + } else { + return initialValue; + } } catch (error) { - log.w(`Error reading file “${key}”: ${error}`); - return initialValue; } }, [initialValue, key]); @@ -28,16 +37,16 @@ export function useNativeFileStorage(key: string, initialValue: T, opt: { jso const [storedValue, setStoredValue] = useState(readValue); const setValue: SetValue = (value) => { - if (typeof window === "undefined") { - log.w(`Tried setting localStorage key “${key}” even though environment is not a client`); - } - try { const newValue = value instanceof Function ? value(storedValue) : value; - file.write(json ? JSON.stringify(newValue) : String(newValue)); + if (loader) { + file.write((loader as any).stringify(newValue)); + } else { + file.write(String(newValue)); + } setStoredValue(newValue); } catch (error) { - log.w(`Error writing file “${key}”: ${error}`); + throw new Error(`Error writing file “${key}”: ${error}`); } }; @@ -47,3 +56,55 @@ export function useNativeFileStorage(key: string, initialValue: T, opt: { jso return [storedValue, setValue]; } + +export interface ConfigContext { + config: object; + setConfig(key: string, state: SetStateAction): void; +} + +export const ConfigContext = React.createContext({ + config: {}, + setConfig(key: string, state: SetStateAction) {}, +}); + +export const useConfig = () => { + return React.useContext(ConfigContext); +}; + +export interface ConfigProvider extends React.PropsWithChildren { + loadFromFile: string; + loader: Loader; + initialConfig: object; +} + +export const ConfigProvider = (props: ConfigProvider) => { + const { loadFromFile, loader = JSON, initialConfig } = props; + + if (!loadFromFile) { + throw new TypeError('"loadFromFile" is undefined'); + } + + if (!initialConfig) { + throw new TypeError('"initialConfig" is undefined'); + } + + const [config, setConfig] = useNativeFileStorage(loadFromFile, initialConfig, { loader: loader }); + + const contextValue = React.useMemo( + () => ({ + config: config, + setConfig: (name, state) => { + setConfig((prev) => { + const newValue = state instanceof Function ? state(prev[name]) : state; + return { + ...prev, + [name]: newValue, + }; + }); + }, + }), + [config] + ); + + return ; +};