From 46066812b12f34c0eb103002810025bd3b761f27 Mon Sep 17 00:00:00 2001 From: dragoni7 Date: Sat, 31 Aug 2024 12:28:16 -0700 Subject: [PATCH] Added snackbar for providing feedback when equipping mods. Prevented equipping of mulitple unique mods --- src/features/armor/components/ArmorConfig.tsx | 206 ++++++++++++------ .../armor/components/ArmorModSelector.tsx | 10 +- 2 files changed, 139 insertions(+), 77 deletions(-) diff --git a/src/features/armor/components/ArmorConfig.tsx b/src/features/armor/components/ArmorConfig.tsx index 453c994..433868b 100644 --- a/src/features/armor/components/ArmorConfig.tsx +++ b/src/features/armor/components/ArmorConfig.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, SyntheticEvent } from 'react'; import { useDispatch } from 'react-redux'; import ArmorIcon from '../../../components/ArmorIcon'; import { updateLoadoutArmorMods, updateRequiredStatMods } from '../../../store/LoadoutReducer'; @@ -6,7 +6,7 @@ import { armorMods, DestinyArmor } from '../../../types/d2l-types'; import ArmorModSelector from './ArmorModSelector'; import { getModsBySlot } from '../mod-utils'; import { ManifestArmorMod, ManifestArmorStatMod } from '../../../types/manifest-types'; -import { Grid } from '@mui/material'; +import { Alert, Grid, Fade, Snackbar, SnackbarCloseReason } from '@mui/material'; import { useSelector } from 'react-redux'; import { RootState, store } from '../../../store'; import { PLUG_CATEGORY_HASH } from '../../../lib/bungie_api/constants'; @@ -17,7 +17,15 @@ interface ArmorConfigProps { artificeMods: (ManifestArmorMod | ManifestArmorStatMod)[]; } +interface SnackbarMessage { + message: string; + key: number; +} + const ArmorConfig: React.FC = ({ armor, statMods, artificeMods }) => { + const [snackPack, setSnackPack] = useState([]); + const [snackbarOpen, setSnackbarOpen] = useState(false); + const [messageInfo, setMessageInfo] = useState(undefined); const [armorMods, setArmorMods] = useState<(ManifestArmorMod | ManifestArmorStatMod)[]>([]); const selectedMods: (ManifestArmorMod | ManifestArmorStatMod)[] = useSelector( (state: RootState) => state.loadoutConfig.loadout[(armor.type + 'Mods') as armorMods] @@ -38,6 +46,25 @@ const ArmorConfig: React.FC = ({ armor, statMods, artificeMods const onSelectMod = async (mod: ManifestArmorMod | ManifestArmorStatMod, slot: number) => { let totalCost = mod.energyCost; + + if (selectedMods[slot].itemHash === mod.itemHash) return; + + if (!mod.isOwned) { + setSnackPack((prev) => [ + ...prev, + { message: 'You do not own ' + mod.name, key: new Date().getTime() }, + ]); + return; + } + + if ('unique' in mod && mod.unique && selectedMods.includes(mod)) { + setSnackPack((prev) => [ + ...prev, + { message: mod.name + ' is unique. Only equip one copy', key: new Date().getTime() }, + ]); + return; + } + for (const key in selectedMods) { if (Number(key) !== slot) { let statEnergyCost = armorMods.find( @@ -63,11 +90,13 @@ const ArmorConfig: React.FC = ({ armor, statMods, artificeMods }) ); + const requiredMods = store.getState().loadoutConfig.loadout.requiredStatMods; + if ( - mod.category === PLUG_CATEGORY_HASH.ARMOR_MODS.STAT_ARMOR_MODS || + (requiredMods.length > 0 && mod.category === PLUG_CATEGORY_HASH.ARMOR_MODS.STAT_ARMOR_MODS) || mod.category === PLUG_CATEGORY_HASH.ARMOR_MODS.ARTIFICE_ARMOR_MODS ) { - const newRequired = [...store.getState().loadoutConfig.loadout.requiredStatMods]; + const newRequired = [...requiredMods]; const idx = newRequired.findIndex((required) => required.mod === selectedMods[slot]); newRequired[idx] = { mod: newRequired[idx].mod, equipped: false }; @@ -75,84 +104,119 @@ const ArmorConfig: React.FC = ({ armor, statMods, artificeMods } }; + function handleSnackbarClose(event: SyntheticEvent | Event, reason?: SnackbarCloseReason) { + if (reason === 'clickaway') return; + + setSnackbarOpen(false); + } + + function handleExited() { + setMessageInfo(undefined); + } + useEffect(() => { updateMods().catch(console.error); }, []); + useEffect(() => { + if (snackPack.length && !messageInfo) { + setMessageInfo({ ...snackPack[0] }); + setSnackPack((prev) => prev.slice(1)); + setSnackbarOpen(true); + } else if (snackPack.length && messageInfo && snackbarOpen) { + setSnackbarOpen(false); + } + }, [snackPack, messageInfo, snackbarOpen]); + return ( - - - - - -
-
- - { - onSelectMod(mod, 0); - }} - /> - - - { - onSelectMod(mod, 1); - }} - /> - - - { - onSelectMod(mod, 2); - }} - /> - - - { - onSelectMod(mod, 3); - }} - /> - - {armor.artifice === true ? ( + <> + + + + + +
+
+ + { + onSelectMod(mod, 0); + }} + /> + + + { + onSelectMod(mod, 1); + }} + /> + { - onSelectMod(mod, 4); + onSelectMod(mod, 2); }} /> - ) : ( - - )} - + + { + onSelectMod(mod, 3); + }} + /> + + {armor.artifice === true ? ( + + { + onSelectMod(mod, 4); + }} + /> + + ) : ( + + )} + + + + {messageInfo ? messageInfo.message : undefined} + + + ); }; diff --git a/src/features/armor/components/ArmorModSelector.tsx b/src/features/armor/components/ArmorModSelector.tsx index 86b6cef..4e41ea5 100644 --- a/src/features/armor/components/ArmorModSelector.tsx +++ b/src/features/armor/components/ArmorModSelector.tsx @@ -1,6 +1,6 @@ import { Box } from '@mui/system'; import { ManifestArmorMod, ManifestArmorStatMod } from '../../../types/manifest-types'; -import { useState } from 'react'; + import { Tooltip } from '@mui/material'; interface ModSelectorProps { @@ -29,22 +29,20 @@ const ArmorModSelector: React.FC = ({ selected, mods, onSelect maxWidth: '91px', width: '58%', height: 'auto', - backgroundColor: 'rgba(60, 60, 60, 0.45)', + backgroundColor: 'rgba(10, 10, 10, 0.8)', }} /> {mods.map((mod) => ( -
{ - if (selected.itemHash !== mod.itemHash && mod.isOwned) { - onSelectMod(mod); - } + onSelectMod(mod); }} />