react-native-story-component
Version:
Story component for React Native.
220 lines • 7.15 kB
JavaScript
import React, { useRef, useState, useEffect, useCallback } from 'react';
import { Dimensions, View, Platform, StatusBar, StyleSheet, Image } from 'react-native';
import Modal from 'react-native-modalbox';
import StoryListItem from './StoryListItem';
import StoryCircleListView from './StoryCircleListView';
import AndroidCubeEffect from '../animations/AndroidCubeEffect';
import CubeNavigationHorizontal from '../animations/CubeNavigationHorizontal';
import { isNullOrWhitespace, isUrl } from '../helpers/ValidationHelpers';
import useMountEffect from '../helpers/useMountEffect';
import { ActionStates } from '../index';
const Story = props => {
const {
data,
unPressedBorderColor,
pressedBorderColor,
storyListStyle,
onStart,
onClose,
duration,
swipeText,
customSwipeUpButton,
customCloseButton,
customStoryList,
customProfileBanner,
customStoryImage,
avatarSize,
showAvatarText,
showProfileBanner,
avatarTextStyle,
prefetchImages,
onImagesPrefetched
} = props;
const cubeRef = useRef(null);
const [dataState, setDataState] = useState(data);
const [isModalOpen, setIsModalOpen] = useState(false);
const [currentPage, setCurrentPage] = useState(0);
const [selectedData, setSelectedData] = useState([]);
const _handleStoryItemPress = (item, index) => {
const newData = dataState.slice(index);
if (onStart) onStart(item);
setCurrentPage(0);
setSelectedData(newData);
setIsModalOpen(true);
};
const handleSeen = useCallback(() => {
const seen = selectedData[currentPage];
const seenIndex = dataState.indexOf(seen);
if (seenIndex > 0) {
var _dataState$seenIndex;
if (!((_dataState$seenIndex = dataState[seenIndex]) !== null && _dataState$seenIndex !== void 0 && _dataState$seenIndex.seen)) {
let tempData = dataState;
dataState[seenIndex] = {
...dataState[seenIndex],
seen: true
};
setDataState(tempData);
}
}
}, [currentPage, dataState, selectedData]);
useMountEffect(() => {
if (prefetchImages) {
let preFetchTasks = [];
const images = data.flatMap(story => {
const storyImages = story.stories.map(storyItem => {
return storyItem.image;
});
return storyImages;
});
images.forEach(image => {
preFetchTasks.push(Image.prefetch(image));
});
Promise.all(preFetchTasks).then(results => {
let downloadedAll = true;
results.forEach(result => {
if (!result) {
//error occurred downloading a pic
downloadedAll = false;
}
});
if (onImagesPrefetched) {
onImagesPrefetched(downloadedAll);
}
});
}
});
useEffect(() => {
handleSeen();
}, [currentPage, handleSeen]);
const onStoryFinish = state => {
if (!isNullOrWhitespace(state)) {
if (state === ActionStates.NEXT) {
const newPage = currentPage + 1;
if (newPage < selectedData.length) {
var _cubeRef$current;
setCurrentPage(newPage);
//@ts-ignore
cubeRef === null || cubeRef === void 0 ? void 0 : (_cubeRef$current = cubeRef.current) === null || _cubeRef$current === void 0 ? void 0 : _cubeRef$current.scrollTo(newPage);
} else {
setIsModalOpen(false);
setCurrentPage(0);
if (onClose) {
onClose(selectedData[selectedData.length - 1]);
}
}
} else if (state === ActionStates.PREVIOUS) {
const newPage = currentPage - 1;
if (newPage < 0) {
setIsModalOpen(false);
setCurrentPage(0);
} else {
var _cubeRef$current2;
setCurrentPage(newPage);
//@ts-ignore
cubeRef === null || cubeRef === void 0 ? void 0 : (_cubeRef$current2 = cubeRef.current) === null || _cubeRef$current2 === void 0 ? void 0 : _cubeRef$current2.scrollTo(newPage);
}
}
}
};
const onClosePress = story => {
setIsModalOpen(false);
if (onClose) onClose(story);
};
const renderStoryList = () => {
return selectedData.map((story, i) => {
if (props.customStoryView) return props.customStoryView({
index: i,
data: story,
currentPage,
changeStory: onStoryFinish,
close: () => onClosePress(story)
});
return /*#__PURE__*/React.createElement(StoryListItem, {
key: `story-${story.id}`,
index: i,
duration: duration ? duration * 1000 : undefined,
profileName: story.name,
profileImage: isUrl(story.avatar) ? {
uri: story.avatar
} : story.avatar,
stories: story.stories,
currentPage: currentPage,
onFinish: onStoryFinish,
swipeText: swipeText,
customSwipeUpButton: customSwipeUpButton,
customCloseButton: customCloseButton,
customProfileBanner: customProfileBanner,
customStoryImage: customStoryImage,
showProfileBanner: showProfileBanner,
onClosePress: () => onClosePress(story)
});
});
};
const renderStoryCircleList = () => {
if (customStoryList) {
return customStoryList({
data: dataState,
onStoryPress: _handleStoryItemPress
});
}
return /*#__PURE__*/React.createElement(StoryCircleListView, {
handleStoryItemPress: _handleStoryItemPress,
data: dataState,
avatarSize: avatarSize,
unPressedBorderColor: unPressedBorderColor,
pressedBorderColor: pressedBorderColor,
showText: showAvatarText,
textStyle: avatarTextStyle
});
};
const renderCube = () => {
if (Platform.OS === 'ios') {
return /*#__PURE__*/React.createElement(CubeNavigationHorizontal, {
ref: cubeRef,
callBackAfterSwipe: x => {
if (parseInt(`${x}`, 10) !== currentPage) {
setCurrentPage(parseInt(`${x}`, 10));
}
}
}, renderStoryList());
}
return /*#__PURE__*/React.createElement(AndroidCubeEffect
//@ts-ignore
, {
ref: cubeRef,
callBackAfterSwipe: x => {
if (parseInt(`${x}`, 10) !== currentPage) {
setCurrentPage(parseInt(`${x}`, 10));
}
}
}, renderStoryList());
};
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(View, {
style: storyListStyle
}, renderStoryCircleList()), /*#__PURE__*/React.createElement(Modal, {
style: styles.modal,
isOpen: isModalOpen,
onClosed: () => setIsModalOpen(false),
position: "center",
swipeToClose: true,
swipeArea: 250,
backButtonClose: true,
coverScreen: true
}, /*#__PURE__*/React.createElement(StatusBar, {
barStyle: "light-content"
}), renderCube()));
};
Story.defaultProps = {
showAvatarText: true,
showProfileBanner: true,
prefetchImages: true
};
const styles = StyleSheet.create({
modal: {
flex: 1,
height: Dimensions.get('window').height,
width: Dimensions.get('window').width
}
});
export default Story;
//# sourceMappingURL=Story.js.map