chore: format
This commit is contained in:
parent
c92adf856f
commit
2b905801c3
103 changed files with 1389 additions and 755 deletions
|
|
@ -3,4 +3,7 @@ import type { Asset, AssetId } from './types'
|
|||
|
||||
export const _customs = new Set<Asset>()
|
||||
export const _overrides = new WeakMap<Asset, Asset>()
|
||||
export const _metas = new WeakMap<Asset, [assetId: AssetId, moduleId: Metro.ModuleID]>()
|
||||
export const _metas = new WeakMap<
|
||||
Asset,
|
||||
[assetId: AssetId, moduleId: Metro.ModuleID]
|
||||
>()
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import { getStorage } from '@revenge-mod/storage'
|
|||
import { createLogger } from '@revenge-mod/utils/logger'
|
||||
import { mergeDeep } from '@revenge-mod/utils/objects'
|
||||
import { proxify } from '@revenge-mod/utils/proxy'
|
||||
|
||||
import type { Metro } from '@revenge-mod/modules/types'
|
||||
import type { Asset } from './types'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
import { ReactNative } from '@revenge-mod/react'
|
||||
|
||||
import { _customs, _metas, _overrides } from './_internal'
|
||||
import { cache } from './caches'
|
||||
import { AssetRegistry } from './preinit'
|
||||
|
||||
import type { Metro } from '@revenge-mod/modules/types'
|
||||
import type { Asset, AssetId, RegisterableAsset } from './types'
|
||||
|
||||
export { AssetRegistry, AssetRegistryModuleId } from './preinit'
|
||||
|
||||
// iOS cannot display SVGs
|
||||
let _preferredType: Asset['type'] = ReactNative.Platform.OS === 'ios' ? 'png' : 'svg'
|
||||
let _preferredType: Asset['type'] =
|
||||
ReactNative.Platform.OS === 'ios' ? 'png' : 'svg'
|
||||
|
||||
/**
|
||||
* Set the preferred asset type. This is used to determine which asset to use when multiple types are available.
|
||||
|
|
@ -26,7 +25,8 @@ export function setPreferredAssetType(type: Asset['type']) {
|
|||
*/
|
||||
export function* getAssets(): Generator<Asset> {
|
||||
for (const name of Object.keys(cache!))
|
||||
for (const mid of Object.values(cache[name])) yield AssetRegistry.getAssetByID(__r(mid)) as Asset
|
||||
for (const mid of Object.values(cache[name]))
|
||||
yield AssetRegistry.getAssetByID(__r(mid)) as Asset
|
||||
|
||||
for (const asset of _customs) yield asset
|
||||
}
|
||||
|
|
@ -48,7 +48,9 @@ export function getAssetByName(name: string): Asset | undefined {
|
|||
* @param name The asset name.
|
||||
* @returns A record keyed by the type of the asset, with the value being the asset itself.
|
||||
*/
|
||||
export function getAssetsByName(name: string): Record<Asset['type'], Asset> | undefined {
|
||||
export function getAssetsByName(
|
||||
name: string,
|
||||
): Record<Asset['type'], Asset> | undefined {
|
||||
const reg = cache[name]
|
||||
if (!reg) return
|
||||
|
||||
|
|
@ -89,7 +91,9 @@ export function getAssetIdByName(name: string): AssetId | undefined {
|
|||
*
|
||||
* @param asset The asset to get the module ID for.
|
||||
*/
|
||||
export function getAssetModuleId(asset: Asset): Metro.ModuleID | -1 | undefined {
|
||||
export function getAssetModuleId(
|
||||
asset: Asset,
|
||||
): Metro.ModuleID | -1 | undefined {
|
||||
return _metas.get(asset)?.[1]
|
||||
}
|
||||
|
||||
|
|
@ -101,7 +105,9 @@ export function getAssetModuleId(asset: Asset): Metro.ModuleID | -1 | undefined
|
|||
*/
|
||||
export function registerAsset(asset: RegisterableAsset): AssetId {
|
||||
if (cache[asset.name]?.[asset.type] !== undefined)
|
||||
throw new Error(`Asset with name ${asset.name} and type ${asset.type} already exists!`)
|
||||
throw new Error(
|
||||
`Asset with name ${asset.name} and type ${asset.type} already exists!`,
|
||||
)
|
||||
|
||||
_customs.add(asset)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,8 @@ import { _initing, _uninits } from '@revenge-mod/modules/_/metro'
|
|||
import { byName, byProps } from '@revenge-mod/modules/finders/filters'
|
||||
import { waitForModules } from '@revenge-mod/modules/finders/wait'
|
||||
import { getModuleDependencies } from '@revenge-mod/modules/metro/utils'
|
||||
|
||||
import { _metas, _overrides } from './_internal'
|
||||
import { cacheAsset } from './caches'
|
||||
|
||||
import type { ReactNative } from '@revenge-mod/react/types'
|
||||
import type { Asset, ReactNativeAsset } from './types'
|
||||
|
||||
|
|
@ -13,52 +11,57 @@ export let AssetRegistry: ReactNative.AssetsRegistry
|
|||
export let AssetRegistryModuleId: number
|
||||
|
||||
// Tracking/caching assets
|
||||
const unsubAR = waitForModules(byProps<ReactNative.AssetsRegistry>('registerAsset'), (exports, id) => {
|
||||
AssetRegistryModuleId = id
|
||||
AssetRegistry = exports as ReactNative.AssetsRegistry
|
||||
const unsubAR = waitForModules(
|
||||
byProps<ReactNative.AssetsRegistry>('registerAsset'),
|
||||
(exports, id) => {
|
||||
AssetRegistryModuleId = id
|
||||
AssetRegistry = exports as ReactNative.AssetsRegistry
|
||||
|
||||
// There are two matching exports. One is the original, and one is a re-export.
|
||||
// The original asset-registry is simply required by the re-exported one with no changes.
|
||||
// There are two matching exports. One is the original, and one is a re-export.
|
||||
// The original asset-registry is simply required by the re-exported one with no changes.
|
||||
|
||||
// This is the re-exported registry, because it has dependencies.
|
||||
// We can begin the caching process here, asset registrar modules have the reexported registry as a dependency.
|
||||
if (getModuleDependencies(id)!.length) {
|
||||
unsubAR()
|
||||
// This is the re-exported registry, because it has dependencies.
|
||||
// We can begin the caching process here, asset registrar modules have the reexported registry as a dependency.
|
||||
if (getModuleDependencies(id)!.length) {
|
||||
unsubAR()
|
||||
|
||||
// TODO(assets/patches): conditionally run this if cache does not exist
|
||||
// More fragile way, but also more performant:
|
||||
// There is exactly one asset before the reexported asset registry :/
|
||||
const firstAssetModuleId = id - 1
|
||||
for (const mId of _uninits) {
|
||||
if (mId < firstAssetModuleId) continue
|
||||
// TODO(assets/patches): conditionally run this if cache does not exist
|
||||
// More fragile way, but also more performant:
|
||||
// There is exactly one asset before the reexported asset registry :/
|
||||
const firstAssetModuleId = id - 1
|
||||
for (const mId of _uninits) {
|
||||
if (mId < firstAssetModuleId) continue
|
||||
|
||||
const deps = getModuleDependencies(mId)!
|
||||
if (deps.length === 1 && deps[0] === id) __r(mId)
|
||||
const deps = getModuleDependencies(mId)!
|
||||
if (deps.length === 1 && deps[0] === id) __r(mId)
|
||||
}
|
||||
|
||||
// We already patched the original asset registry, so we don't need to patch again.
|
||||
return
|
||||
}
|
||||
|
||||
// We already patched the original asset registry, so we don't need to patch again.
|
||||
return
|
||||
}
|
||||
const orig = exports.registerAsset
|
||||
exports.registerAsset = (asset: Asset) => {
|
||||
const result = orig(asset as ReactNativeAsset)
|
||||
|
||||
const orig = exports.registerAsset
|
||||
exports.registerAsset = (asset: Asset) => {
|
||||
const result = orig(asset as ReactNativeAsset)
|
||||
// Cache packager assets only
|
||||
if ((asset as ReactNativeAsset).__packager_asset) {
|
||||
cacheAsset(asset, _initing)
|
||||
// In-memory cache
|
||||
_metas.set(asset, [result, _initing])
|
||||
}
|
||||
|
||||
// Cache packager assets only
|
||||
if ((asset as ReactNativeAsset).__packager_asset) {
|
||||
cacheAsset(asset, _initing)
|
||||
// In-memory cache
|
||||
_metas.set(asset, [result, _initing])
|
||||
return result
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
// Asset overrides
|
||||
const unsubRAS = waitForModules(
|
||||
byName<{
|
||||
addCustomSourceTransformer: (transformer: (arg: { asset: Asset }) => ReactNativeAsset) => void
|
||||
addCustomSourceTransformer: (
|
||||
transformer: (arg: { asset: Asset }) => ReactNativeAsset,
|
||||
) => void
|
||||
}>('resolveAssetSource'),
|
||||
rAS => {
|
||||
unsubRAS()
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ export type Asset = ReactNativeAsset | RevengeAsset
|
|||
export type AssetId = number
|
||||
|
||||
export type ReactNativeAsset = ReactNative.AssetsRegistry.PackagerAsset
|
||||
export interface RevengeAsset extends Pick<ReactNativeAsset, 'name' | 'width' | 'height' | 'type'> {
|
||||
export interface RevengeAsset
|
||||
extends Pick<ReactNativeAsset, 'name' | 'width' | 'height' | 'type'> {
|
||||
uri: string
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,80 +6,91 @@ import {
|
|||
relativeDep,
|
||||
withoutProps,
|
||||
} from '@revenge-mod/modules/finders/filters'
|
||||
import { lookupModule, lookupModules } from '@revenge-mod/modules/finders/lookup'
|
||||
|
||||
import {
|
||||
lookupModule,
|
||||
lookupModules,
|
||||
} from '@revenge-mod/modules/finders/lookup'
|
||||
import { ReactJsxRuntimeModuleId, ReactModuleId } from '@revenge-mod/react'
|
||||
|
||||
import { proxify } from '@revenge-mod/utils/proxy'
|
||||
|
||||
import { DispatcherModuleId } from './common/flux'
|
||||
|
||||
import type { DiscordModules } from './types'
|
||||
|
||||
export let ActionSheetActionCreators: DiscordModules.Actions.ActionSheetActionCreators = proxify(
|
||||
() => {
|
||||
const [module] = lookupModule(
|
||||
export let ActionSheetActionCreators: DiscordModules.Actions.ActionSheetActionCreators =
|
||||
proxify(
|
||||
() => {
|
||||
const [module] = lookupModule(
|
||||
preferExports(
|
||||
byProps<DiscordModules.Actions.ActionSheetActionCreators>(
|
||||
'hideActionSheet',
|
||||
),
|
||||
byDependencies([
|
||||
undefined,
|
||||
ReactModuleId,
|
||||
ReactJsxRuntimeModuleId,
|
||||
DispatcherModuleId,
|
||||
relativeDep(1),
|
||||
relativeDep(2),
|
||||
2,
|
||||
]),
|
||||
),
|
||||
{
|
||||
includeUninitialized: true,
|
||||
},
|
||||
)
|
||||
|
||||
if (module) return (ActionSheetActionCreators = module)
|
||||
},
|
||||
{
|
||||
hint: 'object',
|
||||
},
|
||||
)!
|
||||
|
||||
export let AlertActionCreators: DiscordModules.Actions.AlertActionCreators =
|
||||
proxify(
|
||||
() => {
|
||||
const [module] = lookupModule(
|
||||
preferExports(
|
||||
byProps<DiscordModules.Actions.AlertActionCreators>(
|
||||
'openAlert',
|
||||
'dismissAlert',
|
||||
),
|
||||
byDependencies([
|
||||
ReactModuleId,
|
||||
ReactJsxRuntimeModuleId,
|
||||
undefined,
|
||||
DispatcherModuleId,
|
||||
relativeDep(1),
|
||||
undefined,
|
||||
2,
|
||||
]),
|
||||
),
|
||||
)
|
||||
|
||||
if (module) return (AlertActionCreators = module)
|
||||
},
|
||||
{
|
||||
hint: 'object',
|
||||
},
|
||||
)!
|
||||
|
||||
export let ToastActionCreators: DiscordModules.Actions.ToastActionCreators =
|
||||
proxify(() => {
|
||||
// Many other modules share the same dependencies, the second yielded should be the correct module.
|
||||
// [Dispatcher, ImportTracker]
|
||||
|
||||
const generator = lookupModules(
|
||||
preferExports(
|
||||
byProps<DiscordModules.Actions.ActionSheetActionCreators>('hideActionSheet'),
|
||||
byDependencies([
|
||||
undefined,
|
||||
ReactModuleId,
|
||||
ReactJsxRuntimeModuleId,
|
||||
DispatcherModuleId,
|
||||
relativeDep(1),
|
||||
relativeDep(2),
|
||||
2,
|
||||
]),
|
||||
every(
|
||||
byProps<DiscordModules.Actions.ToastActionCreators>('open'),
|
||||
withoutProps('init'),
|
||||
),
|
||||
byDependencies([DispatcherModuleId, 2]),
|
||||
),
|
||||
{
|
||||
includeUninitialized: true,
|
||||
},
|
||||
)
|
||||
|
||||
if (module) return (ActionSheetActionCreators = module)
|
||||
},
|
||||
{
|
||||
hint: 'object',
|
||||
},
|
||||
)!
|
||||
|
||||
export let AlertActionCreators: DiscordModules.Actions.AlertActionCreators = proxify(
|
||||
() => {
|
||||
const [module] = lookupModule(
|
||||
preferExports(
|
||||
byProps<DiscordModules.Actions.AlertActionCreators>('openAlert', 'dismissAlert'),
|
||||
byDependencies([
|
||||
ReactModuleId,
|
||||
ReactJsxRuntimeModuleId,
|
||||
undefined,
|
||||
DispatcherModuleId,
|
||||
relativeDep(1),
|
||||
undefined,
|
||||
2,
|
||||
]),
|
||||
),
|
||||
)
|
||||
|
||||
if (module) return (AlertActionCreators = module)
|
||||
},
|
||||
{
|
||||
hint: 'object',
|
||||
},
|
||||
)!
|
||||
|
||||
export let ToastActionCreators: DiscordModules.Actions.ToastActionCreators = proxify(() => {
|
||||
// Many other modules share the same dependencies, the second yielded should be the correct module.
|
||||
// [Dispatcher, ImportTracker]
|
||||
|
||||
const generator = lookupModules(
|
||||
preferExports(
|
||||
every(byProps<DiscordModules.Actions.ToastActionCreators>('open'), withoutProps('init')),
|
||||
byDependencies([DispatcherModuleId, 2]),
|
||||
),
|
||||
{
|
||||
includeUninitialized: true,
|
||||
},
|
||||
)
|
||||
|
||||
for (const [module] of generator) if (module.open.length === 1) return (ToastActionCreators = module)
|
||||
})!
|
||||
for (const [module] of generator)
|
||||
if (module.open.length === 1) return (ToastActionCreators = module)
|
||||
})!
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import {
|
||||
type Filter,
|
||||
byDependencies,
|
||||
byProps,
|
||||
createFilterGenerator,
|
||||
|
|
@ -9,7 +8,7 @@ import {
|
|||
import { lookupModule } from '@revenge-mod/modules/finders/lookup'
|
||||
import { waitForModules } from '@revenge-mod/modules/finders/wait'
|
||||
import { noopFalse } from '@revenge-mod/utils/callbacks'
|
||||
|
||||
import type { Filter } from '@revenge-mod/modules/finders/filters'
|
||||
import type { Metro } from '@revenge-mod/modules/types'
|
||||
import type { DiscordModules } from '../types'
|
||||
|
||||
|
|
@ -18,7 +17,14 @@ import type { DiscordModules } from '../types'
|
|||
export const [Dispatcher, DispatcherModuleId] = lookupModule(
|
||||
preferExports(
|
||||
byProps<DiscordModules.Flux.Dispatcher>('_interceptors'),
|
||||
byDependencies([relativeDep(1), undefined, undefined, undefined, undefined, 2]),
|
||||
byDependencies([
|
||||
relativeDep(1),
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
2,
|
||||
]),
|
||||
),
|
||||
{
|
||||
includeUninitialized: true,
|
||||
|
|
@ -37,7 +43,10 @@ export const Stores = new Proxy(_stores, {
|
|||
get: (_, name) => _stores[name as string],
|
||||
})
|
||||
|
||||
export function getStore<T>(name: string, callback: (store: DiscordModules.Flux.Store<T>) => void) {
|
||||
export function getStore<T>(
|
||||
name: string,
|
||||
callback: (store: DiscordModules.Flux.Store<T>) => void,
|
||||
) {
|
||||
const store = Stores[name]
|
||||
if (store) {
|
||||
callback(store)
|
||||
|
|
@ -47,9 +56,12 @@ export function getStore<T>(name: string, callback: (store: DiscordModules.Flux.
|
|||
return waitForModules(byStoreName<T>(name), callback)
|
||||
}
|
||||
|
||||
export type ByStoreName = <T>(name: string) => Filter<DiscordModules.Flux.Store<T>>
|
||||
export type ByStoreName = <T>(
|
||||
name: string,
|
||||
) => Filter<DiscordModules.Flux.Store<T>>
|
||||
|
||||
export const byStoreName = createFilterGenerator<[name: string]>(
|
||||
([name], _, exports) => exports.getName?.length === 0 && exports.getName() === name,
|
||||
([name], _, exports) =>
|
||||
exports.getName?.length === 0 && exports.getName() === name,
|
||||
([name]) => `revenge.discord.byStoreName(${name})`,
|
||||
) as ByStoreName
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
import { byName, byProps } from '@revenge-mod/modules/finders/filters'
|
||||
import { lookupModule } from '@revenge-mod/modules/finders/lookup'
|
||||
|
||||
import type { Metro } from '@revenge-mod/modules/types'
|
||||
import type { DiscordModules } from '../types'
|
||||
|
||||
export { AppStartPerformance } from '../preinit'
|
||||
|
||||
export * as Flux from './flux'
|
||||
|
||||
// ../discord_common/js/shared/Logger.tsx
|
||||
export const [Logger] = lookupModule(byName<DiscordModules.Logger>('Logger')) as [DiscordModules.Logger, Metro.ModuleID]
|
||||
export const [Logger] = lookupModule(
|
||||
byName<DiscordModules.Logger>('Logger'),
|
||||
) as [DiscordModules.Logger, Metro.ModuleID]
|
||||
|
||||
// ../discord_common/js/packages/tokens/native.tsx
|
||||
export const [Tokens, TokensModuleId] = lookupModule(byProps('RawColor')) as [any, Metro.ModuleID]
|
||||
export const [Tokens, TokensModuleId] = lookupModule(byProps('RawColor')) as [
|
||||
any,
|
||||
Metro.ModuleID,
|
||||
]
|
||||
|
|
|
|||
|
|
@ -7,11 +7,12 @@ import {
|
|||
relativeDep,
|
||||
} from '@revenge-mod/modules/finders/filters'
|
||||
import { lookupModule } from '@revenge-mod/modules/finders/lookup'
|
||||
|
||||
import { ReactJsxRuntimeModuleId, ReactModuleId, ReactNativeModuleId } from '@revenge-mod/react'
|
||||
|
||||
import {
|
||||
ReactJsxRuntimeModuleId,
|
||||
ReactModuleId,
|
||||
ReactNativeModuleId,
|
||||
} from '@revenge-mod/react'
|
||||
import { proxify } from '@revenge-mod/utils/proxy'
|
||||
|
||||
import type { DiscordModules } from './types'
|
||||
|
||||
// design/native.tsx
|
||||
|
|
@ -22,7 +23,15 @@ export let Design: Design = proxify(
|
|||
const [module] = lookupModule(
|
||||
preferExports(
|
||||
byProps<Design>('TableRow', 'Button'),
|
||||
byDependencies(looseDeps([relativeDep(1), undefined, relativeDep(2), relativeDep(3), 2])),
|
||||
byDependencies(
|
||||
looseDeps([
|
||||
relativeDep(1),
|
||||
undefined,
|
||||
relativeDep(2),
|
||||
relativeDep(3),
|
||||
2,
|
||||
]),
|
||||
),
|
||||
),
|
||||
{
|
||||
includeUninitialized: true,
|
||||
|
|
@ -45,7 +54,9 @@ export let FormSwitch: DiscordModules.Components.FormSwitch = proxify(() => {
|
|||
|
||||
const [module] = lookupModule(
|
||||
preferExports(
|
||||
bySingleProp<{ FormSwitch: DiscordModules.Components.FormSwitch }>('FormSwitch'),
|
||||
bySingleProp<{ FormSwitch: DiscordModules.Components.FormSwitch }>(
|
||||
'FormSwitch',
|
||||
),
|
||||
byDependencies([
|
||||
[relativeDep(1), relativeDep(2), undefined, relativeDep(3)],
|
||||
ReactModuleId,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import { _initExts, _uapi } from '@revenge-mod/plugins/_'
|
||||
import { defineLazyProperty } from '@revenge-mod/utils/objects'
|
||||
|
||||
import { Logger } from './common'
|
||||
|
||||
import type { UnscopedInitPluginApi } from '@revenge-mod/plugins/types'
|
||||
import type { PluginApiDiscord } from './types/revenge'
|
||||
|
||||
|
|
@ -10,10 +8,18 @@ const uapi = _uapi as UnscopedInitPluginApi
|
|||
const discord = (uapi.discord = {} as PluginApiDiscord)
|
||||
|
||||
// TODO: Rolldown issue: Bug requires us to do { ...require('...') } instead of just require('...')
|
||||
defineLazyProperty(discord, 'actions', () => ({ ...require('@revenge-mod/discord/actions') }))
|
||||
defineLazyProperty(discord, 'common', () => ({ ...require('@revenge-mod/discord/common') }))
|
||||
defineLazyProperty(discord, 'design', () => ({ ...require('@revenge-mod/discord/design') }))
|
||||
defineLazyProperty(discord, 'native', () => ({ ...require('@revenge-mod/discord/native') }))
|
||||
defineLazyProperty(discord, 'actions', () => ({
|
||||
...require('@revenge-mod/discord/actions'),
|
||||
}))
|
||||
defineLazyProperty(discord, 'common', () => ({
|
||||
...require('@revenge-mod/discord/common'),
|
||||
}))
|
||||
defineLazyProperty(discord, 'design', () => ({
|
||||
...require('@revenge-mod/discord/design'),
|
||||
}))
|
||||
defineLazyProperty(discord, 'native', () => ({
|
||||
...require('@revenge-mod/discord/native'),
|
||||
}))
|
||||
defineLazyProperty(discord, 'modules', () => ({
|
||||
settings: {
|
||||
...require('@revenge-mod/discord/modules/settings'),
|
||||
|
|
@ -22,5 +28,9 @@ defineLazyProperty(discord, 'modules', () => ({
|
|||
}))
|
||||
|
||||
_initExts.push((api, { manifest: { id } }) =>
|
||||
defineLazyProperty(api, 'logger', () => new Logger(`Revenge > Plugins (${id})`)),
|
||||
defineLazyProperty(
|
||||
api,
|
||||
'logger',
|
||||
() => new Logger(`Revenge > Plugins (${id})`),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
import type { SettingsItem, SettingsModulesLoadedSubscription, SettingsSection } from '.'
|
||||
import type {
|
||||
SettingsItem,
|
||||
SettingsModulesLoadedSubscription,
|
||||
SettingsSection,
|
||||
} from '.'
|
||||
|
||||
export const _data = [
|
||||
// Sections to splice in the SettingsOverviewScreen
|
||||
|
|
@ -6,6 +10,10 @@ export const _data = [
|
|||
// SETTING_RENDERER_CONFIG settings
|
||||
{},
|
||||
false,
|
||||
] as [sections: Record<string, SettingsSection>, config: Record<string, SettingsItem>, loaded: boolean]
|
||||
] as [
|
||||
sections: Record<string, SettingsSection>,
|
||||
config: Record<string, SettingsItem>,
|
||||
loaded: boolean,
|
||||
]
|
||||
|
||||
export const _subs = new Set<SettingsModulesLoadedSubscription>()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { _data, _subs } from './_internal'
|
||||
|
||||
import type { DiscordModules } from '../../types'
|
||||
|
||||
export type SettingsItem = DiscordModules.Modules.Settings.SettingsItem
|
||||
|
|
@ -22,7 +21,9 @@ export function isSettingsModulesLoaded() {
|
|||
* @returns A function to unsubscribe from the event.
|
||||
* @throws Throws an error if the settings modules are already loaded. Check with {@link isSettingsModulesLoaded} first.
|
||||
*/
|
||||
export function onSettingsModulesLoaded(subcription: SettingsModulesLoadedSubscription) {
|
||||
export function onSettingsModulesLoaded(
|
||||
subcription: SettingsModulesLoadedSubscription,
|
||||
) {
|
||||
if (_data[2]) throw new Error('Settings modules already loaded')
|
||||
|
||||
_subs.add(subcription)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,21 @@
|
|||
import { byDependencies, byProps, looseDeps, preferExports, relativeDep } from '@revenge-mod/modules/finders/filters'
|
||||
import {
|
||||
byDependencies,
|
||||
byProps,
|
||||
looseDeps,
|
||||
preferExports,
|
||||
relativeDep,
|
||||
} from '@revenge-mod/modules/finders/filters'
|
||||
import { lookupModule } from '@revenge-mod/modules/finders/lookup'
|
||||
|
||||
import { ReactJsxRuntimeModuleId, ReactModuleId, ReactNativeModuleId } from '@revenge-mod/react'
|
||||
|
||||
import {
|
||||
ReactJsxRuntimeModuleId,
|
||||
ReactModuleId,
|
||||
ReactNativeModuleId,
|
||||
} from '@revenge-mod/react'
|
||||
import { proxify } from '@revenge-mod/utils/proxy'
|
||||
|
||||
import type { DiscordModules } from '../../types'
|
||||
|
||||
export type SettingListRenderer = DiscordModules.Modules.Settings.SettingListRenderer
|
||||
export type SettingListRenderer =
|
||||
DiscordModules.Modules.Settings.SettingListRenderer
|
||||
|
||||
export let SettingListRenderer: SettingListRenderer = proxify(
|
||||
() => {
|
||||
|
|
|
|||
|
|
@ -11,9 +11,15 @@ import type { DiscordNativeModules } from './types/native'
|
|||
|
||||
const nmp = nativeModuleProxy
|
||||
|
||||
export const CacheModule = nmp.NativeCacheModule as DiscordNativeModules.CacheModule
|
||||
export const FileModule = nmp.NativeFileModule as DiscordNativeModules.FileModule
|
||||
export const ClientInfoModule = nmp.NativeClientInfoModule as DiscordNativeModules.ClientInfoModule
|
||||
export const DeviceModule = nmp.NativeDeviceModule as DiscordNativeModules.DeviceModule
|
||||
export const BundleUpdaterManager = nmp.BundleUpdaterManager as DiscordNativeModules.BundleUpdaterManager
|
||||
export const ThemeModule = nmp.NativeThemeModule as DiscordNativeModules.ThemeModule
|
||||
export const CacheModule =
|
||||
nmp.NativeCacheModule as DiscordNativeModules.CacheModule
|
||||
export const FileModule =
|
||||
nmp.NativeFileModule as DiscordNativeModules.FileModule
|
||||
export const ClientInfoModule =
|
||||
nmp.NativeClientInfoModule as DiscordNativeModules.ClientInfoModule
|
||||
export const DeviceModule =
|
||||
nmp.NativeDeviceModule as DiscordNativeModules.DeviceModule
|
||||
export const BundleUpdaterManager =
|
||||
nmp.BundleUpdaterManager as DiscordNativeModules.BundleUpdaterManager
|
||||
export const ThemeModule =
|
||||
nmp.NativeThemeModule as DiscordNativeModules.ThemeModule
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import { byDependencies, byProps, preferExports, relativeDep } from '@revenge-mod/modules/finders/filters'
|
||||
import {
|
||||
byDependencies,
|
||||
byProps,
|
||||
preferExports,
|
||||
relativeDep,
|
||||
} from '@revenge-mod/modules/finders/filters'
|
||||
import { lookupModule } from '@revenge-mod/modules/finders/lookup'
|
||||
|
||||
import type { Metro } from '@revenge-mod/modules/types'
|
||||
import type { DiscordModules } from './types'
|
||||
|
||||
|
|
|
|||
|
|
@ -51,12 +51,20 @@ export namespace DiscordModules {
|
|||
|
||||
export interface Dispatcher {
|
||||
_actionHandlers: unknown
|
||||
_interceptors?: ((payload: DispatcherPayload) => undefined | boolean)[]
|
||||
_interceptors?: ((
|
||||
payload: DispatcherPayload,
|
||||
) => undefined | boolean)[]
|
||||
_currentDispatchActionType: undefined | string
|
||||
_processingWaitQueue: boolean
|
||||
_subscriptions: Record<string, Set<(payload: DispatcherPayload) => void>>
|
||||
_subscriptions: Record<
|
||||
string,
|
||||
Set<(payload: DispatcherPayload) => void>
|
||||
>
|
||||
_waitQueue: unknown[]
|
||||
addDependencies(node1: DispatcherDependency, node2: DispatcherDependency): void
|
||||
addDependencies(
|
||||
node1: DispatcherDependency,
|
||||
node2: DispatcherDependency,
|
||||
): void
|
||||
dispatch(payload: DispatcherPayload): Promise<void>
|
||||
flushWaitQueue(): void
|
||||
isDispatching(): boolean
|
||||
|
|
@ -65,19 +73,29 @@ export namespace DiscordModules {
|
|||
actionHandler: Record<string, (e: DispatcherPayload) => void>,
|
||||
storeDidChange: (e: DispatcherPayload) => boolean,
|
||||
): string
|
||||
setInterceptor(interceptor?: (payload: DispatcherPayload) => undefined | boolean): void
|
||||
setInterceptor(
|
||||
interceptor?: (
|
||||
payload: DispatcherPayload,
|
||||
) => undefined | boolean,
|
||||
): void
|
||||
/**
|
||||
* Subscribes to an action type
|
||||
* @param actionType The action type to subscribe to
|
||||
* @param callback The callback to call when the action is dispatched
|
||||
*/
|
||||
subscribe(actionType: string, callback: (payload: DispatcherPayload) => void): void
|
||||
subscribe(
|
||||
actionType: string,
|
||||
callback: (payload: DispatcherPayload) => void,
|
||||
): void
|
||||
/**
|
||||
* Unsubscribes from an action type
|
||||
* @param actionType The action type to unsubscribe from
|
||||
* @param callback The callback to remove
|
||||
*/
|
||||
unsubscribe(actionType: string, callback: (payload: DispatcherPayload) => void): void
|
||||
unsubscribe(
|
||||
actionType: string,
|
||||
callback: (payload: DispatcherPayload) => void,
|
||||
): void
|
||||
wait(cb: () => void): void
|
||||
}
|
||||
}
|
||||
|
|
@ -88,7 +106,10 @@ export namespace DiscordModules {
|
|||
|
||||
export interface AppStartPerformance {
|
||||
mark(...args: AppStartPerformance.MarkArgs): void
|
||||
markAndLog(logger: InstanceType<DiscordModules.Logger>, ...args: AppStartPerformance.MarkArgs): void
|
||||
markAndLog(
|
||||
logger: InstanceType<DiscordModules.Logger>,
|
||||
...args: AppStartPerformance.MarkArgs
|
||||
): void
|
||||
[index: string]: unknown
|
||||
}
|
||||
|
||||
|
|
@ -132,7 +153,9 @@ export namespace DiscordModules {
|
|||
| 'eyebrow'
|
||||
|
||||
export type TextStyleSheet = Record<TextVariant, RNTextProps>
|
||||
export type CreateStylesFunction = <const S extends Record<string, TextStyle | ViewStyle | ImageStyle>>(
|
||||
export type CreateStylesFunction = <
|
||||
const S extends Record<string, TextStyle | ViewStyle | ImageStyle>,
|
||||
>(
|
||||
styles: S,
|
||||
) => () => S
|
||||
}
|
||||
|
|
@ -303,7 +326,8 @@ export namespace DiscordModules {
|
|||
label?: string
|
||||
}
|
||||
|
||||
export interface TextAreaProps extends Omit<TextInputProps, 'multiline'> {}
|
||||
export interface TextAreaProps
|
||||
extends Omit<TextInputProps, 'multiline'> {}
|
||||
|
||||
export type TextInput = FC<TextInputProps>
|
||||
export type TextField = FC<TextFieldProps>
|
||||
|
|
@ -326,7 +350,8 @@ export namespace DiscordModules {
|
|||
|
||||
export type ActionSheet = FC<ActionSheetProps>
|
||||
|
||||
export interface ActionSheetCloseButtonProps extends Pick<ComponentProps<IconButton>, 'variant' | 'onPress'> {}
|
||||
export interface ActionSheetCloseButtonProps
|
||||
extends Pick<ComponentProps<IconButton>, 'variant' | 'onPress'> {}
|
||||
|
||||
export type ActionSheetCloseButton = FC<ActionSheetCloseButtonProps>
|
||||
|
||||
|
|
@ -374,7 +399,8 @@ export namespace DiscordModules {
|
|||
Icon: TableRowIcon
|
||||
}
|
||||
|
||||
export interface TableSwitchRowProps extends Omit<TableRowProps, 'trailing'> {
|
||||
export interface TableSwitchRowProps
|
||||
extends Omit<TableRowProps, 'trailing'> {
|
||||
accessibilityHint?: string
|
||||
value: boolean
|
||||
onValueChange(value: boolean): void
|
||||
|
|
@ -496,7 +522,11 @@ export namespace DiscordModules {
|
|||
|
||||
export interface SettingsListProps {
|
||||
ListHeaderComponent?: ComponentType
|
||||
sections: Array<{ label?: string | ReactNode; settings: string[]; subLabel?: string | ReactNode }>
|
||||
sections: Array<{
|
||||
label?: string | ReactNode
|
||||
settings: string[]
|
||||
subLabel?: string | ReactNode
|
||||
}>
|
||||
}
|
||||
|
||||
export type SettingsList = FC<SettingsListProps>
|
||||
|
|
|
|||
|
|
@ -42,7 +42,10 @@ export namespace DiscordNativeModules {
|
|||
* @param encoding The encoding to read the asset with
|
||||
* @returns
|
||||
*/
|
||||
readAsset: (nameOrUri: string, encoding: 'base64' | 'utf8') => Promise<string>
|
||||
readAsset: (
|
||||
nameOrUri: string,
|
||||
encoding: 'base64' | 'utf8',
|
||||
) => Promise<string>
|
||||
/**
|
||||
* Allowed URI schemes on Android: `file://`, `content://` ([See here](https://developer.android.com/reference/android/content/ContentResolver#accepts-the-following-uri-schemes:_3))
|
||||
*/
|
||||
|
|
@ -52,7 +55,11 @@ export namespace DiscordNativeModules {
|
|||
* @param encoding Set to `base64` in order to encode response
|
||||
*/
|
||||
readFile(path: string, encoding: 'base64' | 'utf8'): Promise<string>
|
||||
saveFileToGallery?(uri: string, fileName: string, fileType: 'PNG' | 'JPEG'): Promise<string>
|
||||
saveFileToGallery?(
|
||||
uri: string,
|
||||
fileName: string,
|
||||
fileType: 'PNG' | 'JPEG',
|
||||
): Promise<string>
|
||||
/**
|
||||
* @param storageDir Either `cache` or `documents`.
|
||||
* @param path Path in `storageDir`, parents are recursively created.
|
||||
|
|
@ -71,7 +78,10 @@ export namespace DiscordNativeModules {
|
|||
* @param storageDir Either `cache` or `documents`
|
||||
* @param path Path to the file to be removed
|
||||
*/
|
||||
removeFile(storageDir: 'cache' | 'documents', path: string): Promise<boolean>
|
||||
removeFile(
|
||||
storageDir: 'cache' | 'documents',
|
||||
path: string,
|
||||
): Promise<boolean>
|
||||
/**
|
||||
* Clear the folder from the path given
|
||||
* (!) On Android, this only clears all *files* and not subdirectories!
|
||||
|
|
@ -79,7 +89,10 @@ export namespace DiscordNativeModules {
|
|||
* @param path Path to the folder to be cleared
|
||||
* @returns Whether the clearance succeeded
|
||||
*/
|
||||
clearFolder(storageDir: 'cache' | 'documents', path: string): Promise<boolean>
|
||||
clearFolder(
|
||||
storageDir: 'cache' | 'documents',
|
||||
path: string,
|
||||
): Promise<boolean>
|
||||
getConstants: () => {
|
||||
/**
|
||||
* The path the `documents` storage dir (see {@link writeFile}) represents.
|
||||
|
|
|
|||
12
lib/externals/src/browserify.ts
vendored
12
lib/externals/src/browserify.ts
vendored
|
|
@ -1,11 +1,19 @@
|
|||
import { byDependencies, byProps, preferExports, relativeDep } from '@revenge-mod/modules/finders/filters'
|
||||
import {
|
||||
byDependencies,
|
||||
byProps,
|
||||
preferExports,
|
||||
relativeDep,
|
||||
} from '@revenge-mod/modules/finders/filters'
|
||||
import { lookupModule } from '@revenge-mod/modules/finders/lookup'
|
||||
import { proxify } from '@revenge-mod/utils/proxy'
|
||||
|
||||
export let nodeUtil: typeof import('node:util') = proxify(
|
||||
() => {
|
||||
const [module] = lookupModule(
|
||||
preferExports(byProps<typeof nodeUtil>('inspect'), byDependencies([relativeDep(1), [], []])),
|
||||
preferExports(
|
||||
byProps<typeof nodeUtil>('inspect'),
|
||||
byDependencies([relativeDep(1), [], []]),
|
||||
),
|
||||
{
|
||||
includeUninitialized: true,
|
||||
},
|
||||
|
|
|
|||
75
lib/externals/src/react-native-clipboard.ts
vendored
75
lib/externals/src/react-native-clipboard.ts
vendored
|
|
@ -1,43 +1,54 @@
|
|||
import { byDependencies, byProps, every, preferExports, relativeDep } from '@revenge-mod/modules/finders/filters'
|
||||
import {
|
||||
byDependencies,
|
||||
byProps,
|
||||
every,
|
||||
preferExports,
|
||||
relativeDep,
|
||||
} from '@revenge-mod/modules/finders/filters'
|
||||
import { lookupModule } from '@revenge-mod/modules/finders/lookup'
|
||||
import { ReactModuleId, ReactNativeModuleId } from '@revenge-mod/react'
|
||||
import { destructure, proxify } from '@revenge-mod/utils/proxy'
|
||||
|
||||
let ClipboardModule: typeof import('@react-native-clipboard/clipboard') = proxify(
|
||||
() => {
|
||||
// ID: 4790
|
||||
let ClipboardModule: typeof import('@react-native-clipboard/clipboard') =
|
||||
proxify(
|
||||
() => {
|
||||
// ID: 4790
|
||||
|
||||
// r6 = 4790;
|
||||
// r5 = [4791, 4792];
|
||||
// r6 = 4790;
|
||||
// r5 = [4791, 4792];
|
||||
|
||||
// r6 = 4791;
|
||||
// r5 = [175, 4792];
|
||||
// r6 = 4791;
|
||||
// r5 = [175, 4792];
|
||||
|
||||
// r6 = 4792;
|
||||
// r5 = [27, 4793];
|
||||
// r6 = 4792;
|
||||
// r5 = [27, 4793];
|
||||
|
||||
const [module] = lookupModule(
|
||||
preferExports(
|
||||
byProps<typeof ClipboardModule>('useClipboard'),
|
||||
every(
|
||||
byDependencies([relativeDep(1), relativeDep(2)]),
|
||||
byDependencies([
|
||||
[ReactModuleId, relativeDep(2, true)],
|
||||
[ReactNativeModuleId, relativeDep(3, true)],
|
||||
]),
|
||||
const [module] = lookupModule(
|
||||
preferExports(
|
||||
byProps<typeof ClipboardModule>('useClipboard'),
|
||||
every(
|
||||
byDependencies([relativeDep(1), relativeDep(2)]),
|
||||
byDependencies([
|
||||
[ReactModuleId, relativeDep(2, true)],
|
||||
[ReactNativeModuleId, relativeDep(3, true)],
|
||||
]),
|
||||
),
|
||||
),
|
||||
),
|
||||
{
|
||||
includeUninitialized: true,
|
||||
},
|
||||
)
|
||||
{
|
||||
includeUninitialized: true,
|
||||
},
|
||||
)
|
||||
|
||||
if (module) return (ClipboardModule = module)
|
||||
},
|
||||
{
|
||||
hint: 'object',
|
||||
},
|
||||
)!
|
||||
if (module) return (ClipboardModule = module)
|
||||
},
|
||||
{
|
||||
hint: 'object',
|
||||
},
|
||||
)!
|
||||
|
||||
export const { useClipboard } = destructure(ClipboardModule, { hint: 'function' })
|
||||
export const { default: Clipboard } = destructure(ClipboardModule, { hint: 'object' })
|
||||
export const { useClipboard } = destructure(ClipboardModule, {
|
||||
hint: 'function',
|
||||
})
|
||||
export const { default: Clipboard } = destructure(ClipboardModule, {
|
||||
hint: 'object',
|
||||
})
|
||||
|
|
|
|||
94
lib/externals/src/react-navigation.ts
vendored
94
lib/externals/src/react-navigation.ts
vendored
|
|
@ -1,47 +1,71 @@
|
|||
import { byDependencies, byName, byProps, looseDeps, preferExports } from '@revenge-mod/modules/finders/filters'
|
||||
import {
|
||||
byDependencies,
|
||||
byName,
|
||||
byProps,
|
||||
looseDeps,
|
||||
preferExports,
|
||||
} from '@revenge-mod/modules/finders/filters'
|
||||
import { lookupModule } from '@revenge-mod/modules/finders/lookup'
|
||||
import { proxify } from '@revenge-mod/utils/proxy'
|
||||
|
||||
const [, _createClassModuleId] = lookupModule(byName('_createClass'))
|
||||
const [, _classCallCheckModuleId] = lookupModule(byName('_classCallCheck'))
|
||||
|
||||
export let ReactNavigationNative: typeof import('@react-navigation/native') = proxify(
|
||||
() => {
|
||||
const [module] = lookupModule(
|
||||
preferExports(
|
||||
byProps<typeof ReactNavigationNative>('useLinkTo'),
|
||||
byDependencies(looseDeps([[], looseDeps([[_createClassModuleId, _classCallCheckModuleId]])])),
|
||||
),
|
||||
{
|
||||
includeUninitialized: true,
|
||||
},
|
||||
)
|
||||
export let ReactNavigationNative: typeof import('@react-navigation/native') =
|
||||
proxify(
|
||||
() => {
|
||||
const [module] = lookupModule(
|
||||
preferExports(
|
||||
byProps<typeof ReactNavigationNative>('useLinkTo'),
|
||||
byDependencies(
|
||||
looseDeps([
|
||||
[],
|
||||
looseDeps([
|
||||
[_createClassModuleId, _classCallCheckModuleId],
|
||||
]),
|
||||
]),
|
||||
),
|
||||
),
|
||||
{
|
||||
includeUninitialized: true,
|
||||
},
|
||||
)
|
||||
|
||||
if (module) return (ReactNavigationNative = module)
|
||||
},
|
||||
{
|
||||
hint: 'object',
|
||||
},
|
||||
)!
|
||||
if (module) return (ReactNavigationNative = module)
|
||||
},
|
||||
{
|
||||
hint: 'object',
|
||||
},
|
||||
)!
|
||||
|
||||
export let ReactNavigationStack: typeof import('@react-navigation/stack') = proxify(
|
||||
() => {
|
||||
const [module] = lookupModule(
|
||||
preferExports(
|
||||
byProps<typeof ReactNavigationStack>('createStackNavigator'),
|
||||
byDependencies(looseDeps([[], looseDeps([[_createClassModuleId, _classCallCheckModuleId]])])),
|
||||
),
|
||||
{
|
||||
includeUninitialized: true,
|
||||
},
|
||||
)
|
||||
export let ReactNavigationStack: typeof import('@react-navigation/stack') =
|
||||
proxify(
|
||||
() => {
|
||||
const [module] = lookupModule(
|
||||
preferExports(
|
||||
byProps<typeof ReactNavigationStack>(
|
||||
'createStackNavigator',
|
||||
),
|
||||
byDependencies(
|
||||
looseDeps([
|
||||
[],
|
||||
looseDeps([
|
||||
[_createClassModuleId, _classCallCheckModuleId],
|
||||
]),
|
||||
]),
|
||||
),
|
||||
),
|
||||
{
|
||||
includeUninitialized: true,
|
||||
},
|
||||
)
|
||||
|
||||
if (module) return (ReactNavigationStack = module)
|
||||
},
|
||||
{
|
||||
hint: 'object',
|
||||
},
|
||||
)!
|
||||
if (module) return (ReactNavigationStack = module)
|
||||
},
|
||||
{
|
||||
hint: 'object',
|
||||
},
|
||||
)!
|
||||
|
||||
export interface ReactNavigationParamList {
|
||||
[Page: string]: any
|
||||
|
|
|
|||
13
lib/externals/src/shopify.ts
vendored
13
lib/externals/src/shopify.ts
vendored
|
|
@ -1,9 +1,16 @@
|
|||
import { byDependencies, byProps, preferExports } from '@revenge-mod/modules/finders/filters'
|
||||
import {
|
||||
byDependencies,
|
||||
byProps,
|
||||
preferExports,
|
||||
} from '@revenge-mod/modules/finders/filters'
|
||||
import { lookupModule } from '@revenge-mod/modules/finders/lookup'
|
||||
import {
|
||||
ReactJsxRuntimeModuleId,
|
||||
ReactModuleId,
|
||||
ReactNativeModuleId,
|
||||
} from '@revenge-mod/react'
|
||||
import { proxify } from '@revenge-mod/utils/proxy'
|
||||
|
||||
import { ReactJsxRuntimeModuleId, ReactModuleId, ReactNativeModuleId } from '@revenge-mod/react'
|
||||
|
||||
export let FlashList: typeof import('@shopify/flash-list') = proxify(
|
||||
() => {
|
||||
const [module] = lookupModule(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { isModuleExportBad } from '../metro/utils'
|
||||
|
||||
import type { If } from '@revenge-mod/utils/types'
|
||||
import type { Metro } from '../types'
|
||||
import type { Filter } from './filters'
|
||||
|
|
@ -13,7 +12,9 @@ export interface RunFilterOptions {
|
|||
skipDefault?: boolean
|
||||
}
|
||||
|
||||
export type RunFilterReturnExportsOptions<ReturnNamespace extends boolean = boolean> = RunFilterOptions &
|
||||
export type RunFilterReturnExportsOptions<
|
||||
ReturnNamespace extends boolean = boolean,
|
||||
> = RunFilterOptions &
|
||||
If<
|
||||
ReturnNamespace,
|
||||
{
|
||||
|
|
@ -44,14 +45,18 @@ const FilterResultFlags = {
|
|||
Namespace: 3,
|
||||
}
|
||||
|
||||
export type FilterResultFlag = (typeof FilterResultFlags)[keyof typeof FilterResultFlags]
|
||||
export type FilterResultFlag =
|
||||
(typeof FilterResultFlags)[keyof typeof FilterResultFlags]
|
||||
|
||||
// The reason this returns a flag is because flags are never falsy, while exports may be falsy when using ID-only filters (eg. `byDependencies`).
|
||||
|
||||
// Currently, we only have options that are relevant for checking exports
|
||||
// If we add more options later on, do NOT forget about
|
||||
// adding them here, and passing them in the lookup* functions
|
||||
export function runFilter(filter: Filter<any, false>, id: Metro.ModuleID): FilterResultFlag | undefined
|
||||
export function runFilter(
|
||||
filter: Filter<any, false>,
|
||||
id: Metro.ModuleID,
|
||||
): FilterResultFlag | undefined
|
||||
|
||||
export function runFilter(
|
||||
filter: Filter,
|
||||
|
|
@ -81,7 +86,11 @@ export function runFilter(
|
|||
}
|
||||
|
||||
const defaultExport = exports.default
|
||||
if (!options?.skipDefault && !isModuleExportBad(defaultExport) && filter(id, defaultExport)) {
|
||||
if (
|
||||
!options?.skipDefault &&
|
||||
!isModuleExportBad(defaultExport) &&
|
||||
filter(id, defaultExport)
|
||||
) {
|
||||
// TODO(modules/finders/caches::default)
|
||||
return FilterResultFlags.Default
|
||||
}
|
||||
|
|
@ -92,6 +101,7 @@ export function exportsFromFilterResultFlag(
|
|||
exports: Metro.ModuleExports,
|
||||
options?: RunFilterReturnExportsOptions,
|
||||
) {
|
||||
if (flag === FilterResultFlags.Default && !options?.returnNamespace) return exports.default
|
||||
if (flag === FilterResultFlags.Default && !options?.returnNamespace)
|
||||
return exports.default
|
||||
return exports
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,22 @@
|
|||
import { _inits } from '../metro/_internal'
|
||||
|
||||
import { getModuleDependencies, initializedModuleHasBadExports, isModuleInitialized } from '../metro/utils'
|
||||
|
||||
import {
|
||||
getModuleDependencies,
|
||||
initializedModuleHasBadExports,
|
||||
isModuleInitialized,
|
||||
} from '../metro/utils'
|
||||
import type { If, LogicalOr } from '@revenge-mod/utils/types'
|
||||
import type { Metro } from '../types'
|
||||
|
||||
export type FilterResult<F> = F extends Filter<infer R, boolean> ? R : never
|
||||
|
||||
export type IsFilterWithExports<F> = F extends Filter<any, infer WE> ? WE : never
|
||||
export type IsFilterWithExports<F> = F extends Filter<any, infer WE>
|
||||
? WE
|
||||
: never
|
||||
|
||||
export interface Filter<_Inferable = any, WithExports extends boolean = boolean> {
|
||||
export interface Filter<
|
||||
_Inferable = any,
|
||||
WithExports extends boolean = boolean,
|
||||
> {
|
||||
(
|
||||
...args: If<
|
||||
WithExports,
|
||||
|
|
@ -41,7 +48,11 @@ export interface Filter<_Inferable = any, WithExports extends boolean = boolean>
|
|||
* @see {@link byProps} for an example on custom-typed filters.
|
||||
*/
|
||||
export function createFilterGenerator<A extends any[]>(
|
||||
filter: (args: A, id: Metro.ModuleID, exports: Metro.ModuleExports) => boolean,
|
||||
filter: (
|
||||
args: A,
|
||||
id: Metro.ModuleID,
|
||||
exports: Metro.ModuleExports,
|
||||
) => boolean,
|
||||
keyFor: (args: A) => string,
|
||||
): (...args: A) => Filter<object, true>
|
||||
|
||||
|
|
@ -55,7 +66,8 @@ export function createFilterGenerator<A extends any[]>(
|
|||
keyFor: (args: A) => string,
|
||||
): (...args: any[]) => Filter {
|
||||
return (...args: A) => {
|
||||
const filter = (id: Metro.ModuleID, exports?: Metro.ModuleExports) => f(args, id, exports)
|
||||
const filter = (id: Metro.ModuleID, exports?: Metro.ModuleExports) =>
|
||||
f(args, id, exports)
|
||||
filter.key = keyFor(args)
|
||||
return filter
|
||||
}
|
||||
|
|
@ -94,7 +106,10 @@ export const byProps = createFilterGenerator<Parameters<ByProps>>(
|
|||
props => `revenge.props(${props.join(',')})`,
|
||||
) as ByProps
|
||||
|
||||
export type WithoutProps = <T extends Record<string, any>>(prop: string, ...props: string[]) => Filter<T, true>
|
||||
export type WithoutProps = <T extends Record<string, any>>(
|
||||
prop: string,
|
||||
...props: string[]
|
||||
) => Filter<T, true>
|
||||
|
||||
/**
|
||||
* Filter modules by their exports having none of the specified properties.
|
||||
|
|
@ -112,7 +127,9 @@ export const withoutProps = createFilterGenerator<Parameters<WithoutProps>>(
|
|||
props => `revenge.withoutProps(${props.join(',')})`,
|
||||
) as WithoutProps
|
||||
|
||||
export type BySingleProp = <T extends Record<string, any>>(prop: keyof T) => Filter<T, true>
|
||||
export type BySingleProp = <T extends Record<string, any>>(
|
||||
prop: keyof T,
|
||||
) => Filter<T, true>
|
||||
|
||||
/**
|
||||
* Filter modules by their exports having only the specified property.
|
||||
|
|
@ -127,11 +144,16 @@ export type BySingleProp = <T extends Record<string, any>>(prop: keyof T) => Fil
|
|||
*/
|
||||
export const bySingleProp = createFilterGenerator<Parameters<BySingleProp>>(
|
||||
// We don't use Reflect.ownKeys here because __esModule is not enumerable, and we don't want to include it in the check
|
||||
([prop], _, exports) => typeof exports === 'object' && Object.keys(exports).length === 1 && prop in exports,
|
||||
([prop], _, exports) =>
|
||||
typeof exports === 'object' &&
|
||||
Object.keys(exports).length === 1 &&
|
||||
prop in exports,
|
||||
([prop]) => `revenge.singleProp(${prop})`,
|
||||
) as BySingleProp
|
||||
|
||||
export type ByName = <T extends object = object>(name: string) => Filter<T, true>
|
||||
export type ByName = <T extends object = object>(
|
||||
name: string,
|
||||
) => Filter<T, true>
|
||||
|
||||
/**
|
||||
* Filter modules by their exports having the specified name.
|
||||
|
|
@ -174,11 +196,19 @@ export interface RelativeDependency {
|
|||
}
|
||||
|
||||
export interface ComparableDependencyMap
|
||||
extends Array<Metro.ModuleID | RelativeDependency | undefined | ComparableDependencyMap> {
|
||||
extends Array<
|
||||
| Metro.ModuleID
|
||||
| RelativeDependency
|
||||
| undefined
|
||||
| ComparableDependencyMap
|
||||
> {
|
||||
l?: boolean
|
||||
}
|
||||
|
||||
type ComputedComparableDependencyMap = Exclude<ComparableDependencyMap, Array<RelativeDependency>>
|
||||
type ComputedComparableDependencyMap = Exclude<
|
||||
ComparableDependencyMap,
|
||||
Array<RelativeDependency>
|
||||
>
|
||||
|
||||
type ByDependencies = <T>(deps: ComparableDependencyMap) => Filter<T, false>
|
||||
|
||||
|
|
@ -229,22 +259,39 @@ export function looseDeps(deps: ComparableDependencyMap) {
|
|||
* @param id The dependency ID to mark as relative.
|
||||
* @param root Whether this dependency should be relatively to the root module ID. Useless for single-depth comparisons.
|
||||
*/
|
||||
export function relativeDep(id: Metro.ModuleID, root?: boolean): RelativeDependency {
|
||||
export function relativeDep(
|
||||
id: Metro.ModuleID,
|
||||
root?: boolean,
|
||||
): RelativeDependency {
|
||||
const o = Object(id) as RelativeDependency
|
||||
if (root) o.r = true
|
||||
return o
|
||||
}
|
||||
|
||||
function compareDeps(rootOf: Metro.ModuleID, compare: ComparableDependencyMap, loose = false): boolean {
|
||||
function compareDeps(
|
||||
rootOf: Metro.ModuleID,
|
||||
compare: ComparableDependencyMap,
|
||||
loose = false,
|
||||
): boolean {
|
||||
const stack: Array<
|
||||
[of: Metro.ModuleID, deps: Metro.ModuleID[], compare: ComparableDependencyMap, loose?: boolean]
|
||||
[
|
||||
of: Metro.ModuleID,
|
||||
deps: Metro.ModuleID[],
|
||||
compare: ComparableDependencyMap,
|
||||
loose?: boolean,
|
||||
]
|
||||
> = []
|
||||
|
||||
stack.push([rootOf, getModuleDependencies(rootOf)!, compare, loose])
|
||||
|
||||
while (stack.length) {
|
||||
const [of, deps, compare, loose] = stack.pop()!
|
||||
if (loose ? deps.length < compare.length : deps.length !== compare.length) return false
|
||||
if (
|
||||
loose
|
||||
? deps.length < compare.length
|
||||
: deps.length !== compare.length
|
||||
)
|
||||
return false
|
||||
|
||||
const ccmp: ComputedComparableDependencyMap = []
|
||||
for (let i = 0; i < compare.length; i++) {
|
||||
|
|
@ -252,7 +299,8 @@ function compareDeps(rootOf: Metro.ModuleID, compare: ComparableDependencyMap, l
|
|||
if (cmp === undefined) continue
|
||||
|
||||
if (Array.isArray(cmp)) ccmp[i] = cmp
|
||||
else if (typeof cmp === 'object') ccmp[i] = cmp.r ? rootOf + cmp.valueOf() : of + cmp.valueOf()
|
||||
else if (typeof cmp === 'object')
|
||||
ccmp[i] = cmp.r ? rootOf + cmp.valueOf() : of + cmp.valueOf()
|
||||
else ccmp[i] = cmp
|
||||
}
|
||||
|
||||
|
|
@ -280,7 +328,8 @@ function genDepsKey(deps: ComparableDependencyMap): string {
|
|||
else if (Array.isArray(dep)) {
|
||||
if (dep.l) key += '#'
|
||||
key += `[${genDepsKey(dep)}],`
|
||||
} else key += `${(dep as RelativeDependency).r ? `r${dep.valueOf()}` : dep.valueOf()},`
|
||||
} else
|
||||
key += `${(dep as RelativeDependency).r ? `r${dep.valueOf()}` : dep.valueOf()},`
|
||||
|
||||
return key.substring(0, key.length - 1)
|
||||
}
|
||||
|
|
@ -289,14 +338,20 @@ export type Every = {
|
|||
<F1 extends Filter, F2 extends Filter>(
|
||||
f1: F1,
|
||||
f2: F2,
|
||||
): Filter<FilterResult<F1> & FilterResult<F2>, LogicalOr<IsFilterWithExports<F1>, IsFilterWithExports<F2>>>
|
||||
): Filter<
|
||||
FilterResult<F1> & FilterResult<F2>,
|
||||
LogicalOr<IsFilterWithExports<F1>, IsFilterWithExports<F2>>
|
||||
>
|
||||
<F1 extends Filter, F2 extends Filter, F3 extends Filter>(
|
||||
f1: F1,
|
||||
f2: F2,
|
||||
f3: F3,
|
||||
): Filter<
|
||||
FilterResult<F1> & FilterResult<F2> & FilterResult<F3>,
|
||||
LogicalOr<LogicalOr<IsFilterWithExports<F1>, IsFilterWithExports<F2>>, IsFilterWithExports<F3>>
|
||||
LogicalOr<
|
||||
LogicalOr<IsFilterWithExports<F1>, IsFilterWithExports<F2>>,
|
||||
IsFilterWithExports<F3>
|
||||
>
|
||||
>
|
||||
(...filters: Filter[]): Filter
|
||||
}
|
||||
|
|
@ -331,14 +386,19 @@ export type Some = {
|
|||
<F1 extends Filter, F2 extends Filter>(
|
||||
f1: F1,
|
||||
f2: F2,
|
||||
): Filter<FilterResult<F1> | FilterResult<F2>, IsFilterWithExports<F1> | IsFilterWithExports<F2>>
|
||||
): Filter<
|
||||
FilterResult<F1> | FilterResult<F2>,
|
||||
IsFilterWithExports<F1> | IsFilterWithExports<F2>
|
||||
>
|
||||
<F1 extends Filter, F2 extends Filter, F3 extends Filter>(
|
||||
f1: F1,
|
||||
f2: F2,
|
||||
f3: F3,
|
||||
): Filter<
|
||||
FilterResult<F1> | FilterResult<F2> | FilterResult<F3>,
|
||||
IsFilterWithExports<F1> | IsFilterWithExports<F2> | IsFilterWithExports<F3>
|
||||
| IsFilterWithExports<F1>
|
||||
| IsFilterWithExports<F2>
|
||||
| IsFilterWithExports<F3>
|
||||
>
|
||||
(...filters: Filter[]): Filter
|
||||
}
|
||||
|
|
@ -392,10 +452,13 @@ export type ModuleStateAware = <IF extends Filter>(
|
|||
* ))
|
||||
* ```
|
||||
*/
|
||||
export const moduleStateAware = createFilterGenerator<Parameters<ModuleStateAware>>(
|
||||
export const moduleStateAware = createFilterGenerator<
|
||||
Parameters<ModuleStateAware>
|
||||
>(
|
||||
([initializedFilter, uninitializedFilter], id, exports) => {
|
||||
if (isModuleInitialized(id)) {
|
||||
if (!initializedModuleHasBadExports(id)) return initializedFilter(id, exports)
|
||||
if (!initializedModuleHasBadExports(id))
|
||||
return initializedFilter(id, exports)
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +1,24 @@
|
|||
import { noopFalse } from '@revenge-mod/utils/callbacks'
|
||||
|
||||
import { type LookupModulesOptions, lookupModule, lookupModuleByImportedPath } from './lookup'
|
||||
import { type WaitForModulesOptions, waitForModuleByImportedPath, waitForModules } from './wait'
|
||||
|
||||
import type { MaybeDefaultExportMatched } from '../types'
|
||||
import type { Metro } from '../types'
|
||||
import { lookupModule, lookupModuleByImportedPath } from './lookup'
|
||||
import { waitForModuleByImportedPath, waitForModules } from './wait'
|
||||
import type { MaybeDefaultExportMatched, Metro } from '../types'
|
||||
import type { RunFilterReturnExportsOptions } from './_internal'
|
||||
import type { Filter, FilterResult } from './filters'
|
||||
import type { LookupModulesOptions } from './lookup'
|
||||
import type { WaitForModulesOptions } from './wait'
|
||||
|
||||
export type GetModuleOptions<
|
||||
ReturnNamespace extends boolean = boolean,
|
||||
IncludeUninitialized extends boolean = boolean,
|
||||
> = WaitForModulesOptions<ReturnNamespace> & LookupModulesOptions<ReturnNamespace, IncludeUninitialized>
|
||||
> = WaitForModulesOptions<ReturnNamespace> &
|
||||
LookupModulesOptions<ReturnNamespace, IncludeUninitialized>
|
||||
|
||||
export type GetModuleResult<
|
||||
F extends Filter,
|
||||
O extends GetModuleOptions,
|
||||
> = O extends RunFilterReturnExportsOptions<true> ? MaybeDefaultExportMatched<FilterResult<F>> : FilterResult<F>
|
||||
> = O extends RunFilterReturnExportsOptions<true>
|
||||
? MaybeDefaultExportMatched<FilterResult<F>>
|
||||
: FilterResult<F>
|
||||
|
||||
export type GetModuleCallback<T> = (exports: T, id: Metro.ModuleID) => any
|
||||
|
||||
|
|
@ -48,11 +50,21 @@ export function getModule<F extends Filter>(
|
|||
): GetModuleUnsubscribeFunction
|
||||
|
||||
export function getModule<
|
||||
F extends O extends GetModuleOptions<boolean, true> ? Filter<any, false> : Filter,
|
||||
F extends O extends GetModuleOptions<boolean, true>
|
||||
? Filter<any, false>
|
||||
: Filter,
|
||||
O extends GetModuleOptions,
|
||||
>(filter: F, callback: GetModuleCallback<FilterResult<F>>, options: O): GetModuleUnsubscribeFunction
|
||||
>(
|
||||
filter: F,
|
||||
callback: GetModuleCallback<FilterResult<F>>,
|
||||
options: O,
|
||||
): GetModuleUnsubscribeFunction
|
||||
|
||||
export function getModule(filter: Filter, callback: GetModuleCallback<any>, options?: GetModuleOptions) {
|
||||
export function getModule(
|
||||
filter: Filter,
|
||||
callback: GetModuleCallback<any>,
|
||||
options?: GetModuleOptions,
|
||||
) {
|
||||
const [exports, id] = lookupModule(filter, options!)
|
||||
if (id != null) {
|
||||
callback(exports, id)
|
||||
|
|
@ -85,7 +97,10 @@ export function getModule(filter: Filter, callback: GetModuleCallback<any>, opti
|
|||
* })
|
||||
* ```
|
||||
*/
|
||||
export function getModuleByImportedPath<T>(path: string, callback: GetModuleCallback<T>): GetModuleUnsubscribeFunction {
|
||||
export function getModuleByImportedPath<T>(
|
||||
path: string,
|
||||
callback: GetModuleCallback<T>,
|
||||
): GetModuleUnsubscribeFunction {
|
||||
const [exports, id] = lookupModuleByImportedPath(path)
|
||||
if (id != null) {
|
||||
callback(exports, id)
|
||||
|
|
|
|||
|
|
@ -1,19 +1,17 @@
|
|||
import { _inits, _metas, _paths, _uninits } from '../metro/_internal'
|
||||
import {
|
||||
type RunFilterOptions,
|
||||
type RunFilterReturnExportsOptions,
|
||||
exportsFromFilterResultFlag,
|
||||
runFilter,
|
||||
} from './_internal'
|
||||
|
||||
import { _inits, _paths, _uninits } from '../metro/_internal'
|
||||
import { getInitializedModuleExports } from '../metro/utils'
|
||||
|
||||
import { exportsFromFilterResultFlag, runFilter } from './_internal'
|
||||
import type { If } from '@revenge-mod/utils/types'
|
||||
import type { MaybeDefaultExportMatched } from '../types'
|
||||
import type { Metro } from '../types'
|
||||
import type { MaybeDefaultExportMatched, Metro } from '../types'
|
||||
import type {
|
||||
RunFilterOptions,
|
||||
RunFilterReturnExportsOptions,
|
||||
} from './_internal'
|
||||
import type { Filter, FilterResult } from './filters'
|
||||
|
||||
export interface BaseLookupModulesOptions<IncludeUninitialized extends boolean = boolean> extends RunFilterOptions {
|
||||
export interface BaseLookupModulesOptions<
|
||||
IncludeUninitialized extends boolean = boolean,
|
||||
> extends RunFilterOptions {
|
||||
/**
|
||||
* Whether to include initialized modules in the lookup.
|
||||
*
|
||||
|
|
@ -35,9 +33,13 @@ export interface BaseLookupModulesOptions<IncludeUninitialized extends boolean =
|
|||
export type LookupModulesOptions<
|
||||
ReturnNamespace extends boolean = boolean,
|
||||
IncludeUninitialized extends boolean = boolean,
|
||||
> = RunFilterReturnExportsOptions<ReturnNamespace> & BaseLookupModulesOptions<IncludeUninitialized>
|
||||
> = RunFilterReturnExportsOptions<ReturnNamespace> &
|
||||
BaseLookupModulesOptions<IncludeUninitialized>
|
||||
|
||||
export type LookupModulesResult<F extends Filter, O extends LookupModulesOptions> = [
|
||||
export type LookupModulesResult<
|
||||
F extends Filter,
|
||||
O extends LookupModulesOptions,
|
||||
> = [
|
||||
exports: O extends RunFilterReturnExportsOptions<true>
|
||||
? MaybeDefaultExportMatched<FilterResult<F>>
|
||||
: FilterResult<F>,
|
||||
|
|
@ -46,7 +48,11 @@ export type LookupModulesResult<F extends Filter, O extends LookupModulesOptions
|
|||
|
||||
export type LookupModuleIdsOptions<
|
||||
IncludeUninitialized extends boolean = boolean,
|
||||
IncludeAll extends If<IncludeUninitialized, false, boolean> = If<IncludeUninitialized, false, boolean>,
|
||||
IncludeAll extends If<IncludeUninitialized, false, boolean> = If<
|
||||
IncludeUninitialized,
|
||||
false,
|
||||
boolean
|
||||
>,
|
||||
> = RunFilterOptions &
|
||||
(
|
||||
| (BaseLookupModulesOptions<IncludeUninitialized> & {
|
||||
|
|
@ -82,10 +88,14 @@ export type LookupModuleIdsOptions<
|
|||
* for (const exports of lookup) console.log(exports)
|
||||
* ```
|
||||
*/
|
||||
export function lookupModules<F extends Filter>(filter: F): Generator<LookupModulesResult<F, never>, undefined>
|
||||
export function lookupModules<F extends Filter>(
|
||||
filter: F,
|
||||
): Generator<LookupModulesResult<F, never>, undefined>
|
||||
|
||||
export function lookupModules<
|
||||
F extends O extends BaseLookupModulesOptions<true> ? Filter<any, false> : Filter,
|
||||
F extends O extends BaseLookupModulesOptions<true>
|
||||
? Filter<any, false>
|
||||
: Filter,
|
||||
O extends LookupModulesOptions,
|
||||
>(filter: F, options: O): Generator<LookupModulesResult<F, O>, undefined>
|
||||
|
||||
|
|
@ -94,7 +104,8 @@ export function* lookupModules(filter: Filter, options?: LookupModulesOptions) {
|
|||
for (const id of _inits) {
|
||||
const exports = getInitializedModuleExports(id)
|
||||
const flag = runFilter(filter, id, exports, options)
|
||||
if (flag) yield [exportsFromFilterResultFlag(flag, exports, options), id]
|
||||
if (flag)
|
||||
yield [exportsFromFilterResultFlag(flag, exports, options), id]
|
||||
}
|
||||
|
||||
if (options?.includeUninitialized)
|
||||
|
|
@ -103,7 +114,11 @@ export function* lookupModules(filter: Filter, options?: LookupModulesOptions) {
|
|||
// Run the filter again to ensure we have the correct exports
|
||||
const exports = __r(id)
|
||||
const flag = runFilter(filter, id, exports, options)
|
||||
if (flag) yield [exportsFromFilterResultFlag(flag, exports, options), id]
|
||||
if (flag)
|
||||
yield [
|
||||
exportsFromFilterResultFlag(flag, exports, options),
|
||||
id,
|
||||
]
|
||||
else warnDeveloperAboutAPartialFilterMatch(id, filter.key)
|
||||
}
|
||||
}
|
||||
|
|
@ -122,7 +137,9 @@ export function* lookupModules(filter: Filter, options?: LookupModulesOptions) {
|
|||
* const React = lookupModule(byProps<typeof import('react')>('createElement'))
|
||||
* ```
|
||||
*/
|
||||
export function lookupModule<F extends Filter>(filter: F): LookupModulesResult<F, never> | []
|
||||
export function lookupModule<F extends Filter>(
|
||||
filter: F,
|
||||
): LookupModulesResult<F, never> | []
|
||||
|
||||
export function lookupModule<F extends Filter, O extends LookupModulesOptions>(
|
||||
filter: F,
|
||||
|
|
@ -134,7 +151,8 @@ export function lookupModule(filter: Filter, options?: LookupModulesOptions) {
|
|||
for (const id of _inits) {
|
||||
const exports = getInitializedModuleExports(id)
|
||||
const flag = runFilter(filter, id, exports, options)
|
||||
if (flag) return [exportsFromFilterResultFlag(flag, exports, options), id]
|
||||
if (flag)
|
||||
return [exportsFromFilterResultFlag(flag, exports, options), id]
|
||||
}
|
||||
|
||||
if (options?.includeUninitialized)
|
||||
|
|
@ -143,7 +161,11 @@ export function lookupModule(filter: Filter, options?: LookupModulesOptions) {
|
|||
const exports = __r(id)
|
||||
// Run the filter again to ensure we have the correct exports
|
||||
const flag = runFilter(filter, id, exports, options)
|
||||
if (flag) return [exportsFromFilterResultFlag(flag, exports, options), id]
|
||||
if (flag)
|
||||
return [
|
||||
exportsFromFilterResultFlag(flag, exports, options),
|
||||
id,
|
||||
]
|
||||
warnDeveloperAboutAPartialFilterMatch(id, filter.key)
|
||||
}
|
||||
|
||||
|
|
@ -163,7 +185,9 @@ export function lookupModule(filter: Filter, options?: LookupModulesOptions) {
|
|||
* const [{ default: Logger }] = lookupModuleByImportedPath<{ default: typeof DiscordModules.Logger }>('modules/debug/Logger.tsx')
|
||||
* ```
|
||||
*/
|
||||
export function lookupModuleByImportedPath<T = any>(path: string): [exports: T, id: Metro.ModuleID] | [] {
|
||||
export function lookupModuleByImportedPath<T = any>(
|
||||
path: string,
|
||||
): [exports: T, id: Metro.ModuleID] | [] {
|
||||
const id = _paths.get(path)
|
||||
if (id == null) return []
|
||||
return [getInitializedModuleExports(id), id]
|
||||
|
|
@ -174,7 +198,10 @@ export function lookupModuleByImportedPath<T = any>(path: string): [exports: T,
|
|||
* This will cause modules to be initialized unnecessarily, and may cause issues.
|
||||
* @internal
|
||||
*/
|
||||
function warnDeveloperAboutAPartialFilterMatch(id: Metro.ModuleID, key: string) {
|
||||
function warnDeveloperAboutAPartialFilterMatch(
|
||||
id: Metro.ModuleID,
|
||||
key: string,
|
||||
) {
|
||||
console.warn(
|
||||
`[revenge.modules.finders.lookup] Module ${id} matched ${key} during uninitialized lookup, but did not match the full filter.`,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,19 @@
|
|||
import { onAnyModuleInitialized, onModuleFinishedImporting } from '../metro/subscriptions'
|
||||
import { getInitializedModuleExports, initializedModuleHasBadExports } from '../metro/utils'
|
||||
import { type RunFilterReturnExportsOptions, exportsFromFilterResultFlag, runFilter } from './_internal'
|
||||
|
||||
import type { MaybeDefaultExportMatched } from '../types'
|
||||
import type { Metro } from '../types'
|
||||
import {
|
||||
onAnyModuleInitialized,
|
||||
onModuleFinishedImporting,
|
||||
} from '../metro/subscriptions'
|
||||
import {
|
||||
getInitializedModuleExports,
|
||||
initializedModuleHasBadExports,
|
||||
} from '../metro/utils'
|
||||
import { exportsFromFilterResultFlag, runFilter } from './_internal'
|
||||
import type { MaybeDefaultExportMatched, Metro } from '../types'
|
||||
import type { RunFilterReturnExportsOptions } from './_internal'
|
||||
import type { Filter, FilterResult } from './filters'
|
||||
|
||||
export interface BaseWaitForModulesOptions<IncludeAll extends boolean = boolean> {
|
||||
export interface BaseWaitForModulesOptions<
|
||||
IncludeAll extends boolean = boolean,
|
||||
> {
|
||||
/**
|
||||
* Whether to include all modules, including ones with bad exports.
|
||||
*
|
||||
|
|
@ -22,12 +29,15 @@ export type WaitForModulesCallback<T> = (exports: T, id: Metro.ModuleID) => any
|
|||
export type WaitForModulesOptions<
|
||||
ReturnNamespace extends boolean = boolean,
|
||||
IncludeBadExports extends boolean = boolean,
|
||||
> = RunFilterReturnExportsOptions<ReturnNamespace> & BaseWaitForModulesOptions<IncludeBadExports>
|
||||
> = RunFilterReturnExportsOptions<ReturnNamespace> &
|
||||
BaseWaitForModulesOptions<IncludeBadExports>
|
||||
|
||||
export type WaitForModulesResult<
|
||||
F extends Filter,
|
||||
O extends WaitForModulesOptions,
|
||||
> = O extends RunFilterReturnExportsOptions<true> ? MaybeDefaultExportMatched<FilterResult<F>> : FilterResult<F>
|
||||
> = O extends RunFilterReturnExportsOptions<true>
|
||||
? MaybeDefaultExportMatched<FilterResult<F>>
|
||||
: FilterResult<F>
|
||||
|
||||
/**
|
||||
* Wait for modules to initialize. **Callback won't be called if the module is already initialized!**
|
||||
|
|
@ -55,7 +65,9 @@ export function waitForModules<F extends Filter>(
|
|||
): WaitForModulesUnsubscribeFunction
|
||||
|
||||
export function waitForModules<
|
||||
F extends O extends WaitForModulesOptions<boolean, true> ? Filter<any, false> : Filter,
|
||||
F extends O extends WaitForModulesOptions<boolean, true>
|
||||
? Filter<any, false>
|
||||
: Filter,
|
||||
O extends WaitForModulesOptions,
|
||||
>(
|
||||
filter: F,
|
||||
|
|
@ -63,17 +75,29 @@ export function waitForModules<
|
|||
options: O,
|
||||
): WaitForModulesUnsubscribeFunction
|
||||
|
||||
export function waitForModules(filter: Filter, callback: WaitForModulesCallback<any>, options?: WaitForModulesOptions) {
|
||||
export function waitForModules(
|
||||
filter: Filter,
|
||||
callback: WaitForModulesCallback<any>,
|
||||
options?: WaitForModulesOptions,
|
||||
) {
|
||||
return onAnyModuleInitialized(
|
||||
options?.includeAll
|
||||
? (id, exports) => {
|
||||
const flag = runFilter(filter, id, exports, options)
|
||||
if (flag) callback(exportsFromFilterResultFlag(flag, exports, options), id)
|
||||
if (flag)
|
||||
callback(
|
||||
exportsFromFilterResultFlag(flag, exports, options),
|
||||
id,
|
||||
)
|
||||
}
|
||||
: (id, exports) => {
|
||||
if (initializedModuleHasBadExports(id)) return
|
||||
const flag = runFilter(filter, id, exports, options)
|
||||
if (flag) callback(exportsFromFilterResultFlag(flag, exports, options), id)
|
||||
if (flag)
|
||||
callback(
|
||||
exportsFromFilterResultFlag(flag, exports, options),
|
||||
id,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { _execInitSubs, _execReqSubs } from './subscriptions/_internal'
|
||||
import { isModuleExportsBad } from './utils'
|
||||
|
||||
import type { Metro } from '../types'
|
||||
|
||||
/** Initializing ID */
|
||||
|
|
@ -19,7 +18,12 @@ export const _metas = new Map<
|
|||
export function patchMetroDefine(metroDefine: Metro.DefineFn) {
|
||||
return ((origFactory, id, deps) => {
|
||||
// deps won't be undefined last time I checked
|
||||
const metadata = [deps!, false] as typeof _metas extends Map<any, infer V> ? V : never
|
||||
const metadata = [deps!, false] as typeof _metas extends Map<
|
||||
any,
|
||||
infer V
|
||||
>
|
||||
? V
|
||||
: never
|
||||
_metas.set(id, metadata)
|
||||
_uninits.add(id)
|
||||
|
||||
|
|
@ -33,7 +37,15 @@ export function patchMetroDefine(metroDefine: Metro.DefineFn) {
|
|||
_execReqSubs(id)
|
||||
|
||||
try {
|
||||
origFactory(global, req, importDefault, importAll, module, exports, deps)
|
||||
origFactory(
|
||||
global,
|
||||
req,
|
||||
importDefault,
|
||||
importAll,
|
||||
module,
|
||||
exports,
|
||||
deps,
|
||||
)
|
||||
metadata[1] = true
|
||||
|
||||
// Add the module to the initialized set only if the factory doesn't error or the exports aren't bad
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import { _initing, _paths } from './_internal'
|
||||
|
||||
import { _execPathSubs } from './subscriptions/_internal'
|
||||
|
||||
import { byProps } from '../finders/filters'
|
||||
import { waitForModules } from '../finders/wait'
|
||||
import { _initing, _paths } from './_internal'
|
||||
import { _execPathSubs } from './subscriptions/_internal'
|
||||
|
||||
const unsubFFI = waitForModules(byProps('fileFinishedImporting'), exports => {
|
||||
unsubFFI()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import type { ModuleFinishedImportingCallback, ModuleFirstRequiredCallback, ModuleInitializedCallback } from '.'
|
||||
import type { Metro } from '../../types'
|
||||
import type {
|
||||
ModuleFinishedImportingCallback,
|
||||
ModuleFirstRequiredCallback,
|
||||
ModuleInitializedCallback,
|
||||
} from '.'
|
||||
|
||||
export const _reqAll = new Set<ModuleFirstRequiredCallback>()
|
||||
export const _req = new Map<Metro.ModuleID, Set<ModuleFirstRequiredCallback>>()
|
||||
|
|
@ -15,7 +19,10 @@ export function _execReqSubs(id: Metro.ModuleID) {
|
|||
}
|
||||
}
|
||||
|
||||
export function _execInitSubs(id: Metro.ModuleID, exports: Metro.ModuleExports) {
|
||||
export function _execInitSubs(
|
||||
id: Metro.ModuleID,
|
||||
exports: Metro.ModuleExports,
|
||||
) {
|
||||
for (const cb of _initAll) cb(id, exports)
|
||||
if (_init.has(id)) {
|
||||
for (const cb of _init.get(id)!) cb(id, exports)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
import { _init, _initAll, _path, _req, _reqAll } from './_internal'
|
||||
|
||||
import type { Metro } from '../../types'
|
||||
import type { initializedModuleHasBadExports } from '../utils'
|
||||
|
||||
export type ModuleFirstRequiredCallback = (id: Metro.ModuleID) => void
|
||||
export type ModuleInitializedCallback = (id: Metro.ModuleID, exports: Metro.ModuleExports) => void
|
||||
export type ModuleFinishedImportingCallback = (id: Metro.ModuleID, path: string) => void
|
||||
export type ModuleInitializedCallback = (
|
||||
id: Metro.ModuleID,
|
||||
exports: Metro.ModuleExports,
|
||||
) => void
|
||||
export type ModuleFinishedImportingCallback = (
|
||||
id: Metro.ModuleID,
|
||||
path: string,
|
||||
) => void
|
||||
|
||||
/**
|
||||
* Registers a callback to be called when any module is initialized.
|
||||
|
|
@ -35,7 +39,10 @@ export function onAnyModuleInitialized(callback: ModuleInitializedCallback) {
|
|||
* @param callback The callback to be called.
|
||||
* @returns A function that unregisters the callback.
|
||||
*/
|
||||
export function onModuleInitialized(id: Metro.ModuleID, callback: ModuleInitializedCallback) {
|
||||
export function onModuleInitialized(
|
||||
id: Metro.ModuleID,
|
||||
callback: ModuleInitializedCallback,
|
||||
) {
|
||||
let set = _init.get(id)
|
||||
if (!set) {
|
||||
set = new Set()
|
||||
|
|
@ -54,7 +61,9 @@ export function onModuleInitialized(id: Metro.ModuleID, callback: ModuleInitiali
|
|||
* @param callback The callback to be called.
|
||||
* @returns A function that unregisters the callback.
|
||||
*/
|
||||
export function onModuleFinishedImporting(callback: ModuleFinishedImportingCallback) {
|
||||
export function onModuleFinishedImporting(
|
||||
callback: ModuleFinishedImportingCallback,
|
||||
) {
|
||||
_path.add(callback)
|
||||
return () => _path.delete(callback)
|
||||
}
|
||||
|
|
@ -69,7 +78,9 @@ export function onModuleFinishedImporting(callback: ModuleFinishedImportingCallb
|
|||
* @param callback The callback to be called.
|
||||
* @returns A function that unregisters the callback.
|
||||
*/
|
||||
export function onAnyModuleFirstRequired(callback: ModuleFirstRequiredCallback) {
|
||||
export function onAnyModuleFirstRequired(
|
||||
callback: ModuleFirstRequiredCallback,
|
||||
) {
|
||||
_reqAll.add(callback)
|
||||
return () => _reqAll.delete(callback)
|
||||
}
|
||||
|
|
@ -85,7 +96,10 @@ export function onAnyModuleFirstRequired(callback: ModuleFirstRequiredCallback)
|
|||
* @param callback The callback to be called.
|
||||
* @returns A function that unregisters the callback.
|
||||
*/
|
||||
export function onModuleFirstRequired(id: Metro.ModuleID, callback: ModuleFirstRequiredCallback) {
|
||||
export function onModuleFirstRequired(
|
||||
id: Metro.ModuleID,
|
||||
callback: ModuleFirstRequiredCallback,
|
||||
) {
|
||||
let set = _req.get(id)
|
||||
if (!set) {
|
||||
set = new Set()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { isProxy } from '@revenge-mod/utils/proxy'
|
||||
import { _inits, _metas, _uninits } from './_internal'
|
||||
|
||||
import type { Metro } from '../types'
|
||||
|
||||
/**
|
||||
|
|
@ -29,7 +28,9 @@ export function initializedModuleHasBadExports(id: Metro.ModuleID): boolean {
|
|||
* Returns the dependencies of a module.
|
||||
* @param id The module ID.
|
||||
*/
|
||||
export function getModuleDependencies(id: Metro.ModuleID): Metro.DependencyMap | undefined {
|
||||
export function getModuleDependencies(
|
||||
id: Metro.ModuleID,
|
||||
): Metro.DependencyMap | undefined {
|
||||
return _metas.get(id)?.[0]
|
||||
}
|
||||
|
||||
|
|
@ -45,7 +46,9 @@ export function isModuleInitialized(id: Metro.ModuleID): boolean {
|
|||
* Returns the exports of an initialized module.
|
||||
* @param id The module ID.
|
||||
*/
|
||||
export function getInitializedModuleExports(id: Metro.ModuleID): Metro.ModuleExports | undefined {
|
||||
export function getInitializedModuleExports(
|
||||
id: Metro.ModuleID,
|
||||
): Metro.ModuleExports | undefined {
|
||||
return _metas.get(id)?.[2]?.exports
|
||||
}
|
||||
|
||||
|
|
@ -53,7 +56,9 @@ export function getInitializedModuleExports(id: Metro.ModuleID): Metro.ModuleExp
|
|||
* Returns whether a particular module export is bad. This is used for filter functions to check whether an export is filterable.
|
||||
* @param exp The export to check.
|
||||
*/
|
||||
export function isModuleExportBad(exp: Metro.ModuleExports[PropertyKey]): boolean {
|
||||
export function isModuleExportBad(
|
||||
exp: Metro.ModuleExports[PropertyKey],
|
||||
): boolean {
|
||||
return (
|
||||
// Nullish?
|
||||
exp == null ||
|
||||
|
|
@ -78,7 +83,8 @@ export function isModuleExportsBad(exports: Metro.ModuleExports): boolean {
|
|||
// - String, Boolean, Symbol, BigInt exports are not useful (who would do `module.exports = ...`?)
|
||||
!(typeof exports === 'object' || typeof exports === 'function') ||
|
||||
// Checking if the object is empty
|
||||
(exports.__proto__ === Object.prototype && !Reflect.ownKeys(exports).length) ||
|
||||
(exports.__proto__ === Object.prototype &&
|
||||
!Reflect.ownKeys(exports).length) ||
|
||||
// Can't run isProxy() on this because this isn't your typical proxy:
|
||||
// https://github.com/facebook/react-native/blob/master/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp
|
||||
exports === nativeModuleProxy
|
||||
|
|
|
|||
|
|
@ -55,7 +55,11 @@ export namespace Metro {
|
|||
|
||||
export type RequireFn = (id: ModuleID) => ModuleExports
|
||||
|
||||
export type DefineFn = (factory: FactoryFn, moduleId: ModuleID, dependencyMap?: DependencyMap | undefined) => void
|
||||
export type DefineFn = (
|
||||
factory: FactoryFn,
|
||||
moduleId: ModuleID,
|
||||
dependencyMap?: DependencyMap | undefined,
|
||||
) => void
|
||||
|
||||
export type ModuleDefiner = (moduleId: ModuleID) => void
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
import {
|
||||
createPatchedFunctionProxy,
|
||||
type HookNode,
|
||||
type PatchedFunctionProxyState,
|
||||
patchedFunctionProxyStates,
|
||||
unproxy,
|
||||
} from '../_internal'
|
||||
|
||||
import type { HookNode, PatchedFunctionProxyState } from '../_internal'
|
||||
import type {
|
||||
AfterHook,
|
||||
BeforeHook,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
import {
|
||||
createPatchedFunctionProxy,
|
||||
type HookNode,
|
||||
type PatchedFunctionProxyState,
|
||||
patchedFunctionProxyStates,
|
||||
unproxy,
|
||||
} from '../_internal'
|
||||
|
||||
import type { HookNode, PatchedFunctionProxyState } from '../_internal'
|
||||
import type { BeforeHook, FiniteDomain, UnknownFunction } from '../types'
|
||||
|
||||
function unpatchBefore<T extends UnknownFunction>(
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
import {
|
||||
createPatchedFunctionProxy,
|
||||
type FunctionProxyState,
|
||||
type InsteadHookNode,
|
||||
type PatchedFunctionProxyState,
|
||||
patchedFunctionProxyHandler,
|
||||
patchedFunctionProxyStates,
|
||||
unproxy,
|
||||
} from '../_internal'
|
||||
|
||||
import type {
|
||||
FunctionProxyState,
|
||||
InsteadHookNode,
|
||||
PatchedFunctionProxyState,
|
||||
} from '../_internal'
|
||||
import type {
|
||||
AbstractNewable,
|
||||
Callable,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import { getStorage, type StorageOptions } from '@revenge-mod/storage'
|
||||
import { getStorage } from '@revenge-mod/storage'
|
||||
import { defineLazyProperty } from '@revenge-mod/utils/objects'
|
||||
import { allSettled, sleepReject } from '@revenge-mod/utils/promises'
|
||||
|
||||
import { _uapi as uapi } from './apis'
|
||||
|
||||
import { PluginFlags as Flag, PluginsStorageDirectory, PluginStatus as Status } from './constants'
|
||||
|
||||
import {
|
||||
PluginFlags as Flag,
|
||||
PluginsStorageDirectory,
|
||||
PluginStatus as Status,
|
||||
} from './constants'
|
||||
import type { StorageOptions } from '@revenge-mod/storage'
|
||||
import type { AnyObject } from '@revenge-mod/utils/types'
|
||||
import type {
|
||||
InitPluginApi,
|
||||
|
|
@ -19,9 +21,15 @@ import type {
|
|||
|
||||
export const _uapi = uapi
|
||||
|
||||
export const _preInitExts: Array<(api: PreInitPluginApi, plugin: InternalPlugin) => void> = []
|
||||
export const _initExts: Array<(api: InitPluginApi, plugin: InternalPlugin) => void> = []
|
||||
export const _startExts: Array<(api: PluginApi, plugin: InternalPlugin) => void> = []
|
||||
export const _preInitExts: Array<
|
||||
(api: PreInitPluginApi, plugin: InternalPlugin) => void
|
||||
> = []
|
||||
export const _initExts: Array<
|
||||
(api: InitPluginApi, plugin: InternalPlugin) => void
|
||||
> = []
|
||||
export const _startExts: Array<
|
||||
(api: PluginApi, plugin: InternalPlugin) => void
|
||||
> = []
|
||||
|
||||
export const _plugins = new Map<PluginManifest['id'], InternalPlugin>()
|
||||
export const _metas = new Map<
|
||||
|
|
@ -41,7 +49,8 @@ export function registerPlugin<S extends AnyObject = AnyObject>(
|
|||
iflags: number,
|
||||
) {
|
||||
// TODO(plugins): verify plugin manifest
|
||||
if (_plugins.has(manifest.id)) throw new Error(`Plugin with ID "${manifest.id}" already registered`)
|
||||
if (_plugins.has(manifest.id))
|
||||
throw new Error(`Plugin with ID "${manifest.id}" already registered`)
|
||||
|
||||
const plugin: InternalPlugin = {
|
||||
_c: [],
|
||||
|
|
@ -95,17 +104,19 @@ function preparePluginPreInit(plugin: InternalPlugin) {
|
|||
meta[3] = PluginApiLevel.PreInit
|
||||
}
|
||||
|
||||
function preparePluginInit(plugin: InternalPlugin, storageOptions?: StorageOptions<AnyObject>) {
|
||||
function preparePluginInit(
|
||||
plugin: InternalPlugin,
|
||||
storageOptions?: StorageOptions<AnyObject>,
|
||||
) {
|
||||
const meta = _metas.get(plugin.manifest.id)!
|
||||
const api = meta[0] as InitPluginApi
|
||||
|
||||
defineLazyProperty(api, 'storage', () => getStorage(
|
||||
`${PluginsStorageDirectory}/${plugin.manifest.id}.json`,
|
||||
{
|
||||
defineLazyProperty(api, 'storage', () =>
|
||||
getStorage(`${PluginsStorageDirectory}/${plugin.manifest.id}.json`, {
|
||||
...storageOptions,
|
||||
directory: 'documents',
|
||||
},
|
||||
))
|
||||
}),
|
||||
)
|
||||
|
||||
for (const ext of _initExts) ext(api, plugin)
|
||||
|
||||
|
|
@ -126,10 +137,13 @@ function preparePluginStart(plugin: InternalPlugin) {
|
|||
async function disablePlugin(plugin: InternalPlugin) {
|
||||
const iflags = _metas.get(plugin.manifest.id)![2] ?? 0
|
||||
if (iflags & InternalPluginFlags.Essential)
|
||||
throw new Error(`Plugin "${plugin.manifest.id}" is essential and cannot be disabled`)
|
||||
throw new Error(
|
||||
`Plugin "${plugin.manifest.id}" is essential and cannot be disabled`,
|
||||
)
|
||||
|
||||
// If plugin is not stopped, and is also not stopping, we need to stop it
|
||||
if (plugin.status && !(plugin.status & Status.Stopping)) await stopPlugin(plugin)
|
||||
if (plugin.status && !(plugin.status & Status.Stopping))
|
||||
await stopPlugin(plugin)
|
||||
|
||||
// TODO(plugins): write to storage
|
||||
plugin.flags &= ~Flag.Enabled
|
||||
|
|
@ -153,9 +167,12 @@ export async function preInitPlugin(plugin: InternalPlugin) {
|
|||
|
||||
preparePluginPreInit(plugin)
|
||||
|
||||
if (!(plugin.flags & Flag.Enabled)) throw new Error(`Plugin "${id}" is not enabled`)
|
||||
if (!(plugin.flags & Flag.Enabled))
|
||||
throw new Error(`Plugin "${id}" is not enabled`)
|
||||
if (plugin.status & (Status.PreIniting | Status.PreInited))
|
||||
throw new Error(`Plugin preInit lifecycle for "${id}" is already running`)
|
||||
throw new Error(
|
||||
`Plugin preInit lifecycle for "${id}" is already running`,
|
||||
)
|
||||
|
||||
plugin.status |= Status.PreIniting
|
||||
|
||||
|
|
@ -182,7 +199,8 @@ export async function initPlugin(plugin: InternalPlugin) {
|
|||
const meta = _metas.get(id)!
|
||||
const [, promises, , apiLevel] = meta
|
||||
|
||||
if (!(plugin.flags & Flag.Enabled)) throw new Error(`Plugin "${id}" is not enabled`)
|
||||
if (!(plugin.flags & Flag.Enabled))
|
||||
throw new Error(`Plugin "${id}" is not enabled`)
|
||||
if (plugin.status & (Status.Initing | Status.Inited))
|
||||
throw new Error(`Plugin init lifecycle for "${id}" is already running`)
|
||||
|
||||
|
|
@ -212,7 +230,8 @@ export async function startPlugin(plugin: InternalPlugin) {
|
|||
const meta = _metas.get(id)!
|
||||
const [, promises, , apiLevel] = meta
|
||||
|
||||
if (!(plugin.flags & Flag.Enabled)) throw new Error(`Plugin "${id}" is not enabled`)
|
||||
if (!(plugin.flags & Flag.Enabled))
|
||||
throw new Error(`Plugin "${id}" is not enabled`)
|
||||
if (plugin.status & (Status.Starting | Status.Started))
|
||||
throw new Error(`Plugin start lifecycle for "${id}" is already running`)
|
||||
|
||||
|
|
@ -245,24 +264,32 @@ export async function stopPlugin(plugin: InternalPlugin) {
|
|||
const meta = _metas.get(id)!
|
||||
const [, promises, iflags, apiLevel] = meta
|
||||
|
||||
if (iflags & InternalPluginFlags.Essential) throw new Error(`Plugin "${id}" is essential and cannot be stopped`)
|
||||
if (iflags & InternalPluginFlags.Essential)
|
||||
throw new Error(`Plugin "${id}" is essential and cannot be stopped`)
|
||||
|
||||
if (!(plugin.flags & Flag.Enabled)) throw new Error(`Plugin "${id}" is not enabled`)
|
||||
if (plugin.status & Status.Stopping) throw new Error(`Plugin "${id}" is stopping`)
|
||||
if (!(plugin.flags & Flag.Enabled))
|
||||
throw new Error(`Plugin "${id}" is not enabled`)
|
||||
if (plugin.status & Status.Stopping)
|
||||
throw new Error(`Plugin "${id}" is stopping`)
|
||||
|
||||
// If the plugin is running its lifecycles, we need to wait for it to finish, then we'll stop it
|
||||
// We want to wait at max 5 seconds for the lifecycles to finish
|
||||
if (plugin.status & (Status.PreIniting | Status.Initing | Status.Starting))
|
||||
await Promise.race([
|
||||
Promise.all(promises),
|
||||
sleepReject(MaxWaitTime, 'Plugin lifecycles timed out, force stopping'),
|
||||
sleepReject(
|
||||
MaxWaitTime,
|
||||
'Plugin lifecycles timed out, force stopping',
|
||||
),
|
||||
])
|
||||
.then(finished => {
|
||||
// If the lifecycles don't finish in 5 seconds, a reload is probably required to unapply the changes
|
||||
if (!finished) plugin.flags |= Flag.ReloadRequired
|
||||
})
|
||||
.catch(e => handlePluginError(e, plugin))
|
||||
else if (!(plugin.status & (Status.PreInited | Status.Inited | Status.Started)))
|
||||
else if (
|
||||
!(plugin.status & (Status.PreInited | Status.Inited | Status.Started))
|
||||
)
|
||||
throw new Error(`Plugin "${id}" is not running`)
|
||||
|
||||
if (apiLevel < PluginApiLevel.PreInit) preparePluginPreInit(plugin)
|
||||
|
|
@ -275,7 +302,10 @@ export async function stopPlugin(plugin: InternalPlugin) {
|
|||
if (lifecycles.stop)
|
||||
await Promise.race([
|
||||
lifecycles.stop(meta[0] as PluginApi),
|
||||
sleepReject(MaxWaitTime, 'Plugin stop lifecycle timed out, force stopping'),
|
||||
sleepReject(
|
||||
MaxWaitTime,
|
||||
'Plugin stop lifecycle timed out, force stopping',
|
||||
),
|
||||
])
|
||||
} catch (e) {
|
||||
await handlePluginError(e, plugin)
|
||||
|
|
|
|||
|
|
@ -10,9 +10,15 @@ export interface PluginApiExternals {
|
|||
export const externals = {} as PluginApiExternals
|
||||
|
||||
// TODO: Rolldown issue: Bug requires us to do { ...require('...') } instead of just require('...')
|
||||
defineLazyProperty(externals, 'Browserify', () => ({ ...require('@revenge-mod/externals/browserify') }))
|
||||
defineLazyProperty(externals, 'Browserify', () => ({
|
||||
...require('@revenge-mod/externals/browserify'),
|
||||
}))
|
||||
defineLazyProperty(externals, 'ReactNativeClipboard', () => ({
|
||||
...require('@revenge-mod/externals/react-native-clipboard'),
|
||||
}))
|
||||
defineLazyProperty(externals, 'ReactNavigation', () => ({ ...require('@revenge-mod/externals/react-navigation') }))
|
||||
defineLazyProperty(externals, 'Shopify', () => ({ ...require('@revenge-mod/externals/shopify') }))
|
||||
defineLazyProperty(externals, 'ReactNavigation', () => ({
|
||||
...require('@revenge-mod/externals/react-navigation'),
|
||||
}))
|
||||
defineLazyProperty(externals, 'Shopify', () => ({
|
||||
...require('@revenge-mod/externals/shopify'),
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,14 @@
|
|||
import * as PatcherLibrary from '@revenge-mod/patcher'
|
||||
import type { UnscopedInitPluginApi, UnscopedPluginApi, UnscopedPreInitPluginApi } from '../types'
|
||||
import type {
|
||||
UnscopedInitPluginApi,
|
||||
UnscopedPluginApi,
|
||||
UnscopedPreInitPluginApi,
|
||||
} from '../types'
|
||||
|
||||
export const _uapi: UnscopedPreInitPluginApi | UnscopedInitPluginApi | UnscopedPluginApi = {
|
||||
export const _uapi:
|
||||
| UnscopedPreInitPluginApi
|
||||
| UnscopedInitPluginApi
|
||||
| UnscopedPluginApi = {
|
||||
modules: require('./modules').modules,
|
||||
plugins: require('./plugins').plugins,
|
||||
patcher: PatcherLibrary,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ export interface PluginApiModules {
|
|||
native: PluginApiModulesNative
|
||||
}
|
||||
|
||||
export type PluginApiModulesNative = typeof import('@revenge-mod/modules/native')
|
||||
export type PluginApiModulesNative =
|
||||
typeof import('@revenge-mod/modules/native')
|
||||
|
||||
export type PluginApiModulesMetro =
|
||||
// biome-ignore format: Don't
|
||||
|
|
|
|||
|
|
@ -1,14 +1,10 @@
|
|||
import { onRunApplication } from '@revenge-mod/react/native'
|
||||
|
||||
import { _uapi } from './apis'
|
||||
|
||||
import * as AssetsLibrary from '@revenge-mod/assets'
|
||||
import { onRunApplication } from '@revenge-mod/react/native'
|
||||
import { _plugins, initPlugin, startPlugin } from './_internal'
|
||||
import { _uapi } from './apis'
|
||||
import { externals } from './apis/externals'
|
||||
import { react } from './apis/react'
|
||||
|
||||
import { _plugins, initPlugin, startPlugin } from './_internal'
|
||||
import { PluginFlags } from './constants'
|
||||
|
||||
import type { UnscopedInitPluginApi } from './types'
|
||||
|
||||
// Setup init plugin APIs
|
||||
|
|
@ -17,7 +13,8 @@ uapi.assets = AssetsLibrary
|
|||
uapi.externals = externals
|
||||
uapi.react = react
|
||||
|
||||
for (const plugin of _plugins.values()) if (plugin.flags & PluginFlags.Enabled) initPlugin(plugin)
|
||||
for (const plugin of _plugins.values())
|
||||
if (plugin.flags & PluginFlags.Enabled) initPlugin(plugin)
|
||||
|
||||
const unsub = onRunApplication(() => {
|
||||
unsub()
|
||||
|
|
@ -26,5 +23,6 @@ const unsub = onRunApplication(() => {
|
|||
// const UILibrary = require('@revenge-mod/ui')
|
||||
// uapi.ui = UiLibrary
|
||||
|
||||
for (const plugin of _plugins.values()) if (plugin.flags & PluginFlags.Enabled) startPlugin(plugin)
|
||||
for (const plugin of _plugins.values())
|
||||
if (plugin.flags & PluginFlags.Enabled) startPlugin(plugin)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { _plugins, preInitPlugin } from './_internal'
|
||||
import { PluginFlags } from './constants'
|
||||
|
||||
for (const plugin of _plugins.values()) if (plugin.flags & PluginFlags.Enabled) preInitPlugin(plugin)
|
||||
for (const plugin of _plugins.values())
|
||||
if (plugin.flags & PluginFlags.Enabled) preInitPlugin(plugin)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
import type { PluginFlags, PluginStatus } from './constants'
|
||||
|
||||
import type { Storage, StorageOptions } from '@revenge-mod/storage'
|
||||
import type { AnyObject } from '@revenge-mod/utils/types'
|
||||
|
||||
import type { PluginApiExternals } from './apis/externals'
|
||||
import type { PluginApiModules } from './apis/modules'
|
||||
import type { PluginApiPlugins } from './apis/plugins'
|
||||
import type { PluginApiReact } from './apis/react'
|
||||
import type { PluginFlags, PluginStatus } from './constants'
|
||||
|
||||
/**
|
||||
* The unscoped plugin API (very limited). This API is available as a global for plugins.
|
||||
|
|
@ -54,7 +52,8 @@ export interface PreInitPluginApi {
|
|||
* The plugin API (limited).
|
||||
* Available in the `init` phase.
|
||||
*/
|
||||
export interface InitPluginApi<S extends AnyObject = AnyObject> extends PreInitPluginApi {
|
||||
export interface InitPluginApi<S extends AnyObject = AnyObject>
|
||||
extends PreInitPluginApi {
|
||||
unscoped: UnscopedInitPluginApi
|
||||
/**
|
||||
* The plugin storage.
|
||||
|
|
@ -71,7 +70,8 @@ export interface InitPluginApi<S extends AnyObject = AnyObject> extends PreInitP
|
|||
* The plugin API.
|
||||
* Available in the `start` and `stop` phase.
|
||||
*/
|
||||
export interface PluginApi<S extends AnyObject = AnyObject> extends InitPluginApi<S> {
|
||||
export interface PluginApi<S extends AnyObject = AnyObject>
|
||||
extends InitPluginApi<S> {
|
||||
unscoped: UnscopedPluginApi
|
||||
}
|
||||
|
||||
|
|
@ -114,7 +114,8 @@ export interface PluginManifest {
|
|||
icon?: string
|
||||
}
|
||||
|
||||
export interface PluginOptions<S extends AnyObject = AnyObject> extends PluginLifecycles<S> {
|
||||
export interface PluginOptions<S extends AnyObject = AnyObject>
|
||||
extends PluginLifecycles<S> {
|
||||
storage?: Omit<StorageOptions<S>, 'directory'>
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { byProps } from '@revenge-mod/modules/finders/filters'
|
||||
import { waitForModules } from '@revenge-mod/modules/finders/wait'
|
||||
|
||||
import type { Metro } from '@revenge-mod/modules/types'
|
||||
|
||||
export let ReactModuleId: Metro.ModuleID
|
||||
|
|
@ -13,25 +12,34 @@ export let ReactJsxRuntime: typeof import('react/jsx-runtime')
|
|||
|
||||
let RtCount = 0
|
||||
|
||||
const unsubRt = waitForModules(byProps<typeof React>('useState'), (exports, id) => {
|
||||
if (RtCount++ === 2) return unsubRt()
|
||||
const unsubRt = waitForModules(
|
||||
byProps<typeof React>('useState'),
|
||||
(exports, id) => {
|
||||
if (RtCount++ === 2) return unsubRt()
|
||||
|
||||
ReactModuleId = id
|
||||
React = exports
|
||||
})
|
||||
ReactModuleId = id
|
||||
React = exports
|
||||
},
|
||||
)
|
||||
|
||||
const unsubRN = waitForModules(byProps<typeof ReactNative>('AppRegistry'), (exports, id) => {
|
||||
unsubRN()
|
||||
const unsubRN = waitForModules(
|
||||
byProps<typeof ReactNative>('AppRegistry'),
|
||||
(exports, id) => {
|
||||
unsubRN()
|
||||
|
||||
ReactNativeModuleId = id
|
||||
ReactNative = exports
|
||||
})
|
||||
ReactNativeModuleId = id
|
||||
ReactNative = exports
|
||||
},
|
||||
)
|
||||
|
||||
let RJsxRCount = 0
|
||||
|
||||
const unsubRJsxR = waitForModules(byProps<typeof ReactJsxRuntime>('jsxs'), (exports, id) => {
|
||||
if (RJsxRCount++ === 2) return unsubRJsxR()
|
||||
const unsubRJsxR = waitForModules(
|
||||
byProps<typeof ReactJsxRuntime>('jsxs'),
|
||||
(exports, id) => {
|
||||
if (RJsxRCount++ === 2) return unsubRJsxR()
|
||||
|
||||
ReactJsxRuntimeModuleId = id
|
||||
ReactJsxRuntime = exports
|
||||
})
|
||||
ReactJsxRuntimeModuleId = id
|
||||
ReactJsxRuntime = exports
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import { _ara, _bra } from './patches'
|
||||
|
||||
import type { AppRegistry } from 'react-native'
|
||||
|
||||
/**
|
||||
* Registers a callback to be run when a call to {@link AppRegistry.runApplication} is made.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import { after, before } from '@revenge-mod/patcher'
|
||||
import { ReactNative } from '..'
|
||||
|
||||
import { byProps } from '@revenge-mod/modules/finders/filters'
|
||||
import { waitForModules } from '@revenge-mod/modules/finders/wait'
|
||||
|
||||
import { after, before } from '@revenge-mod/patcher'
|
||||
import { ReactNative } from '..'
|
||||
import type { RunApplicationCallback } from '.'
|
||||
|
||||
export const _bra = new Set<RunApplicationCallback>()
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import { FileModule } from '@revenge-mod/discord/native'
|
|||
import { React } from '@revenge-mod/react'
|
||||
import { mergeDeep } from '@revenge-mod/utils/objects'
|
||||
import { useReRender } from '@revenge-mod/utils/react'
|
||||
|
||||
import type { AnyObject, DeepPartial } from '@revenge-mod/utils/types'
|
||||
|
||||
/**
|
||||
|
|
@ -13,7 +12,10 @@ import type { AnyObject, DeepPartial } from '@revenge-mod/utils/types'
|
|||
* @param path Path relative to the directory.
|
||||
* @param directory Directory to use. Can be either 'cache' or 'documents'.
|
||||
*/
|
||||
export function getStorage<T extends AnyObject = AnyObject>(path: string, options?: StorageOptions<T>): Storage<T> {
|
||||
export function getStorage<T extends AnyObject = AnyObject>(
|
||||
path: string,
|
||||
options?: StorageOptions<T>,
|
||||
): Storage<T> {
|
||||
const { CacheDirPath, DocumentsDirPath } = FileModule.getConstants()
|
||||
|
||||
const directory = options?.directory ?? 'documents'
|
||||
|
|
@ -71,7 +73,12 @@ export function getStorage<T extends AnyObject = AnyObject>(path: string, option
|
|||
for (const sub of subs) sub(cache)
|
||||
return cache
|
||||
} catch (e) {
|
||||
console.error('Failed to parse storage file (most likely corrupted)', directory, path, e)
|
||||
console.error(
|
||||
'Failed to parse storage file (most likely corrupted)',
|
||||
directory,
|
||||
path,
|
||||
e,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -85,7 +92,12 @@ export function getStorage<T extends AnyObject = AnyObject>(path: string, option
|
|||
|
||||
for (const sub of subs) sub(value)
|
||||
} catch (e) {
|
||||
console.error('Failed to write storage file', directory, path, e)
|
||||
console.error(
|
||||
'Failed to write storage file',
|
||||
directory,
|
||||
path,
|
||||
e,
|
||||
)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
@ -110,7 +122,9 @@ export interface StorageOptions<T> {
|
|||
load?: boolean
|
||||
}
|
||||
|
||||
export type UseStorageFilter<T extends AnyObject> = (newValue: DeepPartial<T>) => any
|
||||
export type UseStorageFilter<T extends AnyObject> = (
|
||||
newValue: DeepPartial<T>,
|
||||
) => any
|
||||
|
||||
export interface Storage<T extends AnyObject> {
|
||||
/**
|
||||
|
|
|
|||
11
lib/types/types.d.ts
vendored
11
lib/types/types.d.ts
vendored
|
|
@ -5,7 +5,10 @@ import type { Metro } from '@revenge-mod/modules/types'
|
|||
/// HERMES
|
||||
|
||||
declare global {
|
||||
function setTimeout(cb: (...args: unknown[]) => unknown, timeout?: number): number
|
||||
function setTimeout(
|
||||
cb: (...args: unknown[]) => unknown,
|
||||
timeout?: number,
|
||||
): number
|
||||
/**
|
||||
* Calls the garbage collector
|
||||
*/
|
||||
|
|
@ -28,13 +31,17 @@ declare global {
|
|||
/// HERMES PROMISES
|
||||
|
||||
declare global {
|
||||
// biome-ignore lint/correctness/noUnusedVariables: Type parameter names must match
|
||||
interface Promise<T> {
|
||||
/// PROMISE POLYFILLS FROM: https://github.com/then/promise
|
||||
/// AND: https://github.com/facebook/hermes/blob/main/lib/InternalBytecode/01-Promise.js
|
||||
_h: 0 | 1 | 2
|
||||
}
|
||||
|
||||
type HermesPromiseRejectionHandler = (promise: Promise<any>, error: any) => void
|
||||
type HermesPromiseRejectionHandler = (
|
||||
promise: Promise<any>,
|
||||
error: any,
|
||||
) => void
|
||||
|
||||
interface PromiseConstructor {
|
||||
_m: HermesPromiseRejectionHandler
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
export function debounce<F extends (...args: any[]) => any>(func: F, timeout: number) {
|
||||
export function debounce<F extends (...args: any[]) => any>(
|
||||
func: F,
|
||||
timeout: number,
|
||||
) {
|
||||
let timer: Parameters<typeof clearTimeout>[0]
|
||||
return (...args: Parameters<F>) =>
|
||||
new Promise(rs => {
|
||||
|
|
|
|||
|
|
@ -1,16 +1,24 @@
|
|||
import { getAssetByName, getAssetModuleId } from '@revenge-mod/assets'
|
||||
import { TokensModuleId } from '@revenge-mod/discord/common'
|
||||
|
||||
import { byDependencies, byProps } from '@revenge-mod/modules/finders/filters'
|
||||
import { lookupModule } from '@revenge-mod/modules/finders/lookup'
|
||||
|
||||
import { ReactJsxRuntimeModuleId, ReactModuleId, ReactNativeModuleId } from '@revenge-mod/react'
|
||||
|
||||
import {
|
||||
ReactJsxRuntimeModuleId,
|
||||
ReactModuleId,
|
||||
ReactNativeModuleId,
|
||||
} from '@revenge-mod/react'
|
||||
import type { FC } from 'react'
|
||||
|
||||
// [React, ReactJsxRuntime, (Tokens), (BaseIconImage), (Asset), 2]
|
||||
// Asset = relativeDep(1)
|
||||
const IconComponentFilter = [ReactModuleId, ReactJsxRuntimeModuleId, TokensModuleId, undefined, undefined, 2]
|
||||
const IconComponentFilter = [
|
||||
ReactModuleId,
|
||||
ReactJsxRuntimeModuleId,
|
||||
TokensModuleId,
|
||||
undefined,
|
||||
undefined,
|
||||
2,
|
||||
]
|
||||
|
||||
// Generated IconComponents that have multiple assets, there is no specific length, so the filter has to be generated on-the-fly.
|
||||
// [React, ReactNative, ReactJsxRuntime, (Tokens), (BaseIconImage), (...Assets), 2]
|
||||
|
|
@ -24,7 +32,10 @@ const MultiIconComponentFilterBase = [
|
|||
]
|
||||
|
||||
// TODO(utils/discord/lookupGeneratedIconComponent): Make this into a filter?
|
||||
export function lookupGeneratedIconComponent<N extends string>(compName: N, ...assetNames: string[]) {
|
||||
export function lookupGeneratedIconComponent<N extends string>(
|
||||
compName: N,
|
||||
...assetNames: string[]
|
||||
) {
|
||||
const icon = lookupModule(byProps(compName))[0]?.[compName]
|
||||
if (icon) return icon
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,9 @@ export function yielded<T>(generator: Generator<T>, skip = 0): T {
|
|||
if (skip)
|
||||
for (let i = 0; i < skip; i++)
|
||||
if (generator.next().done)
|
||||
throw new Error(`Generator has no more values to drop (dropped ${i + 1} out of ${skip})`)
|
||||
throw new Error(
|
||||
`Generator has no more values to drop (dropped ${i + 1} out of ${skip})`,
|
||||
)
|
||||
|
||||
return generator.next().value
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ export function createLogger(tag: string) {
|
|||
log: (message: string) => console.log(`[${tag}] ${message}`),
|
||||
warn: (message: string) => console.warn(`[${tag}] ${message}`),
|
||||
error: (message: string) => console.error(`[${tag}] ${message}`),
|
||||
wtf: (message: string) => console.error(`\u001b[31m[${tag}] ${message}\u001b[0m`),
|
||||
wtf: (message: string) =>
|
||||
console.error(`\u001b[31m[${tag}] ${message}\u001b[0m`),
|
||||
} satisfies BasicLogger
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,8 +31,12 @@ export type InterceptPropertyCallback = (target: any, value: any) => any
|
|||
* @param callback The callback to call when the property is set, can return a non-null value to set the property to.
|
||||
* @returns The function to unintercept the property.
|
||||
*/
|
||||
export function interceptProperty(prop: PropertyKey, callback: (target: object, value: any) => any) {
|
||||
if (_intercepting.has(prop)) throw new Error(`Property ${String(prop)} is already being intercepted`)
|
||||
export function interceptProperty(
|
||||
prop: PropertyKey,
|
||||
callback: (target: object, value: any) => any,
|
||||
) {
|
||||
if (_intercepting.has(prop))
|
||||
throw new Error(`Property ${String(prop)} is already being intercepted`)
|
||||
|
||||
const proto = Object.prototype
|
||||
_intercepting.set(prop, Object.getOwnPropertyDescriptor(proto, prop))
|
||||
|
|
@ -102,7 +106,11 @@ export function mergeDeep(target: AnyObject, source: AnyObject) {
|
|||
* @param property The property key to define.
|
||||
* @param loader The function that will be called to load the property value when accessed.
|
||||
*/
|
||||
export function defineLazyProperty<T extends object, K extends keyof T>(target: T, property: K, loader: () => T[K]) {
|
||||
export function defineLazyProperty<T extends object, K extends keyof T>(
|
||||
target: T,
|
||||
property: K,
|
||||
loader: () => T[K],
|
||||
) {
|
||||
Object.defineProperty(target, property, {
|
||||
configurable: true,
|
||||
get() {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
export function allSettled(promises: Promise<any>[]): Promise<PromiseSettledResult<any>[]> {
|
||||
export function allSettled(
|
||||
promises: Promise<any>[],
|
||||
): Promise<PromiseSettledResult<any>[]> {
|
||||
const mapped = promises.map(p =>
|
||||
p instanceof Promise
|
||||
? p
|
||||
|
|
|
|||
|
|
@ -45,7 +45,10 @@ export function getProxyTarget(obj: object) {
|
|||
// Heavily inspired by Wintry's lazy utils, but more optimized and stripped down, with a few fixes.
|
||||
// https://github.com/pylixonly/wintry/blob/main/src/utils/lazy.ts
|
||||
|
||||
const _metas = new WeakMap<object, [factory: () => unknown, cacheable: boolean, cache?: unknown]>()
|
||||
const _metas = new WeakMap<
|
||||
object,
|
||||
[factory: () => unknown, cacheable: boolean, cache?: unknown]
|
||||
>()
|
||||
|
||||
const _handler = {
|
||||
...Object.fromEntries(
|
||||
|
|
@ -64,7 +67,12 @@ const _handler = {
|
|||
if (typeof val === 'function')
|
||||
return new Proxy(val, {
|
||||
// If thisArg happens to be a proxified value, we will use the target object instead
|
||||
apply: (fn, thisArg, args) => Reflect.apply(fn, thisArg === recv ? target : thisArg, args),
|
||||
apply: (fn, thisArg, args) =>
|
||||
Reflect.apply(
|
||||
fn,
|
||||
thisArg === recv ? target : thisArg,
|
||||
args,
|
||||
),
|
||||
})
|
||||
|
||||
return val
|
||||
|
|
@ -73,7 +81,8 @@ const _handler = {
|
|||
// TypeError: getOwnPropertyDescriptor trap result is not configurable but target property '...' is configurable or non-existent
|
||||
getOwnPropertyDescriptor: (hint, p) => {
|
||||
const d = Reflect.getOwnPropertyDescriptor(unproxifyFromHint(hint)!, p)
|
||||
if (d && !Reflect.getOwnPropertyDescriptor(hint, p)) Object.defineProperty(hint, p, d)
|
||||
if (d && !Reflect.getOwnPropertyDescriptor(hint, p))
|
||||
Object.defineProperty(hint, p, d)
|
||||
return d
|
||||
},
|
||||
} as ProxyHandler<object>
|
||||
|
|
@ -151,7 +160,8 @@ export function proxify<T>(signal: () => T, options?: ProxifyOptions): T {
|
|||
*/
|
||||
export function unproxify<T extends object>(proxified: T): T {
|
||||
const hint = getProxyTarget(proxified)
|
||||
if (!hint) throw new TypeError(`${typeof proxified} is not a proxified value`)
|
||||
if (!hint)
|
||||
throw new TypeError(`${typeof proxified} is not a proxified value`)
|
||||
return unproxifyFromHint(hint)
|
||||
}
|
||||
|
||||
|
|
@ -188,16 +198,24 @@ function unproxifyFromHint(hint: object) {
|
|||
* z // TypeError: Cannot destructure and proxify null (reading 'z')
|
||||
* ```
|
||||
*/
|
||||
export function destructure<T extends object>(proxified: T, options?: ProxifyOptions): T {
|
||||
export function destructure<T extends object>(
|
||||
proxified: T,
|
||||
options?: ProxifyOptions,
|
||||
): T {
|
||||
return new Proxy({} as T, {
|
||||
get: (_, p) =>
|
||||
proxify(() => {
|
||||
// @ts-expect-error
|
||||
const v = unproxify(proxified)[p]
|
||||
|
||||
if (v == null) throw new TypeError(`Cannot destructure and proxify ${v} (reading '${String(p)}')`)
|
||||
if (v == null)
|
||||
throw new TypeError(
|
||||
`Cannot destructure and proxify ${v} (reading '${String(p)}')`,
|
||||
)
|
||||
if (typeof v === 'function' || typeof v === 'object') return v
|
||||
throw new TypeError(`Cannot destructure and proxify a primitive (reading '${String(p)}')`)
|
||||
throw new TypeError(
|
||||
`Cannot destructure and proxify a primitive (reading '${String(p)}')`,
|
||||
)
|
||||
}, options),
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import { React } from '@revenge-mod/react'
|
||||
|
||||
import { type SearchFilter, findInTree } from './trees'
|
||||
|
||||
import { findInTree } from './trees'
|
||||
import type { ReactNode } from 'react'
|
||||
import type { SearchFilter } from './trees'
|
||||
|
||||
export function useIsFirstRender() {
|
||||
let firstRender = false
|
||||
|
|
@ -21,7 +20,10 @@ export function useReRender() {
|
|||
|
||||
const ReactFiberWalkable = new Set(['props', 'children', 'child', 'sibling'])
|
||||
|
||||
export function findInReactFiber(tree: Extract<ReactNode, { props: unknown }>, filter: SearchFilter): any {
|
||||
export function findInReactFiber(
|
||||
tree: Extract<ReactNode, { props: unknown }>,
|
||||
filter: SearchFilter,
|
||||
): any {
|
||||
return findInTree(tree, filter, {
|
||||
walkable: ReactFiberWalkable,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -18,7 +18,11 @@ export interface FindInTreeOptions {
|
|||
maxDepth?: number
|
||||
}
|
||||
|
||||
export function findInTree(tree: SearchTree, filter: SearchFilter, opts?: FindInTreeOptions): any | undefined {
|
||||
export function findInTree(
|
||||
tree: SearchTree,
|
||||
filter: SearchFilter,
|
||||
opts?: FindInTreeOptions,
|
||||
): any | undefined {
|
||||
type StackItem = [node: any, depth: number]
|
||||
const stack: StackItem[] = [[tree, 0]]
|
||||
const maxDepth = opts?.maxDepth ?? 100
|
||||
|
|
@ -37,7 +41,8 @@ export function findInTree(tree: SearchTree, filter: SearchFilter, opts?: FindIn
|
|||
|
||||
if (typeof item !== 'object' || item == null) continue
|
||||
|
||||
if (!isArray && opts?.walkable?.size && !opts.walkable?.has(key)) continue
|
||||
if (!isArray && opts?.walkable?.size && !opts.walkable?.has(key))
|
||||
continue
|
||||
if (!isArray && opts?.ignore?.has(key)) continue
|
||||
|
||||
stack.push([item, depth + 1])
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
export type Nullish = null | undefined
|
||||
export type If<T, Then, Else> = T extends true ? Then : Else
|
||||
export type AnyObject = Record<any, any>
|
||||
export type LogicalOr<T1, T2> = T1 extends true ? true : T2 extends true ? true : false
|
||||
export type LogicalOr<T1, T2> = T1 extends true
|
||||
? true
|
||||
: T2 extends true
|
||||
? true
|
||||
: false
|
||||
export type DeepPartial<T> = {
|
||||
[K in keyof T]?: T[K] extends AnyObject ? DeepPartial<T[K]> : T[K]
|
||||
}
|
||||
|
|
|
|||
11
package.json
11
package.json
|
|
@ -29,7 +29,14 @@
|
|||
"build": "bun --bun run scripts/build.ts",
|
||||
"dev": "bun --bun run scripts/dev.ts"
|
||||
},
|
||||
"trustedDependencies": ["@biomejs/biome", "@parcel/watcher", "@swc/core"],
|
||||
"trustedDependencies": [
|
||||
"@biomejs/biome",
|
||||
"@parcel/watcher",
|
||||
"@swc/core"
|
||||
],
|
||||
"type": "module",
|
||||
"workspaces": ["lib/*", "plugins/*"]
|
||||
"workspaces": [
|
||||
"lib/*",
|
||||
"plugins/*"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { parse } from 'path'
|
||||
import { transform } from '@swc/core'
|
||||
import { $, main } from 'bun'
|
||||
import chalk from 'chalk'
|
||||
import { exists, mkdir, readdir, rm, writeFile } from 'fs/promises'
|
||||
import { type OutputChunk, type RolldownPlugin, rolldown } from 'rolldown'
|
||||
import { parse } from 'path'
|
||||
import { rolldown } from 'rolldown'
|
||||
import { aliasPlugin, importGlobPlugin } from 'rolldown/experimental'
|
||||
|
||||
import pkg from '../package.json'
|
||||
import type { OutputChunk, RolldownPlugin } from 'rolldown'
|
||||
|
||||
const ShimsDir = `${import.meta.dir}/../shims`
|
||||
const AssetsDir = `${import.meta.dir}/../src/assets`
|
||||
|
|
@ -45,8 +45,12 @@ export default async function build(dev = false, log = true) {
|
|||
},
|
||||
define: {
|
||||
__BUILD_VERSION__: JSON.stringify(pkg.version),
|
||||
__BUILD_COMMIT__: JSON.stringify((await $`git rev-parse HEAD`.text()).trim().substring(0, 7)),
|
||||
__BUILD_BRANCH__: JSON.stringify((await $`git rev-parse --abbrev-ref HEAD`.text()).trim()),
|
||||
__BUILD_COMMIT__: JSON.stringify(
|
||||
(await $`git rev-parse HEAD`.text()).trim().substring(0, 7),
|
||||
),
|
||||
__BUILD_BRANCH__: JSON.stringify(
|
||||
(await $`git rev-parse --abbrev-ref HEAD`.text()).trim(),
|
||||
),
|
||||
__BUILD_ENV__: JSON.stringify(dev ? 'development' : 'production'),
|
||||
__BUILD_FLAG_LOG_PROMISE_REJECTIONS__: String(dev),
|
||||
},
|
||||
|
|
@ -71,11 +75,24 @@ export default async function build(dev = false, log = true) {
|
|||
'-Wno-undefined-variable',
|
||||
],
|
||||
before() {
|
||||
if (log) console.debug(chalk.cyanBright('\u{1F5CE} JS compilation finished...'))
|
||||
if (log) console.debug(chalk.gray('\u{1F5CE} Compiling bytecode...'))
|
||||
if (log)
|
||||
console.debug(
|
||||
chalk.cyanBright(
|
||||
'\u{1F5CE} JS compilation finished...',
|
||||
),
|
||||
)
|
||||
if (log)
|
||||
console.debug(
|
||||
chalk.gray('\u{1F5CE} Compiling bytecode...'),
|
||||
)
|
||||
},
|
||||
after() {
|
||||
if (log) console.debug(chalk.cyanBright('\u{1F5CE} Bytecode compilation finished'))
|
||||
if (log)
|
||||
console.debug(
|
||||
chalk.cyanBright(
|
||||
'\u{1F5CE} Bytecode compilation finished',
|
||||
),
|
||||
)
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
|
@ -122,7 +139,7 @@ function swcPlugin() {
|
|||
},
|
||||
},
|
||||
env: {
|
||||
// https://github.com/discord/hermes/blob/main/doc/Features.md
|
||||
// https://github.com/facebook/hermes/blob/main/doc/Features.md
|
||||
targets: 'fully supports es6',
|
||||
include: [
|
||||
'transform-async-generator-functions',
|
||||
|
|
@ -152,14 +169,23 @@ function swcPlugin() {
|
|||
} satisfies RolldownPlugin
|
||||
}
|
||||
|
||||
function hermesCPlugin({ after, before, flags }: { flags?: string[]; before?: () => void; after?: () => void } = {}) {
|
||||
function hermesCPlugin({
|
||||
after,
|
||||
before,
|
||||
flags,
|
||||
}: {
|
||||
flags?: string[]
|
||||
before?: () => void
|
||||
after?: () => void
|
||||
} = {}) {
|
||||
const paths = {
|
||||
win32: 'hermesc.exe',
|
||||
darwin: 'hermesc',
|
||||
linux: 'hermesc',
|
||||
}
|
||||
|
||||
if (!(process.platform in paths)) throw new Error(`Unsupported platform: ${process.platform}`)
|
||||
if (!(process.platform in paths))
|
||||
throw new Error(`Unsupported platform: ${process.platform}`)
|
||||
|
||||
const binPath = paths[process.platform as keyof typeof paths]
|
||||
|
||||
|
|
@ -201,7 +227,8 @@ function hermesCPlugin({ after, before, flags }: { flags?: string[]; before?: ()
|
|||
}
|
||||
|
||||
async function generateAssets() {
|
||||
if (!(await exists(GeneratedAssetsDir))) await mkdir(GeneratedAssetsDir, { recursive: true })
|
||||
if (!(await exists(GeneratedAssetsDir)))
|
||||
await mkdir(GeneratedAssetsDir, { recursive: true })
|
||||
|
||||
const promises: Promise<void>[] = []
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import os from 'os'
|
||||
import { join } from 'path'
|
||||
import watcher from '@parcel/watcher'
|
||||
import { debounce } from '@revenge-mod/utils/callbacks'
|
||||
import chalk from 'chalk'
|
||||
import os from 'os'
|
||||
import { join } from 'path'
|
||||
import pkg from '../package.json'
|
||||
import build from './build'
|
||||
|
||||
|
|
@ -13,9 +13,18 @@ console.info(chalk.redBright(`\nRevenge ${chalk.white(`v${pkg.version}`)}\n`))
|
|||
|
||||
const cwdify = (path: string) => join(process.cwd(), path)
|
||||
const Sources = ['src', 'lib', 'plugins'].map(cwdify)
|
||||
const ExitTriggers = ['scripts', 'shims', 'package.json', 'bun.lock', 'tsconfig.json'].map(cwdify)
|
||||
const ExitTriggers = [
|
||||
'scripts',
|
||||
'shims',
|
||||
'package.json',
|
||||
'bun.lock',
|
||||
'tsconfig.json',
|
||||
].map(cwdify)
|
||||
|
||||
const debouncedBuildDev = debounce(() => ((needRebuild = false), build(true).catch(console.error)), 250)
|
||||
const debouncedBuildDev = debounce(
|
||||
() => ((needRebuild = false), build(true).catch(console.error)),
|
||||
250,
|
||||
)
|
||||
|
||||
let needRebuild = true
|
||||
|
||||
|
|
@ -23,25 +32,40 @@ watcher.subscribe(process.cwd(), (err, events) => {
|
|||
if (err) return console.error(err)
|
||||
|
||||
if (events.some(it => ExitTriggers.some(se => it.path.startsWith(se)))) {
|
||||
console.error(chalk.redBright('\u26A0 Scripts, packages, shims, or tsconfig.json has changed, exiting!'))
|
||||
console.error(
|
||||
chalk.redBright(
|
||||
'\u26A0 Scripts, packages, shims, or tsconfig.json has changed, exiting!',
|
||||
),
|
||||
)
|
||||
process.exit()
|
||||
}
|
||||
|
||||
needRebuild ||= events.some(it => Sources.some(src => it.path.startsWith(src)))
|
||||
needRebuild ||= events.some(it =>
|
||||
Sources.some(src => it.path.startsWith(src)),
|
||||
)
|
||||
})
|
||||
|
||||
const server = Bun.serve({
|
||||
hostname: lanHost ? '0.0.0.0' : '127.0.0.1',
|
||||
async fetch(req, srv) {
|
||||
if (needRebuild) await debouncedBuildDev()
|
||||
console.debug(chalk.gray(`\u{1F79B} Receiving request from ${srv.requestIP(req)!.address}`))
|
||||
console.debug(
|
||||
chalk.gray(
|
||||
`\u{1F79B} Receiving request from ${srv.requestIP(req)!.address}`,
|
||||
),
|
||||
)
|
||||
return new Response(Bun.file('./dist/revenge.bundle'))
|
||||
},
|
||||
port: 4040,
|
||||
})
|
||||
|
||||
if (lanHost) console.info(chalk.gray('\u24D8 Listening on all interfaces...'))
|
||||
else console.info(chalk.gray('\u24D8 Listening locally... Use --lan to listen on all interfaces'))
|
||||
else
|
||||
console.info(
|
||||
chalk.gray(
|
||||
'\u24D8 Listening locally... Use --lan to listen on all interfaces',
|
||||
),
|
||||
)
|
||||
|
||||
if (!prod) console.info(chalk.gray('\u24D8 Use --prod to build for production'))
|
||||
else console.info(chalk.gray('\u24D8 Building for production...'))
|
||||
|
|
|
|||
|
|
@ -1,13 +1,22 @@
|
|||
import { ReactJsxRuntime } from '@revenge-mod/react'
|
||||
import { isProxified, unproxify } from '@revenge-mod/utils/proxy'
|
||||
|
||||
import type { ElementType } from 'react'
|
||||
|
||||
const { jsx: _jsx, jsxs: _jsxs, Fragment } = ReactJsxRuntime
|
||||
|
||||
const jsx = ((type, ...args) =>
|
||||
_jsx(isProxified(type as object) ? (unproxify(type as object) as ElementType) : type, ...args)) as typeof _jsx
|
||||
_jsx(
|
||||
isProxified(type as object)
|
||||
? (unproxify(type as object) as ElementType)
|
||||
: type,
|
||||
...args,
|
||||
)) as typeof _jsx
|
||||
const jsxs = ((type, ...args) =>
|
||||
_jsxs(isProxified(type as object) ? (unproxify(type as object) as ElementType) : type, ...args)) as typeof _jsxs
|
||||
_jsxs(
|
||||
isProxified(type as object)
|
||||
? (unproxify(type as object) as ElementType)
|
||||
: type,
|
||||
...args,
|
||||
)) as typeof _jsxs
|
||||
|
||||
export { jsx, jsxs, Fragment }
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import { FormSwitch as Switch } from '@revenge-mod/discord/design'
|
||||
import { ReactNative } from '@revenge-mod/react'
|
||||
|
||||
import type { DiscordModules } from '@revenge-mod/discord/types'
|
||||
|
||||
/**
|
||||
* A switch component that is styled to match Discord's configuration
|
||||
*/
|
||||
export default function FormSwitch(props: DiscordModules.Components.FormSwitchProps) {
|
||||
export default function FormSwitch(
|
||||
props: DiscordModules.Components.FormSwitchProps,
|
||||
) {
|
||||
return (
|
||||
<ReactNative.View style={props.disabled && styles.disabled}>
|
||||
<Switch {...props} />
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { Design } from '@revenge-mod/discord/design'
|
||||
import type { DiscordModules } from '@revenge-mod/discord/types'
|
||||
import { ReactNative } from '@revenge-mod/react'
|
||||
import type { DiscordModules } from '@revenge-mod/discord/types'
|
||||
|
||||
export default function Page(props: DiscordModules.Components.StackProps) {
|
||||
return (
|
||||
<Design.Stack style={styles.page} spacing={24} {...props}>
|
||||
<Design.Stack spacing={24} style={styles.page} {...props}>
|
||||
{props.children}
|
||||
</Design.Stack>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
import { Design } from '@revenge-mod/discord/design'
|
||||
import { lookupGeneratedIconComponent } from '@revenge-mod/utils/discord'
|
||||
|
||||
import type { DiscordModules } from '@revenge-mod/discord/types'
|
||||
|
||||
const MagnifyingGlassIcon = lookupGeneratedIconComponent('MagnifyingGlassIcon')
|
||||
|
||||
export default function SearchInput(props: DiscordModules.Components.TextInputProps) {
|
||||
export default function SearchInput(
|
||||
props: DiscordModules.Components.TextInputProps,
|
||||
) {
|
||||
return (
|
||||
<Design.TextInput
|
||||
leadingIcon={MagnifyingGlassIcon}
|
||||
size="md"
|
||||
placeholder="Search"
|
||||
returnKeyType="search"
|
||||
size="md"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,20 @@
|
|||
import { getAssetIdByName } from '@revenge-mod/assets'
|
||||
import { Design } from '@revenge-mod/discord/design'
|
||||
|
||||
import type { DiscordModules } from '@revenge-mod/discord/types'
|
||||
|
||||
export default function TableRowAssetIcon(props: TableRowAssetIconProps) {
|
||||
return <Design.TableRow.Icon source={props.name ? getAssetIdByName(props.name)! : props.id!} {...props} />
|
||||
return (
|
||||
<Design.TableRow.Icon
|
||||
source={props.name ? getAssetIdByName(props.name)! : props.id!}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export type TableRowAssetIconProps = Omit<DiscordModules.Components.TableRowIconProps, 'source'> &
|
||||
export type TableRowAssetIconProps = Omit<
|
||||
DiscordModules.Components.TableRowIconProps,
|
||||
'source'
|
||||
> &
|
||||
(
|
||||
| {
|
||||
name: string
|
||||
|
|
|
|||
19
src/index.ts
19
src/index.ts
|
|
@ -4,16 +4,23 @@ import { interceptProperty } from '@revenge-mod/utils/objects'
|
|||
|
||||
// By doing this, we are assuming __METRO_GLOBAL_PREFIX__ is an empty string and globalThis.__d isn't already set.
|
||||
// But both should be true either way.
|
||||
const unintercept = interceptProperty('__d', (_, mDefine) => (unintercept(), patchMetroDefine(mDefine)))
|
||||
const unintercept = interceptProperty(
|
||||
'__d',
|
||||
(_, mDefine) => (unintercept(), patchMetroDefine(mDefine)),
|
||||
)
|
||||
|
||||
import { onModuleFirstRequired, onModuleInitialized } from '@revenge-mod/modules/metro/subscriptions'
|
||||
import {
|
||||
onModuleFirstRequired,
|
||||
onModuleInitialized,
|
||||
} from '@revenge-mod/modules/metro/subscriptions'
|
||||
import { getErrorStack } from '@revenge-mod/utils/errors'
|
||||
|
||||
const IndexModuleId = 0
|
||||
|
||||
onModuleFirstRequired(IndexModuleId, function onIndexRequired() {
|
||||
runCatching(() => {
|
||||
if (__BUILD_FLAG_LOG_PROMISE_REJECTIONS__) require('./patches/log-promise-rejections')
|
||||
if (__BUILD_FLAG_LOG_PROMISE_REJECTIONS__)
|
||||
require('./patches/log-promise-rejections')
|
||||
|
||||
// Initialize preinit libraries
|
||||
require('@revenge-mod/modules/preinit')
|
||||
|
|
@ -44,12 +51,10 @@ function runCatching(fn: () => void) {
|
|||
} catch (e) {
|
||||
// TODO(init): Move to use native provided alert function, which will accept a string stack trace
|
||||
// Above will reduce the need for runCatching() to exist, as the code won't be able to be deduped any further
|
||||
const { ClientInfoModule, DeviceModule } = require('@revenge-mod/discord/native') as
|
||||
// biome-ignore format: Don't format this please
|
||||
typeof import('@revenge-mod/discord/native')
|
||||
const { ClientInfoModule, DeviceModule } =
|
||||
require('@revenge-mod/discord/native') as typeof import('@revenge-mod/discord/native')
|
||||
|
||||
alert(
|
||||
// biome-ignore lint/style/useTemplate: I want this to be readable, thank you
|
||||
`Failed to load Revenge (${__BUILD_VERSION__}-${__BUILD_COMMIT__}-${__BUILD_BRANCH__} (${__BUILD_ENV__}))\n` +
|
||||
`Discord: ${ClientInfoModule.Version} (${ClientInfoModule.Build})\n` +
|
||||
`Device: ${DeviceModule.deviceManufacturer} ${DeviceModule.deviceModel}\n\n` +
|
||||
|
|
|
|||
|
|
@ -16,7 +16,10 @@ Promise._m = (promise, err) => {
|
|||
if (err)
|
||||
setTimeout(
|
||||
() => {
|
||||
if (promise._h === 0) logger.error(`Unhandled promise rejection: ${getErrorStack(err)}`)
|
||||
if (promise._h === 0)
|
||||
logger.error(
|
||||
`Unhandled promise rejection: ${getErrorStack(err)}`,
|
||||
)
|
||||
},
|
||||
ErrorTypeWhitelist.some(it => err instanceof it) ? 0 : 2000,
|
||||
// The time is completely arbitary. I've picked what Hermes chose.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import TableRowAssetIcon from '~/components/TableRowAssetIcon'
|
||||
|
||||
import { MobileSetting, UserSettingsSections } from '../constants'
|
||||
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const AssetBrowserSetting: SettingsItem = {
|
||||
|
|
@ -11,7 +9,8 @@ const AssetBrowserSetting: SettingsItem = {
|
|||
title: () => 'Asset Browser',
|
||||
screen: {
|
||||
route: UserSettingsSections[MobileSetting.ASSET_BROWSER],
|
||||
getComponent: () => require('../screens/AssetBrowserSettingScreen').default,
|
||||
getComponent: () =>
|
||||
require('../screens/AssetBrowserSettingScreen').default,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,11 @@
|
|||
import { React } from '@revenge-mod/react'
|
||||
|
||||
import { AlertActionCreators } from '@revenge-mod/discord/actions'
|
||||
import { Design } from '@revenge-mod/discord/design'
|
||||
|
||||
import { nodeUtil } from '@revenge-mod/externals/browserify'
|
||||
|
||||
import { React } from '@revenge-mod/react'
|
||||
import { getErrorStack } from '@revenge-mod/utils/errors'
|
||||
|
||||
import TableRowAssetIcon from '~/components/TableRowAssetIcon'
|
||||
|
||||
import { api } from '..'
|
||||
import { MobileSetting } from '../constants'
|
||||
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const ALERT_KEY = 'evaluate-javascript'
|
||||
|
|
@ -21,7 +15,8 @@ const EvaluateJavaScriptSetting: SettingsItem = {
|
|||
IconComponent: () => <TableRowAssetIcon name="FileIcon" />,
|
||||
title: () => 'Evaluate JavaScript',
|
||||
useDescription: () => 'Runs a JavaScript code snippet.',
|
||||
onPress: () => AlertActionCreators.openAlert(ALERT_KEY, <EvaluateJavaScriptAlert />),
|
||||
onPress: () =>
|
||||
AlertActionCreators.openAlert(ALERT_KEY, <EvaluateJavaScriptAlert />),
|
||||
type: 'pressable',
|
||||
}
|
||||
|
||||
|
|
@ -47,60 +42,21 @@ function EvaluateJavaScriptAlert() {
|
|||
|
||||
return (
|
||||
<AlertModal
|
||||
title="Evaluate JavaScript"
|
||||
extraContent={
|
||||
<Stack>
|
||||
<TextArea
|
||||
autoFocus
|
||||
label="Code"
|
||||
size="md"
|
||||
placeholder="ReactNative.NativeModules.BundleUpdaterManager.reload()"
|
||||
onChange={v => (code.current = v)}
|
||||
/>
|
||||
<TableRowGroup>
|
||||
<TableSwitchRow
|
||||
label="Await result"
|
||||
subLabel="Wait for the result of the code before displaying it."
|
||||
value={awaitResult}
|
||||
onValueChange={setAwaitResult}
|
||||
/>
|
||||
<TableSwitchRow
|
||||
label="Show hidden"
|
||||
subLabel="Show hidden properties of the object."
|
||||
value={showHidden}
|
||||
onValueChange={setShowHidden}
|
||||
/>
|
||||
<TableRow
|
||||
label="Inspect depth"
|
||||
subLabel="The depth of the object to inspect."
|
||||
trailing={<Text>{inspectDepth}</Text>}
|
||||
/>
|
||||
<Slider
|
||||
startIcon={<Text>1</Text>}
|
||||
endIcon={<Text>10</Text>}
|
||||
step={1}
|
||||
minimumValue={1}
|
||||
maximumValue={10}
|
||||
value={inspectDepth}
|
||||
onValueChange={setInspectDepth}
|
||||
/>
|
||||
</TableRowGroup>
|
||||
</Stack>
|
||||
}
|
||||
actions={
|
||||
<Stack>
|
||||
<Button
|
||||
text="Evaluate"
|
||||
variant="primary"
|
||||
loading={loading}
|
||||
// Async arrow functions are not supported
|
||||
onPress={async function onPress() {
|
||||
setLoading(true)
|
||||
|
||||
try {
|
||||
if (!api) {
|
||||
alert('Unable to provide plugin API. Running snippet in a second...')
|
||||
await new Promise(rs => setTimeout(rs, 1000))
|
||||
alert(
|
||||
'Unable to provide plugin API. Running snippet in a second...',
|
||||
)
|
||||
await new Promise(rs =>
|
||||
setTimeout(rs, 1000),
|
||||
)
|
||||
} else {
|
||||
// @ts-expect-error
|
||||
globalThis.revenge = api.unscoped
|
||||
|
|
@ -113,10 +69,15 @@ function EvaluateJavaScriptAlert() {
|
|||
const res = globalThis.eval?.(code.current)
|
||||
|
||||
alert(
|
||||
nodeUtil.inspect(awaitResult && res instanceof Promise ? await res : res, {
|
||||
depth: inspectDepth,
|
||||
showHidden,
|
||||
}),
|
||||
nodeUtil.inspect(
|
||||
awaitResult && res instanceof Promise
|
||||
? await res
|
||||
: res,
|
||||
{
|
||||
depth: inspectDepth,
|
||||
showHidden,
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
// @ts-expect-error
|
||||
|
|
@ -129,10 +90,53 @@ function EvaluateJavaScriptAlert() {
|
|||
setLoading(false)
|
||||
AlertActionCreators.dismissAlert(ALERT_KEY)
|
||||
}}
|
||||
text="Evaluate"
|
||||
// Async arrow functions are not supported
|
||||
variant="primary"
|
||||
/>
|
||||
<AlertActionButton text="Cancel" variant="secondary" />
|
||||
</Stack>
|
||||
}
|
||||
extraContent={
|
||||
<Stack>
|
||||
<TextArea
|
||||
autoFocus
|
||||
label="Code"
|
||||
onChange={v => (code.current = v)}
|
||||
placeholder="ReactNative.NativeModules.BundleUpdaterManager.reload()"
|
||||
size="md"
|
||||
/>
|
||||
<TableRowGroup>
|
||||
<TableSwitchRow
|
||||
label="Await result"
|
||||
onValueChange={setAwaitResult}
|
||||
subLabel="Wait for the result of the code before displaying it."
|
||||
value={awaitResult}
|
||||
/>
|
||||
<TableSwitchRow
|
||||
label="Show hidden"
|
||||
onValueChange={setShowHidden}
|
||||
subLabel="Show hidden properties of the object."
|
||||
value={showHidden}
|
||||
/>
|
||||
<TableRow
|
||||
label="Inspect depth"
|
||||
subLabel="The depth of the object to inspect."
|
||||
trailing={<Text>{inspectDepth}</Text>}
|
||||
/>
|
||||
<Slider
|
||||
endIcon={<Text>10</Text>}
|
||||
maximumValue={10}
|
||||
minimumValue={1}
|
||||
onValueChange={setInspectDepth}
|
||||
startIcon={<Text>1</Text>}
|
||||
step={1}
|
||||
value={inspectDepth}
|
||||
/>
|
||||
</TableRowGroup>
|
||||
</Stack>
|
||||
}
|
||||
title="Evaluate JavaScript"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,19 @@
|
|||
import { React } from '@revenge-mod/react'
|
||||
|
||||
import TableRowAssetIcon from '~/components/TableRowAssetIcon'
|
||||
|
||||
import { api } from '..'
|
||||
import { MobileSetting } from '../constants'
|
||||
import { DevToolsContext } from '../react-devtools'
|
||||
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const ReactDevToolsAutoConnectSetting: SettingsItem = {
|
||||
parent: MobileSetting.REVENGE_DEVELOPER,
|
||||
IconComponent: () => <TableRowAssetIcon name="LinkIcon" />,
|
||||
title: () => 'Auto-connect to React DevTools',
|
||||
useDescription: () => 'Automatically connect to React DevTools during startup.',
|
||||
useDescription: () =>
|
||||
'Automatically connect to React DevTools during startup.',
|
||||
usePredicate: () => DevToolsContext.available,
|
||||
useValue: () => api.storage.use(s => s.devtools?.autoConnect !== undefined)!.devtools.autoConnect,
|
||||
useValue: () =>
|
||||
api.storage.use(s => s.devtools?.autoConnect !== undefined)!.devtools
|
||||
.autoConnect,
|
||||
onValueChange: v => api.storage.set({ devtools: { autoConnect: v } }),
|
||||
type: 'toggle',
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { React } from '@revenge-mod/react'
|
||||
|
||||
import TableRowAssetIcon from '~/components/TableRowAssetIcon'
|
||||
|
||||
import { MobileSetting } from '../constants'
|
||||
import { DevToolsContext, connectToDevTools, useIsDevToolsOpen } from '../react-devtools'
|
||||
|
||||
import {
|
||||
connectToDevTools,
|
||||
DevToolsContext,
|
||||
useIsDevToolsOpen,
|
||||
} from '../react-devtools'
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const ReactDevToolsConnectSetting: SettingsItem = {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
import { React } from '@revenge-mod/react'
|
||||
|
||||
import TableRowAssetIcon from '~/components/TableRowAssetIcon'
|
||||
|
||||
import { MobileSetting } from '../constants'
|
||||
import { disconnectFromDevTools, useIsDevToolsOpen } from '../react-devtools'
|
||||
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const ReactDevToolsDisconnectSetting: SettingsItem = {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
import { React } from '@revenge-mod/react'
|
||||
|
||||
import TableRowAssetIcon from '~/components/TableRowAssetIcon'
|
||||
|
||||
import { MobileSetting } from '../constants'
|
||||
import { DevToolsContext } from '../react-devtools'
|
||||
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const ReactDevToolsVersionSetting: SettingsItem = {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import TableRowAssetIcon from '~/components/TableRowAssetIcon'
|
||||
|
||||
import { MobileSetting, UserSettingsSections } from '../constants'
|
||||
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const RevengeDeveloperSetting: SettingsItem = {
|
||||
|
|
@ -11,7 +9,8 @@ const RevengeDeveloperSetting: SettingsItem = {
|
|||
title: () => 'Developer',
|
||||
screen: {
|
||||
route: UserSettingsSections[MobileSetting.REVENGE_DEVELOPER],
|
||||
getComponent: () => require('../screens/RevengeDeveloperSettingScreen').default,
|
||||
getComponent: () =>
|
||||
require('../screens/RevengeDeveloperSettingScreen').default,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import {
|
||||
isSettingsModulesLoaded,
|
||||
onSettingsModulesLoaded,
|
||||
} from '@revenge-mod/discord/modules/settings'
|
||||
import { InternalPluginFlags, registerPlugin } from '@revenge-mod/plugins/_'
|
||||
import { PluginFlags } from '@revenge-mod/plugins/constants'
|
||||
|
||||
import { _data } from '@revenge-mod/discord/_/modules/settings'
|
||||
import { isSettingsModulesLoaded, onSettingsModulesLoaded } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
import type { PluginApi } from '@revenge-mod/plugins/types'
|
||||
|
||||
interface Storage {
|
||||
|
|
@ -40,7 +40,10 @@ registerPlugin<Storage>(
|
|||
else onSettingsModulesLoaded(() => require('./register').register())
|
||||
|
||||
api.storage.get().then(settings => {
|
||||
if (settings.devtools.autoConnect) import('./react-devtools').then(it => it.connectToDevTools())
|
||||
if (settings.devtools.autoConnect)
|
||||
import('./react-devtools').then(it =>
|
||||
it.connectToDevTools(),
|
||||
)
|
||||
})
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -20,11 +20,16 @@ export const DevToolsContext: {
|
|||
type Subscription = (event: 1 | 2 | 3, err?: unknown) => void
|
||||
const subscriptions: Set<Subscription> = new Set()
|
||||
|
||||
const CircleXIcon = lookupGeneratedIconComponent('CircleXIcon', 'CircleXIcon-secondary', 'CircleXIcon-primary')
|
||||
const CircleXIcon = lookupGeneratedIconComponent(
|
||||
'CircleXIcon',
|
||||
'CircleXIcon-secondary',
|
||||
'CircleXIcon-primary',
|
||||
)
|
||||
|
||||
subscriptions.add((e, err) => {
|
||||
if (e === 3) {
|
||||
const actualError = (err as { message: string }).message ?? getErrorStack(err)
|
||||
const actualError =
|
||||
(err as { message: string }).message ?? getErrorStack(err)
|
||||
|
||||
api.logger.error('React DevTools error:', actualError)
|
||||
|
||||
|
|
@ -39,7 +44,9 @@ subscriptions.add((e, err) => {
|
|||
export function connectToDevTools() {
|
||||
if (!DevToolsContext.available || DevToolsContext.ws) return
|
||||
|
||||
const websocket = (DevToolsContext.ws = new WebSocket(`ws://${DevToolsContext.address}`))
|
||||
const websocket = (DevToolsContext.ws = new WebSocket(
|
||||
`ws://${DevToolsContext.address}`,
|
||||
))
|
||||
|
||||
websocket.addEventListener('open', () => {
|
||||
DevToolsContext.open = true
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import { addSettingsItemToSection, registerSettingsItems } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
import {
|
||||
addSettingsItemToSection,
|
||||
registerSettingsItems,
|
||||
} from '@revenge-mod/discord/modules/settings'
|
||||
import { api } from '.'
|
||||
import { MobileSetting } from './constants'
|
||||
|
||||
import AssetBrowserSetting from './definitions/AssetBrowserSetting'
|
||||
import EvaluateJavaScriptSetting from './definitions/EvaluateJavaScriptSetting'
|
||||
import ReactDevToolsAutoConnectSetting from './definitions/ReactDevToolsAutoConnectSetting'
|
||||
|
|
@ -10,16 +12,16 @@ import ReactDevToolsDisconnectSetting from './definitions/ReactDevToolsDisconnec
|
|||
import ReactDevToolsVersionSetting from './definitions/ReactDevToolsVersionSetting'
|
||||
import RevengeDeveloperSetting from './definitions/RevengeDeveloperSetting'
|
||||
|
||||
import { api } from '.'
|
||||
|
||||
export function register() {
|
||||
api.cleanup(
|
||||
registerSettingsItems({
|
||||
[MobileSetting.REVENGE_DEVELOPER]: RevengeDeveloperSetting,
|
||||
[MobileSetting.REACT_DEVTOOLS_VERSION]: ReactDevToolsVersionSetting,
|
||||
[MobileSetting.REACT_DEVTOOLS_AUTO_CONNECT]: ReactDevToolsAutoConnectSetting,
|
||||
[MobileSetting.REACT_DEVTOOLS_AUTO_CONNECT]:
|
||||
ReactDevToolsAutoConnectSetting,
|
||||
[MobileSetting.REACT_DEVTOOLS_CONNECT]: ReactDevToolsConnectSetting,
|
||||
[MobileSetting.REACT_DEVTOOLS_DISCONNECT]: ReactDevToolsDisconnectSetting,
|
||||
[MobileSetting.REACT_DEVTOOLS_DISCONNECT]:
|
||||
ReactDevToolsDisconnectSetting,
|
||||
[MobileSetting.EVALUATE_JAVASCRIPT]: EvaluateJavaScriptSetting,
|
||||
[MobileSetting.ASSET_BROWSER]: AssetBrowserSetting,
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -1,15 +1,12 @@
|
|||
import { getAssetId, getAssetModuleId, getAssets } from '@revenge-mod/assets'
|
||||
import { AlertActionCreators } from '@revenge-mod/discord/actions'
|
||||
import { Design } from '@revenge-mod/discord/design'
|
||||
import { Clipboard } from '@revenge-mod/externals/react-native-clipboard'
|
||||
import { FlashList } from '@revenge-mod/externals/shopify'
|
||||
import { React, ReactNative } from '@revenge-mod/react'
|
||||
|
||||
import Page from '~/components/Page'
|
||||
import SearchInput from '~/components/SearchInput'
|
||||
import TableRowAssetIcon from '~/components/TableRowAssetIcon'
|
||||
|
||||
import { getAssetId, getAssetModuleId, getAssets } from '@revenge-mod/assets'
|
||||
|
||||
import type { Asset, AssetId } from '@revenge-mod/assets/types'
|
||||
import type { Metro } from '@revenge-mod/modules/types'
|
||||
|
||||
|
|
@ -28,7 +25,15 @@ const UndisplayableFallback = {
|
|||
export default function AssetBrowserSettingScreen() {
|
||||
const [search, setSearch] = React.useState('')
|
||||
const assets = React.useMemo(
|
||||
() => [...getAssets()].map(asset => [getAssetId(asset)!, getAssetModuleId(asset)!, asset] as const),
|
||||
() =>
|
||||
[...getAssets()].map(
|
||||
asset =>
|
||||
[
|
||||
getAssetId(asset)!,
|
||||
getAssetModuleId(asset)!,
|
||||
asset,
|
||||
] as const,
|
||||
),
|
||||
[getAssets],
|
||||
)
|
||||
|
||||
|
|
@ -36,26 +41,28 @@ export default function AssetBrowserSettingScreen() {
|
|||
() =>
|
||||
!search.length
|
||||
? assets
|
||||
: assets.filter(([, , asset]) => asset.name.toLowerCase().includes(search.toLowerCase())),
|
||||
: assets.filter(([, , asset]) =>
|
||||
asset.name.toLowerCase().includes(search.toLowerCase()),
|
||||
),
|
||||
[assets, search],
|
||||
)
|
||||
|
||||
return (
|
||||
<Page spacing={16}>
|
||||
<SearchInput size="md" onChange={(v: string) => setSearch(v)} />
|
||||
<SearchInput onChange={(v: string) => setSearch(v)} size="md" />
|
||||
<FlashList.FlashList
|
||||
data={filteredAssets}
|
||||
estimatedItemSize={80}
|
||||
keyExtractor={([id]) => id.toString()}
|
||||
renderItem={({ item: [id, moduleId, asset], index }) => (
|
||||
<AssetDisplay
|
||||
start={!index}
|
||||
asset={asset}
|
||||
end={index === assets.length - 1}
|
||||
id={id}
|
||||
moduleId={moduleId}
|
||||
asset={asset}
|
||||
start={!index}
|
||||
/>
|
||||
)}
|
||||
estimatedItemSize={80}
|
||||
/>
|
||||
</Page>
|
||||
)
|
||||
|
|
@ -83,26 +90,30 @@ function AssetDisplay({
|
|||
|
||||
return (
|
||||
<TableRow
|
||||
start={start}
|
||||
end={end}
|
||||
variant={isDisplayable ? 'default' : 'danger'}
|
||||
label={asset.name}
|
||||
subLabel={metadata.map(([name, value]) => `${name}: ${value}`).join(' • ')}
|
||||
icon={
|
||||
isDisplayable ? (
|
||||
<Image source={id} style={styles.smallPreview} />
|
||||
) : (
|
||||
<TableRowAssetIcon
|
||||
variant="danger"
|
||||
name={
|
||||
asset.type in UndisplayableFallback
|
||||
? UndisplayableFallback[asset.type as keyof typeof UndisplayableFallback]
|
||||
? UndisplayableFallback[
|
||||
asset.type as keyof typeof UndisplayableFallback
|
||||
]
|
||||
: UndisplayableFallback.default
|
||||
}
|
||||
variant="danger"
|
||||
/>
|
||||
)
|
||||
}
|
||||
label={asset.name}
|
||||
onPress={() => openAssetDisplayAlert(asset, id, metadata)}
|
||||
start={start}
|
||||
subLabel={metadata
|
||||
.map(([name, value]) => `${name}: ${value}`)
|
||||
.join(' • ')}
|
||||
variant={isDisplayable ? 'default' : 'danger'}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
@ -117,7 +128,7 @@ function openAssetDisplayAlert(
|
|||
AlertActionCreators.openAlert(
|
||||
'asset-display',
|
||||
<AlertModal
|
||||
title={asset.name}
|
||||
actions={<AlertActionButton text="Close" variant="secondary" />}
|
||||
extraContent={
|
||||
<>
|
||||
{isDisplayable ? (
|
||||
|
|
@ -130,25 +141,30 @@ function openAssetDisplayAlert(
|
|||
<TableRow
|
||||
key={id}
|
||||
label={name}
|
||||
subLabel={value}
|
||||
onPress={() => Clipboard.setString(value)}
|
||||
subLabel={value}
|
||||
/>
|
||||
))}
|
||||
</TableRowGroup>
|
||||
<Text variant="text-xs/semibold" color="text-danger">
|
||||
Note: Asset IDs and module IDs are not consistent between app launches and app versions
|
||||
respectively and should only be used when absolutely needed.
|
||||
<Text color="text-danger" variant="text-xs/semibold">
|
||||
Note: Asset IDs and module IDs are not consistent
|
||||
between app launches and app versions respectively and
|
||||
should only be used when absolutely needed.
|
||||
</Text>
|
||||
</>
|
||||
}
|
||||
actions={<AlertActionButton text="Close" variant="secondary" />}
|
||||
title={asset.name}
|
||||
/>,
|
||||
)
|
||||
}
|
||||
|
||||
function PreviewUnavailable({ type }: { type: string }) {
|
||||
return (
|
||||
<Text variant="text-sm/medium" color="text-danger" style={styles.centeredText}>
|
||||
<Text
|
||||
color="text-danger"
|
||||
style={styles.centeredText}
|
||||
variant="text-sm/medium"
|
||||
>
|
||||
Asset type {type.toUpperCase()} is not supported for preview.
|
||||
</Text>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { ToastActionCreators } from '@revenge-mod/discord/actions'
|
||||
import { Design } from '@revenge-mod/discord/design'
|
||||
import { SettingListRenderer } from '@revenge-mod/discord/modules/settings/renderer'
|
||||
import { React, ReactNative } from '@revenge-mod/react'
|
||||
import { ReactNative } from '@revenge-mod/react'
|
||||
import { lookupGeneratedIconComponent } from '@revenge-mod/utils/discord'
|
||||
|
||||
import { api } from '..'
|
||||
import { MobileSetting } from '../constants'
|
||||
import { DevToolsContext, useIsDevToolsOpen } from '../react-devtools'
|
||||
|
|
@ -34,7 +33,10 @@ export default function RevengeDeveloperSettingScreen() {
|
|||
},
|
||||
{
|
||||
label: 'Tools',
|
||||
settings: [MobileSetting.EVALUATE_JAVASCRIPT, MobileSetting.ASSET_BROWSER],
|
||||
settings: [
|
||||
MobileSetting.EVALUATE_JAVASCRIPT,
|
||||
MobileSetting.ASSET_BROWSER,
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
|
@ -49,12 +51,13 @@ export function ReactDevToolsAddressSetting() {
|
|||
return (
|
||||
<ReactNative.View style={{ paddingHorizontal: 16, paddingTop: 16 }}>
|
||||
<Design.TextInput
|
||||
defaultValue={
|
||||
settings?.devtools.address ?? DevToolsContext.address
|
||||
}
|
||||
editable={!open}
|
||||
isDisabled={open}
|
||||
leadingText="ws://"
|
||||
defaultValue={settings?.devtools.address ?? DevToolsContext.address}
|
||||
label="React DevTools"
|
||||
onChange={text => (DevToolsContext.address = text)}
|
||||
leadingText="ws://"
|
||||
onBlur={() => {
|
||||
api.storage
|
||||
.set({
|
||||
|
|
@ -70,6 +73,7 @@ export function ReactDevToolsAddressSetting() {
|
|||
})
|
||||
})
|
||||
}}
|
||||
onChange={text => (DevToolsContext.address = text)}
|
||||
returnKeyType="done"
|
||||
/>
|
||||
</ReactNative.View>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
import { React } from '@revenge-mod/react'
|
||||
|
||||
import TableRowAssetIcon from '~/components/TableRowAssetIcon'
|
||||
|
||||
import { MobileSetting } from '../constants'
|
||||
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
// @ts-expect-error
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
import { React, ReactNative } from '@revenge-mod/react'
|
||||
|
||||
import { ReactNative } from '@revenge-mod/react'
|
||||
import TableRowAssetIcon from '~/components/TableRowAssetIcon'
|
||||
|
||||
import { MobileSetting } from '../constants'
|
||||
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const { major, minor, patch, prerelease } = ReactNative.Platform.constants.reactNativeVersion
|
||||
const { major, minor, patch, prerelease } =
|
||||
ReactNative.Platform.constants.reactNativeVersion
|
||||
const version = `${major}.${minor}.${patch}${prerelease ? `-${prerelease}` : ''}`
|
||||
|
||||
const ReactNativeVersionSetting: SettingsItem = {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
import { React } from '@revenge-mod/react'
|
||||
|
||||
import { MobileSetting } from '../constants'
|
||||
|
||||
import TableRowAssetIcon from '~/components/TableRowAssetIcon'
|
||||
|
||||
import { MobileSetting } from '../constants'
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const ReactVersionSetting: SettingsItem = {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
import { BundleUpdaterManager } from '@revenge-mod/discord/native'
|
||||
import { React } from '@revenge-mod/react'
|
||||
|
||||
import TableRowAssetIcon from '~/components/TableRowAssetIcon'
|
||||
|
||||
import { MobileSetting } from '../constants'
|
||||
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const ReloadAppSetting: SettingsItem = {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { MobileSetting, UserSettingsSections } from '../constants'
|
||||
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const RevengeCustomPageSetting: SettingsItem = {
|
||||
|
|
@ -9,7 +8,8 @@ const RevengeCustomPageSetting: SettingsItem = {
|
|||
unsearchable: true,
|
||||
screen: {
|
||||
route: UserSettingsSections[MobileSetting.REVENGE_CUSTOM_PAGE],
|
||||
getComponent: () => require('../screens/RevengeCustomPageScreen').default,
|
||||
getComponent: () =>
|
||||
require('../screens/RevengeCustomPageScreen').default,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
import { React, ReactNative } from '@revenge-mod/react'
|
||||
|
||||
import { ReactNative } from '@revenge-mod/react'
|
||||
import TableRowAssetIcon from '~/components/TableRowAssetIcon'
|
||||
import { DiscordServerURL } from '~/constants'
|
||||
|
||||
import { MobileSetting } from '../constants'
|
||||
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const RevengeDiscordServerSetting: SettingsItem = {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import TableRowAssetIcon from '~/components/TableRowAssetIcon'
|
||||
|
||||
import { MobileSetting, UserSettingsSections } from '../constants'
|
||||
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const RevengeFontsSetting: SettingsItem = {
|
||||
|
|
@ -11,7 +9,8 @@ const RevengeFontsSetting: SettingsItem = {
|
|||
title: () => 'Fonts',
|
||||
screen: {
|
||||
route: UserSettingsSections[MobileSetting.REVENGE_PLUGINS],
|
||||
getComponent: () => require('../screens/RevengePluginsSettingScreen').default,
|
||||
getComponent: () =>
|
||||
require('../screens/RevengePluginsSettingScreen').default,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
import { React, ReactNative } from '@revenge-mod/react'
|
||||
|
||||
import { ReactNative } from '@revenge-mod/react'
|
||||
import TableRowAssetIcon from '~/components/TableRowAssetIcon'
|
||||
import { GitHubOrganizationURL } from '~/constants'
|
||||
|
||||
import { MobileSetting } from '../constants'
|
||||
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const RevengeGitHubOrganizationSetting: SettingsItem = {
|
||||
parent: MobileSetting.REVENGE,
|
||||
IconComponent: () => <TableRowAssetIcon name="img_account_sync_github_white" />,
|
||||
IconComponent: () => (
|
||||
<TableRowAssetIcon name="img_account_sync_github_white" />
|
||||
),
|
||||
title: () => 'GitHub',
|
||||
onPress: () => ReactNative.Linking.openURL(GitHubOrganizationURL),
|
||||
type: 'pressable',
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import { React } from '@revenge-mod/react'
|
||||
|
||||
import TableRowAssetIcon from '~/components/TableRowAssetIcon'
|
||||
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const RevengeNotImplementedSetting: SettingsItem = {
|
||||
parent: null,
|
||||
IconComponent: () => <TableRowAssetIcon name="CircleXIcon-primary" variant="danger" />,
|
||||
IconComponent: () => (
|
||||
<TableRowAssetIcon name="CircleXIcon-primary" variant="danger" />
|
||||
),
|
||||
title: () => 'Not Implemented',
|
||||
useDescription: () => 'This feature is not implemented yet. Check back later!',
|
||||
useDescription: () =>
|
||||
'This feature is not implemented yet. Check back later!',
|
||||
variant: 'danger',
|
||||
type: 'pressable',
|
||||
unsearchable: true,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
import { _plugins } from '@revenge-mod/plugins/_'
|
||||
import { PluginFlags } from '@revenge-mod/plugins/constants'
|
||||
|
||||
import TableRowAssetIcon from '~/components/TableRowAssetIcon'
|
||||
|
||||
import { MobileSetting, UserSettingsSections } from '../constants'
|
||||
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const RevengePluginsSetting: SettingsItem = {
|
||||
|
|
@ -14,12 +11,14 @@ const RevengePluginsSetting: SettingsItem = {
|
|||
title: () => 'Plugins',
|
||||
useTrailing: () => {
|
||||
let count = 0
|
||||
for (const plugin of _plugins.values()) if (plugin.flags & PluginFlags.Enabled) count++
|
||||
for (const plugin of _plugins.values())
|
||||
if (plugin.flags & PluginFlags.Enabled) count++
|
||||
return `${count} enabled`
|
||||
},
|
||||
screen: {
|
||||
route: UserSettingsSections[MobileSetting.REVENGE_PLUGINS],
|
||||
getComponent: () => require('../screens/RevengePluginsSettingScreen').default,
|
||||
getComponent: () =>
|
||||
require('../screens/RevengePluginsSettingScreen').default,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import TableRowAssetIcon from '~/components/TableRowAssetIcon'
|
||||
import RevengeIcon from '~assets/RevengeIcon'
|
||||
|
||||
import { MobileSetting, UserSettingsSections } from '../constants'
|
||||
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const RevengeSetting: SettingsItem = {
|
||||
|
|
@ -10,7 +8,8 @@ const RevengeSetting: SettingsItem = {
|
|||
type: 'route',
|
||||
IconComponent: () => <TableRowAssetIcon id={RevengeIcon} />,
|
||||
title: () => 'Revenge',
|
||||
useTrailing: () => `${__BUILD_VERSION__}-${__BUILD_COMMIT__}-${__BUILD_BRANCH__}`,
|
||||
useTrailing: () =>
|
||||
`${__BUILD_VERSION__}-${__BUILD_COMMIT__}-${__BUILD_BRANCH__}`,
|
||||
screen: {
|
||||
route: UserSettingsSections[MobileSetting.REVENGE],
|
||||
getComponent: () => require('../screens/RevengeSettingScreen').default,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import TableRowAssetIcon from '~/components/TableRowAssetIcon'
|
||||
|
||||
import { MobileSetting, UserSettingsSections } from '../constants'
|
||||
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const RevengeThemesSetting: SettingsItem = {
|
||||
|
|
@ -11,7 +9,8 @@ const RevengeThemesSetting: SettingsItem = {
|
|||
title: () => 'Themes',
|
||||
screen: {
|
||||
route: UserSettingsSections[MobileSetting.REVENGE_THEMES],
|
||||
getComponent: () => require('../screens/RevengeThemesSettingScreen').default,
|
||||
getComponent: () =>
|
||||
require('../screens/RevengeThemesSettingScreen').default,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
import TableRowAssetIcon from '~/components/TableRowAssetIcon'
|
||||
import RevengeIcon from '~assets/RevengeIcon'
|
||||
|
||||
import { MobileSetting } from '../constants'
|
||||
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const RevengeVersionSetting: SettingsItem = {
|
||||
parent: MobileSetting.REVENGE,
|
||||
IconComponent: () => <TableRowAssetIcon id={RevengeIcon} />,
|
||||
title: () => 'Revenge',
|
||||
useDescription: () => `${__BUILD_VERSION__}-${__BUILD_COMMIT__}-${__BUILD_BRANCH__} (${__BUILD_ENV__})`,
|
||||
useDescription: () =>
|
||||
`${__BUILD_VERSION__}-${__BUILD_COMMIT__}-${__BUILD_BRANCH__} (${__BUILD_ENV__})`,
|
||||
type: 'static',
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,11 @@
|
|||
import { InternalPluginFlags, registerPlugin } from '@revenge-mod/plugins/_'
|
||||
import { PluginFlags } from '@revenge-mod/plugins/constants'
|
||||
|
||||
import { _data, _subs } from '@revenge-mod/discord/_/modules/settings'
|
||||
import { onSettingsModulesLoaded } from '@revenge-mod/discord/modules/settings'
|
||||
import { byName, byProps } from '@revenge-mod/modules/finders/filters'
|
||||
import { waitForModules } from '@revenge-mod/modules/finders/wait'
|
||||
|
||||
import { _data, _subs } from '@revenge-mod/discord/_/modules/settings'
|
||||
import { type SettingsItem, onSettingsModulesLoaded } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
import { after } from '@revenge-mod/patcher'
|
||||
|
||||
import { InternalPluginFlags, registerPlugin } from '@revenge-mod/plugins/_'
|
||||
import { PluginFlags } from '@revenge-mod/plugins/constants'
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
import type { FC } from 'react'
|
||||
|
||||
onSettingsModulesLoaded(() => require('./register'))
|
||||
|
|
@ -30,29 +27,39 @@ registerPlugin(
|
|||
SettingRendererConfig => {
|
||||
unsubForRendererConfig()
|
||||
|
||||
logger.info('Settings modules loaded, running subscriptions and patching...')
|
||||
logger.info(
|
||||
'Settings modules loaded, running subscriptions and patching...',
|
||||
)
|
||||
|
||||
for (const sub of _subs)
|
||||
try {
|
||||
sub()
|
||||
} catch (e) {
|
||||
logger.error('Failed to run settings modules subscription', e)
|
||||
logger.error(
|
||||
'Failed to run settings modules subscription',
|
||||
e,
|
||||
)
|
||||
}
|
||||
|
||||
// We don't ever need to call this again
|
||||
_subs.clear()
|
||||
_data[2] = true
|
||||
|
||||
let ORIGINAL_RENDERER_CONFIG = SettingRendererConfig.SETTING_RENDERER_CONFIG
|
||||
let ORIGINAL_RENDERER_CONFIG =
|
||||
SettingRendererConfig.SETTING_RENDERER_CONFIG
|
||||
|
||||
Object.defineProperty(SettingRendererConfig, 'SETTING_RENDERER_CONFIG', {
|
||||
get: () =>
|
||||
({
|
||||
...ORIGINAL_RENDERER_CONFIG,
|
||||
..._data[1],
|
||||
}) as Record<string, SettingsItem>,
|
||||
set: v => (ORIGINAL_RENDERER_CONFIG = v),
|
||||
})
|
||||
Object.defineProperty(
|
||||
SettingRendererConfig,
|
||||
'SETTING_RENDERER_CONFIG',
|
||||
{
|
||||
get: () =>
|
||||
({
|
||||
...ORIGINAL_RENDERER_CONFIG,
|
||||
..._data[1],
|
||||
}) as Record<string, SettingsItem>,
|
||||
set: v => (ORIGINAL_RENDERER_CONFIG = v),
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
|
|
@ -76,14 +83,17 @@ registerPlugin(
|
|||
}
|
||||
|
||||
// Check if we even have custom sections
|
||||
const firstCustomSection = customSections[Object.keys(customSections)[0]]
|
||||
const firstCustomSection =
|
||||
customSections[Object.keys(customSections)[0]]
|
||||
if (!firstCustomSection) return tree
|
||||
|
||||
// Check if sections are already spliced
|
||||
const firstCustomItem = firstCustomSection.settings[0]
|
||||
if (
|
||||
sections.findIndex(section =>
|
||||
section.settings.some(setting => setting === firstCustomItem),
|
||||
section.settings.some(
|
||||
setting => setting === firstCustomItem,
|
||||
),
|
||||
) !== -1
|
||||
)
|
||||
return tree
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { registerSettingsItems, registerSettingsSection } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
import {
|
||||
registerSettingsItems,
|
||||
registerSettingsSection,
|
||||
} from '@revenge-mod/discord/modules/settings'
|
||||
import { MobileSetting } from './constants'
|
||||
|
||||
import HermesVersionSetting from './definitions/HermesVersionSetting'
|
||||
import ReactNativeVersionSetting from './definitions/ReactNativeVersionSetting'
|
||||
// import RevengeThemesSetting from './definitions/RevengeThemesSetting'
|
||||
|
|
@ -22,7 +23,8 @@ registerSettingsItems({
|
|||
// [MobileSetting.REVENGE_THEMES]: RevengeThemesSetting,
|
||||
// [MobileSetting.REVENGE_FONTS]: RevengeFontsSetting,
|
||||
[MobileSetting.REVENGE_CUSTOM_PAGE]: RevengeCustomPageSetting,
|
||||
[MobileSetting.REVENGE_GITHUB_ORGANIZATION]: RevengeGitHubOrganizationSetting,
|
||||
[MobileSetting.REVENGE_GITHUB_ORGANIZATION]:
|
||||
RevengeGitHubOrganizationSetting,
|
||||
[MobileSetting.REVENGE_DISCORD_SERVER]: RevengeDiscordServerSetting,
|
||||
[MobileSetting.RELOAD_APP]: ReloadAppSetting,
|
||||
[MobileSetting.REVENGE_VERSION]: RevengeVersionSetting,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import { React } from '@revenge-mod/react'
|
||||
|
||||
import type { StackScreenProps } from '@react-navigation/stack'
|
||||
import type { ReactNavigationParamList } from '@revenge-mod/externals/react-navigation'
|
||||
|
||||
import type { MobileSetting, UserSettingsSections } from '../constants'
|
||||
|
||||
export default function RevengeCustomPageScreen({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { SettingListRenderer } from '@revenge-mod/discord/modules/settings/renderer'
|
||||
|
||||
import { MobileSetting } from '../constants'
|
||||
|
||||
export default function RevengeFontsSettingScreen() {
|
||||
|
|
|
|||
|
|
@ -1,25 +1,23 @@
|
|||
import { getAssetIdByName } from '@revenge-mod/assets'
|
||||
import { Tokens } from '@revenge-mod/discord/common'
|
||||
import { Design } from '@revenge-mod/discord/design'
|
||||
import { FlashList } from '@revenge-mod/externals/shopify'
|
||||
import {
|
||||
type InternalPlugin,
|
||||
InternalPluginFlags,
|
||||
_metas,
|
||||
_plugins,
|
||||
enablePlugin,
|
||||
InternalPluginFlags,
|
||||
initPlugin,
|
||||
preInitPlugin,
|
||||
startPlugin,
|
||||
} from '@revenge-mod/plugins/_'
|
||||
import { PluginFlags } from '@revenge-mod/plugins/constants'
|
||||
import { React, ReactNative } from '@revenge-mod/react'
|
||||
|
||||
import { useReRender } from '@revenge-mod/utils/react'
|
||||
import FormSwitch from '~/components/FormSwitch'
|
||||
import Page from '~/components/Page'
|
||||
import SearchInput from '~/components/SearchInput'
|
||||
|
||||
import { getAssetIdByName } from '@revenge-mod/assets'
|
||||
import { Tokens } from '@revenge-mod/discord/common'
|
||||
import { PluginFlags } from '@revenge-mod/plugins/constants'
|
||||
import { useReRender } from '@revenge-mod/utils/react'
|
||||
import type { InternalPlugin } from '@revenge-mod/plugins/_'
|
||||
|
||||
const { Image, useWindowDimensions } = ReactNative
|
||||
const { Card, Text, Stack } = Design
|
||||
|
|
@ -31,7 +29,10 @@ export default function RevengePluginsSettingScreen() {
|
|||
const numColumns = Math.floor((width - 16) / 448)
|
||||
|
||||
const plugins = React.useMemo(
|
||||
() => [..._plugins.values()].map(plugin => [plugin, _metas.get(plugin.manifest.id)![2]] as const),
|
||||
() =>
|
||||
[..._plugins.values()].map(
|
||||
plugin => [plugin, _metas.get(plugin.manifest.id)![2]] as const,
|
||||
),
|
||||
[_plugins],
|
||||
)
|
||||
|
||||
|
|
@ -51,16 +52,16 @@ export default function RevengePluginsSettingScreen() {
|
|||
|
||||
return (
|
||||
<Page spacing={16}>
|
||||
<SearchInput size="md" onChange={(v: string) => setSearch(v)} />
|
||||
<SearchInput onChange={(v: string) => setSearch(v)} size="md" />
|
||||
<FlashList.MasonryFlashList
|
||||
data={filteredPlugins}
|
||||
numColumns={numColumns}
|
||||
estimatedItemSize={108}
|
||||
numColumns={numColumns}
|
||||
renderItem={({ item: [plugin, iflags], columnIndex }) => (
|
||||
<InstalledPluginCard
|
||||
iflags={iflags}
|
||||
key={plugin.manifest.id}
|
||||
plugin={plugin}
|
||||
iflags={iflags}
|
||||
rightGap={columnIndex + 1 < numColumns}
|
||||
/>
|
||||
)}
|
||||
|
|
@ -103,7 +104,11 @@ function InstalledPluginCard({
|
|||
plugin,
|
||||
iflags,
|
||||
rightGap,
|
||||
}: { plugin: InternalPlugin; iflags: number; rightGap?: boolean }) {
|
||||
}: {
|
||||
plugin: InternalPlugin
|
||||
iflags: number
|
||||
rightGap?: boolean
|
||||
}) {
|
||||
const {
|
||||
manifest: { name, description, author, icon },
|
||||
flags,
|
||||
|
|
@ -116,14 +121,23 @@ function InstalledPluginCard({
|
|||
|
||||
return (
|
||||
<Card style={[styles.card, rightGap && styles.rightGap]}>
|
||||
<Stack direction="horizontal" style={[styles.grow, styles.topContainer]}>
|
||||
<Stack spacing={8} direction="horizontal" style={[styles.topContainer, styles.autoSize]}>
|
||||
<Image source={getAssetIdByName(icon ?? 'PuzzlePieceIcon')!} style={styles.icon} />
|
||||
<Stack
|
||||
direction="horizontal"
|
||||
style={[styles.grow, styles.topContainer]}
|
||||
>
|
||||
<Stack
|
||||
direction="horizontal"
|
||||
spacing={8}
|
||||
style={[styles.topContainer, styles.autoSize]}
|
||||
>
|
||||
<Image
|
||||
source={getAssetIdByName(icon ?? 'PuzzlePieceIcon')!}
|
||||
style={styles.icon}
|
||||
/>
|
||||
<Text variant="heading-lg/semibold">{name}</Text>
|
||||
</Stack>
|
||||
<FormSwitch
|
||||
disabled={essential}
|
||||
value={enabled}
|
||||
// biome-ignore lint/complexity/useArrowFunction: Async arrows are not supported
|
||||
onValueChange={async function (v) {
|
||||
if (v) {
|
||||
|
|
@ -139,10 +153,15 @@ function InstalledPluginCard({
|
|||
// make an event based system for this, so we can register a listener for when plugins are disabled
|
||||
// and check its flags afterwards
|
||||
}}
|
||||
value={enabled}
|
||||
/>
|
||||
</Stack>
|
||||
<Stack spacing={4} style={[styles.alignedContainer, styles.grow]}>
|
||||
<Text style={styles.grow} variant="heading-md/medium" color="text-muted">
|
||||
<Text
|
||||
color="text-muted"
|
||||
style={styles.grow}
|
||||
variant="heading-md/medium"
|
||||
>
|
||||
by {author}
|
||||
</Text>
|
||||
<Text style={styles.grow} variant="text-md/medium">
|
||||
|
|
|
|||
|
|
@ -18,8 +18,12 @@ export default function RevengeSettingScreen() {
|
|||
<Design.Text variant="text-xs/medium">
|
||||
You are using the next version of Revenge!
|
||||
</Design.Text>
|
||||
<Design.Text variant="text-xs/semibold" color="text-danger">
|
||||
This version is experimental and may be unstable.
|
||||
<Design.Text
|
||||
color="text-danger"
|
||||
variant="text-xs/semibold"
|
||||
>
|
||||
This version is experimental and may be
|
||||
unstable.
|
||||
</Design.Text>
|
||||
</>
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { SettingListRenderer } from '@revenge-mod/discord/modules/settings/renderer'
|
||||
|
||||
import { MobileSetting } from '../constants'
|
||||
|
||||
export default function RevengeThemesSettingScreen() {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
import { AlertActionCreators } from '@revenge-mod/discord/actions'
|
||||
import { Stores, getStore } from '@revenge-mod/discord/common/flux'
|
||||
import { getStore, Stores } from '@revenge-mod/discord/common/flux'
|
||||
import { Design } from '@revenge-mod/discord/design'
|
||||
|
||||
import { byProps } from '@revenge-mod/modules/finders/filters'
|
||||
import { getModule } from '@revenge-mod/modules/finders/get'
|
||||
|
||||
import { instead } from '@revenge-mod/patcher'
|
||||
|
||||
import { InternalPluginFlags, registerPlugin } from '@revenge-mod/plugins/_'
|
||||
import { PluginFlags } from '@revenge-mod/plugins/constants'
|
||||
|
||||
|
|
@ -24,15 +21,24 @@ registerPlugin(
|
|||
|
||||
function reinitDEStore() {
|
||||
cleanup(
|
||||
getStore<{ initialize(): void }>('DeveloperExperimentStore', store => {
|
||||
logger.log('Reinitializing DeveloperExperimentStore to apply changes...')
|
||||
getStore<{ initialize(): void }>(
|
||||
'DeveloperExperimentStore',
|
||||
store => {
|
||||
logger.log(
|
||||
'Reinitializing DeveloperExperimentStore to apply changes...',
|
||||
)
|
||||
|
||||
setTimeout(() => {
|
||||
const unpatch = instead(Object, 'defineProperties', () => {})
|
||||
store.initialize()
|
||||
unpatch()
|
||||
})
|
||||
}),
|
||||
setTimeout(() => {
|
||||
const unpatch = instead(
|
||||
Object,
|
||||
'defineProperties',
|
||||
() => {},
|
||||
)
|
||||
store.initialize()
|
||||
unpatch()
|
||||
})
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -41,7 +47,12 @@ registerPlugin(
|
|||
logger.log('Patching UserStoreUtils...')
|
||||
|
||||
cleanup(
|
||||
instead(UserStoreUtils, 'isStaffEnv', ([user]) => user === Stores.UserStore.getCurrentUser()),
|
||||
instead(
|
||||
UserStoreUtils,
|
||||
'isStaffEnv',
|
||||
([user]) =>
|
||||
user === Stores.UserStore.getCurrentUser(),
|
||||
),
|
||||
)
|
||||
|
||||
reinitDEStore()
|
||||
|
|
@ -59,9 +70,11 @@ function showNoticeAlert() {
|
|||
AlertActionCreators.openAlert(
|
||||
'staff-settings-notice',
|
||||
<Design.AlertModal
|
||||
title="Notice"
|
||||
actions={
|
||||
<Design.AlertActionButton text="Got it" variant="secondary" />
|
||||
}
|
||||
content="Close and open the Settings page again to apply changes."
|
||||
actions={<Design.AlertActionButton variant="secondary" text="Got it" />}
|
||||
title="Notice"
|
||||
/>,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue