import { AUTH_ACCESS_TOKEN, AUTH_REFRESH_TOKEN } from "@/constants"; import { REFRESH_TOKEN } from "@/graphql/auth"; import { IFollower } from "@/types/actor/follower.model"; import { IParticipant } from "@/types/participant.model"; import { Paginate } from "@/types/paginate"; import { saveTokenData } from "@/utils/auth"; import { ApolloClient, FieldPolicy, NormalizedCacheObject, Reference, TypePolicies, } from "@apollo/client/core"; import introspectionQueryResultData from "../../fragmentTypes.json"; import { IMember } from "@/types/actor/member.model"; import { IComment } from "@/types/comment.model"; import { IEvent } from "@/types/event.model"; import { IActivity } from "@/types/activity.model"; import uniqBy from "lodash/uniqBy"; type possibleTypes = { name: string }; type schemaType = { kind: string; name: string; possibleTypes: possibleTypes[]; }; // eslint-disable-next-line no-underscore-dangle const types = introspectionQueryResultData.__schema.types as schemaType[]; export const possibleTypes = types.reduce((acc, type) => { if (type.kind === "INTERFACE") { acc[type.name] = type.possibleTypes.map(({ name }) => name); } return acc; }, {} as Record); const replaceMergePolicy = ( _existing: TExisting, incoming: TIncoming ): TIncoming => incoming; export const typePolicies: TypePolicies = { Discussion: { fields: { comments: paginatedLimitPagination(), }, }, Group: { fields: { organizedEvents: paginatedLimitPagination([ "afterDatetime", "beforeDatetime", ]), activity: paginatedLimitPagination(["type", "author"]), }, }, Person: { fields: { organizedEvents: paginatedLimitPagination(), participations: paginatedLimitPagination(["eventId"]), memberships: paginatedLimitPagination(["group"]), }, }, Event: { fields: { participants: paginatedLimitPagination(["roles"]), comments: pageLimitPagination(), relatedEvents: pageLimitPagination(), options: { merge: replaceMergePolicy }, participantStats: { merge: replaceMergePolicy }, }, }, Instance: { keyFields: ["domain"], }, RootQueryType: { fields: { relayFollowers: paginatedLimitPagination(), relayFollowings: paginatedLimitPagination([ "orderBy", "direction", ]), events: paginatedLimitPagination(), groups: paginatedLimitPagination([ "preferredUsername", "name", "domain", "local", "suspended", ]), persons: paginatedLimitPagination([ "preferredUsername", "name", "domain", "local", "suspended", ]), }, }, }; export async function refreshAccessToken( apolloClient: ApolloClient ): Promise { // Remove invalid access token, so the next request is not authenticated localStorage.removeItem(AUTH_ACCESS_TOKEN); const refreshToken = localStorage.getItem(AUTH_REFRESH_TOKEN); if (!refreshToken) { console.debug("Refresh token not found"); return false; } console.log("Refreshing access token."); try { const res = await apolloClient.mutate({ mutation: REFRESH_TOKEN, variables: { refreshToken, }, }); saveTokenData(res.data.refreshToken); return true; } catch (err) { console.debug("Failed to refresh token"); return false; } } type KeyArgs = FieldPolicy["keyArgs"]; export function pageLimitPagination( keyArgs: KeyArgs = false ): FieldPolicy { return { keyArgs, // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore merge(existing, incoming, { args }) { if (!incoming) return existing; if (!existing) return incoming; // existing will be empty the first time return doMerge(existing as Array, incoming as Array, args); }, }; } export function paginatedLimitPagination>( keyArgs: KeyArgs = false ): FieldPolicy> { return { keyArgs, // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore merge(existing, incoming, { args }) { if (!incoming) return existing; if (!existing) return incoming; // existing will be empty the first time return { total: incoming.total, elements: doMerge(existing.elements, incoming.elements, args), }; }, }; } function doMerge( existing: Array, incoming: Array, args: Record | null ): Array { const merged = existing && Array.isArray(existing) ? existing.slice(0) : []; const previous = incoming && Array.isArray(incoming) ? incoming.slice(0) : []; let res; if (args) { // Assume an page of 1 if args.page omitted. const { page = 1, limit = 10 } = args; for (let i = 0; i < previous.length; ++i) { merged[(page - 1) * limit + i] = previous[i]; } res = merged; } else { // It's unusual (probably a mistake) for a paginated field not // to receive any arguments, so you might prefer to throw an // exception here, instead of recovering by appending incoming // onto the existing array. res = [...merged, ...previous]; // eslint-disable-next-line no-underscore-dangle res = uniqBy(res, (elem: any) => elem.__ref); } return res; }