diff --git a/src/plugins/start/settings.plugins/components/PluginIcon.tsx b/src/plugins/start/settings.plugins/components/PluginIcon.tsx index 98e69ba..63836c3 100644 --- a/src/plugins/start/settings.plugins/components/PluginIcon.tsx +++ b/src/plugins/start/settings.plugins/components/PluginIcon.tsx @@ -8,8 +8,10 @@ const PuzzlePieceIcon = getAssetIdByName('PuzzlePieceIcon', 'png')! export function PluginIcon({ icon, size = 20, + danger = false, }: { icon?: string + danger?: boolean size?: number }) { const styles = usePluginIconStyles() @@ -26,7 +28,11 @@ export function PluginIcon({ ? (getAssetIdByName(icon) ?? PuzzlePieceIcon) : PuzzlePieceIcon } - style={[!dataUrl && styles.icon, { width: size, height: size }]} + style={[ + !dataUrl && styles.icon, + danger && styles.danger, + { width: size, height: size }, + ]} /> ) } @@ -35,4 +41,7 @@ const usePluginIconStyles = Design.createStyles({ icon: { tintColor: Tokens.default.colors.TEXT_DEFAULT, }, + danger: { + tintColor: Tokens.default.colors.TEXT_FEEDBACK_CRITICAL, + }, }) diff --git a/src/plugins/start/settings.plugins/components/PluginOptionsActionSheet.tsx b/src/plugins/start/settings.plugins/components/PluginOptionsActionSheet.tsx index ba1a76b..440b3e7 100644 --- a/src/plugins/start/settings.plugins/components/PluginOptionsActionSheet.tsx +++ b/src/plugins/start/settings.plugins/components/PluginOptionsActionSheet.tsx @@ -19,6 +19,7 @@ import { isPluginPendingUpdate, isPluginStartable, PluginFlags, + pList, runPluginLate, stopPlugin, } from '@revenge-mod/plugins/_' @@ -197,8 +198,15 @@ function AdvancedSection({ plugin }: { plugin: AnyPlugin }) { `plugin-deps-${id}`, { title: `Dependencies of ${name}`, + unsatisfiedTitle: `Unsatisfied dependencies of ${name}`, + unsatisfiedPlugins: + meta.unsatisfiedOptionalDependencies.map( + id => pList.get(id) ?? id, + ), plugins: dependencies, + dependencyMap: plugin.manifest.dependencies!, }, + 'stack', ) }} /> @@ -216,6 +224,7 @@ function AdvancedSection({ plugin }: { plugin: AnyPlugin }) { title: `Dependents of ${name}`, plugins: dependents, }, + 'stack', ) }} /> diff --git a/src/plugins/start/settings.plugins/components/PluginRelationsListActionSheet.tsx b/src/plugins/start/settings.plugins/components/PluginRelationsListActionSheet.tsx index c39926b..64c2820 100644 --- a/src/plugins/start/settings.plugins/components/PluginRelationsListActionSheet.tsx +++ b/src/plugins/start/settings.plugins/components/PluginRelationsListActionSheet.tsx @@ -1,31 +1,130 @@ -import { TableRowAssetIcon } from '@revenge-mod/components' import { Design } from '@revenge-mod/discord/design' +import { Clipboard } from '@revenge-mod/externals/react-native-clipboard' +import { formatVersion } from '@revenge-mod/plugins/utils' +import { PluginIcon } from './PluginIcon' +import type { DiscordModules } from '@revenge-mod/discord/types' import type { AnyPlugin } from '@revenge-mod/plugins/_' +import type { + PluginDependency, + PluginManifest, +} from '@revenge-mod/plugins/types' const { ActionSheet, Stack, TableRow, TableRowGroup } = Design export interface PluginRelationsListActionSheetProps { title: string + unsatisfiedTitle?: string + dependencyMap?: NonNullable plugins: AnyPlugin[] + unsatisfiedPlugins?: Array +} + +interface PluginRowProps { + plugin: AnyPlugin + variant?: DiscordModules.Components.TableRowProps['variant'] + dependency?: PluginDependency + showInstalledVersion?: boolean +} + +const formatDependencyRequirement = (dependency: PluginDependency) => + `(${dependency.version ?? '*'} ${dependency.optional ? 'optional' : 'required'})` + +function PluginRow({ + plugin, + dependency, + variant, + showInstalledVersion = false, +}: PluginRowProps) { + const { id, name, version, icon } = plugin.manifest + const installedVersion = showInstalledVersion + ? `@${formatVersion(version)}` + : '' + const depRequirement = dependency + ? ` ${formatDependencyRequirement(dependency)}` + : '' + + const subLabel = `${id}${installedVersion}${depRequirement}` + + return ( + + } + label={name} + subLabel={subLabel} + variant={variant} + onPress={() => Clipboard.setString(subLabel)} + /> + ) } export default function PluginRelationsListActionSheet({ title, + unsatisfiedTitle, + dependencyMap, plugins, + unsatisfiedPlugins, }: PluginRelationsListActionSheetProps) { return ( {plugins.map(plugin => ( - } - label={plugin.manifest.name} - subLabel={plugin.manifest.id} + plugin={plugin} + // biome-ignore lint/suspicious/noNonNullAssertedOptionalChain: Can't be undefined + dependency={dependencyMap?.[plugin.manifest.id]!} + showInstalledVersion /> ))} + + {Boolean(unsatisfiedPlugins?.length) && ( + + {unsatisfiedPlugins?.map(plugin => { + if (typeof plugin === 'string') { + // biome-ignore lint/suspicious/noNonNullAssertedOptionalChain: Can't be undefined + const dep = dependencyMap?.[plugin]! + + return ( + + } + label={plugin} + subLabel={`${plugin} (not installed) ${formatDependencyRequirement(dep)}`} + variant="danger" + onPress={() => + Clipboard.setString(plugin) + } + /> + ) + } + + return ( + + ) + })} + + )} )