diff --git a/Website/src/activitys/AboutActivity.tsx b/Website/src/activitys/AboutActivity.tsx index 284f0ec7..f13147e7 100644 --- a/Website/src/activitys/AboutActivity.tsx +++ b/Website/src/activitys/AboutActivity.tsx @@ -39,7 +39,7 @@ const CheckRoot = (props: CheckRootProps): React.JSX.Element => { const AboutActivity = () => { const { strings } = useStrings(); - const { settings, _modConf } = useSettings(); + const { settings } = useSettings(); const { theme } = useTheme(); const { context, extra } = useActivity(); diff --git a/Website/src/activitys/ConfigureActivity.tsx b/Website/src/activitys/ConfigureActivity.tsx index fc426bda..e6881e8f 100644 --- a/Website/src/activitys/ConfigureActivity.tsx +++ b/Website/src/activitys/ConfigureActivity.tsx @@ -3,9 +3,9 @@ import { Toolbar } from "@Components/onsenui/Toolbar"; import { useActivity } from "@Hooks/useActivity"; import React from "react"; import { SuFile } from "@Native/SuFile"; -import { useSettings } from "@Hooks/useSettings"; import { ConfigureView } from "@Components/ConfigureView"; import { PreviewErrorBoundary } from "./PlaygroundsActivity"; +import { useModConf } from "@Hooks/useModConf"; type Extra = { raw_data?: string; @@ -14,7 +14,7 @@ type Extra = { }; const ConfigureActivity = () => { - const { modConf } = useSettings(); + const { modConf } = useModConf(); const { context, extra } = useActivity(); const config: string = React.useMemo(() => { diff --git a/Website/src/activitys/MainApplication.tsx b/Website/src/activitys/MainApplication.tsx index 0e87b807..837ecca1 100644 --- a/Website/src/activitys/MainApplication.tsx +++ b/Website/src/activitys/MainApplication.tsx @@ -31,6 +31,7 @@ import { AnimatePresence, motion } from "framer-motion"; import InputBase from "@mui/material/InputBase"; import SearchIcon from "@mui/icons-material/Search"; import ClearIcon from "@mui/icons-material/Clear"; +import { useModConf } from "@Hooks/useModConf"; interface SearchbarRef { clear(): void; @@ -91,7 +92,8 @@ const SearchBar = React.forwardRef((props, ref) => const MainApplication = () => { const { strings } = useStrings(); - const { settings, modConf } = useSettings(); + const { settings } = useSettings(); + const { modConf } = useModConf(); const { context } = useActivity(); const { theme } = useTheme(); const { modules } = useRepos(); diff --git a/Website/src/activitys/ModConfActivity.tsx b/Website/src/activitys/ModConfActivity.tsx index 8418e34e..ba1467cb 100644 --- a/Website/src/activitys/ModConfActivity.tsx +++ b/Website/src/activitys/ModConfActivity.tsx @@ -3,7 +3,8 @@ import { Alert, Box, Divider, List, ListSubheader } from "@mui/material"; import { Toolbar } from "@Components/onsenui/Toolbar"; import { Page } from "@Components/onsenui/Page"; import { useActivity } from "@Hooks/useActivity"; -import { ModConf, useSettings } from "@Hooks/useSettings"; +import { useSettings } from "@Hooks/useSettings"; +import { ModConf, useModConf } from "@Hooks/useModConf"; import { StyledListItemText } from "@Components/StyledListItemText"; import { Shell } from "@Native/Shell"; import { DialogEditTextListItem } from "@Components/DialogEditTextListItem"; @@ -29,7 +30,7 @@ interface ModConfListItem { function ModConfActivity() { const { context } = useActivity(); - const { _modConf, setModConf } = useSettings(); + const { _modConf, setModConf } = useModConf(); const renderToolbar = () => { return ( @@ -171,8 +172,7 @@ function ModConfActivity() { - {item.logoText && } - [{item.confKey}]: {item.text} + {item.logoText && }[{item.confKey}]: {item.text} } secondary={_modConf[item.confKey]} diff --git a/Website/src/activitys/TerminalActivity.tsx b/Website/src/activitys/TerminalActivity.tsx index 8fea36be..84feb973 100644 --- a/Website/src/activitys/TerminalActivity.tsx +++ b/Website/src/activitys/TerminalActivity.tsx @@ -7,13 +7,14 @@ import Ansi from "ansi-to-react"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import React from "react"; import { Shell } from "@Native/Shell"; -import { formatString, useSettings } from "@Hooks/useSettings"; -import { useNewerVersion } from "@Hooks/useNewerVersion"; +import { useSettings } from "@Hooks/useSettings"; import { BuildConfig } from "@Native/BuildConfig"; +import { useModConf, formatString } from "@Hooks/useModConf"; const TerminalActivity = () => { const { context, extra } = useActivity(); - const { settings, modConf, _modConf } = useSettings(); + const { settings } = useSettings(); + const { modConf, _modConf } = useModConf(); const [active, setActive] = React.useState(true); const [lines, setLines] = React.useState([]); diff --git a/Website/src/components/ConfigureView/index.tsx b/Website/src/components/ConfigureView/index.tsx index 354e8f2f..04565698 100644 --- a/Website/src/components/ConfigureView/index.tsx +++ b/Website/src/components/ConfigureView/index.tsx @@ -14,7 +14,7 @@ import { PluginObj } from "@babel/core"; import { globals, libraries } from "./libs"; import { DialogEditListItem, StyledListSubheader } from "./components"; import { SuFile, wasmFs } from "@Native/SuFile"; -import { ModConf, useSettings } from "@Hooks/useSettings"; +import { ModConf, useModConf } from "@Hooks/useModConf"; function plugin({ types: t }): PluginObj { return { @@ -69,7 +69,7 @@ const scope = { export const ConfigureView = React.memo((props) => { const { theme } = useTheme(); - const { modConf } = useSettings(); + const { modConf } = useModConf(); const format = React.useCallback<(key: K) => ModConf[K]>((key) => modConf(key, { MODID: props.modid }), []); diff --git a/Website/src/components/DeviceModule.tsx b/Website/src/components/DeviceModule.tsx index def1fc8b..7b139d7e 100644 --- a/Website/src/components/DeviceModule.tsx +++ b/Website/src/components/DeviceModule.tsx @@ -10,10 +10,11 @@ import { ConfigureActivity } from "@Activitys/ConfigureActivity"; import { StyledIconButton } from "./StyledIconButton"; import { useLog } from "@Hooks/native/useLog"; import { Properties } from "properties-file"; -import { ModConf, colors, useSettings } from "@Hooks/useSettings"; +import { colors, useSettings } from "@Hooks/useSettings"; import { useTheme } from "@Hooks/useTheme"; import TerminalActivity from "@Activitys/TerminalActivity"; import { useRepos } from "@Hooks/useRepos"; +import { ModConf, useModConf } from "@Hooks/useModConf"; export const badgeStyle: (color: (typeof colors)["blue" | "teal" | "red" | "orange"]) => SxProps = (color) => { return { @@ -33,7 +34,8 @@ interface Props { const DeviceModule = React.memo((props) => { const { strings } = useStrings(); - const { settings, modConf } = useSettings(); + const { settings } = useSettings(); + const { modConf } = useModConf(); const { theme } = useTheme(); const { context, extra } = useActivity(); const { modules } = useRepos(); diff --git a/Website/src/hooks/useLocalForage.ts b/Website/src/hooks/useLocalForage.ts index b8e77246..c33b7ac9 100644 --- a/Website/src/hooks/useLocalForage.ts +++ b/Website/src/hooks/useLocalForage.ts @@ -26,32 +26,27 @@ export function useLocalForage(key: string, initialValue: D, errorHandler?: E }; useEffect(() => { - (async function () { - try { - const value: D | null = await localForage.getItem(key); - setStoredValue(value == null ? initialValue : value); - } catch (e) { - error(e as any); + localForage.getItem(key).then((value) => { + if (value !== null) { + setStoredValue(value); + } else { + setStoredValue(initialValue); } - })(); + }); }, []); - const setValue: SetValue = useCallback( - (value, callback) => { - const newValue = value instanceof Function ? value(storedValue) : value; - async function set(value: D, callback: ((state: D) => void) | undefined) { - try { - setStoredValue(value, callback); - await localForage.setItem(key, value); - } catch (e) { - error(e as any); - } + const setValue: SetValue = (value, callback) => { + setStoredValue(value, callback); + const newValue = value instanceof Function ? value(storedValue) : value; + localForage.setItem(key, newValue).then((_value) => { + const _newValue = _value instanceof Function ? _value(storedValue) : _value; + if (_newValue !== null) { + setStoredValue(_newValue, callback); + } else { + setStoredValue(initialValue, callback); } - - set(newValue, callback); - }, - [key] - ); + }); + }; const removeValue = useCallback(() => { async function remove() { diff --git a/Website/src/hooks/useModConf.tsx b/Website/src/hooks/useModConf.tsx new file mode 100644 index 00000000..0ed19fa4 --- /dev/null +++ b/Website/src/hooks/useModConf.tsx @@ -0,0 +1,148 @@ +import React, { createContext, useContext } from "react"; +import { colors as kolors } from "@mui/material"; +import { defaultComposer } from "default-composer"; +import { useNativeStorage } from "./useNativeStorage"; +import { os } from "@Native/Os"; +import { SetStateAction } from "./useStateCallback"; +import { useLanguageMap } from "./../locales/declaration"; +import { useLocalForage } from "./useLocalForage"; + +export interface ModConf { + //cli + MSUCLI: string; + KSUCLI: string; + + // default paths + ADB: string; + MODULES: string; + MODULECWD: string; + PROPS: string; + SYSTEM: string; + SEPOLICY: string; + CONFIG: string; + + // service paths + LATESERVICE: string; + POSTSERVICE: string; + POSTMOUNT: string; + BOOTCOMP: string; + + // status paths + SKIPMOUNT: string; + DISABLE: string; + REMOVE: string; + UPDATE: string; + + // others + MMRLINI: string; + CONFCWD: string; + CONFINDEX: string; +} + +export const INITIAL_MOD_CONF: ModConf = { + //cli + MSUCLI: "/system/bin/magisk --install-module ", + KSUCLI: "/ksu/bin/ksud module install ", + + // default paths + ADB: "/data/adb", + MODULES: "/modules", + MODULECWD: "/", + PROPS: "/module.prop", + SYSTEM: "/system.prop", + SEPOLICY: "/sepolicy.rule", + CONFIG: `/system/usr/share/mmrl/config/.mdx`, + + // service paths + LATESERVICE: "/service.sh", + POSTSERVICE: "/post-fs-data.sh", + POSTMOUNT: "/post-mount.sh", + BOOTCOMP: "/boot-completed.sh", + + // status paths + SKIPMOUNT: "/skip_mount", + DISABLE: "/disable", + REMOVE: "/remove", + UPDATE: "/update", + + // others + MMRLINI: "/mmrl_install_tools", + CONFCWD: "/system/usr/share/mmrl/config/", + CONFINDEX: "/index.jsx", +}; + +export interface ModConfContext { + _modConf: ModConf; + modConf(key: K, adds?: Record): ModConf[K]; + setModConf(key: K, state: SetStateAction, callback?: (state: ModConf[K]) => void): void; +} + +export const ModConfContext = createContext({ + _modConf: INITIAL_MOD_CONF, + modConf(key: K, adds?: Record) { + return key; + }, + setModConf(key: K, state: SetStateAction, callback?: (state: ModConf[K]) => void) {}, +}); + +export const useModConf = () => { + return useContext(ModConfContext); +}; + +export function formatString(template: string, object: object): string { + return template.replace(/\<(\w+(\.\w+)*)\>/gi, (match, key) => { + const keys = key.split("."); + let value = object; + for (const k of keys) { + if (k in value) { + value = value[k]; + } else { + return match; + } + } + return formatString(String(value), object); + }); +} + +export const ModConfProvider = (props: React.PropsWithChildren) => { + const [modConf, setModConf] = useNativeStorage("modconf", INITIAL_MOD_CONF); + + // Test purposes + // React.useEffect(() => { + // for (const k in modConf) { + // console.info( + // formatString(defaultComposer(INITIAL_MOD_CONF, modConf)[k], { + // ...modConf, + // ...{ + // MODID: "node_on_android", + // ZIPFILE: "/sdard/xh.zip", + // }, + // }) + // ); + // } + // }, [modConf]); + + const contextValue = React.useMemo( + () => ({ + _modConf: defaultComposer(INITIAL_MOD_CONF, modConf), + modConf: (key, adds) => { + return formatString(defaultComposer(INITIAL_MOD_CONF, modConf)[key], { ...modConf, ...adds }); + }, + setModConf: (name, state, callback) => { + setModConf( + (prev) => { + const newValue = React.useMemo(() => (state instanceof Function ? state(prev[name]) : state), [name]); + return { + ...prev, + [name]: newValue, + }; + }, + (state) => callback && callback(state[name]) + ); + }, + }), + [modConf] + ); + + return ; +}; diff --git a/Website/src/hooks/useModulesFilter.tsx b/Website/src/hooks/useModulesFilter.tsx index a022eb81..bc697189 100644 --- a/Website/src/hooks/useModulesFilter.tsx +++ b/Website/src/hooks/useModulesFilter.tsx @@ -3,7 +3,6 @@ import UpdateDisabledIcon from "@mui/icons-material/UpdateDisabled"; import CalendarMonthIcon from "@mui/icons-material/CalendarMonth"; import AbcIcon from "@mui/icons-material/Abc"; import { useTheme } from "./useTheme"; -import Button from "@mui/material/Button"; import Avatar from "@mui/material/Avatar"; import List from "@mui/material/List"; import ListItem from "@mui/material/ListItem"; @@ -12,7 +11,6 @@ import ListItemButton from "@mui/material/ListItemButton"; import ListItemText from "@mui/material/ListItemText"; import Dialog from "@mui/material/Dialog"; import DialogTitle from "@mui/material/DialogTitle"; -import { SortInterface } from "flatlist-react/lib"; import React from "react"; export const filters = [ diff --git a/Website/src/hooks/useRepos.tsx b/Website/src/hooks/useRepos.tsx index 3f95c108..a537b779 100644 --- a/Website/src/hooks/useRepos.tsx +++ b/Website/src/hooks/useRepos.tsx @@ -61,7 +61,7 @@ type SetRepoStateData = { export const RepoProvider = (props: React.PropsWithChildren) => { const TAG = "RepoProvider"; const log = useLog(TAG); - const [repos, setRepos, removeRepos] = useLocalForage("repos", [ + const [repos, setRepos] = useNativeStorage("repos", [ { name: "Magisk Modules Alt Repo (pre-configured)", website: "", diff --git a/Website/src/hooks/useSettings.tsx b/Website/src/hooks/useSettings.tsx index adf2c927..94602e7e 100644 --- a/Website/src/hooks/useSettings.tsx +++ b/Website/src/hooks/useSettings.tsx @@ -1,10 +1,10 @@ import React, { createContext, useContext } from "react"; import { colors as kolors } from "@mui/material"; import { defaultComposer } from "default-composer"; -import { useNativeStorage } from "./useNativeStorage"; import { os } from "@Native/Os"; import { SetStateAction } from "./useStateCallback"; import { useLanguageMap } from "./../locales/declaration"; +import { useNativeStorage } from "./useNativeStorage"; export const accent_colors: Picker[] = [ { @@ -165,38 +165,6 @@ export interface StorageDeclaration { term_scroll_behavior: { name: string; value: ScrollBehavior }; } -export interface ModConf { - //cli - MSUCLI: string; - KSUCLI: string; - - // default paths - ADB: string; - MODULES: string; - MODULECWD: string; - PROPS: string; - SYSTEM: string; - SEPOLICY: string; - CONFIG: string; - - // service paths - LATESERVICE: string; - POSTSERVICE: string; - POSTMOUNT: string; - BOOTCOMP: string; - - // status paths - SKIPMOUNT: string; - DISABLE: string; - REMOVE: string; - UPDATE: string; - - // others - MMRLINI: string; - CONFCWD: string; - CONFINDEX: string; -} - export const termScrollBehaviors: StorageDeclaration["term_scroll_behavior"][] = [ { name: "Smooth", @@ -208,86 +176,31 @@ export const termScrollBehaviors: StorageDeclaration["term_scroll_behavior"][] = }, ]; -export const INITIAL_MOD_CONF: ModConf = { - //cli - MSUCLI: "/system/bin/magisk --install-module ", - KSUCLI: "/ksu/bin/ksud module install ", - - // default paths - ADB: "/data/adb", - MODULES: "/modules", - MODULECWD: "/", - PROPS: "/module.prop", - SYSTEM: "/system.prop", - SEPOLICY: "/sepolicy.rule", - CONFIG: `/system/usr/share/mmrl/config/.mdx`, - - // service paths - LATESERVICE: "/service.sh", - POSTSERVICE: "/post-fs-data.sh", - POSTMOUNT: "/post-mount.sh", - BOOTCOMP: "/boot-completed.sh", - - // status paths - SKIPMOUNT: "/skip_mount", - DISABLE: "/disable", - REMOVE: "/remove", - UPDATE: "/update", - - // others - MMRLINI: "/mmrl_install_tools", - CONFCWD: "/system/usr/share/mmrl/config/", - CONFINDEX: "/index.jsx", -}; - export interface Context { patchSettings: () => void; settings: StorageDeclaration; - _modConf: ModConf; - modConf(key: K, adds?: Record): ModConf[K]; setSettings( key: K, state: SetStateAction, callback?: (state: StorageDeclaration[K]) => void ): void; - setModConf(key: K, state: SetStateAction, callback?: (state: ModConf[K]) => void): void; } export const SettingsContext = createContext({ patchSettings: () => {}, // @ts-ignore settings: {}, - _modConf: INITIAL_MOD_CONF, - modConf(key: K, adds?: Record) { - return key; - }, setSettings( key: K, state: SetStateAction, callback?: (state: StorageDeclaration[K]) => void ) {}, - setModConf(key: K, state: SetStateAction, callback?: (state: ModConf[K]) => void) {}, }); export const useSettings = () => { return useContext(SettingsContext); }; -export function formatString(template: string, object: object): string { - return template.replace(/\<(\w+(\.\w+)*)\>/gi, (match, key) => { - const keys = key.split("."); - let value = object; - for (const k of keys) { - if (k in value) { - value = value[k]; - } else { - return match; - } - } - return formatString(String(value), object); - }); -} - export const SettingsProvider = (props: React.PropsWithChildren) => { const availableLangs = useLanguageMap(); @@ -309,60 +222,30 @@ export const SettingsProvider = (props: React.PropsWithChildren) => { ); const [settings, setSettings] = useNativeStorage("settings", INITIAL_SETTINGS); - const [modConf, setModConf] = useNativeStorage("mod-conf", INITIAL_MOD_CONF); - - // Test purposes - // React.useEffect(() => { - // for (const k in modConf) { - // console.info( - // formatString(defaultComposer(INITIAL_MOD_CONF, modConf)[k], { - // ...modConf, - // ...{ - // MODID: "node_on_android", - // ZIPFILE: "/sdard/xh.zip", - // }, - // }) - // ); - // } - // }, [modConf]); - return ( - { - setSettings(defaultComposer(INITIAL_SETTINGS, settings)); - }, - _modConf: defaultComposer(INITIAL_MOD_CONF, modConf), - modConf: (key, adds) => { - return formatString(defaultComposer(INITIAL_MOD_CONF, modConf)[key], { ...modConf, ...adds }); - }, - settings: defaultComposer(INITIAL_SETTINGS, settings), - setSettings: (name, state, callback) => { - setSettings( - (prev) => { - const newValue = state instanceof Function ? state(prev[name]) : state; - return { - ...prev, - [name]: newValue, - }; - }, - (state) => callback && callback(state[name]) - ); - }, - setModConf: (name, state, callback) => { - setModConf( - (prev) => { - const newValue = state instanceof Function ? state(prev[name]) : state; - return { - ...prev, - [name]: newValue, - }; - }, - (state) => callback && callback(state[name]) - ); - }, - }} - children={props.children} - /> + const _setSettings = (name, state, callback) => { + setSettings( + (prev) => { + const newValue = state instanceof Function ? state(prev[name]) : state; + return { + ...prev, + [name]: newValue, + }; + }, + (state) => callback && callback(state[name]) + ); + }; + + const contextValue = React.useMemo( + () => ({ + patchSettings: () => { + setSettings(defaultComposer(INITIAL_SETTINGS, settings)); + }, + settings: defaultComposer(INITIAL_SETTINGS, settings), + setSettings: _setSettings, + }), + [settings] ); + + return ; }; diff --git a/Website/src/index.tsx b/Website/src/index.tsx index 2ab55b86..1952026d 100644 --- a/Website/src/index.tsx +++ b/Website/src/index.tsx @@ -8,7 +8,9 @@ import { StringsProvider } from "@Hooks/useStrings"; import { Preventer, render } from "react-render-tools"; import { MainActivity } from "@Activitys/MainActivity"; import { RepoProvider } from "@Hooks/useRepos"; + import { SettingsProvider } from "@Hooks/useSettings"; +import { ModConfProvider } from "@Hooks/useModConf"; import { MMRLApp } from "./custom-elements/app"; import { MMRLAnchor } from "./custom-elements/anchor"; @@ -28,21 +30,23 @@ ons.ready(() => { render( - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + , "mmrl-app" );