import React from "react"; import { useContext } from "react"; import { connect, DispatchProp } from "react-redux"; import { rulesEngineSelectors, CampaignUtils, serviceDataActions, Constants, Container, ContainerUtils, InventoryManager, InfusionUtils, Item, ItemUtils, RuleData, GroupedMenuOption, serviceDataSelectors, PartyInfo, } from "@dndbeyond/character-rules-engine/es"; import { Button } from "~/components/Button"; import { Popover } from "~/components/Popover"; import { PopoverContent } from "~/components/PopoverContent"; import { useSidebar } from "~/contexts/Sidebar"; import { PaneInfo } from "~/contexts/Sidebar/Sidebar"; import { PaneComponentEnum } from "~/subApps/sheet/components/Sidebar/types"; import SimpleQuantity from "../../components/SimpleQuantity"; import { RemoveButton, ThemeButton, ThemeButtonWithMenu, } from "../../components/common/Button"; import { InventoryManagerContext } from "../../managers/InventoryManagerContext"; import * as appEnvSelectors from "../../selectors/appEnv"; import { SharedAppState } from "../../stores/typings"; import { PaneIdentifierUtils } from "../../utils"; interface LabelLookup { consume: string; remove: string; unequip: string; equip: string; attune: string; unattune: string; } interface Props extends DispatchProp { item: Item; hasMaxAttunedItems: boolean; ruleData: RuleData; isReadonly: boolean; containers: Array; onPostRemoveNavigation?: PaneComponentEnum; partyInfo: PartyInfo | null; inventoryManager: InventoryManager; paneHistoryStart: PaneInfo["paneHistoryStart"]; } interface State { quantity: number; newQuantity: number; } class ItemDetailActions extends React.PureComponent { static defaultProps = { hasMaxAttunedItems: false, }; constructor(props: Props) { super(props); this.state = { quantity: ItemUtils.getQuantity(props.item), newQuantity: ItemUtils.getQuantity(props.item), }; } componentDidUpdate( prevProps: Readonly, prevState: Readonly, snapshot?: any ): void { const { item } = this.props; const nextQuantity = ItemUtils.getQuantity(item); if (nextQuantity !== prevState.quantity) { this.setState({ quantity: nextQuantity, newQuantity: nextQuantity, }); } } handleQuantityUpdate = (newValue: number): void => { const { item, inventoryManager } = this.props; inventoryManager.handleQuantitySet({ item, quantity: newValue }); }; handleRemove = (): void => { const { paneHistoryStart, item, onPostRemoveNavigation, inventoryManager } = this.props; if (onPostRemoveNavigation) { paneHistoryStart(onPostRemoveNavigation); } inventoryManager.handleRemove({ item }); if (ItemUtils.isContainer(item)) { paneHistoryStart(PaneComponentEnum.EQUIPMENT_MANAGE); } }; handleRemoveInfusion = (): void => { const { dispatch, paneHistoryStart, item } = this.props; const infusion = ItemUtils.getInfusion(item); if (infusion) { const infusionId = InfusionUtils.getId(infusion); if (infusionId === null) { return; } if ( InfusionUtils.getType(infusion) === Constants.InfusionTypeEnum.REPLICATE ) { const choiceKey = InfusionUtils.getChoiceKey(infusion); if (choiceKey !== null) { paneHistoryStart( PaneComponentEnum.INFUSION_CHOICE, PaneIdentifierUtils.generateInfusionChoice(choiceKey) ); } } dispatch( serviceDataActions.infusionMappingDestroy( infusionId, InfusionUtils.getInventoryMappingId(infusion) ) ); } }; handleRemoveInfusions = (itemIds: Array): void => { const { dispatch } = this.props; itemIds.forEach((id) => { dispatch(serviceDataActions.infusionMappingsDestroy(itemIds)); }); }; handleUnequip = (): void => { const { inventoryManager, item } = this.props; inventoryManager.handleUnequip({ item }); }; handleEquip = (): void => { const { inventoryManager, item } = this.props; inventoryManager.handleEquip({ item }); }; handleAttune = (): void => { const { inventoryManager, item } = this.props; inventoryManager.handleEquip({ item }); inventoryManager.handleAttune({ item }); }; handleUnattune = (): void => { const { inventoryManager, item } = this.props; inventoryManager.handleUnattune({ item }); }; handleMove = (containerDefinitionKey: string): void => { const { inventoryManager, item } = this.props; inventoryManager.handleMove({ item, containerDefinitionKey }); }; getLabels = (): LabelLookup => { return { consume: "Consume", remove: "Delete Item", unequip: "Unequip", equip: "Equip", attune: "Attune", unattune: "Unattune", }; }; getContainerOptions = (): Array => { const { containers, item, partyInfo } = this.props; return ContainerUtils.getGroupedOptions( ItemUtils.getContainerDefinitionKey(item), containers, "Move To:", partyInfo ? CampaignUtils.getSharingState(partyInfo) : Constants.PartyInventorySharingStateEnum.OFF ); }; renderRemove = (): React.ReactNode => { const { item, containers, isReadonly, inventoryManager } = this.props; const canRemoveItem = inventoryManager.canRemoveItem(item); if (isReadonly || !canRemoveItem) { return null; } const infusion = ItemUtils.getInfusion(item); const name = ItemUtils.getName(item); const isContainer = ItemUtils.isContainer(item); if (isContainer) { const container = containers.find((container) => { return ( ContainerUtils.getContainerType(container) === Constants.ContainerTypeEnum.ITEM && ContainerUtils.getMappingId(container) === ItemUtils.getMappingId(item) ); }); if (container) { if (infusion) { return ( Remove Infusion ); } const hasInfusions = ContainerUtils.hasInfusions(container); return ( Delete } position="bottom" data-testid="delete-item-button" maxWidth={250} > this.handleRemove()} withCancel /> // { // this.handleRemove(); // }} // data-testid="delete-item-button" // /> ); } } if (infusion) { return ( Remove Infusion ); } return ( Delete ); }; render() { const { item, hasMaxAttunedItems, isReadonly, containers, inventoryManager, } = this.props; const labels = this.getLabels(); const containerMoveOptions = this.getContainerOptions(); const isEquipped = ItemUtils.isEquipped(item); const isAttuned = ItemUtils.isAttuned(item); const isStackable = ItemUtils.isStackable(item); const quantity = ItemUtils.getQuantity(item); const isContainer = ItemUtils.getDefinitionIsContainer(item); const parentContainer = ContainerUtils.getItemParentContainer( containers, ItemUtils.getContainerDefinitionKey(item) ); if (!parentContainer) { return null; } const canEquip = inventoryManager.canEquipUnequipItem(item); const canAttune = inventoryManager.canAttuneUnattuneItem(item); const canModifyQuantity = inventoryManager.canModifyQuantity(item); const canMoveItem = inventoryManager.canMoveItem(item) && !isContainer && !isReadonly && !!containers.length; const canShareItem = inventoryManager.canShareItem(item) && !isReadonly; const canClaimItem = inventoryManager.canClaimItem(item) && !isReadonly; return (
{isStackable && ( )} {canEquip && !isReadonly && ( {isEquipped ? labels.unequip : labels.equip} )} {canAttune && !isReadonly && ( {isAttuned ? labels.unattune : labels.attune} )} {canShareItem && ( { const definitionKey = inventoryManager.getPartyEquipmentContainerDefinitionKey(); if (definitionKey) { this.handleMove(definitionKey); } }} size="small" className="ct-item-detail__action" data-testid="move-item-button" > Move )} {canClaimItem && ( { const definitionKey = inventoryManager.getCharacterContainerDefinitionKey(); if (definitionKey) { this.handleMove(definitionKey); } }} size="small" className="ct-item-detail__action" data-testid="move-item-button" > Move )} {canMoveItem && ( Move )} {this.renderRemove()}
); } } function mapStateToProps(state: SharedAppState) { return { ruleData: rulesEngineSelectors.getRuleData(state), isReadonly: appEnvSelectors.getIsReadonly(state), hasMaxAttunedItems: rulesEngineSelectors.hasMaxAttunedItems(state), containers: rulesEngineSelectors.getInventoryContainers(state), partyInfo: serviceDataSelectors.getPartyInfo(state), }; } function ItemDetailActionsContainer(props) { const { inventoryManager } = useContext(InventoryManagerContext); const { pane: { paneHistoryStart }, } = useSidebar(); return ( ); } export default connect(mapStateToProps)(ItemDetailActionsContainer);