Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

☑️ Refacto "Select All/Unselect all" on indexes #5320

Merged
merged 49 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
0655efd
☑️ Refacto "Select All/Unselect all" on indexes
gitstart-twenty May 7, 2024
e056193
Merge commit '005045c596d8b080539ec180c83aa75d301bf770' of https://gi…
gitstart-twenty May 8, 2024
40faff3
Fix jest
gitstart-twenty May 8, 2024
1cf0db6
Refactor according to self review
gitstart-twenty May 8, 2024
d6f0675
Merge commit 'fdf10f17e2fa91da60336140fb859ae56e8acfb9' of https://gi…
gitstart-twenty May 16, 2024
29dd4f6
Refactor according to review
gitstart-twenty May 16, 2024
7de431d
Merge main
gitstart-twenty May 23, 2024
c0ca3bd
Merge main
gitstart-twenty May 23, 2024
5a52f64
Merge main
gitstart-twenty May 23, 2024
bd8c53e
Merge main
gitstart-twenty Jun 11, 2024
fb13ca2
Merge branch 'main' into TWNTY-4397
FelixMalfait Jun 14, 2024
7acce56
Merge branch 'main' into TWNTY-4397
lucasbordeau Jun 17, 2024
2477b14
Fixed typing problem
lucasbordeau Jun 17, 2024
856fa81
Fix
lucasbordeau Jun 17, 2024
cb51287
Merge branch 'main' into TWNTY-4397
lucasbordeau Jun 19, 2024
9d7d570
Change download predicate
gitstart-twenty Jun 21, 2024
de563bf
Merge main
gitstart-twenty Jun 21, 2024
52a64c7
Merge commit 'd126b148a19bc32a7cb52e7ed04d2470bf8be6b5' of https://gi…
gitstart-twenty Jun 21, 2024
a43bf6b
Fix Select All checkbox
gitstart-twenty Jun 21, 2024
dfe033d
Merge branch 'main' into TWNTY-4397
lucasbordeau Jun 24, 2024
fe9c465
Fixed bug
lucasbordeau Jun 24, 2024
72a4e70
Refactor according to review
gitstart-twenty Jun 28, 2024
922942d
Revert temporary changes
gitstart-twenty Jun 28, 2024
db93173
Fix linter issues
gitstart-twenty Jun 28, 2024
72aa58c
Fix records count
gitstart-twenty Jun 28, 2024
cf1ac0e
Fix linter
gitstart-twenty Jun 28, 2024
079a412
Merge branch 'main' into TWNTY-4397
lucasbordeau Jul 1, 2024
d91482d
Merge commit '5df0ea6466be885ce85dcb2bb849b8493698ce52' of https://gi…
gitstart-twenty Jul 4, 2024
d5011b3
Fix click outside table
gitstart-twenty Jul 4, 2024
e1343a3
Merge branch 'main' into TWNTY-4397
lucasbordeau Jul 5, 2024
7c7edaa
WIP
lucasbordeau Jul 5, 2024
0c76416
Fix
lucasbordeau Jul 5, 2024
4cefd9c
Fix
lucasbordeau Jul 5, 2024
1bb1d27
removed refetch queries
lucasbordeau Jul 5, 2024
968046d
WIP
lucasbordeau Jul 9, 2024
d5f863a
WIP
lucasbordeau Jul 9, 2024
6e6c7f6
WIP
lucasbordeau Jul 9, 2024
ec1b8be
Merge branch 'main' into TWNTY-4397
lucasbordeau Jul 9, 2024
b676c8b
WIP
lucasbordeau Jul 9, 2024
1ae7a4f
Fixed bug
lucasbordeau Jul 9, 2024
5cf6444
Cleaned
lucasbordeau Jul 9, 2024
c04d308
Fixed favorites
lucasbordeau Jul 9, 2024
e4bd470
Naming fix
lucasbordeau Jul 9, 2024
9714cd6
Fixed typo
lucasbordeau Jul 9, 2024
1156e08
Fxed naming
lucasbordeau Jul 9, 2024
8ffbbdf
Fix
lucasbordeau Jul 9, 2024
0cc7e64
Fixed test
lucasbordeau Jul 9, 2024
2521676
Fixed coverage with test on new useFetchAllRecordIds and other utils
lucasbordeau Jul 10, 2024
80621c6
Fix lint
lucasbordeau Jul 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ describe('useDeleteManyRecords', () => {
await act(async () => {
const res = await result.current.deleteManyRecords(people);
expect(res).toBeDefined();
expect(res).toHaveProperty('id');
expect(res[0]).toHaveProperty('id');
});

expect(mocks[0].result).toHaveBeenCalled();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { useApolloClient } from '@apollo/client';
import {
FetchResult,
InternalRefetchQueriesInclude,
useApolloClient,
} from '@apollo/client';
import { v4 } from 'uuid';

import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
Expand All @@ -18,6 +22,10 @@ type useCreateManyRecordsProps = {
objectNameSingular: string;
recordGqlFields?: RecordGqlOperationGqlRecordFields;
skipPostOptmisticEffect?: boolean;
refetchQueries?:
| InternalRefetchQueriesInclude
| ((result: FetchResult<any>) => InternalRefetchQueriesInclude)
| undefined;
};

export const useCreateManyRecords = <
Expand All @@ -26,6 +34,7 @@ export const useCreateManyRecords = <
objectNameSingular,
recordGqlFields,
skipPostOptmisticEffect = false,
refetchQueries,
}: useCreateManyRecordsProps) => {
const apolloClient = useApolloClient();

Expand Down Expand Up @@ -107,6 +116,7 @@ export const useCreateManyRecords = <
objectMetadataItems,
});
},
refetchQueries,
});

return createdObjects.data?.[mutationResponseField] ?? [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ type DeleteManyRecordsOptions = {
skipOptimisticEffect?: boolean;
};

const chunkArray = <T>(array: T[], chunkSize: number): T[][] =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extract in a util

array.reduce<T[][]>((acc, item, index) => {
const chunkIndex = Math.floor(index / chunkSize);
if (!acc[chunkIndex]) {
acc[chunkIndex] = [];
}
acc[chunkIndex].push(item);
return acc;
}, []);

export const useDeleteManyRecords = ({
objectNameSingular,
}: useDeleteOneRecordProps) => {
Expand All @@ -41,7 +51,7 @@ export const useDeleteManyRecords = ({
objectMetadataItem.namePlural,
);

const deleteManyRecords = async (
const deleteRecordsWithIds = async (
idsToDelete: string[],
options?: DeleteManyRecordsOptions,
) => {
Expand Down Expand Up @@ -81,5 +91,16 @@ export const useDeleteManyRecords = ({
return deletedRecords.data?.[mutationResponseField] ?? null;
};

const deleteManyRecords = async (
idsToDelete: string[],
options?: DeleteManyRecordsOptions,
chunkSize = 30,
) => {
const chunkedIds = chunkArray(idsToDelete, chunkSize);
return Promise.all(
chunkedIds.map((ids) => deleteRecordsWithIds(ids, options)),
);
};

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're now deleting records in chunks of 30

return { deleteManyRecords };
};
Original file line number Diff line number Diff line change
@@ -1,43 +1,23 @@
import { useCallback, useMemo } from 'react';
import { useQuery, WatchQueryFetchPolicy } from '@apollo/client';
import { isNonEmptyArray } from '@apollo/client/utilities';
import { isNonEmptyString } from '@sniptt/guards';
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
import { useRecoilValue } 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';
import { RecordGqlOperationFindManyResult } from '@/object-record/graphql/types/RecordGqlOperationFindManyResult';
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
import { RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables';
import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { filterUniqueRecordEdgesByCursor } from '@/object-record/utils/filterUniqueRecordEdgesByCursor';
import { useFindManyRecordsState } from '@/object-record/utils/useFindManyRecordsUtils';
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { isDefined } from '~/utils/isDefined';
import { logError } from '~/utils/logError';
import { capitalize } from '~/utils/string/capitalize';

import { cursorFamilyState } from '../states/cursorFamilyState';
import { hasNextPageFamilyState } from '../states/hasNextPageFamilyState';
import { isFetchingMoreRecordsFamilyState } from '../states/isFetchingMoreRecordsFamilyState';

export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
objectNameSingular,
filter,
orderBy,
limit,
onCompleted,
onError,
skip,
recordGqlFields,
fetchPolicy,
}: ObjectMetadataItemIdentifier &
export type UseFindManyRecordsParams<T> = ObjectMetadataItemIdentifier &
RecordGqlOperationVariables & {
onCompleted?: (
records: T[],
Expand All @@ -50,37 +30,29 @@ export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
skip?: boolean;
recordGqlFields?: RecordGqlOperationGqlRecordFields;
fetchPolicy?: WatchQueryFetchPolicy;
}) => {
const findManyQueryStateIdentifier =
objectNameSingular +
JSON.stringify(filter) +
JSON.stringify(orderBy) +
limit;

const [lastCursor, setLastCursor] = useRecoilState(
cursorFamilyState(findManyQueryStateIdentifier),
);

const [hasNextPage, setHasNextPage] = useRecoilState(
hasNextPageFamilyState(findManyQueryStateIdentifier),
);

const setIsFetchingMoreObjects = useSetRecoilState(
isFetchingMoreRecordsFamilyState(findManyQueryStateIdentifier),
);
};

export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
objectNameSingular,
filter,
orderBy,
limit,
onCompleted,
skip,
recordGqlFields,
fetchPolicy,
onError,
}: UseFindManyRecordsParams<T>) => {
const { enqueueSnackBar } = useSnackBar();
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
});

const { findManyRecordsQuery } = useFindManyRecordsQuery({
objectNameSingular,
recordGqlFields,
});

const { enqueueSnackBar } = useSnackBar();
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);

const { data, loading, error, fetchMore } =
useQuery<RecordGqlOperationFindManyResult>(findManyRecordsQuery, {
skip: skip || !objectMetadataItem || !currentWorkspaceMember,
Expand Down Expand Up @@ -126,111 +98,25 @@ export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
},
});
lucasbordeau marked this conversation as resolved.
Show resolved Hide resolved

const fetchMoreRecords = useCallback(async () => {
// Remote objects does not support hasNextPage. We cannot rely on it to fetch more records.
if (hasNextPage || (!isAggregationEnabled(objectMetadataItem) && !error)) {
setIsFetchingMoreObjects(true);

try {
await fetchMore({
variables: {
filter,
orderBy,
lastCursor: isNonEmptyString(lastCursor) ? lastCursor : undefined,
},
updateQuery: (prev, { fetchMoreResult }) => {
const previousEdges = prev?.[objectMetadataItem.namePlural]?.edges;
const nextEdges =
fetchMoreResult?.[objectMetadataItem.namePlural]?.edges;

let newEdges: RecordGqlEdge[] = previousEdges ?? [];

if (isNonEmptyArray(nextEdges)) {
newEdges = filterUniqueRecordEdgesByCursor([
...newEdges,
...(fetchMoreResult?.[objectMetadataItem.namePlural]?.edges ??
[]),
]);
}

const pageInfo =
fetchMoreResult?.[objectMetadataItem.namePlural]?.pageInfo;

if (isDefined(data?.[objectMetadataItem.namePlural])) {
setLastCursor(pageInfo.endCursor ?? '');
setHasNextPage(pageInfo.hasNextPage ?? false);
}

const records = getRecordsFromRecordConnection({
recordConnection: {
edges: newEdges,
pageInfo,
},
}) as T[];

onCompleted?.(records, {
pageInfo,
totalCount:
fetchMoreResult?.[objectMetadataItem.namePlural]?.totalCount,
});

return Object.assign({}, prev, {
[objectMetadataItem.namePlural]: {
__typename: `${capitalize(
objectMetadataItem.nameSingular,
)}Connection`,
edges: newEdges,
pageInfo:
fetchMoreResult?.[objectMetadataItem.namePlural].pageInfo,
totalCount:
fetchMoreResult?.[objectMetadataItem.namePlural].totalCount,
},
} as RecordGqlOperationFindManyResult);
},
});
} catch (error) {
logError(
`fetchMoreObjects for "${objectMetadataItem.namePlural}" error : ` +
error,
);
enqueueSnackBar(
`Error during fetchMoreObjects for "${objectMetadataItem.namePlural}", ${error}`,
{
variant: SnackBarVariant.Error,
},
);
} finally {
setIsFetchingMoreObjects(false);
}
}
}, [
const {
findManyQueryStateIdentifier,
setLastCursor,
setHasNextPage,
fetchMoreRecords,
totalCount,
records,
hasNextPage,
objectMetadataItem,
error,
setIsFetchingMoreObjects,
fetchMore,
} = useFindManyRecordsState<T>({
objectNameSingular,
filter,
orderBy,
lastCursor,
data,
limit,
onCompleted,
setLastCursor,
setHasNextPage,
enqueueSnackBar,
]);

const totalCount = data?.[objectMetadataItem.namePlural]?.totalCount;

const records = useMemo(
() =>
data?.[objectMetadataItem.namePlural]
? getRecordsFromRecordConnection<T>({
recordConnection: data?.[objectMetadataItem.namePlural],
})
: ([] as T[]),

[data, objectMetadataItem.namePlural],
);
fetchMore,
data,
error,
objectMetadataItem,
});

return {
objectMetadataItem,
Expand Down
Loading
Loading