import TableRowAssetIcon from '@revenge-mod/components/TableRowAssetIcon' import { AlertActionCreators, ToastActionCreators, } from '@revenge-mod/discord/actions' import { refreshSettingsNavigator, registerSettingsItem, } from '@revenge-mod/discord/modules/settings' import { PluginFlags, pEmitter, pList } from '@revenge-mod/plugins/_' import { lookupGeneratedIconComponent } from '@revenge-mod/utils/discord' import { useLayoutEffect } from 'react' import defer * as NavigatorHeaderWithIcon from './components/NavigatorHeaderWithIcon' import PluginInstallFailedAlert from './components/PluginInstallFailedAlert' import PluginsFailedToStartAlert from './components/PluginsFailedToStartAlert' import PluginsRequireReloadAlert from './components/PluginsRequireReloadAlert' import { Setting } from './constants' import type { StackScreenProps } from '@react-navigation/stack' import type { ReactNavigationParamList } from '@revenge-mod/externals/react-navigation' import type { PluginApi } from '@revenge-mod/plugins/types' const PluginsRequireReloadAlertKey = 'plugins-require-reload' const PluginsFailedToStartAlertKey = 'plugins-failed-to-start' /// SETTINGS ROUTES pEmitter.on('started', plugin => { if (plugin.SettingsComponent) { const api = plugin.api as PluginApi const Component = plugin.SettingsComponent! function PluginSettings({ navigation, }: StackScreenProps) { // biome-ignore lint/correctness/useExhaustiveDependencies: We only want to set options once useLayoutEffect(() => { if (plugin.manifest.icon) navigation.setOptions({ headerTitle: () => ( ), }) else navigation.setOptions({ title: plugin.manifest.name, }) }, []) return } api.cleanup( registerSettingsItem(plugin.manifest.id, { parent: Setting.Revenge, type: 'route', IconComponent: plugin.manifest.icon ? () => : undefined, useTitle: () => plugin.manifest.name, screen: { route: plugin.manifest.id, getComponent: () => PluginSettings, }, }), refreshSettingsNavigator, // TODO(PalmDevs): In the future, we may allow pinning plugin settings, so we'll need this cleanup // () => refreshSettingsOverviewScreen(), ) refreshSettingsNavigator() } }) /// RELOAD REQUIRED ALERT function showPendingReloadAlertIfNeeded() { const plugins = [...pList.values()].filter( plugin => plugin.flags & (PluginFlags.PendingReload | PluginFlags.PendingUpdate), ) if (!plugins.length) return AlertActionCreators.dismissAlert(PluginsRequireReloadAlertKey) AlertActionCreators.openAlert( PluginsRequireReloadAlertKey, , ) } function showErrorAlertIfNeeded() { const plugins = [...pList.values()].filter( plugin => plugin.flags & PluginFlags.Errored, ) if (!plugins.length) return AlertActionCreators.dismissAlert(PluginsFailedToStartAlertKey) AlertActionCreators.openAlert( PluginsFailedToStartAlertKey, , ) } pEmitter.on('flagUpdate', plugin => { if (plugin.flags & (PluginFlags.PendingReload | PluginFlags.PendingUpdate)) showPendingReloadAlertIfNeeded() }) pEmitter.on('stopped', plugin => { if (plugin.flags & PluginFlags.Errored) showErrorAlertIfNeeded() }) /// PLUGIN INSTALL FEEDBACK const pluginInstallToastKeyFor = (id: string) => `REVENGE_PLUGIN_INSTALL:${id}` const PluginInstallFailedAlertKey = 'plugin-install-failed' pEmitter.on('install', result => { if (result.error !== false) { AlertActionCreators.dismissAlert(PluginInstallFailedAlertKey) AlertActionCreators.openAlert( PluginInstallFailedAlertKey, , ) return } showInstallToast( result.updated ? `Updated ${result.manifest.name}` : `Installed ${result.manifest.name}`, result.manifest.id, ) }) const CircleCheckIcon = lookupGeneratedIconComponent( 'CircleCheckIcon', 'CircleCheckIcon-secondary', 'CircleCheckIcon-primary', ) function showInstallToast(content: string, id: string) { ToastActionCreators.open({ key: pluginInstallToastKeyFor(id), content, IconComponent: CircleCheckIcon, }) } showErrorAlertIfNeeded() showPendingReloadAlertIfNeeded()