fix(plugins/settings): show Reload Required dialog before settings modules load

This commit is contained in:
PalmDevs 2025-09-17 17:12:25 +07:00
parent d54623aa20
commit 6df0716ec5
No known key found for this signature in database
GPG key ID: 6FAE8F8677ACA8BA
4 changed files with 139 additions and 135 deletions

View file

@ -1,16 +1,10 @@
import TableRowAssetIcon from '@revenge-mod/components/TableRowAssetIcon'
import { AlertActionCreators } from '@revenge-mod/discord/actions'
import { Design } from '@revenge-mod/discord/design'
import { BundleUpdaterManager } from '@revenge-mod/discord/native'
import { isPluginEnabled, pEmitter, pList } from '@revenge-mod/plugins/_'
import { PluginFlags } from '@revenge-mod/plugins/constants'
import { debounce } from '@revenge-mod/utils/callback'
import { useReRender } from '@revenge-mod/utils/react'
import { useEffect } from 'react'
import { RouteNames, Setting } from '../constants'
import defer * as RevengePluginsSettingScreen from '../screens/RevengePluginsSettingScreen'
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
import type { AnyPlugin } from '@revenge-mod/plugins/_'
const RevengePluginsSetting: SettingsItem = {
parent: null,
@ -52,60 +46,4 @@ function useEnabledPluginCount() {
return enabledCount
}
const { AlertActionButton, AlertModal, Text } = Design
pEmitter.on(
'flagUpdate',
debounce(function showReloadRequiredAlertIfNeeded(plugin: AnyPlugin) {
if (plugin.flags & PluginFlags.ReloadRequired) {
const plugins = [...pList.values()].filter(
plugin => plugin.flags & PluginFlags.ReloadRequired,
)
AlertActionCreators.openAlert(
'plugin-reload-required',
<PluginReloadRequiredAlert plugins={plugins} />,
)
}
}, 500),
)
function PluginReloadRequiredAlert({ plugins }: { plugins: AnyPlugin[] }) {
return (
<AlertModal
title="Reload required"
content={
<Text variant="text-md/medium" color="header-secondary">
The following plugins require a reload to apply changes:
{'\n'}
{plugins.map((plugin, index) => (
<>
{index ? ' ,' : null}
<Text
key={plugin.manifest.id}
variant="text-md/bold"
color="text-normal"
>
{plugin.manifest.name}
</Text>
</>
))}
</Text>
}
actions={
<>
<AlertActionButton
variant="destructive"
text="Reload"
onPress={() => {
BundleUpdaterManager.reload()
}}
/>
<AlertActionButton variant="secondary" text="Not now" />
</>
}
/>
)
}
export default RevengePluginsSetting

View file

@ -8,13 +8,11 @@ import { withName } from '@revenge-mod/modules/finders/filters'
import { instead } from '@revenge-mod/patcher'
import { InternalPluginFlags, registerPlugin } from '@revenge-mod/plugins/_'
import { PluginFlags } from '@revenge-mod/plugins/constants'
import { useEffect } from 'react'
import type { FC } from 'react'
import './register-routes'
import { React } from '@revenge-mod/react'
import { noop } from '@revenge-mod/utils/callback'
import { useReRender } from '@revenge-mod/utils/react'
import { useEffect } from 'react'
import type { FC } from 'react'
const pluginSettings = registerPlugin(
{
@ -26,6 +24,9 @@ const pluginSettings = registerPlugin(
},
{
start() {
// @as-require
import('./plugins')
onSettingsModulesLoaded(() => {
// @as-require
import('./register')

View file

@ -0,0 +1,134 @@
import TableRowAssetIcon from '@revenge-mod/components/TableRowAssetIcon'
import { AlertActionCreators } from '@revenge-mod/discord/actions'
import { Design } from '@revenge-mod/discord/design'
import {
refreshSettingsNavigator,
registerSettingsItem,
} from '@revenge-mod/discord/modules/settings'
import { BundleUpdaterManager } from '@revenge-mod/discord/native'
import { pEmitter, pList } from '@revenge-mod/plugins/_'
import { PluginFlags } from '@revenge-mod/plugins/constants'
import { debounce } from '@revenge-mod/utils/callback'
import { useLayoutEffect } from 'react'
import defer * as NavigatorHeaderWithIcon from './components/NavigatorHeaderWithIcon'
import { Setting } from './constants'
import type { StackScreenProps } from '@react-navigation/stack'
import type { ReactNavigationParamList } from '@revenge-mod/externals/react-navigation'
import type { AnyPlugin } from '@revenge-mod/plugins/_'
import type { PluginApi } from '@revenge-mod/plugins/types'
/// SETTINGS ROUTES
pEmitter.on('started', plugin => {
if (plugin.SettingsComponent) {
const api = plugin.api as PluginApi<any>
const Component = plugin.SettingsComponent!
function PluginSettings({
navigation,
}: StackScreenProps<ReactNavigationParamList>) {
// biome-ignore lint/correctness/useExhaustiveDependencies: We only want to set options once
useLayoutEffect(() => {
if (plugin.manifest.icon)
navigation.setOptions({
headerTitle: () => (
<NavigatorHeaderWithIcon.default
title={plugin.manifest.name}
icon={plugin.manifest.icon!}
/>
),
})
else
navigation.setOptions({
title: plugin.manifest.name,
})
}, [])
return <Component api={api} />
}
api.cleanup(
registerSettingsItem(plugin.manifest.id, {
parent: Setting.Revenge,
type: 'route',
IconComponent: plugin.manifest.icon
? () => <TableRowAssetIcon name={plugin.manifest.icon!} />
: undefined,
title: () => 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
const { AlertActionButton, AlertModal, Text } = Design
function showReloadRequiredAlertIfNeeded() {
const plugins = [...pList.values()].filter(
plugin => plugin.flags & PluginFlags.ReloadRequired,
)
if (!plugins.length) return
AlertActionCreators.openAlert(
'plugin-reload-required',
<PluginReloadRequiredAlert plugins={plugins} />,
)
}
pEmitter.on(
'flagUpdate',
debounce(plugin => {
if (plugin.flags & PluginFlags.ReloadRequired)
showReloadRequiredAlertIfNeeded()
}, 500),
)
showReloadRequiredAlertIfNeeded()
function PluginReloadRequiredAlert({ plugins }: { plugins: AnyPlugin[] }) {
return (
<AlertModal
title="Reload Required"
content={
<Text variant="text-md/medium" color="header-secondary">
The following plugins require a reload to apply changes:
{'\n'}
{plugins.map((plugin, index) => (
<>
{index ? ', ' : null}
<Text
key={plugin.manifest.id}
variant="text-md/bold"
color="text-normal"
>
{plugin.manifest.name}
</Text>
</>
))}
</Text>
}
actions={
<>
<AlertActionButton
variant="destructive"
text="Reload"
onPress={() => {
BundleUpdaterManager.reload()
}}
/>
<AlertActionButton variant="secondary" text="Not now" />
</>
}
/>
)
}

View file

@ -1,69 +0,0 @@
import TableRowAssetIcon from '@revenge-mod/components/TableRowAssetIcon'
import {
refreshSettingsNavigator,
registerSettingsItem,
} from '@revenge-mod/discord/modules/settings'
import { pEmitter } from '@revenge-mod/plugins/_'
import { proxify } from '@revenge-mod/utils/proxy'
import { useLayoutEffect } from 'react'
import defer * as NavigatorHeaderWithIcon_ from './components/NavigatorHeaderWithIcon'
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'
type NavigatorHeaderWithIcon = (typeof NavigatorHeaderWithIcon_)['default']
let NavigatorHeaderWithIcon: NavigatorHeaderWithIcon = proxify(
() => (NavigatorHeaderWithIcon = NavigatorHeaderWithIcon_.default),
)
pEmitter.on('started', plugin => {
if (plugin.SettingsComponent) {
refreshSettingsNavigator()
const api = plugin.api as PluginApi<any>
function PluginSettings({
navigation,
}: StackScreenProps<ReactNavigationParamList>) {
const SC = plugin.SettingsComponent!
// biome-ignore lint/correctness/useExhaustiveDependencies: We only want to set options once
useLayoutEffect(() => {
if (plugin.manifest.icon)
navigation.setOptions({
headerTitle: () => (
<NavigatorHeaderWithIcon
title={plugin.manifest.name}
icon={plugin.manifest.icon!}
/>
),
})
else
navigation.setOptions({
title: plugin.manifest.name,
})
}, [])
return <SC api={api} />
}
api.cleanup(
registerSettingsItem(plugin.manifest.id, {
parent: Setting.Revenge,
type: 'route',
IconComponent: plugin.manifest.icon
? () => <TableRowAssetIcon name={plugin.manifest.icon!} />
: undefined,
title: () => plugin.manifest.name,
screen: {
route: plugin.manifest.id,
getComponent: () => PluginSettings,
},
}),
// TODO(PalmDevs): In the future, we may allow pinning plugin settings, so we'll need this cleanup
// () => refreshSettingsOverviewScreen(),
)
}
})