import * as React from "react"; import { useTTP } from "@tensamin/ttp"; import { ttp as schemas } from "@tensamin/shared/data"; import type z from "zod"; export type User = z.infer; interface contextValue { get(userId: number): Promise; } const UserContext = React.createContext(undefined); /** * Executes UserProvider. * @param props Parameter props. * @returns unknown. */ export default function UserProvider(props: { children: React.ReactNode }) { const storageRef = React.useRef>({}); const pendingRef = React.useRef | undefined>>( {}, ); const { send } = useTTP(); /** * Executes get. * @param userId Parameter userId. * @returns Promise. */ const get = React.useCallback( async (userId: number): Promise => { if (userId == null) { throw new Error("userId is required"); } const cachedUser = storageRef.current[userId]; if (cachedUser !== undefined) { return cachedUser; } const pendingUser = pendingRef.current[userId]; if (pendingUser !== undefined) { return pendingUser; } const request = (async () => { const userData = await send("get_user_data", { user_id: userId }); const user = { ...userData.data, avatar: userData.data.avatar ? `data:image/webp;base64,${atob(userData.data.avatar)}` : undefined, }; storageRef.current[userId] = user; return user; })(); pendingRef.current[userId] = request; try { return await request; } finally { delete pendingRef.current[userId]; } }, [send], ); return ( {props.children} ); } /** * Executes useUser. * @param none This function has no parameters. * @returns contextValue. */ export function useUser(): contextValue { const context = React.useContext(UserContext); if (!context) { throw new Error("useUser must be used within a UserProvider"); } return context; }