UNPKG

@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
// 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