Store compact view status (#3850)

* Store compact view status

* Rename to isCompact

* Fixes

---------

Co-authored-by: Thomas Trompette <thomast@twenty.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Thomas Trompette
2024-02-08 16:33:52 +01:00
committed by GitHub
parent 6ee179442a
commit 719da29795
16 changed files with 110 additions and 7 deletions

View File

@ -14,6 +14,7 @@ import { RecordIndexOptionsDropdown } from '@/object-record/record-index/options
import { RECORD_INDEX_OPTIONS_DROPDOWN_ID } from '@/object-record/record-index/options/constants/RecordIndexOptionsDropdownId'; import { RECORD_INDEX_OPTIONS_DROPDOWN_ID } from '@/object-record/record-index/options/constants/RecordIndexOptionsDropdownId';
import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState'; import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState';
import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState'; import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState';
import { recordIndexIsCompactModeActiveState } from '@/object-record/record-index/states/recordIndexIsCompactModeActiveState';
import { recordIndexSortsState } from '@/object-record/record-index/states/recordIndexSortsState'; import { recordIndexSortsState } from '@/object-record/record-index/states/recordIndexSortsState';
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
import { SpreadsheetImportProvider } from '@/spreadsheet-import/provider/components/SpreadsheetImportProvider'; import { SpreadsheetImportProvider } from '@/spreadsheet-import/provider/components/SpreadsheetImportProvider';
@ -62,6 +63,9 @@ export const RecordIndexContainer = ({
const setRecordIndexFilters = useSetRecoilState(recordIndexFiltersState); const setRecordIndexFilters = useSetRecoilState(recordIndexFiltersState);
const setRecordIndexSorts = useSetRecoilState(recordIndexSortsState); const setRecordIndexSorts = useSetRecoilState(recordIndexSortsState);
const setRecordIndexIsCompactModeActive = useSetRecoilState(
recordIndexIsCompactModeActiveState,
);
const { setTableFilters, setTableSorts, setTableColumns } = useRecordTable({ const { setTableFilters, setTableSorts, setTableColumns } = useRecordTable({
recordTableId: recordIndexId, recordTableId: recordIndexId,
@ -118,6 +122,9 @@ export const RecordIndexContainer = ({
onViewTypeChange={(viewType: ViewType) => { onViewTypeChange={(viewType: ViewType) => {
setRecordIndexViewType(viewType); setRecordIndexViewType(viewType);
}} }}
onViewCompactModeChange={(isCompactModeActive: boolean) => {
setRecordIndexIsCompactModeActive(isCompactModeActive);
}}
/> />
<RecordIndexViewBarEffect <RecordIndexViewBarEffect
objectNamePlural={objectNamePlural} objectNamePlural={objectNamePlural}

View File

@ -1,5 +1,5 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue, useSetRecoilState } from 'recoil';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
@ -8,6 +8,7 @@ import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoar
import { turnObjectDropdownFilterIntoQueryFilter } from '@/object-record/record-filter/utils/turnObjectDropdownFilterIntoQueryFilter'; import { turnObjectDropdownFilterIntoQueryFilter } from '@/object-record/record-filter/utils/turnObjectDropdownFilterIntoQueryFilter';
import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState'; import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState';
import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState'; import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState';
import { recordIndexIsCompactModeActiveState } from '@/object-record/record-index/states/recordIndexIsCompactModeActiveState';
import { recordIndexSortsState } from '@/object-record/record-index/states/recordIndexSortsState'; import { recordIndexSortsState } from '@/object-record/record-index/states/recordIndexSortsState';
import { useSetRecordInStore } from '@/object-record/record-store/hooks/useSetRecordInStore'; import { useSetRecordInStore } from '@/object-record/record-store/hooks/useSetRecordInStore';
import { useViewBar } from '@/views/hooks/useViewBar'; import { useViewBar } from '@/views/hooks/useViewBar';
@ -26,8 +27,11 @@ export const useLoadRecordIndexBoard = ({
const { objectMetadataItem } = useObjectMetadataItem({ const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular, objectNameSingular,
}); });
const { setRecordIds: setRecordIdsInBoard, setFieldDefinitions } = const {
useRecordBoard(recordBoardId); setRecordIds: setRecordIdsInBoard,
setFieldDefinitions,
getIsCompactModeActiveState,
} = useRecordBoard(recordBoardId);
const { setRecords: setRecordsInStore } = useSetRecordInStore(); const { setRecords: setRecordsInStore } = useSetRecordInStore();
const recordIndexFieldDefinitions = useRecoilValue( const recordIndexFieldDefinitions = useRecoilValue(
@ -48,6 +52,10 @@ export const useLoadRecordIndexBoard = ({
objectMetadataItem?.fields ?? [], objectMetadataItem?.fields ?? [],
); );
const recordIndexIsCompactModeActive = useRecoilValue(
recordIndexIsCompactModeActiveState,
);
const { records, loading, fetchMoreRecords, queryStateIdentifier } = const { records, loading, fetchMoreRecords, queryStateIdentifier } =
useFindManyRecords({ useFindManyRecords({
objectNameSingular, objectNameSingular,
@ -59,6 +67,10 @@ export const useLoadRecordIndexBoard = ({
viewBarId, viewBarId,
}); });
const setIsCompactModeActive = useSetRecoilState(
getIsCompactModeActiveState(),
);
useEffect(() => { useEffect(() => {
setRecordIdsInBoard(records); setRecordIdsInBoard(records);
}, [records, setRecordIdsInBoard]); }, [records, setRecordIdsInBoard]);
@ -71,6 +83,10 @@ export const useLoadRecordIndexBoard = ({
setEntityCountInCurrentView(records.length); setEntityCountInCurrentView(records.length);
}, [records.length, setEntityCountInCurrentView]); }, [records.length, setEntityCountInCurrentView]);
useEffect(() => {
setIsCompactModeActive(recordIndexIsCompactModeActive);
}, [recordIndexIsCompactModeActive, setIsCompactModeActive]);
return { return {
records, records,
loading, loading,

View File

@ -93,7 +93,7 @@ export const RecordIndexOptionsDropdownContent = ({
handleReorderBoardFields, handleReorderBoardFields,
handleBoardFieldVisibilityChange, handleBoardFieldVisibilityChange,
isCompactModeActive, isCompactModeActive,
setIsCompactModeActive, setAndPersistIsCompactModeActive,
} = useRecordIndexOptionsForBoard({ } = useRecordIndexOptionsForBoard({
objectNameSingular, objectNameSingular,
recordBoardId: recordIndexId, recordBoardId: recordIndexId,
@ -184,7 +184,12 @@ export const RecordIndexOptionsDropdownContent = ({
<DropdownMenuItemsContainer> <DropdownMenuItemsContainer>
<MenuItemToggle <MenuItemToggle
LeftIcon={IconBaselineDensitySmall} LeftIcon={IconBaselineDensitySmall}
onToggleChange={setIsCompactModeActive} onToggleChange={() =>
setAndPersistIsCompactModeActive(
!isCompactModeActive,
currentView,
)
}
toggled={isCompactModeActive} toggled={isCompactModeActive}
text="Compact view" text="Compact view"
toggleSize="small" toggleSize="small"

View File

@ -11,6 +11,8 @@ import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/s
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition'; import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
import { filterAvailableTableColumns } from '@/object-record/utils/filterAvailableTableColumns'; import { filterAvailableTableColumns } from '@/object-record/utils/filterAvailableTableColumns';
import { useViewFields } from '@/views/hooks/internal/useViewFields'; import { useViewFields } from '@/views/hooks/internal/useViewFields';
import { useViews } from '@/views/hooks/internal/useViews';
import { GraphQLView } from '@/views/types/GraphQLView';
type useRecordIndexOptionsForBoardParams = { type useRecordIndexOptionsForBoardParams = {
objectNameSingular: string; objectNameSingular: string;
@ -27,6 +29,7 @@ export const useRecordIndexOptionsForBoard = ({
useRecoilState(recordIndexFieldDefinitionsState); useRecoilState(recordIndexFieldDefinitionsState);
const { persistViewFields } = useViewFields(viewBarId); const { persistViewFields } = useViewFields(viewBarId);
const { updateView } = useViews(viewBarId);
const { getIsCompactModeActiveState } = useRecordBoard(recordBoardId); const { getIsCompactModeActiveState } = useRecordBoard(recordBoardId);
const [isCompactModeActive, setIsCompactModeActive] = useRecoilState( const [isCompactModeActive, setIsCompactModeActive] = useRecoilState(
@ -164,12 +167,24 @@ export const useRecordIndexOptionsForBoard = ({
], ],
); );
const setAndPersistIsCompactModeActive = useCallback(
(isCompactModeActive: boolean, view: GraphQLView | undefined) => {
if (!view) return;
setIsCompactModeActive(isCompactModeActive);
updateView({
...view,
isCompact: isCompactModeActive,
});
},
[setIsCompactModeActive, updateView],
);
return { return {
handleReorderBoardFields, handleReorderBoardFields,
handleBoardFieldVisibilityChange, handleBoardFieldVisibilityChange,
visibleBoardFields, visibleBoardFields,
hiddenBoardFields, hiddenBoardFields,
isCompactModeActive, isCompactModeActive,
setIsCompactModeActive, setAndPersistIsCompactModeActive,
}; };
}; };

View File

@ -0,0 +1,6 @@
import { atom } from 'recoil';
export const recordIndexIsCompactModeActiveState = atom<boolean>({
key: 'recordIndexIsCompactModeActiveState',
default: false,
});

View File

@ -32,6 +32,9 @@ export type ViewBarProps = {
onViewFiltersChange?: (filters: ViewFilter[]) => void | Promise<void>; onViewFiltersChange?: (filters: ViewFilter[]) => void | Promise<void>;
onViewFieldsChange?: (fields: ViewField[]) => void | Promise<void>; onViewFieldsChange?: (fields: ViewField[]) => void | Promise<void>;
onViewTypeChange?: (viewType: ViewType) => void | Promise<void>; onViewTypeChange?: (viewType: ViewType) => void | Promise<void>;
onViewCompactModeChange?: (
isCompactModeActive: boolean,
) => void | Promise<void>;
}; };
export const ViewBar = ({ export const ViewBar = ({
@ -43,6 +46,7 @@ export const ViewBar = ({
onViewFiltersChange, onViewFiltersChange,
onViewSortsChange, onViewSortsChange,
onViewTypeChange, onViewTypeChange,
onViewCompactModeChange,
}: ViewBarProps) => { }: ViewBarProps) => {
const { openDropdown: openOptionsDropdownButton } = useDropdown( const { openDropdown: openOptionsDropdownButton } = useDropdown(
optionsDropdownScopeId, optionsDropdownScopeId,
@ -62,6 +66,7 @@ export const ViewBar = ({
onViewFiltersChange={onViewFiltersChange} onViewFiltersChange={onViewFiltersChange}
onViewSortsChange={onViewSortsChange} onViewSortsChange={onViewSortsChange}
onViewTypeChange={onViewTypeChange} onViewTypeChange={onViewTypeChange}
onViewCompactModeChange={onViewCompactModeChange}
> >
<ViewBarEffect /> <ViewBarEffect />
<ViewBarFilterEffect <ViewBarFilterEffect

View File

@ -41,6 +41,7 @@ export const useViewScopedStates = (args?: { viewScopeId?: string }) => {
onViewFiltersChangeState, onViewFiltersChangeState,
onViewSortsChangeState, onViewSortsChangeState,
onViewTypeChangeState, onViewTypeChangeState,
onViewCompactModeChangeState,
savedViewFieldsByKeySelector, savedViewFieldsByKeySelector,
savedViewFieldsState, savedViewFieldsState,
savedViewFiltersByKeySelector, savedViewFiltersByKeySelector,
@ -74,6 +75,7 @@ export const useViewScopedStates = (args?: { viewScopeId?: string }) => {
onViewFiltersChangeState, onViewFiltersChangeState,
onViewSortsChangeState, onViewSortsChangeState,
onViewTypeChangeState, onViewTypeChangeState,
onViewCompactModeChangeState,
savedViewFieldsByKeySelector, savedViewFieldsByKeySelector,
savedViewFieldsState, savedViewFieldsState,
savedViewFiltersByKeySelector, savedViewFiltersByKeySelector,

View File

@ -54,6 +54,7 @@ export const useViews = (scopeId: string) => {
input: { input: {
id: view.id, id: view.id,
name: view.name, name: view.name,
isCompact: view.isCompact,
}, },
}, },
refetchQueries: [findManyQuery], refetchQueries: [findManyQuery],

View File

@ -236,7 +236,7 @@ export const useViewBar = (props?: UseViewProps) => {
(viewId: string) => { (viewId: string) => {
setCurrentViewId?.(viewId); setCurrentViewId?.(viewId);
const { currentView, onViewTypeChange } = const { currentView, onViewTypeChange, onViewCompactModeChange } =
getViewScopedStateValuesFromSnapshot({ getViewScopedStateValuesFromSnapshot({
snapshot, snapshot,
viewScopeId: scopeId, viewScopeId: scopeId,
@ -248,6 +248,7 @@ export const useViewBar = (props?: UseViewProps) => {
} }
onViewTypeChange?.(currentView.type); onViewTypeChange?.(currentView.type);
onViewCompactModeChange?.(currentView.isCompact);
loadViewFields(currentView.viewFields, viewId); loadViewFields(currentView.viewFields, viewId);
loadViewFilters(currentView.viewFilters, viewId); loadViewFilters(currentView.viewFilters, viewId);
loadViewSorts(currentView.viewSorts, viewId); loadViewSorts(currentView.viewSorts, viewId);

View File

@ -16,6 +16,9 @@ type ViewScopeProps = {
onViewFiltersChange?: (filters: ViewFilter[]) => void | Promise<void>; onViewFiltersChange?: (filters: ViewFilter[]) => void | Promise<void>;
onViewFieldsChange?: (fields: ViewField[]) => void | Promise<void>; onViewFieldsChange?: (fields: ViewField[]) => void | Promise<void>;
onViewTypeChange?: (viewType: ViewType) => void | Promise<void>; onViewTypeChange?: (viewType: ViewType) => void | Promise<void>;
onViewCompactModeChange?: (
isCompactModeActive: boolean,
) => void | Promise<void>;
}; };
export const ViewScope = ({ export const ViewScope = ({
@ -25,6 +28,7 @@ export const ViewScope = ({
onViewFiltersChange, onViewFiltersChange,
onViewFieldsChange, onViewFieldsChange,
onViewTypeChange, onViewTypeChange,
onViewCompactModeChange,
}: ViewScopeProps) => { }: ViewScopeProps) => {
return ( return (
<ViewScopeInternalContext.Provider <ViewScopeInternalContext.Provider
@ -38,6 +42,7 @@ export const ViewScope = ({
onViewFiltersChange={onViewFiltersChange} onViewFiltersChange={onViewFiltersChange}
onViewFieldsChange={onViewFieldsChange} onViewFieldsChange={onViewFieldsChange}
onViewTypeChange={onViewTypeChange} onViewTypeChange={onViewTypeChange}
onViewCompactModeChange={onViewCompactModeChange}
/> />
{children} {children}
</ViewScopeInternalContext.Provider> </ViewScopeInternalContext.Provider>

View File

@ -13,6 +13,9 @@ type ViewScopeInitEffectProps = {
onViewFiltersChange?: (filters: ViewFilter[]) => void | Promise<void>; onViewFiltersChange?: (filters: ViewFilter[]) => void | Promise<void>;
onViewFieldsChange?: (fields: ViewField[]) => void | Promise<void>; onViewFieldsChange?: (fields: ViewField[]) => void | Promise<void>;
onViewTypeChange?: (viewType: ViewType) => void | Promise<void>; onViewTypeChange?: (viewType: ViewType) => void | Promise<void>;
onViewCompactModeChange?: (
isCompactModeActive: boolean,
) => void | Promise<void>;
}; };
export const ViewScopeInitEffect = ({ export const ViewScopeInitEffect = ({
@ -20,29 +23,37 @@ export const ViewScopeInitEffect = ({
onViewFiltersChange, onViewFiltersChange,
onViewFieldsChange, onViewFieldsChange,
onViewTypeChange, onViewTypeChange,
onViewCompactModeChange,
}: ViewScopeInitEffectProps) => { }: ViewScopeInitEffectProps) => {
const { const {
onViewFieldsChangeState, onViewFieldsChangeState,
onViewFiltersChangeState, onViewFiltersChangeState,
onViewSortsChangeState, onViewSortsChangeState,
onViewTypeChangeState, onViewTypeChangeState,
onViewCompactModeChangeState,
} = useViewScopedStates(); } = useViewScopedStates();
const setOnViewSortsChange = useSetRecoilState(onViewSortsChangeState); const setOnViewSortsChange = useSetRecoilState(onViewSortsChangeState);
const setOnViewFiltersChange = useSetRecoilState(onViewFiltersChangeState); const setOnViewFiltersChange = useSetRecoilState(onViewFiltersChangeState);
const setOnViewFieldsChange = useSetRecoilState(onViewFieldsChangeState); const setOnViewFieldsChange = useSetRecoilState(onViewFieldsChangeState);
const setOnViewTypeChange = useSetRecoilState(onViewTypeChangeState); const setOnViewTypeChange = useSetRecoilState(onViewTypeChangeState);
const setOnViewCompactModeChange = useSetRecoilState(
onViewCompactModeChangeState,
);
useEffect(() => { useEffect(() => {
setOnViewSortsChange(() => onViewSortsChange); setOnViewSortsChange(() => onViewSortsChange);
setOnViewFiltersChange(() => onViewFiltersChange); setOnViewFiltersChange(() => onViewFiltersChange);
setOnViewFieldsChange(() => onViewFieldsChange); setOnViewFieldsChange(() => onViewFieldsChange);
setOnViewTypeChange(() => onViewTypeChange); setOnViewTypeChange(() => onViewTypeChange);
setOnViewCompactModeChange(() => onViewCompactModeChange);
}, [ }, [
onViewCompactModeChange,
onViewFieldsChange, onViewFieldsChange,
onViewFiltersChange, onViewFiltersChange,
onViewSortsChange, onViewSortsChange,
onViewTypeChange, onViewTypeChange,
setOnViewCompactModeChange,
setOnViewFieldsChange, setOnViewFieldsChange,
setOnViewFiltersChange, setOnViewFiltersChange,
setOnViewSortsChange, setOnViewSortsChange,

View File

@ -0,0 +1,8 @@
import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap';
export const onViewCompactModeChangeScopeState = createStateScopeMap<
((isCompactModeActive: boolean) => void | Promise<void>) | undefined
>({
key: 'onViewCompactModeChangeScopeState',
defaultValue: undefined,
});

View File

@ -8,6 +8,7 @@ export type GraphQLView = {
name: string; name: string;
type: ViewType; type: ViewType;
objectMetadataId: string; objectMetadataId: string;
isCompact: boolean;
viewFields: ViewField[]; viewFields: ViewField[];
viewFilters: ViewFilter[]; viewFilters: ViewFilter[];
viewSorts: ViewSort[]; viewSorts: ViewSort[];

View File

@ -42,6 +42,7 @@ export const getViewScopedStateValuesFromSnapshot = ({
onViewFiltersChangeState, onViewFiltersChangeState,
onViewSortsChangeState, onViewSortsChangeState,
onViewTypeChangeState, onViewTypeChangeState,
onViewCompactModeChangeState,
savedViewFieldsByKeySelector, savedViewFieldsByKeySelector,
savedViewFieldsState, savedViewFieldsState,
savedViewFiltersByKeySelector, savedViewFiltersByKeySelector,
@ -87,6 +88,10 @@ export const getViewScopedStateValuesFromSnapshot = ({
onViewFiltersChange: getSnapshotValue(snapshot, onViewFiltersChangeState), onViewFiltersChange: getSnapshotValue(snapshot, onViewFiltersChangeState),
onViewSortsChange: getSnapshotValue(snapshot, onViewSortsChangeState), onViewSortsChange: getSnapshotValue(snapshot, onViewSortsChangeState),
onViewTypeChange: getSnapshotValue(snapshot, onViewTypeChangeState), onViewTypeChange: getSnapshotValue(snapshot, onViewTypeChangeState),
onViewCompactModeChange: getSnapshotValue(
snapshot,
onViewCompactModeChangeState,
),
savedViewFieldsByKey: getSnapshotValue( savedViewFieldsByKey: getSnapshotValue(
snapshot, snapshot,
savedViewFieldsByKeySelector, savedViewFieldsByKeySelector,

View File

@ -3,6 +3,7 @@ import { getScopedSelectorDeprecated } from '@/ui/utilities/recoil-scope/utils/g
import { getScopedStateDeprecated } from '@/ui/utilities/recoil-scope/utils/getScopedStateDeprecated'; import { getScopedStateDeprecated } from '@/ui/utilities/recoil-scope/utils/getScopedStateDeprecated';
import { currentViewIdScopedState } from '@/views/states/currentViewIdScopedState'; import { currentViewIdScopedState } from '@/views/states/currentViewIdScopedState';
import { isPersistingViewScopedState } from '@/views/states/isPersistingViewScopedState'; import { isPersistingViewScopedState } from '@/views/states/isPersistingViewScopedState';
import { onViewCompactModeChangeScopeState } from '@/views/states/onViewCompactModeChangeScopeState';
import { onViewTypeChangeScopedState } from '@/views/states/onViewTypeChangeScopedState'; import { onViewTypeChangeScopedState } from '@/views/states/onViewTypeChangeScopedState';
import { currentViewScopedSelector } from '@/views/states/selectors/currentViewScopedSelector'; import { currentViewScopedSelector } from '@/views/states/selectors/currentViewScopedSelector';
@ -170,6 +171,11 @@ export const getViewScopedStates = ({
viewScopeId, viewScopeId,
); );
const onViewCompactModeChangeState = getScopedStateDeprecated(
onViewCompactModeChangeScopeState,
viewScopeId,
);
const currentViewIdState = getScopedStateDeprecated( const currentViewIdState = getScopedStateDeprecated(
currentViewIdScopedState, currentViewIdScopedState,
viewScopeId, viewScopeId,
@ -214,5 +220,6 @@ export const getViewScopedStates = ({
onViewFiltersChangeState, onViewFiltersChangeState,
onViewFieldsChangeState, onViewFieldsChangeState,
onViewTypeChangeState, onViewTypeChangeState,
onViewCompactModeChangeState,
}; };
}; };

View File

@ -41,6 +41,14 @@ export class ViewObjectMetadata extends BaseObjectMetadata {
}) })
type: string; type: string;
@FieldMetadata({
type: FieldMetadataType.BOOLEAN,
label: 'Compact View',
description: 'Describes if the view is in compact mode',
defaultValue: { value: false },
})
isCompact: boolean;
@FieldMetadata({ @FieldMetadata({
type: FieldMetadataType.RELATION, type: FieldMetadataType.RELATION,
label: 'View Fields', label: 'View Fields',