The weeb for the next gen discord boat - Wamellow
wamellow.com
bot
discord
1import type { ApiError } from "@/typings";
2import type { HTMLProps } from "react";
3import { useCallback, useRef, useState } from "react";
4
5export enum InputState {
6 Idle = 0,
7 Loading = 1,
8 Success = 2
9}
10
11interface InputOptions<T> {
12 endpoint?: string;
13 k?: string;
14
15 defaultState: T;
16 transform?: (value: T) => unknown;
17
18 onSave?: (value: T) => void;
19}
20
21export type InputProps<T> = InputOptions<T> & HTMLProps<HTMLDivElement> & {
22 label: string;
23 description?: string;
24 disabled?: boolean;
25};
26
27export function useInput<T>(options: InputOptions<T>) {
28 const [value, setValue] = useState<T>(options.defaultState);
29 const [state, setState] = useState<InputState>(InputState.Idle);
30 const [error, setError] = useState<string | null>(null);
31 const timeout = useRef<NodeJS.Timeout | null>(null);
32
33 const onSave = useCallback(
34 async (val: T) => {
35 options.onSave?.(val);
36
37 if (!options.endpoint || !options.k) return;
38
39 if (timeout.current) {
40 clearTimeout(timeout.current);
41 timeout.current = null;
42 }
43
44 setState(InputState.Loading);
45 setError(null);
46
47 const res = await fetch(process.env.NEXT_PUBLIC_API + options.endpoint, {
48 method: "PATCH",
49 credentials: "include",
50 headers: {
51 "Content-Type": "application/json"
52 },
53 body: JSON.stringify(options.k.includes(".")
54 ? { [options.k.split(".")[0]]: { [options.k.split(".")[1]]: options.transform?.(val) ?? val } }
55 : { [options.k]: options.transform?.(val) ?? val }
56 )
57 })
58 .catch((error) => String(error));
59
60 if (typeof res === "string" || !res.ok) {
61 setState(InputState.Idle);
62
63 if (typeof res === "string") {
64 setError(res);
65 } else {
66 const data = await res
67 .json()
68 .catch(() => null) as ApiError | null;
69
70 setError(data?.message || "Unknown error");
71 }
72
73 return;
74 }
75
76 setState(InputState.Success);
77 timeout.current = setTimeout(() => setState(InputState.Idle), 1_000 * 8);
78 },
79 [options.onSave, options.endpoint, options.k, options.transform]
80 );
81
82 return {
83 value,
84 state,
85 error,
86 update: (val: T) => {
87 setValue(val);
88 onSave(val);
89 }
90 };
91}