-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
Conversation
- 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
There was a problem hiding this 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
andUpdateViewButtonGroup
components by removing state dependencies and using direct filter comparisons
17 file(s) reviewed, 15 comment(s)
Edit PR Review Bot Settings | Greptile
const shouldExpandViewBar = | ||
canPersistView || | ||
viewFiltersAreDifferentFromRecordFilters || | ||
((currentViewWithCombinedFiltersAndSorts?.viewSorts?.length || | ||
currentViewWithCombinedFiltersAndSorts?.viewFilters?.length) && | ||
currentRecordFilters?.length) && | ||
isViewBarExpanded); |
There was a problem hiding this comment.
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
const viewSortsAreDifferentFromRecordSorts = useRecoilComponentFamilyValueV2( | ||
areViewSortsDifferentFromRecordSortsSelector, | ||
{ viewId: currentView?.id }, | ||
); |
There was a problem hiding this comment.
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
const canShowButton = | ||
(viewFiltersAreDifferentFromRecordFilters || | ||
viewSortsAreDifferentFromRecordSorts) && | ||
!hasFiltersQueryParams; |
There was a problem hiding this comment.
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
await saveViewFilterGroups(viewId); | ||
await saveViewFilters(viewId); | ||
// await saveViewFilters(viewId); | ||
await saveViewSorts(viewId); | ||
|
||
await saveRecordFiltersToViewFilters(); |
There was a problem hiding this comment.
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:
await saveViewFilterGroups(viewId); | |
await saveViewFilters(viewId); | |
// await saveViewFilters(viewId); | |
await saveViewSorts(viewId); | |
await saveRecordFiltersToViewFilters(); | |
await Promise.all([ | |
saveViewFilterGroups(viewId), | |
saveViewSorts(viewId), | |
saveRecordFiltersToViewFilters() | |
]); |
await saveRecordFiltersToViewFilters(); | ||
|
||
resetUnsavedViewStates(viewId); |
There was a problem hiding this comment.
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.
const propertiesToCompare: (keyof ViewFilter)[] = [ | ||
'displayValue', | ||
'fieldMetadataId', | ||
'viewFilterGroupId', | ||
'operand', | ||
'positionInViewFilterGroup', | ||
'value', | ||
]; |
There was a problem hiding this comment.
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
const baseFilter: ViewFilter = { | ||
__typename: 'ViewFilter', | ||
id: 'filter-1', | ||
fieldMetadataId: 'field-1', | ||
operand: ViewFilterOperand.Contains, | ||
value: 'test', | ||
displayValue: 'test', | ||
viewFilterGroupId: 'group-1', | ||
positionInViewFilterGroup: 0, | ||
}; |
There was a problem hiding this comment.
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
if (!isDefined(correspondingViewFilter)) { | ||
return false; | ||
} |
There was a problem hiding this comment.
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
return newViewFilters.filter((newViewFilter) => { | ||
const correspondingViewFilter = currentViewFilters.find( | ||
(currentViewFilter) => | ||
currentViewFilter.fieldMetadataId === newViewFilter.fieldMetadataId && | ||
currentViewFilter.viewFilterGroupId === newViewFilter.viewFilterGroupId, | ||
); |
There was a problem hiding this comment.
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
return { | ||
__typename: 'ViewFilter', | ||
...recordFilter, | ||
} satisfies ViewFilter; |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice!
There was a problem hiding this comment.
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(() => { |
There was a problem hiding this comment.
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
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.