diff --git a/src/pageEditor/modListingPanel/ModActionMenu.tsx b/src/pageEditor/modListingPanel/ModActionMenu.tsx index 1fef83339..9b1e87f4e 100644 --- a/src/pageEditor/modListingPanel/ModActionMenu.tsx +++ b/src/pageEditor/modListingPanel/ModActionMenu.tsx @@ -28,44 +28,49 @@ import styles from "./ActionMenu.module.scss"; import EllipsisMenu, { type EllipsisMenuItem, } from "@/components/ellipsisMenu/EllipsisMenu"; -import { type AddNewModComponent } from "@/pageEditor/hooks/useAddNewModComponent"; +import useAddNewModComponent from "@/pageEditor/hooks/useAddNewModComponent"; import { useAvailableFormStateAdapters } from "@/pageEditor/starterBricks/adapter"; +import useDeactivateMod from "@/pageEditor/hooks/useDeactivateMod"; +import useSaveMod from "@/pageEditor/hooks/useSaveMod"; +import { type ModMetadata } from "@/types/modComponentTypes"; +import useClearModChanges from "@/pageEditor/hooks/useClearModChanges"; +import { isInnerDefinitionRegistryId } from "@/types/helpers"; +import { actions } from "@/pageEditor/store/editor/editorSlice"; +import { useDispatch, useSelector } from "react-redux"; +import { + selectActiveModId, + selectModIsDirty, +} from "@/pageEditor/store/editor/editorSelectors"; -type OptionalAction = (() => Promise) | undefined; - -type ActionMenuProps = { - isDirty: boolean; - isActive: boolean; +const ModActionMenu: React.FC<{ + modMetadata: ModMetadata; labelRoot: string; - onDeactivate: () => Promise; - onMakeCopy: () => Promise; - onAddStarterBrick: AddNewModComponent; - // Actions only defined if there are changes - onSave: OptionalAction; - onClearChanges: OptionalAction; -}; +}> = ({ modMetadata, labelRoot }) => { + const { id: modId } = modMetadata; + const activeModId = useSelector(selectActiveModId); -const ModActionMenu: React.FC = ({ - isActive, - labelRoot, - isDirty, - onAddStarterBrick, - onDeactivate, - onMakeCopy, - // Convert to null because EllipsisMenuItem expects null vs. undefined - onSave = null, - onClearChanges = null, -}) => { + const dispatch = useDispatch(); const modComponentFormStateAdapters = useAvailableFormStateAdapters(); + const deactivateMod = useDeactivateMod(); + const saveMod = useSaveMod(); + const clearModChanges = useClearModChanges(); + const addNewModComponent = useAddNewModComponent(modMetadata); + + const isUnsavedMod = isInnerDefinitionRegistryId(modId); + const isDirty = useSelector(selectModIsDirty(modId)); + const isActive = activeModId === modId; + const menuItems: EllipsisMenuItem[] = [ { title: "Clear Changes", icon: , - action: onClearChanges, - // Always show Clear Changes button, even if there are no changes so the UI is more consistent / the user doesn't - // wonder why the menu item is missing - disabled: !isDirty || !onClearChanges, + async action() { + await clearModChanges(modId); + }, + // Always show Clear Changes button, even if there are no changes or the mod is an unsaved mod so the UI is more + // consistent / the user doesn't wonder why the menu item is missing + disabled: !isDirty || isUnsavedMod, }, { title: "Add Starter Brick", @@ -73,34 +78,37 @@ const ModActionMenu: React.FC = ({ submenu: modComponentFormStateAdapters.map((adapter) => ({ title: adapter.label, action() { - onAddStarterBrick(adapter); + addNewModComponent(adapter); }, icon: , })), - hide: !onAddStarterBrick, }, { title: "Make a copy", icon: , - action: onMakeCopy, + async action() { + dispatch(actions.showCreateModModal({ keepLocalCopy: true })); + }, }, { title: "Deactivate", icon: , - action: onDeactivate, - hide: !onDeactivate, + async action() { + await deactivateMod({ modId }); + }, }, ]; return (
- {onSave != null && ( - - )} + {/* TODO: did we really want to always show SaveButton? That is the current behavior as of 2.1.5-beta.1 */} + { + await saveMod(modId); + }} + disabled={!isDirty} + /> {isActive && ( { - const dispatch = useDispatch(); const activeModComponentId = useSelector(selectActiveModComponentId); const activeModId = useSelector(selectActiveModId); const expandedModId = useSelector(selectExpandedModId); @@ -87,31 +82,12 @@ const ModComponents: React.FunctionComponent = () => { ], ); - const saveMod = useSaveMod(); - const clearModChanges = useClearModChanges(); - const deactivateMod = useDeactivateMod(); - const listItems = filteredSidebarItems.map((sidebarItem) => { if (isModSidebarItem(sidebarItem)) { const { modMetadata, modComponents } = sidebarItem; return ( - { - await saveMod(modMetadata.id); - }} - onClearChanges={async () => { - await clearModChanges(modMetadata.id); - }} - onDeactivate={async () => { - await deactivateMod({ modId: modMetadata.id }); - }} - onMakeCopy={async () => { - dispatch(actions.showCreateModModal({ keepLocalCopy: true })); - }} - > + {modComponents.map((modComponentSidebarItem) => ( { render( - +
test children
@@ -69,13 +63,7 @@ describe("ModListItem", () => { render( - +
test children
@@ -109,13 +97,7 @@ describe("ModListItem", () => { render( - +
test children
diff --git a/src/pageEditor/modListingPanel/ModListItem.tsx b/src/pageEditor/modListingPanel/ModListItem.tsx index c3fa69d73..3256df055 100644 --- a/src/pageEditor/modListingPanel/ModListItem.tsx +++ b/src/pageEditor/modListingPanel/ModListItem.tsx @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -import React, { type PropsWithChildren } from "react"; +import React from "react"; import styles from "./Entry.module.scss"; import { ModHasUpdateIcon } from "@/pageEditor/modListingPanel/ModComponentIcons"; import { Accordion, ListGroup } from "react-bootstrap"; @@ -33,38 +33,21 @@ import { selectActiveModId, selectDirtyMetadataForModId, selectExpandedModId, - selectModIsDirty, } from "@/pageEditor/store/editor/editorSelectors"; import * as semver from "semver"; import { useGetModDefinitionQuery } from "@/data/service/api"; -import useAddNewModComponent from "@/pageEditor/hooks/useAddNewModComponent"; import { type ModMetadata } from "@/types/modComponentTypes"; -import { isInnerDefinitionRegistryId } from "@/types/helpers"; import ModActionMenu from "@/pageEditor/modListingPanel/ModActionMenu"; -export type ModListItemProps = PropsWithChildren<{ +const ModListItem: React.FC<{ modMetadata: ModMetadata; - onSave: () => Promise; - onClearChanges: () => Promise; - onDeactivate: () => Promise; - onMakeCopy: () => Promise; -}>; - -const ModListItem: React.FC = ({ - modMetadata, - children, - onSave, - onClearChanges, - onDeactivate, - onMakeCopy, -}) => { +}> = ({ modMetadata, children }) => { const dispatch = useDispatch(); const activeModId = useSelector(selectActiveModId); const expandedModId = useSelector(selectExpandedModId); const activeModComponentFormState = useSelector( selectActiveModComponentFormState, ); - const addNewModComponent = useAddNewModComponent(modMetadata); const { id: modId, name: savedName, version: activatedVersion } = modMetadata; const isActive = activeModId === modId; @@ -80,9 +63,6 @@ const ModListItem: React.FC = ({ const dirtyName = useSelector(selectDirtyMetadataForModId(modId))?.name; const name = dirtyName ?? savedName ?? "Loading..."; - const isDirty = useSelector(selectModIsDirty(modId)); - - const isUnsavedMod = isInnerDefinitionRegistryId(modMetadata.id); const hasUpdate = latestModVersion != null && @@ -115,16 +95,7 @@ const ModListItem: React.FC = ({ /> )} - + <>{children}