Skip to content

Instantly share code, notes, and snippets.

@crashmax-dev
Forked from artalar/client.ts
Created April 28, 2024 00:28
Show Gist options
  • Save crashmax-dev/96b0a95d7e4a091dcbc3e9f2c9a52195 to your computer and use it in GitHub Desktop.
Save crashmax-dev/96b0a95d7e4a091dcbc3e9f2c9a52195 to your computer and use it in GitHub Desktop.
reatomGql real example
import { fingerprint } from 'src/infrastructure/fingerprint';
import { Client, fetchExchange, makeOperation, mapExchange } from 'urql';
export const client = new Client({
url: '/api/graphql',
requestPolicy: 'network-only',
exchanges: [
mapExchange({
async onOperation(operation) {
return makeOperation(operation.kind, operation, {
...operation.context,
fetchOptions: {
headers: {
fingerprint,
'apollo-require-preflight': 'true',
},
},
});
},
}),
// It is important that fetchExchange should follow after mapExchange.
fetchExchange,
],
});
import { atom, onConnect, withDataAtom } from '@reatom/framework';
import { ROUTES } from 'src/config/routes';
import { reatomQuery } from 'src/infrastructure/graphql/reatomGql';
import { GET_LIST } from 'src/queries/list';
const paginationAtom = atom(100);
export const fetchList = reatomQuery(GET_LIST, (ctx, id: string) => ({
id,
take: ctx.get(paginationAtom),
})).pipe(withDataAtom([]));
onConnect(fetchVisitsList.dataAtom, (ctx) => {
const id = ctx.get(ROUTES.LIST.params.id);
id && fetchList(ctx, id);
});
import { parseAtoms, noop, type Atom, atom } from '@reatom/framework';
import {
type GqlAction,
reatomGql,
SKIP,
type CtxGql,
} from 'src/infrastructure/graphql';
import { type TypedDocumentNode } from 'urql';
import { type z } from 'zod';
import { reatomZod } from './reatomZod';
import { type PartialDeep, type ZodAtomization } from './reatomZod/types';
export const reatomZodGql = <
Schema extends z.ZodFirstPartySchemaTypes,
VariableData,
>(
type: Schema,
{
name,
query,
prepare,
initState,
type: gqlType = 'mutation',
debounce = 500,
}: {
name: string;
query: TypedDocumentNode<any, { data: VariableData }>;
prepare: (ctx: CtxGql, data: z.infer<Schema>) => SKIP | VariableData;
initState?: PartialDeep<z.infer<Schema>>;
type?: 'query' | 'mutation';
debounce?: number;
},
): {
model: ZodAtomization<Schema>;
state: Atom<z.infer<Schema>>;
sync: GqlAction<unknown, [], z.infer<Schema>>;
} => {
const state = atom((ctx) => parseAtoms(ctx, model), `${name}.state`);
const fetchQuery = reatomGql({
type: gqlType,
query,
prepare: (ctx) => {
const data = prepare(ctx, ctx.get(state));
return data === SKIP ? SKIP : { data };
},
debounce,
abort: 'last-in-win',
});
const model = reatomZod(type, {
sync: (ctx) => {
fetchQuery(ctx).catch(noop);
},
initState,
name,
});
return {
model,
sync: fetchQuery,
state,
};
};
export const updateNotificationPreference = reatomMutation(
UPDATE_NOTIFICATIONS_PREFERENCES,
(ctx, item: NotificationPreference) => ({ preferences: [item] }),
).pipe(
withStatusesAtom(),
withStatusNotification({
success: 'account:notifications.success',
error: 'account:notifications.error',
}),
);
import { onConnect, withCache, withDataAtom } from '@reatom/framework';
import { ROUTES } from 'src/config/routes';
import {
reatomMutation,
reatomQuery,
} from 'src/infrastructure/graphql/reatomGql';
import { AUTH_QUERIES } from 'src/queries/user/auth';
import { ME } from 'src/queries/user/profile';
export const fetchMe = reatomQuery(ME).pipe(
withDataAtom(null, (ctx, data) => data.me),
withCache({ length: 1, staleTime: Infinity, swr: false }),
);
onConnect(fetchMe.dataAtom, fetchMe);
export const logout = reatomMutation(AUTH_QUERIES.LOGOUT);
logout.onSettle.onCall(() => {
// TODO just recrate Reatom ctx in the app root and stay with SPA
// ensure to drop whole states
window.location.pathname = ROUTES.LOGIN.abs;
});
import { type AsyncAction } from "@reatom/framework";
import { t, type Namespace, type TFuncKey } from "i18next";
import { notification } from "src/shared/notifications";
export const withErrorNotification =
<T extends AsyncAction>(key: TFuncKey<Namespace>) =>
(anAsync: T): T => {
anAsync.onReject.onCall((ctx, error) =>
notification.error({ message: t(key) as string })
);
return anAsync;
};
export const withStatusNotification =
<T extends AsyncAction>({
success,
error,
}: {
success?: TFuncKey<Namespace>;
error?: TFuncKey<Namespace>;
}) =>
(anAsync: T): T => {
if (error) {
anAsync.onReject.onCall((ctx, e) =>
notification.error({ message: t(error) as string })
);
}
if (success) {
anAsync.onFulfill.onCall((ctx, e) =>
notification.success({ message: t(success) as string })
);
}
return anAsync;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment