The weeb for the next gen discord boat - Wamellow
wamellow.com
bot
discord
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}