import { ReactNode, useCallback, useEffect } from 'react'; import { UseMutationOptions, UseMutationResult } from 'react-query'; import { ToastContextState, useToast } from './ToastProvider'; export type MutationConfig = { mutation: T; name: string; }; declare type GetResults = T extends MutationConfig< MinimalMutationResult > ? MutationConfig> : T extends MinimalMutationResult ? MutationConfig> : MutationConfig>; declare type GetDescriptionBuilder = T extends MutationConfig< MinimalMutationResult > ? DescriptionBuilder : T extends MutationConfig> ? DescriptionBuilder : T extends MinimalMutationResult ? DescriptionBuilder : never; /** * MutationResults reducer recursively maps type param to results */ declare type MutationsResults = T extends [] ? [] : T extends [infer Head, ...infer Tail] ? [GetResults, ...MutationsResults] : T extends [infer Head] ? [GetResults] : unknown[] extends T ? T : never; enum DescriptionBuilderStatus { Success = 'success', Error = 'error', Idle = 'idle', } type DescriptionBuilder = { error?: Error; data?: Data; status: DescriptionBuilderStatus; name: string; }; declare type DescriptionBuilders = T extends [] ? [] : T extends [infer Head, ...infer Tail] ? [GetDescriptionBuilder, ...DescriptionBuilders] : T extends [infer Head] ? [GetDescriptionBuilder] : T extends UseMutationOptions< infer TMutationFnData, infer TError, infer TData >[] ? DescriptionBuilder< unknown extends TData ? TMutationFnData : TData, TError >[] : DescriptionBuilder[]; type Props = { mainMutation: MutationConfig; dependantMutations?: | readonly [...MutationsResults] | MutationConfig>[]; messageDescriptionBuilder: ( mutations: T extends [] ? [GetDescriptionBuilder] : [GetDescriptionBuilder, ...DescriptionBuilders], ) => ReactNode; toastProps?: Pick< ToastContextState, 'style' | 'autoDismiss' | 'position' | 'duration' | 'withProgressBar' >; } & ( | { onMainMutationSuccess?: () => void; onAllMutationsSuccess?: never } | { onAllMutationsSuccess?: () => void; onMainMutationSuccess?: never } ); export type MinimalMutationResult = Pick< UseMutationResult, 'isError' | 'isIdle' | 'isSuccess' | 'isLoading' | 'error' | 'data' >; export const useMutationsHandler = < MainMutationType extends MinimalMutationResult, T extends any[] | [], >({ mainMutation, dependantMutations, messageDescriptionBuilder, toastProps, ...rest }: Props) => { const { showToast } = useToast(); const mutations = [ mainMutation, ...(dependantMutations ? dependantMutations : []), ] as MutationConfig>[]; const handleMutationsCompletion = useCallback(async () => { const results = await Promise.all(mutations.map((m) => m.mutation)); const loadingMutations = mutations.filter( (_, index) => results[index].isLoading, ); const successMutations = mutations.filter( (_, index) => results[index].isSuccess, ); const errorMutations = mutations.filter( (_, index) => results[index].isError, ); const mainMutationDesc: GetDescriptionBuilder = { data: mainMutation.mutation?.data, status: mainMutation.mutation?.isSuccess ? DescriptionBuilderStatus.Success : mainMutation.mutation?.isIdle ? DescriptionBuilderStatus.Idle : DescriptionBuilderStatus.Error, name: mainMutation.name, } as GetDescriptionBuilder; const descriptionBuilders = [ mainMutationDesc, ...((dependantMutations?.map(({ mutation, name }) => ({ data: mutation.data, error: mutation.isError ? mutation.error : null, status: mutation.isSuccess ? DescriptionBuilderStatus.Success : mutation.isIdle ? DescriptionBuilderStatus.Idle : DescriptionBuilderStatus.Error, name, })) as DescriptionBuilders) || ([] as DescriptionBuilders)), ] as T extends [] ? [GetDescriptionBuilder] : [GetDescriptionBuilder, ...DescriptionBuilders]; if (loadingMutations.length === 0) { if (errorMutations.length > 0) { if (mainMutation.mutation?.isSuccess) { 'onMainMutationSuccess' in rest && rest?.onMainMutationSuccess?.(); } showToast({ open: true, status: 'error', message: messageDescriptionBuilder(descriptionBuilders), ...toastProps, }); return; } else if (successMutations.length > 0) { 'onMainMutationSuccess' in rest && rest?.onMainMutationSuccess?.(); if (successMutations.length === mutations.length) { 'onAllMutationsSuccess' in rest && rest?.onAllMutationsSuccess?.(); } showToast({ open: true, status: 'success', message: messageDescriptionBuilder(descriptionBuilders), ...(toastProps ? toastProps : {}), }); } } }, [JSON.stringify(mutations)]); useEffect(() => { handleMutationsCompletion(); }, [handleMutationsCompletion]); };