Refactor action menu (#7586)

Introduces effects to set the actionMenuEntries
This commit is contained in:
Raphaël Bosi
2024-10-11 15:25:35 +02:00
committed by GitHub
parent 9b9b34f991
commit 3761fbf86f
26 changed files with 447 additions and 319 deletions

View File

@ -0,0 +1,28 @@
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
export const useActionMenuEntries = () => {
const setActionMenuEntries = useSetRecoilComponentStateV2(
actionMenuEntriesComponentState,
);
const addActionMenuEntry = (entry: ActionMenuEntry) => {
setActionMenuEntries(
(prevEntries) => new Map([...prevEntries, [entry.key, entry]]),
);
};
const removeActionMenuEntry = (key: string) => {
setActionMenuEntries((prevEntries) => {
const newMap = new Map(prevEntries);
newMap.delete(key);
return newMap;
});
};
return {
addActionMenuEntry,
removeActionMenuEntry,
};
};

View File

@ -1,150 +0,0 @@
import { useHandleFavoriteButton } from '@/action-menu/hooks/useHandleFavoriteButton';
import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry';
import { contextStoreTargetedRecordIdsState } from '@/context-store/states/contextStoreTargetedRecordIdsState';
import { useFavorites } from '@/favorites/hooks/useFavorites';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { isObjectMetadataReadOnly } from '@/object-metadata/utils/isObjectMetadataReadOnly';
import { DELETE_MAX_COUNT } from '@/object-record/constants/DeleteMaxCount';
import { useDeleteTableData } from '@/object-record/record-index/options/hooks/useDeleteTableData';
import {
displayedExportProgress,
useExportTableData,
} from '@/object-record/record-index/options/hooks/useExportTableData';
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
import { isNonEmptyString } from '@sniptt/guards';
import { useCallback, useMemo, useState } from 'react';
import { useRecoilValue } from 'recoil';
import {
IconFileExport,
IconHeart,
IconHeartOff,
IconTrash,
isDefined,
} from 'twenty-ui';
export const useComputeActionsBasedOnContextStore = ({
objectMetadataItem,
}: {
objectMetadataItem: ObjectMetadataItem;
}) => {
const contextStoreTargetedRecordIds = useRecoilValue(
contextStoreTargetedRecordIdsState,
);
const [isDeleteRecordsModalOpen, setIsDeleteRecordsModalOpen] =
useState(false);
const { handleFavoriteButtonClick } = useHandleFavoriteButton(
contextStoreTargetedRecordIds,
objectMetadataItem,
);
const baseTableDataParams = {
delayMs: 100,
objectNameSingular: objectMetadataItem.nameSingular,
recordIndexId: objectMetadataItem.namePlural,
};
const { deleteTableData } = useDeleteTableData(baseTableDataParams);
const handleDeleteClick = useCallback(() => {
deleteTableData(contextStoreTargetedRecordIds);
}, [deleteTableData, contextStoreTargetedRecordIds]);
const { progress, download } = useExportTableData({
...baseTableDataParams,
filename: `${objectMetadataItem.nameSingular}.csv`,
});
const isRemote = objectMetadataItem.isRemote;
const numberOfSelectedRecords = contextStoreTargetedRecordIds.length;
const canDelete =
!isObjectMetadataReadOnly(objectMetadataItem) &&
numberOfSelectedRecords < DELETE_MAX_COUNT;
const menuActions: ActionMenuEntry[] = useMemo(
() =>
[
{
label: displayedExportProgress(progress),
Icon: IconFileExport,
accent: 'default',
onClick: () => download(),
} satisfies ActionMenuEntry,
canDelete
? ({
label: 'Delete',
Icon: IconTrash,
accent: 'danger',
onClick: () => {
setIsDeleteRecordsModalOpen(true);
},
ConfirmationModal: (
<ConfirmationModal
isOpen={isDeleteRecordsModalOpen}
setIsOpen={setIsDeleteRecordsModalOpen}
title={`Delete ${numberOfSelectedRecords} ${
numberOfSelectedRecords === 1 ? `record` : 'records'
}`}
subtitle={`Are you sure you want to delete ${
numberOfSelectedRecords === 1
? 'this record'
: 'these records'
}? ${
numberOfSelectedRecords === 1 ? 'It' : 'They'
} can be recovered from the Options menu.`}
onConfirmClick={() => handleDeleteClick()}
deleteButtonText={`Delete ${
numberOfSelectedRecords > 1 ? 'Records' : 'Record'
}`}
/>
),
} satisfies ActionMenuEntry)
: undefined,
].filter(isDefined),
[
download,
progress,
canDelete,
handleDeleteClick,
isDeleteRecordsModalOpen,
numberOfSelectedRecords,
],
);
const hasOnlyOneRecordSelected = contextStoreTargetedRecordIds.length === 1;
const { favorites } = useFavorites();
const isFavorite =
isNonEmptyString(contextStoreTargetedRecordIds[0]) &&
!!favorites?.find(
(favorite) => favorite.recordId === contextStoreTargetedRecordIds[0],
);
return {
availableActionsInContext: [
...menuActions,
...(!isRemote && isFavorite && hasOnlyOneRecordSelected
? [
{
label: 'Remove from favorites',
Icon: IconHeartOff,
onClick: handleFavoriteButtonClick,
},
]
: []),
...(!isRemote && !isFavorite && hasOnlyOneRecordSelected
? [
{
label: 'Add to favorites',
Icon: IconHeart,
onClick: handleFavoriteButtonClick,
},
]
: []),
],
};
};

View File

@ -1,49 +0,0 @@
import { useFavorites } from '@/favorites/hooks/useFavorites';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { useRecoilCallback } from 'recoil';
import { isDefined } from 'twenty-ui';
export const useHandleFavoriteButton = (
selectedRecordIds: string[],
objectMetadataItem: ObjectMetadataItem,
callback?: () => void,
) => {
const { createFavorite, favorites, deleteFavorite } = useFavorites();
const handleFavoriteButtonClick = useRecoilCallback(
({ snapshot }) =>
() => {
if (selectedRecordIds.length > 1) {
return;
}
const selectedRecordId = selectedRecordIds[0];
const selectedRecord = snapshot
.getLoadable(recordStoreFamilyState(selectedRecordId))
.getValue();
const foundFavorite = favorites?.find(
(favorite) => favorite.recordId === selectedRecordId,
);
const isFavorite = !!selectedRecordId && !!foundFavorite;
if (isFavorite) {
deleteFavorite(foundFavorite.id);
} else if (isDefined(selectedRecord)) {
createFavorite(selectedRecord, objectMetadataItem.nameSingular);
}
callback?.();
},
[
callback,
createFavorite,
deleteFavorite,
favorites,
objectMetadataItem.nameSingular,
selectedRecordIds,
],
);
return { handleFavoriteButtonClick };
};