@kunlexze1/react-native-otp-timer
Version:
OTP input component + countdown timer hook for React Native (using react-native-otp-entry)
245 lines (242 loc) • 7.12 kB
JavaScript
// src/OtpWithTimer.tsx
import React, { useEffect as useEffect2 } from "react";
import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
import { OtpInput } from "react-native-otp-entry";
// src/useTimer.ts
import { useState, useRef, useCallback, useEffect } from "react";
import { AppState } from "react-native";
var useTimer = (seconds) => {
const [timeLeft, setTimeLeft] = useState(seconds);
const [hasTimeout, setHasTimeout] = useState(false);
const [isRunning, setIsRunning] = useState(false);
const startTimeRef = useRef(Date.now());
const timerRef = useRef(null);
const formatTime = useCallback((time) => {
const minutes = Math.floor(time / 60);
const timeSeconds = time % 60;
return `${minutes}mins : ${timeSeconds.toString().padStart(2, "0")}secs`;
}, []);
const updateTimer = useCallback(() => {
const elapsedTime = Math.floor((Date.now() - startTimeRef.current) / 1e3);
const remainingTime = seconds - elapsedTime;
if (remainingTime <= 0) {
if (timerRef.current) {
clearInterval(timerRef.current);
timerRef.current = null;
}
setHasTimeout(true);
setIsRunning(false);
setTimeLeft(0);
} else {
setTimeLeft(remainingTime);
}
}, [seconds]);
const startTimer = useCallback(() => {
if (timerRef.current) {
clearInterval(timerRef.current);
timerRef.current = null;
}
setIsRunning(true);
setHasTimeout(false);
setTimeLeft(seconds);
startTimeRef.current = Date.now();
timerRef.current = setInterval(updateTimer, 1e3);
}, [seconds, updateTimer]);
const resetOtpTimer = useCallback(() => {
setHasTimeout(false);
setIsRunning(false);
setTimeLeft(seconds);
startTimeRef.current = Date.now();
if (timerRef.current) {
clearInterval(timerRef.current);
timerRef.current = null;
}
}, [seconds]);
const endTimer = useCallback(() => {
if (timerRef.current) {
clearInterval(timerRef.current);
timerRef.current = null;
}
setHasTimeout(true);
setTimeLeft(0);
setIsRunning(false);
}, []);
useEffect(() => {
const handleAppStateChange = (nextAppState) => {
if (nextAppState === "active") {
const elapsedTime = Math.floor((Date.now() - startTimeRef.current) / 1e3);
const remainingTime = seconds - elapsedTime;
setTimeLeft(remainingTime > 0 ? remainingTime : 0);
}
};
const subscription = AppState.addEventListener("change", handleAppStateChange);
return () => {
if (timerRef.current) {
clearInterval(timerRef.current);
timerRef.current = null;
}
subscription.remove();
};
}, [seconds]);
return {
timeLeft: formatTime(timeLeft),
hasTimeout,
isRunning,
startTimer,
resetOtpTimer,
endTimer
};
};
var useTimer_default = useTimer;
// src/OtpWithTimer.tsx
import { jsx, jsxs } from "react/jsx-runtime";
var OtpWithTimer = React.forwardRef(
({
// Timer props
timerDuration = 60,
timerStyle,
timerFormatter,
showTimer = true,
timerPosition = "top",
onTimerExpire,
onTimerStart,
onTimerReset,
onRestartTimer,
autoStartTimer = true,
showRestartButton = true,
restartButtonText = "Resend OTP",
restartButtonStyle,
containerStyle,
// OTP Entry props
numberOfDigits,
focusColor,
autoFocus,
hideStick,
placeholder,
blurOnFilled,
disabled,
type,
secureTextEntry,
focusStickBlinkingDuration,
onFilled,
onTextChange,
textInputProps,
textProps,
theme,
...rest
}, ref) => {
const { timeLeft, hasTimeout, startTimer, resetOtpTimer } = useTimer_default(timerDuration);
const handleRestartTimer = () => {
resetOtpTimer();
startTimer();
onRestartTimer == null ? void 0 : onRestartTimer();
onTimerStart == null ? void 0 : onTimerStart();
};
useEffect2(() => {
if (autoStartTimer) {
startTimer();
onTimerStart == null ? void 0 : onTimerStart();
}
}, [autoStartTimer, startTimer, onTimerStart]);
useEffect2(() => {
if (hasTimeout) {
onTimerExpire == null ? void 0 : onTimerExpire();
}
}, [hasTimeout, onTimerExpire]);
const handleFilled = (text) => {
onFilled == null ? void 0 : onFilled(text);
};
const handleTextChange = (text) => {
onTextChange == null ? void 0 : onTextChange(text);
};
const formatTimerDisplay = (time) => {
return timerFormatter ? timerFormatter(time) : time;
};
const renderTimer = () => {
if (!showTimer) return null;
return /* @__PURE__ */ jsx(Text, { style: [styles.timerText, timerStyle], children: hasTimeout ? "Timer expired" : formatTimerDisplay(timeLeft) });
};
return /* @__PURE__ */ jsxs(View, { style: [styles.container, containerStyle], children: [
timerPosition === "top" && renderTimer(),
/* @__PURE__ */ jsx(
OtpInput,
{
ref,
numberOfDigits,
focusColor,
autoFocus,
hideStick,
placeholder,
blurOnFilled,
disabled: disabled || hasTimeout,
type,
secureTextEntry,
focusStickBlinkingDuration,
onFilled: handleFilled,
onTextChange: handleTextChange,
textInputProps,
textProps,
theme,
...rest
}
),
timerPosition === "bottom" && renderTimer(),
hasTimeout && /* @__PURE__ */ jsxs(View, { style: styles.expiredContainer, children: [
/* @__PURE__ */ jsx(Text, { style: styles.expiredText, children: "Time expired. Please request a new OTP." }),
showRestartButton && /* @__PURE__ */ jsx(TouchableOpacity, { onPress: handleRestartTimer, children: /* @__PURE__ */ jsx(Text, { style: [styles.restartButton, restartButtonStyle], children: restartButtonText }) })
] })
] });
}
);
var useOtpTimer = (timerDuration = 60) => {
const timerHook = useTimer_default(timerDuration);
const restartTimer = () => {
timerHook.resetOtpTimer();
timerHook.startTimer();
};
return {
...timerHook,
resetTimer: timerHook.resetOtpTimer,
restartTimer
};
};
var styles = StyleSheet.create({
container: {
alignItems: "center"
},
timerText: {
fontSize: 16,
color: "#333",
marginVertical: 10,
textAlign: "center"
},
expiredContainer: {
alignItems: "center",
marginTop: 10
},
expiredText: {
fontSize: 12,
color: "#FF0000",
textAlign: "center",
marginBottom: 8
},
restartButton: {
fontSize: 14,
color: "#007AFF",
fontWeight: "600",
textDecorationLine: "underline",
paddingVertical: 8,
paddingHorizontal: 16
}
});
OtpWithTimer.displayName = "OtpWithTimer";
var OtpWithTimer_default = OtpWithTimer;
// src/index.ts
import { OtpInput as OtpInput2 } from "react-native-otp-entry";
export {
OtpInput2 as OtpInput,
OtpWithTimer_default as OtpWithTimer,
useOtpTimer,
useTimer_default as useTimer
};
//# sourceMappingURL=index.mjs.map