add cooldown notification repeat

This commit is contained in:
lendry
2026-06-25 14:45:01 +03:00
parent 6c63343fc7
commit 9671fe458b
11 changed files with 173 additions and 25 deletions

View File

@@ -0,0 +1,59 @@
'use client';
import { useEffect, useState } from 'react';
export function formatCooldownSeconds(seconds: number) {
const safe = Math.max(0, seconds);
const minutes = Math.floor(safe / 60);
const rest = safe % 60;
return `${minutes}:${String(rest).padStart(2, '0')}`;
}
export function useOtpResendCooldown(resendAvailableAt: string | null) {
const [secondsLeft, setSecondsLeft] = useState(0);
useEffect(() => {
if (!resendAvailableAt) {
setSecondsLeft(0);
return;
}
const tick = () => {
const diff = Math.ceil((new Date(resendAvailableAt).getTime() - Date.now()) / 1000);
setSecondsLeft(Math.max(0, diff));
};
tick();
const timer = window.setInterval(tick, 1000);
return () => window.clearInterval(timer);
}, [resendAvailableAt]);
return secondsLeft;
}
export function readOtpResendAvailableAt(recipient: string) {
if (typeof window === 'undefined' || !recipient) return null;
try {
return sessionStorage.getItem(`otp-resend:${recipient.toLowerCase()}`);
} catch {
return null;
}
}
export function writeOtpResendAvailableAt(recipient: string, resendAvailableAt: string) {
if (typeof window === 'undefined' || !recipient) return;
try {
sessionStorage.setItem(`otp-resend:${recipient.toLowerCase()}`, resendAvailableAt);
} catch {
// ignore quota errors
}
}
export function clearOtpResendAvailableAt(recipient: string) {
if (typeof window === 'undefined' || !recipient) return;
try {
sessionStorage.removeItem(`otp-resend:${recipient.toLowerCase()}`);
} catch {
// ignore
}
}