Improved activity editor re-renders (#4149)

* Refactor task count

* Fixed show page rerender

* Less rerenders and way better title and body UX

* Finished breaking down activity editor subscriptions

* Removed console.log

* Last console.log

* Fixed bugs and cleaned
This commit is contained in:
Lucas Bordeau
2024-02-23 17:54:27 +01:00
committed by GitHub
parent 5de1c2c31d
commit fb920a92e7
48 changed files with 1114 additions and 527 deletions

View File

@ -1,11 +1,9 @@
import { useEffect } from 'react';
import { useSetRecoilState } from 'recoil';
import { useRecoilState, useRecoilValue } from 'recoil';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
import { isObjectMetadataAvailableForRelation } from '@/object-metadata/utils/isObjectMetadataAvailableForRelation';
import { parseFieldType } from '@/object-metadata/utils/parseFieldType';
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
import {
FieldContext,
@ -16,7 +14,9 @@ import { RecordInlineCell } from '@/object-record/record-inline-cell/components/
import { PropertyBox } from '@/object-record/record-inline-cell/property-box/components/PropertyBox';
import { InlineCellHotkeyScope } from '@/object-record/record-inline-cell/types/InlineCellHotkeyScope';
import { RecordRelationFieldCardSection } from '@/object-record/record-relation-card/components/RecordRelationFieldCardSection';
import { recordLoadingFamilyState } from '@/object-record/record-store/states/recordLoadingFamilyState';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { recordStoreIdentifierFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreIdentifierSelector';
import { isFieldMetadataItemAvailable } from '@/object-record/utils/isFieldMetadataItemAvailable';
import { ShowPageContainer } from '@/ui/layout/page/ShowPageContainer';
import { ShowPageLeftContainer } from '@/ui/layout/show-page/components/ShowPageLeftContainer';
@ -29,6 +29,7 @@ import {
FileFolder,
useUploadImageMutation,
} from '~/generated/graphql';
import { isDefined } from '~/utils/isDefined';
type RecordShowContainerProps = {
objectNameSingular: string;
@ -39,28 +40,25 @@ export const RecordShowContainer = ({
objectNameSingular,
objectRecordId,
}: RecordShowContainerProps) => {
const {
objectMetadataItem,
labelIdentifierFieldMetadata,
mapToObjectRecordIdentifier,
} = useObjectMetadataItem({
objectNameSingular,
});
const { objectMetadataItem, labelIdentifierFieldMetadata } =
useObjectMetadataItem({
objectNameSingular,
});
const setEntityFields = useSetRecoilState(
const [recordLoading] = useRecoilState(
recordLoadingFamilyState(objectRecordId),
);
const [recordFromStore] = useRecoilState(
recordStoreFamilyState(objectRecordId),
);
const { record, loading } = useFindOneRecord({
objectRecordId,
objectNameSingular,
depth: 3,
});
useEffect(() => {
if (!record) return;
setEntityFields(record);
}, [record, setEntityFields]);
const recordIdentifier = useRecoilValue(
recordStoreIdentifierFamilySelector({
objectNameSingular,
recordId: objectRecordId,
}),
);
const [uploadImage] = useUploadImageMutation();
const { updateOneRecord } = useUpdateOneRecord({ objectNameSingular });
@ -96,12 +94,12 @@ export const RecordShowContainer = ({
if (!updateOneRecord) {
return;
}
if (!record) {
if (!recordFromStore) {
return;
}
await updateOneRecord({
idToUpdate: record.id,
idToUpdate: objectRecordId,
updateOneRecordInput: {
avatarUrl,
},
@ -132,23 +130,19 @@ export const RecordShowContainer = ({
<RecoilScope CustomRecoilScopeContext={ShowPageRecoilScopeContext}>
<ShowPageContainer>
<ShowPageLeftContainer>
{!loading && !!record && (
{!recordLoading && isDefined(recordFromStore) && (
<>
<ShowPageSummaryCard
id={record.id}
logoOrAvatar={
mapToObjectRecordIdentifier(record).avatarUrl ?? ''
}
avatarPlaceholder={
mapToObjectRecordIdentifier(record).name ?? ''
}
date={record.createdAt ?? ''}
id={objectRecordId}
logoOrAvatar={recordIdentifier?.avatarUrl ?? ''}
avatarPlaceholder={recordIdentifier?.name ?? ''}
date={recordFromStore.createdAt ?? ''}
title={
<FieldContext.Provider
value={{
entityId: record.id,
entityId: objectRecordId,
recoilScopeId:
record.id + labelIdentifierFieldMetadata?.id,
objectRecordId + labelIdentifierFieldMetadata?.id,
isLabelIdentifier: false,
fieldDefinition: {
type: parseFieldType(
@ -169,9 +163,7 @@ export const RecordShowContainer = ({
<RecordInlineCell />
</FieldContext.Provider>
}
avatarType={
mapToObjectRecordIdentifier(record).avatarType ?? 'rounded'
}
avatarType={recordIdentifier?.avatarType ?? 'rounded'}
onUploadPicture={
objectNameSingular === 'person' ? onUploadPicture : undefined
}
@ -179,11 +171,11 @@ export const RecordShowContainer = ({
<PropertyBox extraPadding={true}>
{inlineFieldMetadataItems.map((fieldMetadataItem, index) => (
<FieldContext.Provider
key={record.id + fieldMetadataItem.id}
key={objectRecordId + fieldMetadataItem.id}
value={{
entityId: record.id,
entityId: objectRecordId,
maxWidth: 200,
recoilScopeId: record.id + fieldMetadataItem.id,
recoilScopeId: objectRecordId + fieldMetadataItem.id,
isLabelIdentifier: false,
fieldDefinition:
formatFieldMetadataItemAsColumnDefinition({
@ -217,10 +209,10 @@ export const RecordShowContainer = ({
})
.map((fieldMetadataItem, index) => (
<FieldContext.Provider
key={record.id + fieldMetadataItem.id}
key={objectRecordId + fieldMetadataItem.id}
value={{
entityId: record.id,
recoilScopeId: record.id + fieldMetadataItem.id,
entityId: objectRecordId,
recoilScopeId: objectRecordId + fieldMetadataItem.id,
isLabelIdentifier: false,
fieldDefinition:
formatFieldMetadataItemAsColumnDefinition({
@ -238,12 +230,11 @@ export const RecordShowContainer = ({
</>
)}
</ShowPageLeftContainer>
{record ? (
{recordFromStore ? (
<ShowPageRightContainer
targetableObject={{
id: record.id,
id: objectRecordId,
targetObjectNameSingular: objectMetadataItem?.nameSingular,
targetObjectRecord: record,
}}
timeline
tasks

View File

@ -0,0 +1,48 @@
import { useEffect } from 'react';
import { useRecoilState, useSetRecoilState } from 'recoil';
import { useActivityConnectionUtils } from '@/activities/hooks/useActivityConnectionUtils';
import { Activity } from '@/activities/types/Activity';
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
import { recordLoadingFamilyState } from '@/object-record/record-store/states/recordLoadingFamilyState';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { isDefined } from '~/utils/isDefined';
export const RecordShowContainer = ({
objectRecordId,
objectNameSingular,
}: {
objectRecordId: string;
objectNameSingular: string;
}) => {
const { record, loading } = useFindOneRecord({
objectRecordId,
objectNameSingular,
depth: 3,
});
const setRecordStore = useSetRecoilState(
recordStoreFamilyState(objectRecordId),
);
const [recordLoading, setRecordLoading] = useRecoilState(
recordLoadingFamilyState(objectRecordId),
);
useEffect(() => {
if (loading !== recordLoading) {
setRecordLoading(loading);
}
}, [loading, recordLoading, setRecordLoading]);
const { makeActivityWithoutConnection } = useActivityConnectionUtils();
useEffect(() => {
if (!loading && isDefined(record)) {
const { activity: activityWithoutConnection } =
makeActivityWithoutConnection(record as any);
setRecordStore(activityWithoutConnection as Activity);
}
}, [loading, record, setRecordStore, makeActivityWithoutConnection]);
};

View File

@ -0,0 +1,6 @@
import { atomFamily } from 'recoil';
export const recordLoadingFamilyState = atomFamily<boolean, string>({
key: 'recordLoadingFamilyState',
default: false,
});

View File

@ -0,0 +1,35 @@
import { selectorFamily } from 'recoil';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { getObjectRecordIdentifier } from '@/object-metadata/utils/getObjectRecordIdentifier';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
export const recordStoreIdentifierFamilySelector = selectorFamily({
key: 'recordStoreIdentifierFamilySelector',
get:
({
recordId,
objectNameSingular,
}: {
recordId: string;
objectNameSingular: string;
}) =>
({ get }) => {
const recordFromStore = get(recordStoreFamilyState(recordId));
const objectMetadataItems = get(objectMetadataItemsState);
const objectMetadataItem = objectMetadataItems.find(
(item) => item.nameSingular === objectNameSingular,
);
if (!objectMetadataItem || !recordFromStore) {
return null;
}
return getObjectRecordIdentifier({
objectMetadataItem: objectMetadataItem,
record: recordFromStore,
});
},
});

View File

@ -0,0 +1,14 @@
import { ObjectRecordEdge } from '@/object-record/types/ObjectRecordEdge';
import { isDefined } from '~/utils/isDefined';
export const getChildRelationArray = ({
childRelation,
}: {
childRelation: any;
}) => {
if (isDefined(childRelation.edges) && Array.isArray(childRelation.edges)) {
return childRelation.edges.map((edge: ObjectRecordEdge) => edge.node);
} else {
return childRelation;
}
};