feat(plugins/settings): refactor to match discord's codebase style

This commit is contained in:
PalmDevs 2025-05-04 16:45:38 +07:00
parent 755021f56e
commit 04c7275b4c
No known key found for this signature in database
GPG key ID: 6FAE8F8677ACA8BA
25 changed files with 370 additions and 113 deletions

View file

@ -0,0 +1,32 @@
export const MobileSetting = {
// MAIN SETTINGS
REVENGE: 'REVENGE',
REVENGE_PLUGINS: 'REVENGE_PLUGINS',
REVENGE_THEMES: 'REVENGE_THEMES',
REVENGE_FONTS: 'REVENGE_FONTS',
REVENGE_DEVELOPER: 'REVENGE_DEVELOPER',
// SUBSETTINGS
REVENGE_VERSION: 'REVENGE_VERSION',
REACT_VERSION: 'REACT_VERSION',
REACT_NATIVE_VERSION: 'REACT_NATIVE_VERSION',
HERMES_VERSION: 'HERMES_VERSION',
CALL_GARBAGE_COLLECTOR: 'CALL_GARBAGE_COLLECTOR',
TRIGGER_ERROR_BOUNDARY: 'TRIGGER_ERROR_BOUNDARY',
REVENGE_NOT_IMPLEMENTED: 'REVENGE_NOT_IMPLEMENTED',
} as const
export const UserSettingsSections = {
[MobileSetting.REVENGE]: 'Revenge',
[MobileSetting.REVENGE_PLUGINS]: 'Revenge Plugins',
[MobileSetting.REVENGE_THEMES]: 'Revenge Themes',
[MobileSetting.REVENGE_FONTS]: 'Revenge Fonts',
[MobileSetting.REVENGE_DEVELOPER]: 'Revenge Developer',
[MobileSetting.TRIGGER_ERROR_BOUNDARY]: 'Trigger Error Boundary',
} as const
export const AlwaysHidden = () => false

View file

@ -0,0 +1,11 @@
import { getAssetByName } from '@revenge-mod/assets'
import type { DiscordModules } from '@revenge-mod/discord/types'
import { Components } from '@revenge-mod/discord/ui'
export default function AssetIcon(props: AssetIconProps) {
return <Components.TableRow.Icon source={getAssetByName(props.name)!.id} {...props} />
}
export interface AssetIconProps extends Omit<DiscordModules.Components.TableRowIconProps, 'source'> {
name: string
}

View file

@ -0,0 +1,16 @@
import { React } from '@revenge-mod/react'
import AssetIcon from '../components/icons/AssetIcon'
import type { SettingsRowConfig } from '@revenge-mod/discord/ui/settings'
const CallGarbageCollectorSetting: SettingsRowConfig = {
parent: null,
IconComponent: () => <AssetIcon name="FireIcon" />,
title: () => 'Call Garbage Collector',
useDescription: () => 'Call the garbage collector manually.',
onPress: gc,
type: 'pressable',
}
export default CallGarbageCollectorSetting

View file

@ -1,21 +0,0 @@
import { getAssetByName } from '@revenge-mod/assets'
import { Components } from '@revenge-mod/discord/ui'
import { addSettingsRow } from '@revenge-mod/discord/ui/settings'
import { React } from '@revenge-mod/react'
import { REVENGE } from './Revenge'
export const HERMES_VERSION = 'HERMES_VERSION'
// @ts-expect-error
const props = HermesInternal.getRuntimeProperties()
addSettingsRow(HERMES_VERSION, {
parent: REVENGE,
IconComponent: () => <Components.TableRow.Icon source={getAssetByName('ClipboardListIcon')!.id} />,
title: () => 'Hermes',
useDescription: () => `${props['Bytecode Version']} (${props.Build})`,
type: 'pressable',
})

View file

@ -0,0 +1,19 @@
import { React } from '@revenge-mod/react'
import AssetIcon from '../components/icons/AssetIcon'
import { MobileSetting } from '../SettingsConstants'
import type { SettingsRowConfig } from '@revenge-mod/discord/ui/settings'
// @ts-expect-error
const props = HermesInternal.getRuntimeProperties()
const HermesVersionSetting: SettingsRowConfig = {
parent: MobileSetting.REVENGE,
IconComponent: () => <AssetIcon name="TranscriptOutlineIcon" />,
title: () => 'Hermes',
useDescription: () => `${props['Bytecode Version']} (${props.Build})`,
type: 'pressable',
}
export default HermesVersionSetting

View file

@ -1,21 +0,0 @@
import { getAssetByName } from '@revenge-mod/assets'
import { Components } from '@revenge-mod/discord/ui'
import { addSettingsRow } from '@revenge-mod/discord/ui/settings'
import { React, ReactNative } from '@revenge-mod/react'
import { REVENGE } from './Revenge'
export const REACT_NATIVE_VERSION = 'REACT_NATIVE_VERSION'
const { major, minor, patch, prerelease } = ReactNative.Platform.constants.reactNativeVersion
const version = `${major}.${minor}.${patch}${prerelease ? `-${prerelease}` : ''}`
addSettingsRow(REACT_NATIVE_VERSION, {
parent: REVENGE,
IconComponent: () => <Components.TableRow.Icon source={getAssetByName('ScienceIcon')!.id} />,
title: () => 'React Native',
useDescription: () => version,
type: 'pressable',
})

View file

@ -0,0 +1,19 @@
import { React, ReactNative } from '@revenge-mod/react'
import AssetIcon from '../components/icons/AssetIcon'
import { MobileSetting } from '../SettingsConstants'
import type { SettingsRowConfig } from '@revenge-mod/discord/ui/settings'
const { major, minor, patch, prerelease } = ReactNative.Platform.constants.reactNativeVersion
const version = `${major}.${minor}.${patch}${prerelease ? `-${prerelease}` : ''}`
const ReactNativeVersionSetting: SettingsRowConfig = {
parent: MobileSetting.REVENGE,
IconComponent: () => <AssetIcon name="ScienceIcon" />,
title: () => 'React Native',
useDescription: () => version,
type: 'pressable',
}
export default ReactNativeVersionSetting

View file

@ -1,18 +1,17 @@
import { getAssetByName } from '@revenge-mod/assets'
import { Components } from '@revenge-mod/discord/ui'
import { addSettingsRow } from '@revenge-mod/discord/ui/settings'
import { React } from '@revenge-mod/react'
import { REVENGE } from './Revenge'
import { MobileSetting } from '../SettingsConstants'
export const REACT_VERSION = 'REACT_VERSION'
import type { SettingsRowConfig } from '@revenge-mod/discord/ui/settings'
addSettingsRow(REACT_VERSION, {
parent: REVENGE,
const ReactVersionSetting: SettingsRowConfig = {
parent: MobileSetting.REVENGE,
IconComponent: () => <Components.TableRow.Icon source={getAssetByName('ScienceIcon')!.id} />,
title: () => 'React',
useDescription: () => React.version,
type: 'pressable',
})
}
export default ReactVersionSetting

View file

@ -1,24 +0,0 @@
import { React } from '@revenge-mod/react'
import { addSettingsSection } from '@revenge-mod/discord/ui/settings'
import RevengeIcon from '../components/icons/RevengeIcon'
export const REVENGE = 'REVENGE'
const ROUTE = 'Revenge'
addSettingsSection(REVENGE, {
label: 'Revenge',
settings: {
[REVENGE]: {
parent: null,
type: 'route',
IconComponent: RevengeIcon,
title: () => ROUTE,
useTrailing: () => `${__BUILD_VERSION__} (${__BUILD_COMMIT__}-${__BUILD_BRANCH__})`,
screen: {
route: ROUTE,
getComponent: () => React.lazy(() => import('../screens/RevengeSettingScreen')),
},
},
},
})

View file

@ -0,0 +1,17 @@
import { MobileSetting, UserSettingsSections } from '../SettingsConstants'
import AssetIcon from '../components/icons/AssetIcon'
import type { SettingsRowConfig } from '@revenge-mod/discord/ui/settings'
const RevengeDeveloperSetting: SettingsRowConfig = {
parent: null,
type: 'route',
IconComponent: () => <AssetIcon name="WrenchIcon" />,
title: () => 'Developer',
screen: {
route: UserSettingsSections[MobileSetting.REVENGE_DEVELOPER],
getComponent: () => require('../screens/RevengeDeveloperSettingScreen').default,
},
}
export default RevengeDeveloperSetting

View file

@ -0,0 +1,18 @@
import { AlwaysHidden, MobileSetting, UserSettingsSections } from '../SettingsConstants'
import AssetIcon from '../components/icons/AssetIcon'
import type { SettingsRowConfig } from '@revenge-mod/discord/ui/settings'
const RevengeFontsSetting: SettingsRowConfig = {
parent: null,
type: 'route',
IconComponent: () => <AssetIcon name="TextIcon" />,
title: () => 'Fonts',
usePredicate: AlwaysHidden,
screen: {
route: UserSettingsSections[MobileSetting.REVENGE_PLUGINS],
getComponent: () => require('../screens/RevengePluginsSettingScreen').default,
},
}
export default RevengeFontsSetting

View file

@ -0,0 +1,18 @@
import { React } from '@revenge-mod/react'
import AssetIcon from '../components/icons/AssetIcon'
import type { SettingsRowConfig } from '@revenge-mod/discord/ui/settings'
const RevengeNotImplementedSetting: SettingsRowConfig = {
parent: null,
IconComponent: () => <AssetIcon name="CircleXIcon-primary" variant="danger" />,
title: () => 'Not Implemented',
useDescription: () => 'This feature is not implemented yet. Check back later!',
variant: 'danger',
type: 'pressable',
unsearchable: true,
useIsDisabled: () => true,
}
export default RevengeNotImplementedSetting

View file

@ -0,0 +1,21 @@
import { getEnabledPluginsCount } from '@revenge-mod/plugins/_'
import { AlwaysHidden, MobileSetting, UserSettingsSections } from '../SettingsConstants'
import AssetIcon from '../components/icons/AssetIcon'
import type { SettingsRowConfig } from '@revenge-mod/discord/ui/settings'
const RevengePluginsSetting: SettingsRowConfig = {
parent: null,
type: 'route',
IconComponent: () => <AssetIcon name="PuzzlePieceIcon" />,
title: () => 'Plugins',
useTrailing: () => `${getEnabledPluginsCount()} enabled`,
usePredicate: AlwaysHidden,
screen: {
route: UserSettingsSections[MobileSetting.REVENGE_PLUGINS],
getComponent: () => require('../screens/RevengePluginsSettingScreen').default,
},
}
export default RevengePluginsSetting

View file

@ -0,0 +1,18 @@
import RevengeIcon from '../components/icons/RevengeIcon'
import { MobileSetting, UserSettingsSections } from '../SettingsConstants'
import type { SettingsRowConfig } from '@revenge-mod/discord/ui/settings'
const RevengeSetting: SettingsRowConfig = {
parent: null,
type: 'route',
IconComponent: RevengeIcon,
title: () => 'Revenge',
useTrailing: () => `${__BUILD_VERSION__}-${__BUILD_COMMIT__}-${__BUILD_BRANCH__}`,
screen: {
route: UserSettingsSections[MobileSetting.REVENGE],
getComponent: () => require('../screens/RevengeSettingScreen').default,
},
}
export default RevengeSetting

View file

@ -0,0 +1,18 @@
import { AlwaysHidden, MobileSetting, UserSettingsSections } from '../SettingsConstants'
import AssetIcon from '../components/icons/AssetIcon'
import type { SettingsRowConfig } from '@revenge-mod/discord/ui/settings'
const RevengeThemesSetting: SettingsRowConfig = {
parent: null,
type: 'route',
IconComponent: () => <AssetIcon name="PaintPaletteIcon" />,
title: () => 'Themes',
usePredicate: AlwaysHidden,
screen: {
route: UserSettingsSections[MobileSetting.REVENGE_THEMES],
getComponent: () => require('../screens/RevengeThemesSettingScreen').default,
},
}
export default RevengeThemesSetting

View file

@ -1,15 +0,0 @@
import { addSettingsRow } from '@revenge-mod/discord/ui/settings'
import { REVENGE } from './Revenge'
import RevengeIcon from '../components/icons/RevengeIcon'
export const REVENGE_VERSION = 'REVENGE_VERSION'
addSettingsRow(REVENGE_VERSION, {
parent: REVENGE,
IconComponent: RevengeIcon,
title: () => 'Revenge',
useDescription: () => `${__BUILD_VERSION__} (${__BUILD_COMMIT__}-${__BUILD_BRANCH__} ${__BUILD_ENV__})`,
type: 'pressable',
})

View file

@ -0,0 +1,14 @@
import RevengeIcon from '../components/icons/RevengeIcon'
import { MobileSetting } from '../SettingsConstants'
import type { SettingsRowConfig } from '@revenge-mod/discord/ui/settings'
const RevengeVersionSetting: SettingsRowConfig = {
parent: MobileSetting.REVENGE,
IconComponent: RevengeIcon,
title: () => 'Revenge',
useDescription: () => `${__BUILD_VERSION__}-${__BUILD_COMMIT__}-${__BUILD_BRANCH__} (${__BUILD_ENV__})`,
type: 'pressable',
}
export default RevengeVersionSetting

View file

@ -0,0 +1,21 @@
import { React } from '@revenge-mod/react'
import AssetIcon from '../components/icons/AssetIcon'
import { MobileSetting, UserSettingsSections } from '../SettingsConstants'
import type { SettingsRowConfig } from '@revenge-mod/discord/ui/settings'
const TriggerErrorBoundarySetting: SettingsRowConfig = {
parent: MobileSetting.REVENGE_DEVELOPER,
IconComponent: () => <AssetIcon name="ScreenXIcon" variant="danger" />,
title: () => 'Trigger Error Boundary',
variant: 'danger',
useDescription: () => 'This will crash the app.',
type: 'route',
screen: {
route: UserSettingsSections[MobileSetting.TRIGGER_ERROR_BOUNDARY],
getComponent: () => require('../screens/TriggerErrorBoundarySettingScreen').default,
},
}
export default TriggerErrorBoundarySetting

View file

@ -4,11 +4,26 @@ import { InternalPluginFlags, registerPlugin } from '@revenge-mod/plugins/_'
import { waitForModules } from '@revenge-mod/modules/finders'
import { byName, byProps } from '@revenge-mod/modules/finders/filters'
import { registerSetting, registerSettingsSection, type SettingsRowConfig } from '@revenge-mod/discord/ui/settings'
import { _suiData } from '@revenge-mod/discord/_/ui/settings'
import { after } from '@revenge-mod/patcher'
import type { SettingsRowConfig } from '@revenge-mod/discord/ui/settings'
import { MobileSetting } from './SettingsConstants'
import RevengeSetting from './definitions/RevengeSetting'
import RevengePluginsSetting from './definitions/RevengePluginsSetting'
import RevengeThemesSetting from './definitions/RevengeThemesSetting'
import RevengeFontsSetting from './definitions/RevengeFontsSetting'
import RevengeDeveloperSetting from './definitions/RevengeDeveloperSetting'
import ReactVersionSetting from './definitions/ReactVersionSetting'
import ReactNativeVersionSetting from './definitions/ReactNativeVersionSetting'
import RevengeVersionSetting from './definitions/RevengeVersionSetting'
import HermesVersionSetting from './definitions/HermesVersionSetting'
import CallGarbageCollectorSetting from './definitions/CallGarbageCollectorSetting'
import TriggerErrorBoundarySetting from './definitions/TriggerErrorBoundarySetting'
import RevengeNotImplementedSetting from './definitions/RevengeNotImplementedSetting'
import type { FC } from 'react'
registerPlugin(
@ -21,7 +36,32 @@ registerPlugin(
},
{
start() {
require('./definitions/Revenge')
for (const [key, setting] of [
[MobileSetting.REVENGE, RevengeSetting],
[MobileSetting.REVENGE_PLUGINS, RevengePluginsSetting],
[MobileSetting.REVENGE_THEMES, RevengeThemesSetting],
[MobileSetting.REVENGE_FONTS, RevengeFontsSetting],
[MobileSetting.REVENGE_DEVELOPER, RevengeDeveloperSetting],
[MobileSetting.REVENGE_VERSION, RevengeVersionSetting],
[MobileSetting.REACT_VERSION, ReactVersionSetting],
[MobileSetting.REACT_NATIVE_VERSION, ReactNativeVersionSetting],
[MobileSetting.HERMES_VERSION, HermesVersionSetting],
[MobileSetting.CALL_GARBAGE_COLLECTOR, CallGarbageCollectorSetting],
[MobileSetting.TRIGGER_ERROR_BOUNDARY, TriggerErrorBoundarySetting],
[MobileSetting.REVENGE_NOT_IMPLEMENTED, RevengeNotImplementedSetting],
] as Array<[string, SettingsRowConfig]>)
registerSetting(key, setting)
registerSettingsSection('REVENGE', {
label: 'Revenge',
settings: [
MobileSetting.REVENGE,
MobileSetting.REVENGE_PLUGINS,
MobileSetting.REVENGE_THEMES,
MobileSetting.REVENGE_FONTS,
MobileSetting.REVENGE_DEVELOPER,
],
})
const unsubForRendererConfig = waitForModules(byProps('SETTING_RENDERER_CONFIG'), (_, exports) => {
unsubForRendererConfig()
@ -36,7 +76,7 @@ registerPlugin(
get: () =>
({
...ORIGINAL_RENDERER_CONFIG,
...getCustomConfig(),
..._suiData.config,
}) as Record<string, SettingsRowConfig>,
set: v => (ORIGINAL_RENDERER_CONFIG = v),
})
@ -67,15 +107,9 @@ registerPlugin(
)
return tree
for (const section of Object.values(_suiData.sections)) {
const data = {
label: section.label,
settings: Object.keys(section.settings),
}
if (!section.index) sections.unshift(data)
else sections.splice(section.index, 0, data)
}
for (const section of Object.values(_suiData.sections))
if (!section.index) sections.unshift(section)
else sections.splice(section.index, 0, section)
return tree
})
@ -89,9 +123,3 @@ registerPlugin(
PluginFlags.Enabled,
InternalPluginFlags.Internal | InternalPluginFlags.Essential,
)
function getCustomConfig(): Record<string, SettingsRowConfig> {
return [...Object.values(_suiData.sections), { label: '', settings: _suiData.settings }]
.map(section => section.settings)
.reduce((acc, settings) => Object.assign(acc, settings), {})
}

View file

@ -0,0 +1,19 @@
import { SettingListRenderer } from '@revenge-mod/discord/ui/settings'
import { MobileSetting } from '../SettingsConstants'
export default function RevengeDeveloperSettingScreen() {
return (
<SettingListRenderer.SettingsList
sections={[
{
settings: [MobileSetting.CALL_GARBAGE_COLLECTOR],
},
{
label: 'Tests',
settings: [MobileSetting.TRIGGER_ERROR_BOUNDARY],
},
]}
/>
)
}

View file

@ -0,0 +1,15 @@
import { SettingListRenderer } from '@revenge-mod/discord/ui/settings'
import { MobileSetting } from '../SettingsConstants'
export default function RevengeFontsSettingScreen() {
return (
<SettingListRenderer.SettingsList
sections={[
{
settings: [MobileSetting.REVENGE_NOT_IMPLEMENTED],
},
]}
/>
)
}

View file

@ -0,0 +1,15 @@
import { SettingListRenderer } from '@revenge-mod/discord/ui/settings'
import { MobileSetting } from '../SettingsConstants'
export default function RevengeSettingScreen() {
return (
<SettingListRenderer.SettingsList
sections={[
{
settings: [MobileSetting.REVENGE_NOT_IMPLEMENTED],
},
]}
/>
)
}

View file

@ -1,16 +1,17 @@
import { SettingListRenderer } from '@revenge-mod/discord/ui/settings'
import { REVENGE_VERSION } from '../definitions/RevengeVersion'
import { REACT_VERSION } from '../definitions/ReactVersion'
import { REACT_NATIVE_VERSION } from '../definitions/ReactNativeVersion'
import { HERMES_VERSION } from '../definitions/HermesVersion'
import { MobileSetting } from '../SettingsConstants'
export default function RevengeSettingScreen() {
return (
<SettingListRenderer.SettingsList
sections={[
{
settings: [REVENGE_VERSION, REACT_VERSION, REACT_NATIVE_VERSION, HERMES_VERSION],
settings: [
MobileSetting.REVENGE_VERSION,
MobileSetting.REACT_VERSION,
MobileSetting.REACT_NATIVE_VERSION,
MobileSetting.HERMES_VERSION,
],
subLabel: [
'You are using the next version of Revenge! This version is experimental and may be unstable.',
],

View file

@ -0,0 +1,15 @@
import { SettingListRenderer } from '@revenge-mod/discord/ui/settings'
import { MobileSetting } from '../SettingsConstants'
export default function RevengeThemesSettingScreen() {
return (
<SettingListRenderer.SettingsList
sections={[
{
settings: [MobileSetting.REVENGE_NOT_IMPLEMENTED],
},
]}
/>
)
}

View file

@ -0,0 +1,4 @@
export default function TriggerErrorBoundarySettingScreen() {
// @ts-expect-error: This is intentional
return <undefined />
}