UNPKG

react-native-story-widget

Version:

A React Native story widget component with carousel functionality

79 lines (78 loc) 3.78 kB
import { useCallback, useEffect, useRef, useState, } from 'react'; import { Dimensions, Platform, View } from 'react-native'; import { FlatList } from 'react-native-gesture-handler'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { createStyleSheet } from 'react-native-unistyles'; import { StoryGroupProvider } from '../../context/StoryProvider'; const screenWidth = Dimensions.get('window').width; const StoryGroup = ({ renderItem, style, userStories, initialGroupIndex, onPressCloseButton, isScreenFocused, onLastStoryOfGroupPlayed, }) => { const [currentGroupIndex, setCurrentGroupIndex] = useState(initialGroupIndex); const [insetTop, setInsetTop] = useState(null); const flatListRef = useRef(null); const insets = useSafeAreaInsets(); const isProgrammaticChange = useRef(false); const previousOffsetX = useRef(screenWidth * initialGroupIndex); useEffect(() => { if (!flatListRef.current || currentGroupIndex < 0 || userStories.length <= currentGroupIndex) { return; } if (Platform.OS === 'android') { flatListRef.current.scrollToIndex({ index: currentGroupIndex, animated: false, }); } else { requestAnimationFrame(() => { var _a; (_a = flatListRef.current) === null || _a === void 0 ? void 0 : _a.scrollToIndex({ index: currentGroupIndex, animated: false, }); }); } }, [currentGroupIndex, userStories.length]); useEffect(() => { setInsetTop(p => (p !== null ? p : insets.top)); }, [insets.top]); const defaultRenderItem = useCallback(({ item, index }) => { const isActive = currentGroupIndex === index; return (<View style={{ width: screenWidth }}> {renderItem({ item, index, isActive })} </View>); }, [currentGroupIndex, isScreenFocused, renderItem]); const handleMomentumScrollEnd = useCallback((e) => { if (isProgrammaticChange.current) { isProgrammaticChange.current = false; return; } const currentOffsetX = e.nativeEvent.contentOffset.x; const expectedGroupIndex = Math.round(currentOffsetX / screenWidth); if (expectedGroupIndex !== currentGroupIndex) { setCurrentGroupIndex(expectedGroupIndex); } previousOffsetX.current = currentOffsetX; }, [setCurrentGroupIndex, currentGroupIndex]); return (<StoryGroupProvider userStories={userStories} currentGroupIndex={currentGroupIndex} setCurrentGroupIndex={(newIndex) => { isProgrammaticChange.current = true; setCurrentGroupIndex(newIndex); }} onPressCloseButton={onPressCloseButton} isScreenFocused={isScreenFocused} onLastStoryOfGroupPlayed={onLastStoryOfGroupPlayed}> <View style={[defaultStyles.container, style]}> <FlatList ref={flatListRef} data={userStories} renderItem={defaultRenderItem} keyExtractor={item => item.id.toString()} horizontal pagingEnabled contentContainerStyle={{ paddingTop: insetTop }} onMomentumScrollEnd={handleMomentumScrollEnd} style={style} showsHorizontalScrollIndicator={false} scrollEnabled={true} bounces={false} windowSize={10} initialNumToRender={10} maxToRenderPerBatch={10} removeClippedSubviews={false} getItemLayout={(_data, index) => ({ length: screenWidth, offset: screenWidth * index, index, })}/> </View> </StoryGroupProvider>); }; export { StoryGroup }; const defaultStyles = createStyleSheet({ container: { flex: 1, overflow: 'hidden', width: screenWidth, }, });