import { type QueryObserverOptions, type UseQueryResult, keepPreviousData, useMutation, useQuery } from "@tanstack/react-query"; import type { AuthResponse } from "auth"; import type { EntityData, RepoQuery, RepositoryResponse } from "data"; import { Api } from "../../../Api"; import type { ApiResponse } from "../../../modules/ModuleApi"; import { queryClient } from "../ClientProvider"; export class AppQueryClient { api: Api; constructor( public baseUrl: string, user?: object ) { this.api = new Api({ host: baseUrl, user }); } queryOptions(options?: Partial): Partial { return { staleTime: 1000 * 60 * 5, placeholderData: keepPreviousData, ...options }; } auth = () => { return { state: (): (AuthResponse & { verified: boolean }) | undefined => { return this.api.getAuthState() as any; }, login: async (data: { email: string; password: string }) => { return await this.api.auth.loginWithPassword(data); }, register: async (data: any) => { return await this.api.auth.registerWithPassword(data); }, logout: async () => { this.api.updateToken(undefined); return true; }, setToken: (token) => { this.api.updateToken(token); return this.api.getAuthState(); }, verify: async () => { try { //console.log("verifiying"); const res = await this.api.auth.me(); //console.log("verifying result", res); if (!res.ok || !res.body.user) { throw new Error(); } this.api.markAuthVerified(true); } catch (e) { this.api.markAuthVerified(false); this.api.updateToken(undefined); } } }; }; media = (options?: Partial) => { const queryOptions = this.queryOptions(options); return { api: () => { return this.api.media; }, list: (query: Partial = { limit: 10 }): UseQueryResult => { return useQuery({ ...(queryOptions as any), // @todo: fix typing queryKey: ["data", "entity", "media", { query }], queryFn: async () => { return await this.api.data.readMany("media", query); } }); }, deleteFile: async (filename: string | { path: string }) => { const res = await this.api.media.deleteFile( typeof filename === "string" ? filename : filename.path ); if (res.ok) { queryClient.invalidateQueries({ queryKey: ["data", "entity", "media"] }); return true; } return false; } }; }; query = (options?: Partial) => { const queryOptions = this.queryOptions(options); return { data: { entity: (name: string) => { return { readOne: ( id: number, query: Partial> = {} ): any => { return useQuery({ ...queryOptions, queryKey: ["data", "entity", name, id, { query }], queryFn: async () => { return await this.api.data.readOne(name, id, query); } }); }, readMany: ( query: Partial = { limit: 10, offset: 0 } ): UseQueryResult => { return useQuery({ ...(queryOptions as any), // @todo: fix typing queryKey: ["data", "entity", name, { query }], queryFn: async () => { return await this.api.data.readMany(name, query); } }); }, readManyByReference: ( id: number, reference: string, referenced_entity?: string, // required for query invalidation query: Partial = { limit: 10, offset: 0 } ): UseQueryResult> => { return useQuery({ ...(queryOptions as any), // @todo: fix typing queryKey: [ "data", "entity", referenced_entity ?? reference, { name, id, reference, query } ], queryFn: async () => { return await this.api.data.readManyByReference( name, id, reference, query ); } }); }, count: ( where: RepoQuery["where"] = {} ): UseQueryResult> => { return useQuery({ ...(queryOptions as any), // @todo: fix typing queryKey: ["data", "entity", name, "fn", "count", { where }], queryFn: async () => { return await this.api.data.count(name, where); } }); } }; } } }; }; // @todo: centralize, improve __invalidate = (...args: any[]) => { console.log("___invalidate", ["data", "entity", ...args]); queryClient.invalidateQueries({ queryKey: ["data", "entity", ...args] }); }; // @todo: must return response... why? mutation = { data: { entity: (name: string) => { return { update: (id: number): any => { return useMutation({ mutationFn: async (input: EntityData) => { return await this.api.data.updateOne(name, id, input); }, onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: ["data", "entity", name] }); } }); }, create: (): any => { return useMutation({ mutationFn: async (input: EntityData) => { return await this.api.data.createOne(name, input); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["data", "entity", name] }); } }); }, delete: (id: number): any => { return useMutation({ mutationFn: async () => { return await this.api.data.deleteOne(name, id); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["data", "entity", name] }); } }); } }; } } }; }