react-native-story-component
Version:
Story component for React Native.
353 lines (349 loc) • 12.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = _interopRequireWildcard(require("react"));
var _reactNative = require("react-native");
var _reactNativeSwipeGestures = _interopRequireDefault(require("react-native-swipe-gestures"));
var _usePrevious = _interopRequireDefault(require("../helpers/usePrevious"));
var _ValidationHelpers = require("../helpers/ValidationHelpers");
var _index = require("../index");
var _StoryImage = _interopRequireDefault(require("./StoryImage"));
var _AnimationBar = _interopRequireDefault(require("./AnimationBar"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
/* eslint-disable react-native/no-inline-styles */
const {
width,
height
} = _reactNative.Dimensions.get('window');
const StoryListItem = props => {
const [loading, setLoading] = (0, _react.useState)(true);
const [pressed, setPressed] = (0, _react.useState)(false);
const [currStoryIndex, setCurrStoryIndex] = (0, _react.useState)(0);
const [content, setContent] = (0, _react.useState)(props.stories);
const [currImageWidth, setCurrImageWidth] = (0, _react.useState)(0);
const [currImageHeight, setCurrImageHeight] = (0, _react.useState)(0);
const currStory = (0, _react.useMemo)(() => content[currStoryIndex], [content, currStoryIndex]);
const currPageIndex = (0, _react.useMemo)(() => props.currentPage, [props.currentPage]);
const swipeText = (0, _react.useMemo)(() => {
var _content$currStoryInd;
return (content === null || content === void 0 ? void 0 : (_content$currStoryInd = content[currStoryIndex]) === null || _content$currStoryInd === void 0 ? void 0 : _content$currStoryInd.swipeText) || props.swipeText || 'Swipe Up';
}, [content, currStoryIndex, props.swipeText]);
const progress = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
const prevPageIndex = (0, _usePrevious.default)(currPageIndex);
const prevStoryIndex = (0, _usePrevious.default)(currStoryIndex);
const close = (0, _react.useCallback)(state => {
let data = [...content];
data.map(x => x.finished = false);
setContent(data);
progress.setValue(0);
if (currPageIndex === props.index) {
if (props.onFinish) {
props.onFinish(state);
}
}
}, [content, currPageIndex, progress, props]);
const next = (0, _react.useCallback)(() => {
// check if the next content is not empty
setLoading(true);
if (currStoryIndex !== content.length - 1) {
let data = [...content];
data[currStoryIndex].finished = true;
setContent(data);
setCurrStoryIndex(currStoryIndex + 1);
progress.setValue(0);
} else {
// the next content is empty
close(_index.ActionStates.NEXT);
}
}, [close, content, currStoryIndex, progress]);
const previous = () => {
// checking if the previous content is not empty
setLoading(true);
if (currStoryIndex - 1 >= 0) {
let data = [...content];
data[currStoryIndex].finished = false;
setContent(data);
setCurrStoryIndex(currStoryIndex - 1);
progress.setValue(0);
} else {
// the previous content is empty
close(_index.ActionStates.PREVIOUS);
}
};
const startProgressAnimation = (0, _react.useCallback)(() => {
_reactNative.Animated.timing(progress, {
toValue: 1,
duration: props.duration,
useNativeDriver: false
}).start(_ref => {
let {
finished
} = _ref;
if (finished) next();
});
}, [next, progress, props.duration]);
const startStory = (0, _react.useCallback)(() => {
_reactNative.Image.getSize(content[currStoryIndex].image, (imageWidth, imageHeight) => {
let newHeight = imageHeight;
let newWidth = imageWidth;
const isImageWidthBiggerThenPhone = imageWidth > width;
if (isImageWidthBiggerThenPhone) {
newWidth = width;
newHeight = imageHeight ? Math.floor(width * (imageHeight / imageWidth)) : width;
}
const isNewHeightBiggerThenPhone = newHeight > height;
if (isNewHeightBiggerThenPhone) {
newWidth = height * (imageWidth / imageHeight);
newHeight = height;
}
setCurrImageWidth(newWidth);
setCurrImageHeight(newHeight);
setLoading(false);
progress.setValue(0);
startProgressAnimation();
}, errorMsg => {
console.log(errorMsg);
});
}, [content, currStoryIndex, progress, startProgressAnimation]);
// call every page changes
(0, _react.useEffect)(() => {
const isPrevious = !!prevPageIndex && prevPageIndex > currPageIndex;
if (isPrevious) {
setCurrStoryIndex(content.length - 1);
} else {
setCurrStoryIndex(0);
}
let data = [...content];
data.map((x, i) => {
if (isPrevious) {
x.finished = true;
if (i === content.length - 1) {
x.finished = false;
}
} else {
x.finished = false;
}
});
setContent(data);
startStory();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currPageIndex]);
// call every story change requests
// ... and decide next or prev
(0, _react.useEffect)(() => {
if (!(0, _ValidationHelpers.isNullOrWhitespace)(prevStoryIndex)) {
const isNextStory = !!prevStoryIndex && currStoryIndex > prevStoryIndex;
const isPrevStory = !isNextStory;
const nextStory = content[currStoryIndex + 1];
const prevStory = content[currStoryIndex - 1];
if (isNextStory && prevStory.id === currStory.id) {
startStory();
} else if (isPrevStory && (nextStory === null || nextStory === void 0 ? void 0 : nextStory.id) === currStory.id) {
startStory();
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currStoryIndex]);
const onSwipeUp = () => {
if (props.onClosePress) props.onClosePress();
if (currStory.onPress) currStory.onPress();
};
const onSwipeDown = () => {
props === null || props === void 0 ? void 0 : props.onClosePress();
};
const renderSwipeButton = () => {
if (props.customSwipeUpButton) {
return props.customSwipeUpButton();
}
return /*#__PURE__*/_react.default.createElement(_reactNative.Text, {
style: styles.swipeText
}, swipeText);
};
const renderCloseButton = () => {
if (props.customCloseButton) {
return props.customCloseButton();
}
return /*#__PURE__*/_react.default.createElement(_reactNative.Text, {
style: styles.closeText
}, "X");
};
const renderProfileBanner = () => {
if (!props.showProfileBanner) return;
if (props.customProfileBanner) return props.customProfileBanner({
image: props.profileImage,
name: props.profileName
});
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_reactNative.Image, {
style: styles.avatarImage,
source: props.profileImage
}), /*#__PURE__*/_react.default.createElement(_reactNative.Text, {
style: styles.avatarText
}, props.profileName));
};
const renderStoryImage = () => {
if (props.customStoryImage) return props.customStoryImage({
image: currStory.image,
onLoadEnd: startStory,
imageWidth: currImageWidth,
imageHeight: currImageHeight
});
return /*#__PURE__*/_react.default.createElement(_StoryImage.default, {
source: {
uri: currStory.image
},
width: currImageWidth,
height: currImageHeight,
onLoadEnd: startStory
});
};
return /*#__PURE__*/_react.default.createElement(_reactNativeSwipeGestures.default, {
onSwipeUp: onSwipeUp,
onSwipeDown: onSwipeDown,
config: {
velocityThreshold: 0.3,
directionalOffsetThreshold: 80
},
style: styles.container
}, /*#__PURE__*/_react.default.createElement(_reactNative.SafeAreaView, {
style: styles.backgroundContainer
}, renderStoryImage(), loading && /*#__PURE__*/_react.default.createElement(_reactNative.View, {
style: styles.spinnerContainer
}, /*#__PURE__*/_react.default.createElement(_reactNative.ActivityIndicator, {
size: "large",
color: "#FFF"
}))), /*#__PURE__*/_react.default.createElement(_reactNative.View, {
style: styles.content
}, /*#__PURE__*/_react.default.createElement(_AnimationBar.default, {
currStoryIndex: currStoryIndex,
stories: content,
progress: progress
}), /*#__PURE__*/_react.default.createElement(_reactNative.View, {
style: styles.userContainer
}, /*#__PURE__*/_react.default.createElement(_reactNative.View, {
style: styles.profileContainer
}, renderProfileBanner()), /*#__PURE__*/_react.default.createElement(_reactNative.TouchableOpacity, {
onPress: () => {
if (props.onClosePress) {
props.onClosePress();
}
}
}, /*#__PURE__*/_react.default.createElement(_reactNative.View, {
style: styles.closeIconContainer
}, renderCloseButton()))), /*#__PURE__*/_react.default.createElement(_reactNative.View, {
style: styles.pressContainer
}, /*#__PURE__*/_react.default.createElement(_reactNative.TouchableWithoutFeedback, {
onPressIn: () => progress.stopAnimation(),
onLongPress: () => setPressed(true),
onPressOut: () => {
setPressed(false);
startProgressAnimation();
},
onPress: () => {
if (!pressed && !loading) previous();
}
}, /*#__PURE__*/_react.default.createElement(_reactNative.View, {
style: {
flex: 0.3
}
})), /*#__PURE__*/_react.default.createElement(_reactNative.TouchableWithoutFeedback, {
onPressIn: () => progress.stopAnimation(),
onLongPress: () => setPressed(true),
onPressOut: () => {
setPressed(false);
startProgressAnimation();
},
onPress: () => {
if (!pressed && !loading) next();
}
}, /*#__PURE__*/_react.default.createElement(_reactNative.View, {
style: {
flex: 0.7
}
})))), currStory.onPress && /*#__PURE__*/_react.default.createElement(_reactNative.TouchableOpacity, {
activeOpacity: 1,
onPress: onSwipeUp,
style: styles.swipeUpBtn
}, renderSwipeButton()));
};
StoryListItem.defaultProps = {
duration: 10000
};
const styles = _reactNative.StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#000'
},
backgroundContainer: {
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
justifyContent: 'center',
alignItems: 'center'
},
spinnerContainer: {
zIndex: -100,
position: 'absolute',
justifyContent: 'center',
backgroundColor: '#000',
alignSelf: 'center',
width: width,
height: height
},
content: {
flexDirection: 'column',
flex: 1
},
userContainer: {
height: 50,
flexDirection: 'row',
justifyContent: 'space-between',
paddingHorizontal: 15
},
avatarImage: {
height: 30,
width: 30,
borderRadius: 100
},
avatarText: {
fontWeight: 'bold',
color: '#FFF',
paddingLeft: 10
},
closeIconContainer: {
alignItems: 'center',
justifyContent: 'center',
height: 50,
paddingHorizontal: 15
},
pressContainer: {
flex: 1,
flexDirection: 'row'
},
swipeUpBtn: {
position: 'absolute',
right: 0,
left: 0,
alignItems: 'center',
bottom: _reactNative.Platform.OS === 'ios' ? 20 : 50
},
swipeText: {
color: '#FFF',
marginTop: 10
},
closeText: {
color: '#FFF'
},
profileContainer: {
flexDirection: 'row',
alignItems: 'center'
}
});
var _default = StoryListItem;
exports.default = _default;
//# sourceMappingURL=StoryListItem.js.map