Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

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