Skip to content

Commit

Permalink
Fix stuck lightbox (#6816)
Browse files Browse the repository at this point in the history
* Fix lightbox getting stuck by fixing rAF order

If you spam opening lightbox too fast, the effect that calls rAF will clean up and set up again midflight. Unfortunately, due to rAF order being unreliable, it may fire in reverse order, causing "open, open, close" instead of "open, close, open", so it would get stuck closed. This fixes the rAF order.

* Don't allow opening another lightbox while it's open
  • Loading branch information
gaearon authored Nov 28, 2024
1 parent 0aee639 commit d08ce0d
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 3 deletions.
10 changes: 9 additions & 1 deletion src/state/lightbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,15 @@ export function Provider({children}: React.PropsWithChildren<{}>) {

const openLightbox = useNonReactiveCallback(
(lightbox: Omit<Lightbox, 'id'>) => {
setActiveLightbox({...lightbox, id: nanoid()})
setActiveLightbox(prevLightbox => {
if (prevLightbox) {
// Ignore duplicate open requests. If it's already open,
// the user has to explicitly close the previous one first.
return prevLightbox
} else {
return {...lightbox, id: nanoid()}
}
})
},
)

Expand Down
33 changes: 31 additions & 2 deletions src/view/com/lightbox/ImageViewing/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,14 @@ export default function ImageViewRoot({
)

// https://github.com/software-mansion/react-native-reanimated/issues/6677
requestAnimationFrame(() => {
rAF_FIXED(() => {
openProgress.set(() =>
canAnimate ? withClampedSpring(1, SLOW_SPRING) : 1,
)
})
return () => {
// https://github.com/software-mansion/react-native-reanimated/issues/6677
requestAnimationFrame(() => {
rAF_FIXED(() => {
openProgress.set(() =>
canAnimate ? withClampedSpring(0, SLOW_SPRING) : 0,
)
Expand Down Expand Up @@ -752,3 +752,32 @@ function withClampedSpring(value: any, config: WithSpringConfig) {
'worklet'
return withSpring(value, {...config, overshootClamping: true})
}

// We have to do this because we can't trust RN's rAF to fire in order.
// https://github.com/facebook/react-native/issues/48005
let isFrameScheduled = false
let pendingFrameCallbacks: Array<() => void> = []
function rAF_FIXED(callback: () => void) {
pendingFrameCallbacks.push(callback)
if (!isFrameScheduled) {
isFrameScheduled = true
requestAnimationFrame(() => {
const callbacks = pendingFrameCallbacks.slice()
isFrameScheduled = false
pendingFrameCallbacks = []
let hasError = false
let error
for (let i = 0; i < callbacks.length; i++) {
try {
callbacks[i]()
} catch (e) {
hasError = true
error = e
}
}
if (hasError) {
throw error
}
})
}
}

0 comments on commit d08ce0d

Please sign in to comment.