feat(lib/discord/modules/settings): new method to refresh SettingsOverviewScreen and SettingsNavigator
This commit is contained in:
parent
d2ac578cff
commit
a586c0cebd
3 changed files with 109 additions and 112 deletions
|
|
@ -1,3 +1,4 @@
|
|||
import { noop } from '@revenge-mod/utils/callback'
|
||||
import type {
|
||||
SettingsItem,
|
||||
SettingsModulesLoadedSubscription,
|
||||
|
|
@ -10,3 +11,10 @@ export const sSections: Record<string, SettingsSection> = {}
|
|||
export const sConfig: Record<string, SettingsItem> = {}
|
||||
|
||||
export const sSubscriptions = new Set<SettingsModulesLoadedSubscription>()
|
||||
|
||||
export const sRefresher = {
|
||||
navigator: false,
|
||||
overviewScreen: false,
|
||||
callNavigator: noop,
|
||||
callOverviewScreen: noop,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,6 @@
|
|||
import { ReactNavigationNative } from '@revenge-mod/externals/react-navigation'
|
||||
import { noop } from '@revenge-mod/utils/callback'
|
||||
import { findInTree } from '@revenge-mod/utils/tree'
|
||||
import { Constants } from '../../common'
|
||||
import { sLoaded } from '../../start'
|
||||
import { RootNavigationRef } from '../main_tabs_v2'
|
||||
import { sConfig, sSections, sSubscriptions } from './_internal'
|
||||
import type { NavigationState, PartialState } from '@react-navigation/core'
|
||||
import { sConfig, sRefresher, sSections, sSubscriptions } from './_internal'
|
||||
import type { DiscordModules } from '../../types'
|
||||
|
||||
export type SettingsItem = DiscordModules.Modules.Settings.SettingsItem
|
||||
|
|
@ -101,71 +96,18 @@ export function addSettingsItemToSection(key: string, item: string) {
|
|||
}
|
||||
}
|
||||
|
||||
const { CommonActions, StackActions } = ReactNavigationNative
|
||||
/**
|
||||
* Refreshes the SettingsOverviewScreen.
|
||||
*/
|
||||
export function refreshSettingsOverviewScreen() {
|
||||
sRefresher.overviewScreen = true
|
||||
sRefresher.callOverviewScreen()
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the SettingsOverviewScreen, applying any changes made to settings modules.
|
||||
*
|
||||
* @param renavigate Whether to renavigate instead of replacing the screen in the stack.
|
||||
* @returns Whether the SettingsOverviewScreen was refreshed.
|
||||
* Refreshes the settings navigator.
|
||||
*/
|
||||
export async function refreshSettingsOverviewScreen(renavigate?: boolean) {
|
||||
const navigation = RootNavigationRef.getRootNavigationRef()
|
||||
if (!navigation.isReady()) return
|
||||
|
||||
let state = navigation.getRootState()
|
||||
// State with SettingsOverviewScreen
|
||||
let settingsState = findInTree(state, isNavigationSettingsState)
|
||||
// We're currently not on the settings screen, so we don't need to reset
|
||||
if (!settingsState) return
|
||||
|
||||
if (renavigate) {
|
||||
const mainState = findInTree(state, isNavigationMainState)
|
||||
if (!mainState) return
|
||||
|
||||
navigation.dispatch({
|
||||
...CommonActions.goBack(),
|
||||
target: mainState.key,
|
||||
})
|
||||
|
||||
navigation.navigate(mainState.routes[mainState.index].name)
|
||||
|
||||
// Wait for navigation to complete and reset to the settings state
|
||||
requestAnimationFrame(() => {
|
||||
navigation.reset({
|
||||
index: settingsState!.routes.length - 1,
|
||||
routes: settingsState!.routes,
|
||||
} as PartialState<NonNullable<typeof settingsState>>)
|
||||
})
|
||||
} else {
|
||||
requestAnimationFrame(() => {
|
||||
// Get updated state
|
||||
state = navigation.getRootState()
|
||||
settingsState = findInTree(state, isNavigationSettingsState)
|
||||
if (!settingsState) return
|
||||
|
||||
const {
|
||||
key: target,
|
||||
routes: [{ name, key: source }],
|
||||
} = settingsState
|
||||
|
||||
navigation.dispatch({
|
||||
...StackActions.replace(name),
|
||||
source,
|
||||
target,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function isNavigationMainState(state: any): state is NavigationState {
|
||||
return Array.isArray(state.routes) && state.routes.length > 1
|
||||
}
|
||||
|
||||
function isNavigationSettingsState(state: any): state is NavigationState {
|
||||
return (
|
||||
Array.isArray(state.routes) &&
|
||||
state.routes[0]?.name ===
|
||||
(Constants.UserSettingsSections as Record<string, string>).OVERVIEW
|
||||
)
|
||||
export function refreshSettingsNavigator() {
|
||||
sRefresher.navigator = true
|
||||
sRefresher.callNavigator()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,22 @@
|
|||
import { sSections } from '@revenge-mod/discord/_/modules/settings'
|
||||
import { sRefresher, sSections } from '@revenge-mod/discord/_/modules/settings'
|
||||
import { onSettingsModulesLoaded } from '@revenge-mod/discord/modules/settings'
|
||||
import { byName } from '@revenge-mod/modules/finders/filters'
|
||||
import { waitForModules } from '@revenge-mod/modules/finders/wait'
|
||||
import { after } from '@revenge-mod/patcher'
|
||||
import {
|
||||
waitForModuleByImportedPath,
|
||||
waitForModules,
|
||||
} from '@revenge-mod/modules/finders/wait'
|
||||
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'
|
||||
|
||||
// Always register before so setting modules that depend on the Revenge section can be registered properly
|
||||
onSettingsModulesLoaded(() => {
|
||||
require('./register')
|
||||
})
|
||||
|
||||
registerPlugin(
|
||||
const pluginSettings = registerPlugin(
|
||||
{
|
||||
id: 'revenge.settings',
|
||||
name: 'Settings',
|
||||
|
|
@ -24,43 +26,22 @@ registerPlugin(
|
|||
},
|
||||
{
|
||||
start() {
|
||||
onSettingsModulesLoaded(() => {
|
||||
require('./register')
|
||||
})
|
||||
|
||||
waitForModuleByImportedPath(
|
||||
'modules/main_tabs_v2/native/settings/SettingsNavigator.tsx',
|
||||
exports => {
|
||||
patchSettingsNavigator(exports)
|
||||
},
|
||||
)
|
||||
|
||||
const unsubSOS = waitForModules(
|
||||
byName('SettingsOverviewScreen'),
|
||||
exports => {
|
||||
unsubSOS()
|
||||
|
||||
const customSections = sSections
|
||||
|
||||
after(exports as { default: FC }, 'default', tree => {
|
||||
const {
|
||||
props: { sections },
|
||||
} = tree as {
|
||||
props: {
|
||||
sections: Array<{
|
||||
label: string
|
||||
settings: string[]
|
||||
}>
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we even have custom sections
|
||||
const firstCustomSection =
|
||||
customSections[Object.keys(customSections)[0]]
|
||||
if (!firstCustomSection) return tree
|
||||
|
||||
// Check if sections are already spliced
|
||||
const [firstCustomItem] = firstCustomSection.settings
|
||||
if (
|
||||
sections.findIndex(section =>
|
||||
section.settings.includes(firstCustomItem),
|
||||
) < 0
|
||||
)
|
||||
for (const section of Object.values(customSections))
|
||||
if (!section.index) sections.unshift(section)
|
||||
else sections.splice(section.index, 0, section)
|
||||
|
||||
return tree
|
||||
})
|
||||
patchSettingsOverviewScreen(exports)
|
||||
},
|
||||
{
|
||||
returnNamespace: true,
|
||||
|
|
@ -71,3 +52,69 @@ registerPlugin(
|
|||
PluginFlags.Enabled,
|
||||
InternalPluginFlags.Internal | InternalPluginFlags.Essential,
|
||||
)
|
||||
|
||||
function patchSettingsNavigator(exports: any) {
|
||||
instead(exports.default, 'type', (args, orig) => {
|
||||
const reRender = useReRender()
|
||||
useEffect(() => {
|
||||
sRefresher.callNavigator = reRender
|
||||
return () => {
|
||||
sRefresher.navigator = false
|
||||
sRefresher.callNavigator = noop
|
||||
}
|
||||
}, [reRender])
|
||||
|
||||
// useMemo(() => getSettingScreens(), [])
|
||||
const unpatchMemo = instead(React, 'useMemo', (args, orig) => {
|
||||
if (!args[1]?.length && sRefresher.navigator) args[1] = undefined
|
||||
return Reflect.apply(orig, React, args)
|
||||
})
|
||||
|
||||
const fiber = Reflect.apply(orig, undefined, args)
|
||||
unpatchMemo()
|
||||
return fiber
|
||||
})
|
||||
}
|
||||
|
||||
function patchSettingsOverviewScreen(exports: any) {
|
||||
instead(
|
||||
exports as {
|
||||
default: FC
|
||||
},
|
||||
'default',
|
||||
(args, orig) => {
|
||||
const reRender = useReRender()
|
||||
useEffect(() => {
|
||||
sRefresher.callOverviewScreen = reRender
|
||||
return () => {
|
||||
sRefresher.overviewScreen = false
|
||||
sRefresher.callOverviewScreen = noop
|
||||
}
|
||||
}, [reRender])
|
||||
|
||||
// In: SearchableSettingsList
|
||||
// useMemo(() => {
|
||||
// return doSettingsBlocklistFiltering(props.sections, canShowNitroSettings)
|
||||
// }, [canShowNitroSettings])
|
||||
const unpatchMemo = instead(React, 'useMemo', (args, orig) => {
|
||||
if (sRefresher.overviewScreen) args[1] = undefined
|
||||
|
||||
const sections = Reflect.apply(orig, React, args)
|
||||
|
||||
// Add our custom sections here
|
||||
for (const section of Object.values(sSections))
|
||||
if (section.index)
|
||||
sections.splice(section.index, 0, section)
|
||||
else sections.unshift(section)
|
||||
|
||||
return sections
|
||||
})
|
||||
|
||||
const tree = Reflect.apply(orig, undefined, args)
|
||||
unpatchMemo()
|
||||
return tree
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
export default pluginSettings
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue