diff --git a/src/core/ui/components/AddonPage.tsx b/src/core/ui/components/AddonPage.tsx index 068d9a6..7a7dbba 100644 --- a/src/core/ui/components/AddonPage.tsx +++ b/src/core/ui/components/AddonPage.tsx @@ -10,7 +10,7 @@ import { FloatingActionButton, HelpMessage } from "@metro/common/components"; import { showInputAlert } from "@ui/alerts"; import { ErrorBoundary, Search } from "@ui/components"; import fuzzysort from "fuzzysort"; -import { useMemo } from "react"; +import { ComponentType, ReactNode, useMemo } from "react"; import { View } from "react-native"; type SearchKeywords = Array string)>; @@ -18,13 +18,14 @@ type SearchKeywords = Array string)>; interface AddonPageProps { title: string; fetchFunction?: (url: string) => Promise; - items: Record; + items: any[]; resolveItem?: (value: any) => T | undefined; safeModeMessage: string; - safeModeExtras?: JSX.Element | JSX.Element[]; - card: React.ComponentType>; + safeModeExtras?: ReactNode; + card: ComponentType>; searchKeywords: SearchKeywords; - onFABPress?: () => void; + onFabPress?: () => void; + ListFooterComponent?: ReactNode | ComponentType; } // TODO: Move to somewhere else @@ -36,7 +37,7 @@ export default function AddonPage({ card: CardComponent, ...pr const [search, setSearch] = React.useState(""); const items = useMemo(() => { - let values = Object.values(props.items); + let values = props.items; if (props.resolveItem) values = values.map(props.resolveItem); return values.filter(i => i && typeof i === "object"); }, [props.items]); @@ -54,8 +55,7 @@ export default function AddonPage({ card: CardComponent, ...pr } setSearch(v.toLowerCase())} - placeholder={Strings.SEARCH} + onChangeText={v => setSearch(v)} /> ), []); @@ -67,15 +67,16 @@ export default function AddonPage({ card: CardComponent, ...pr estimatedItemSize={136} ListHeaderComponent={headerElement} contentContainerStyle={{ paddingBottom: 90, paddingHorizontal: 5 }} + ListFooterComponent={props.ListFooterComponent} renderItem={({ item }: any) => ( )} /> - {(props.fetchFunction ?? props.onFABPress) && { + onPress={props.onFabPress ?? (() => { // from ./InstallButton.tsx clipboard.getString().then((content: string) => showInputAlert({ diff --git a/src/core/ui/settings/pages/Developer/index.tsx b/src/core/ui/settings/pages/Developer/index.tsx index 9150969..f8338dd 100644 --- a/src/core/ui/settings/pages/Developer/index.tsx +++ b/src/core/ui/settings/pages/Developer/index.tsx @@ -101,7 +101,7 @@ export default function Developer() { label={Strings.ASSET_BROWSER} icon={} trailing={TableRow.Arrow} - onPress={() => navigation.push("VendettaCustomPage", { + onPress={() => navigation.push("BUNNY_CUSTOM_PAGE", { title: Strings.ASSET_BROWSER, render: AssetBrowser, })} @@ -120,8 +120,8 @@ export default function Developer() { options: [ // @ts-expect-error // Of course, to trigger an error, we need to do something incorrectly. The below will do! - { label: Strings.BUNNY, onPress: () => navigation.push("VendettaCustomPage", { render: () => }) }, - { label: "Discord", isDestructive: true, onPress: () => navigation.push("VendettaCustomPage", { noErrorBoundary: true }) }, + { label: Strings.BUNNY, onPress: () => navigation.push("BUNNY_CUSTOM_PAGE", { render: () => }) }, + { label: "Discord", isDestructive: true, onPress: () => navigation.push("BUNNY_CUSTOM_PAGE", { noErrorBoundary: true }) }, ], })} /> diff --git a/src/core/ui/settings/pages/Fonts/FontCard.tsx b/src/core/ui/settings/pages/Fonts/FontCard.tsx index f84c27c..8a53fc7 100644 --- a/src/core/ui/settings/pages/Fonts/FontCard.tsx +++ b/src/core/ui/settings/pages/Fonts/FontCard.tsx @@ -83,7 +83,7 @@ export default function FontCard({ item: font }: CardWrapper) { { - navigation.push("VendettaCustomPage", { + navigation.push("BUNNY_CUSTOM_PAGE", { title: "Edit Font", render: () => }); diff --git a/src/core/ui/settings/pages/Fonts/index.tsx b/src/core/ui/settings/pages/Fonts/index.tsx index 3e788b5..913071d 100644 --- a/src/core/ui/settings/pages/Fonts/index.tsx +++ b/src/core/ui/settings/pages/Fonts/index.tsx @@ -19,11 +19,11 @@ export default function Fonts() { title={Strings.FONTS} searchKeywords={["name", "description"]} fetchFunction={installFont} - items={fonts as Record} + items={Object.values(fonts)} safeModeMessage={Strings.SAFE_MODE_NOTICE_FONTS} card={FontCard} - onFABPress={() => { - navigation.push("VendettaCustomPage", { + onFabPress={() => { + navigation.push("BUNNY_CUSTOM_PAGE", { title: "Import Font", render: () => }); diff --git a/src/core/ui/settings/pages/General/index.tsx b/src/core/ui/settings/pages/General/index.tsx index 094ba14..bffb4b1 100644 --- a/src/core/ui/settings/pages/General/index.tsx +++ b/src/core/ui/settings/pages/General/index.tsx @@ -35,7 +35,7 @@ export default function General() { label={Strings.ABOUT} icon={} trailing={TableRow.Arrow} - onPress={() => navigation.push("VendettaCustomPage", { + onPress={() => navigation.push("BUNNY_CUSTOM_PAGE", { title: Strings.ABOUT, render: () => , })} diff --git a/src/core/ui/settings/pages/PluginBrowser/index.tsx b/src/core/ui/settings/pages/PluginBrowser/index.tsx new file mode 100644 index 0000000..b0b4e13 --- /dev/null +++ b/src/core/ui/settings/pages/PluginBrowser/index.tsx @@ -0,0 +1,5 @@ + + +export default function PluginBrowser() { + return null; +} diff --git a/src/core/ui/settings/pages/Plugins/PluginCard.tsx b/src/core/ui/settings/pages/Plugins/PluginCard.tsx index 038f9b7..c7c3590 100644 --- a/src/core/ui/settings/pages/Plugins/PluginCard.tsx +++ b/src/core/ui/settings/pages/Plugins/PluginCard.tsx @@ -6,10 +6,10 @@ import { showSheet } from "@ui/sheets"; import { createContext, memo, useContext } from "react"; import { Image, View } from "react-native"; -import { PluginManifest } from "."; +import { UnifiedPluginModel } from "."; import { usePluginCardStyles } from "./usePluginCardStyles"; -const PluginContext = createContext(null!); +const PluginContext = createContext(null!); const usePlugin = () => useContext(PluginContext); function Authors() { @@ -92,7 +92,7 @@ const Actions = memo(() => { variant="secondary" icon={findAssetId("WrenchIcon")} disabled={!plugin.getPluginSettingsComponent()} - onPress={() => navigation.push("VendettaCustomPage", { + onPress={() => navigation.push("BUNNY_CUSTOM_PAGE", { title: plugin.name, render: plugin.getPluginSettingsComponent(), })} @@ -110,7 +110,7 @@ const Actions = memo(() => { ; }); -export default function PluginCard({ item: plugin }: CardWrapper) { +export default function PluginCard({ item: plugin }: CardWrapper) { plugin.usePluginState(); return ( diff --git a/src/core/ui/settings/pages/Plugins/index.tsx b/src/core/ui/settings/pages/Plugins/index.tsx index fe5c8fc..ec6fee7 100644 --- a/src/core/ui/settings/pages/Plugins/index.tsx +++ b/src/core/ui/settings/pages/Plugins/index.tsx @@ -1,17 +1,21 @@ import { Strings } from "@core/i18n"; import AddonPage from "@core/ui/components/AddonPage"; import PluginCard from "@core/ui/settings/pages/Plugins/PluginCard"; -import { VdPluginManager, VendettaPlugin } from "@core/vendetta/plugins"; +import { VdPluginManager } from "@core/vendetta/plugins"; +import { findAssetId } from "@lib/api/assets"; import { settings } from "@lib/api/settings"; import { useProxy } from "@lib/api/storage"; -import { useProxy as useProxyNew } from "@lib/api/storage/new"; -import { getId, getPluginSettingsComponent, isPluginEnabled, pluginSettings, registeredPlugins, startPlugin, stopPlugin } from "@lib/plugins"; -import { BunnyPluginManifest } from "@lib/plugins/types"; +import { registeredPlugins } from "@lib/plugins"; import { Author } from "@lib/utils/types"; -import { SegmentedControl, SegmentedControlPages, useSegmentedControlState } from "@metro/common/components"; +import { NavigationNative } from "@metro/common"; +import { Button, SegmentedControl, SegmentedControlPages, Text, useSegmentedControlState } from "@metro/common/components"; +import { ComponentProps } from "react"; import { useWindowDimensions, View } from "react-native"; -export interface PluginManifest { +import unifyBunnyPlugin from "./models/bunny"; +import unifyVdPlugin from "./models/vendetta"; + +export interface UnifiedPluginModel { id: string; name: string; description?: string; @@ -25,62 +29,15 @@ export interface PluginManifest { getPluginSettingsComponent(): React.ComponentType | null; } -function resolveFromVdPlugin(vdPlugin: VendettaPlugin): PluginManifest { - return { - id: vdPlugin.id, - name: vdPlugin.manifest.name, - description: vdPlugin.manifest.description, - authors: vdPlugin.manifest.authors, - icon: vdPlugin.manifest.vendetta?.icon, - isEnabled() { - return vdPlugin.enabled; - }, - usePluginState() { - useProxy(VdPluginManager.plugins[vdPlugin.id]); - }, - toggle(start: boolean) { - start - ? VdPluginManager.startPlugin(vdPlugin.id) - : VdPluginManager.stopPlugin(vdPlugin.id); - }, - resolveSheetComponent() { - return import("./sheets/VdPluginInfoActionSheet"); - }, - getPluginSettingsComponent() { - return VdPluginManager.getSettings(vdPlugin.id); - }, - }; +function navigateToPluginBrowser(navigation: any) { + navigation.push("BUNNY_CUSTOM_PAGE", { + title: "Plugin Browser", + render: React.lazy(() => import("../PluginBrowser")), + }); } -function resolveFromBunnyPlugin(manifest: BunnyPluginManifest): PluginManifest { - - return { - id: manifest.id, - name: manifest.name, - description: manifest.description, - authors: manifest.authors, - isEnabled() { - return isPluginEnabled(getId(manifest)); - }, - usePluginState() { - useProxyNew(pluginSettings); - }, - toggle(start: boolean) { - start - ? startPlugin(getId(manifest)) - : stopPlugin(getId(manifest)); - }, - resolveSheetComponent() { - return import("./sheets/PluginInfoActionSheet"); - }, - getPluginSettingsComponent() { - return getPluginSettingsComponent(getId(manifest)); - }, - }; -} - -function Page({ items, resolveItem, fetchFunction }: Record) { - return +function Page(props: Partial>>) { + return card={PluginCard} title={Strings.PLUGINS} searchKeywords={[ @@ -88,16 +45,16 @@ function Page({ items, resolveItem, fetchFunction }: Record) { "description", p => p.authors?.map((a: Author | string) => typeof a === "string" ? a : a.name).join() ]} - items={items} - resolveItem={resolveItem} - fetchFunction={fetchFunction} safeModeMessage={Strings.SAFE_MODE_NOTICE_PLUGINS} + items={props.items!} + {...props} />; } export default function Plugins() { useProxy(settings); + const navigation = NavigationNative.useNavigation(); const { width: pageWidth } = useWindowDimensions(); const state = useSegmentedControlState({ @@ -108,8 +65,8 @@ export default function Plugins() { id: "vendetta-plugins", page: ( VdPluginManager.installPlugin(url)} /> ) @@ -119,15 +76,34 @@ export default function Plugins() { id: "bunny-plugins", page: ( ( + + {"Looking for more?"} +