useItemActions.jsx 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import React from "react";
  2. import { useRecoilValue, useRecoilCallback } from "recoil";
  3. import { SelectedItemsAtom } from "../";
  4. import intersection from "lodash.intersection";
  5. import { ItemMapAtom } from "../";
  6. export const getDefaultActionsFromItem = (item, itemMap) => {
  7. if (item.type in itemMap) {
  8. const actions = itemMap[item.type].defaultActions;
  9. if (typeof actions === "function") {
  10. return actions(item);
  11. }
  12. return actions;
  13. }
  14. return [];
  15. };
  16. export const getAvailableActionsFromItem = (item, itemMap) => {
  17. if (item.type in itemMap) {
  18. const actions = itemMap[item.type].availableActions;
  19. if (typeof actions === "function") {
  20. return actions(item);
  21. }
  22. return actions;
  23. }
  24. return [];
  25. };
  26. export const getActionsFromItem = (item, itemMap) => {
  27. const { actions = getDefaultActionsFromItem(item, itemMap) } = item;
  28. // Filter availableActions to keep same order
  29. return getAvailableActionsFromItem(item, itemMap).filter((action) =>
  30. actions.includes(action)
  31. );
  32. };
  33. export const useItemActions = (itemMap) => {
  34. const selected = useRecoilValue(SelectedItemsAtom);
  35. const [availableActions, setAvailableActions] = React.useState([]);
  36. const isMountedRef = React.useRef(false);
  37. const getItemListOrSelected = useRecoilCallback(
  38. ({ snapshot }) => async (itemIds) => {
  39. const itemMap = await snapshot.getPromise(ItemMapAtom);
  40. if (itemIds) {
  41. return [itemIds, itemIds.map((id) => itemMap[id])];
  42. } else {
  43. const selectedItems = await snapshot.getPromise(SelectedItemsAtom);
  44. return [selectedItems, selectedItems.map((id) => itemMap[id])];
  45. }
  46. },
  47. []
  48. );
  49. React.useEffect(() => {
  50. // Mounted guard
  51. isMountedRef.current = true;
  52. return () => {
  53. isMountedRef.current = false;
  54. };
  55. }, []);
  56. const updateAvailableActions = React.useCallback(async () => {
  57. const [selectedItemIds, selectedItemList] = await getItemListOrSelected();
  58. if (selectedItemIds.length > 0) {
  59. // Prevent set state on unmounted component
  60. if (!isMountedRef.current) return;
  61. const allActions = selectedItemList.reduce((acc, item) => {
  62. return intersection(acc, getActionsFromItem(item, itemMap));
  63. }, getActionsFromItem(selectedItemList[0], itemMap));
  64. setAvailableActions(allActions);
  65. } else {
  66. setAvailableActions([]);
  67. }
  68. }, [getItemListOrSelected, itemMap]);
  69. // Update available actions when selection change
  70. React.useEffect(() => {
  71. updateAvailableActions();
  72. }, [updateAvailableActions, selected]);
  73. return {
  74. availableActions,
  75. };
  76. };
  77. export default useItemActions;