mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
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}