2025-05-05 19:13:01 +07:00
|
|
|
import { ToastActionCreators } from '@revenge-mod/discord/actions'
|
2025-05-19 01:43:41 +07:00
|
|
|
import { lookupGeneratedIconComponent } from '@revenge-mod/utils/discord'
|
2025-05-05 19:13:01 +07:00
|
|
|
import { getErrorStack } from '@revenge-mod/utils/errors'
|
|
|
|
|
import { useReRender } from '@revenge-mod/utils/react'
|
2025-06-08 19:01:33 +07:00
|
|
|
import { useEffect } from 'react'
|
2025-05-18 22:24:48 +07:00
|
|
|
import { api } from '.'
|
2025-05-05 19:13:01 +07:00
|
|
|
|
|
|
|
|
export const DevToolsContext: {
|
|
|
|
|
ws: WebSocket | undefined
|
|
|
|
|
address: string
|
|
|
|
|
open: boolean
|
|
|
|
|
available: boolean
|
|
|
|
|
} = {
|
|
|
|
|
available: Boolean(globalThis.__REACT_DEVTOOLS__),
|
|
|
|
|
ws: undefined,
|
|
|
|
|
address: 'localhost:8097',
|
|
|
|
|
open: false,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Subscription = (event: 1 | 2 | 3, err?: unknown) => void
|
|
|
|
|
const subscriptions: Set<Subscription> = new Set()
|
|
|
|
|
|
2025-06-03 16:24:58 +07:00
|
|
|
const CircleXIcon = lookupGeneratedIconComponent(
|
|
|
|
|
'CircleXIcon',
|
|
|
|
|
'CircleXIcon-secondary',
|
|
|
|
|
'CircleXIcon-primary',
|
|
|
|
|
)
|
2025-05-10 02:11:44 +07:00
|
|
|
|
2025-05-05 19:13:01 +07:00
|
|
|
subscriptions.add((e, err) => {
|
|
|
|
|
if (e === 3) {
|
2025-06-03 16:24:58 +07:00
|
|
|
const actualError =
|
|
|
|
|
(err as { message: string }).message ?? getErrorStack(err)
|
2025-05-05 19:13:01 +07:00
|
|
|
|
2025-05-18 22:24:48 +07:00
|
|
|
api.logger.error('React DevTools error:', actualError)
|
2025-05-10 02:11:44 +07:00
|
|
|
|
2025-05-05 19:13:01 +07:00
|
|
|
ToastActionCreators.open({
|
2025-05-22 02:48:54 +07:00
|
|
|
key: 'REACT_DEVTOOLS_ERROR',
|
2025-05-10 02:11:44 +07:00
|
|
|
IconComponent: CircleXIcon,
|
2025-05-05 19:13:01 +07:00
|
|
|
content: actualError,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
export function connectToDevTools() {
|
|
|
|
|
if (!DevToolsContext.available || DevToolsContext.ws) return
|
|
|
|
|
|
2025-06-03 16:24:58 +07:00
|
|
|
const websocket = (DevToolsContext.ws = new WebSocket(
|
|
|
|
|
`ws://${DevToolsContext.address}`,
|
|
|
|
|
))
|
2025-05-05 19:13:01 +07:00
|
|
|
|
|
|
|
|
websocket.addEventListener('open', () => {
|
|
|
|
|
DevToolsContext.open = true
|
|
|
|
|
for (const sub of subscriptions) sub(1)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
websocket.addEventListener('close', () => {
|
|
|
|
|
cleanup()
|
|
|
|
|
for (const sub of subscriptions) sub(2)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
websocket.addEventListener('error', e => {
|
|
|
|
|
cleanup()
|
|
|
|
|
for (const sub of subscriptions) sub(3, e)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
__REACT_DEVTOOLS__!.exports.connectToDevTools({
|
|
|
|
|
websocket,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function cleanup() {
|
|
|
|
|
DevToolsContext.open = false
|
|
|
|
|
DevToolsContext.ws = undefined
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function disconnectFromDevTools() {
|
|
|
|
|
if (DevToolsContext.ws) DevToolsContext.ws.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function useIsDevToolsOpen() {
|
|
|
|
|
const rerender = useReRender()
|
|
|
|
|
|
2025-06-08 19:01:33 +07:00
|
|
|
useEffect(() => {
|
2025-05-05 19:13:01 +07:00
|
|
|
const sub: Subscription = e => (e === 1 || e === 2) && rerender()
|
|
|
|
|
subscriptions.add(sub)
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
subscriptions.delete(sub)
|
|
|
|
|
}
|
2025-06-08 19:01:33 +07:00
|
|
|
}, [rerender])
|
2025-05-05 19:13:01 +07:00
|
|
|
|
|
|
|
|
return DevToolsContext.open
|
|
|
|
|
}
|