Use optimistic rendering when executing a workflow with manual trigger (#12695)
This PR adds optimistic rendering at two places: - In the `runWorkflowVersion`, to create a workflow run entry as fast as possible in the cache and render it immediately in the side panel. - In the `ListenUpdatesEffect`, to be sure the cache is properly set; we also need to set the record in the record store that's used in the fields card. ## Before https://github.com/user-attachments/assets/8b360ea9-c292-4e05-82a0-d2f12176bb6f ## After https://github.com/user-attachments/assets/2d11023c-2ceb-4fa3-a951-187b9a0b5743 ### With a slowed-down network https://github.com/user-attachments/assets/7d2a592a-1ea7-455b-856f-bf3d9d905061 ## Follow up I will create next a PR to ensure the viewport is always set when we know the dimensions of the nodes.
This commit is contained in:
committed by
GitHub
parent
a6b8830b91
commit
dae282ca0f
@ -1,6 +1,18 @@
|
||||
import { triggerUpdateRecordOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRecordOptimisticEffect';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
||||
import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename';
|
||||
import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord';
|
||||
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
||||
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { useOnDbEvent } from '@/subscription/hooks/useOnDbEvent';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { DatabaseEventAction } from '~/generated/graphql';
|
||||
|
||||
type ListenRecordUpdatesEffectProps = {
|
||||
@ -16,28 +28,70 @@ export const ListenRecordUpdatesEffect = ({
|
||||
}: ListenRecordUpdatesEffectProps) => {
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular,
|
||||
});
|
||||
const getRecordFromCache = useGetRecordFromCache({
|
||||
objectNameSingular,
|
||||
});
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
const computedRecordGqlFields = generateDepthOneRecordGqlFields({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const setRecordInStore = useRecoilCallback(
|
||||
({ set }) =>
|
||||
(record: ObjectRecord) => {
|
||||
set(recordStoreFamilyState(record.id), record);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
useOnDbEvent({
|
||||
input: { recordId, action: DatabaseEventAction.UPDATED },
|
||||
onData: (data) => {
|
||||
const updatedRecord = data.onDbEvent.record;
|
||||
|
||||
const fieldsUpdater = listenedFields.reduce((acc, listenedField) => {
|
||||
if (!isDefined(updatedRecord[listenedField])) {
|
||||
return acc;
|
||||
}
|
||||
return {
|
||||
...acc,
|
||||
[listenedField]: () => updatedRecord[listenedField],
|
||||
};
|
||||
}, {});
|
||||
|
||||
apolloClient.cache.modify({
|
||||
id: apolloClient.cache.identify({
|
||||
__typename: capitalize(objectNameSingular),
|
||||
id: recordId,
|
||||
}),
|
||||
fields: fieldsUpdater,
|
||||
const cachedRecord = getRecordFromCache<ObjectRecord>(recordId);
|
||||
const cachedRecordNode = getRecordNodeFromRecord<ObjectRecord>({
|
||||
record: cachedRecord,
|
||||
objectMetadataItem,
|
||||
objectMetadataItems,
|
||||
computeReferences: false,
|
||||
});
|
||||
|
||||
const shouldHandleOptimisticCache =
|
||||
isDefined(cachedRecordNode) && isDefined(cachedRecord);
|
||||
|
||||
if (shouldHandleOptimisticCache) {
|
||||
const computedOptimisticRecord: ObjectRecord = {
|
||||
...cachedRecord,
|
||||
...updatedRecord,
|
||||
id: recordId,
|
||||
__typename: getObjectTypename(objectMetadataItem.nameSingular),
|
||||
};
|
||||
|
||||
updateRecordFromCache({
|
||||
objectMetadataItems,
|
||||
objectMetadataItem,
|
||||
cache: apolloClient.cache,
|
||||
record: computedOptimisticRecord,
|
||||
recordGqlFields: computedRecordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
|
||||
triggerUpdateRecordOptimisticEffect({
|
||||
cache: apolloClient.cache,
|
||||
objectMetadataItem,
|
||||
currentRecord: cachedRecordNode,
|
||||
updatedRecord: updatedRecord,
|
||||
objectMetadataItems,
|
||||
});
|
||||
|
||||
setRecordInStore(computedOptimisticRecord);
|
||||
}
|
||||
},
|
||||
skip: listenedFields.length === 0,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user