Skip to content

Commit

Permalink
Add back better devtools blocker
Browse files Browse the repository at this point in the history
  • Loading branch information
IanPhilips committed Sep 18, 2024
1 parent c2795a3 commit c03264f
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 33 deletions.
115 changes: 115 additions & 0 deletions web/lib/util/devtools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// devtoolsDetector.ts
export interface DevtoolsDetectorConfig {
pollingIntervalSeconds: number
maxMillisBeforeAckWhenClosed: number
moreAnnoyingDebuggerStatements: number
onDetectOpen?: () => void
onDetectClose?: () => void
startup: 'asap' | 'manual' | 'domContentLoaded'
onCheckOpennessWhilePaused: 'returnStaleValue' | 'throw'
}

export interface DevtoolsDetector {
config: DevtoolsDetectorConfig
readonly isOpen: boolean
paused: boolean
}

export const setupDevtoolsDetector = (): DevtoolsDetector => {
const config: DevtoolsDetectorConfig = {
pollingIntervalSeconds: 0.25,
maxMillisBeforeAckWhenClosed: 100,
moreAnnoyingDebuggerStatements: 1,
onDetectOpen: undefined,
onDetectClose: undefined,
startup: 'asap',
onCheckOpennessWhilePaused: 'returnStaleValue',
}

Object.seal(config)

const heart = new Worker(
URL.createObjectURL(
new Blob(
[
`"use strict";
onmessage = (ev) => { postMessage({ isOpenBeat: true });
debugger;
for (let i = 0; i < ev.data.moreDebugs; i++) {
debugger;
}
postMessage({ isOpenBeat: false });
};`,
],
{ type: 'text/javascript' }
)
)
)

let _isDevtoolsOpen = false
let _isDetectorPaused = true
let resolveVerdict: (value: boolean | null) => void = () => {}
let nextPulse$: NodeJS.Timeout | number = NaN

const onHeartMsg = (msg: MessageEvent<{ isOpenBeat: boolean }>) => {
if (msg.data.isOpenBeat) {
const p = new Promise<boolean | null>((_resolveVerdict) => {
resolveVerdict = _resolveVerdict
let wait$: NodeJS.Timeout | number = setTimeout(() => {
wait$ = NaN
resolveVerdict(true)
}, config.maxMillisBeforeAckWhenClosed + 1)
})

p.then((verdict) => {
if (verdict === null) return
if (verdict !== _isDevtoolsOpen) {
_isDevtoolsOpen = verdict
const cb = { true: config.onDetectOpen, false: config.onDetectClose }[
verdict + ''
]
if (cb) cb()
}
nextPulse$ = setTimeout(() => {
nextPulse$ = NaN
doOnePulse()
}, config.pollingIntervalSeconds * 1000)
})
} else {
resolveVerdict(false)
}
}

const doOnePulse = () => {
heart.postMessage({ moreDebugs: config.moreAnnoyingDebuggerStatements })
}

const detector: DevtoolsDetector = {
config,
get isOpen() {
if (_isDetectorPaused && config.onCheckOpennessWhilePaused === 'throw') {
throw new Error('`onCheckOpennessWhilePaused` is set to `"throw"`.')
}
return _isDevtoolsOpen
},
get paused() {
return _isDetectorPaused
},
set paused(pause: boolean) {
if (_isDetectorPaused === pause) return
_isDetectorPaused = pause
if (pause) {
heart.removeEventListener('message', onHeartMsg as EventListener)
clearTimeout(nextPulse$)
nextPulse$ = NaN
resolveVerdict(null)
} else {
heart.addEventListener('message', onHeartMsg as EventListener)
doOnePulse()
}
},
}

Object.freeze(detector)
return detector
}
58 changes: 25 additions & 33 deletions web/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { AppProps } from 'next/app'
import Head from 'next/head'
import Script from 'next/script'
import { useEffect } from 'react'
import { useEffect, useState } from 'react'
import { AuthProvider, AuthUser } from 'web/components/auth-context'
import { NativeMessageListener } from 'web/components/native-message-listener'
import { useHasLoaded } from 'web/hooks/use-has-loaded'
Expand All @@ -12,13 +12,11 @@ import { GoogleOneTapSetup } from 'web/lib/firebase/google-onetap-login'
import clsx from 'clsx'
import { useRefreshAllClients } from 'web/hooks/use-refresh-all-clients'
import { postMessageToNative } from 'web/lib/native/post-message'
import { useThemeManager } from 'web/hooks/use-theme'
import { ENV_CONFIG, TRADE_TERM, TWOMBA_ENABLED } from 'common/envs/constants'
import { ENV_CONFIG, TRADE_TERM } from 'common/envs/constants'
import { SweepstakesProvider } from 'web/components/sweestakes-context'
import { capitalize } from 'lodash'
import { usePersistentInMemoryState } from 'web/hooks/use-persistent-in-memory-state'
import { useIsMobile } from 'web/hooks/use-is-mobile'

import { useThemeManager } from 'web/hooks/use-theme'
import { DevtoolsDetector, setupDevtoolsDetector } from 'web/lib/util/devtools'
// See https://nextjs.org/docs/basic-features/font-optimization#google-fonts
// and if you add a font, you must add it to tailwind config as well for it to work.

Expand Down Expand Up @@ -70,39 +68,33 @@ function printBuildInfo() {

// ian: Required by GambleId
const useDevtoolsDetector = () => {
const [devtoolsOpen, setDevtoolsOpen] = usePersistentInMemoryState(
false,
'devtools'
)
const isMobile = useIsMobile()
const [_, setDetector] = useState<DevtoolsDetector | null>(null)
const [isDevtoolsOpen, setIsDevtoolsOpen] = useState(false)

useEffect(() => {
const disable =
window.location.hostname === 'localhost' ||
window.location.pathname.includes('/embed/')
if (!TWOMBA_ENABLED || disable) {
const ignore =
window.location.host === 'localhost:3000' ||
process.env.NEXT_PUBLIC_FIREBASE_ENV === 'DEV'

if (ignore) {
return
}
const detectDevTools = () => {
const threshold = 160
const adjustedHeightThreshold = isMobile ? 200 : threshold

const widthThreshold = window.outerWidth - window.innerWidth > threshold
const heightThreshold =
window.outerHeight - window.innerHeight > adjustedHeightThreshold
const devtoolsDetector = setupDevtoolsDetector()
setDetector(devtoolsDetector)

if ((widthThreshold || heightThreshold) && !devtoolsOpen) {
setDevtoolsOpen(true)
}
devtoolsDetector.config.onDetectOpen = () => {
setIsDevtoolsOpen(true)
}

const intervalId = setInterval(() => {
detectDevTools()
}, 1000)
// Start detecting right away
devtoolsDetector.paused = false

return () => clearInterval(intervalId)
}, [isMobile])

return devtoolsOpen
return () => {
// Pause the detector when component unmounts
devtoolsDetector.paused = true
}
}, [])
return isDevtoolsOpen
}

// specially treated props that may be present in the server/static props
Expand All @@ -113,7 +105,7 @@ function MyApp({ Component, pageProps }: AppProps<ManifoldPageProps>) {
useHasLoaded()
useRefreshAllClients()
// ian: Required by GambleId
const devToolsOpen = false //useDevtoolsDetector()
const devToolsOpen = useDevtoolsDetector()
useThemeManager()

const title = 'Manifold'
Expand Down

0 comments on commit c03264f

Please sign in to comment.