DocDoeAI / src /lib /hooks /useRateLimit.ts
asnannp's picture
deploy: sync backend to Space root (learn-lesson HF cache fix)
65d68f5
Raw History Blame Contribute Delete
2.1 kB
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,
};
}