Implement ViewGroups optimistic rendering (#14388)
# Context We have recently migrated from workspace.views to core.views. While doing it, we've lost the optimistic rendering on views in frontend. This is an issue for viewField and viewGroups that are persisted without having an intermediate storing layer (viewFilters, viewSorts, viewFilterGroups are not directly persisted when we do the changes, therefore we have an underlying layer to keep them and the optimistic was implemented there already). ViewFields have been treated in a previous PR, this PR focus on ViewGroups ## What Optimistic on ViewGroups ## Other + fix a bug in the sequencing to persist viewFilter + viewFilterGroups
This commit is contained in:
+34
-32
@@ -21,7 +21,7 @@ export const useTriggerViewFieldOptimisticEffect = () => {
|
||||
}: {
|
||||
createdViewFields?: CoreViewField[];
|
||||
updatedViewFields?: CoreViewField[];
|
||||
deletedViewFields?: CoreViewField[];
|
||||
deletedViewFields?: Pick<CoreViewField, 'id' | 'viewId'>[];
|
||||
}) => {
|
||||
const coreViews = getSnapshotValue(snapshot, coreViewsState);
|
||||
let newCoreViews = [...coreViews];
|
||||
@@ -96,38 +96,40 @@ export const useTriggerViewFieldOptimisticEffect = () => {
|
||||
}
|
||||
});
|
||||
|
||||
deletedViewFields.forEach((deletedViewField) => {
|
||||
cache.modify<CoreViewWithRelations>({
|
||||
id: cache.identify({
|
||||
__typename: 'CoreView',
|
||||
id: deletedViewField.viewId,
|
||||
}),
|
||||
fields: {
|
||||
viewFields: (existingViewFields, { readField }) =>
|
||||
existingViewFields.filter(
|
||||
(viewField) =>
|
||||
readField('id', viewField) !== deletedViewField.id,
|
||||
),
|
||||
},
|
||||
});
|
||||
const toBeModifiedCoreView = newCoreViews.find(
|
||||
(coreView) => coreView.id === deletedViewField.viewId,
|
||||
);
|
||||
|
||||
if (isDefined(toBeModifiedCoreView)) {
|
||||
newCoreViews = [
|
||||
...newCoreViews.filter(
|
||||
(coreView) => coreView.id !== deletedViewField.viewId,
|
||||
),
|
||||
{
|
||||
...toBeModifiedCoreView,
|
||||
viewFields: toBeModifiedCoreView.viewFields.filter(
|
||||
(viewField) => viewField.id !== deletedViewField.id,
|
||||
),
|
||||
deletedViewFields.forEach(
|
||||
(deletedViewField: Pick<CoreViewField, 'id' | 'viewId'>) => {
|
||||
cache.modify<CoreViewWithRelations>({
|
||||
id: cache.identify({
|
||||
__typename: 'CoreView',
|
||||
id: deletedViewField.viewId,
|
||||
}),
|
||||
fields: {
|
||||
viewFields: (existingViewFields, { readField }) =>
|
||||
existingViewFields.filter(
|
||||
(viewField) =>
|
||||
readField('id', viewField) !== deletedViewField.id,
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
});
|
||||
});
|
||||
const toBeModifiedCoreView = newCoreViews.find(
|
||||
(coreView) => coreView.id === deletedViewField.viewId,
|
||||
);
|
||||
|
||||
if (isDefined(toBeModifiedCoreView)) {
|
||||
newCoreViews = [
|
||||
...newCoreViews.filter(
|
||||
(coreView) => coreView.id !== deletedViewField.viewId,
|
||||
),
|
||||
{
|
||||
...toBeModifiedCoreView,
|
||||
viewFields: toBeModifiedCoreView.viewFields.filter(
|
||||
(viewField) => viewField.id !== deletedViewField.id,
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (!isDeeplyEqual(coreViews, newCoreViews)) {
|
||||
set(coreViewsState, newCoreViews);
|
||||
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
|
||||
import { coreViewsState } from '@/views/states/coreViewState';
|
||||
import { type CoreViewWithRelations } from '@/views/types/CoreViewWithRelations';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type CoreViewGroup } from '~/generated/graphql';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
|
||||
export const useTriggerViewGroupOptimisticEffect = () => {
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const cache = apolloClient.cache;
|
||||
|
||||
const triggerViewGroupOptimisticEffect = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
({
|
||||
createdViewGroups = [],
|
||||
updatedViewGroups = [],
|
||||
deletedViewGroups = [],
|
||||
}: {
|
||||
createdViewGroups?: CoreViewGroup[];
|
||||
updatedViewGroups?: CoreViewGroup[];
|
||||
deletedViewGroups?: Pick<CoreViewGroup, 'id' | 'viewId'>[];
|
||||
}) => {
|
||||
const coreViews = getSnapshotValue(snapshot, coreViewsState);
|
||||
let newCoreViews = [...coreViews];
|
||||
|
||||
createdViewGroups.forEach((createdViewGroup) => {
|
||||
cache.modify<CoreViewWithRelations>({
|
||||
id: cache.identify({
|
||||
__typename: 'CoreView',
|
||||
id: createdViewGroup.viewId,
|
||||
}),
|
||||
fields: {
|
||||
viewGroups: (existingViewGroups, { toReference }) => [
|
||||
...(existingViewGroups ?? []),
|
||||
toReference(createdViewGroup),
|
||||
],
|
||||
},
|
||||
});
|
||||
const toBeModifiedCoreView = newCoreViews.find(
|
||||
(coreView) => coreView.id === createdViewGroup.viewId,
|
||||
);
|
||||
if (isDefined(toBeModifiedCoreView)) {
|
||||
newCoreViews = [
|
||||
...newCoreViews.filter(
|
||||
(coreView) => coreView.id !== createdViewGroup.viewId,
|
||||
),
|
||||
{
|
||||
...toBeModifiedCoreView,
|
||||
viewGroups: [
|
||||
...toBeModifiedCoreView.viewGroups,
|
||||
createdViewGroup,
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
updatedViewGroups.forEach((updatedViewGroup) => {
|
||||
cache.modify<CoreViewWithRelations>({
|
||||
id: cache.identify({
|
||||
__typename: 'CoreView',
|
||||
id: updatedViewGroup.viewId,
|
||||
}),
|
||||
fields: {
|
||||
viewGroups: (existingViewGroups, { readField, toReference }) =>
|
||||
existingViewGroups.map((viewGroup) => {
|
||||
const viewGroupId = readField<string>('id', viewGroup);
|
||||
if (viewGroupId === updatedViewGroup.id) {
|
||||
return toReference(updatedViewGroup);
|
||||
}
|
||||
return viewGroup;
|
||||
}),
|
||||
},
|
||||
});
|
||||
const toBeModifiedCoreView = newCoreViews.find(
|
||||
(coreView) => coreView.id === updatedViewGroup.viewId,
|
||||
);
|
||||
if (isDefined(toBeModifiedCoreView)) {
|
||||
newCoreViews = [
|
||||
...newCoreViews.filter(
|
||||
(coreView) => coreView.id !== updatedViewGroup.viewId,
|
||||
),
|
||||
{
|
||||
...toBeModifiedCoreView,
|
||||
viewGroups: [
|
||||
...toBeModifiedCoreView.viewGroups.filter(
|
||||
(viewGroup) => viewGroup.id !== updatedViewGroup.id,
|
||||
),
|
||||
updatedViewGroup,
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
deletedViewGroups.forEach((deletedViewGroup) => {
|
||||
cache.modify<CoreViewWithRelations>({
|
||||
id: cache.identify({
|
||||
__typename: 'CoreView',
|
||||
id: deletedViewGroup.viewId,
|
||||
}),
|
||||
fields: {
|
||||
viewGroups: (existingViewGroups, { readField }) =>
|
||||
existingViewGroups.filter(
|
||||
(viewGroup) =>
|
||||
readField('id', viewGroup) !== deletedViewGroup.id,
|
||||
),
|
||||
},
|
||||
});
|
||||
const toBeModifiedCoreView = newCoreViews.find(
|
||||
(coreView) => coreView.id === deletedViewGroup.viewId,
|
||||
);
|
||||
|
||||
if (isDefined(toBeModifiedCoreView)) {
|
||||
newCoreViews = [
|
||||
...newCoreViews.filter(
|
||||
(coreView) => coreView.id !== deletedViewGroup.viewId,
|
||||
),
|
||||
{
|
||||
...toBeModifiedCoreView,
|
||||
viewGroups: toBeModifiedCoreView.viewGroups.filter(
|
||||
(viewGroup) => viewGroup.id !== deletedViewGroup.id,
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
if (!isDeeplyEqual(coreViews, newCoreViews)) {
|
||||
set(coreViewsState, newCoreViews);
|
||||
}
|
||||
},
|
||||
[cache],
|
||||
);
|
||||
|
||||
return {
|
||||
triggerViewGroupOptimisticEffect,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user