From 7a4a8ec23e82b30f6b56685421ed7e24fa5d7b3b Mon Sep 17 00:00:00 2001 From: cocobo1 Date: Sat, 11 Oct 2025 18:06:31 +0300 Subject: [PATCH] Revert "port information menu look from ShiggyCord (was exclusive only for core" This reverts commit 93d5c354a119be52a3a8d99bcc49860c737e6b3f. --- .../pages/Plugins/components/PluginCard.tsx | 256 +++++++-------- src/core/ui/settings/pages/Plugins/index.tsx | 258 ++++++--------- .../ui/settings/pages/Plugins/models/bunny.ts | 79 ++--- .../settings/pages/Plugins/models/vendetta.ts | 60 ++-- .../Plugins/sheets/PluginInfoActionSheet.tsx | 299 ++++++------------ 5 files changed, 365 insertions(+), 587 deletions(-) diff --git a/src/core/ui/settings/pages/Plugins/components/PluginCard.tsx b/src/core/ui/settings/pages/Plugins/components/PluginCard.tsx index 6ab8f1e..b199f55 100644 --- a/src/core/ui/settings/pages/Plugins/components/PluginCard.tsx +++ b/src/core/ui/settings/pages/Plugins/components/PluginCard.tsx @@ -3,182 +3,154 @@ import { UnifiedPluginModel } from "@core/ui/settings/pages/Plugins/models"; import { usePluginCardStyles } from "@core/ui/settings/pages/Plugins/usePluginCardStyles"; import { findAssetId } from "@lib/api/assets"; import { NavigationNative, tokens } from "@metro/common"; -import { - Card, - IconButton, - Stack, - TableSwitch, - Text, -} from "@metro/common/components"; +import { Card, IconButton, Stack, TableSwitch, Text } from "@metro/common/components"; import { showSheet } from "@ui/sheets"; import chroma from "chroma-js"; import { createContext, useContext, useMemo } from "react"; import { Image, View } from "react-native"; -const CardContext = createContext<{ - plugin: UnifiedPluginModel; - result: Fuzzysort.KeysResult; -}>(null!); +const CardContext = createContext<{ plugin: UnifiedPluginModel, result: Fuzzysort.KeysResult; }>(null!); const useCardContext = () => useContext(CardContext); function getHighlightColor(): import("react-native").ColorValue { - return chroma(tokens.unsafe_rawColors.YELLOW_300).alpha(0.3).hex(); + return chroma(tokens.unsafe_rawColors.YELLOW_300).alpha(0.3).hex(); } function Title() { - const styles = usePluginCardStyles(); - const { plugin, result } = useCardContext(); + const styles = usePluginCardStyles(); + const { plugin, result } = useCardContext(); - // could be empty if the plugin name is irrelevant! - const highlightedNode = result[0].highlight((m, i) => ( - - {m} - - )); + // could be empty if the plugin name is irrelevant! + const highlightedNode = result[0].highlight((m, i) => + + {m} + + ); - const icon = plugin.icon && findAssetId(plugin.icon); + const icon = plugin.icon && findAssetId(plugin.icon); - const textNode = ( - - {highlightedNode.length ? highlightedNode : plugin.name} - - ); + const textNode = ( + + {highlightedNode.length ? highlightedNode : plugin.name} + + ); - return ( - - {icon && } - {textNode} - - ); + return + {icon && } + {textNode} + ; } function Authors() { - const { plugin, result } = useCardContext(); - const styles = usePluginCardStyles(); + const { plugin, result } = useCardContext(); + const styles = usePluginCardStyles(); - if (!plugin.authors) return null; + if (!plugin.authors) return null; - // could be empty if the author(s) are irrelevant with the search! - const highlightedNode = result[2].highlight((m, i) => ( - - {m} - - )); + // could be empty if the author(s) are irrelevant with the search! + const highlightedNode = result[2].highlight((m, i) => + + {m} + + ); - const badges = plugin.getBadges(); - const authorText = - highlightedNode.length > 0 - ? highlightedNode - : plugin.authors.map((a) => a.name).join(", "); + const badges = plugin.getBadges(); + const authorText = highlightedNode.length > 0 ? highlightedNode : plugin.authors.map(a => a.name).join(", "); - return ( - - - by {authorText} - - {badges.length > 0 && ( - - {badges.map((b, i) => ( - - ))} + return ( + + + by {authorText} + + {badges.length > 0 && + {badges.map((b, i) => )} + } - )} - - ); + ); } function Description() { - const { plugin, result } = useCardContext(); + const { plugin, result } = useCardContext(); - // could be empty if the description is irrelevant with the search! - const highlightedNode = result[1].highlight((m, i) => ( - - {m} - - )); + // could be empty if the description is irrelevant with the search! + const highlightedNode = result[1].highlight((m, i) => + {m} + ); - return ( - - {highlightedNode.length ? highlightedNode : plugin.description} - - ); + return + {highlightedNode.length ? highlightedNode : plugin.description} + ; } const Actions = () => { - const { plugin } = useCardContext(); - const navigation = NavigationNative.useNavigation(); + const { plugin } = useCardContext(); + const navigation = NavigationNative.useNavigation(); - return ( - - - navigation.push("PUPU_CUSTOM_PAGE", { - title: plugin.name, - render: plugin.getPluginSettingsComponent(), - }) - } - /> - - void showSheet( - "PluginInfoActionSheet", - plugin.resolveSheetComponent(), - { plugin, navigation }, - ) - } - /> - - ); + return + navigation.push("PUPU_CUSTOM_PAGE", { + title: plugin.name, + render: plugin.getPluginSettingsComponent(), + })} + /> + void showSheet( + "PluginInfoActionSheet", + plugin.resolveSheetComponent(), + { plugin, navigation } + )} + /> + ; }; -export default function PluginCard({ - result, - item: plugin, -}: CardWrapper) { - plugin.usePluginState(); +export default function PluginCard({ result, item: plugin }: CardWrapper) { + plugin.usePluginState(); - const [, forceUpdate] = React.useReducer(() => ({}), 0); - const cardContextValue = useMemo( - () => ({ plugin, result }), - [plugin, result], - ); + const [, forceUpdate] = React.useReducer(() => ({}), 0); + const cardContextValue = useMemo(() => ({ plugin, result }), [plugin, result]); - return ( - - - - - - - <Authors /> - </View> - <View> - <Stack spacing={12} direction="horizontal"> - <Actions /> - <TableSwitch - value={plugin.isEnabled()} - onValueChange={(v: boolean) => { - plugin.toggle(v); - forceUpdate(); - }} - /> - </Stack> - </View> - </View> - <Description /> - </Stack> - </Card> - </CardContext.Provider> - ); + return ( + <CardContext.Provider value={cardContextValue}> + <Card> + <Stack spacing={16}> + <View style={{ flexDirection: "row", justifyContent: "space-between" }}> + <View style={{ flexShrink: 1 }}> + <Title /> + <Authors /> + </View> + <View> + <Stack spacing={12} direction="horizontal"> + <Actions /> + <TableSwitch + value={plugin.isEnabled()} + onValueChange={(v: boolean) => { + plugin.toggle(v); + forceUpdate(); + }} + /> + </Stack> + </View> + </View> + <Description /> + </Stack> + </Card> + </CardContext.Provider> + ); } diff --git a/src/core/ui/settings/pages/Plugins/index.tsx b/src/core/ui/settings/pages/Plugins/index.tsx index 86b8470..afdf9fe 100644 --- a/src/core/ui/settings/pages/Plugins/index.tsx +++ b/src/core/ui/settings/pages/Plugins/index.tsx @@ -13,13 +13,7 @@ import { BUNNY_PROXY_PREFIX, VD_PROXY_PREFIX } from "@lib/utils/constants"; import { lazyDestructure } from "@lib/utils/lazy"; import { findByProps } from "@metro"; import { NavigationNative } from "@metro/common"; -import { - Button, - Card, - FlashList, - IconButton, - Text, -} from "@metro/common/components"; +import { Button, Card, FlashList, IconButton, Text } from "@metro/common/components"; import { ComponentProps } from "react"; import { View } from "react-native"; import type { BunnyPluginManifest } from "@lib/addons/plugins/types"; @@ -28,53 +22,44 @@ import { UnifiedPluginModel } from "./models"; import unifyBunnyPlugin from "./models/bunny"; import unifyVdPlugin from "./models/vendetta"; -const { openAlert } = lazyDestructure(() => - findByProps("openAlert", "dismissAlert"), -); -const { AlertModal, AlertActions, AlertActionButton } = lazyDestructure(() => - findByProps("AlertModal", "AlertActions"), -); +const { openAlert } = lazyDestructure(() => findByProps("openAlert", "dismissAlert")); +const { AlertModal, AlertActions, AlertActionButton } = lazyDestructure(() => findByProps("AlertModal", "AlertActions")); -interface PluginPageProps - extends Partial<ComponentProps<typeof AddonPage<UnifiedPluginModel>>> { - useItems: () => unknown[]; +interface PluginPageProps extends Partial<ComponentProps<typeof AddonPage<UnifiedPluginModel>>> { + useItems: () => unknown[]; } function PluginPage(props: PluginPageProps) { - const items = props.useItems(); + const items = props.useItems(); - return ( - <AddonPage<UnifiedPluginModel> - CardComponent={PluginCard} - title={Strings.PLUGINS} - searchKeywords={[ - "name", - "description", - (p) => - p.authors - ?.map((a: Author | string) => (typeof a === "string" ? a : a.name)) - .join() || "", - ]} - sortOptions={{ - "Name (A-Z)": (a, b) => a.name.localeCompare(b.name), - "Name (Z-A)": (a, b) => b.name.localeCompare(a.name), - }} - safeModeHint={{ message: Strings.SAFE_MODE_NOTICE_PLUGINS }} - items={items} - {...props} - /> - ); + return <AddonPage<UnifiedPluginModel> + CardComponent={PluginCard} + title={Strings.PLUGINS} + searchKeywords={[ + "name", + "description", + p => p.authors?.map( + (a: Author | string) => typeof a === "string" ? a : a.name + ).join() || "" + ]} + sortOptions={{ + "Name (A-Z)": (a, b) => a.name.localeCompare(b.name), + "Name (Z-A)": (a, b) => b.name.localeCompare(a.name) + }} + safeModeHint={{ message: Strings.SAFE_MODE_NOTICE_PLUGINS }} + items={items} + {...props} + />; } export default function Plugins() { - useProxy(settings); - const navigation = NavigationNative.useNavigation(); + useProxy(settings); + const navigation = NavigationNative.useNavigation(); - return ( - <PluginPage - useItems={() => { - useProxy(VdPluginManager.plugins); - useObservable([pluginSettings]); + return <PluginPage + useItems={() => { + useProxy(VdPluginManager.plugins); + useObservable([pluginSettings]); const vdPlugins = Object.values(VdPluginManager.plugins).map(unifyVdPlugin); const bnPlugins = [...registeredPlugins.values()].filter(p => isPluginInstalled(p.id) && isCorePlugin(p.id)).map(unifyBunnyPlugin); @@ -85,127 +70,68 @@ export default function Plugins() { const unproxiedPlugins = Object.values(VdPluginManager.plugins).filter(p => !p.id.startsWith(VD_PROXY_PREFIX) && !p.id.startsWith(BUNNY_PROXY_PREFIX)); if (!unproxiedPlugins.length) return null; - return ( - <View style={{ marginVertical: 12, marginHorizontal: 10 }}> - <Card border="strong"> - <View - style={{ - flex: 1, - justifyContent: "center", - alignItems: "center", - flexDirection: "row", - }} - > - <View style={{ gap: 6, flexShrink: 1 }}> - <Text variant="heading-md/bold">Unproxied Plugins Found</Text> - <Text variant="text-sm/medium" color="text-muted"> - Plugins installed from unproxied sources may run unverified - code in this app without your awareness. - </Text> - </View> - <View style={{ marginLeft: "auto" }}> - <IconButton - size="sm" - variant="secondary" - icon={findAssetId("CircleInformationIcon-primary")} - style={{ marginLeft: 8 }} - onPress={() => { - navigation.push("PUPU_CUSTOM_PAGE", { - title: "Unproxied Plugins", - render: () => { - return ( - <FlashList - data={unproxiedPlugins} - contentContainerStyle={{ padding: 8 }} - ItemSeparatorComponent={() => ( - <View style={{ height: 8 }} /> - )} - renderItem={({ item: p }: any) => ( - <Card> - <Text variant="heading-md/semibold"> - {p.id} - </Text> - </Card> - )} + return <View style={{ marginVertical: 12, marginHorizontal: 10 }}> + <Card border="strong"> + <View style={{ flex: 1, justifyContent: "center", alignItems: "center", flexDirection: "row" }}> + <View style={{ gap: 6, flexShrink: 1 }}> + <Text variant="heading-md/bold">Unproxied Plugins Found</Text> + <Text variant="text-sm/medium" color="text-muted"> + Plugins installed from unproxied sources may run unverified code in this app without your awareness. + </Text> + </View> + <View style={{ marginLeft: "auto" }}> + <IconButton + size="sm" + variant="secondary" + icon={findAssetId("CircleInformationIcon-primary")} + style={{ marginLeft: 8 }} + onPress={() => { + navigation.push("PUPU_CUSTOM_PAGE", { + title: "Unproxied Plugins", + render: () => { + return <FlashList + data={unproxiedPlugins} + contentContainerStyle={{ padding: 8 }} + ItemSeparatorComponent={() => <View style={{ height: 8 }} />} + renderItem={({ item: p }: any) => <Card> + <Text variant="heading-md/semibold">{p.id}</Text> + </Card>} + />; + } + }); + }} /> - ); - }, - }); - }} - /> - </View> - </View> - </Card> - </View> - ); - }} - installAction={{ - label: "Install a plugin", - fetchFn: async (url: string) => { - if ( - !url.startsWith(VD_PROXY_PREFIX) && - !url.startsWith(BUNNY_PROXY_PREFIX) && - !settings.developerSettings - ) { - openAlert( - "bunny-plugin-unproxied-confirmation", - <AlertModal - title="Hold On!" - content="You're trying to install a plugin from an unproxied external source. This means you're trusting the creator to run their code in this app without your knowledge. Are you sure you want to continue?" - extraContent={ - <Card> - <Text variant="text-md/bold">{url}</Text> - </Card> + </View> + </View> + </Card> + </View>; + }} + installAction={{ + label: "Install a plugin", + fetchFn: async (url: string) => { + if (!url.startsWith(VD_PROXY_PREFIX) && !url.startsWith(BUNNY_PROXY_PREFIX) && !settings.developerSettings) { + openAlert("bunny-plugin-unproxied-confirmation", <AlertModal + title="Hold On!" + content="You're trying to install a plugin from an unproxied external source. This means you're trusting the creator to run their code in this app without your knowledge. Are you sure you want to continue?" + extraContent={<Card><Text variant="text-md/bold">{url}</Text></Card>} + actions={<AlertActions> + <AlertActionButton text="Continue" variant="primary" onPress={() => { + VdPluginManager.installPlugin(url) + .then(() => showToast(Strings.TOASTS_INSTALLED_PLUGIN, findAssetId("Check"))) + .catch(e => openAlert("bunny-plugin-install-failed", <AlertModal + title="Install Failed" + content={`Unable to install plugin from '${url}':`} + extraContent={<Card><Text variant="text-md/normal">{e instanceof Error ? e.message : String(e)}</Text></Card>} + actions={<AlertActionButton text="Okay" variant="primary" />} + />)); + }} /> + <AlertActionButton text="Cancel" variant="secondary" /> + </AlertActions>} + />); + } else { + return await VdPluginManager.installPlugin(url); } - actions={ - <AlertActions> - <AlertActionButton - text="Continue" - variant="primary" - onPress={() => { - VdPluginManager.installPlugin(url) - .then(() => - showToast( - Strings.TOASTS_INSTALLED_PLUGIN, - findAssetId("Check"), - ), - ) - .catch((e) => - openAlert( - "bunny-plugin-install-failed", - <AlertModal - title="Install Failed" - content={`Unable to install plugin from '${url}':`} - extraContent={ - <Card> - <Text variant="text-md/normal"> - {e instanceof Error - ? e.message - : String(e)} - </Text> - </Card> - } - actions={ - <AlertActionButton - text="Okay" - variant="primary" - /> - } - />, - ), - ); - }} - /> - <AlertActionButton text="Cancel" variant="secondary" /> - </AlertActions> - } - />, - ); - } else { - return await VdPluginManager.installPlugin(url); - } - }, - }} - /> - ); + } + }} + />; } diff --git a/src/core/ui/settings/pages/Plugins/models/bunny.ts b/src/core/ui/settings/pages/Plugins/models/bunny.ts index 252e6ff..106be65 100644 --- a/src/core/ui/settings/pages/Plugins/models/bunny.ts +++ b/src/core/ui/settings/pages/Plugins/models/bunny.ts @@ -1,52 +1,43 @@ import { PupuIcon } from "@core/ui/settings"; -import { - disablePlugin, - enablePlugin, - getPluginSettingsComponent, - isPluginEnabled, - pluginSettings, -} from "@lib/addons/plugins"; +import { disablePlugin, enablePlugin, getPluginSettingsComponent, isPluginEnabled, pluginSettings } from "@lib/addons/plugins"; import { BunnyPluginManifest } from "@lib/addons/plugins/types"; import { useObservable } from "@lib/api/storage"; import { UnifiedPluginModel } from "."; -export default function unifyBunnyPlugin( - manifest: BunnyPluginManifest, -): UnifiedPluginModel { - return { - id: manifest.id, - name: manifest.display.name, - description: manifest.display.description, - authors: manifest.display.authors, +export default function unifyBunnyPlugin(manifest: BunnyPluginManifest): UnifiedPluginModel { + return { + id: manifest.id, + name: manifest.display.name, + description: manifest.display.description, + authors: manifest.display.authors, - getBadges() { - return [ - { source: { uri: PupuIcon } }, - // { source: findAssetId("CheckmarkLargeBoldIcon")! } - ]; - }, - isEnabled: () => isPluginEnabled(manifest.id), - isInstalled: () => manifest.id in pluginSettings, - usePluginState() { - useObservable([pluginSettings]); - }, - toggle(start: boolean) { - try { - start ? enablePlugin(manifest.id, true) : disablePlugin(manifest.id); - } catch (e) { - console.error(e); - // showToast("Failed to toggle plugin " + e, findAssetId("Small")); - } - }, - resolveSheetComponent() { - // Return a Promise resolving to the sheet component for interface consistency - return Promise.resolve({ - default: require("../sheets/PluginInfoActionSheet").default, - }); - }, - getPluginSettingsComponent() { - return getPluginSettingsComponent(manifest.id); - }, - }; + getBadges() { + return [ + { source: { uri: PupuIcon } }, + // { source: findAssetId("CheckmarkLargeBoldIcon")! } + ]; + }, + isEnabled: () => isPluginEnabled(manifest.id), + isInstalled: () => manifest.id in pluginSettings, + usePluginState() { + useObservable([pluginSettings]); + }, + toggle(start: boolean) { + try { + start + ? enablePlugin(manifest.id, true) + : disablePlugin(manifest.id); + } catch (e) { + console.error(e); + // showToast("Failed to toggle plugin " + e, findAssetId("Small")); + } + }, + resolveSheetComponent() { + return import("../sheets/PluginInfoActionSheet"); + }, + getPluginSettingsComponent() { + return getPluginSettingsComponent(manifest.id); + }, + }; } diff --git a/src/core/ui/settings/pages/Plugins/models/vendetta.ts b/src/core/ui/settings/pages/Plugins/models/vendetta.ts index fc36653..ee735f5 100644 --- a/src/core/ui/settings/pages/Plugins/models/vendetta.ts +++ b/src/core/ui/settings/pages/Plugins/models/vendetta.ts @@ -3,38 +3,32 @@ import { useProxy } from "@core/vendetta/storage"; import { UnifiedPluginModel } from "."; -export default function unifyVdPlugin( - vdPlugin: VendettaPlugin, -): UnifiedPluginModel { - return { - id: vdPlugin.id, - name: vdPlugin.manifest.name, - description: vdPlugin.manifest.description, - authors: vdPlugin.manifest.authors, - icon: vdPlugin.manifest.vendetta?.icon, +export default function unifyVdPlugin(vdPlugin: VendettaPlugin): UnifiedPluginModel { + return { + id: vdPlugin.id, + name: vdPlugin.manifest.name, + description: vdPlugin.manifest.description, + authors: vdPlugin.manifest.authors, + icon: vdPlugin.manifest.vendetta?.icon, - getBadges() { - return []; - }, - isEnabled: () => vdPlugin.enabled, - isInstalled: () => - Boolean(vdPlugin && VdPluginManager.plugins[vdPlugin.id]), - usePluginState() { - useProxy(VdPluginManager.plugins[vdPlugin.id]); - }, - toggle(start: boolean) { - start - ? VdPluginManager.startPlugin(vdPlugin.id) - : VdPluginManager.stopPlugin(vdPlugin.id); - }, - resolveSheetComponent() { - // Return a Promise resolving to the unified PluginInfoActionSheet for consistency - return Promise.resolve({ - default: require("../sheets/PluginInfoActionSheet").default, - }); - }, - getPluginSettingsComponent() { - return VdPluginManager.getSettings(vdPlugin.id); - }, - }; + getBadges() { + return []; + }, + isEnabled: () => vdPlugin.enabled, + isInstalled: () => Boolean(vdPlugin && VdPluginManager.plugins[vdPlugin.id]), + 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); + }, + }; } diff --git a/src/core/ui/settings/pages/Plugins/sheets/PluginInfoActionSheet.tsx b/src/core/ui/settings/pages/Plugins/sheets/PluginInfoActionSheet.tsx index e105640..4c769df 100644 --- a/src/core/ui/settings/pages/Plugins/sheets/PluginInfoActionSheet.tsx +++ b/src/core/ui/settings/pages/Plugins/sheets/PluginInfoActionSheet.tsx @@ -1,211 +1,106 @@ -import { hideSheet } from "@lib/ui/sheets"; -import { ActionSheet, Card, IconButton, Text } from "@metro/common/components"; -import { clipboard } from "@metro/common"; -import { showToast } from "@lib/ui/toasts"; -import { showConfirmationAlert } from "@core/vendetta/alerts"; -import { VdPluginManager } from "@core/vendetta/plugins"; -import { purgeStorage as purgeVdStorage } from "@core/vendetta/storage"; -import { purgeStorage } from "@lib/api/storage"; +import { startPlugin } from "@lib/addons/plugins"; import { findAssetId } from "@lib/api/assets"; -import { useState } from "react"; +import { hideSheet } from "@lib/ui/sheets"; +import { ActionSheet, Card, ContextMenu, IconButton, Text } from "@metro/common/components"; +import { ComponentProps } from "react"; import { ScrollView, View } from "react-native"; -import TitleComponent from "./TitleComponent"; + import { PluginInfoActionSheetProps } from "./common"; +import TitleComponent from "./TitleComponent"; -function PluginInfoIconButton(props) { - const { onPress } = props; - props.onPress &&= () => { - hideSheet("PluginInfoActionSheet"); - onPress?.(); - }; - return <IconButton {...props} />; +function PluginInfoIconButton(props: ComponentProps<typeof IconButton>) { + const { onPress } = props; + props.onPress &&= () => { + hideSheet("PluginInfoActionSheet"); + onPress?.(); + }; + + return <IconButton {...props} />; } -export default function PluginInfoActionSheet({ - plugin, - navigation, -}: PluginInfoActionSheetProps) { - plugin.usePluginState(); - const [loading, setLoading] = useState(false); +export default function PluginInfoActionSheet({ plugin, navigation }: PluginInfoActionSheetProps) { + plugin.usePluginState(); - // Determine plugin type - const isVendettaPlugin = plugin.id.includes("/"); - const isCorePlugin = - plugin.id.startsWith("bunny.") || plugin.id.startsWith("vendetta."); + return <ActionSheet> + <ScrollView contentContainerStyle={{ gap: 12, marginBottom: 12 }}> + <View style={{ flexDirection: "row", alignItems: "center", gap: 8, paddingVertical: 24, justifyContent: "space-between", width: "100%" }}> + <TitleComponent plugin={plugin} /> + <ContextMenu + items={[ + { + label: "Details", + iconSource: findAssetId("CircleInformationIcon-primary"), + action: () => { + } + }, + // { + // label: true ? "Disable Updates" : "Enable Updates", + // iconSource: true ? findAssetId("ClockXIcon") : findAssetId("ClockIcon"), + // action: () => { - const copyPluginUrl = () => { - let url = plugin.id; - if (isVendettaPlugin) { - url = plugin.id; - } else { - try { - const pluginAny = plugin; - const repoUrl = - pluginAny._manifest?.parentRepository || - pluginAny.manifest?.parentRepository; - url = repoUrl ? `${repoUrl}/builds/${plugin.id}` : plugin.id; - } catch (e) { - url = plugin.id; - } - } - clipboard.setString(url); - showToast("Copied to clipboard!", findAssetId("toast_copy_link")); - }; - - const refetchPlugin = async () => { - setLoading(true); - try { - if (isVendettaPlugin) { - const vdPlugin = VdPluginManager.plugins[plugin.id]; - if (vdPlugin.enabled) VdPluginManager.stopPlugin(plugin.id, false); - await VdPluginManager.fetchPlugin(plugin.id); - if (vdPlugin.enabled) await VdPluginManager.startPlugin(plugin.id); - showToast("Plugin refreshed successfully"); - } else { - // For Bunny plugins - // If you have a refreshPlugin function, call it here - showToast("Plugin refreshed successfully"); - } - } catch (e) { - showToast("Failed to refresh plugin"); - } finally { - setLoading(false); - } - }; - - const clearPluginData = () => { - showConfirmationAlert({ - title: "Clear Data", - content: - "Are you sure you want to clear all data for this plugin? This action cannot be undone.", - confirmText: "Clear", - confirmColor: "red", - cancelText: "Cancel", - onConfirm: async () => { - hideSheet("PluginInfoActionSheet"); - try { - if (isVendettaPlugin) { - const vdPlugin = VdPluginManager.plugins[plugin.id]; - if (vdPlugin.enabled) VdPluginManager.stopPlugin(plugin.id, false); - await purgeVdStorage(plugin.id); - if (vdPlugin.enabled) await VdPluginManager.startPlugin(plugin.id); - } else { - await purgeStorage(`plugins/storage/${plugin.id}.json`); - } - showToast("Plugin data cleared successfully"); - } catch (e) { - showToast("Failed to clear plugin data"); - } - }, - }); - }; - - const uninstallPluginHandler = () => { - if (isCorePlugin) { - showToast("Core plugins cannot be uninstalled"); - return; - } - showConfirmationAlert({ - title: "Uninstall Plugin", - content: - "Are you sure you want to uninstall this plugin? This action cannot be undone.", - confirmText: "Uninstall", - confirmColor: "red", - cancelText: "Cancel", - onConfirm: async () => { - hideSheet("PluginInfoActionSheet"); - try { - if (isVendettaPlugin) { - await VdPluginManager.removePlugin(plugin.id); - } else { - // If you have an uninstallPlugin function, call it here - } - showToast("Plugin uninstalled successfully"); - } catch (e) { - showToast( - `Failed to uninstall plugin: ${e instanceof Error ? e.message : String(e)}`, - ); - } - }, - }); - }; - - return ( - <ActionSheet> - <ScrollView contentContainerStyle={{ gap: 12, marginBottom: 12 }}> - <View - style={{ - flexDirection: "row", - alignItems: "center", - gap: 8, - paddingVertical: 24, - justifyContent: "space-between", - width: "100%", - }} - > - <TitleComponent plugin={plugin} /> - </View> - <View - style={{ - flexDirection: "row", - justifyContent: "center", - alignItems: "center", - flexWrap: "wrap", - gap: 12, - paddingHorizontal: 4, - }} - > - <PluginInfoIconButton - label="Configure" - variant="secondary" - disabled={!plugin.getPluginSettingsComponent()} - icon={findAssetId("WrenchIcon")} - onPress={() => { - navigation.push("PUPU_CUSTOM_PAGE", { - title: plugin.name, - render: plugin.getPluginSettingsComponent(), - }); - }} - /> - <PluginInfoIconButton - label="Refetch" - variant="secondary" - icon={findAssetId("RetryIcon")} - onPress={refetchPlugin} - disabled={loading} - /> - <PluginInfoIconButton - label="Copy URL" - variant="secondary" - icon={findAssetId("LinkIcon")} - onPress={copyPluginUrl} - /> - <PluginInfoIconButton - label="Clear Data" - variant="secondary" - icon={findAssetId("FileIcon")} - onPress={clearPluginData} - /> - {!isCorePlugin && ( - <PluginInfoIconButton - label="Uninstall" - variant="secondary" - icon={findAssetId("TrashIcon")} - onPress={uninstallPluginHandler} - /> - )} - </View> - <Card> - <Text - variant="text-md/semibold" - color="text-primary" - style={{ marginBottom: 4 }} - > - Description - </Text> - <Text variant="text-md/medium">{plugin.description}</Text> - </Card> - </ScrollView> - </ActionSheet> - ); + // } + // }, + { + label: "Clear Data", + iconSource: findAssetId("FileIcon"), + variant: "destructive", + action: () => { + } + }, + { + label: "Uninstall", + iconSource: findAssetId("TrashIcon"), + variant: "destructive", + action: () => { + } + } + ]} + > + {props => <IconButton + {...props} + icon={findAssetId("MoreHorizontalIcon")} + variant="secondary" + size="sm" + />} + </ContextMenu> + </View> + <View style={{ flexDirection: "row", justifyContent: "space-around", alignContent: "center" }}> + <PluginInfoIconButton + label="Configure" + variant="secondary" + disabled={!plugin.getPluginSettingsComponent()} + icon={findAssetId("WrenchIcon")} + onPress={() => { + navigation.push("PUPU_CUSTOM_PAGE", { + title: plugin.name, + render: plugin.getPluginSettingsComponent(), + }); + }} + /> + <PluginInfoIconButton + label="Refetch" + variant="secondary" + icon={findAssetId("RetryIcon")} + onPress={() => { + startPlugin(plugin.id); + }} + /> + <PluginInfoIconButton + label="Copy URL" + variant="secondary" + icon={findAssetId("LinkIcon")} + onPress={() => { + }} + /> + </View> + <Card> + <Text variant="text-md/semibold" color="text-primary" style={{ marginBottom: 4 }}> + Description + </Text> + <Text variant="text-md/medium"> + {plugin.description} + </Text> + </Card> + </ScrollView> + </ActionSheet>; }