Fix optimistic rendering issues on board and table (#2846)
* Fix optimistic rendering issues on board and table * Remove dead code * Improve re-renders of Table * Remove re-renders on board
This commit is contained in:
@ -3,6 +3,7 @@ import styled from '@emotion/styled';
|
||||
import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { RecordTable } from '@/ui/object/record-table/components/RecordTable';
|
||||
import { TableOptionsDropdownId } from '@/ui/object/record-table/constants/TableOptionsDropdownId';
|
||||
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
|
||||
@ -12,8 +13,6 @@ import { mapViewFieldsToColumnDefinitions } from '@/views/utils/mapViewFieldsToC
|
||||
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
|
||||
import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts';
|
||||
|
||||
import { useUpdateOneRecord } from '../hooks/useUpdateOneRecord';
|
||||
|
||||
import { RecordTableEffect } from './RecordTableEffect';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
|
||||
@ -6,6 +6,7 @@ import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
|
||||
import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus';
|
||||
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||
import { IconBuildingSkyscraper } from '@/ui/display/icon';
|
||||
import { PageAddButton } from '@/ui/layout/page/PageAddButton';
|
||||
import { PageBody } from '@/ui/layout/page/PageBody';
|
||||
@ -15,8 +16,6 @@ import { PageHotkeysEffect } from '@/ui/layout/page/PageHotkeysEffect';
|
||||
import { RecordTableActionBar } from '@/ui/object/record-table/action-bar/components/RecordTableActionBar';
|
||||
import { RecordTableContextMenu } from '@/ui/object/record-table/context-menu/components/RecordTableContextMenu';
|
||||
|
||||
import { useCreateOneRecord } from '../hooks/useCreateOneRecord';
|
||||
|
||||
import { RecordTableContainer } from './RecordTableContainer';
|
||||
|
||||
const StyledTableContainer = styled.div`
|
||||
|
||||
@ -16,26 +16,49 @@ export const getRecordOptimisticEffectDefinition = ({
|
||||
resolver: ({
|
||||
currentData,
|
||||
newData,
|
||||
deletedRecordIds,
|
||||
}: {
|
||||
currentData: unknown;
|
||||
newData: unknown;
|
||||
newData: { id: string } & Record<string, any>;
|
||||
deletedRecordIds?: string[];
|
||||
}) => {
|
||||
const newRecordPaginatedCacheField = produce<
|
||||
PaginatedRecordTypeResults<any>
|
||||
>(currentData as PaginatedRecordTypeResults<any>, (draft) => {
|
||||
if (!draft) {
|
||||
return {
|
||||
edges: [{ node: newData, cursor: '' }],
|
||||
pageInfo: {
|
||||
endCursor: '',
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
startCursor: '',
|
||||
},
|
||||
};
|
||||
if (newData) {
|
||||
if (!draft) {
|
||||
return {
|
||||
__typename: `${capitalize(objectMetadataItem.nameSingular)}Edge`,
|
||||
edges: [{ node: newData, cursor: '' }],
|
||||
pageInfo: {
|
||||
endCursor: '',
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
startCursor: '',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const existingRecord = draft.edges.find(
|
||||
(edge) => edge.node.id === newData.id,
|
||||
);
|
||||
if (existingRecord) {
|
||||
existingRecord.node = newData;
|
||||
return;
|
||||
}
|
||||
|
||||
draft.edges.unshift({
|
||||
node: newData,
|
||||
cursor: '',
|
||||
__typename: `${capitalize(objectMetadataItem.nameSingular)}Edge`,
|
||||
});
|
||||
}
|
||||
|
||||
draft.edges.unshift({ node: newData, cursor: '' });
|
||||
if (deletedRecordIds) {
|
||||
draft.edges = draft.edges.filter(
|
||||
(edge) => !deletedRecordIds.includes(edge.node.id),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return newRecordPaginatedCacheField;
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import { useMutation } from '@apollo/client';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { useOptimisticEffect } from '@/apollo/optimistic-effect/hooks/useOptimisticEffect';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
|
||||
import { useGenerateEmptyRecord } from '@/object-record/hooks/useGenerateEmptyRecord';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const useCreateOneRecord = <T>({
|
||||
@ -20,21 +21,42 @@ export const useCreateOneRecord = <T>({
|
||||
);
|
||||
|
||||
// TODO: type this with a minimal type at least with Record<string, any>
|
||||
const [mutate] = useMutation(createOneRecordMutation);
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const { generateEmptyRecord } = useGenerateEmptyRecord({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const createOneRecord = async (input: Record<string, any>) => {
|
||||
const createdObject = await mutate({
|
||||
const recordId = v4();
|
||||
|
||||
triggerOptimisticEffects(
|
||||
`${capitalize(objectMetadataItem.nameSingular)}Edge`,
|
||||
generateEmptyRecord(recordId),
|
||||
);
|
||||
|
||||
const createdObject = await apolloClient.mutate({
|
||||
mutation: createOneRecordMutation,
|
||||
variables: {
|
||||
input: { ...input, id: v4() },
|
||||
input: { ...input, id: recordId },
|
||||
},
|
||||
optimisticResponse: {
|
||||
[`create${capitalize(objectMetadataItem.nameSingular)}`]:
|
||||
generateEmptyRecord(recordId),
|
||||
},
|
||||
});
|
||||
|
||||
if (!createdObject.data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
triggerOptimisticEffects(
|
||||
`${capitalize(objectMetadataItem.nameSingular)}Edge`,
|
||||
createdObject.data[
|
||||
`create${capitalize(objectMetadataItem.nameSingular)}`
|
||||
],
|
||||
);
|
||||
|
||||
return createdObject.data[
|
||||
`create${capitalize(objectMetadataItem.nameSingular)}`
|
||||
] as T;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useMutation } from '@apollo/client';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
|
||||
import { useOptimisticEffect } from '@/apollo/optimistic-effect/hooks/useOptimisticEffect';
|
||||
import { useOptimisticEvict } from '@/apollo/optimistic-effect/hooks/useOptimisticEvict';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
|
||||
@ -10,6 +11,9 @@ export const useDeleteOneRecord = <T>({
|
||||
objectNameSingular,
|
||||
}: ObjectMetadataItemIdentifier) => {
|
||||
const { performOptimisticEvict } = useOptimisticEvict();
|
||||
const { triggerOptimisticEffects } = useOptimisticEffect({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { objectMetadataItem, deleteOneRecordMutation } = useObjectMetadataItem(
|
||||
{
|
||||
@ -17,16 +21,15 @@ export const useDeleteOneRecord = <T>({
|
||||
},
|
||||
);
|
||||
|
||||
// TODO: type this with a minimal type at least with Record<string, any>
|
||||
const [mutate] = useMutation(deleteOneRecordMutation);
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const deleteOneRecord = useCallback(
|
||||
async (idToDelete: string) => {
|
||||
const deletedRecord = await mutate({
|
||||
variables: {
|
||||
idToDelete,
|
||||
},
|
||||
});
|
||||
triggerOptimisticEffects(
|
||||
`${capitalize(objectMetadataItem.nameSingular)}Edge`,
|
||||
undefined,
|
||||
[idToDelete],
|
||||
);
|
||||
|
||||
performOptimisticEvict(
|
||||
capitalize(objectMetadataItem.nameSingular),
|
||||
@ -34,11 +37,24 @@ export const useDeleteOneRecord = <T>({
|
||||
idToDelete,
|
||||
);
|
||||
|
||||
const deletedRecord = await apolloClient.mutate({
|
||||
mutation: deleteOneRecordMutation,
|
||||
variables: {
|
||||
idToDelete,
|
||||
},
|
||||
});
|
||||
|
||||
return deletedRecord.data[
|
||||
`create${capitalize(objectMetadataItem.nameSingular)}`
|
||||
] as T;
|
||||
},
|
||||
[performOptimisticEvict, objectMetadataItem, mutate],
|
||||
[
|
||||
triggerOptimisticEffects,
|
||||
objectMetadataItem.nameSingular,
|
||||
performOptimisticEvict,
|
||||
apolloClient,
|
||||
deleteOneRecordMutation,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@ -214,7 +214,7 @@ export const useFindManyRecords = <
|
||||
|
||||
return {
|
||||
objectMetadataItem,
|
||||
records,
|
||||
records: records as RecordType[],
|
||||
loading,
|
||||
error,
|
||||
fetchMoreRecords,
|
||||
|
||||
177
front/src/modules/object-record/hooks/useGenerateEmptyRecord.ts
Normal file
177
front/src/modules/object-record/hooks/useGenerateEmptyRecord.ts
Normal file
@ -0,0 +1,177 @@
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
|
||||
export const useGenerateEmptyRecord = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
const generateEmptyRecord = (id: string) => {
|
||||
if (objectMetadataItem.nameSingular === 'company') {
|
||||
return {
|
||||
id,
|
||||
domainName: '',
|
||||
accountOwnerId: null,
|
||||
createdAt: '2023-12-05T16:04:42.261Z',
|
||||
address: '',
|
||||
people: [
|
||||
{
|
||||
edges: [],
|
||||
__typename: 'PersonConnection',
|
||||
},
|
||||
],
|
||||
xLink: {
|
||||
label: '',
|
||||
url: '',
|
||||
__typename: 'Link',
|
||||
},
|
||||
attachments: {
|
||||
edges: [],
|
||||
__typename: 'AttachmentConnection',
|
||||
},
|
||||
activityTargets: {
|
||||
edges: [],
|
||||
__typename: 'ActivityTargetConnection',
|
||||
},
|
||||
idealCustomerProfile: null,
|
||||
annualRecurringRevenue: {
|
||||
amountMicros: null,
|
||||
currencyCode: null,
|
||||
__typename: 'Currency',
|
||||
},
|
||||
updatedAt: '2023-12-05T16:04:42.261Z',
|
||||
employees: null,
|
||||
accountOwner: null,
|
||||
name: '',
|
||||
linkedinLink: {
|
||||
label: '',
|
||||
url: '',
|
||||
__typename: 'Link',
|
||||
},
|
||||
favorites: {
|
||||
edges: [],
|
||||
__typename: 'FavoriteConnection',
|
||||
},
|
||||
opportunities: {
|
||||
edges: [],
|
||||
__typename: 'OpportunityConnection',
|
||||
},
|
||||
__typename: 'Company',
|
||||
};
|
||||
}
|
||||
|
||||
if (objectMetadataItem.nameSingular === 'person') {
|
||||
return {
|
||||
id,
|
||||
activityTargets: {
|
||||
edges: [],
|
||||
__typename: 'ActivityTargetConnection',
|
||||
},
|
||||
opportunities: {
|
||||
edges: [],
|
||||
__typename: 'OpportunityConnection',
|
||||
},
|
||||
companyId: null,
|
||||
favorites: {
|
||||
edges: [],
|
||||
__typename: 'FavoriteConnection',
|
||||
},
|
||||
phone: '',
|
||||
company: null,
|
||||
xLink: {
|
||||
label: '',
|
||||
url: '',
|
||||
__typename: 'Link',
|
||||
},
|
||||
jobTitle: '',
|
||||
pointOfContactForOpportunities: {
|
||||
edges: [],
|
||||
__typename: 'OpportunityConnection',
|
||||
},
|
||||
email: '',
|
||||
attachments: {
|
||||
edges: [],
|
||||
__typename: 'AttachmentConnection',
|
||||
},
|
||||
name: {
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
__typename: 'FullName',
|
||||
},
|
||||
avatarUrl: '',
|
||||
updatedAt: '2023-12-05T16:45:11.840Z',
|
||||
createdAt: '2023-12-05T16:45:11.840Z',
|
||||
city: '',
|
||||
linkedinLink: {
|
||||
label: '',
|
||||
url: '',
|
||||
__typename: 'Link',
|
||||
},
|
||||
__typename: 'Person',
|
||||
};
|
||||
}
|
||||
|
||||
if (objectMetadataItem.nameSingular === 'opportunity') {
|
||||
return {
|
||||
id,
|
||||
pipelineStepId: '30b14887-d592-427d-bd97-6e670158db02',
|
||||
closeDate: null,
|
||||
companyId: '04b2e9f5-0713-40a5-8216-82802401d33e',
|
||||
updatedAt: '2023-12-05T16:46:27.621Z',
|
||||
pipelineStep: {
|
||||
id: '30b14887-d592-427d-bd97-6e670158db02',
|
||||
position: 2,
|
||||
name: 'Meeting',
|
||||
updatedAt: '2023-12-05T11:29:21.485Z',
|
||||
createdAt: '2023-12-05T11:29:21.485Z',
|
||||
color: 'sky',
|
||||
__typename: 'PipelineStep',
|
||||
},
|
||||
probability: '0',
|
||||
pointOfContactId: null,
|
||||
personId: null,
|
||||
amount: {
|
||||
amountMicros: null,
|
||||
currencyCode: null,
|
||||
__typename: 'Currency',
|
||||
},
|
||||
createdAt: '2023-12-05T16:46:27.621Z',
|
||||
pointOfContact: null,
|
||||
person: null,
|
||||
company: {
|
||||
id: '04b2e9f5-0713-40a5-8216-82802401d33e',
|
||||
domainName: 'qonto.com',
|
||||
accountOwnerId: null,
|
||||
createdAt: '2023-12-05T11:29:21.484Z',
|
||||
address: '',
|
||||
xLink: {
|
||||
label: '',
|
||||
url: '',
|
||||
__typename: 'Link',
|
||||
},
|
||||
idealCustomerProfile: null,
|
||||
annualRecurringRevenue: {
|
||||
amountMicros: null,
|
||||
currencyCode: null,
|
||||
__typename: 'Currency',
|
||||
},
|
||||
updatedAt: '2023-12-05T11:29:21.484Z',
|
||||
employees: null,
|
||||
name: 'Qonto',
|
||||
linkedinLink: {
|
||||
label: '',
|
||||
url: '',
|
||||
__typename: 'Link',
|
||||
},
|
||||
__typename: 'Company',
|
||||
},
|
||||
__typename: 'Opportunity',
|
||||
};
|
||||
}
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
return {
|
||||
generateEmptyRecord: generateEmptyRecord,
|
||||
};
|
||||
};
|
||||
104
front/src/modules/object-record/hooks/useObjectRecordBoard.1.ts
Normal file
104
front/src/modules/object-record/hooks/useObjectRecordBoard.1.ts
Normal file
@ -0,0 +1,104 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { Company } from '@/companies/types/Company';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { PaginatedRecordTypeResults } from '@/object-record/types/PaginatedRecordTypeResults';
|
||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||
import { PipelineStep } from '@/pipeline/types/PipelineStep';
|
||||
import { turnFiltersIntoWhereClause } from '@/ui/object/object-filter-dropdown/utils/turnFiltersIntoWhereClause';
|
||||
import { turnSortsIntoOrderBy } from '@/ui/object/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
||||
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||
|
||||
import { useFindManyRecords } from './useFindManyRecords';
|
||||
|
||||
export const useObjectRecordBoard = () => {
|
||||
const objectNameSingular = 'opportunity';
|
||||
|
||||
const { objectMetadataItem: foundObjectMetadataItem } = useObjectMetadataItem(
|
||||
{
|
||||
objectNameSingular,
|
||||
},
|
||||
);
|
||||
|
||||
const {
|
||||
isBoardLoadedState,
|
||||
boardFiltersState,
|
||||
boardSortsState,
|
||||
savedCompaniesState,
|
||||
savedOpportunitiesState,
|
||||
savedPipelineStepsState,
|
||||
} = useRecordBoardScopedStates();
|
||||
|
||||
const setIsBoardLoaded = useSetRecoilState(isBoardLoadedState);
|
||||
|
||||
const boardFilters = useRecoilValue(boardFiltersState);
|
||||
const boardSorts = useRecoilValue(boardSortsState);
|
||||
|
||||
const setSavedCompanies = useSetRecoilState(savedCompaniesState);
|
||||
|
||||
const [savedOpportunities] = useRecoilState(savedOpportunitiesState);
|
||||
|
||||
const [savedPipelineSteps, setSavedPipelineSteps] = useRecoilState(
|
||||
savedPipelineStepsState,
|
||||
);
|
||||
|
||||
const filter = turnFiltersIntoWhereClause(
|
||||
boardFilters,
|
||||
foundObjectMetadataItem?.fields ?? [],
|
||||
);
|
||||
const orderBy = turnSortsIntoOrderBy(
|
||||
boardSorts,
|
||||
foundObjectMetadataItem?.fields ?? [],
|
||||
);
|
||||
|
||||
useFindManyRecords({
|
||||
objectNameSingular: 'pipelineStep',
|
||||
filter: {},
|
||||
onCompleted: useCallback(
|
||||
(data: PaginatedRecordTypeResults<PipelineStep>) => {
|
||||
setSavedPipelineSteps(data.edges.map((edge) => edge.node));
|
||||
},
|
||||
[setSavedPipelineSteps],
|
||||
),
|
||||
});
|
||||
|
||||
const {
|
||||
records: opportunities,
|
||||
loading,
|
||||
fetchMoreRecords: fetchMoreOpportunities,
|
||||
} = useFindManyRecords<Opportunity>({
|
||||
skip: !savedPipelineSteps.length,
|
||||
objectNameSingular: 'opportunity',
|
||||
filter: filter,
|
||||
orderBy: orderBy,
|
||||
onCompleted: useCallback(() => {
|
||||
setIsBoardLoaded(true);
|
||||
}, [setIsBoardLoaded]),
|
||||
});
|
||||
|
||||
const { fetchMoreRecords: fetchMoreCompanies } = useFindManyRecords({
|
||||
skip: !savedOpportunities.length,
|
||||
objectNameSingular: 'company',
|
||||
filter: {
|
||||
id: {
|
||||
in: savedOpportunities.map(
|
||||
(opportunity) => opportunity.companyId || '',
|
||||
),
|
||||
},
|
||||
},
|
||||
onCompleted: useCallback(
|
||||
(data: PaginatedRecordTypeResults<Company>) => {
|
||||
setSavedCompanies(data.edges.map((edge) => edge.node));
|
||||
},
|
||||
[setSavedCompanies],
|
||||
),
|
||||
});
|
||||
|
||||
return {
|
||||
opportunities,
|
||||
loading,
|
||||
fetchMoreOpportunities,
|
||||
fetchMoreCompanies,
|
||||
};
|
||||
};
|
||||
@ -9,13 +9,11 @@ import { PipelineStep } from '@/pipeline/types/PipelineStep';
|
||||
import { turnFiltersIntoWhereClause } from '@/ui/object/object-filter-dropdown/utils/turnFiltersIntoWhereClause';
|
||||
import { turnSortsIntoOrderBy } from '@/ui/object/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
||||
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||
import { useUpdateCompanyBoardCardIdsInternal } from '@/ui/object/record-board/hooks/internal/useUpdateCompanyBoardCardIdsInternal';
|
||||
|
||||
import { useFindManyRecords } from './useFindManyRecords';
|
||||
|
||||
export const useObjectRecordBoard = () => {
|
||||
const objectNameSingular = 'opportunity';
|
||||
const updateCompanyBoardCardIds = useUpdateCompanyBoardCardIdsInternal();
|
||||
|
||||
const { objectMetadataItem: foundObjectMetadataItem } = useObjectMetadataItem(
|
||||
{
|
||||
@ -71,24 +69,14 @@ export const useObjectRecordBoard = () => {
|
||||
records: opportunities,
|
||||
loading,
|
||||
fetchMoreRecords: fetchMoreOpportunities,
|
||||
} = useFindManyRecords({
|
||||
} = useFindManyRecords<Opportunity>({
|
||||
skip: !savedPipelineSteps.length,
|
||||
objectNameSingular: 'opportunity',
|
||||
filter: filter,
|
||||
orderBy: orderBy,
|
||||
onCompleted: useCallback(
|
||||
(data: PaginatedRecordTypeResults<Opportunity>) => {
|
||||
const pipelineProgresses: Array<Opportunity> = data.edges.map(
|
||||
(edge) => edge.node,
|
||||
);
|
||||
|
||||
updateCompanyBoardCardIds(pipelineProgresses);
|
||||
|
||||
setSavedOpportunities(pipelineProgresses);
|
||||
setIsBoardLoaded(true);
|
||||
},
|
||||
[setIsBoardLoaded, setSavedOpportunities, updateCompanyBoardCardIds],
|
||||
),
|
||||
onCompleted: useCallback(() => {
|
||||
setIsBoardLoaded(true);
|
||||
}, [setIsBoardLoaded]),
|
||||
});
|
||||
|
||||
const { fetchMoreRecords: fetchMoreCompanies } = useFindManyRecords({
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { useOptimisticEffect } from '@/apollo/optimistic-effect/hooks/useOptimisticEffect';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
||||
import { turnFiltersIntoWhereClause } from '@/ui/object/object-filter-dropdown/utils/turnFiltersIntoWhereClause';
|
||||
@ -8,8 +7,6 @@ import { turnSortsIntoOrderBy } from '@/ui/object/object-sort-dropdown/utils/tur
|
||||
import { useRecordTableScopedStates } from '@/ui/object/record-table/hooks/internal/useRecordTableScopedStates';
|
||||
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
|
||||
|
||||
import { getRecordOptimisticEffectDefinition } from '../graphql/optimistic-effect-definition/getRecordOptimisticEffectDefinition';
|
||||
|
||||
import { useFindManyRecords } from './useFindManyRecords';
|
||||
|
||||
export const useObjectRecordTable = () => {
|
||||
@ -25,10 +22,6 @@ export const useObjectRecordTable = () => {
|
||||
},
|
||||
);
|
||||
|
||||
const { registerOptimisticEffect } = useOptimisticEffect({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { tableFiltersState, tableSortsState } = useRecordTableScopedStates();
|
||||
|
||||
const tableFilters = useRecoilValue(tableFiltersState);
|
||||
@ -47,25 +40,12 @@ export const useObjectRecordTable = () => {
|
||||
objectNameSingular,
|
||||
filter,
|
||||
orderBy,
|
||||
onCompleted: (data) => {
|
||||
const entities = data.edges.map((edge) => edge.node) ?? [];
|
||||
|
||||
setRecordTableData(entities);
|
||||
|
||||
if (foundObjectMetadataItem) {
|
||||
registerOptimisticEffect({
|
||||
variables: { orderBy, filter, limit: 60 },
|
||||
definition: getRecordOptimisticEffectDefinition({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
}),
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
records,
|
||||
loading,
|
||||
fetchMoreRecords,
|
||||
setRecordTableData,
|
||||
};
|
||||
};
|
||||
|
||||
@ -12,7 +12,6 @@ import { ContextMenuEntry } from '@/ui/navigation/context-menu/types/ContextMenu
|
||||
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
|
||||
import { RecordTableScopeInternalContext } from '@/ui/object/record-table/scopes/scope-internal-context/RecordTableScopeInternalContext';
|
||||
import { selectedRowIdsSelector } from '@/ui/object/record-table/states/selectors/selectedRowIdsSelector';
|
||||
import { tableRowIdsState } from '@/ui/object/record-table/states/tableRowIdsState';
|
||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||
|
||||
type useRecordTableContextMenuEntriesProps = {
|
||||
@ -31,7 +30,6 @@ export const useRecordTableContextMenuEntries = (
|
||||
const setContextMenuEntries = useSetRecoilState(contextMenuEntriesState);
|
||||
const setActionBarEntriesState = useSetRecoilState(actionBarEntriesState);
|
||||
|
||||
const setTableRowIds = useSetRecoilState(tableRowIdsState);
|
||||
const selectedRowIds = useRecoilValue(selectedRowIdsSelector);
|
||||
|
||||
const { scopeId: objectNamePlural, resetTableRowSelection } = useRecordTable({
|
||||
@ -76,16 +74,11 @@ export const useRecordTableContextMenuEntries = (
|
||||
.getValue();
|
||||
|
||||
resetTableRowSelection();
|
||||
|
||||
if (deleteOneRecord) {
|
||||
for (const rowId of rowIdsToDelete) {
|
||||
await Promise.all(
|
||||
rowIdsToDelete.map(async (rowId) => {
|
||||
await deleteOneRecord(rowId);
|
||||
}
|
||||
|
||||
setTableRowIds((tableRowIds) =>
|
||||
tableRowIds.filter((id) => !rowIdsToDelete.includes(id)),
|
||||
);
|
||||
}
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@ -1,46 +0,0 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { entityFieldsFamilyState } from '@/ui/object/field/states/entityFieldsFamilyState';
|
||||
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
|
||||
import { isFetchingRecordTableDataState } from '@/ui/object/record-table/states/isFetchingRecordTableDataState';
|
||||
import { numberOfTableRowsState } from '@/ui/object/record-table/states/numberOfTableRowsState';
|
||||
import { tableRowIdsState } from '@/ui/object/record-table/states/tableRowIdsState';
|
||||
import { useViewBar } from '@/views/hooks/useViewBar';
|
||||
|
||||
export const useSetRecordTableData = () => {
|
||||
const { resetTableRowSelection } = useRecordTable();
|
||||
const { setEntityCountInCurrentView } = useViewBar();
|
||||
|
||||
return useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
<T extends { id: string } & Record<string, any>>(newEntityArray: T[]) => {
|
||||
for (const entity of newEntityArray) {
|
||||
const currentEntity = snapshot
|
||||
.getLoadable(entityFieldsFamilyState(entity.id))
|
||||
.valueOrThrow();
|
||||
|
||||
if (JSON.stringify(currentEntity) !== JSON.stringify(entity)) {
|
||||
set(entityFieldsFamilyState(entity.id), entity);
|
||||
}
|
||||
}
|
||||
|
||||
const entityIds = newEntityArray.map((entity) => entity.id);
|
||||
|
||||
set(tableRowIdsState, (currentRowIds) => {
|
||||
if (JSON.stringify(currentRowIds) !== JSON.stringify(entityIds)) {
|
||||
return entityIds;
|
||||
}
|
||||
|
||||
return currentRowIds;
|
||||
});
|
||||
|
||||
resetTableRowSelection();
|
||||
|
||||
set(numberOfTableRowsState, entityIds.length);
|
||||
setEntityCountInCurrentView(entityIds.length);
|
||||
|
||||
set(isFetchingRecordTableDataState, false);
|
||||
},
|
||||
[resetTableRowSelection, setEntityCountInCurrentView],
|
||||
);
|
||||
};
|
||||
@ -1,5 +1,4 @@
|
||||
import { useMutation } from '@apollo/client';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
|
||||
@ -17,7 +16,7 @@ export const useUpdateOneRecord = <T>({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const [mutateUpdateOneRecord] = useMutation(updateOneRecordMutation);
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const updateOneRecord = async ({
|
||||
idToUpdate,
|
||||
@ -30,7 +29,8 @@ export const useUpdateOneRecord = <T>({
|
||||
}) => {
|
||||
const cachedRecord = getRecordFromCache(idToUpdate);
|
||||
|
||||
const updatedRecord = await mutateUpdateOneRecord({
|
||||
const updatedRecord = await apolloClient.mutate({
|
||||
mutation: updateOneRecordMutation,
|
||||
variables: {
|
||||
idToUpdate: idToUpdate,
|
||||
input: {
|
||||
@ -43,12 +43,12 @@ export const useUpdateOneRecord = <T>({
|
||||
...input,
|
||||
},
|
||||
},
|
||||
refetchQueries: forceRefetch
|
||||
? [getOperationName(findManyRecordsQuery) ?? '']
|
||||
: undefined,
|
||||
awaitRefetchQueries: forceRefetch,
|
||||
});
|
||||
|
||||
if (!updatedRecord?.data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return updatedRecord.data[
|
||||
`update${capitalize(objectMetadataItem.nameSingular)}`
|
||||
] as T;
|
||||
|
||||
@ -3,6 +3,7 @@ export type PaginatedRecordTypeEdge<
|
||||
> = {
|
||||
node: RecordType;
|
||||
cursor: string;
|
||||
__typename?: string;
|
||||
};
|
||||
|
||||
export type PaginatedRecordTypeResults<
|
||||
|
||||
Reference in New Issue
Block a user