Implement Optimistic Effects (#1415)
* Fix person deletion not reflected on Opportunities POC * Fix companies, user deletion * Implement optimistic effects * Implement optimistic effects * Implement optimistic effects * Fix accoding to PR
This commit is contained in:
@ -0,0 +1,48 @@
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { optimisticEffectState } from '../states/optimisticEffectState';
|
||||
import { OptimisticEffect } from '../types/OptimisticEffect';
|
||||
|
||||
export function useOptimisticEffect() {
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const registerOptimisticEffect = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
(optimisticEffect: OptimisticEffect<unknown, unknown>) => {
|
||||
const { key } = optimisticEffect;
|
||||
const optimisticEffects = snapshot
|
||||
.getLoadable(optimisticEffectState)
|
||||
.getValue();
|
||||
|
||||
set(optimisticEffectState, {
|
||||
...optimisticEffects,
|
||||
[key]: optimisticEffect,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
const triggerOptimisticEffects = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
(typename: string, entities: any[]) => {
|
||||
const optimisticEffects = snapshot
|
||||
.getLoadable(optimisticEffectState)
|
||||
.getValue();
|
||||
|
||||
Object.values(optimisticEffects).forEach((optimisticEffect) => {
|
||||
if (optimisticEffect.typename === typename) {
|
||||
optimisticEffect.resolver({
|
||||
cache: apolloClient.cache,
|
||||
entities,
|
||||
variables: optimisticEffect.variables,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
registerOptimisticEffect,
|
||||
triggerOptimisticEffects,
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
import { OptimisticEffect } from '../types/OptimisticEffect';
|
||||
|
||||
export const optimisticEffectState = atom<
|
||||
Record<string, OptimisticEffect<unknown, unknown>>
|
||||
>({
|
||||
key: 'optimisticEffectState',
|
||||
default: {},
|
||||
});
|
||||
@ -0,0 +1,18 @@
|
||||
import { ApolloCache } from '@apollo/client';
|
||||
|
||||
type OptimisticEffectResolver<T, QueryVariables> = ({
|
||||
cache,
|
||||
entities,
|
||||
variables,
|
||||
}: {
|
||||
cache: ApolloCache<T>;
|
||||
entities: T[];
|
||||
variables: QueryVariables;
|
||||
}) => void;
|
||||
|
||||
export type OptimisticEffect<T, QueryVariables> = {
|
||||
key: string;
|
||||
typename: string;
|
||||
variables: QueryVariables;
|
||||
resolver: OptimisticEffectResolver<T, QueryVariables>;
|
||||
};
|
||||
@ -0,0 +1,47 @@
|
||||
import { ApolloCache } from '@apollo/client';
|
||||
|
||||
import {
|
||||
Company,
|
||||
GetCompaniesQuery,
|
||||
GetCompaniesQueryVariables,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
import { GET_COMPANIES } from '../queries/getCompanies';
|
||||
|
||||
function optimisticEffectResolver({
|
||||
cache,
|
||||
entities,
|
||||
variables,
|
||||
}: {
|
||||
cache: ApolloCache<Company>;
|
||||
entities: Company[];
|
||||
variables: GetCompaniesQueryVariables;
|
||||
}) {
|
||||
const existingData = cache.readQuery<GetCompaniesQuery>({
|
||||
query: GET_COMPANIES,
|
||||
variables: { orderBy: variables.orderBy, where: variables.where },
|
||||
});
|
||||
|
||||
if (!existingData) {
|
||||
return;
|
||||
}
|
||||
|
||||
cache.writeQuery({
|
||||
query: GET_COMPANIES,
|
||||
variables: { orderBy: variables.orderBy, where: variables.where },
|
||||
data: {
|
||||
companies: [...entities, ...existingData.companies],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function getCompaniesOptimisticEffect(
|
||||
variables: GetCompaniesQueryVariables,
|
||||
) {
|
||||
return {
|
||||
key: 'generic-entity-table-data-companies',
|
||||
variables: variables,
|
||||
typename: 'Company',
|
||||
resolver: optimisticEffectResolver,
|
||||
};
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
import { companiesAvailableColumnDefinitions } from '@/companies/constants/companiesAvailableColumnDefinitions';
|
||||
import { getCompaniesOptimisticEffect } from '@/companies/graphql/optimistic-effects/getCompaniesOptimisticEffect';
|
||||
import { useCompanyTableActionBarEntries } from '@/companies/hooks/useCompanyTableActionBarEntries';
|
||||
import { useCompanyTableContextMenuEntries } from '@/companies/hooks/useCompanyTableContextMenuEntries';
|
||||
import { useSpreadsheetCompanyImport } from '@/companies/hooks/useSpreadsheetCompanyImport';
|
||||
@ -53,6 +54,7 @@ export function CompanyTable() {
|
||||
<GenericEntityTableData
|
||||
getRequestResultKey="companies"
|
||||
useGetRequest={useGetCompaniesQuery}
|
||||
getRequestOptimisticEffect={getCompaniesOptimisticEffect}
|
||||
orderBy={orderBy.length ? orderBy : [{ createdAt: SortOrder.Desc }]}
|
||||
whereFilters={whereFilters}
|
||||
filterDefinitionArray={companiesFilters}
|
||||
|
||||
@ -0,0 +1,45 @@
|
||||
import { ApolloCache } from '@apollo/client';
|
||||
|
||||
import {
|
||||
GetPeopleQuery,
|
||||
GetPeopleQueryVariables,
|
||||
Person,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
import { GET_PEOPLE } from '../queries/getPeople';
|
||||
|
||||
function optimisticEffectResolver({
|
||||
cache,
|
||||
entities,
|
||||
variables,
|
||||
}: {
|
||||
cache: ApolloCache<Person>;
|
||||
entities: Person[];
|
||||
variables: GetPeopleQueryVariables;
|
||||
}) {
|
||||
const existingData = cache.readQuery<GetPeopleQuery>({
|
||||
query: GET_PEOPLE,
|
||||
variables: { orderBy: variables.orderBy, where: variables.where },
|
||||
});
|
||||
|
||||
if (!existingData) {
|
||||
return;
|
||||
}
|
||||
|
||||
cache.writeQuery({
|
||||
query: GET_PEOPLE,
|
||||
variables: { orderBy: variables.orderBy, where: variables.where },
|
||||
data: {
|
||||
people: [...entities, ...existingData.people],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function getPeopleOptimisticEffect(variables: GetPeopleQueryVariables) {
|
||||
return {
|
||||
key: 'generic-entity-table-data-person',
|
||||
variables: variables,
|
||||
typename: 'Person',
|
||||
resolver: optimisticEffectResolver,
|
||||
};
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
import { peopleAvailableColumnDefinitions } from '@/people/constants/peopleAvailableColumnDefinitions';
|
||||
import { getPeopleOptimisticEffect } from '@/people/graphql/optimistic-effect-callback/getPeopleOptimisticEffect';
|
||||
import { usePersonTableContextMenuEntries } from '@/people/hooks/usePeopleTableContextMenuEntries';
|
||||
import { usePersonTableActionBarEntries } from '@/people/hooks/usePersonTableActionBarEntries';
|
||||
import { useSpreadsheetPersonImport } from '@/people/hooks/useSpreadsheetPersonImport';
|
||||
@ -52,6 +53,7 @@ export function PeopleTable() {
|
||||
<GenericEntityTableData
|
||||
getRequestResultKey="people"
|
||||
useGetRequest={useGetPeopleQuery}
|
||||
getRequestOptimisticEffect={getPeopleOptimisticEffect}
|
||||
orderBy={orderBy.length ? orderBy : [{ createdAt: SortOrder.Desc }]}
|
||||
whereFilters={whereFilters}
|
||||
filterDefinitionArray={peopleFilters}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { useOptimisticEffect } from '@/apollo/optimistic-effect/hooks/useOptimisticEffect';
|
||||
import { OptimisticEffect } from '@/apollo/optimistic-effect/types/OptimisticEffect';
|
||||
import { FilterDefinition } from '@/ui/filter-n-sort/types/FilterDefinition';
|
||||
import { useSetEntityTableData } from '@/ui/table/hooks/useSetEntityTableData';
|
||||
import { SortOrder } from '~/generated/graphql';
|
||||
@ -7,6 +9,7 @@ import { SortOrder } from '~/generated/graphql';
|
||||
export function GenericEntityTableData({
|
||||
useGetRequest,
|
||||
getRequestResultKey,
|
||||
getRequestOptimisticEffect,
|
||||
orderBy = [
|
||||
{
|
||||
createdAt: SortOrder.Desc,
|
||||
@ -19,6 +22,7 @@ export function GenericEntityTableData({
|
||||
}: {
|
||||
useGetRequest: any;
|
||||
getRequestResultKey: string;
|
||||
getRequestOptimisticEffect: (variables: any) => OptimisticEffect<any, any>;
|
||||
orderBy?: any;
|
||||
whereFilters?: any;
|
||||
filterDefinitionArray: FilterDefinition[];
|
||||
@ -26,11 +30,16 @@ export function GenericEntityTableData({
|
||||
setContextMenuEntries?: () => void;
|
||||
}) {
|
||||
const setEntityTableData = useSetEntityTableData();
|
||||
const { registerOptimisticEffect } = useOptimisticEffect();
|
||||
|
||||
useGetRequest({
|
||||
variables: { orderBy, where: whereFilters },
|
||||
onCompleted: (data: any) => {
|
||||
const entities = data[getRequestResultKey] ?? [];
|
||||
setEntityTableData(entities, filterDefinitionArray);
|
||||
registerOptimisticEffect(
|
||||
getRequestOptimisticEffect({ orderBy, where: whereFilters }),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { useOptimisticEffect } from '@/apollo/optimistic-effect/hooks/useOptimisticEffect';
|
||||
import { CompanyTable } from '@/companies/table/components/CompanyTable';
|
||||
import { SEARCH_COMPANY_QUERY } from '@/search/graphql/queries/searchCompanyQuery';
|
||||
import { SpreadsheetImportProvider } from '@/spreadsheet-import/provider/components/SpreadsheetImportProvider';
|
||||
@ -30,6 +31,7 @@ export function Companies() {
|
||||
const [insertCompany] = useInsertOneCompanyMutation();
|
||||
const upsertEntityTableItem = useUpsertEntityTableItem();
|
||||
const upsertTableRowIds = useUpsertTableRowId();
|
||||
const { triggerOptimisticEffects } = useOptimisticEffect();
|
||||
|
||||
async function handleAddButtonClick() {
|
||||
const newCompanyId: string = v4();
|
||||
@ -61,6 +63,7 @@ export function Companies() {
|
||||
if (data?.createOneCompany) {
|
||||
upsertTableRowIds(data?.createOneCompany.id);
|
||||
upsertEntityTableItem(data?.createOneCompany);
|
||||
triggerOptimisticEffects('Company', [data?.createOneCompany]);
|
||||
}
|
||||
},
|
||||
refetchQueries: [getOperationName(SEARCH_COMPANY_QUERY) ?? ''],
|
||||
|
||||
@ -2,6 +2,7 @@ import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { useOptimisticEffect } from '@/apollo/optimistic-effect/hooks/useOptimisticEffect';
|
||||
import { PeopleTable } from '@/people/table/components/PeopleTable';
|
||||
import { SpreadsheetImportProvider } from '@/spreadsheet-import/provider/components/SpreadsheetImportProvider';
|
||||
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
|
||||
@ -28,6 +29,7 @@ export function People() {
|
||||
const [insertOnePerson] = useInsertOnePersonMutation();
|
||||
const upsertEntityTableItem = useUpsertEntityTableItem();
|
||||
const upsertTableRowIds = useUpsertTableRowId();
|
||||
const { triggerOptimisticEffects } = useOptimisticEffect();
|
||||
|
||||
async function handleAddButtonClick() {
|
||||
const newPersonId: string = v4();
|
||||
@ -50,10 +52,11 @@ export function People() {
|
||||
createdAt: '',
|
||||
},
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
update: (_cache, { data }) => {
|
||||
if (data?.createOnePerson) {
|
||||
upsertTableRowIds(data?.createOnePerson.id);
|
||||
upsertEntityTableItem(data?.createOnePerson);
|
||||
triggerOptimisticEffects('Person', [data?.createOnePerson]);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user