[REQUIRES_CACHE_FLUSH][GQL_VIEW_GROUP_API_BREAKING_CHANGE] ViewGroup in v2 (#15052)
# Introduction Adding view-group to core engine v2 Following https://github.com/twentyhq/twenty/pull/15010 ( same pattern ) ## What's done - Created flat-view-group - flat view group runner - flat view group builder - create view group service v2 and input transpilers - refactor the existing view group resolver to fix standard ( BREAKING_CHANGE on graphql api update especially ) REST stays the same - refactored the front to consume the mutations autogenerated close https://github.com/twentyhq/core-team-issues/issues/1665
This commit is contained in:
+8
-4
@@ -58,10 +58,14 @@ export const useSetViewTypeFromLayoutOptionsMenu = () => {
|
||||
fieldMetadataId: randomFieldForKanban,
|
||||
} satisfies ViewGroup);
|
||||
|
||||
await createViewGroupRecords({
|
||||
viewGroupsToCreate,
|
||||
viewId: currentViewId,
|
||||
});
|
||||
await createViewGroupRecords(
|
||||
viewGroupsToCreate.map((viewGroup) => ({
|
||||
input: {
|
||||
...viewGroup,
|
||||
viewId: currentViewId,
|
||||
},
|
||||
})),
|
||||
);
|
||||
|
||||
return viewGroupsToCreate;
|
||||
},
|
||||
|
||||
+14
-5
@@ -106,14 +106,22 @@ export const useHandleRecordGroupField = () => {
|
||||
);
|
||||
|
||||
if (viewGroupsToCreate.length > 0) {
|
||||
await createViewGroupRecords({ viewGroupsToCreate, viewId: view.id });
|
||||
await createViewGroupRecords(
|
||||
viewGroupsToCreate.map((viewGroup) => ({
|
||||
input: {
|
||||
...viewGroup,
|
||||
viewId: view.id,
|
||||
},
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
if (viewGroupsToDelete.length > 0) {
|
||||
await deleteViewGroupRecords(
|
||||
viewGroupsToDelete.map((group) => ({
|
||||
id: group.id,
|
||||
viewId: view.id,
|
||||
input: {
|
||||
id: group.id,
|
||||
},
|
||||
})),
|
||||
);
|
||||
}
|
||||
@@ -151,8 +159,9 @@ export const useHandleRecordGroupField = () => {
|
||||
|
||||
await deleteViewGroupRecords(
|
||||
view.viewGroups.map((group) => ({
|
||||
id: group.id,
|
||||
viewId: view.id,
|
||||
input: {
|
||||
id: group.id,
|
||||
},
|
||||
})),
|
||||
);
|
||||
|
||||
|
||||
@@ -8,5 +8,8 @@ export const VIEW_GROUP_FRAGMENT = gql`
|
||||
fieldValue
|
||||
position
|
||||
viewId
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { VIEW_GROUP_FRAGMENT } from '@/views/graphql/fragments/viewGroupFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const DELETE_CORE_VIEW_GROUP = gql`
|
||||
mutation DeleteCoreViewGroup($id: String!) {
|
||||
deleteCoreViewGroup(id: $id)
|
||||
${VIEW_GROUP_FRAGMENT}
|
||||
mutation DeleteCoreViewGroup($input: DeleteViewGroupInput!) {
|
||||
deleteCoreViewGroup(input: $input) {
|
||||
...ViewGroupFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { VIEW_GROUP_FRAGMENT } from '@/views/graphql/fragments/viewGroupFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const DESTROY_CORE_VIEW_GROUP = gql`
|
||||
mutation DestroyCoreViewGroup($id: String!) {
|
||||
destroyCoreViewGroup(id: $id)
|
||||
${VIEW_GROUP_FRAGMENT}
|
||||
mutation DestroyCoreViewGroup($input: DestroyViewGroupInput!) {
|
||||
destroyCoreViewGroup(input: $input) {
|
||||
...ViewGroupFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -3,8 +3,8 @@ import { gql } from '@apollo/client';
|
||||
|
||||
export const UPDATE_CORE_VIEW_GROUP = gql`
|
||||
${VIEW_GROUP_FRAGMENT}
|
||||
mutation UpdateCoreViewGroup($id: String!, $input: UpdateViewGroupInput!) {
|
||||
updateCoreViewGroup(id: $id, input: $input) {
|
||||
mutation UpdateCoreViewGroup($input: UpdateViewGroupInput!) {
|
||||
updateCoreViewGroup(input: $input) {
|
||||
...ViewGroupFragment
|
||||
}
|
||||
}
|
||||
|
||||
+88
-75
@@ -1,115 +1,128 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { CREATE_CORE_VIEW_GROUP } from '@/views/graphql/mutations/createCoreViewGroup';
|
||||
import { DESTROY_CORE_VIEW_GROUP } from '@/views/graphql/mutations/destroyCoreViewGroup';
|
||||
import { UPDATE_CORE_VIEW_GROUP } from '@/views/graphql/mutations/updateCoreViewGroup';
|
||||
import { useTriggerViewGroupOptimisticEffect } from '@/views/optimistic-effects/hooks/useTriggerViewGroupOptimisticEffect';
|
||||
import { type ViewGroup } from '@/views/types/ViewGroup';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { type CoreViewGroup } from '~/generated/graphql';
|
||||
|
||||
type CreateViewGroupRecordsArgs = {
|
||||
viewGroupsToCreate: ViewGroup[];
|
||||
viewId: string;
|
||||
};
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
type CreateCoreViewGroupMutationVariables,
|
||||
type DeleteCoreViewGroupMutationVariables,
|
||||
type DestroyCoreViewGroupMutationVariables,
|
||||
type UpdateCoreViewGroupMutationVariables,
|
||||
useCreateCoreViewGroupMutation,
|
||||
useDeleteCoreViewGroupMutation,
|
||||
useDestroyCoreViewGroupMutation,
|
||||
useUpdateCoreViewGroupMutation,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
export const usePersistViewGroupRecords = () => {
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const { triggerViewGroupOptimisticEffect } =
|
||||
useTriggerViewGroupOptimisticEffect();
|
||||
const [createCoreViewGroupMutation] = useCreateCoreViewGroupMutation();
|
||||
const [updateCoreViewGroupMutation] = useUpdateCoreViewGroupMutation();
|
||||
const [deleteCoreViewGroupMutation] = useDeleteCoreViewGroupMutation();
|
||||
const [destroyCoreViewGroupMutation] = useDestroyCoreViewGroupMutation();
|
||||
|
||||
const createCoreViewGroupRecords = useCallback(
|
||||
({ viewGroupsToCreate, viewId }: CreateViewGroupRecordsArgs) => {
|
||||
if (viewGroupsToCreate.length === 0) return;
|
||||
(createCoreViewGroupInputs: CreateCoreViewGroupMutationVariables[]) => {
|
||||
if (createCoreViewGroupInputs.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
return Promise.all(
|
||||
viewGroupsToCreate.map((viewGroup) =>
|
||||
apolloClient.mutate({
|
||||
mutation: CREATE_CORE_VIEW_GROUP,
|
||||
variables: {
|
||||
input: {
|
||||
id: viewGroup.id,
|
||||
viewId,
|
||||
fieldMetadataId: viewGroup.fieldMetadataId,
|
||||
fieldValue: viewGroup.fieldValue,
|
||||
isVisible: viewGroup.isVisible,
|
||||
position: viewGroup.position,
|
||||
},
|
||||
},
|
||||
createCoreViewGroupInputs.map((variables) =>
|
||||
createCoreViewGroupMutation({
|
||||
variables,
|
||||
update: (_cache, { data }) => {
|
||||
const record = data?.['createCoreViewGroup'];
|
||||
if (!record) return;
|
||||
const createdViewGroup = data?.createCoreViewGroup;
|
||||
if (!isDefined(createdViewGroup)) {
|
||||
return;
|
||||
}
|
||||
|
||||
triggerViewGroupOptimisticEffect({
|
||||
createdViewGroups: [record],
|
||||
createdViewGroups: [createdViewGroup],
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[apolloClient, triggerViewGroupOptimisticEffect],
|
||||
[triggerViewGroupOptimisticEffect, createCoreViewGroupMutation],
|
||||
);
|
||||
|
||||
const updateCoreViewGroupRecords = useCallback(
|
||||
async (viewGroupsToUpdate: ViewGroup[]) => {
|
||||
if (!viewGroupsToUpdate.length) return;
|
||||
|
||||
const mutationPromises = viewGroupsToUpdate.map((viewGroup) =>
|
||||
apolloClient.mutate<{ updateCoreViewGroup: CoreViewGroup }>({
|
||||
mutation: UPDATE_CORE_VIEW_GROUP,
|
||||
variables: {
|
||||
id: viewGroup.id,
|
||||
input: {
|
||||
isVisible: viewGroup.isVisible,
|
||||
position: viewGroup.position,
|
||||
},
|
||||
},
|
||||
// Avoid cache being updated with stale data
|
||||
fetchPolicy: 'no-cache',
|
||||
update: (_cache, { data }) => {
|
||||
const record = data?.['updateCoreViewGroup'];
|
||||
if (!record) return;
|
||||
|
||||
triggerViewGroupOptimisticEffect({
|
||||
updatedViewGroups: [record],
|
||||
});
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
return Promise.all(mutationPromises);
|
||||
},
|
||||
[apolloClient, triggerViewGroupOptimisticEffect],
|
||||
);
|
||||
|
||||
const deleteCoreViewGroupRecords = useCallback(
|
||||
async (viewGroupsToDelete: Pick<CoreViewGroup, 'id' | 'viewId'>[]) => {
|
||||
if (!viewGroupsToDelete.length) return;
|
||||
(updateCoreViewGroupInputs: UpdateCoreViewGroupMutationVariables[]) => {
|
||||
if (updateCoreViewGroupInputs.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
return Promise.all(
|
||||
viewGroupsToDelete.map((viewGroup) =>
|
||||
apolloClient.mutate({
|
||||
mutation: DESTROY_CORE_VIEW_GROUP,
|
||||
variables: {
|
||||
id: viewGroup.id,
|
||||
},
|
||||
update: () => {
|
||||
updateCoreViewGroupInputs.map((variables) =>
|
||||
updateCoreViewGroupMutation({
|
||||
variables,
|
||||
update: (_cache, { data }) => {
|
||||
const updatedViewGroup = data?.updateCoreViewGroup;
|
||||
if (!isDefined(updatedViewGroup)) {
|
||||
return;
|
||||
}
|
||||
|
||||
triggerViewGroupOptimisticEffect({
|
||||
deletedViewGroups: [viewGroup],
|
||||
updatedViewGroups: [updatedViewGroup],
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[apolloClient, triggerViewGroupOptimisticEffect],
|
||||
[triggerViewGroupOptimisticEffect, updateCoreViewGroupMutation],
|
||||
);
|
||||
|
||||
const deleteCoreViewGroupRecords = useCallback(
|
||||
(deleteCoreViewGroupInputs: DeleteCoreViewGroupMutationVariables[]) => {
|
||||
if (deleteCoreViewGroupInputs.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
return Promise.all(
|
||||
deleteCoreViewGroupInputs.map((variables) =>
|
||||
deleteCoreViewGroupMutation({
|
||||
variables,
|
||||
update: (_cache, { data }) => {
|
||||
const deletedViewGroup = data?.deleteCoreViewGroup;
|
||||
if (!isDefined(deletedViewGroup)) {
|
||||
return;
|
||||
}
|
||||
|
||||
triggerViewGroupOptimisticEffect({
|
||||
deletedViewGroups: [deletedViewGroup],
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[triggerViewGroupOptimisticEffect, deleteCoreViewGroupMutation],
|
||||
);
|
||||
|
||||
const destroyCoreViewGroupRecords = useCallback(
|
||||
(destroyCoreViewGroupInputs: DestroyCoreViewGroupMutationVariables[]) => {
|
||||
if (destroyCoreViewGroupInputs.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
return Promise.all(
|
||||
destroyCoreViewGroupInputs.map((variables) =>
|
||||
destroyCoreViewGroupMutation({
|
||||
variables,
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[destroyCoreViewGroupMutation],
|
||||
);
|
||||
|
||||
return {
|
||||
createViewGroupRecords: createCoreViewGroupRecords,
|
||||
updateViewGroupRecords: updateCoreViewGroupRecords,
|
||||
deleteViewGroupRecords: deleteCoreViewGroupRecords,
|
||||
destroyViewGroupRecords: destroyCoreViewGroupRecords,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -194,10 +194,14 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
|
||||
fieldMetadataId: kanbanFieldMetadataId,
|
||||
} satisfies ViewGroup);
|
||||
|
||||
await createViewGroupRecords({
|
||||
viewGroupsToCreate,
|
||||
viewId: newViewId,
|
||||
});
|
||||
await createViewGroupRecords(
|
||||
viewGroupsToCreate.map((viewGroup) => ({
|
||||
input: {
|
||||
...viewGroup,
|
||||
viewId: newViewId,
|
||||
},
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
if (shouldCopyFiltersAndSortsAndAggregate === true) {
|
||||
|
||||
@@ -63,7 +63,17 @@ export const useSaveCurrentViewGroups = () => {
|
||||
}
|
||||
|
||||
await updateViewGroupRecords([
|
||||
{ ...viewGroupToSave, id: existingField.id },
|
||||
{
|
||||
input: {
|
||||
id: existingField.id,
|
||||
update: {
|
||||
isVisible: viewGroupToSave.isVisible,
|
||||
position: viewGroupToSave.position,
|
||||
fieldMetadataId: viewGroupToSave.fieldMetadataId,
|
||||
fieldValue: viewGroupToSave.fieldValue,
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
},
|
||||
[
|
||||
@@ -118,7 +128,17 @@ export const useSaveCurrentViewGroups = () => {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return { ...viewGroupToSave, id: existingField.id };
|
||||
return {
|
||||
input: {
|
||||
id: existingField.id,
|
||||
update: {
|
||||
isVisible: viewGroupToSave.isVisible,
|
||||
position: viewGroupToSave.position,
|
||||
fieldMetadataId: viewGroupToSave.fieldMetadataId,
|
||||
fieldValue: viewGroupToSave.fieldValue,
|
||||
},
|
||||
},
|
||||
};
|
||||
})
|
||||
.filter(isDefined);
|
||||
|
||||
@@ -131,7 +151,14 @@ export const useSaveCurrentViewGroups = () => {
|
||||
);
|
||||
|
||||
await Promise.all([
|
||||
createViewGroupRecords({ viewGroupsToCreate, viewId: view.id }),
|
||||
createViewGroupRecords(
|
||||
viewGroupsToCreate.map((viewGroup) => ({
|
||||
input: {
|
||||
...viewGroup,
|
||||
viewId: view.id,
|
||||
},
|
||||
})),
|
||||
),
|
||||
updateViewGroupRecords(viewGroupsToUpdate),
|
||||
]);
|
||||
},
|
||||
|
||||
+6
-6
@@ -6,7 +6,11 @@ import { useRecoilCallback } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type CoreViewGroup } from '~/generated/graphql';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
|
||||
type UpdatedDeletedCoreViewGroup = {
|
||||
createdViewGroups?: Omit<CoreViewGroup, 'workspaceId'>[];
|
||||
updatedViewGroups?: Omit<CoreViewGroup, 'workspaceId'>[];
|
||||
deletedViewGroups?: Pick<CoreViewGroup, 'id' | 'viewId'>[];
|
||||
};
|
||||
export const useTriggerViewGroupOptimisticEffect = () => {
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
@@ -18,11 +22,7 @@ export const useTriggerViewGroupOptimisticEffect = () => {
|
||||
createdViewGroups = [],
|
||||
updatedViewGroups = [],
|
||||
deletedViewGroups = [],
|
||||
}: {
|
||||
createdViewGroups?: CoreViewGroup[];
|
||||
updatedViewGroups?: CoreViewGroup[];
|
||||
deletedViewGroups?: Pick<CoreViewGroup, 'id' | 'viewId'>[];
|
||||
}) => {
|
||||
}: UpdatedDeletedCoreViewGroup) => {
|
||||
const coreViews = getSnapshotValue(snapshot, coreViewsState);
|
||||
let newCoreViews = [...coreViews];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user