Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 2x 13x 13x 13x 13x 13x 13x 13x 4x 13x 13x 13x 13x 9x 92x 3x 13x 13x 13x 13x | import { useEffect, useRef, useState } from "react";
import { ChatbotPendingAction } from "@/types/chat";
export const usePendingActionCountdown = (
pendingAction:
| (Pick<ChatbotPendingAction, "id"> & Partial<ChatbotPendingAction>)
| null
| undefined,
) => {
const expiresAtMs =
typeof pendingAction?.expiresAt === "string" ? Date.parse(pendingAction.expiresAt) : null;
const hasExpiry = expiresAtMs !== null && !Number.isNaN(expiresAtMs);
const createdAtMs =
typeof pendingAction?.createdAt === "string" ? Date.parse(pendingAction.createdAt) : null;
const [nowMs, setNowMs] = useState(() => Date.now());
const windowKey = hasExpiry ? `${pendingAction?.id ?? ""}:${expiresAtMs}` : null;
const anchorRef = useRef<{ key: string; atMs: number } | null>(null);
if (windowKey !== null && anchorRef.current?.key !== windowKey)
anchorRef.current = { key: windowKey, atMs: Date.now() };
const anchorMs = windowKey !== null ? (anchorRef.current?.atMs ?? null) : null;
const effectiveNowMs = anchorMs !== null ? Math.max(nowMs, anchorMs) : nowMs;
const isExpired = hasExpiry && expiresAtMs <= effectiveNowMs;
useEffect(() => {
if (!hasExpiry || isExpired) return undefined;
const interval = setInterval(() => setNowMs(Date.now()), 1000);
return () => clearInterval(interval);
}, [hasExpiry, isExpired]);
const rawRemainingMs = hasExpiry ? expiresAtMs - effectiveNowMs : null;
const ttlBoundMs =
createdAtMs !== null && !Number.isNaN(createdAtMs) && hasExpiry && anchorMs !== null
? expiresAtMs - createdAtMs - (effectiveNowMs - anchorMs)
: null;
const remainingSec =
rawRemainingMs !== null
? Math.max(0, Math.ceil(Math.min(rawRemainingMs, ttlBoundMs ?? rawRemainingMs) / 1000))
: null;
return {
isExpired,
countdownLabel:
remainingSec === null
? null
: `Expires in ${Math.floor(remainingSec / 60)}:${String(remainingSec % 60).padStart(2, "0")}`,
};
};
|