The weeb for the next gen discord boat - Wamellow wamellow.com
bot discord
at master 91 lines 2.6 kB view raw
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}