Fix optimistic rendering issues on board and table (#2846)
* Fix optimistic rendering issues on board and table * Remove dead code * Improve re-renders of Table * Remove re-renders on board
This commit is contained in:
@ -11,7 +11,6 @@ import { RecordBoardInternalEffect } from '@/ui/object/record-board/components/R
|
||||
import { RecordBoardContextMenu } from '@/ui/object/record-board/context-menu/components/RecordBoardContextMenu';
|
||||
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||
import { useSetRecordBoardCardSelectedInternal } from '@/ui/object/record-board/hooks/internal/useSetRecordBoardCardSelectedInternal';
|
||||
import { useUpdateRecordBoardCardIdsInternal } from '@/ui/object/record-board/hooks/internal/useUpdateRecordBoardCardIdsInternal';
|
||||
import { RecordBoardScope } from '@/ui/object/record-board/scopes/RecordBoardScope';
|
||||
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
@ -94,21 +93,14 @@ export const RecordBoard = ({
|
||||
callback: unselectAllActiveCards,
|
||||
});
|
||||
|
||||
const updateBoardCardIds = useUpdateRecordBoardCardIdsInternal({
|
||||
recordBoardScopeId,
|
||||
});
|
||||
|
||||
const onDragEnd: OnDragEndResponder = useCallback(
|
||||
async (result) => {
|
||||
if (!boardColumns) return;
|
||||
|
||||
updateBoardCardIds(result);
|
||||
|
||||
try {
|
||||
const draggedEntityId = result.draggableId;
|
||||
const destinationColumnId = result.destination?.droppableId;
|
||||
|
||||
// TODO: abstract
|
||||
if (
|
||||
draggedEntityId &&
|
||||
destinationColumnId &&
|
||||
@ -123,7 +115,7 @@ export const RecordBoard = ({
|
||||
logError(e);
|
||||
}
|
||||
},
|
||||
[boardColumns, updatePipelineProgressStageInDB, updateBoardCardIds],
|
||||
[boardColumns, updatePipelineProgressStageInDB],
|
||||
);
|
||||
|
||||
const sortedBoardColumns = [...boardColumns].sort((a, b) => {
|
||||
|
||||
@ -1,24 +1,17 @@
|
||||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Draggable, Droppable, DroppableProvided } from '@hello-pangea/dnd';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { IconDotsVertical } from '@/ui/display/icon';
|
||||
import { Tag } from '@/ui/display/tag/components/Tag';
|
||||
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
|
||||
import { RecordBoardCard } from '@/ui/object/record-board/components/RecordBoardCard';
|
||||
import { RecordBoardColumnHeader } from '@/ui/object/record-board/components/RecordBoardColumnHeader';
|
||||
import { BoardCardIdContext } from '@/ui/object/record-board/contexts/BoardCardIdContext';
|
||||
import { BoardColumnDefinition } from '@/ui/object/record-board/types/BoardColumnDefinition';
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
|
||||
import { BoardColumnContext } from '../contexts/BoardColumnContext';
|
||||
import { recordBoardCardIdsByColumnIdFamilyState } from '../states/recordBoardCardIdsByColumnIdFamilyState';
|
||||
import { recordBoardColumnTotalsFamilySelector } from '../states/selectors/recordBoardColumnTotalsFamilySelector';
|
||||
import { BoardColumnHotkeyScope } from '../types/BoardColumnHotkeyScope';
|
||||
import { BoardOptions } from '../types/BoardOptions';
|
||||
|
||||
import { RecordBoardColumnDropdownMenu } from './RecordBoardColumnDropdownMenu';
|
||||
|
||||
const StyledPlaceholder = styled.div`
|
||||
min-height: 1px;
|
||||
`;
|
||||
@ -47,40 +40,6 @@ const StyledColumn = styled.div<{ isFirstColumn: boolean }>`
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const StyledHeader = styled.div`
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 24px;
|
||||
justify-content: left;
|
||||
margin-bottom: ${({ theme }) => theme.spacing(2)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledAmount = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
margin-left: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledNumChildren = styled.div`
|
||||
align-items: center;
|
||||
background-color: ${({ theme }) => theme.background.tertiary};
|
||||
border-radius: ${({ theme }) => theme.border.radius.rounded};
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
display: flex;
|
||||
height: 20px;
|
||||
justify-content: center;
|
||||
line-height: ${({ theme }) => theme.text.lineHeight.lg};
|
||||
margin-left: auto;
|
||||
width: 16px;
|
||||
`;
|
||||
|
||||
const StyledHeaderActions = styled.div`
|
||||
display: flex;
|
||||
margin-left: auto;
|
||||
`;
|
||||
|
||||
type BoardColumnCardsContainerProps = {
|
||||
children: React.ReactNode;
|
||||
droppableProvided: DroppableProvided;
|
||||
@ -119,30 +78,6 @@ export const RecordBoardColumn = ({
|
||||
onDelete,
|
||||
onTitleEdit,
|
||||
}: RecordBoardColumnProps) => {
|
||||
const [isBoardColumnMenuOpen, setIsBoardColumnMenuOpen] = useState(false);
|
||||
const [isHeaderHovered, setIsHeaderHovered] = useState(false);
|
||||
|
||||
const {
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
goBackToPreviousHotkeyScope,
|
||||
} = usePreviousHotkeyScope();
|
||||
|
||||
const handleBoardColumnMenuOpen = () => {
|
||||
setIsBoardColumnMenuOpen(true);
|
||||
setHotkeyScopeAndMemorizePreviousScope(BoardColumnHotkeyScope.BoardColumn, {
|
||||
goto: false,
|
||||
});
|
||||
};
|
||||
|
||||
const handleBoardColumnMenuClose = () => {
|
||||
goBackToPreviousHotkeyScope();
|
||||
setIsBoardColumnMenuOpen(false);
|
||||
};
|
||||
|
||||
const boardColumnTotal = useRecoilValue(
|
||||
recordBoardColumnTotalsFamilySelector(recordBoardColumnId),
|
||||
);
|
||||
|
||||
const cardIds = useRecoilValue(
|
||||
recordBoardCardIdsByColumnIdFamilyState(recordBoardColumnId),
|
||||
);
|
||||
@ -165,53 +100,12 @@ export const RecordBoardColumn = ({
|
||||
<Droppable droppableId={recordBoardColumnId}>
|
||||
{(droppableProvided) => (
|
||||
<StyledColumn isFirstColumn={isFirstColumn}>
|
||||
<StyledHeader
|
||||
onMouseEnter={() => setIsHeaderHovered(true)}
|
||||
onMouseLeave={() => setIsHeaderHovered(false)}
|
||||
>
|
||||
<Tag
|
||||
onClick={handleBoardColumnMenuOpen}
|
||||
color={columnDefinition.colorCode ?? 'gray'}
|
||||
text={columnDefinition.title}
|
||||
/>
|
||||
{!!boardColumnTotal && (
|
||||
<StyledAmount>${boardColumnTotal}</StyledAmount>
|
||||
)}
|
||||
{!isHeaderHovered && (
|
||||
<StyledNumChildren>{cardIds.length}</StyledNumChildren>
|
||||
)}
|
||||
{isHeaderHovered && (
|
||||
<StyledHeaderActions>
|
||||
<LightIconButton
|
||||
accent="tertiary"
|
||||
Icon={IconDotsVertical}
|
||||
onClick={handleBoardColumnMenuOpen}
|
||||
/>
|
||||
{/* <LightIconButton
|
||||
accent="tertiary"
|
||||
Icon={IconPlus}
|
||||
onClick={() => {}}
|
||||
/> */}
|
||||
</StyledHeaderActions>
|
||||
)}
|
||||
</StyledHeader>
|
||||
{isBoardColumnMenuOpen && (
|
||||
<RecordBoardColumnDropdownMenu
|
||||
onClose={handleBoardColumnMenuClose}
|
||||
onDelete={onDelete}
|
||||
onTitleEdit={handleTitleEdit}
|
||||
stageId={recordBoardColumnId}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isBoardColumnMenuOpen && (
|
||||
<RecordBoardColumnDropdownMenu
|
||||
onClose={handleBoardColumnMenuClose}
|
||||
onDelete={onDelete}
|
||||
onTitleEdit={handleTitleEdit}
|
||||
stageId={recordBoardColumnId}
|
||||
/>
|
||||
)}
|
||||
<RecordBoardColumnHeader
|
||||
recordBoardColumnId={recordBoardColumnId}
|
||||
columnDefinition={columnDefinition}
|
||||
onDelete={onDelete}
|
||||
onTitleEdit={handleTitleEdit}
|
||||
/>
|
||||
<BoardColumnCardsContainer droppableProvided={droppableProvided}>
|
||||
{cardIds.map((cardId, index) => (
|
||||
<BoardCardIdContext.Provider value={cardId} key={cardId}>
|
||||
|
||||
@ -0,0 +1,136 @@
|
||||
import React, { useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { IconDotsVertical } from '@/ui/display/icon';
|
||||
import { Tag } from '@/ui/display/tag/components/Tag';
|
||||
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
|
||||
import { recordBoardColumnTotalsFamilySelector } from '@/ui/object/record-board/states/selectors/recordBoardColumnTotalsFamilySelector';
|
||||
import { BoardColumnDefinition } from '@/ui/object/record-board/types/BoardColumnDefinition';
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
|
||||
import { recordBoardCardIdsByColumnIdFamilyState } from '../states/recordBoardCardIdsByColumnIdFamilyState';
|
||||
import { BoardColumnHotkeyScope } from '../types/BoardColumnHotkeyScope';
|
||||
|
||||
import { RecordBoardColumnDropdownMenu } from './RecordBoardColumnDropdownMenu';
|
||||
|
||||
const StyledHeader = styled.div`
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 24px;
|
||||
justify-content: left;
|
||||
margin-bottom: ${({ theme }) => theme.spacing(2)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledAmount = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
margin-left: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledNumChildren = styled.div`
|
||||
align-items: center;
|
||||
background-color: ${({ theme }) => theme.background.tertiary};
|
||||
border-radius: ${({ theme }) => theme.border.radius.rounded};
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
display: flex;
|
||||
height: 20px;
|
||||
justify-content: center;
|
||||
line-height: ${({ theme }) => theme.text.lineHeight.lg};
|
||||
margin-left: auto;
|
||||
width: 16px;
|
||||
`;
|
||||
|
||||
const StyledHeaderActions = styled.div`
|
||||
display: flex;
|
||||
margin-left: auto;
|
||||
`;
|
||||
|
||||
type RecordBoardColumnHeaderProps = {
|
||||
recordBoardColumnId: string;
|
||||
columnDefinition: BoardColumnDefinition;
|
||||
onDelete?: (columnId: string) => void;
|
||||
onTitleEdit: (columnId: string, title: string, color: string) => void;
|
||||
};
|
||||
|
||||
export const RecordBoardColumnHeader = ({
|
||||
recordBoardColumnId,
|
||||
columnDefinition,
|
||||
onDelete,
|
||||
onTitleEdit,
|
||||
}: RecordBoardColumnHeaderProps) => {
|
||||
const [isBoardColumnMenuOpen, setIsBoardColumnMenuOpen] = useState(false);
|
||||
const [isHeaderHovered, setIsHeaderHovered] = useState(false);
|
||||
|
||||
const {
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
goBackToPreviousHotkeyScope,
|
||||
} = usePreviousHotkeyScope();
|
||||
|
||||
const handleBoardColumnMenuOpen = () => {
|
||||
setIsBoardColumnMenuOpen(true);
|
||||
setHotkeyScopeAndMemorizePreviousScope(BoardColumnHotkeyScope.BoardColumn, {
|
||||
goto: false,
|
||||
});
|
||||
};
|
||||
|
||||
const handleBoardColumnMenuClose = () => {
|
||||
goBackToPreviousHotkeyScope();
|
||||
setIsBoardColumnMenuOpen(false);
|
||||
};
|
||||
|
||||
const boardColumnTotal = useRecoilValue(
|
||||
recordBoardColumnTotalsFamilySelector(recordBoardColumnId),
|
||||
);
|
||||
|
||||
const cardIds = useRecoilValue(
|
||||
recordBoardCardIdsByColumnIdFamilyState(recordBoardColumnId),
|
||||
);
|
||||
|
||||
const handleTitleEdit = (title: string, color: string) => {
|
||||
onTitleEdit(recordBoardColumnId, title, color);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledHeader
|
||||
onMouseEnter={() => setIsHeaderHovered(true)}
|
||||
onMouseLeave={() => setIsHeaderHovered(false)}
|
||||
>
|
||||
<Tag
|
||||
onClick={handleBoardColumnMenuOpen}
|
||||
color={columnDefinition.colorCode ?? 'gray'}
|
||||
text={columnDefinition.title}
|
||||
/>
|
||||
{!!boardColumnTotal && <StyledAmount>${boardColumnTotal}</StyledAmount>}
|
||||
{!isHeaderHovered && (
|
||||
<StyledNumChildren>{cardIds.length}</StyledNumChildren>
|
||||
)}
|
||||
{isHeaderHovered && (
|
||||
<StyledHeaderActions>
|
||||
<LightIconButton
|
||||
accent="tertiary"
|
||||
Icon={IconDotsVertical}
|
||||
onClick={handleBoardColumnMenuOpen}
|
||||
/>
|
||||
{/* <LightIconButton
|
||||
accent="tertiary"
|
||||
Icon={IconPlus}
|
||||
onClick={() => {}}
|
||||
/> */}
|
||||
</StyledHeaderActions>
|
||||
)}
|
||||
</StyledHeader>
|
||||
{isBoardColumnMenuOpen && (
|
||||
<RecordBoardColumnDropdownMenu
|
||||
onClose={handleBoardColumnMenuClose}
|
||||
onDelete={onDelete}
|
||||
onTitleEdit={handleTitleEdit}
|
||||
stageId={recordBoardColumnId}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -1,7 +1,7 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useObjectRecordBoard } from '@/object-record/hooks/useObjectRecordBoard';
|
||||
import { useObjectRecordBoard } from '@/object-record/hooks/useObjectRecordBoard.1';
|
||||
import { useRecordBoardActionBarEntriesInternal } from '@/ui/object/record-board/hooks/internal/useRecordBoardActionBarEntriesInternal';
|
||||
import { useRecordBoardContextMenuEntriesInternal } from '@/ui/object/record-board/hooks/internal/useRecordBoardContextMenuEntriesInternal';
|
||||
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||
@ -18,7 +18,24 @@ export const RecordBoardInternalEffect = ({}) => {
|
||||
const { setActionBarEntries } = useRecordBoardActionBarEntriesInternal();
|
||||
const { setContextMenuEntries } = useRecordBoardContextMenuEntriesInternal();
|
||||
|
||||
const { fetchMoreOpportunities, fetchMoreCompanies } = useObjectRecordBoard();
|
||||
const {
|
||||
savedPipelineStepsState,
|
||||
savedOpportunitiesState,
|
||||
savedCompaniesState,
|
||||
} = useRecordBoardScopedStates();
|
||||
|
||||
const { fetchMoreOpportunities, fetchMoreCompanies, opportunities } =
|
||||
useObjectRecordBoard();
|
||||
|
||||
const [savedOpportunities, setSavedOpportunities] = useRecoilState(
|
||||
savedOpportunitiesState,
|
||||
);
|
||||
const savedPipelineSteps = useRecoilValue(savedPipelineStepsState);
|
||||
const savedCompanies = useRecoilValue(savedCompaniesState);
|
||||
|
||||
useEffect(() => {
|
||||
setSavedOpportunities(opportunities);
|
||||
}, [opportunities, setSavedOpportunities]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isDefined(fetchMoreOpportunities)) {
|
||||
@ -32,16 +49,6 @@ export const RecordBoardInternalEffect = ({}) => {
|
||||
}
|
||||
}, [fetchMoreCompanies]);
|
||||
|
||||
const {
|
||||
savedPipelineStepsState,
|
||||
savedOpportunitiesState,
|
||||
savedCompaniesState,
|
||||
} = useRecordBoardScopedStates();
|
||||
|
||||
const savedPipelineSteps = useRecoilValue(savedPipelineStepsState);
|
||||
const savedOpportunities = useRecoilValue(savedOpportunitiesState);
|
||||
const savedCompanies = useRecoilValue(savedCompaniesState);
|
||||
|
||||
useEffect(() => {
|
||||
if (savedOpportunities && savedCompanies) {
|
||||
setActionBarEntries();
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||
import { recordBoardCardIdsByColumnIdFamilyState } from '@/ui/object/record-board/states/recordBoardCardIdsByColumnIdFamilyState';
|
||||
|
||||
export const useUpdateCompanyBoardCardIdsInternal = () => {
|
||||
const { boardColumnsState } = useRecordBoardScopedStates();
|
||||
|
||||
return useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
(pipelineProgresses: Pick<Opportunity, 'pipelineStepId' | 'id'>[]) => {
|
||||
const boardColumns = snapshot
|
||||
.getLoadable(boardColumnsState)
|
||||
.valueOrThrow();
|
||||
|
||||
for (const boardColumn of boardColumns) {
|
||||
const boardCardIds = pipelineProgresses
|
||||
.filter((pipelineProgressToFilter) => {
|
||||
return pipelineProgressToFilter.pipelineStepId === boardColumn.id;
|
||||
})
|
||||
.map((pipelineProgress) => pipelineProgress.id);
|
||||
|
||||
set(
|
||||
recordBoardCardIdsByColumnIdFamilyState(boardColumn.id),
|
||||
boardCardIds,
|
||||
);
|
||||
}
|
||||
},
|
||||
[boardColumnsState],
|
||||
);
|
||||
};
|
||||
@ -1,106 +0,0 @@
|
||||
import { DropResult } 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 { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||
import { RecordBoardScopeInternalContext } from '@/ui/object/record-board/scopes/scope-internal-context/RecordBoardScopeInternalContext';
|
||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||
|
||||
import { recordBoardCardIdsByColumnIdFamilyState } from '../../states/recordBoardCardIdsByColumnIdFamilyState';
|
||||
import { BoardColumnDefinition } from '../../types/BoardColumnDefinition';
|
||||
|
||||
type useUpdateRecordBoardCardIdsInternalProps = {
|
||||
recordBoardScopeId?: string;
|
||||
};
|
||||
|
||||
export const useUpdateRecordBoardCardIdsInternal = (
|
||||
props: useUpdateRecordBoardCardIdsInternalProps,
|
||||
) => {
|
||||
const scopeId = useAvailableScopeIdOrThrow(
|
||||
RecordBoardScopeInternalContext,
|
||||
props?.recordBoardScopeId,
|
||||
);
|
||||
|
||||
const { boardColumnsState } = useRecordBoardScopedStates({
|
||||
recordBoardScopeId: scopeId,
|
||||
});
|
||||
|
||||
return useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
(result: DropResult) => {
|
||||
const currentBoardColumns = snapshot
|
||||
.getLoadable(boardColumnsState)
|
||||
.valueOrThrow();
|
||||
|
||||
const newBoardColumns = [...currentBoardColumns];
|
||||
|
||||
const { destination, source } = result;
|
||||
|
||||
if (!destination) return;
|
||||
|
||||
const sourceColumnIndex = newBoardColumns.findIndex(
|
||||
(boardColumn: BoardColumnDefinition) =>
|
||||
boardColumn.id === source.droppableId,
|
||||
);
|
||||
|
||||
const sourceColumn = newBoardColumns[sourceColumnIndex];
|
||||
|
||||
const destinationColumnIndex = newBoardColumns.findIndex(
|
||||
(boardColumn: BoardColumnDefinition) =>
|
||||
boardColumn.id === destination.droppableId,
|
||||
);
|
||||
|
||||
const destinationColumn = newBoardColumns[destinationColumnIndex];
|
||||
|
||||
if (!destinationColumn || !sourceColumn) return;
|
||||
|
||||
const sourceCardIds = [
|
||||
...snapshot
|
||||
.getLoadable(
|
||||
recordBoardCardIdsByColumnIdFamilyState(sourceColumn.id),
|
||||
)
|
||||
.valueOrThrow(),
|
||||
];
|
||||
|
||||
const destinationCardIds = [
|
||||
...snapshot
|
||||
.getLoadable(
|
||||
recordBoardCardIdsByColumnIdFamilyState(destinationColumn.id),
|
||||
)
|
||||
.valueOrThrow(),
|
||||
];
|
||||
|
||||
const destinationIndex =
|
||||
destination.index >= destinationCardIds.length
|
||||
? destinationCardIds.length - 1
|
||||
: destination.index;
|
||||
|
||||
if (sourceColumn.id === destinationColumn.id) {
|
||||
const [deletedCardId] = sourceCardIds.splice(source.index, 1);
|
||||
|
||||
sourceCardIds.splice(destinationIndex, 0, deletedCardId);
|
||||
|
||||
set(
|
||||
recordBoardCardIdsByColumnIdFamilyState(sourceColumn.id),
|
||||
sourceCardIds,
|
||||
);
|
||||
} else {
|
||||
const [removedCardId] = sourceCardIds.splice(source.index, 1);
|
||||
|
||||
destinationCardIds.splice(destinationIndex, 0, removedCardId);
|
||||
|
||||
set(
|
||||
recordBoardCardIdsByColumnIdFamilyState(sourceColumn.id),
|
||||
sourceCardIds,
|
||||
);
|
||||
|
||||
set(
|
||||
recordBoardCardIdsByColumnIdFamilyState(destinationColumn.id),
|
||||
destinationCardIds,
|
||||
);
|
||||
}
|
||||
|
||||
return newBoardColumns;
|
||||
},
|
||||
[boardColumnsState],
|
||||
);
|
||||
};
|
||||
@ -1,9 +1,6 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
||||
import { isFetchingMoreRecordsFamilyState } from '@/object-record/states/isFetchingMoreRecordsFamilyState';
|
||||
import {
|
||||
RecordTableRow,
|
||||
@ -11,33 +8,34 @@ import {
|
||||
} from '@/ui/object/record-table/components/RecordTableRow';
|
||||
import { RowIdContext } from '@/ui/object/record-table/contexts/RowIdContext';
|
||||
import { RowIndexContext } from '@/ui/object/record-table/contexts/RowIndexContext';
|
||||
import { useRecordTableScopedStates } from '@/ui/object/record-table/hooks/internal/useRecordTableScopedStates';
|
||||
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
|
||||
import { isFetchingRecordTableDataState } from '@/ui/object/record-table/states/isFetchingRecordTableDataState';
|
||||
|
||||
import { useRecordTable } from '../hooks/useRecordTable';
|
||||
import { tableRowIdsState } from '../states/tableRowIdsState';
|
||||
import { tableRowIdsState } from '@/ui/object/record-table/states/tableRowIdsState';
|
||||
import { getRecordTableScopedStates } from '@/ui/object/record-table/utils/getRecordTableScopedStates';
|
||||
|
||||
export const RecordTableBody = () => {
|
||||
const { ref: lastTableRowRef, inView: lastTableRowIsVisible } = useInView();
|
||||
const { scopeId } = useRecordTable();
|
||||
|
||||
const onLastRowVisible = useRecoilCallback(
|
||||
({ set }) =>
|
||||
async (inView: boolean) => {
|
||||
const { tableLastRowVisibleState } = getRecordTableScopedStates({
|
||||
recordTableScopeId: scopeId,
|
||||
});
|
||||
|
||||
set(tableLastRowVisibleState, inView);
|
||||
},
|
||||
[scopeId],
|
||||
);
|
||||
|
||||
const { ref: lastTableRowRef } = useInView({
|
||||
onChange: onLastRowVisible,
|
||||
});
|
||||
|
||||
const tableRowIds = useRecoilValue(tableRowIdsState);
|
||||
|
||||
const { scopeId: objectNamePlural } = useRecordTable();
|
||||
const { tableLastRowVisibleState } = useRecordTableScopedStates();
|
||||
const setTableLastRowVisible = useSetRecoilState(tableLastRowVisibleState);
|
||||
|
||||
const { objectNameSingular } = useObjectNameSingularFromPlural({
|
||||
objectNamePlural,
|
||||
});
|
||||
|
||||
const { objectMetadataItem: foundObjectMetadataItem } = useObjectMetadataItem(
|
||||
{
|
||||
objectNameSingular,
|
||||
},
|
||||
);
|
||||
|
||||
const [isFetchingMoreObjects] = useRecoilState(
|
||||
isFetchingMoreRecordsFamilyState(foundObjectMetadataItem?.namePlural),
|
||||
isFetchingMoreRecordsFamilyState(scopeId),
|
||||
);
|
||||
|
||||
const isFetchingRecordTableData = useRecoilValue(
|
||||
@ -45,10 +43,6 @@ export const RecordTableBody = () => {
|
||||
);
|
||||
const lastRowId = tableRowIds[tableRowIds.length - 1];
|
||||
|
||||
useEffect(() => {
|
||||
setTableLastRowVisible(lastTableRowIsVisible);
|
||||
}, [lastTableRowIsVisible, setTableLastRowVisible]);
|
||||
|
||||
if (isFetchingRecordTableData) {
|
||||
return <></>;
|
||||
}
|
||||
@ -60,7 +54,11 @@ export const RecordTableBody = () => {
|
||||
<RowIndexContext.Provider value={rowIndex}>
|
||||
<RecordTableRow
|
||||
key={rowId}
|
||||
ref={rowId === lastRowId ? lastTableRowRef : undefined}
|
||||
ref={
|
||||
rowId === lastRowId && rowIndex > 30
|
||||
? lastTableRowRef
|
||||
: undefined
|
||||
}
|
||||
rowId={rowId}
|
||||
/>
|
||||
</RowIndexContext.Provider>
|
||||
|
||||
@ -6,10 +6,18 @@ import { useRecordTableScopedStates } from '@/ui/object/record-table/hooks/inter
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
export const RecordTableBodyEffect = () => {
|
||||
const { fetchMoreRecords: fetchMoreObjects } = useObjectRecordTable();
|
||||
const {
|
||||
fetchMoreRecords: fetchMoreObjects,
|
||||
records,
|
||||
setRecordTableData,
|
||||
} = useObjectRecordTable();
|
||||
const { tableLastRowVisibleState } = useRecordTableScopedStates();
|
||||
const tableLastRowVisible = useRecoilValue(tableLastRowVisibleState);
|
||||
|
||||
useEffect(() => {
|
||||
setRecordTableData(records);
|
||||
}, [records, setRecordTableData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (tableLastRowVisible && isDefined(fetchMoreObjects)) {
|
||||
fetchMoreObjects();
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { entityFieldsFamilyState } from '@/ui/object/field/states/entityFieldsFamilyState';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
|
||||
import { isFetchingRecordTableDataState } from '../../states/isFetchingRecordTableDataState';
|
||||
import { numberOfTableRowsState } from '../../states/numberOfTableRowsState';
|
||||
@ -29,16 +30,13 @@ export const useSetRecordTableData = ({
|
||||
set(entityFieldsFamilyState(entity.id), entity);
|
||||
}
|
||||
}
|
||||
const currentRowIds = snapshot.getLoadable(tableRowIdsState).getValue();
|
||||
|
||||
const entityIds = newEntityArray.map((entity) => entity.id);
|
||||
|
||||
set(tableRowIdsState, (currentRowIds) => {
|
||||
if (JSON.stringify(currentRowIds) !== JSON.stringify(entityIds)) {
|
||||
return entityIds;
|
||||
}
|
||||
|
||||
return currentRowIds;
|
||||
});
|
||||
if (!isDeeplyEqual(currentRowIds, entityIds)) {
|
||||
set(tableRowIdsState, entityIds);
|
||||
}
|
||||
|
||||
resetTableRowSelection();
|
||||
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { tableRowIdsState } from '../states/tableRowIdsState';
|
||||
|
||||
// Used only in company table and people table
|
||||
// Remove after refactoring
|
||||
|
||||
export const useUpsertTableRowId = () =>
|
||||
useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
(rowId: string) => {
|
||||
const currentRowIds = snapshot
|
||||
.getLoadable(tableRowIdsState)
|
||||
.valueOrThrow();
|
||||
|
||||
set(tableRowIdsState, Array.from(new Set([rowId, ...currentRowIds])));
|
||||
},
|
||||
[],
|
||||
);
|
||||
Reference in New Issue
Block a user