From 4e8febd49112d2d5e661cf37e5dd72ea0c604a15 Mon Sep 17 00:00:00 2001 From: dragoni7 Date: Sat, 24 Aug 2024 12:53:11 -0700 Subject: [PATCH 01/12] Began refactoring loadout equipping page --- .../loadouts/components/EquipLoadout.tsx | 46 +++++---- src/features/loadouts/types/index.ts | 2 +- src/features/loadouts/util/armorEquipper.ts | 55 ++++++----- src/features/loadouts/util/equipper.ts | 17 +--- .../loadouts/util/subclassEquipper.ts | 97 +++++++++++++------ 5 files changed, 132 insertions(+), 85 deletions(-) diff --git a/src/features/loadouts/components/EquipLoadout.tsx b/src/features/loadouts/components/EquipLoadout.tsx index 4b21625..2ebf26e 100644 --- a/src/features/loadouts/components/EquipLoadout.tsx +++ b/src/features/loadouts/components/EquipLoadout.tsx @@ -6,7 +6,7 @@ import ArmorIcon from '../../../components/ArmorIcon'; import { CheckRounded, Close } from '@mui/icons-material'; import { STATUS } from '../constants'; import { ArmorEquipper } from '../util/armorEquipper'; -import { DestinyArmor, Plug, SubclassConfig } from '../../../types/d2l-types'; +import { DestinyArmor, SubclassConfig } from '../../../types/d2l-types'; import React from 'react'; import { SubclassEquipper } from '../util/subclassEquipper'; import { ManifestArmorStatMod, ManifestPlug } from '../../../types/manifest-types'; @@ -16,7 +16,7 @@ const EquipLoadout: React.FC = () => { const [processing, setProcessing] = useState([]); const [equipStep, setEquipStep] = useState(''); const [open, setOpen] = useState(false); - const [results, setResults] = useState([]); + const [results, setResults] = useState([]); const [equipping, setEquipping] = useState(false); const onEquipLoadout = async () => { @@ -36,7 +36,7 @@ const EquipLoadout: React.FC = () => { setEquipping(true); const armorEquipper = new ArmorEquipper(); const tempEquipped: any[] = []; - const tempResults: EquipResult[] = []; + const tempResults: EquipResult[][] = []; await armorEquipper.setCharacter(loadout.characterId); @@ -84,7 +84,7 @@ const EquipLoadout: React.FC = () => { const processArmor = async ( tempEquipped: DestinyArmor[], equipper: ArmorEquipper, - tempResults: EquipResult[], + tempResults: EquipResult[][], armor: DestinyArmor, armorMods: { [key: number]: ManifestPlug | ManifestArmorStatMod } ) => { @@ -101,7 +101,7 @@ const EquipLoadout: React.FC = () => { const processSubclass = async ( tempEquipped: any[], equipper: SubclassEquipper, - tempResults: EquipResult[], + tempResults: EquipResult[][], subclassConfig: SubclassConfig ) => { tempEquipped.push(subclassConfig.subclass); @@ -188,7 +188,7 @@ const EquipLoadout: React.FC = () => { ) : ( @@ -201,19 +201,25 @@ const EquipLoadout: React.FC = () => { - {results[index].status === STATUS.SUCCESS - ? 'Success' - : results[index]?.operationsStatus - .slice(1) - .map((error) => ( - - {error === 'Success' ? ( - - ) : ( - - )} - - ))} + {results[index][0].status === STATUS.SUCCESS ? ( +

Mods Successfully Inserted

+ ) : ( + results[index].slice(1).map((result) => ( + + {result.status === STATUS.SUCCESS ? ( + <> + + {result.subject.name} + + ) : ( + <> + + {result.subject.name} + + )} + + )) + )}
) : ( @@ -236,7 +242,7 @@ const EquipLoadout: React.FC = () => { ) : ( - {equipStep} +

{equipStep}

theme.zIndex.drawer + 1 }}> - - + + {equipping ? ( + false + ) : ( + + +

{equipStep}

+
+
+ )} + {processing.map((item, index) => ( - + <> {results[index] !== undefined ? ( - + <> + - - - ) : ( - - ) + + - - - - - - {results[index].slice(1).map((result) => ( - - - ) : ( - - ) - } - anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }} - > - - + + + + + {results[index].slice(1).map((result, index) => ( + + + + + + + ))} - + ) : ( - + - - - - + - + {equipStep} )} - + ))} - {equipping ? ( - false - ) : ( - - -

{equipStep}

-
- - + + {equipping ? ( + false + ) : ( + <> + + + + + - - + + + + - - + + + + -
- )} -
+
+ + )}
diff --git a/src/features/loadouts/components/FadeIn.tsx b/src/features/loadouts/components/FadeIn.tsx new file mode 100644 index 0000000..76d4ae0 --- /dev/null +++ b/src/features/loadouts/components/FadeIn.tsx @@ -0,0 +1,23 @@ +import React, { ReactNode } from 'react'; +import { useSpring, animated } from 'react-spring'; + +const FadeIn: React.FC<{ children: ReactNode; duration?: number; delay?: number }> = (props: { + children: ReactNode; + duration?: number; + delay?: number; +}) => { + const [opacity, api] = useSpring( + () => ({ + from: { opacity: '0' }, + delay: props.delay ? props.delay : 0, + to: { opacity: '1' }, + + config: { duration: props.duration ? props.duration : 2000, tension: 120, friction: 14 }, + }), + [] + ); + + return {props.children}; +}; + +export default FadeIn; diff --git a/src/features/loadouts/components/LoadingBorder.tsx b/src/features/loadouts/components/LoadingBorder.tsx new file mode 100644 index 0000000..3a2f07a --- /dev/null +++ b/src/features/loadouts/components/LoadingBorder.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { useSpring, animated } from 'react-spring'; +import { styled } from '@mui/system'; +import { DestinyArmor } from '../../../types/d2l-types'; + +const DefaultIconContainer = styled(animated.img)({ + borderRadius: '5px', + padding: '2px', + alignItems: 'center', + justifyContent: 'center', + marginRight: '5px', +}); + +interface LoadingBorderProps { + armor: DestinyArmor; + size?: number; +} + +const LoadingBorder: React.FC = ({ armor, size }) => { + const [border, api] = useSpring( + () => ({ + from: { border: '3px solid rgba(195, 195, 195, 0)' }, + to: async (next) => { + await next({ border: '3px solid rgba(195, 195, 195, 1)' }); + await next({ border: '3px solid rgba(195, 195, 195, 0)' }); + }, + loop: true, + + config: { duration: 2000, tension: 120, friction: 14 }, + }), + [] + ); + + return ( + + ); +}; + +export default LoadingBorder; diff --git a/src/features/loadouts/types/index.ts b/src/features/loadouts/types/index.ts index 2d9134e..e723dfb 100644 --- a/src/features/loadouts/types/index.ts +++ b/src/features/loadouts/types/index.ts @@ -4,6 +4,6 @@ export type EquipStatus = STATUS.SUCCESS | STATUS.FAIL; export type EquipResult = { status: EquipStatus; - operationsStatus: string; + message: string; subject: any; }; diff --git a/src/features/loadouts/util/armorEquipper.ts b/src/features/loadouts/util/armorEquipper.ts index b30ad47..e924ab6 100644 --- a/src/features/loadouts/util/armorEquipper.ts +++ b/src/features/loadouts/util/armorEquipper.ts @@ -6,7 +6,7 @@ import { transferItemRequest, } from '../../../lib/bungie_api/requests'; import { DestinyArmor } from '../../../types/d2l-types'; -import { ManifestPlug, ManifestArmorStatMod } from '../../../types/manifest-types'; +import { ManifestArmorStatMod, ManifestArmorMod } from '../../../types/manifest-types'; import { STATUS } from '../constants'; import { Equipper } from './equipper'; @@ -67,7 +67,7 @@ export class ArmorEquipper extends Equipper { public async equipArmor(armor: DestinyArmor): Promise { const result = { status: STATUS.SUCCESS, - operationsStatus: '', + message: '', subject: armor, }; @@ -85,10 +85,7 @@ export class ArmorEquipper extends Equipper { ).catch((error) => { if (error.response) { result.status = STATUS.FAIL; - result.operationsStatus = error.response.data.ErrorStatus.replace( - /([a-z])([A-Z])/g, - '$1 $2' - ); + result.message = error.response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); } }); } @@ -103,10 +100,7 @@ export class ArmorEquipper extends Equipper { ).catch((error) => { if (error.response) { result.status = STATUS.FAIL; - result.operationsStatus = error.response.data.ErrorStatus.replace( - /([a-z])([A-Z])/g, - '$1 $2' - ); + result.message = error.response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); } }); } @@ -115,61 +109,55 @@ export class ArmorEquipper extends Equipper { const response = await equipItemRequest(armor.instanceHash, this.characterId).catch((error) => { if (error.response) { result.status = STATUS.FAIL; - result.operationsStatus = error.response.data.ErrorStatus.replace( - /([a-z])([A-Z])/g, - '$1 $2' - ); + result.message = error.response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); } }); - if (response) - result.operationsStatus = response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); + if (response) { + result.status = STATUS.SUCCESS; + result.message = response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); + } this.result.push(result); } public async equipArmorMods(mods: { - [key: number]: ManifestPlug | ManifestArmorStatMod; + [key: number]: ManifestArmorMod | ManifestArmorStatMod; }): Promise { const armor = this.result[0].subject; - if (armor) { - for (let i = 0; i < 5; i++) { - const result = { - status: STATUS.SUCCESS, - operationsStatus: '', - subject: mods[i], - }; - - if (i === 4 && armor.artifice === false) continue; - - const response = await insertSocketPlugFreeRequest( - armor.instanceHash, - { - plugItemHash: String(mods[i].itemHash), - socketArrayType: 0, - socketIndex: i === 4 && armor.artifice === true ? 11 : i, - }, - this.characterId - ).catch((error) => { - if (error.response) { - result.status = - error.response.data.ErrorCode === ERRORS.SOCKET_ALREADY_CONTAINS_PLUG - ? STATUS.SUCCESS - : STATUS.FAIL; - this.result[0].status = result.status; - result.operationsStatus = error.response.data.ErrorStatus.replace( - /([a-z])([A-Z])/g, - '$1 $2' - ); - } - }); + if (!armor) return; - if (response) - result.operationsStatus = response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); + for (let i = 0; i < 5; i++) { + const result = { + status: STATUS.SUCCESS, + message: '', + subject: mods[i], + }; - this.result.push(result); - } + if (i === 4 && armor.artifice === false) continue; + + const response = await insertSocketPlugFreeRequest( + armor.instanceHash, + { + plugItemHash: String(mods[i].itemHash), + socketArrayType: 0, + socketIndex: i === 4 && armor.artifice === true ? 11 : i, + }, + this.characterId + ).catch((error) => { + if (error.response) { + result.status = + error.response.data.ErrorCode === ERRORS.SOCKET_ALREADY_CONTAINS_PLUG + ? STATUS.SUCCESS + : STATUS.FAIL; + result.message = error.response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); + } + }); + + if (response) result.message = response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); + + this.result.push(result); } } } diff --git a/src/features/loadouts/util/subclassEquipper.ts b/src/features/loadouts/util/subclassEquipper.ts index fc49273..8699533 100644 --- a/src/features/loadouts/util/subclassEquipper.ts +++ b/src/features/loadouts/util/subclassEquipper.ts @@ -9,7 +9,7 @@ export class SubclassEquipper extends Equipper { public async equipSubclass(subclass: Subclass) { const result = { status: STATUS.SUCCESS, - operationsStatus: '', + message: '', subject: subclass, }; @@ -17,16 +17,12 @@ export class SubclassEquipper extends Equipper { (error) => { if (error.response) { result.status = STATUS.FAIL; - result.operationsStatus = error.response.data.ErrorStatus.replace( - /([a-z])([A-Z])/g, - '$1 $2' - ); + result.message = error.response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); } } ); - if (response) - result.operationsStatus = response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); + if (response) result.message = response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); this.result.push(result); } @@ -38,7 +34,7 @@ export class SubclassEquipper extends Equipper { const result = { status: STATUS.SUCCESS, - operationsStatus: '', + message: '', subject: ability, }; @@ -55,16 +51,11 @@ export class SubclassEquipper extends Equipper { error.response.data.ErrorCode === ERRORS.SOCKET_ALREADY_CONTAINS_PLUG ? STATUS.SUCCESS : STATUS.FAIL; - this.result[0].status = result.status; - result.operationsStatus = error.response.data.ErrorStatus.replace( - /([a-z])([A-Z])/g, - '$1 $2' - ); + result.message = error.response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); } }); - if (response) - result.operationsStatus = response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); + if (response) result.message = response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); this.result.push(result); } @@ -76,7 +67,7 @@ export class SubclassEquipper extends Equipper { const result = { status: STATUS.SUCCESS, - operationsStatus: '', + message: '', subject: aspect, }; @@ -93,16 +84,11 @@ export class SubclassEquipper extends Equipper { error.response.data.ErrorCode === ERRORS.SOCKET_ALREADY_CONTAINS_PLUG ? STATUS.SUCCESS : STATUS.FAIL; - this.result[0].status = result.status; - result.operationsStatus = error.response.data.ErrorStatus?.replace( - /([a-z])([A-Z])/g, - '$1 $2' - ); + result.message = error.response.data.ErrorStatus?.replace(/([a-z])([A-Z])/g, '$1 $2'); } }); - if (response) - result.operationsStatus = response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); + if (response) result.message = response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); this.result.push(result); } @@ -114,7 +100,7 @@ export class SubclassEquipper extends Equipper { const result = { status: STATUS.SUCCESS, - operationsStatus: '', + message: '', subject: fragment, }; @@ -131,16 +117,11 @@ export class SubclassEquipper extends Equipper { error.response.data.ErrorCode === ERRORS.SOCKET_ALREADY_CONTAINS_PLUG ? STATUS.SUCCESS : STATUS.FAIL; - this.result[0].status = result.status; - result.operationsStatus = error.response.data.ErrorStatus?.replace( - /([a-z])([A-Z])/g, - '$1 $2' - ); + result.message = error.response.data.ErrorStatus?.replace(/([a-z])([A-Z])/g, '$1 $2'); } }); - if (response) - result.operationsStatus = response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); + if (response) result.message = response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); this.result.push(result); } From 4ec2d3f8ce45fa9af2b4a281b0b4dafd64132734 Mon Sep 17 00:00:00 2001 From: dragoni7 Date: Sun, 25 Aug 2024 13:23:15 -0700 Subject: [PATCH 08/12] Fixed failing to update isOwned for new tables --- src/features/profile/destiny-profile.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/features/profile/destiny-profile.ts b/src/features/profile/destiny-profile.ts index 8aee6f5..75b048d 100644 --- a/src/features/profile/destiny-profile.ts +++ b/src/features/profile/destiny-profile.ts @@ -479,6 +479,16 @@ export async function getProfileData(): Promise { .where('itemHash') .equals(plug.plugItemHash) .modify({ isOwned: true }); + + await db.manifestSubclassAspectsDef + .where('itemHash') + .equals(plug.plugItemHash) + .modify({ isOwned: true }); + + await db.manifestSubclassFragmentsDef + .where('itemHash') + .equals(plug.plugItemHash) + .modify({ isOwned: true }); } } } From 8c371acb97e1d16c42da1a2c1a2d28c09e62eb7f Mon Sep 17 00:00:00 2001 From: dragoni7 Date: Sun, 25 Aug 2024 13:31:48 -0700 Subject: [PATCH 09/12] Fixed type check error --- src/app/routes/Dashboard.tsx | 14 ++++++++++---- src/store/LoadoutReducer.tsx | 35 ++++++++++++++++++++--------------- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/app/routes/Dashboard.tsx b/src/app/routes/Dashboard.tsx index c331f65..2ba374c 100644 --- a/src/app/routes/Dashboard.tsx +++ b/src/app/routes/Dashboard.tsx @@ -9,7 +9,13 @@ import { updateProfileData } from '../../store/ProfileReducer'; import { useDispatch, useSelector } from 'react-redux'; import { generatePermutations } from '../../features/armor-optimization/generate-permutations'; import { filterPermutations } from '../../features/armor-optimization/filter-permutations'; -import { DestinyArmor, Character, FilteredPermutation, DamageType } from '../../types/d2l-types'; +import { + DestinyArmor, + Character, + FilteredPermutation, + DamageType, + SubclassConfig, +} from '../../types/d2l-types'; import StatsTable from '../../features/armor-optimization/StatsTable'; import { RootState } from '../../store'; import HeaderComponent from '../../components/HeaderComponent'; @@ -199,11 +205,11 @@ export const Dashboard: React.FC = () => { const handleSubclassSelect = (subclass: ManifestSubclass) => { setSelectedSubclass(subclass); - if (selectedCharacter) { + + if (selectedCharacter && subclass.damageType in selectedCharacter.subclasses) { dispatch( updateSubclass({ - damageType: subclass.damageType as DamageType, - subclass: selectedCharacter.subclasses[subclass.damageType], + subclass: selectedCharacter.subclasses[subclass.damageType]?.subclass, }) ); } diff --git a/src/store/LoadoutReducer.tsx b/src/store/LoadoutReducer.tsx index 50282cb..a774371 100644 --- a/src/store/LoadoutReducer.tsx +++ b/src/store/LoadoutReducer.tsx @@ -193,21 +193,26 @@ export const loadoutConfigSlice = createSlice({ state.loadout.legArmorMods = initialState.loadout.legArmorMods; state.loadout.classArmorMods = initialState.loadout.classArmorMods; }, - updateSubclass: ( - state, - action: PayloadAction<{ damageType: DamageType; subclass: Subclass }> - ) => { - state.loadout.subclassConfig = { - subclass: action.payload.subclass, - damageType: action.payload.damageType, - super: EMPTY_MANIFEST_PLUG, - aspects: [EMPTY_ASPECT, EMPTY_ASPECT], - fragments: [EMPTY_FRAGMENT, EMPTY_FRAGMENT, EMPTY_FRAGMENT, EMPTY_FRAGMENT, EMPTY_FRAGMENT], - classAbility: null, - meleeAbility: null, - movementAbility: null, - grenade: null, - }; + updateSubclass: (state, action: PayloadAction<{ subclass: Subclass | undefined }>) => { + if (action.payload.subclass !== undefined) { + state.loadout.subclassConfig = { + subclass: action.payload.subclass, + damageType: action.payload.subclass.damageType as DamageType, + super: EMPTY_MANIFEST_PLUG, + aspects: [EMPTY_ASPECT, EMPTY_ASPECT], + fragments: [ + EMPTY_FRAGMENT, + EMPTY_FRAGMENT, + EMPTY_FRAGMENT, + EMPTY_FRAGMENT, + EMPTY_FRAGMENT, + ], + classAbility: null, + meleeAbility: null, + movementAbility: null, + grenade: null, + }; + } }, updateSubclassMods: (state, action: PayloadAction<{ category: string; mods: any[] }>) => { const { category, mods } = action.payload; From 1286ec062378e20dfb7b4622bbe0780fb864ae02 Mon Sep 17 00:00:00 2001 From: dragoni7 Date: Sun, 25 Aug 2024 13:36:51 -0700 Subject: [PATCH 10/12] Additional formatting in EquipLoadout --- src/features/loadouts/components/EquipLoadout.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/features/loadouts/components/EquipLoadout.tsx b/src/features/loadouts/components/EquipLoadout.tsx index ddae79f..44ec557 100644 --- a/src/features/loadouts/components/EquipLoadout.tsx +++ b/src/features/loadouts/components/EquipLoadout.tsx @@ -174,7 +174,13 @@ const EquipLoadout: React.FC = () => { {equipping ? ( false ) : ( - +

{equipStep}

From 612ed8df2f85bca10f9b9d4d85afe2002159239b Mon Sep 17 00:00:00 2001 From: dragoni7 Date: Sun, 25 Aug 2024 14:21:58 -0700 Subject: [PATCH 11/12] Fix equipping screen layout --- src/features/loadouts/components/EquipLoadout.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/features/loadouts/components/EquipLoadout.tsx b/src/features/loadouts/components/EquipLoadout.tsx index 44ec557..47b5b61 100644 --- a/src/features/loadouts/components/EquipLoadout.tsx +++ b/src/features/loadouts/components/EquipLoadout.tsx @@ -186,13 +186,13 @@ const EquipLoadout: React.FC = () => {
)} - + {processing.map((item, index) => ( <> {results[index] !== undefined ? ( <> - + { - + {results[index].slice(1).map((result, index) => ( From c5f5ebf7b207a5c1186154e16b7ebe35857dbc45 Mon Sep 17 00:00:00 2001 From: dragoni7 Date: Sun, 25 Aug 2024 15:57:32 -0700 Subject: [PATCH 12/12] Tweaked formatting --- .../loadouts/components/EquipLoadout.tsx | 70 +++++++++---------- .../loadouts/components/LoadingBorder.tsx | 1 - 2 files changed, 33 insertions(+), 38 deletions(-) diff --git a/src/features/loadouts/components/EquipLoadout.tsx b/src/features/loadouts/components/EquipLoadout.tsx index 47b5b61..05ff0e1 100644 --- a/src/features/loadouts/components/EquipLoadout.tsx +++ b/src/features/loadouts/components/EquipLoadout.tsx @@ -209,7 +209,6 @@ const EquipLoadout: React.FC = () => { border: `4px solid ${ results[index][0].status === STATUS.SUCCESS ? 'green' : 'red' }`, - borderRadius: '5px', }} /> @@ -228,7 +227,6 @@ const EquipLoadout: React.FC = () => { border: `4px solid ${ result.status === STATUS.SUCCESS ? 'green' : 'red' }`, - borderRadius: '5px', }} /> @@ -253,42 +251,40 @@ const EquipLoadout: React.FC = () => { {equipping ? ( false ) : ( - <> - - - - - - - - - - - - - - - - + + + + + + + + + + - + + + + + + )} diff --git a/src/features/loadouts/components/LoadingBorder.tsx b/src/features/loadouts/components/LoadingBorder.tsx index 3a2f07a..ece4b6b 100644 --- a/src/features/loadouts/components/LoadingBorder.tsx +++ b/src/features/loadouts/components/LoadingBorder.tsx @@ -4,7 +4,6 @@ import { styled } from '@mui/system'; import { DestinyArmor } from '../../../types/d2l-types'; const DefaultIconContainer = styled(animated.img)({ - borderRadius: '5px', padding: '2px', alignItems: 'center', justifyContent: 'center',