mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
at thread-bug 115 lines 3.1 kB view raw
1import React, { 2 useCallback, 3 useEffect, 4 useId, 5 useMemo, 6 useRef, 7 useState, 8} from 'react' 9import {useWindowDimensions} from 'react-native' 10 11import {isNative, isWeb} from '#/platform/detection' 12 13const Context = React.createContext<{ 14 activeViewId: string | null 15 setActiveView: (viewId: string) => void 16 sendViewPosition: (viewId: string, y: number) => void 17} | null>(null) 18Context.displayName = 'ActiveVideoWebContext' 19 20export function Provider({children}: {children: React.ReactNode}) { 21 if (!isWeb) { 22 throw new Error('ActiveVideoWebContext may only be used on web.') 23 } 24 25 const [activeViewId, setActiveViewId] = useState<string | null>(null) 26 const activeViewLocationRef = useRef(Infinity) 27 const {height: windowHeight} = useWindowDimensions() 28 29 // minimising re-renders by using refs 30 const manuallySetRef = useRef(false) 31 const activeViewIdRef = useRef(activeViewId) 32 useEffect(() => { 33 activeViewIdRef.current = activeViewId 34 }, [activeViewId]) 35 36 const setActiveView = useCallback( 37 (viewId: string) => { 38 setActiveViewId(viewId) 39 manuallySetRef.current = true 40 // we don't know the exact position, but it's definitely on screen 41 // so just guess that it's in the middle. Any value is fine 42 // so long as it's not offscreen 43 activeViewLocationRef.current = windowHeight / 2 44 }, 45 [windowHeight], 46 ) 47 48 const sendViewPosition = useCallback( 49 (viewId: string, y: number) => { 50 if (isNative) return 51 52 if (viewId === activeViewIdRef.current) { 53 activeViewLocationRef.current = y 54 } else { 55 if ( 56 distanceToIdealPosition(y) < 57 distanceToIdealPosition(activeViewLocationRef.current) 58 ) { 59 // if the old view was manually set, only usurp if the old view is offscreen 60 if ( 61 manuallySetRef.current && 62 withinViewport(activeViewLocationRef.current) 63 ) { 64 return 65 } 66 67 setActiveViewId(viewId) 68 activeViewLocationRef.current = y 69 manuallySetRef.current = false 70 } 71 } 72 73 function distanceToIdealPosition(yPos: number) { 74 return Math.abs(yPos - windowHeight / 2.5) 75 } 76 77 function withinViewport(yPos: number) { 78 return yPos > 0 && yPos < windowHeight 79 } 80 }, 81 [windowHeight], 82 ) 83 84 const value = useMemo( 85 () => ({ 86 activeViewId, 87 setActiveView, 88 sendViewPosition, 89 }), 90 [activeViewId, setActiveView, sendViewPosition], 91 ) 92 93 return <Context.Provider value={value}>{children}</Context.Provider> 94} 95 96export function useActiveVideoWeb() { 97 const context = React.useContext(Context) 98 if (!context) { 99 throw new Error( 100 'useActiveVideoWeb must be used within a ActiveVideoWebProvider', 101 ) 102 } 103 104 const {activeViewId, setActiveView, sendViewPosition} = context 105 const id = useId() 106 107 return { 108 active: activeViewId === id, 109 setActive: () => { 110 setActiveView(id) 111 }, 112 currentActiveView: activeViewId, 113 sendPosition: (y: number) => sendViewPosition(id, y), 114 } 115}