feat(plugins/settings): register plugins as own routes instead
This commit is contained in:
parent
86e02202b6
commit
72f4232a95
30 changed files with 132 additions and 132 deletions
|
|
@ -7,7 +7,10 @@ import type { InternalPlugin } from '@revenge-mod/plugins/_'
|
|||
export interface FilterAndSortActionSheetProps {
|
||||
filters: Record<
|
||||
string,
|
||||
[icon: AssetId, (p: InternalPlugin, iflags: number) => boolean]
|
||||
{
|
||||
icon: AssetId
|
||||
filter: (p: InternalPlugin, iflags: number) => boolean
|
||||
}
|
||||
>
|
||||
filter: string[]
|
||||
setFilter: (filters: string[]) => void
|
||||
|
|
@ -54,7 +57,7 @@ export default function FilterAndSortActionSheet({
|
|||
<ActionSheet>
|
||||
<BottomSheetTitleHeader title="Filter & Sort" />
|
||||
<TableRowGroup title="Filter by">
|
||||
{Object.entries(filters).map(([label, [icon]]) => {
|
||||
{Object.entries(filters).map(([label, { icon }]) => {
|
||||
const checked = filter_.includes(label)
|
||||
|
||||
return (
|
||||
|
|
@ -5,7 +5,6 @@ export const Setting = {
|
|||
RevengePlugins: 'RevengePlugins',
|
||||
RevengeThemes: 'RevengeThemes',
|
||||
RevengeFonts: 'RevengeFonts',
|
||||
RevengeCustomPage: 'RevengeCustomPage',
|
||||
|
||||
// SUBSETTINGS
|
||||
|
||||
|
|
@ -25,5 +24,4 @@ export const RouteNames = {
|
|||
[Setting.RevengePlugins]: 'Revenge Plugins',
|
||||
[Setting.RevengeThemes]: 'Revenge Themes',
|
||||
[Setting.RevengeFonts]: 'Revenge Fonts',
|
||||
[Setting.RevengeCustomPage]: 'Revenge Custom Page',
|
||||
} as const
|
||||
|
|
@ -13,6 +13,8 @@ import { PluginFlags } from '@revenge-mod/plugins/constants'
|
|||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
import type { FC } from 'react'
|
||||
|
||||
import './routes'
|
||||
|
||||
onSettingsModulesLoaded(() => {
|
||||
require('./register')
|
||||
})
|
||||
|
|
@ -9,7 +9,6 @@ import ReactNativeVersionSetting from './definitions/ReactNativeVersionSetting'
|
|||
// import RevengeFontsSetting from './definitions/RevengeFontsSetting'
|
||||
import ReactVersionSetting from './definitions/ReactVersionSetting'
|
||||
import ReloadSetting from './definitions/ReloadSetting'
|
||||
import RevengeCustomPageSetting from './definitions/RevengeCustomPageSetting'
|
||||
import RevengeDiscordSetting from './definitions/RevengeDiscordSetting'
|
||||
import RevengeLicenseSetting from './definitions/RevengeLicenseSetting'
|
||||
import RevengePluginsSetting from './definitions/RevengePluginsSetting'
|
||||
|
|
@ -22,7 +21,6 @@ registerSettingsItems({
|
|||
[Setting.RevengePlugins]: RevengePluginsSetting,
|
||||
// [MobileSetting.REVENGE_THEMES]: RevengeThemesSetting,
|
||||
// [MobileSetting.REVENGE_FONTS]: RevengeFontsSetting,
|
||||
[Setting.RevengeCustomPage]: RevengeCustomPageSetting,
|
||||
[Setting.RevengeSourceRepository]: RevengeSourceRepositorySetting,
|
||||
[Setting.RevengeLicense]: RevengeLicenseSetting,
|
||||
[Setting.RevengeDiscord]: RevengeDiscordSetting,
|
||||
95
src/plugins/init/settings/routes.tsx
Normal file
95
src/plugins/init/settings/routes.tsx
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import { getAssetIdByName } from '@revenge-mod/assets'
|
||||
import TableRowAssetIcon from '@revenge-mod/components/TableRowAssetIcon'
|
||||
import { Tokens } from '@revenge-mod/discord/common'
|
||||
import { Stores } from '@revenge-mod/discord/common/flux'
|
||||
import { Design } from '@revenge-mod/discord/design'
|
||||
import { registerSettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
import { pEmitter, pMetadata } from '@revenge-mod/plugins/_'
|
||||
import { useLayoutEffect } from 'react'
|
||||
import { Image } from 'react-native'
|
||||
import { Setting } from './constants'
|
||||
import type { StackScreenProps } from '@react-navigation/stack'
|
||||
import type { DiscordModules } from '@revenge-mod/discord/types'
|
||||
import type { ReactNavigationParamList } from '@revenge-mod/externals/react-navigation'
|
||||
import type { PluginApi } from '@revenge-mod/plugins/types'
|
||||
|
||||
const { NavigatorHeader } = Design
|
||||
const { default: tokens } = Tokens
|
||||
|
||||
const ThemeStore = Stores.ThemeStore as DiscordModules.Flux.Store<{
|
||||
theme: string
|
||||
}>
|
||||
|
||||
pEmitter.on('started', plugin => {
|
||||
if (plugin.SettingsComponent) {
|
||||
const api = pMetadata.get(plugin.manifest.id)!.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,
|
||||
},
|
||||
}),
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
function NavigatorHeaderWithIcon({
|
||||
title,
|
||||
icon,
|
||||
}: {
|
||||
title: string
|
||||
icon?: string
|
||||
}) {
|
||||
return (
|
||||
<NavigatorHeader
|
||||
icon={
|
||||
<Image
|
||||
style={{
|
||||
width: 24,
|
||||
height: 24,
|
||||
marginEnd: 8,
|
||||
tintColor: tokens.internal.resolveSemanticColor(
|
||||
ThemeStore.theme,
|
||||
tokens.colors.HEADER_PRIMARY,
|
||||
),
|
||||
}}
|
||||
source={getAssetIdByName(icon!)}
|
||||
/>
|
||||
}
|
||||
title={title}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
@ -49,22 +49,22 @@ export default function RevengePluginsSettingScreen() {
|
|||
const SearchDebounceTime = 100
|
||||
|
||||
const Filters: FilterAndSortActionSheetProps['filters'] = {
|
||||
Enabled: [
|
||||
getAssetIdByName('CircleCheckIcon')!,
|
||||
plugin => Boolean(plugin.flags & PluginFlags.Enabled),
|
||||
],
|
||||
Disabled: [
|
||||
getAssetIdByName('CircleXIcon')!,
|
||||
plugin => !(plugin.flags & PluginFlags.Enabled),
|
||||
],
|
||||
Internal: [
|
||||
RevengeIcon,
|
||||
plugin => Boolean(plugin.flags & InternalPluginFlags.Internal),
|
||||
],
|
||||
Essential: [
|
||||
getAssetIdByName('StarIcon')!,
|
||||
(_, iflags) => Boolean(iflags & InternalPluginFlags.Essential),
|
||||
],
|
||||
Enabled: {
|
||||
icon: getAssetIdByName('CircleCheckIcon')!,
|
||||
filter: plugin => Boolean(plugin.flags & PluginFlags.Enabled),
|
||||
},
|
||||
Disabled: {
|
||||
icon: getAssetIdByName('CircleXIcon')!,
|
||||
filter: plugin => !(plugin.flags & PluginFlags.Enabled),
|
||||
},
|
||||
Internal: {
|
||||
icon: RevengeIcon,
|
||||
filter: plugin => Boolean(plugin.flags & InternalPluginFlags.Internal),
|
||||
},
|
||||
Essential: {
|
||||
icon: getAssetIdByName('StarIcon')!,
|
||||
filter: (_, iflags) => Boolean(iflags & InternalPluginFlags.Essential),
|
||||
},
|
||||
} satisfies FilterAndSortActionSheetProps['filters']
|
||||
const DefaultFilters: FilterAndSortActionSheetProps['filter'] = []
|
||||
|
||||
|
|
@ -121,9 +121,9 @@ function Screen() {
|
|||
.filter(([plugin, iflags]) => {
|
||||
if (filter.length === 0) return true
|
||||
if (matchAll)
|
||||
return filter.every(f => Filters[f][1](plugin, iflags))
|
||||
return filter.every(f => Filters[f].filter(plugin, iflags))
|
||||
|
||||
return filter.some(f => Filters[f][1](plugin, iflags))
|
||||
return filter.some(f => Filters[f].filter(plugin, iflags))
|
||||
})
|
||||
.filter(([plugin]) => {
|
||||
const { name, description, author } = plugin.manifest
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
import type { StackNavigationOptions } from '@react-navigation/stack'
|
||||
import type { ComponentType } from 'react'
|
||||
import type { RouteNames, Setting } from './constants'
|
||||
|
||||
declare module '@revenge-mod/externals/react-navigation' {
|
||||
|
|
@ -8,11 +6,6 @@ declare module '@revenge-mod/externals/react-navigation' {
|
|||
|
||||
type RevengeSettingsParamList = {
|
||||
[K in (typeof RouteNames)[keyof typeof RouteNames]]: object
|
||||
} & {
|
||||
[K in (typeof RouteNames)[(typeof Setting)['RevengeCustomPage']]]: {
|
||||
render: ComponentType
|
||||
options?: StackNavigationOptions
|
||||
}
|
||||
} & {
|
||||
[K in (typeof RouteNames)[(typeof Setting)['RevengePlugins']]]: {
|
||||
sort?: string
|
||||
11
src/plugins/init/settings/utils.tsx
Normal file
11
src/plugins/init/settings/utils.tsx
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { RootNavigationRef } from '@revenge-mod/discord/modules/main_tabs_v2'
|
||||
import type { ReactNavigationParamList } from '@revenge-mod/externals/react-navigation'
|
||||
import type { Plugin } from '@revenge-mod/plugins/types'
|
||||
|
||||
export function navigatePluginSettings(plugin: Plugin) {
|
||||
const navigation =
|
||||
RootNavigationRef.getRootNavigationRef<ReactNavigationParamList>()
|
||||
if (!navigation.isReady()) return
|
||||
|
||||
navigation.navigate(plugin.manifest.id)
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import TableRowAssetIcon from '@revenge-mod/components/TableRowAssetIcon'
|
||||
import { CopyableSetting } from '~/plugins/start/settings/definitions/shared'
|
||||
import { CopyableSetting } from '~/plugins/init/settings/definitions/shared'
|
||||
import { Setting } from '../constants'
|
||||
import { RDTContext } from '../react-devtools'
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
import { RouteNames, Setting } from '../constants'
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const RevengeCustomPageSetting: SettingsItem = {
|
||||
parent: null,
|
||||
title: () => 'Revenge Custom Page',
|
||||
type: 'route',
|
||||
unsearchable: true,
|
||||
screen: {
|
||||
route: RouteNames[Setting.RevengeCustomPage],
|
||||
getComponent: () =>
|
||||
require('../screens/RevengeCustomPageScreen').default,
|
||||
},
|
||||
}
|
||||
|
||||
export default RevengeCustomPageSetting
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
import { useLayoutEffect } from 'react'
|
||||
import type { StackScreenProps } from '@react-navigation/stack'
|
||||
import type { ReactNavigationParamList } from '@revenge-mod/externals/react-navigation'
|
||||
import type { RouteNames, Setting } from '../constants'
|
||||
|
||||
export default function RevengeCustomPageScreen({
|
||||
route,
|
||||
navigation,
|
||||
}: StackScreenProps<
|
||||
ReactNavigationParamList,
|
||||
(typeof RouteNames)[(typeof Setting)['RevengeCustomPage']]
|
||||
>) {
|
||||
const { render: PageComponent, options } = route.params
|
||||
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: We only want to set options once
|
||||
useLayoutEffect(() => {
|
||||
if (options) navigation.setOptions(options)
|
||||
}, [])
|
||||
|
||||
return <PageComponent />
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
import { getAssetIdByName } from '@revenge-mod/assets'
|
||||
import { Tokens } from '@revenge-mod/discord/common'
|
||||
import { Stores } from '@revenge-mod/discord/common/flux'
|
||||
import { Design } from '@revenge-mod/discord/design'
|
||||
import { RootNavigationRef } from '@revenge-mod/discord/modules/main_tabs_v2'
|
||||
import { pMetadata } from '@revenge-mod/plugins/_'
|
||||
import { createElement } from 'react'
|
||||
import { Image } from 'react-native'
|
||||
import { RouteNames, Setting } from './constants'
|
||||
import type { DiscordModules } from '@revenge-mod/discord/types'
|
||||
import type { ReactNavigationParamList } from '@revenge-mod/externals/react-navigation'
|
||||
import type { Plugin, PluginApi } from '@revenge-mod/plugins/types'
|
||||
|
||||
const { NavigatorHeader } = Design
|
||||
|
||||
// TODO(plugins/settings): Register a custom route instead, so plugin "settings" can actually be pinned and navigated to without hassle.
|
||||
// This would require us to implement the event-based plugin management system first, so we can listen to plugin enable/disable events and update the settings accordingly.
|
||||
export function navigatePluginSettings(plugin: Plugin) {
|
||||
const { api } = pMetadata.get(plugin.manifest.id)!
|
||||
const navigation =
|
||||
RootNavigationRef.getRootNavigationRef<ReactNavigationParamList>()
|
||||
if (!navigation.isReady()) return
|
||||
|
||||
navigation.navigate(RouteNames[Setting.RevengeCustomPage], {
|
||||
render: () =>
|
||||
createElement(plugin.SettingsComponent!, {
|
||||
api: api as PluginApi,
|
||||
}),
|
||||
options: plugin.manifest.icon
|
||||
? {
|
||||
headerTitle: () => (
|
||||
<NavigatorHeader
|
||||
icon={
|
||||
<Image
|
||||
style={{
|
||||
width: 24,
|
||||
height: 24,
|
||||
marginEnd: 8,
|
||||
tintColor:
|
||||
Tokens.default.internal.resolveSemanticColor(
|
||||
(
|
||||
Stores.ThemeStore as DiscordModules.Flux.Store<{
|
||||
theme: string
|
||||
}>
|
||||
).theme,
|
||||
Tokens.default.colors
|
||||
.HEADER_PRIMARY,
|
||||
),
|
||||
}}
|
||||
source={getAssetIdByName(
|
||||
plugin.manifest.icon!,
|
||||
)}
|
||||
/>
|
||||
}
|
||||
title={plugin.manifest.name}
|
||||
/>
|
||||
),
|
||||
}
|
||||
: {
|
||||
title: plugin.manifest.name,
|
||||
},
|
||||
})
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue