react-native-preview-story
Version:
React Native Story Component Which takes Stories array and show exact story view like infamous instagram
241 lines (240 loc) • 8.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = _interopRequireWildcard(require("react"));
var _reactNative = require("react-native");
var _Styled = _interopRequireDefault(require("../Styled.js"));
var _Header = _interopRequireDefault(require("./Header.js"));
var _ContentView = _interopRequireDefault(require("./ContentView.js"));
var _jsxRuntime = require("react/jsx-runtime");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
const {
width
} = _reactNative.Dimensions.get('window');
const StoryView = ({
onComplete,
stories,
visible,
imageStyle,
maxDuration = 3,
renderHeaderComponent = null,
storyName = '',
onChangePosition,
index,
close = true,
playPause = true,
storyNameText = {},
headerStyle = {
cornerRadius: 10,
progressColor: 'orange',
progressBarHeight: 7,
progressBarBackground: 'white'
},
noPause = false,
noControls = false,
nativeDriver = false,
onPressBack = () => null,
noLoop = false
}, ref) => {
const [currentStoryIndex, setCurrentStoryIndex] = (0, _react.useState)(index || 0);
const progressAnim = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
const pausedProgress = (0, _react.useRef)(0);
const [isPaused, setIsPaused] = (0, _react.useState)(false);
const isCompletedRef = (0, _react.useRef)(false);
const currentStory = _react.default.useMemo(() => stories[currentStoryIndex], [currentStoryIndex, stories]);
const maxDurationPerStory = _react.default.useMemo(() => currentStory?.duration || maxDuration, [currentStory, maxDuration]);
const [wentBack, setWentBack] = (0, _react.useState)(0);
let onClose = (0, _react.useCallback)(() => {
if (!isCompletedRef.current) {
isCompletedRef.current = true;
onComplete();
return;
}
}, [onComplete]);
const backAction = (0, _react.useCallback)(() => {
if (visible) {
onPressBack();
return true;
}
return false;
}, [visible, onPressBack]);
(0, _react.useEffect)(() => {
const backHandler = _reactNative.BackHandler.addEventListener('hardwareBackPress', backAction);
return () => backHandler.remove();
}, [backAction]);
const goToNextStory = (0, _react.useCallback)(() => {
if (currentStoryIndex < stories.length - 1) {
_reactNative.Animated.timing(progressAnim, {
toValue: 1,
duration: 3,
useNativeDriver: nativeDriver
}).start(() => {
pausedProgress.current = 0;
setCurrentStoryIndex(currentStoryIndex + 1);
progressAnim.setValue(0);
onChangePosition && onChangePosition(currentStoryIndex + 1);
});
} else {
setWentBack(0);
onClose();
if (!noLoop) {
setCurrentStoryIndex(0);
onChangePosition && onChangePosition(0);
}
onChangePosition && onChangePosition(currentStoryIndex);
}
}, [currentStoryIndex, stories, progressAnim, onChangePosition, onClose, nativeDriver, noLoop]);
(0, _react.useImperativeHandle)(ref, () => ({
setPause: pause => {
setIsPaused(pause);
},
setCurrentStoryIndex: indexFromRef => {
setCurrentStoryIndex(indexFromRef);
},
progressAnim
}));
const runProgressAnimation = (0, _react.useCallback)(() => {
// this will run the animations at the top for the story
progressAnim.setValue(pausedProgress.current); //set the value of the progress of the story
_reactNative.Animated.timing(progressAnim, {
toValue: 1,
duration: (1 - pausedProgress.current) * maxDurationPerStory * 1000,
//for how long each story currently 6 seconds
useNativeDriver: nativeDriver
}).start(({
finished
}) => {
if (finished) {
goToNextStory(); //once finished goes to nextStory()
}
});
}, [maxDurationPerStory, progressAnim, goToNextStory, nativeDriver]);
const getProgressBarWidth = (storyIndex, currentIndex) => {
if (currentIndex > storyIndex) {
return '100%';
} // this is when the Story has been viewed
if (currentIndex === storyIndex) {
return progressAnim.interpolate({
inputRange: [0, 1],
outputRange: ['0%', '100%'] // this is when the story is being viewed
});
}
return '0%'; // this is when the Story has not been viewed yet
};
const goToPreviousStory = () => {
if (isPaused) {
setIsPaused(false);
}
pausedProgress.current = 0;
progressAnim.setValue(0);
if (currentStoryIndex === 0) {
setWentBack(wentBack + 1);
runProgressAnimation();
} else {
setCurrentStoryIndex(currentStoryIndex - 1);
onChangePosition && onChangePosition(currentStoryIndex - 1);
}
};
(0, _react.useEffect)(() => {
if (index) {
setCurrentStoryIndex(index);
}
}, [index]);
const handlePressIn = () => {
//for pause if user holds the screen
setIsPaused(true);
};
const handlePressOut = () => {
//for pause if user releases the holded screen
setIsPaused(false);
};
const handleScreenTouch = evt => {
//this function takes the width and decided where the click was pressed if left or right
if (noControls) return;
const touchX = evt?.nativeEvent?.locationX;
if (touchX < width / 2) {
goToPreviousStory();
} else {
goToNextStory();
}
};
const pausePlay = () => {
if (isPaused) {
setIsPaused(false);
} else {
setIsPaused(true);
}
};
(0, _react.useEffect)(() => {
if (!isPaused) {
runProgressAnimation();
} else {
progressAnim.stopAnimation(value => {
pausedProgress.current = value;
});
}
return () => {
progressAnim.stopAnimation();
};
}, [currentStoryIndex, isPaused, progressAnim, runProgressAnimation]);
return visible ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.SafeAreaView, {
style: _Styled.default.safeArea,
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
onPress: handleScreenTouch,
...(noPause || noControls ? {} : {
onLongPress: handlePressIn,
onPressOut: handlePressOut
}),
style: ({
pressed
}) => [{
opacity: pressed && !noControls ? 0.9 : 1 //when clicked shows the user screen a little dimmed for feedback
}, _Styled.default.container],
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
style: _Styled.default.container,
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.SafeAreaView, {
style: _Styled.default.topBarContainer,
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
children: renderHeaderComponent ? renderHeaderComponent({
getProgressBarWidth,
storyName,
stories,
currentStoryIndex,
pausePlay,
isPaused,
onComplete: onClose,
close,
playPause,
storyNameText,
headerStyle
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_Header.default, {
getProgressBarWidth: getProgressBarWidth,
storyName: storyName,
stories: stories,
currentStoryIndex: currentStoryIndex,
pausePlay: pausePlay,
isPaused: isPaused,
onComplete: onClose,
close: !noControls ? close : false,
playPause: !noControls ? playPause : false,
storyNameText: storyNameText,
headerStyle: headerStyle
})
})
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
style: _Styled.default.imageContainer,
children: currentStory?.type && /*#__PURE__*/(0, _jsxRuntime.jsx)(_ContentView.default, {
story: currentStory,
imageStyle: imageStyle
})
})]
})
})
}) : null;
};
var _default = exports.default = /*#__PURE__*/(0, _react.forwardRef)(StoryView);
//# sourceMappingURL=StoryView.js.map