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