91 lines
2.2 KiB
TypeScript
91 lines
2.2 KiB
TypeScript
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<typeof schemas.get_user_data.response>;
|
|
|
|
interface contextValue {
|
|
get(userId: number): Promise<User>;
|
|
}
|
|
|
|
const UserContext = React.createContext<contextValue | undefined>(undefined);
|
|
|
|
/**
|
|
* Executes UserProvider.
|
|
* @param props Parameter props.
|
|
* @returns unknown.
|
|
*/
|
|
export default function UserProvider(props: { children: React.ReactNode }) {
|
|
const storageRef = React.useRef<Record<number, User>>({});
|
|
const pendingRef = React.useRef<Record<number, Promise<User> | undefined>>(
|
|
{},
|
|
);
|
|
|
|
const { send } = useTTP();
|
|
|
|
/**
|
|
* Executes get.
|
|
* @param userId Parameter userId.
|
|
* @returns Promise<User>.
|
|
*/
|
|
const get = React.useCallback(
|
|
async (userId: number): Promise<User> => {
|
|
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 (
|
|
<UserContext.Provider value={{ get }}>
|
|
{props.children}
|
|
</UserContext.Provider>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|