Handled SSE create event (#17293)
This PR handles SSE create event. It also fixes a regression on board, which was making it flash with skeletons on any update / create. This PR was a good test case to experience how each main components of the app : table, board, calendar, reacts to a create event. It is not straightforward for now, because each component handles records with its own state management to turn those records into a coherent display of rows or cards. The requests for each component are also different : fetch more, group by, multiple requests in parallel, so the cleanest way to handle optimistic effect requires to create one small optimistic engine per component tuned for its internal data logic. For now we've decided to implement what's doable in a reasonable amount of time and that includes not handling table with groups for now. Creating a clean optimistic logic for each component will be done later. # QA ## Importing 100 records via the API (could be a script, a workflow, an AI calling tools, a CSV import, etc.) https://github.com/user-attachments/assets/d38a3770-8b0a-4f83-8275-5e7d1be0a5c6 ## Creating from different components and seeing the SSE event being processed by other components https://github.com/user-attachments/assets/106b2f49-19cb-4190-92b7-0653c8373366
This commit is contained in:
+1
-1
@@ -11,7 +11,7 @@ export const CreateNewIndexRecordNoSelectionRecordAction = () => {
|
||||
|
||||
return (
|
||||
<Action
|
||||
onClick={() => createNewIndexRecord()}
|
||||
onClick={() => createNewIndexRecord({ position: 'first' })}
|
||||
closeSidePanelOnCommandMenuListActionExecution={false}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -18,13 +18,18 @@ import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import {
|
||||
type ObjectRecord as ObjectRecordShared,
|
||||
type RecordGqlOperationGqlRecordFields,
|
||||
} from 'twenty-shared/types';
|
||||
|
||||
import { type BaseObjectRecord } from '@/object-record/types/BaseObjectRecord';
|
||||
import { computeOptimisticCreateRecordBaseRecordInput } from '@/object-record/utils/computeOptimisticCreateRecordBaseRecordInput';
|
||||
import { computeOptimisticRecordFromInput } from '@/object-record/utils/computeOptimisticRecordFromInput';
|
||||
import { dispatchObjectRecordOperationBrowserEvent } from '@/object-record/utils/dispatchObjectRecordOperationBrowserEvent';
|
||||
import { getCreateOneRecordMutationResponseField } from '@/object-record/utils/getCreateOneRecordMutationResponseField';
|
||||
import { sanitizeRecordInput } from '@/object-record/utils/sanitizeRecordInput';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { type RecordGqlOperationGqlRecordFields } from 'twenty-shared/types';
|
||||
import { CustomError, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
type useCreateOneRecordProps = {
|
||||
@@ -135,7 +140,7 @@ export const useCreateOneRecord = <
|
||||
getCreateOneRecordMutationResponseField(objectNameSingular);
|
||||
|
||||
const createdObject = await apolloCoreClient
|
||||
.mutate({
|
||||
.mutate<ObjectRecord>({
|
||||
mutation: createOneRecordMutation,
|
||||
variables: {
|
||||
input: sanitizedInput,
|
||||
@@ -186,17 +191,31 @@ export const useCreateOneRecord = <
|
||||
|
||||
await refetchAggregateQueries();
|
||||
|
||||
const positionToUse =
|
||||
recordInput.position === 'first'
|
||||
? 'first'
|
||||
: recordInput.position === 'last'
|
||||
? 'last'
|
||||
: null;
|
||||
|
||||
const createdRecord = createdObject.data?.[
|
||||
mutationResponseField
|
||||
] as ObjectRecordShared & BaseObjectRecord;
|
||||
|
||||
dispatchObjectRecordOperationBrowserEvent({
|
||||
objectMetadataItem,
|
||||
operation: { type: 'create-one' },
|
||||
operation: {
|
||||
type: 'create-one',
|
||||
createdRecord: { ...createdRecord, position: positionToUse },
|
||||
},
|
||||
});
|
||||
|
||||
if (!isDefined(createdObject.data?.[mutationResponseField])) {
|
||||
if (!isDefined(createdRecord)) {
|
||||
throw new CustomError('Failed to create record');
|
||||
}
|
||||
|
||||
return getRecordFromRecordNode({
|
||||
recordNode: createdObject.data?.[mutationResponseField],
|
||||
recordNode: createdRecord,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
+124
-21
@@ -1,8 +1,16 @@
|
||||
import { useListenToObjectRecordOperationBrowserEvent } from '@/object-record/hooks/useListenToObjectRecordOperationBrowserEvent';
|
||||
import { useGetShouldInitializeRecordBoardForUpdateInputs } from '@/object-record/record-board/hooks/useGetShouldInitializeRecordBoardForUpdateInputs';
|
||||
import { useTriggerRecordBoardInitialQuery } from '@/object-record/record-board/hooks/useTriggerRecordBoardInitialQuery';
|
||||
import { recordGroupFromGroupValueComponentFamilySelector } from '@/object-record/record-group/states/selectors/recordGroupFromGroupValueComponentFamilySelector';
|
||||
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
import { recordIndexGroupFieldMetadataItemComponentState } from '@/object-record/record-index/states/recordIndexGroupFieldMetadataComponentState';
|
||||
import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { type ObjectRecordOperationBrowserEventDetail } from '@/object-record/types/ObjectRecordOperationBrowserEventDetail';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const RecordBoardDataChangedEffect = () => {
|
||||
const { objectMetadataItem } = useRecordIndexContextOrThrow();
|
||||
@@ -11,31 +19,126 @@ export const RecordBoardDataChangedEffect = () => {
|
||||
const { getShouldInitializeRecordBoardForUpdateInputs } =
|
||||
useGetShouldInitializeRecordBoardForUpdateInputs();
|
||||
|
||||
const handleObjectRecordOperation = (
|
||||
objectRecordOperationEventDetail: ObjectRecordOperationBrowserEventDetail,
|
||||
) => {
|
||||
const objectRecordOperation = objectRecordOperationEventDetail.operation;
|
||||
const recordGroupFromGroupValueCallbackState =
|
||||
useRecoilComponentCallbackState(
|
||||
recordGroupFromGroupValueComponentFamilySelector,
|
||||
);
|
||||
const recordIndexGroupFieldMetadataItemCallbackState =
|
||||
useRecoilComponentCallbackState(
|
||||
recordIndexGroupFieldMetadataItemComponentState,
|
||||
);
|
||||
const recordIndexRecordIdsByGroupCallbackState =
|
||||
useRecoilComponentCallbackState(
|
||||
recordIndexRecordIdsByGroupComponentFamilyState,
|
||||
);
|
||||
|
||||
const isUpdateOperation =
|
||||
objectRecordOperation.type === 'update-one' ||
|
||||
objectRecordOperation.type === 'update-many';
|
||||
const handleObjectRecordOperation = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
(
|
||||
objectRecordOperationEventDetail: ObjectRecordOperationBrowserEventDetail,
|
||||
) => {
|
||||
const objectRecordOperation =
|
||||
objectRecordOperationEventDetail.operation;
|
||||
|
||||
if (isUpdateOperation) {
|
||||
const updateInputs =
|
||||
objectRecordOperation.type === 'update-one'
|
||||
? [objectRecordOperation.result.updateInput]
|
||||
: objectRecordOperation.result.updateInputs;
|
||||
switch (objectRecordOperation.type) {
|
||||
case 'update-one':
|
||||
case 'update-many':
|
||||
{
|
||||
const updateInputs =
|
||||
objectRecordOperation.type === 'update-one'
|
||||
? [objectRecordOperation.result.updateInput]
|
||||
: objectRecordOperation.result.updateInputs;
|
||||
|
||||
const shouldInitializeForUpdateOperation =
|
||||
getShouldInitializeRecordBoardForUpdateInputs(updateInputs);
|
||||
const shouldInitializeForUpdateOperation =
|
||||
getShouldInitializeRecordBoardForUpdateInputs(updateInputs);
|
||||
|
||||
if (shouldInitializeForUpdateOperation) {
|
||||
triggerRecordBoardInitialQuery();
|
||||
}
|
||||
} else {
|
||||
triggerRecordBoardInitialQuery();
|
||||
}
|
||||
};
|
||||
if (shouldInitializeForUpdateOperation) {
|
||||
triggerRecordBoardInitialQuery();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'create-one': {
|
||||
if (objectRecordOperation.createdRecord.position === 'first') {
|
||||
triggerRecordBoardInitialQuery();
|
||||
} else {
|
||||
const createdRecordPosition =
|
||||
objectRecordOperation.createdRecord.position;
|
||||
|
||||
if (!isDefined(createdRecordPosition)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const recordIndexGroupFieldMetadataItem = getSnapshotValue(
|
||||
snapshot,
|
||||
recordIndexGroupFieldMetadataItemCallbackState,
|
||||
);
|
||||
|
||||
if (!isDefined(recordIndexGroupFieldMetadataItem)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const recordGroupValue =
|
||||
objectRecordOperation.createdRecord[
|
||||
recordIndexGroupFieldMetadataItem.name
|
||||
];
|
||||
|
||||
const recordGroupDefinitionFromGroupValue = getSnapshotValue(
|
||||
snapshot,
|
||||
recordGroupFromGroupValueCallbackState({ recordGroupValue }),
|
||||
);
|
||||
|
||||
if (!isDefined(recordGroupDefinitionFromGroupValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const recordIdsForGroup = getSnapshotValue(
|
||||
snapshot,
|
||||
recordIndexRecordIdsByGroupCallbackState(
|
||||
recordGroupDefinitionFromGroupValue.id,
|
||||
),
|
||||
);
|
||||
|
||||
const recordIdsWithoutCreatedRecord = recordIdsForGroup.filter(
|
||||
(recordId) =>
|
||||
recordId !== objectRecordOperation.createdRecord.id,
|
||||
);
|
||||
|
||||
const groupIsEmpty = recordIdsWithoutCreatedRecord.length === 0;
|
||||
|
||||
if (groupIsEmpty) {
|
||||
triggerRecordBoardInitialQuery();
|
||||
return;
|
||||
}
|
||||
|
||||
const firstRecordIdInGroup = recordIdsWithoutCreatedRecord[0];
|
||||
const firstExistingRecordInGroup = getSnapshotValue(
|
||||
snapshot,
|
||||
recordStoreFamilyState(firstRecordIdInGroup),
|
||||
);
|
||||
|
||||
if (!isDefined(firstExistingRecordInGroup)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (createdRecordPosition < firstExistingRecordInGroup.position) {
|
||||
triggerRecordBoardInitialQuery();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
triggerRecordBoardInitialQuery();
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
triggerRecordBoardInitialQuery,
|
||||
getShouldInitializeRecordBoardForUpdateInputs,
|
||||
recordIndexGroupFieldMetadataItemCallbackState,
|
||||
recordGroupFromGroupValueCallbackState,
|
||||
recordIndexRecordIdsByGroupCallbackState,
|
||||
],
|
||||
);
|
||||
|
||||
useListenToObjectRecordOperationBrowserEvent({
|
||||
onObjectRecordOperationBrowserEvent: handleObjectRecordOperation,
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ export const useGetShouldInitializeRecordBoardForUpdateInputs = () => {
|
||||
}
|
||||
|
||||
if (updatedFieldNames.has('position')) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
+7
-17
@@ -9,10 +9,8 @@ import { RecordBoardColumnContext } from '@/object-record/record-board/record-bo
|
||||
|
||||
import { RecordBoardColumnLoadingSkeletonCards } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnLoadingSkeletonCards';
|
||||
import { recordBoardShouldFetchMoreInColumnComponentFamilyState } from '@/object-record/record-board/states/recordBoardShouldFetchMoreInColumnComponentFamilyState';
|
||||
import { recordIndexRecordGroupsAreInInitialLoadingComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupsAreInInitialLoadingComponentState';
|
||||
import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState';
|
||||
import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
|
||||
const StyledColumnCardsContainer = styled.div`
|
||||
display: flex;
|
||||
@@ -40,10 +38,6 @@ export const RecordBoardColumnCardsContainer = ({
|
||||
recordBoardColumnId,
|
||||
);
|
||||
|
||||
const recordIndexRecordGroupsAreInInitialLoading = useRecoilComponentValue(
|
||||
recordIndexRecordGroupsAreInInitialLoadingComponentState,
|
||||
);
|
||||
|
||||
const recordBoardShouldFetchMoreInColumn = useRecoilComponentFamilyValue(
|
||||
recordBoardShouldFetchMoreInColumnComponentFamilyState,
|
||||
recordBoardColumnId,
|
||||
@@ -55,17 +49,13 @@ export const RecordBoardColumnCardsContainer = ({
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...droppableProvided?.droppableProps}
|
||||
>
|
||||
{recordIndexRecordGroupsAreInInitialLoading ? (
|
||||
<RecordBoardColumnLoadingSkeletonCards />
|
||||
) : (
|
||||
recordIds.map((recordId, index) => (
|
||||
<RecordBoardCardDraggableContainer
|
||||
key={recordId}
|
||||
recordId={recordId}
|
||||
rowIndex={index}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
{recordIds.map((recordId, index) => (
|
||||
<RecordBoardCardDraggableContainer
|
||||
key={recordId}
|
||||
recordId={recordId}
|
||||
rowIndex={index}
|
||||
/>
|
||||
))}
|
||||
{recordBoardShouldFetchMoreInColumn ? (
|
||||
<RecordBoardColumnLoadingSkeletonCards />
|
||||
) : null}
|
||||
|
||||
+1
-1
@@ -4,6 +4,6 @@ import { createComponentFamilyState } from '@/ui/utilities/state/component-state
|
||||
export const recordBoardShouldFetchMoreInColumnComponentFamilyState =
|
||||
createComponentFamilyState<boolean, string>({
|
||||
key: 'recordBoardShouldFetchMoreInColumnComponentFamilyState',
|
||||
defaultValue: false,
|
||||
defaultValue: true,
|
||||
componentInstanceContext: RecordBoardComponentInstanceContext,
|
||||
});
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
|
||||
import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState';
|
||||
import { type RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition';
|
||||
import { createComponentFamilySelector } from '@/ui/utilities/state/component-state/utils/createComponentFamilySelector';
|
||||
|
||||
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
|
||||
import { type Nullable } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const recordGroupFromGroupValueComponentFamilySelector =
|
||||
createComponentFamilySelector<
|
||||
RecordGroupDefinition | undefined,
|
||||
{ recordGroupValue: Nullable<string> }
|
||||
>({
|
||||
key: 'recordGroupFromGroupValueComponentSelector',
|
||||
componentInstanceContext: ViewComponentInstanceContext,
|
||||
get:
|
||||
({ instanceId, familyKey }) =>
|
||||
({ get }): RecordGroupDefinition | undefined => {
|
||||
const recordGroupIds = get(
|
||||
recordGroupIdsComponentState.atomFamily({
|
||||
instanceId,
|
||||
}),
|
||||
);
|
||||
|
||||
const recordGroupId = recordGroupIds.find((recordGroupId) => {
|
||||
const recordGroupDefinition = get(
|
||||
recordGroupDefinitionFamilyState(recordGroupId),
|
||||
);
|
||||
|
||||
return recordGroupDefinition?.value === familyKey.recordGroupValue;
|
||||
});
|
||||
|
||||
if (!isDefined(recordGroupId)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const recordGroupDefinition = get(
|
||||
recordGroupDefinitionFamilyState(recordGroupId),
|
||||
);
|
||||
|
||||
return recordGroupDefinition;
|
||||
},
|
||||
});
|
||||
+3
-1
@@ -23,7 +23,9 @@ export const RecordTableHeaderLabelIdentifierCellPlusButton = () => {
|
||||
});
|
||||
|
||||
const handlePlusButtonClick = () => {
|
||||
createNewIndexRecord();
|
||||
createNewIndexRecord({
|
||||
position: 'first',
|
||||
});
|
||||
};
|
||||
|
||||
const isReadOnly = isObjectMetadataReadOnly({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { type ObjectRecordOperationUpdateInput } from '@/object-record/types/ObjectRecordOperationUpdateInput';
|
||||
import { type ObjectRecord } from 'twenty-shared/types';
|
||||
|
||||
export type ObjectRecordOperation =
|
||||
| {
|
||||
@@ -13,9 +14,12 @@ export type ObjectRecordOperation =
|
||||
updateInputs: ObjectRecordOperationUpdateInput[];
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: 'create-one';
|
||||
createdRecord: ObjectRecord;
|
||||
}
|
||||
| {
|
||||
type:
|
||||
| 'create-one'
|
||||
| 'create-many'
|
||||
| 'destroy-one'
|
||||
| 'destroy-many'
|
||||
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
||||
import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename';
|
||||
import { type RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRefetchAggregateQueriesForObjectMetadataItem } from '@/object-record/hooks/useRefetchAggregateQueriesForObjectMetadataItem';
|
||||
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
|
||||
import { useCallback } from 'react';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
import {
|
||||
DatabaseEventAction,
|
||||
type ObjectRecordEvent,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
export const useTriggerOptimisticEffectFromSseCreateEvents = () => {
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
const { refetchAggregateQueriesForObjectMetadataItem } =
|
||||
useRefetchAggregateQueriesForObjectMetadataItem();
|
||||
const { upsertRecordsInStore } = useUpsertRecordsInStore();
|
||||
|
||||
const debouncedRefetchAggregateQueriesForObjectMetadataItem =
|
||||
useDebouncedCallback(refetchAggregateQueriesForObjectMetadataItem, 100);
|
||||
|
||||
const triggerOptimisticEffectFromSseCreateEvents = useCallback(
|
||||
({
|
||||
objectRecordEvents,
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectRecordEvents: ObjectRecordEvent[];
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
const createEvents = objectRecordEvents.filter((objectRecordEvent) => {
|
||||
return objectRecordEvent.action === DatabaseEventAction.CREATED;
|
||||
});
|
||||
|
||||
const cache = apolloCoreClient.cache;
|
||||
|
||||
const recordsToCreate = createEvents.map((createEvent) => {
|
||||
return {
|
||||
...createEvent.properties.after,
|
||||
__typename: getObjectTypename(objectMetadataItem.nameSingular),
|
||||
} as RecordGqlNode;
|
||||
});
|
||||
|
||||
triggerCreateRecordsOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
recordsToCreate: recordsToCreate,
|
||||
objectMetadataItems,
|
||||
shouldMatchRootQueryFilter: true,
|
||||
checkForRecordInCache: true,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
upsertRecordsInStore,
|
||||
});
|
||||
|
||||
debouncedRefetchAggregateQueriesForObjectMetadataItem({
|
||||
objectMetadataItem,
|
||||
});
|
||||
},
|
||||
[
|
||||
apolloCoreClient.cache,
|
||||
objectMetadataItems,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
upsertRecordsInStore,
|
||||
debouncedRefetchAggregateQueriesForObjectMetadataItem,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
triggerOptimisticEffectFromSseCreateEvents,
|
||||
};
|
||||
};
|
||||
+15
-1
@@ -1,4 +1,5 @@
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { useTriggerOptimisticEffectFromSseCreateEvents } from '@/sse-db-event/hooks/useTriggerOptimisticEffectFromSseCreateEvents';
|
||||
import { useTriggerOptimisticEffectFromSseUpdateEvents } from '@/sse-db-event/hooks/useTriggerOptimisticEffectFromSseUpdateEvents';
|
||||
import { groupObjectRecordSseEventsByEventType } from '@/sse-db-event/utils/groupObjectRecordSseEventsByEventType';
|
||||
import { groupObjectRecordSseEventsByObjectMetadataItemNameSingular } from '@/sse-db-event/utils/groupObjectRecordSseEventsByObjectMetadataItemNameSingular';
|
||||
@@ -15,6 +16,9 @@ export const useTriggerOptimisticEffectFromSseEvents = () => {
|
||||
const { triggerOptimisticEffectFromSseUpdateEvents } =
|
||||
useTriggerOptimisticEffectFromSseUpdateEvents();
|
||||
|
||||
const { triggerOptimisticEffectFromSseCreateEvents } =
|
||||
useTriggerOptimisticEffectFromSseCreateEvents();
|
||||
|
||||
const triggerOptimisticEffectFromSseEvents = useCallback(
|
||||
({ objectRecordEvents }: { objectRecordEvents: ObjectRecordEvent[] }) => {
|
||||
const objectRecordEventsByObjectMetadataItemNameSingular =
|
||||
@@ -58,11 +62,21 @@ export const useTriggerOptimisticEffectFromSseEvents = () => {
|
||||
objectMetadataItem,
|
||||
});
|
||||
break;
|
||||
case DatabaseEventAction.CREATED:
|
||||
triggerOptimisticEffectFromSseCreateEvents({
|
||||
objectRecordEvents: objectRecordEventsForThisEventType,
|
||||
objectMetadataItem,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
[objectMetadataItems, triggerOptimisticEffectFromSseUpdateEvents],
|
||||
[
|
||||
objectMetadataItems,
|
||||
triggerOptimisticEffectFromSseUpdateEvents,
|
||||
triggerOptimisticEffectFromSseCreateEvents,
|
||||
],
|
||||
);
|
||||
|
||||
return { triggerOptimisticEffectFromSseEvents };
|
||||
|
||||
+10
-2
@@ -90,7 +90,11 @@ export const turnSseObjectRecordEventsToObjectRecordOperationBrowserEvents = ({
|
||||
if (hasSingleEvent) {
|
||||
objectRecordOperationBrowserEvents.push({
|
||||
objectMetadataItem,
|
||||
operation: { type: 'create-one' },
|
||||
operation: {
|
||||
type: 'create-one',
|
||||
createdRecord:
|
||||
objectRecordEventsForThisEventType[0].properties.after,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
objectRecordOperationBrowserEvents.push({
|
||||
@@ -103,7 +107,11 @@ export const turnSseObjectRecordEventsToObjectRecordOperationBrowserEvents = ({
|
||||
if (hasSingleEvent) {
|
||||
objectRecordOperationBrowserEvents.push({
|
||||
objectMetadataItem,
|
||||
operation: { type: 'create-one' },
|
||||
operation: {
|
||||
type: 'create-one',
|
||||
createdRecord:
|
||||
objectRecordEventsForThisEventType[0].properties.after,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
objectRecordOperationBrowserEvents.push({
|
||||
|
||||
Reference in New Issue
Block a user