Sync stripe tables (#5475)

Stripe tables do not support `hasNextPage` and `totalCount`. This may be
because of stripe wrapper do not properly support `COUNT` request.
Waiting on pg_graphql answer
[here](https://github.com/supabase/pg_graphql/issues/519).

This PR:
- removes `totalCount` and `hasNextPage` form queries for remote
objects. Even if it works for postgres, this may really be inefficient
- adapt the `fetchMore` functions so it works despite `hasNextPage`
missing
- remove `totalCount` display for remotes
- fix `orderBy`

---------

Co-authored-by: Thomas Trompette <thomast@twenty.com>
This commit is contained in:
Thomas Trompette
2024-05-22 11:20:44 +02:00
committed by GitHub
parent 35c1f97511
commit 2e79bcc70b
22 changed files with 191 additions and 87 deletions

View File

@ -7,6 +7,7 @@ import { RecordGqlRefEdge } from '@/object-record/cache/types/RecordGqlRefEdge';
import { getEdgeTypename } from '@/object-record/cache/utils/getEdgeTypename';
import { isObjectRecordConnectionWithRefs } from '@/object-record/cache/utils/isObjectRecordConnectionWithRefs';
import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
import { isDefined } from '~/utils/isDefined';
/*
TODO: for now new records are added to all cached record lists, no matter what the variables (filters, orderBy, etc.) are.
@ -61,11 +62,10 @@ export const triggerCreateRecordsOptimisticEffect = ({
rootQueryCachedObjectRecordConnection,
);
const rootQueryCachedRecordTotalCount =
readField<number>(
'totalCount',
rootQueryCachedObjectRecordConnection,
) || 0;
const rootQueryCachedRecordTotalCount = readField<number | undefined>(
'totalCount',
rootQueryCachedObjectRecordConnection,
);
const nextRootQueryCachedRecordEdges = rootQueryCachedRecordEdges
? [...rootQueryCachedRecordEdges]
@ -113,7 +113,9 @@ export const triggerCreateRecordsOptimisticEffect = ({
return {
...rootQueryCachedObjectRecordConnection,
edges: nextRootQueryCachedRecordEdges,
totalCount: rootQueryCachedRecordTotalCount + 1,
totalCount: isDefined(rootQueryCachedRecordTotalCount)
? rootQueryCachedRecordTotalCount + 1
: undefined,
};
},
},

View File

@ -50,11 +50,10 @@ export const triggerDeleteRecordsOptimisticEffect = ({
rootQueryCachedObjectRecordConnection,
);
const totalCount =
readField<number>(
'totalCount',
rootQueryCachedObjectRecordConnection,
) || 0;
const totalCount = readField<number | undefined>(
'totalCount',
rootQueryCachedObjectRecordConnection,
);
const nextCachedEdges =
cachedEdges?.filter((cachedEdge) => {
@ -77,7 +76,9 @@ export const triggerDeleteRecordsOptimisticEffect = ({
return {
...rootQueryCachedObjectRecordConnection,
edges: nextCachedEdges,
totalCount: totalCount - recordIdsToDelete.length,
totalCount: isDefined(totalCount)
? totalCount - recordIdsToDelete.length
: undefined,
};
},
},

View File

@ -0,0 +1,4 @@
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
export const hasPositionField = (objectMetadataItem: ObjectMetadataItem) =>
!objectMetadataItem.isRemote;

View File

@ -0,0 +1,4 @@
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
export const isAggregationEnabled = (objectMetadataItem: ObjectMetadataItem) =>
!objectMetadataItem.isRemote;

View File

@ -76,7 +76,7 @@ export const useFindDuplicateRecords = <T extends ObjectRecord = ObjectRecord>({
return {
objectMetadataItem,
records,
totalCount: objectRecordConnection?.totalCount || 0,
totalCount: objectRecordConnection?.totalCount,
loading,
error,
queryStateIdentifier: findDuplicateQueryStateIdentifier,

View File

@ -3,6 +3,7 @@ import { useRecoilValue } from 'recoil';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { isAggregationEnabled } from '@/object-metadata/utils/isAggregationEnabled';
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
import { getFindDuplicateRecordsQueryResponseField } from '@/object-record/utils/getFindDuplicateRecordsQueryResponseField';
import { capitalize } from '~/utils/string/capitalize';
@ -33,11 +34,11 @@ export const useFindDuplicateRecordsQuery = ({
cursor
}
pageInfo {
hasNextPage
${isAggregationEnabled(objectMetadataItem) ? 'hasNextPage' : ''}
startCursor
endCursor
}
totalCount
${isAggregationEnabled(objectMetadataItem) ? 'totalCount' : ''}
}
}
`;

View File

@ -7,6 +7,7 @@ import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
import { isAggregationEnabled } from '@/object-metadata/utils/isAggregationEnabled';
import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection';
import { RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
import { RecordGqlEdge } from '@/object-record/graphql/types/RecordGqlEdge';
@ -122,7 +123,8 @@ export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
});
const fetchMoreRecords = useCallback(async () => {
if (hasNextPage) {
// Remote objects does not support hasNextPage. We cannot rely on it to fetch more records.
if (hasNextPage || (!isAggregationEnabled(objectMetadataItem) && !error)) {
setIsFetchingMoreObjects(true);
try {
@ -137,11 +139,11 @@ export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
const nextEdges =
fetchMoreResult?.[objectMetadataItem.namePlural]?.edges;
let newEdges: RecordGqlEdge[] = [];
let newEdges: RecordGqlEdge[] = previousEdges ?? [];
if (isNonEmptyArray(previousEdges) && isNonEmptyArray(nextEdges)) {
if (isNonEmptyArray(nextEdges)) {
newEdges = filterUniqueRecordEdgesByCursor([
...(prev?.[objectMetadataItem.namePlural]?.edges ?? []),
...newEdges,
...(fetchMoreResult?.[objectMetadataItem.namePlural]?.edges ??
[]),
]);
@ -199,21 +201,21 @@ export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
}
}, [
hasNextPage,
objectMetadataItem,
error,
setIsFetchingMoreObjects,
fetchMore,
filter,
orderBy,
lastCursor,
objectMetadataItem.namePlural,
objectMetadataItem.nameSingular,
onCompleted,
data,
onCompleted,
setLastCursor,
setHasNextPage,
enqueueSnackBar,
]);
const totalCount = data?.[objectMetadataItem.namePlural].totalCount ?? 0;
const totalCount = data?.[objectMetadataItem.namePlural]?.totalCount;
const records = useMemo(
() =>

View File

@ -1,3 +1,5 @@
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { Sort } from '@/object-record/object-sort-dropdown/types/Sort';
import { SortDefinition } from '@/object-record/object-sort-dropdown/types/SortDefinition';
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
@ -8,12 +10,30 @@ const sortDefinition: SortDefinition = {
iconName: 'icon',
};
const objectMetadataItem: ObjectMetadataItem = {
id: 'object1',
fields: [],
createdAt: '2021-01-01',
updatedAt: '2021-01-01',
nameSingular: 'object1',
namePlural: 'object1s',
icon: 'icon',
isActive: true,
isSystem: false,
isCustom: false,
isRemote: false,
labelPlural: 'object1s',
labelSingular: 'object1',
};
describe('turnSortsIntoOrderBy', () => {
it('should sort by recordPosition if no sorts', () => {
const fields = [{ id: 'field1', name: 'createdAt' }];
expect(turnSortsIntoOrderBy([], fields)).toEqual({
position: 'AscNullsFirst',
});
const fields = [{ id: 'field1', name: 'createdAt' }] as FieldMetadataItem[];
expect(turnSortsIntoOrderBy({ ...objectMetadataItem, fields }, [])).toEqual(
{
position: 'AscNullsFirst',
},
);
});
it('should create OrderByField with single sort', () => {
@ -24,8 +44,10 @@ describe('turnSortsIntoOrderBy', () => {
definition: sortDefinition,
},
];
const fields = [{ id: 'field1', name: 'field1' }];
expect(turnSortsIntoOrderBy(sorts, fields)).toEqual({
const fields = [{ id: 'field1', name: 'field1' }] as FieldMetadataItem[];
expect(
turnSortsIntoOrderBy({ ...objectMetadataItem, fields }, sorts),
).toEqual({
field1: 'AscNullsFirst',
position: 'AscNullsFirst',
});
@ -47,8 +69,10 @@ describe('turnSortsIntoOrderBy', () => {
const fields = [
{ id: 'field1', name: 'field1' },
{ id: 'field2', name: 'field2' },
];
expect(turnSortsIntoOrderBy(sorts, fields)).toEqual({
] as FieldMetadataItem[];
expect(
turnSortsIntoOrderBy({ ...objectMetadataItem, fields }, sorts),
).toEqual({
field1: 'AscNullsFirst',
field2: 'DescNullsLast',
position: 'AscNullsFirst',
@ -63,8 +87,21 @@ describe('turnSortsIntoOrderBy', () => {
definition: sortDefinition,
},
];
expect(turnSortsIntoOrderBy(sorts, [])).toEqual({
expect(turnSortsIntoOrderBy(objectMetadataItem, sorts)).toEqual({
position: 'AscNullsFirst',
});
});
it('should not return position for remotes', () => {
const sorts: Sort[] = [
{
fieldMetadataId: 'invalidField',
direction: 'asc',
definition: sortDefinition,
},
];
expect(
turnSortsIntoOrderBy({ ...objectMetadataItem, isRemote: true }, sorts),
).toEqual({});
});
});

View File

@ -1,4 +1,6 @@
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { OrderBy } from '@/object-metadata/types/OrderBy';
import { hasPositionField } from '@/object-metadata/utils/hasPositionColumn';
import { RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy';
import { Field } from '~/generated/graphql';
import { mapArrayToObject } from '~/utils/array/mapArrayToObject';
@ -8,9 +10,10 @@ import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
import { Sort } from '../types/Sort';
export const turnSortsIntoOrderBy = (
objectMetadataItem: ObjectMetadataItem,
sorts: Sort[],
fields: Pick<Field, 'id' | 'name'>[],
): RecordGqlOperationOrderBy => {
const fields: Pick<Field, 'id' | 'name'>[] = objectMetadataItem?.fields ?? [];
const fieldsById = mapArrayToObject(fields, ({ id }) => id);
const sortsOrderBy = Object.fromEntries(
sorts
@ -29,8 +32,12 @@ export const turnSortsIntoOrderBy = (
.filter(isDefined),
);
return {
...sortsOrderBy,
position: 'AscNullsFirst',
};
if (hasPositionField(objectMetadataItem)) {
return {
...sortsOrderBy,
position: 'AscNullsFirst',
};
}
return sortsOrderBy;
};

View File

@ -15,7 +15,10 @@ import { useFavorites } from '@/favorites/hooks/useFavorites';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords';
import { useExecuteQuickActionOnOneRecord } from '@/object-record/hooks/useExecuteQuickActionOnOneRecord';
import { useExportTableData } from '@/object-record/record-index/options/hooks/useExportTableData';
import {
displayedExportProgress,
useExportTableData,
} from '@/object-record/record-index/options/hooks/useExportTableData';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
import { actionBarEntriesState } from '@/ui/navigation/action-bar/states/actionBarEntriesState';
@ -111,7 +114,7 @@ export const useRecordActionBar = ({
const baseActions: ContextMenuEntry[] = useMemo(
() => [
{
label: `${progress === undefined ? `Export` : `Export (${progress}%)`}`,
label: displayedExportProgress(progress),
Icon: IconFileExport,
accent: 'default',
onClick: () => download(),

View File

@ -48,9 +48,7 @@ export const useLoadRecordIndexBoard = ({
recordIndexFilters,
objectMetadataItem?.fields ?? [],
);
const orderBy = !objectMetadataItem.isRemote
? turnSortsIntoOrderBy(recordIndexSorts, objectMetadataItem?.fields ?? [])
: undefined;
const orderBy = turnSortsIntoOrderBy(objectMetadataItem, recordIndexSorts);
const recordIndexIsCompactModeActive = useRecoilValue(
recordIndexIsCompactModeActiveState,

View File

@ -29,14 +29,7 @@ export const useFindManyParams = (
objectMetadataItem?.fields ?? [],
);
if (objectMetadataItem?.isRemote) {
return { objectNameSingular, filter };
}
const orderBy = turnSortsIntoOrderBy(
tableSorts,
objectMetadataItem?.fields ?? [],
);
const orderBy = turnSortsIntoOrderBy(objectMetadataItem, tableSorts);
return { objectNameSingular, filter, orderBy };
};
@ -71,7 +64,7 @@ export const useLoadRecordIndexTable = (objectNameSingular: string) => {
currentWorkspace?.activationStatus === 'active'
? records
: SIGN_IN_BACKGROUND_MOCK_COMPANIES,
totalCount: totalCount || 0,
totalCount: totalCount,
loading,
fetchMoreRecords,
queryStateIdentifier,

View File

@ -9,7 +9,10 @@ import {
} from 'twenty-ui';
import { RECORD_INDEX_OPTIONS_DROPDOWN_ID } from '@/object-record/record-index/options/constants/RecordIndexOptionsDropdownId';
import { useExportTableData } from '@/object-record/record-index/options/hooks/useExportTableData';
import {
displayedExportProgress,
useExportTableData,
} from '@/object-record/record-index/options/hooks/useExportTableData';
import { useRecordIndexOptionsForBoard } from '@/object-record/record-index/options/hooks/useRecordIndexOptionsForBoard';
import { useRecordIndexOptionsForTable } from '@/object-record/record-index/options/hooks/useRecordIndexOptionsForTable';
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
@ -123,7 +126,7 @@ export const RecordIndexOptionsDropdownContent = ({
<MenuItem
onClick={download}
LeftIcon={IconFileExport}
text={progress === undefined ? `Export` : `Export (${progress}%)`}
text={displayedExportProgress(progress)}
/>
</DropdownMenuItemsContainer>
)}

View File

@ -3,9 +3,9 @@ import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefin
import {
csvDownloader,
displayedExportProgress,
download,
generateCsv,
percentage,
sleep,
} from '../useExportTableData';
@ -92,17 +92,24 @@ describe('csvDownloader', () => {
});
});
describe('percentage', () => {
describe('displayedExportProgress', () => {
it.each([
[20, 50, 40],
[0, 100, 0],
[10, 10, 100],
[10, 10, 100],
[7, 9, 78],
[undefined, undefined, 'percentage', 'Export'],
[20, 50, 'percentage', 'Export (40%)'],
[0, 100, 'number', 'Export (0)'],
[10, 10, 'percentage', 'Export (100%)'],
[10, 10, 'number', 'Export (10)'],
[7, 9, 'percentage', 'Export (78%)'],
])(
'calculates the percentage %p/%p = %p',
(part, whole, expectedPercentage) => {
expect(percentage(part, whole)).toEqual(expectedPercentage);
'displays the export progress',
(exportedRecordCount, totalRecordCount, displayType, expected) => {
expect(
displayedExportProgress({
exportedRecordCount,
totalRecordCount,
displayType: displayType as 'percentage' | 'number',
}),
).toEqual(expected);
},
);
});

View File

@ -7,6 +7,7 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
import { isDefined } from '~/utils/isDefined';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
import { useFindManyParams } from '../../hooks/useLoadRecordIndexTable';
@ -30,6 +31,12 @@ type GenerateExportOptions = {
type GenerateExport = (data: GenerateExportOptions) => string;
type ExportProgress = {
exportedRecordCount?: number;
totalRecordCount?: number;
displayType: 'percentage' | 'number';
};
export const generateCsv: GenerateExport = ({
columns,
rows,
@ -77,10 +84,28 @@ export const generateCsv: GenerateExport = ({
});
};
export const percentage = (part: number, whole: number): number => {
const percentage = (part: number, whole: number): number => {
return Math.round((part / whole) * 100);
};
export const displayedExportProgress = (progress?: ExportProgress): string => {
if (isUndefinedOrNull(progress?.exportedRecordCount)) {
return 'Export';
}
if (
progress.displayType === 'percentage' &&
isDefined(progress?.totalRecordCount)
) {
return `Export (${percentage(
progress.exportedRecordCount,
progress.totalRecordCount,
)}%)`;
}
return `Export (${progress.exportedRecordCount})`;
};
const downloader = (mimeType: string, generator: GenerateExport) => {
return (filename: string, data: GenerateExportOptions) => {
const blob = new Blob([generator(data)], { type: mimeType });
@ -110,8 +135,10 @@ export const useExportTableData = ({
const [isDownloading, setIsDownloading] = useState(false);
const [inflight, setInflight] = useState(false);
const [pageCount, setPageCount] = useState(0);
const [progress, setProgress] = useState<number | undefined>(undefined);
const [hasNextPage, setHasNextPage] = useState(true);
const [progress, setProgress] = useState<ExportProgress>({
displayType: 'number',
});
const [previousRecordCount, setPreviousRecordCount] = useState(0);
const { visibleTableColumnsSelector, selectedRowIdsSelector } =
useRecordTableStates(recordIndexId);
@ -144,25 +171,31 @@ export const useExportTableData = ({
const { totalCount, records, fetchMoreRecords } = useFindManyRecords({
...usedFindManyParams,
limit: pageSize,
onCompleted: (_data, options) => {
setHasNextPage(options?.pageInfo?.hasNextPage ?? false);
},
});
useEffect(() => {
const MAXIMUM_REQUESTS = Math.min(maximumRequests, totalCount / pageSize);
const MAXIMUM_REQUESTS = isDefined(totalCount)
? Math.min(maximumRequests, totalCount / pageSize)
: maximumRequests;
const downloadCsv = (rows: object[]) => {
csvDownloader(filename, { rows, columns });
setIsDownloading(false);
setProgress(undefined);
setProgress({
displayType: 'number',
});
};
const fetchNextPage = async () => {
setInflight(true);
setPreviousRecordCount(records.length);
await fetchMoreRecords();
setPageCount((state) => state + 1);
setProgress(percentage(pageCount, MAXIMUM_REQUESTS));
setProgress({
exportedRecordCount: records.length,
totalRecordCount: totalCount,
displayType: totalCount ? 'percentage' : 'number',
});
await sleep(delayMs);
setInflight(false);
};
@ -171,7 +204,10 @@ export const useExportTableData = ({
return;
}
if (!hasNextPage || pageCount >= MAXIMUM_REQUESTS) {
if (
pageCount >= MAXIMUM_REQUESTS ||
records.length === previousRecordCount
) {
downloadCsv(records);
} else {
fetchNextPage();
@ -180,7 +216,6 @@ export const useExportTableData = ({
delayMs,
fetchMoreRecords,
filename,
hasNextPage,
inflight,
isDownloading,
pageCount,
@ -189,6 +224,7 @@ export const useExportTableData = ({
columns,
maximumRequests,
pageSize,
previousRecordCount,
]);
return { progress, download: () => setIsDownloading(true) };

View File

@ -8,7 +8,7 @@ import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
type useSetRecordTableDataProps = {
recordTableId?: string;
onEntityCountChange: (entityCount: number) => void;
onEntityCountChange: (entityCount?: number) => void;
};
export const useSetRecordTableData = ({
@ -24,7 +24,7 @@ export const useSetRecordTableData = ({
return useRecoilCallback(
({ set, snapshot }) =>
<T extends ObjectRecord>(newEntityArray: T[], totalCount: number) => {
<T extends ObjectRecord>(newEntityArray: T[], totalCount?: number) => {
for (const entity of newEntityArray) {
// TODO: refactor with scoped state later
const currentEntity = snapshot
@ -54,7 +54,7 @@ export const useSetRecordTableData = ({
}
}
set(numberOfTableRowsState, totalCount);
set(numberOfTableRowsState, totalCount ?? 0);
onEntityCountChange(totalCount);
},
[

View File

@ -99,7 +99,7 @@ export const useRecordTable = (props?: useRecordTableProps) => {
const onEntityCountChange = useRecoilCallback(
({ snapshot }) =>
(count: number) => {
(count?: number) => {
const onEntityCountChange = getSnapshotValue(
snapshot,
onEntityCountChangeState,

View File

@ -83,6 +83,7 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => {
snapshot,
numberOfTableRowsState,
);
const currentColumnNumber = softFocusPosition.column;
const currentRowNumber = softFocusPosition.row;

View File

@ -1,7 +1,7 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
export const onEntityCountChangeComponentState = createComponentState<
((entityCount: number) => void) | undefined
((entityCount?: number) => void) | undefined
>({
key: 'onEntityCountChangeComponentState',
defaultValue: undefined,

View File

@ -1,6 +1,7 @@
import gql from 'graphql-tag';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { isAggregationEnabled } from '@/object-metadata/utils/isAggregationEnabled';
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
import { capitalize } from '~/utils/string/capitalize';
@ -36,11 +37,11 @@ query FindMany${capitalize(
cursor
}
pageInfo {
hasNextPage
${isAggregationEnabled(objectMetadataItem) ? 'hasNextPage' : ''}
startCursor
endCursor
}
totalCount
${isAggregationEnabled(objectMetadataItem) ? 'totalCount' : ''}
}
}
`;

View File

@ -1,7 +1,8 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
export const entityCountInCurrentViewComponentState =
createComponentState<number>({
key: 'entityCountInCurrentViewComponentState',
defaultValue: 0,
});
export const entityCountInCurrentViewComponentState = createComponentState<
number | undefined
>({
key: 'entityCountInCurrentViewComponentState',
defaultValue: undefined,
});

View File

@ -15,6 +15,7 @@ import { ViewPickerListContent } from '@/views/view-picker/components/ViewPicker
import { VIEW_PICKER_DROPDOWN_ID } from '@/views/view-picker/constants/ViewPickerDropdownId';
import { useViewPickerMode } from '@/views/view-picker/hooks/useViewPickerMode';
import { useViewPickerPersistView } from '@/views/view-picker/hooks/useViewPickerPersistView';
import { isDefined } from '~/utils/isDefined';
import { useViewStates } from '../../hooks/internal/useViewStates';
@ -89,7 +90,9 @@ export const ViewPickerDropdown = () => {
{currentViewWithCombinedFiltersAndSorts?.name ?? 'All'}
</StyledViewName>
<StyledDropdownLabelAdornments>
· {entityCountInCurrentView}{' '}
{isDefined(entityCountInCurrentView) && (
<>· {entityCountInCurrentView} </>
)}
<IconChevronDown size={theme.icon.size.sm} />
</StyledDropdownLabelAdornments>
</StyledDropdownButtonContainer>