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

Refactored record filter saving to view filters #9844

Merged
merged 4 commits into from
Jan 24, 2025

Conversation

lucasbordeau
Copy link
Contributor

This PR refactors the record filter saving to view filters.

Before we used states to track the change of view filters, now we just check if there's a difference between the current record filters and the current view filters before saving.

We also use this check to show the reset and save buttons.

CRUD operations to perform on view filters are computed by utils , and .

Also added unit tests on those utils.

- Used this to create hooks to compare view filters and record filters
- Added getCurrentViewOnly() hook
- Created hook useSaveRecordFiltersToViewFilters
- Created util to get CRUD operations to perform on view filters from record filters
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

PR Summary

This PR refactors the view filter saving mechanism by replacing state-based tracking with direct comparisons between current record filters and view filters, introducing new utility functions and hooks for better modularity and maintainability.

Key changes:

  • Added useAreViewFiltersDifferentFromRecordFilters hook to efficiently compare current record filters with view filters
  • Introduced utility functions (getViewFiltersToCreate, getViewFiltersToDelete, getViewFiltersToUpdate) to handle CRUD operations based on filter differences
  • Implemented useSaveRecordFiltersToViewFilters hook to manage filter persistence using the new comparison-based approach
  • Added comprehensive unit tests for all new utility functions ensuring proper filter comparison logic
  • Simplified ViewBarDetails and UpdateViewButtonGroup components by removing state dependencies and using direct filter comparisons

17 file(s) reviewed, 15 comment(s)
Edit PR Review Bot Settings | Greptile

Comment on lines 175 to 179
const shouldExpandViewBar =
canPersistView ||
viewFiltersAreDifferentFromRecordFilters ||
((currentViewWithCombinedFiltersAndSorts?.viewSorts?.length ||
currentViewWithCombinedFiltersAndSorts?.viewFilters?.length) &&
currentRecordFilters?.length) &&
isViewBarExpanded);
Copy link
Contributor

Choose a reason for hiding this comment

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

style: shouldExpandViewBar logic could be simplified by combining conditions and using optional chaining

Comment on lines +8 to +11
const viewSortsAreDifferentFromRecordSorts = useRecoilComponentFamilyValueV2(
areViewSortsDifferentFromRecordSortsSelector,
{ viewId: currentView?.id },
);
Copy link
Contributor

Choose a reason for hiding this comment

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

style: hook will trigger recomputation on every currentView change, even if id hasn't changed. Consider memoizing the config object

Comment on lines +93 to +96
const canShowButton =
(viewFiltersAreDifferentFromRecordFilters ||
viewSortsAreDifferentFromRecordSorts) &&
!hasFiltersQueryParams;
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: Consider adding a check for currentViewId here - the button could show up even when there's no current view selected

Comment on lines 199 to 203
await saveViewFilterGroups(viewId);
await saveViewFilters(viewId);
// await saveViewFilters(viewId);
await saveViewSorts(viewId);

await saveRecordFiltersToViewFilters();
Copy link
Contributor

Choose a reason for hiding this comment

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

style: These operations are being executed sequentially but are independent. Consider using Promise.all() to run them concurrently for better performance:

Suggested change
await saveViewFilterGroups(viewId);
await saveViewFilters(viewId);
// await saveViewFilters(viewId);
await saveViewSorts(viewId);
await saveRecordFiltersToViewFilters();
await Promise.all([
saveViewFilterGroups(viewId),
saveViewSorts(viewId),
saveRecordFiltersToViewFilters()
]);

Comment on lines +203 to 205
await saveRecordFiltersToViewFilters();

resetUnsavedViewStates(viewId);
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: resetUnsavedViewStates is called after all save operations complete, but there's no error handling. If any save operation fails, the states may be incorrectly reset.

Comment on lines 7 to 14
const propertiesToCompare: (keyof ViewFilter)[] = [
'displayValue',
'fieldMetadataId',
'viewFilterGroupId',
'operand',
'positionInViewFilterGroup',
'value',
];
Copy link
Contributor

Choose a reason for hiding this comment

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

style: consider making this array constant and moving it outside the function to prevent recreation on each call

Comment on lines +6 to +15
const baseFilter: ViewFilter = {
__typename: 'ViewFilter',
id: 'filter-1',
fieldMetadataId: 'field-1',
operand: ViewFilterOperand.Contains,
value: 'test',
displayValue: 'test',
viewFilterGroupId: 'group-1',
positionInViewFilterGroup: 0,
};
Copy link
Contributor

Choose a reason for hiding this comment

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

style: baseFilter test fixture could be moved to a shared test utils file since it's likely used across multiple test files

Comment on lines +16 to +18
if (!isDefined(correspondingViewFilter)) {
return false;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: this early return should be removed - filters that don't exist in currentViewFilters should be created, not ignored

Comment on lines +9 to +14
return newViewFilters.filter((newViewFilter) => {
const correspondingViewFilter = currentViewFilters.find(
(currentViewFilter) =>
currentViewFilter.fieldMetadataId === newViewFilter.fieldMetadataId &&
currentViewFilter.viewFilterGroupId === newViewFilter.viewFilterGroupId,
);
Copy link
Contributor

Choose a reason for hiding this comment

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

style: consider using a Map for O(1) lookup instead of find() for better performance with large filter sets

Comment on lines +7 to +10
return {
__typename: 'ViewFilter',
...recordFilter,
} satisfies ViewFilter;
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Consider explicitly mapping each field instead of using spread operator to ensure type safety and catch breaking changes to either type.

@@ -89,7 +84,16 @@ export const UpdateViewButtonGroup = ({

const { hasFiltersQueryParams } = useViewFromQueryParams();

const canShowButton = canPersistView && !hasFiltersQueryParams;
const { viewFiltersAreDifferentFromRecordFilters } =
useAreViewFiltersDifferentFromRecordFilters();
Copy link
Member

Choose a reason for hiding this comment

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

nice!

Copy link
Member

Choose a reason for hiding this comment

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

idea: we could also justify returning a diff (could be more versatile in the usage) and the condition would be diff.length > 0

but happy with the current hook!

(!viewFilter.variant || viewFilter.variant === 'default') &&
!viewFilter.viewFilterGroupId,
);
const otherViewFilters = useMemo(() => {
Copy link
Member

Choose a reason for hiding this comment

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

neat: not a big fan of useMemo => I would rather investigate the re-renders root cause

@charlesBochet charlesBochet merged commit 570b2e3 into main Jan 24, 2025
47 checks passed
@charlesBochet charlesBochet deleted the refactor/record-filter-6 branch January 24, 2025 17:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants