diff --git a/src/lib/hooks/useWebBodyScrollLock.ts b/src/lib/hooks/useWebBodyScrollLock.ts
new file mode 100644
index 0000000000..585f193f1f
--- /dev/null
+++ b/src/lib/hooks/useWebBodyScrollLock.ts
@@ -0,0 +1,28 @@
+import {useEffect} from 'react'
+import {isWeb} from '#/platform/detection'
+
+let refCount = 0
+
+function incrementRefCount() {
+ if (refCount === 0) {
+ document.body.style.overflow = 'hidden'
+ }
+ refCount++
+}
+
+function decrementRefCount() {
+ refCount--
+ if (refCount === 0) {
+ document.body.style.overflow = ''
+ }
+}
+
+export function useWebBodyScrollLock(isLockActive: boolean) {
+ useEffect(() => {
+ if (!isWeb || !isLockActive) {
+ return
+ }
+ incrementRefCount()
+ return () => decrementRefCount()
+ })
+}
diff --git a/src/view/com/lightbox/Lightbox.web.tsx b/src/view/com/lightbox/Lightbox.web.tsx
index 19228570d0..f01b14a44c 100644
--- a/src/view/com/lightbox/Lightbox.web.tsx
+++ b/src/view/com/lightbox/Lightbox.web.tsx
@@ -23,6 +23,7 @@ import {
ImagesLightbox,
ProfileImageLightbox,
} from '#/state/lightbox'
+import {useWebBodyScrollLock} from '#/lib/hooks/useWebBodyScrollLock'
interface Img {
uri: string
@@ -32,8 +33,10 @@ interface Img {
export function Lightbox() {
const {activeLightbox} = useLightbox()
const {closeLightbox} = useLightboxControls()
+ const isActive = !!activeLightbox
+ useWebBodyScrollLock(isActive)
- if (!activeLightbox) {
+ if (!isActive) {
return null
}
diff --git a/src/view/com/modals/Modal.web.tsx b/src/view/com/modals/Modal.web.tsx
index 1510084af4..b73550b581 100644
--- a/src/view/com/modals/Modal.web.tsx
+++ b/src/view/com/modals/Modal.web.tsx
@@ -3,6 +3,7 @@ import {TouchableWithoutFeedback, StyleSheet, View} from 'react-native'
import Animated, {FadeIn, FadeOut} from 'react-native-reanimated'
import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
+import {useWebBodyScrollLock} from '#/lib/hooks/useWebBodyScrollLock'
import {useModals, useModalControls} from '#/state/modals'
import type {Modal as ModalIface} from '#/state/modals'
@@ -37,6 +38,7 @@ import * as LinkWarningModal from './LinkWarning'
export function ModalsContainer() {
const {isModalActive, activeModals} = useModals()
+ useWebBodyScrollLock(isModalActive)
if (!isModalActive) {
return null
diff --git a/src/view/shell/Composer.web.tsx b/src/view/shell/Composer.web.tsx
index 61a3616cb1..2e68508edb 100644
--- a/src/view/shell/Composer.web.tsx
+++ b/src/view/shell/Composer.web.tsx
@@ -5,6 +5,7 @@ import {ComposePost} from '../com/composer/Composer'
import {useComposerState} from 'state/shell/composer'
import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
+import {useWebBodyScrollLock} from '#/lib/hooks/useWebBodyScrollLock'
const BOTTOM_BAR_HEIGHT = 61
@@ -12,11 +13,13 @@ export function Composer({}: {winHeight: number}) {
const pal = usePalette('default')
const {isMobile} = useWebMediaQueries()
const state = useComposerState()
+ const isActive = !!state
+ useWebBodyScrollLock(isActive)
// rendering
// =
- if (!state) {
+ if (!isActive) {
return
}
diff --git a/src/view/shell/index.web.tsx b/src/view/shell/index.web.tsx
index cc627da8d1..030a607fcb 100644
--- a/src/view/shell/index.web.tsx
+++ b/src/view/shell/index.web.tsx
@@ -15,6 +15,7 @@ import {useAuxClick} from 'lib/hooks/useAuxClick'
import {t} from '@lingui/macro'
import {useIsDrawerOpen, useSetDrawerOpen} from '#/state/shell'
import {useCloseAllActiveElements} from '#/state/util'
+import {useWebBodyScrollLock} from '#/lib/hooks/useWebBodyScrollLock'
function ShellInner() {
const isDrawerOpen = useIsDrawerOpen()
@@ -23,6 +24,7 @@ function ShellInner() {
const navigator = useNavigation()
const closeAllActiveElements = useCloseAllActiveElements()
+ useWebBodyScrollLock(isDrawerOpen)
useAuxClick()
useEffect(() => {