revenge-bundle-next/src/plugins/developer-settings/definitions/EvaluateJavaScriptSetting.tsx

139 lines
5.3 KiB
TypeScript

import { React } from '@revenge-mod/react'
import { AlertActionCreators } from '@revenge-mod/discord/actions'
import components from '@revenge-mod/discord/design/components'
import { nodeUtil } from '@revenge-mod/externals/browserify'
import { getErrorStack } from '@revenge-mod/utils/errors'
import TableRowAssetIcon from '~/components/TableRowAssetIcon'
import { pluginApi } from '..'
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
const ALERT_KEY = 'evaluate-javascript'
const EvaluateJavaScriptSetting: SettingsItem = {
parent: null,
IconComponent: () => <TableRowAssetIcon name="FileIcon" />,
title: () => 'Evaluate JavaScript',
useDescription: () => 'Runs a JavaScript code snippet.',
onPress: () => AlertActionCreators.openAlert(ALERT_KEY, <EvaluateJavaScriptAlert />),
type: 'pressable',
}
const {
AlertActionButton,
AlertModal,
Button,
Slider,
Stack,
TableRow,
TableRowGroup,
TableSwitchRow,
Text,
TextArea,
} = components
function EvaluateJavaScriptAlert() {
const code = React.useRef('')
const [awaitResult, setAwaitResult] = React.useState(true)
const [showHidden, setShowHidden] = React.useState(true)
const [inspectDepth, setInspectDepth] = React.useState(3)
const [loading, setLoading] = React.useState(false)
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 (!pluginApi) {
alert('Unable to provide plugin API. Running snippet in a second...')
await new Promise(rs => setTimeout(rs, 1000))
} else {
// @ts-expect-error
globalThis.revenge = pluginApi.unscoped
// @ts-expect-error
globalThis.api = pluginApi
}
// Do a no-local-scope-access eval
// biome-ignore lint/security/noGlobalEval: This is intentional
const res = globalThis.eval?.(code.current)
alert(
nodeUtil.inspect(awaitResult && res instanceof Promise ? await res : res, {
depth: inspectDepth,
showHidden,
}),
)
// @ts-expect-error
// biome-ignore lint/performance/noDelete: This is fine.
delete globalThis.revenge, delete globalThis.api
} catch (e) {
alert(getErrorStack(e))
}
setLoading(false)
AlertActionCreators.dismissAlert(ALERT_KEY)
}}
/>
<AlertActionButton text="Cancel" variant="secondary" />
</Stack>
}
/>
)
}
export default EvaluateJavaScriptSetting