Improve Board performances (#2626)

Improve app performances
This commit is contained in:
Charles Bochet
2023-11-22 09:58:49 +01:00
committed by GitHub
parent ee8f6899fc
commit 10febd9aeb
11 changed files with 88 additions and 122 deletions

View File

@ -1,5 +1,4 @@
import styled from '@emotion/styled';
import { useSetRecoilState } from 'recoil';
import { BoardContext } from '@/companies/states/contexts/BoardContext';
import { BoardOptionsDropdown } from '@/ui/layout/board/components/BoardOptionsDropdown';
@ -11,7 +10,6 @@ import {
import { EntityBoardActionBar } from '@/ui/layout/board/components/EntityBoardActionBar';
import { EntityBoardContextMenu } from '@/ui/layout/board/components/EntityBoardContextMenu';
import { ViewBar } from '@/views/components/ViewBar';
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
import { ViewScope } from '@/views/scopes/ViewScope';
import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
@ -38,30 +36,12 @@ export const CompanyBoard = ({
}: CompanyBoardProps) => {
const viewScopeId = 'company-board-view';
const {
currentViewFieldsState,
currentViewFiltersState,
currentViewSortsState,
} = useViewScopedStates({
customViewScopeId: viewScopeId,
});
const setCurrentViewFields = useSetRecoilState(currentViewFieldsState);
const setCurrentViewFilters = useSetRecoilState(currentViewFiltersState);
const setCurrentViewSorts = useSetRecoilState(currentViewSortsState);
return (
<ViewScope
viewScopeId={viewScopeId}
onViewFieldsChange={(viewFields) => {
setCurrentViewFields(viewFields);
}}
onViewFiltersChange={(viewFilters) => {
setCurrentViewFilters(viewFilters);
}}
onViewSortsChange={(viewSorts) => {
setCurrentViewSorts(viewSorts);
}}
onViewFieldsChange={() => {}}
onViewFiltersChange={() => {}}
onViewSortsChange={() => {}}
>
<StyledContainer>
<BoardContext.Provider

View File

@ -127,7 +127,7 @@ const StyledCompactIconContainer = styled.div`
export const CompanyBoardCard = () => {
const { BoardRecoilScopeContext } = useBoardContext();
const { currentCardSelected, setCurrentCardSelected } =
const { isCurrentCardSelected, setCurrentCardSelected } =
useCurrentCardSelected();
const boardCardId = useContext(BoardCardIdContext);
@ -200,9 +200,9 @@ export const CompanyBoardCard = () => {
return (
<StyledBoardCardWrapper>
<StyledBoardCard
selected={currentCardSelected}
selected={isCurrentCardSelected}
onMouseLeave={OnMouseLeaveBoard}
onClick={() => setCurrentCardSelected(!currentCardSelected)}
onClick={() => setCurrentCardSelected(!isCurrentCardSelected)}
>
<StyledBoardCardHeader showCompactView={showCompactView}>
<CompanyChip
@ -225,8 +225,8 @@ export const CompanyBoardCard = () => {
)}
<StyledCheckboxContainer className="checkbox-container">
<Checkbox
checked={currentCardSelected}
onChange={() => setCurrentCardSelected(!currentCardSelected)}
checked={isCurrentCardSelected}
onChange={() => setCurrentCardSelected(!isCurrentCardSelected)}
variant={CheckboxVariant.Secondary}
/>
</StyledCheckboxContainer>

View File

@ -164,21 +164,19 @@ export const HooksCompanyBoardEffect = () => {
setViewType?.(ViewType.Kanban);
}, [objectMetadataItem, setViewObjectMetadataId, setViewType]);
const loading = !companies;
const { setActionBarEntries } = useBoardActionBarEntries();
const { setContextMenuEntries } = useBoardContextMenuEntries();
useEffect(() => {
if (!loading && opportunities && companies) {
if (opportunities && companies) {
setActionBarEntries();
setContextMenuEntries();
updateCompanyBoard(pipelineSteps, opportunities, companies);
setEntityCountInCurrentView(opportunities.length);
}
}, [
companies,
loading,
opportunities,
pipelineSteps,
setActionBarEntries,

View File

@ -105,7 +105,10 @@ export const useUpdateCompanyBoard = () =>
position: pipelineStep.position ?? 0,
};
});
if (currentBoardColumns.length === 0) {
if (
currentBoardColumns.length === 0 &&
!isDeeplyEqual(newBoardColumns, currentBoardColumns)
) {
set(boardColumnsState, newBoardColumns);
set(savedBoardColumnsState, newBoardColumns);
}

View File

@ -1,15 +1,12 @@
import { useRecoilValue } from 'recoil';
import { useRecoilCallback } from 'recoil';
import { useCreateOneObjectRecord } from '@/object-record/hooks/useCreateOneObjectRecord';
import { useDeleteOneObjectRecord } from '@/object-record/hooks/useDeleteOneObjectRecord';
import { currentPipelineState } from '@/pipeline/states/currentPipelineState';
import { PipelineStep } from '@/pipeline/types/PipelineStep';
import { BoardColumnDefinition } from '@/ui/layout/board/types/BoardColumnDefinition';
import { currentPipelineState } from '../states/currentPipelineState';
export const usePipelineSteps = () => {
const currentPipeline = useRecoilValue(currentPipelineState);
const { createOneObject: createOnePipelineStep } =
useCreateOneObjectRecord<PipelineStep>({
objectNameSingular: 'pipelineStep',
@ -20,28 +17,38 @@ export const usePipelineSteps = () => {
objectNameSingular: 'pipelineStep',
});
const handlePipelineStepAdd = async (boardColumn: BoardColumnDefinition) => {
if (!currentPipeline?.id) return;
const handlePipelineStepAdd = useRecoilCallback(
({ snapshot }) =>
async (boardColumn: BoardColumnDefinition) => {
const currentPipeline = await snapshot.getPromise(currentPipelineState);
if (!currentPipeline?.id) return;
return createOnePipelineStep?.({
variables: {
data: {
color: boardColumn.colorCode ?? 'gray',
id: boardColumn.id,
position: boardColumn.position,
name: boardColumn.title,
pipeline: { connect: { id: currentPipeline.id } },
type: 'ongoing',
},
return createOnePipelineStep?.({
variables: {
data: {
color: boardColumn.colorCode ?? 'gray',
id: boardColumn.id,
position: boardColumn.position,
name: boardColumn.title,
pipeline: { connect: { id: currentPipeline.id } },
type: 'ongoing',
},
},
});
},
});
};
[createOnePipelineStep],
);
const handlePipelineStepDelete = async (boardColumnId: string) => {
if (!currentPipeline?.id) return;
const handlePipelineStepDelete = useRecoilCallback(
({ snapshot }) =>
async (boardColumnId: string) => {
const currentPipeline = await snapshot.getPromise(currentPipelineState);
if (!currentPipeline?.id) return;
return deleteOnePipelineStep?.(boardColumnId);
};
return deleteOnePipelineStep?.(boardColumnId);
},
[deleteOnePipelineStep],
);
return { handlePipelineStepAdd, handlePipelineStepDelete };
};

View File

@ -1,9 +1,10 @@
import { useEffect, useState } from 'react';
import { useRecoilState } from 'recoil';
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
import { iconsState } from '@/ui/input/states/iconsState';
export const useLazyLoadIcons = () => {
const [icons, setIcons] = useState<Record<string, IconComponent>>({});
const [icons, setIcons] = useRecoilState(iconsState);
const [isLoadingIcons, setIsLoadingIcons] = useState(true);
useEffect(() => {
@ -11,7 +12,7 @@ export const useLazyLoadIcons = () => {
setIcons(lazyLoadedIcons);
setIsLoadingIcons(false);
});
}, []);
}, [setIcons]);
return { icons, isLoadingIcons };
};

View File

@ -0,0 +1,8 @@
import { atom } from 'recoil';
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
export const iconsState = atom<Record<string, IconComponent>>({
key: 'iconsState',
default: {},
});

View File

@ -1,5 +1,4 @@
import { useCallback, useRef } from 'react';
import { useApolloClient } from '@apollo/client';
import styled from '@emotion/styled';
import { DragDropContext, OnDragEndResponder } from '@hello-pangea/dnd'; // Atlassian dnd does not support StrictMode from RN 18, so we use a fork @hello-pangea/dnd https://github.com/atlassian/react-beautiful-dnd/issues/2350
import { useRecoilValue } from 'recoil';
@ -9,6 +8,9 @@ import { Opportunity } from '@/pipeline/types/Opportunity';
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
import { StyledBoard } from '@/ui/layout/board/components/StyledBoard';
import { BoardColumnContext } from '@/ui/layout/board/contexts/BoardColumnContext';
import { useSetCardSelected } from '@/ui/layout/board/hooks/useSetCardSelected';
import { useUpdateBoardCardIds } from '@/ui/layout/board/hooks/useUpdateBoardCardIds';
import { boardColumnsState } from '@/ui/layout/board/states/boardColumnsState';
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useListenClickOutsideByClassName } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
@ -16,10 +18,6 @@ import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { logError } from '~/utils/logError';
import { useCurrentCardSelected } from '../hooks/useCurrentCardSelected';
import { useSetCardSelected } from '../hooks/useSetCardSelected';
import { useUpdateBoardCardIds } from '../hooks/useUpdateBoardCardIds';
import { boardColumnsState } from '../states/boardColumnsState';
import { BoardColumnRecoilScopeContext } from '../states/recoil-scope-contexts/BoardColumnRecoilScopeContext';
import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
import { BoardOptions } from '../types/BoardOptions';
@ -53,16 +51,13 @@ export const EntityBoard = ({
onEditColumnTitle,
}: EntityBoardProps) => {
const boardColumns = useRecoilValue(boardColumnsState);
const setCardSelected = useSetCardSelected();
const { updateOneObject: updateOneOpportunity } =
useUpdateOneObjectRecord<Opportunity>({
objectNameSingular: 'opportunity',
});
const apolloClient = useApolloClient();
const { unselectAllActiveCards } = useCurrentCardSelected();
const { unselectAllActiveCards, setCardSelected } = useSetCardSelected();
const updatePipelineProgressStageInDB = useCallback(
async (pipelineProgressId: string, pipelineStepId: string) => {
@ -72,19 +67,8 @@ export const EntityBoard = ({
pipelineStepId: pipelineStepId,
},
});
const cache = apolloClient.cache;
cache.modify({
id: cache.identify({
id: pipelineProgressId,
__typename: 'PipelineProgress',
}),
fields: {
pipelineStepId: () => pipelineStepId,
},
});
},
[apolloClient.cache, updateOneOpportunity],
[updateOneOpportunity],
);
useListenClickOutsideByClassName({
@ -135,7 +119,7 @@ export const EntityBoard = ({
PageHotkeyScope.OpportunitiesPage,
);
return (boardColumns?.length ?? 0) > 0 ? (
return (
<StyledWrapper>
<StyledBoardHeader />
<ScrollWrapper>
@ -172,7 +156,5 @@ export const EntityBoard = ({
onDragSelectionChange={setCardSelected}
/>
</StyledWrapper>
) : (
<></>
);
};

View File

@ -1,16 +1,16 @@
import { useContext } from 'react';
import { useRecoilCallback, useRecoilValue, useSetRecoilState } from 'recoil';
import { activeCardIdsState } from '@/ui/layout/board/states/activeCardIdsState';
import { actionBarOpenState } from '@/ui/navigation/action-bar/states/actionBarIsOpenState';
import { BoardCardIdContext } from '../contexts/BoardCardIdContext';
import { activeCardIdsState } from '../states/activeCardIdsState';
import { isCardSelectedFamilyState } from '../states/isCardSelectedFamilyState';
export const useCurrentCardSelected = () => {
const currentCardId = useContext(BoardCardIdContext);
const isCardSelected = useRecoilValue(
const isCurrentCardSelected = useRecoilValue(
isCardSelectedFamilyState(currentCardId ?? ''),
);
@ -38,24 +38,8 @@ export const useCurrentCardSelected = () => {
[currentCardId, setActiveCardIds],
);
const unselectAllActiveCards = useRecoilCallback(
({ set, snapshot }) =>
() => {
const activeCardIds = snapshot.getLoadable(activeCardIdsState).contents;
activeCardIds.forEach((cardId: string) => {
set(isCardSelectedFamilyState(cardId), false);
});
set(activeCardIdsState, []);
set(actionBarOpenState, false);
},
[],
);
return {
currentCardSelected: isCardSelected,
isCurrentCardSelected,
setCurrentCardSelected,
unselectAllActiveCards,
};
};

View File

@ -1,4 +1,4 @@
import { useRecoilCallback, useSetRecoilState } from 'recoil';
import { useRecoilCallback } from 'recoil';
import { actionBarOpenState } from '@/ui/navigation/action-bar/states/actionBarIsOpenState';
@ -6,15 +6,13 @@ import { activeCardIdsState } from '../states/activeCardIdsState';
import { isCardSelectedFamilyState } from '../states/isCardSelectedFamilyState';
export const useSetCardSelected = () => {
const setActionBarOpenState = useSetRecoilState(actionBarOpenState);
return useRecoilCallback(
const setCardSelected = useRecoilCallback(
({ set, snapshot }) =>
(cardId: string, selected: boolean) => {
const activeCardIds = snapshot.getLoadable(activeCardIdsState).contents;
set(isCardSelectedFamilyState(cardId), selected);
setActionBarOpenState(selected || activeCardIds.length > 0);
set(actionBarOpenState, selected || activeCardIds.length > 0);
if (selected) {
set(activeCardIdsState, [...activeCardIds, cardId]);
@ -26,4 +24,24 @@ export const useSetCardSelected = () => {
}
},
);
const unselectAllActiveCards = useRecoilCallback(
({ set, snapshot }) =>
() => {
const activeCardIds = snapshot.getLoadable(activeCardIdsState).contents;
activeCardIds.forEach((cardId: string) => {
set(isCardSelectedFamilyState(cardId), false);
});
set(activeCardIdsState, []);
set(actionBarOpenState, false);
},
[],
);
return {
setCardSelected,
unselectAllActiveCards,
};
};

View File

@ -1,9 +1,7 @@
import { useEffect } from 'react';
import styled from '@emotion/styled';
import { CompanyBoard } from '@/companies/board/components/CompanyBoard';
import { CompanyBoardRecoilScopeContext } from '@/companies/states/recoil-scope-contexts/CompanyBoardRecoilScopeContext';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useUpdateOneObjectRecord } from '@/object-record/hooks/useUpdateOneObjectRecord';
import { PipelineAddButton } from '@/pipeline/components/PipelineAddButton';
import { usePipelineSteps } from '@/pipeline/hooks/usePipelineSteps';
@ -14,7 +12,6 @@ import { PageBody } from '@/ui/layout/page/PageBody';
import { PageContainer } from '@/ui/layout/page/PageContainer';
import { PageHeader } from '@/ui/layout/page/PageHeader';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { useView } from '@/views/hooks/useView';
import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
const StyledBoardContainer = styled.div`
@ -46,18 +43,6 @@ export const Opportunities = () => {
});
};
const opportunitiesV2MetadataId = useObjectMetadataItem({
objectNameSingular: 'opportunity',
}).objectMetadataItem?.id;
const { setViewObjectMetadataId } = useView({
viewScopeId: 'company-board-view',
});
useEffect(() => {
setViewObjectMetadataId?.(opportunitiesV2MetadataId);
}, [opportunitiesV2MetadataId, setViewObjectMetadataId]);
return (
<PageContainer>
<RecoilScope>