mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
0
fork

Configure Feed

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

at main 212 lines 6.5 kB view raw
1import { 2 type JSX, 3 memo, 4 useCallback, 5 useContext, 6 useImperativeHandle, 7 useMemo, 8 useRef, 9 useState, 10} from 'react' 11import {View} from 'react-native' 12import {DrawerGestureContext} from 'react-native-drawer-layout' 13import {Gesture, GestureDetector} from 'react-native-gesture-handler' 14import PagerView, { 15 type PagerViewOnPageScrollEventData, 16 type PagerViewOnPageSelectedEvent, 17 type PagerViewOnPageSelectedEventData, 18 type PageScrollStateChangedNativeEventData, 19} from 'react-native-pager-view' 20import Animated, { 21 runOnJS, 22 type SharedValue, 23 useEvent, 24 useHandler, 25 useSharedValue, 26} from 'react-native-reanimated' 27import {useFocusEffect} from '@react-navigation/native' 28 29import {useSetDrawerSwipeDisabled} from '#/state/shell' 30import {atoms as a, native} from '#/alf' 31 32export type PageSelectedEvent = PagerViewOnPageSelectedEvent 33 34export interface PagerRef { 35 setPage: (index: number) => void 36} 37 38export interface RenderTabBarFnProps { 39 selectedPage: number 40 onSelect?: (index: number) => void 41 tabBarAnchor?: JSX.Element | null | undefined // Ignored on native. 42 dragProgress: SharedValue<number> // Ignored on web. 43 dragState: SharedValue<'idle' | 'dragging' | 'settling'> // Ignored on web. 44} 45export type RenderTabBarFn = (props: RenderTabBarFnProps) => JSX.Element 46 47interface Props { 48 ref?: React.Ref<PagerRef> 49 initialPage?: number 50 renderTabBar: RenderTabBarFn 51 // tab pressed, yet to scroll to page 52 onTabPressed?: (index: number) => void 53 // scroll settled 54 onPageSelected?: (index: number) => void 55 onPageScrollStateChanged?: ( 56 scrollState: 'idle' | 'dragging' | 'settling', 57 ) => void 58 testID?: string 59} 60 61const AnimatedPagerView = Animated.createAnimatedComponent(PagerView) 62const MemoizedAnimatedPagerView = memo(AnimatedPagerView) 63 64export function Pager({ 65 ref, 66 children, 67 initialPage = 0, 68 renderTabBar, 69 onPageSelected: parentOnPageSelected, 70 onTabPressed: parentOnTabPressed, 71 onPageScrollStateChanged: parentOnPageScrollStateChanged, 72 testID, 73}: React.PropsWithChildren<Props>) { 74 const [selectedPage, setSelectedPage] = useState(initialPage) 75 const pagerView = useRef<PagerView>(null) 76 77 const [isIdle, setIsIdle] = useState(true) 78 const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled() 79 useFocusEffect( 80 useCallback(() => { 81 const canSwipeDrawer = selectedPage === 0 && isIdle 82 setDrawerSwipeDisabled(!canSwipeDrawer) 83 return () => { 84 setDrawerSwipeDisabled(false) 85 } 86 }, [setDrawerSwipeDisabled, selectedPage, isIdle]), 87 ) 88 89 useImperativeHandle(ref, () => ({ 90 setPage: (index: number) => { 91 pagerView.current?.setPage(index) 92 }, 93 })) 94 95 const onPageSelectedJSThread = useCallback( 96 (nextPosition: number) => { 97 setSelectedPage(nextPosition) 98 parentOnPageSelected?.(nextPosition) 99 }, 100 [setSelectedPage, parentOnPageSelected], 101 ) 102 103 const onTabBarSelect = useCallback( 104 (index: number) => { 105 parentOnTabPressed?.(index) 106 pagerView.current?.setPage(index) 107 }, 108 [pagerView, parentOnTabPressed], 109 ) 110 111 const dragState = useSharedValue<'idle' | 'settling' | 'dragging'>('idle') 112 const dragProgress = useSharedValue(selectedPage) 113 const didInit = useSharedValue(false) 114 const handlePageScroll = usePagerHandlers( 115 { 116 onPageScroll(e: PagerViewOnPageScrollEventData) { 117 'worklet' 118 if (didInit.get() === false) { 119 // On iOS, there's a spurious scroll event with 0 position 120 // even if a different page was supplied as the initial page. 121 // Ignore it and wait for the first confirmed selection instead. 122 return 123 } 124 dragProgress.set(e.offset + e.position) 125 }, 126 onPageScrollStateChanged(e: PageScrollStateChangedNativeEventData) { 127 'worklet' 128 runOnJS(setIsIdle)(e.pageScrollState === 'idle') 129 if (dragState.get() === 'idle' && e.pageScrollState === 'settling') { 130 // This is a programmatic scroll on Android. 131 // Stay "idle" to match iOS and avoid confusing downstream code. 132 return 133 } 134 dragState.set(e.pageScrollState) 135 parentOnPageScrollStateChanged?.(e.pageScrollState) 136 }, 137 onPageSelected(e: PagerViewOnPageSelectedEventData) { 138 'worklet' 139 didInit.set(true) 140 runOnJS(onPageSelectedJSThread)(e.position) 141 }, 142 }, 143 [parentOnPageScrollStateChanged], 144 ) 145 146 return ( 147 <View testID={testID} style={[a.flex_1, native(a.overflow_hidden)]}> 148 {renderTabBar({ 149 selectedPage, 150 onSelect: onTabBarSelect, 151 dragProgress, 152 dragState, 153 })} 154 <DrawerGestureRequireFail> 155 <MemoizedAnimatedPagerView 156 ref={pagerView} 157 style={a.flex_1} 158 initialPage={initialPage} 159 onPageScroll={handlePageScroll}> 160 {children} 161 </MemoizedAnimatedPagerView> 162 </DrawerGestureRequireFail> 163 </View> 164 ) 165} 166 167function DrawerGestureRequireFail({children}: {children: React.ReactNode}) { 168 const drawerGesture = useContext(DrawerGestureContext) 169 170 const nativeGesture = useMemo(() => { 171 const gesture = Gesture.Native() 172 if (drawerGesture) { 173 gesture.requireExternalGestureToFail(drawerGesture) 174 } 175 return gesture 176 }, [drawerGesture]) 177 178 return <GestureDetector gesture={nativeGesture}>{children}</GestureDetector> 179} 180 181function usePagerHandlers( 182 handlers: { 183 onPageScroll: (e: PagerViewOnPageScrollEventData) => void 184 onPageScrollStateChanged: (e: PageScrollStateChangedNativeEventData) => void 185 onPageSelected: (e: PagerViewOnPageSelectedEventData) => void 186 }, 187 dependencies: unknown[], 188) { 189 const {doDependenciesDiffer} = useHandler(handlers as any, dependencies) 190 const subscribeForEvents = [ 191 'onPageScroll', 192 'onPageScrollStateChanged', 193 'onPageSelected', 194 ] 195 return useEvent( 196 event => { 197 'worklet' 198 const {onPageScroll, onPageScrollStateChanged, onPageSelected} = handlers 199 if (event.eventName.endsWith('onPageScroll')) { 200 onPageScroll(event as any as PagerViewOnPageScrollEventData) 201 } else if (event.eventName.endsWith('onPageScrollStateChanged')) { 202 onPageScrollStateChanged( 203 event as any as PageScrollStateChangedNativeEventData, 204 ) 205 } else if (event.eventName.endsWith('onPageSelected')) { 206 onPageSelected(event as any as PagerViewOnPageSelectedEventData) 207 } 208 }, 209 subscribeForEvents, 210 doDependenciesDiffer, 211 ) 212}