File size: 2,098 Bytes
65d68f5 | 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 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | import { useState, useCallback } from "react";
import { classifyError } from "@/lib/api/errorTypes";
import { isApiError, type RateLimitScope } from "@/lib/api/client";
type RateLimitState = {
isRateLimited: boolean;
remainingSeconds: number;
canRetry: boolean;
scope: RateLimitScope | null;
};
export function useRateLimit() {
const [rateLimitState, setRateLimitState] = useState<RateLimitState>({
isRateLimited: false,
remainingSeconds: 0,
canRetry: true,
scope: null,
});
const checkRateLimit = useCallback((error: unknown): boolean => {
const errorInfo = classifyError(error);
if (errorInfo.type === "RATE_LIMITED") {
const scope = isApiError(error) ? error.rateLimitScope : null;
const retryAfter = isApiError(error) && typeof error.retryAfter === "number" ? error.retryAfter : 30;
setRateLimitState({
isRateLimited: true,
remainingSeconds: retryAfter,
canRetry: false,
scope,
});
// Start countdown
let secondsLeft = retryAfter;
const countdownInterval = setInterval(() => {
secondsLeft -= 1;
if (secondsLeft <= 0) {
clearInterval(countdownInterval);
setRateLimitState({
isRateLimited: false,
remainingSeconds: 0,
canRetry: true,
scope: null,
});
} else {
setRateLimitState(prev => ({
...prev,
remainingSeconds: secondsLeft,
}));
}
}, 1000);
return true;
}
return false;
}, []);
const resetRateLimit = useCallback(() => {
setRateLimitState({
isRateLimited: false,
remainingSeconds: 0,
canRetry: true,
scope: null,
});
}, []);
const getRateLimitMessage = useCallback(() => {
if (rateLimitState.isRateLimited) {
return `You're moving fast. I'll be ready in ${rateLimitState.remainingSeconds} seconds.`;
}
return "";
}, [rateLimitState]);
return {
rateLimitState,
checkRateLimit,
resetRateLimit,
getRateLimitMessage,
};
} |