Fix From Many relation for deleted notes crashing (#11117)
In this PR, I'm: - fixing the root cause (we should not try to render a RecordChip if the record is not defined in RelationFromMany Display) - fixing related typing issues - we won't be able to catch the issue from TS perspective as ObjectRecord is a Record of string, any
This commit is contained in:
@ -1,5 +1,4 @@
|
|||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { Nullable } from 'twenty-ui';
|
|
||||||
|
|
||||||
import { ActivityTargetWithTargetRecord } from '@/activities/types/ActivityTargetObject';
|
import { ActivityTargetWithTargetRecord } from '@/activities/types/ActivityTargetObject';
|
||||||
import { Note } from '@/activities/types/Note';
|
import { Note } from '@/activities/types/Note';
|
||||||
@ -9,6 +8,7 @@ import { TaskTarget } from '@/activities/types/TaskTarget';
|
|||||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
|
||||||
export const useActivityTargetObjectRecords = (
|
export const useActivityTargetObjectRecords = (
|
||||||
@ -34,7 +34,7 @@ export const useActivityTargetObjectRecords = (
|
|||||||
: [];
|
: [];
|
||||||
|
|
||||||
const activityTargetObjectRecords = targets
|
const activityTargetObjectRecords = targets
|
||||||
.map<Nullable<ActivityTargetWithTargetRecord>>((activityTarget) => {
|
.map<ActivityTargetWithTargetRecord | undefined>((activityTarget) => {
|
||||||
if (!isDefined(activityTarget)) {
|
if (!isDefined(activityTarget)) {
|
||||||
throw new Error(`Cannot find activity target`);
|
throw new Error(`Cannot find activity target`);
|
||||||
}
|
}
|
||||||
@ -51,10 +51,11 @@ export const useActivityTargetObjectRecords = (
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const targetObjectRecord =
|
const targetObjectRecord = activityTarget[
|
||||||
activityTarget[correspondingObjectMetadataItem.nameSingular];
|
correspondingObjectMetadataItem.nameSingular
|
||||||
|
] as ObjectRecord | undefined;
|
||||||
|
|
||||||
if (!targetObjectRecord) {
|
if (!isDefined(targetObjectRecord)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Cannot find target object record of type ${correspondingObjectMetadataItem.nameSingular}, make sure the request for activities eagerly loads for the target objects on activity target relation.`,
|
`Cannot find target object record of type ${correspondingObjectMetadataItem.nameSingular}, make sure the request for activities eagerly loads for the target objects on activity target relation.`,
|
||||||
);
|
);
|
||||||
@ -62,7 +63,7 @@ export const useActivityTargetObjectRecords = (
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
activityTarget,
|
activityTarget,
|
||||||
targetObject: targetObjectRecord ?? undefined,
|
targetObject: targetObjectRecord,
|
||||||
targetObjectMetadataItem: correspondingObjectMetadataItem,
|
targetObjectMetadataItem: correspondingObjectMetadataItem,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||||
|
|
||||||
export const getLabelIdentifierFieldValue = (
|
export const getLabelIdentifierFieldValue = (
|
||||||
record: ObjectRecord,
|
record: ObjectRecord,
|
||||||
|
|||||||
@ -22,6 +22,7 @@ export const useRecordChipData = ({
|
|||||||
|
|
||||||
const identifierChipGenerator =
|
const identifierChipGenerator =
|
||||||
identifierChipGeneratorPerObject[objectNameSingular];
|
identifierChipGeneratorPerObject[objectNameSingular];
|
||||||
|
|
||||||
if (isDefined(identifierChipGenerator)) {
|
if (isDefined(identifierChipGenerator)) {
|
||||||
return {
|
return {
|
||||||
recordChipData: identifierChipGenerator(record),
|
recordChipData: identifierChipGenerator(record),
|
||||||
|
|||||||
@ -48,13 +48,17 @@ export const RelationFromManyFieldDisplay = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ExpandableList isChipCountDisplayed={isFocused}>
|
<ExpandableList isChipCountDisplayed={isFocused}>
|
||||||
{fieldValue.filter(isDefined).map((record) => (
|
{fieldValue
|
||||||
<RecordChip
|
.map((record) =>
|
||||||
key={record.id}
|
isDefined(record) && isDefined(record[relationFieldName]) ? (
|
||||||
objectNameSingular={objectNameSingular}
|
<RecordChip
|
||||||
record={record[relationFieldName]}
|
key={record.id}
|
||||||
/>
|
objectNameSingular={objectNameSingular}
|
||||||
))}
|
record={record[relationFieldName]}
|
||||||
|
/>
|
||||||
|
) : undefined,
|
||||||
|
)
|
||||||
|
.filter(isDefined)}
|
||||||
</ExpandableList>
|
</ExpandableList>
|
||||||
);
|
);
|
||||||
} else if (isRelationFromActivityTargets) {
|
} else if (isRelationFromActivityTargets) {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||||
|
|
||||||
@ -10,14 +10,20 @@ export const DropdownOnToggleEffect = ({
|
|||||||
onDropdownOpen?: () => void;
|
onDropdownOpen?: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { isDropdownOpen } = useDropdown();
|
const { isDropdownOpen } = useDropdown();
|
||||||
|
const [currentIsDropdownOpen, setCurrentIsDropdownOpen] =
|
||||||
|
useState(isDropdownOpen);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isDropdownOpen) {
|
if (isDropdownOpen && !currentIsDropdownOpen) {
|
||||||
|
setCurrentIsDropdownOpen(isDropdownOpen);
|
||||||
onDropdownOpen?.();
|
onDropdownOpen?.();
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
if (!isDropdownOpen && currentIsDropdownOpen) {
|
||||||
|
setCurrentIsDropdownOpen(isDropdownOpen);
|
||||||
onDropdownClose?.();
|
onDropdownClose?.();
|
||||||
}
|
}
|
||||||
}, [isDropdownOpen, onDropdownClose, onDropdownOpen]);
|
}, [currentIsDropdownOpen, isDropdownOpen, onDropdownClose, onDropdownOpen]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user