The weeb for the next gen discord boat - Wamellow wamellow.com
bot discord
at master 2.3 kB view raw
1import React, { useState } from "react"; 2 3import { Button } from "./ui/button"; 4 5enum State { 6 Idle = 0, 7 Loading = 1, 8 Success = 2, 9 Ratelimited = 3 10} 11 12export default function Fetch({ 13 url, 14 payload, 15 icon, 16 label, 17 method, 18 size, 19 className 20}: { 21 url: string; 22 payload?: Record<string, unknown>; 23 icon: React.ReactNode; 24 label: string; 25 method: "PUT" | "POST" | "DELETE"; 26 size?: "sm" | "lg"; 27 className?: string; 28}) { 29 const [state, setState] = useState<State>(State.Idle); 30 const [error, setError] = useState<string | null>(null); 31 32 const handle = async () => { 33 if (state === State.Ratelimited || state === State.Success) return; 34 35 setState(State.Loading); 36 setError(null); 37 38 const res = await fetch(`${process.env.NEXT_PUBLIC_API}${url}`, { 39 method, 40 credentials: "include", 41 headers: { 42 "Content-Type": "application/json" 43 }, 44 body: JSON.stringify(payload || {}) 45 }); 46 47 if (res.status === 429) { 48 setState(State.Ratelimited); 49 setTimeout(() => setState(State.Idle), 6 * 1_000); 50 } 51 52 if (res.ok) { 53 setState(State.Success); 54 setTimeout(() => setState(State.Idle), 6 * 1_000); 55 return; 56 } 57 58 setState(State.Idle); 59 60 if (res.headers.get("Content-Type")?.includes("application/json")) { 61 const data = await res.json(); 62 setError(data.message); 63 return; 64 } 65 66 setError(res.status + ": " + res.statusText); 67 }; 68 69 return ( 70 <div className={className}> 71 <Button 72 className="w-full" 73 onClick={handle} 74 variant={state === State.Success 75 ? "success" 76 : "flat" 77 } 78 disabled={state !== State.Idle} 79 size={size} 80 loading={state === State.Loading} 81 > 82 {state !== State.Loading && icon} 83 {label} 84 </Button> 85 86 {error && ( 87 <div className="text-red-500 text-sm mt-1"> 88 {error} 89 </div> 90 )} 91 </div> 92 ); 93}