react-native-story-widget
Version:
A React Native story widget component with carousel functionality
79 lines (78 loc) • 3.78 kB
JavaScript
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,
},
});