The React children to be wrapped by the provider.
The rate limit context provider with value for consumers.
export function RateLimitProvider({ children }: { children: ReactNode }) {
const [rateLimitState, setRateLimitState] = useState<RateLimitState>({
isRateLimited: false,
retryAfter: null,
message: null,
});
// Use string type only for toast ID to fix TypeScript error
const [toastId, setToastId] = useState<string | null>(null);
// Function to set rate limit state
const setRateLimit = (
isLimited: boolean,
retryTime?: number,
message?: string,
) => {
const retryTimestamp = retryTime ? Date.now() + retryTime * 1000 : null;
console.log("Setting rate limit state:", {
isLimited,
retryTimestamp,
message,
});
setRateLimitState({
isRateLimited: isLimited,
retryAfter: retryTimestamp,
message:
message ||
"AniList API rate limit reached. Please wait before making more requests.",
});
};
// Function to clear rate limit state
const clearRateLimit = () => {
setRateLimitState({
isRateLimited: false,
retryAfter: null,
message: null,
});
};
// Periodically check rate limit status from main process
useEffect(() => {
// Skip if we're in a browser environment without Electron
if (!window.electronAPI?.anilist?.getRateLimitStatus) return;
const checkRateLimitStatus = async () => {
try {
const status = await window.electronAPI.anilist.getRateLimitStatus();
if (status.isRateLimited) {
setRateLimitState({
isRateLimited: true,
retryAfter: status.retryAfter,
message:
"AniList API rate limit reached. Please wait before making more requests.",
});
} else if (rateLimitState.isRateLimited) {
// Clear rate limit if it was previously set but is now cleared
clearRateLimit();
}
} catch (error) {
console.error("Error checking rate limit status:", error);
}
};
// Check immediately on component mount
checkRateLimitStatus();
// Then check periodically
const interval = setInterval(checkRateLimitStatus, 1000);
return () => clearInterval(interval);
}, [rateLimitState.isRateLimited]);
// Listen for global rate limiting events
useEffect(() => {
const handleRateLimit = (event: Event) => {
const customEvent = event as CustomEvent;
if (customEvent.detail) {
const { retryAfter, message } = customEvent.detail;
console.log("Received rate limit event:", customEvent.detail);
setRateLimit(true, retryAfter, message);
}
};
// Add event listener for the custom rate limiting event
window.addEventListener("anilist:rate-limited", handleRateLimit);
// Clean up the listener on unmount
return () => {
window.removeEventListener("anilist:rate-limited", handleRateLimit);
};
}, []);
// Effect to show/hide toast notification based on rate limit state
useEffect(() => {
if (rateLimitState.isRateLimited && rateLimitState.retryAfter) {
// Create a dismissible persistent toast
const id = toast.warning(
<RateLimitToast
message={rateLimitState.message || "Rate limited by AniList API"}
retryAfter={rateLimitState.retryAfter}
onComplete={clearRateLimit}
/>,
{
id: "rate-limit-toast",
duration: Infinity, // Don't auto-dismiss
},
);
// Force cast to string to fix TypeScript error
setToastId(id as unknown as string);
} else if (toastId) {
// Dismiss the toast when no longer rate limited
toast.dismiss(toastId);
setToastId(null);
}
}, [rateLimitState.isRateLimited, rateLimitState.retryAfter]);
return (
<RateLimitContext.Provider
value={{ rateLimitState, setRateLimit, clearRateLimit }}
>
{children}
</RateLimitContext.Provider>
);
}
Provides rate limit context to its children, managing rate limit state and notifications.