[UI] Move to Discord's navigation stack

This commit is contained in:
Beef 2023-02-04 22:37:28 +00:00
parent b175ac18c1
commit a97293673d
6 changed files with 29 additions and 101 deletions

1
src/def.d.ts vendored
View file

@ -289,7 +289,6 @@ interface VendettaObject {
interface VendettaPluginObject { interface VendettaPluginObject {
manifest: PluginManifest, manifest: PluginManifest,
storage: Indexable<any>, storage: Indexable<any>,
showSettings: () => void,
} }
declare global { declare global {

View file

@ -1,9 +1,7 @@
import { Indexable, PluginManifest, Plugin } from "@types"; import { Indexable, PluginManifest, Plugin } from "@types";
import { navigation } from "@metro/common";
import { awaitSyncWrapper, createStorage, wrapSync } from "@lib/storage"; import { awaitSyncWrapper, createStorage, wrapSync } from "@lib/storage";
import safeFetch from "@utils/safeFetch"; import safeFetch from "@utils/safeFetch";
import logger from "@lib/logger"; import logger from "@lib/logger";
import Subpage from "@ui/settings/components/Subpage";
// TODO: Properly implement hash-based updating // TODO: Properly implement hash-based updating
@ -58,7 +56,6 @@ export async function evalPlugin(plugin: Plugin) {
manifest: plugin.manifest, manifest: plugin.manifest,
// Wrapping this with wrapSync is NOT an option. // Wrapping this with wrapSync is NOT an option.
storage: await createStorage<Indexable<any>>(plugin.id), storage: await createStorage<Indexable<any>>(plugin.id),
showSettings: () => showSettings(plugin),
} }
}; };
const pluginString = `vendetta=>{return ${plugin.js}}\n//# sourceURL=${plugin.id}`; const pluginString = `vendetta=>{return ${plugin.js}}\n//# sourceURL=${plugin.id}`;
@ -125,14 +122,4 @@ export async function initPlugins() {
const stopAllPlugins = () => Object.keys(plugins).forEach(p => stopPlugin(p, false)); const stopAllPlugins = () => Object.keys(plugins).forEach(p => stopPlugin(p, false));
export const getSettings = (id: string) => loadedPlugins[id]?.settings; export const getSettings = (id: string) => loadedPlugins[id]?.settings;
export function showSettings(plugin: Plugin) {
const settings = getSettings(plugin.id);
if (!settings) return logger.error(`Plugin ${plugin.id} is not loaded or has no settings`);
navigation.push(Subpage, {
name: plugin.manifest.name,
children: settings,
});
}

View file

@ -1,10 +1,10 @@
import { ReactNative as RN, stylesheet } from "@metro/common"; import { ReactNative as RN, stylesheet, NavigationNative } from "@metro/common";
import { Forms, General } from "@ui/components"; import { Forms, General } from "@ui/components";
import { Plugin } from "@types"; import { Plugin } from "@types";
import { getAssetIDByName } from "@ui/assets"; import { getAssetIDByName } from "@ui/assets";
import { showToast } from "@ui/toasts"; import { showToast } from "@ui/toasts";
import { removePlugin, startPlugin, stopPlugin, showSettings, getSettings } from "@lib/plugins"; import { removePlugin, startPlugin, stopPlugin, getSettings } from "@lib/plugins";
import copyText from "@lib/utils/copyText"; import copyText from "@utils/copyText";
const { FormRow, FormSwitch } = Forms; const { FormRow, FormSwitch } = Forms;
const { TouchableOpacity, Image } = General; const { TouchableOpacity, Image } = General;
@ -39,6 +39,8 @@ interface PluginCardProps {
} }
export default function PluginCard({ plugin }: PluginCardProps) { export default function PluginCard({ plugin }: PluginCardProps) {
const settings = getSettings(plugin.id);
const navigation = NavigationNative.useNavigation();
const [removed, setRemoved] = React.useState(false); const [removed, setRemoved] = React.useState(false);
// This is needed because of React™ // This is needed because of React™
if (removed) return null; if (removed) return null;
@ -86,7 +88,10 @@ export default function PluginCard({ plugin }: PluginCardProps) {
> >
<Image style={styles.icon} source={getAssetIDByName(plugin.update ? "Check" : "Small")} /> <Image style={styles.icon} source={getAssetIDByName(plugin.update ? "Check" : "Small")} />
</TouchableOpacity> </TouchableOpacity>
{getSettings(plugin.id) && <TouchableOpacity onPress={() => showSettings(plugin)}> {settings && <TouchableOpacity onPress={() => navigation.push("VendettaCustomPage", {
title: plugin.manifest.name,
render: settings,
})}>
<Image style={styles.icon} source={getAssetIDByName("settings")} /> <Image style={styles.icon} source={getAssetIDByName("settings")} />
</TouchableOpacity>} </TouchableOpacity>}
</RN.View> </RN.View>

View file

@ -1,72 +0,0 @@
import { navigation, navigationStack, NavigationNative, stylesheet, constants } from "@metro/common";
import { General } from "@ui/components";
import { getAssetIDByName } from "@ui/assets";
interface SubpageProps {
name: string;
children: JSX.Element;
}
const styles = stylesheet.createThemedStyleSheet({
container: {
backgroundColor: stylesheet.ThemeColorMap.BACKGROUND_MOBILE_SECONDARY,
flex: 1,
},
card: {
backgroundColor: stylesheet.ThemeColorMap.BACKGROUND_MOBILE_PRIMARY,
color: stylesheet.ThemeColorMap.TEXT_NORMAL,
},
header: {
backgroundColor: stylesheet.ThemeColorMap.BACKGROUND_MOBILE_SECONDARY,
shadowColor: "transparent",
elevation: 0,
},
headerTitleContainer: {
color: stylesheet.ThemeColorMap.HEADER_PRIMARY,
},
headerTitle: {
fontFamily: constants.Fonts.PRIMARY_BOLD,
color: stylesheet.ThemeColorMap.HEADER_PRIMARY,
},
backIcon: {
tintColor: stylesheet.ThemeColorMap.INTERACTIVE_ACTIVE,
marginLeft: 15,
marginRight: 20,
}
});
export const Settings = navigationStack.createStackNavigator();
const { TouchableOpacity, Image } = General;
export default function Subpage({ name, children }: SubpageProps) {
return (
<NavigationNative.NavigationContainer independent>
<Settings.Navigator
initialRouteName={name}
style={styles.container}
screenOptions={{
cardOverlayEnabled: false,
cardShadowEnabled: false,
cardStyle: styles.card,
headerStyle: styles.header,
headerTitleContainerStyle: styles.headerTitleContainer,
}}
>
<Settings.Screen
name={name}
component={children}
options={{
headerTitleStyle: styles.headerTitle,
headerLeft: () => (
<TouchableOpacity
onPress={() => navigation.pop()}
>
<Image style={styles.backIcon} source={getAssetIDByName("back-icon")} />
</TouchableOpacity>
),
}}
/>
</Settings.Navigator>
</NavigationNative.NavigationContainer>
)
}

View file

@ -1,4 +1,4 @@
import { i18n } from "@metro/common"; import { NavigationNative, i18n } from "@metro/common";
import { findByDisplayName } from "@metro/filters"; import { findByDisplayName } from "@metro/filters";
import { after } from "@lib/patcher"; import { after } from "@lib/patcher";
import findInReactTree from "@utils/findInReactTree"; import findInReactTree from "@utils/findInReactTree";
@ -6,6 +6,7 @@ import SettingsSection from "@ui/settings/components/SettingsSection";
import General from "@ui/settings/pages/General"; import General from "@ui/settings/pages/General";
import Plugins from "@ui/settings/pages/Plugins"; import Plugins from "@ui/settings/pages/Plugins";
import Developer from "@ui/settings/pages/Developer"; import Developer from "@ui/settings/pages/Developer";
import AssetBrowser from "@ui/settings/pages/AssetBrowser";
const screensModule = findByDisplayName("getScreens", false); const screensModule = findByDisplayName("getScreens", false);
const settingsModule = findByDisplayName("UserSettingsOverviewWrapper", false); const settingsModule = findByDisplayName("UserSettingsOverviewWrapper", false);
@ -22,11 +23,23 @@ export default function initSettings() {
}, },
VendettaPlugins: { VendettaPlugins: {
title: "Plugins", title: "Plugins",
render: Plugins render: Plugins,
}, },
VendettaDeveloper: { VendettaDeveloper: {
title: "Developer", title: "Developer",
render: Developer render: Developer,
},
VendettaAssetBrowser: {
title: "Asset Browser",
render: AssetBrowser,
},
VendettaCustomPage: {
title: "Vendetta Page",
render: ({ render: PageView, ...options }: { render: React.ComponentType }) => {
const navigation = NavigationNative.useNavigation();
React.useEffect(() => options && navigation.setOptions(options));
return <PageView />;
}
} }
} }
})); }));

View file

@ -1,4 +1,4 @@
import { ReactNative as RN, navigation } from "@metro/common"; import { ReactNative as RN, NavigationNative } from "@metro/common";
import { Forms } from "@ui/components"; import { Forms } from "@ui/components";
import { getAssetIDByName } from "@ui/assets"; import { getAssetIDByName } from "@ui/assets";
import { showToast } from "@ui/toasts"; import { showToast } from "@ui/toasts";
@ -6,12 +6,11 @@ import { connectToDebugger } from "@lib/debug";
import { useProxy } from "@lib/storage"; import { useProxy } from "@lib/storage";
import settings from "@lib/settings"; import settings from "@lib/settings";
import logger from "@lib/logger"; import logger from "@lib/logger";
import Subpage from "@ui/settings/components/Subpage";
import AssetBrowser from "@ui/settings/pages/AssetBrowser";
const { FormSection, FormRow, FormInput, FormDivider } = Forms; const { FormSection, FormRow, FormInput, FormDivider } = Forms;
export default function Developer() { export default function Developer() {
const navigation = NavigationNative.useNavigation();
useProxy(settings); useProxy(settings);
return ( return (
@ -52,10 +51,7 @@ export default function Developer() {
label="Asset Browser" label="Asset Browser"
leading={<FormRow.Icon source={getAssetIDByName("ic_media_upload")} />} leading={<FormRow.Icon source={getAssetIDByName("ic_media_upload")} />}
trailing={FormRow.Arrow} trailing={FormRow.Arrow}
onPress={() => navigation.push(Subpage, { onPress={() => navigation.push("VendettaAssetBrowser")}
name: "Asset Browser",
children: AssetBrowser,
})}
/> />
</FormSection> </FormSection>
</RN.ScrollView> </RN.ScrollView>