From a8cc595bacf5d688a4c51370db7f9889806c83f7 Mon Sep 17 00:00:00 2001 From: dragoni7 Date: Thu, 5 Sep 2024 13:55:29 -0700 Subject: [PATCH 1/7] Refactor and reorganization. Added share link to local store. --- src/app/routes/Dashboard.tsx | 4 + src/app/routes/Landing.tsx | 3 + .../loadouts/components/EquipLoadout.tsx | 2 +- .../loadouts/components/ShareLoadout.tsx | 7 +- src/features/loadouts/types/index.ts | 23 ++++++ .../{armorEquipper.ts => armor-equipper.ts} | 0 .../loadout-encoder.ts} | 81 ++++++++----------- .../{loadoutUtils.ts => loadout-utils.ts} | 4 +- ...bclassEquipper.ts => subclass-equipper.ts} | 0 9 files changed, 71 insertions(+), 53 deletions(-) rename src/features/loadouts/util/{armorEquipper.ts => armor-equipper.ts} (100%) rename src/features/loadouts/{components/loadoutEncoder.ts => util/loadout-encoder.ts} (73%) rename src/features/loadouts/util/{loadoutUtils.ts => loadout-utils.ts} (97%) rename src/features/loadouts/util/{subclassEquipper.ts => subclass-equipper.ts} (100%) diff --git a/src/app/routes/Dashboard.tsx b/src/app/routes/Dashboard.tsx index 63acfab..6957506 100644 --- a/src/app/routes/Dashboard.tsx +++ b/src/app/routes/Dashboard.tsx @@ -139,6 +139,10 @@ export const Dashboard: React.FC = () => { updateProfile().catch(console.error); + // if navigated here using a share link + if (localStorage.getItem('lastShared') !== '') { + } + setDataLoading(false); }, []); diff --git a/src/app/routes/Landing.tsx b/src/app/routes/Landing.tsx index cb81994..bc88178 100644 --- a/src/app/routes/Landing.tsx +++ b/src/app/routes/Landing.tsx @@ -21,6 +21,9 @@ export const LandingRoute: React.FC = () => { }); useEffect(() => { + // if navigated with share link, store the data before authenticating + if (window.location.href.includes('d=')) + localStorage.setItem('lastShared', window.location.href.split('d=')[1]); setTimeout(async () => { if (isAuthenticated()) { console.log('Already authenticated'); diff --git a/src/features/loadouts/components/EquipLoadout.tsx b/src/features/loadouts/components/EquipLoadout.tsx index e29a8a1..300b2e0 100644 --- a/src/features/loadouts/components/EquipLoadout.tsx +++ b/src/features/loadouts/components/EquipLoadout.tsx @@ -20,7 +20,7 @@ import { STATUS } from '../constants'; import React from 'react'; import LoadingBorder from './LoadingBorder'; import FadeIn from './FadeIn'; -import { equipLoadout } from '../util/loadoutUtils'; +import { equipLoadout } from '../util/loadout-utils'; import { TransitionProps } from '@mui/material/transitions'; const StyledTitle = styled(Typography)(({ theme }) => ({ diff --git a/src/features/loadouts/components/ShareLoadout.tsx b/src/features/loadouts/components/ShareLoadout.tsx index 5f2098d..b192c88 100644 --- a/src/features/loadouts/components/ShareLoadout.tsx +++ b/src/features/loadouts/components/ShareLoadout.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { RootState, AppDispatch } from '../../../store/index'; import { createSelector } from '@reduxjs/toolkit'; -import { encodeLoadout, decodeLoadout, LoadoutData } from './loadoutEncoder'; +import { encodeLoadout, decodeLoadout } from '../util/loadout-encoder'; import { findMatchingArmorSet, DecodedLoadoutInfo } from './findMatchingArmorSet'; import { Button, @@ -24,6 +24,7 @@ import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'; import { styled } from '@mui/material/styles'; import { CharacterClass, FilteredPermutation, StatName } from '../../../types/d2l-types'; +import { SharedLoadoutDto } from '../types'; const StyledDialog = styled(Dialog)(({ theme }) => ({ '& .MuiDialog-paper': { @@ -239,7 +240,7 @@ const ShareLoadout: React.FC = () => { }; const generateShareLink = () => { - const dataToShare: LoadoutData = { + const dataToShare: SharedLoadoutDto = { mods: { helmet: loadoutState.helmetMods.map((mod) => mod.itemHash), gauntlets: loadoutState.gauntletMods.map((mod) => mod.itemHash), @@ -261,7 +262,7 @@ const ShareLoadout: React.FC = () => { characterClass: selectedCharacterClass || null, }; const encodedData = encodeLoadout(dataToShare); - const shareableLink = `${window.location.origin}/loadout?d=${encodedData}`; + const shareableLink = `${window.location.origin}/?d=${encodedData}`; setShareLink(shareableLink); console.log('Generated link:', shareableLink); }; diff --git a/src/features/loadouts/types/index.ts b/src/features/loadouts/types/index.ts index 781a2a8..c735638 100644 --- a/src/features/loadouts/types/index.ts +++ b/src/features/loadouts/types/index.ts @@ -1,3 +1,4 @@ +import { CharacterClass } from '../../../types/d2l-types'; import { STATUS } from '../constants'; export type EquipStatus = STATUS.SUCCESS | STATUS.FAIL; @@ -9,3 +10,25 @@ export type EquipResult = { }; export type setState = (value: React.SetStateAction) => void; + +export type SharedLoadoutDto = { + mods: { + helmet: number[]; + gauntlets: number[]; + chest: number[]; + legs: number[]; + }; + subclass: { + super: number; + aspects: number[]; + fragments: number[]; + classAbility: number; + meleeAbility: number; + movementAbility: number; + grenade: number; + }; + selectedExoticItemHash: string; + selectedValues: Record; + statPriority: string[]; + characterClass: CharacterClass | null; +}; diff --git a/src/features/loadouts/util/armorEquipper.ts b/src/features/loadouts/util/armor-equipper.ts similarity index 100% rename from src/features/loadouts/util/armorEquipper.ts rename to src/features/loadouts/util/armor-equipper.ts diff --git a/src/features/loadouts/components/loadoutEncoder.ts b/src/features/loadouts/util/loadout-encoder.ts similarity index 73% rename from src/features/loadouts/components/loadoutEncoder.ts rename to src/features/loadouts/util/loadout-encoder.ts index 41f7ae5..84625b3 100644 --- a/src/features/loadouts/components/loadoutEncoder.ts +++ b/src/features/loadouts/util/loadout-encoder.ts @@ -1,16 +1,17 @@ -import { CharacterClass } from "../../../types/d2l-types"; +import { CharacterClass } from '../../../types/d2l-types'; +import { SharedLoadoutDto } from '../types'; /** * This file provides functions to encode and decode `LoadoutData` objects - * into compact, URL-safe strings and back. It uses a custom base64-like + * into compact, URL-safe strings and back. It uses a custom base64-like * character set for encoding numbers, ensuring the result is URL-safe. - * + * * Functions: * - `encodeNumber(num)`: Encodes a number into a custom base64 string. * - `decodeNumber(str)`: Decodes a custom base64 string back into a number. * - `encodeLoadout(loadout)`: Converts a `LoadoutData` object into a compact string. * - `decodeLoadout(encoded)`: Reconstructs a `LoadoutData` object from an encoded string. - * + * * Example: * Given the number 1234, `encodeNumber(1234)` returns "JI". * Decoding "JI" with `decodeNumber("JI")` returns 1234. @@ -19,7 +20,7 @@ import { CharacterClass } from "../../../types/d2l-types"; * ``` * const loadout = { * mods: { helmet: [1], gauntlets: [2], chest: [3], legs: [4] }, - * subclass: { super: 5, aspects: [6], fragments: [7], classAbility: 8, + * subclass: { super: 5, aspects: [6], fragments: [7], classAbility: 8, * meleeAbility: 9, movementAbility: 10, grenade: 11 }, * selectedExoticItemHash: '12', * selectedValues: { mobility: 13, resilience: 14 }, @@ -39,7 +40,7 @@ function encodeNumber(num: number): string { let encoded = ''; while (num > 0) { encoded = base64Chars[num & 63] + encoded; - num = Math.floor(num / 64); // Use Math.floor for integer division + num = Math.floor(num / 64); // Use Math.floor for integer division } return encoded; } @@ -52,41 +53,13 @@ function decodeNumber(str: string): number { return decoded; } -export interface LoadoutData { - mods: { - helmet: number[]; - gauntlets: number[]; - chest: number[]; - legs: number[]; - }; - subclass: { - super: number; - aspects: number[]; - fragments: number[]; - classAbility: number; - meleeAbility: number; - movementAbility: number; - grenade: number; - }; - selectedExoticItemHash: string; - selectedValues: Record; - statPriority: string[]; - characterClass: CharacterClass | null; -} - -export function encodeLoadout(loadout: LoadoutData): string { - const { - mods, - subclass, - selectedExoticItemHash, - selectedValues, - statPriority, - characterClass - } = loadout; +export function encodeLoadout(loadout: SharedLoadoutDto): string { + const { mods, subclass, selectedExoticItemHash, selectedValues, statPriority, characterClass } = + loadout; // Encode mods const encodedMods = Object.values(mods) - .map(arr => arr.map(encodeNumber).join(',')) + .map((arr) => arr.map(encodeNumber).join(',')) .join('|'); // Encode subclass @@ -97,7 +70,7 @@ export function encodeLoadout(loadout: LoadoutData): string { encodeNumber(subclass.classAbility), encodeNumber(subclass.meleeAbility), encodeNumber(subclass.movementAbility), - encodeNumber(subclass.grenade) + encodeNumber(subclass.grenade), ].join('|'); // Encode selected exotic @@ -110,16 +83,30 @@ export function encodeLoadout(loadout: LoadoutData): string { // Encode stat priority const statOrder = ['mobility', 'resilience', 'recovery', 'discipline', 'intellect', 'strength']; - const encodedPriority = statPriority.map(stat => statOrder.indexOf(stat).toString()).join(''); + const encodedPriority = statPriority.map((stat) => statOrder.indexOf(stat).toString()).join(''); // Encode character class const encodedClass = characterClass || ''; - return [encodedMods, encodedSubclass, encodedExotic, encodedValues, encodedPriority, encodedClass].join('~'); + return [ + encodedMods, + encodedSubclass, + encodedExotic, + encodedValues, + encodedPriority, + encodedClass, + ].join('~'); } -export function decodeLoadout(encoded: string): LoadoutData { - const [encodedMods, encodedSubclass, encodedExotic, encodedValues, encodedPriority, encodedClass] = encoded.split('~'); +export function decodeLoadout(encoded: string): SharedLoadoutDto { + const [ + encodedMods, + encodedSubclass, + encodedExotic, + encodedValues, + encodedPriority, + encodedClass, + ] = encoded.split('~'); const mods = { helmet: encodedMods.split('|')[0].split(',').map(decodeNumber), @@ -142,14 +129,14 @@ export function decodeLoadout(encoded: string): LoadoutData { const selectedExoticItemHash = decodeNumber(encodedExotic).toString(); const selectedValues = Object.fromEntries( - encodedValues.split(',').map(pair => { + encodedValues.split(',').map((pair) => { const [key, value] = pair.split(':'); return [key, decodeNumber(value)]; }) ); const statOrder = ['mobility', 'resilience', 'recovery', 'discipline', 'intellect', 'strength']; - const statPriority = encodedPriority.split('').map(index => statOrder[parseInt(index)]); + const statPriority = encodedPriority.split('').map((index) => statOrder[parseInt(index)]); // Decode character class const characterClass = (encodedClass as CharacterClass) || null; @@ -160,6 +147,6 @@ export function decodeLoadout(encoded: string): LoadoutData { selectedExoticItemHash, selectedValues, statPriority, - characterClass + characterClass, }; -} \ No newline at end of file +} diff --git a/src/features/loadouts/util/loadoutUtils.ts b/src/features/loadouts/util/loadout-utils.ts similarity index 97% rename from src/features/loadouts/util/loadoutUtils.ts rename to src/features/loadouts/util/loadout-utils.ts index 70498ac..cfa1fd1 100644 --- a/src/features/loadouts/util/loadoutUtils.ts +++ b/src/features/loadouts/util/loadout-utils.ts @@ -15,8 +15,8 @@ import { ManifestStatPlug, } from '../../../types/manifest-types'; import { EquipResult, setState } from '../types'; -import { ArmorEquipper } from './armorEquipper'; -import { SubclassEquipper } from './subclassEquipper'; +import { ArmorEquipper } from './armor-equipper'; +import { SubclassEquipper } from './subclass-equipper'; export async function createInGameLoadout( characterId: string, diff --git a/src/features/loadouts/util/subclassEquipper.ts b/src/features/loadouts/util/subclass-equipper.ts similarity index 100% rename from src/features/loadouts/util/subclassEquipper.ts rename to src/features/loadouts/util/subclass-equipper.ts From b36d9c7f3451a97544e05f6ae31e08b6c12556a6 Mon Sep 17 00:00:00 2001 From: dragoni7 Date: Fri, 6 Sep 2024 14:11:09 -0700 Subject: [PATCH 2/7] Integration of full sharing to equipping of loadouts. --- src/app/routes/Dashboard.tsx | 286 ++++++++++++++---- .../armor-optimization/StatsTable.tsx | 24 +- .../armor-optimization/filter-permutations.ts | 66 +++- .../loadouts/components/ShareLoadout.tsx | 113 +------ .../components/findMatchingArmorSet.ts | 107 ------- src/features/loadouts/types/index.ts | 1 + src/features/loadouts/util/loadout-encoder.ts | 16 +- src/features/loadouts/util/loadout-utils.ts | 8 + src/types/d2l-types.ts | 17 +- 9 files changed, 345 insertions(+), 293 deletions(-) delete mode 100644 src/features/loadouts/components/findMatchingArmorSet.ts diff --git a/src/app/routes/Dashboard.tsx b/src/app/routes/Dashboard.tsx index 6957506..14b113b 100644 --- a/src/app/routes/Dashboard.tsx +++ b/src/app/routes/Dashboard.tsx @@ -3,24 +3,48 @@ import { styled } from '@mui/system'; import { useDispatch, useSelector } from 'react-redux'; import { AppDispatch, RootState } from '../../store'; import { generatePermutations } from '../../features/armor-optimization/generate-permutations'; -import { filterPermutations } from '../../features/armor-optimization/filter-permutations'; +import { + filterFromSharedLoadout, + filterPermutations, +} from '../../features/armor-optimization/filter-permutations'; import SingleDiamondButton from '../../components/SingleDiamondButton'; import NumberBoxes from '../../features/armor-optimization/NumberBoxes'; import { getDestinyMembershipId } from '../../features/membership/bungie-account'; import { updateMembership } from '../../store/MembershipReducer'; import { getProfileData } from '../../features/profile/destiny-profile'; import { updateProfileData, updateSelectedCharacter } from '../../store/ProfileReducer'; -import { Character, FilteredPermutation, SubclassConfig } from '../../types/d2l-types'; +import { + armor, + Character, + CharacterClass, + DecodedLoadoutData, + DestinyArmor, + FilteredPermutation, + StatName, + SubclassConfig, +} from '../../types/d2l-types'; import StatsTable from '../../features/armor-optimization/StatsTable'; import HeaderComponent from '../../components/HeaderComponent'; import { db } from '../../store/db'; -import { resetLoadout, updateLoadoutCharacter, updateSubclass } from '../../store/LoadoutReducer'; +import { + resetLoadout, + resetLoadoutArmorMods, + updateLoadoutArmor, + updateLoadoutCharacter, + updateRequiredStatMods, + updateSubclass, + updateSubclassMods, +} from '../../store/LoadoutReducer'; import SubclassCustomizationWrapper from '../../features/subclass/SubclassCustomizationWrapper'; import { updateManifest } from '../../lib/bungie_api/manifest'; import LoadoutCustomization from '../../components/LoadoutCustomization'; import greyBackground from '../../assets/grey.png'; import ExoticSelector from '../../features/armor-optimization/ExoticSelector'; import { DAMAGE_TYPE } from '../../lib/bungie_api/constants'; +import { decodeLoadout } from '../../features/loadouts/util/loadout-encoder'; +import { updateSelectedExoticItemHash } from '../../store/DashboardReducer'; +import { ManifestArmorStatMod, ManifestExoticArmor } from '../../types/manifest-types'; +import { SharedLoadoutDto } from '../../features/loadouts/types'; const PageContainer = styled('div')({ display: 'flex', @@ -111,7 +135,6 @@ export const Dashboard: React.FC = () => { ); const selectedCharacter = useSelector((state: RootState) => state.profile.selectedCharacter); - const [dataLoading, setDataLoading] = useState(true); const [generatingPermutations, setGeneratingPermutations] = useState(false); const [subclasses, setSubclasses] = useState< { [key: number]: SubclassConfig | undefined } | undefined @@ -121,8 +144,9 @@ export const Dashboard: React.FC = () => { const [lastNonPrismaticSubclass, setLastNonPrismaticSubclass] = useState( null ); - const [showArmorCustomization, setShowArmorCustomization] = useState(false); + const [showLoadoutCustomization, setShowLoadoutCustomization] = useState(false); const [showAbilitiesModification, setShowAbilitiesModification] = useState(false); + const [sharedLoadoutDto, setSharedLoadoutDto] = useState(undefined); useEffect(() => { const updateProfile = async () => { @@ -132,46 +156,161 @@ export const Dashboard: React.FC = () => { const profileData = await getProfileData(); dispatch(updateProfileData(profileData)); - if (profileData.characters.length > 0) { - dispatch(updateSelectedCharacter(profileData.characters[0])); + let targetCharacter = profileData.characters[0]; + let sharedExotic: ManifestExoticArmor | undefined = undefined; + + // if navigated here using a share link + const sharedLoadoutLink = localStorage.getItem('lastShared'); + + if (sharedLoadoutLink !== '' && sharedLoadoutLink !== null) { + const sharedLoadoutDto = decodeLoadout(sharedLoadoutLink!); + setSharedLoadoutDto(sharedLoadoutDto); + + const sharedClassCharacter = profileData.characters.find( + (character: Character) => character.class === sharedLoadoutDto.characterClass + ); + + if (sharedClassCharacter) { + targetCharacter = sharedClassCharacter; + } else { + console.log('Missing required character class'); + } + + sharedExotic = await db.manifestExoticArmorCollection + .where('itemHash') + .equals(Number(sharedLoadoutDto.selectedExoticItemHash)) + .first(); + + if (sharedExotic === undefined || sharedExotic?.isOwned === false) { + console.log('You do not own required exotic'); + } } - }; - updateProfile().catch(console.error); + dispatch(updateSelectedCharacter(targetCharacter)); - // if navigated here using a share link - if (localStorage.getItem('lastShared') !== '') { - } + if (sharedExotic) + dispatch( + updateSelectedExoticItemHash({ + itemHash: sharedExotic.itemHash, + slot: sharedExotic.slot as armor, + }) + ); + }; - setDataLoading(false); + updateProfile().catch(console.error); }, []); useEffect(() => { + const initSharedSubclass = async ( + sharedLoadoutDto: SharedLoadoutDto, + selectedCharacter: Character + ) => { + const damageType = sharedLoadoutDto.subclass.damageType; + if (Object.keys(selectedCharacter.subclasses).includes(String(damageType))) { + setSelectedSubclass(selectedCharacter.subclasses[damageType]!); + setLastNonPrismaticSubclass(selectedCharacter.subclasses[damageType]!); + + // set subclass abilities + dispatch( + updateSubclass({ + subclass: selectedCharacter.subclasses[damageType], + }) + ); + dispatch( + updateSubclassMods({ + category: 'SUPERS', + mods: await db.manifestSubclassModDef + .where('itemHash') + .equals(sharedLoadoutDto.subclass.super) + .toArray(), + }) + ); + dispatch( + updateSubclassMods({ + category: 'MOVMENT_ABILITIES', + mods: await db.manifestSubclassModDef + .where('itemHash') + .equals(sharedLoadoutDto.subclass.movementAbility) + .toArray(), + }) + ); + dispatch( + updateSubclassMods({ + category: 'MELEE_ABILITY', + mods: await db.manifestSubclassModDef + .where('itemHash') + .equals(sharedLoadoutDto.subclass.meleeAbility) + .toArray(), + }) + ); + dispatch( + updateSubclassMods({ + category: 'CLASS_ABILITIES', + mods: await db.manifestSubclassModDef + .where('itemHash') + .equals(sharedLoadoutDto.subclass.classAbility) + .toArray(), + }) + ); + dispatch( + updateSubclassMods({ + category: 'GRENADES', + mods: await db.manifestSubclassModDef + .where('itemHash') + .equals(sharedLoadoutDto.subclass.grenade) + .toArray(), + }) + ); + dispatch( + updateSubclassMods({ + category: 'ASPECTS', + mods: await db.manifestSubclassAspectsDef + .where('itemHash') + .anyOf(sharedLoadoutDto.subclass.aspects) + .toArray(), + }) + ); + dispatch( + updateSubclassMods({ + category: 'FRAGMENTS', + mods: await db.manifestSubclassFragmentsDef + .where('itemHash') + .anyOf(sharedLoadoutDto.subclass.fragments) + .toArray(), + }) + ); + } + }; + if (selectedCharacter) { dispatch(resetLoadout()); dispatch(updateLoadoutCharacter(selectedCharacter)); setSubclasses(selectedCharacter.subclasses); - const keys = Object.keys(selectedCharacter.subclasses); - - for (let i = 0; i < keys.length; i++) { - if ( - selectedCharacter.subclasses[Number(keys[i])] !== undefined && - selectedCharacter.subclasses[Number(keys[i])]!.damageType !== DAMAGE_TYPE.KINETIC - ) { - setSelectedSubclass(selectedCharacter.subclasses[Number(keys[i])]!); - setLastNonPrismaticSubclass(selectedCharacter.subclasses[Number(keys[i])]!); - dispatch( - updateSubclass({ - subclass: selectedCharacter.subclasses[Number(keys[i])], - }) - ); - break; + if (sharedLoadoutDto) { + initSharedSubclass(sharedLoadoutDto, selectedCharacter).catch(console.error); + } else { + const keys = Object.keys(selectedCharacter.subclasses); + + for (let i = 0; i < keys.length; i++) { + if ( + selectedCharacter.subclasses[Number(keys[i])] !== undefined && + selectedCharacter.subclasses[Number(keys[i])]!.damageType !== DAMAGE_TYPE.KINETIC + ) { + setSelectedSubclass(selectedCharacter.subclasses[Number(keys[i])]!); + setLastNonPrismaticSubclass(selectedCharacter.subclasses[Number(keys[i])]!); + dispatch( + updateSubclass({ + subclass: selectedCharacter.subclasses[Number(keys[i])], + }) + ); + break; + } } } } - }, [selectedCharacter, dispatch]); + }, [selectedCharacter, sharedLoadoutDto, dispatch]); const permutations = useMemo(() => { if (selectedCharacter && selectedExotic !== undefined) { @@ -188,14 +327,60 @@ export const Dashboard: React.FC = () => { }, [selectedCharacter, selectedExotic, selectedExoticClassCombo]); const filteredPermutations = useMemo(() => { - if (permutations && selectedValues) { + let filtered: FilteredPermutation[] | null = null; + + if (permutations && sharedLoadoutDto) { + const decodedLoadoutData: DecodedLoadoutData = { + selectedExoticItemHash: sharedLoadoutDto.selectedExoticItemHash, + selectedValues: { + mobility: sharedLoadoutDto.selectedValues.mobility || 0, + resilience: sharedLoadoutDto.selectedValues.resilience || 0, + recovery: sharedLoadoutDto.selectedValues.recovery || 0, + discipline: sharedLoadoutDto.selectedValues.discipline || 0, + intellect: sharedLoadoutDto.selectedValues.intellect || 0, + strength: sharedLoadoutDto.selectedValues.strength || 0, + }, + statPriority: sharedLoadoutDto.statPriority as StatName[], + characterClass: sharedLoadoutDto.characterClass as CharacterClass, + }; + setGeneratingPermutations(true); + const sharedLoadoutPermutation = filterFromSharedLoadout(decodedLoadoutData, permutations); + filtered = sharedLoadoutPermutation === null ? null : [sharedLoadoutPermutation]; + } else if (permutations && selectedValues) { setGeneratingPermutations(true); - const filtered = filterPermutations(permutations, selectedValues); - setGeneratingPermutations(false); - return filtered; + filtered = filterPermutations(permutations, selectedValues); } - return null; - }, [permutations, selectedValues]); + + setGeneratingPermutations(false); + + return filtered; + }, [permutations, selectedValues, sharedLoadoutDto]); + + useEffect(() => { + if (filteredPermutations && sharedLoadoutDto) { + openLoadoutCustomization(filteredPermutations[0]).catch(console.error); + localStorage.removeItem('lastShared'); + } + }, [filteredPermutations, sharedLoadoutDto]); + + async function openLoadoutCustomization(filteredPermutation: FilteredPermutation) { + dispatch(resetLoadoutArmorMods()); + dispatch(updateLoadoutArmor(filteredPermutation.permutation)); + let requiredMods: { mod: ManifestArmorStatMod; equipped: boolean }[] = []; + + for (const [stat, costs] of Object.entries(filteredPermutation.modsArray)) { + for (const cost of costs) { + const mod = await db.manifestArmorStatModDef + .where(stat + 'Mod') + .equals(cost) + .first(); + if (mod !== undefined) requiredMods.push({ mod: mod, equipped: false }); + } + } + + dispatch(updateRequiredStatMods(requiredMods)); + setShowLoadoutCustomization(true); + } const handleCharacterClick = (character: Character) => { dispatch(updateSelectedCharacter(character)); @@ -227,33 +412,24 @@ export const Dashboard: React.FC = () => { setShowAbilitiesModification(true); }; - const handleBackClick = () => { - setShowAbilitiesModification(false); - }; - - const handlePermutationClick = () => { - setShowArmorCustomization(true); - }; - - const handleLoadoutCustomizationBackClick = () => { - setShowArmorCustomization(false); - }; - - return !dataLoading ? ( + return ( {showAbilitiesModification && customizingSubclass ? ( setShowAbilitiesModification(false)} subclass={customizingSubclass} screenshot={customizingSubclass.subclass.screenshot} /> - ) : showArmorCustomization ? ( + ) : showLoadoutCustomization ? ( { + setShowLoadoutCustomization(false); + setSharedLoadoutDto(undefined); + }} screenshot={selectedSubclass?.subclass.screenshot || ''} subclass={selectedSubclass!} /> - ) : ( + ) : sharedLoadoutDto === undefined && selectedCharacter && selectedSubclass ? ( <> {selectedCharacter?.emblem?.secondarySpecial && ( { ) : filteredPermutations ? ( ) : (

Loading....

@@ -302,10 +478,10 @@ export const Dashboard: React.FC = () => { + ) : ( +
loading...
)}
- ) : ( -
loading...
); }; diff --git a/src/features/armor-optimization/StatsTable.tsx b/src/features/armor-optimization/StatsTable.tsx index a3a0ca5..53563a9 100644 --- a/src/features/armor-optimization/StatsTable.tsx +++ b/src/features/armor-optimization/StatsTable.tsx @@ -5,11 +5,6 @@ import ChevronLeftIcon from '@mui/icons-material/ChevronLeft'; import ChevronRightIcon from '@mui/icons-material/ChevronRight'; import { FilteredPermutation, DestinyArmor } from '../../types/d2l-types'; import { useDispatch } from 'react-redux'; -import { - resetLoadoutArmorMods, - updateLoadoutArmor, - updateRequiredStatMods, -} from '../../store/LoadoutReducer'; import ArmorIcon from '../../components/ArmorIcon'; import { STAT_MOD_HASHES, STATS } from '../../lib/bungie_api/constants'; import { db } from '../../store/db'; @@ -17,7 +12,7 @@ import { ManifestArmorStatMod } from '../../types/manifest-types'; interface StatsTableProps { permutations: FilteredPermutation[]; - onPermutationClick: () => void; + onPermutationClick: (filteredPermutation: FilteredPermutation) => void; } const StatsTableContainer = styled(Box)(({ theme }) => ({ @@ -190,22 +185,7 @@ const StatsTable: React.FC = ({ permutations, onPermutationClic { - dispatch(resetLoadoutArmorMods()); - dispatch(updateLoadoutArmor(perm.permutation)); - let requiredMods: { mod: ManifestArmorStatMod; equipped: boolean }[] = []; - - for (const [stat, costs] of Object.entries(perm.modsArray)) { - for (const cost of costs) { - const mod = await db.manifestArmorStatModDef - .where(stat + 'Mod') - .equals(cost) - .first(); - if (mod !== undefined) requiredMods.push({ mod: mod, equipped: false }); - } - } - - dispatch(updateRequiredStatMods(requiredMods)); - onPermutationClick(); + await onPermutationClick(perm); }} > diff --git a/src/features/armor-optimization/filter-permutations.ts b/src/features/armor-optimization/filter-permutations.ts index 39c3204..814d0f9 100644 --- a/src/features/armor-optimization/filter-permutations.ts +++ b/src/features/armor-optimization/filter-permutations.ts @@ -1,4 +1,9 @@ -import { DestinyArmor, FilteredPermutation } from '../../types/d2l-types'; +import { + DecodedLoadoutData, + DestinyArmor, + FilteredPermutation, + StatName, +} from '../../types/d2l-types'; import { precalculatedModCombinations } from './precalculatedModCombinations'; interface SelectedThresholds { @@ -89,3 +94,62 @@ export const filterPermutations = ( return results; }; + +export function filterFromSharedLoadout( + decodedLoadout: DecodedLoadoutData, + permutations: DestinyArmor[][] +): FilteredPermutation | null { + console.log('Starting findMatchingArmorSet with decoded loadout:', decodedLoadout); + + // Start with only the highest priority stat + let currentThresholds: { [K in StatName]: number } = { + mobility: 0, + resilience: 0, + recovery: 0, + discipline: 0, + intellect: 0, + strength: 0, + }; + + for (let priorityIndex = 0; priorityIndex < decodedLoadout.statPriority.length; priorityIndex++) { + const currentStat = decodedLoadout.statPriority[priorityIndex]; + currentThresholds[currentStat] = 100; + + console.log(`\nConsidering stat: ${currentStat}`); + console.log('Current thresholds:', currentThresholds); + + let found = false; + while (!found) { + console.log('Filtering permutations...'); + const filteredPermutations = filterPermutations(permutations, currentThresholds); + console.log(`Found ${filteredPermutations.length} matching permutations`); + + if (filteredPermutations.length > 0) { + found = true; + if (priorityIndex === decodedLoadout.statPriority.length - 1) { + // If we're on the last stat and found a match, we're done + console.log('Match found! Returning best matching permutation.'); + console.log('Matched Permutation Details:'); + console.log('Mods Array:', filteredPermutations[0].modsArray); + return filteredPermutations[0]; + } + } else { + // Reduce the threshold of the current stat + currentThresholds[currentStat] = Math.max(0, currentThresholds[currentStat] - 10); + console.log(`Reduced ${currentStat} threshold to ${currentThresholds[currentStat]}`); + + if (currentThresholds[currentStat] === 0) { + // If we've reduced the current stat to 0 and still no match, move to the next stat + break; + } + } + } + + if (!found) { + console.log(`Could not find a match considering up to ${currentStat}`); + break; + } + } + + return null; +} diff --git a/src/features/loadouts/components/ShareLoadout.tsx b/src/features/loadouts/components/ShareLoadout.tsx index b192c88..67df154 100644 --- a/src/features/loadouts/components/ShareLoadout.tsx +++ b/src/features/loadouts/components/ShareLoadout.tsx @@ -3,7 +3,6 @@ import { useSelector, useDispatch } from 'react-redux'; import { RootState, AppDispatch } from '../../../store/index'; import { createSelector } from '@reduxjs/toolkit'; import { encodeLoadout, decodeLoadout } from '../util/loadout-encoder'; -import { findMatchingArmorSet, DecodedLoadoutInfo } from './findMatchingArmorSet'; import { Button, TextField, @@ -23,7 +22,12 @@ import CloseIcon from '@mui/icons-material/Close'; import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'; import { styled } from '@mui/material/styles'; -import { CharacterClass, FilteredPermutation, StatName } from '../../../types/d2l-types'; +import { + CharacterClass, + DecodedLoadoutData, + FilteredPermutation, + StatName, +} from '../../../types/d2l-types'; import { SharedLoadoutDto } from '../types'; const StyledDialog = styled(Dialog)(({ theme }) => ({ @@ -131,18 +135,12 @@ const ArrowContainer = styled(Box)(({ theme }) => ({ })); const statIcons: Record = { - mobility: - 'https://www.bungie.net/common/destiny2_content/icons/e26e0e93a9daf4fdd21bf64eb9246340.png', - resilience: - 'https://www.bungie.net/common/destiny2_content/icons/202ecc1c6febeb6b97dafc856e863140.png', - recovery: - 'https://www.bungie.net/common/destiny2_content/icons/128eee4ee7fc127851ab32eac6ca91cf.png', - discipline: - 'https://www.bungie.net/common/destiny2_content/icons/79be2d4adef6a19203f7385e5c63b45b.png', - intellect: - 'https://www.bungie.net/common/destiny2_content/icons/d1c154469670e9a592c9d4cbdcae5764.png', - strength: - 'https://www.bungie.net/common/destiny2_content/icons/ea5af04ccd6a3470a44fd7bb0f66e2f7.png', + mobility: 'src/assets/mob.png', + resilience: 'src/assets/res.png', + recovery: 'src/assets/rec.png', + discipline: 'src/assets/disc.png', + intellect: 'src/assets/int.png', + strength: 'src/assets/str.png', }; const selectLoadoutState = createSelector( @@ -214,15 +212,11 @@ const StatPriorityList: React.FC<{ ); }); const ShareLoadout: React.FC = () => { - const dispatch = useDispatch(); - const state = useSelector((state: RootState) => state); const loadoutState = useSelector(selectLoadoutState); const selectedCharacterClass = useSelector(selectSelectedCharacterClass); const [open, setOpen] = useState(false); const [shareLink, setShareLink] = useState(''); - const [parsedLink, setParsedLink] = useState(''); - const [matchingArmorSet, setMatchingArmorSet] = useState(null); const [statPriority, setStatPriority] = useState([ 'mobility', 'resilience', @@ -248,6 +242,7 @@ const ShareLoadout: React.FC = () => { legs: loadoutState.legArmorMods.map((mod) => mod.itemHash), }, subclass: { + damageType: loadoutState.subclassConfig.subclass.damageType, super: loadoutState.subclassConfig.super?.itemHash ?? 0, aspects: loadoutState.subclassConfig.aspects.map((aspect) => aspect.itemHash), fragments: loadoutState.subclassConfig.fragments.map((fragment) => fragment.itemHash), @@ -267,60 +262,12 @@ const ShareLoadout: React.FC = () => { console.log('Generated link:', shareableLink); }; - const parseLink = () => { - try { - const url = new URL(shareLink); - const encodedData = url.searchParams.get('d'); - if (!encodedData) { - throw new Error('Invalid loadout link'); - } - const decodedData = decodeLoadout(encodedData); - setParsedLink(JSON.stringify(decodedData, null, 2)); - console.log('Parsed loadout:', decodedData); - } catch (error) { - console.error('Error parsing loadout link:', error); - setParsedLink('Error parsing link'); - } - }; - const copyToClipboard = () => { navigator.clipboard.writeText(shareLink).then(() => { alert('Link copied to clipboard!'); }); }; - const findMatchingSet = () => { - try { - const url = new URL(shareLink); - const encodedData = url.searchParams.get('d'); - if (!encodedData) { - throw new Error('Invalid loadout link'); - } - const decodedLoadout = decodeLoadout(encodedData); - - const decodedLoadoutInfo: DecodedLoadoutInfo = { - selectedExoticItemHash: decodedLoadout.selectedExoticItemHash, - selectedValues: { - mobility: decodedLoadout.selectedValues.mobility || 0, - resilience: decodedLoadout.selectedValues.resilience || 0, - recovery: decodedLoadout.selectedValues.recovery || 0, - discipline: decodedLoadout.selectedValues.discipline || 0, - intellect: decodedLoadout.selectedValues.intellect || 0, - strength: decodedLoadout.selectedValues.strength || 0, - }, - statPriority: decodedLoadout.statPriority as StatName[], - characterClass: decodedLoadout.characterClass as CharacterClass, - }; - - const matchingSet = findMatchingArmorSet(decodedLoadoutInfo, state); - setMatchingArmorSet(matchingSet); - console.log('Matching armor set:', matchingSet); - } catch (error) { - console.error('Error finding matching armor set:', error); - setMatchingArmorSet(null); - } - }; - return ( <> Share Loadout @@ -366,43 +313,9 @@ const ShareLoadout: React.FC = () => { Copy Link - - Parse Link - - Find Matching Set )} - {parsedLink && ( - -

Parsed Loadout:

- -
- )} - {matchingArmorSet && ( - -

Matching Armor Set:

- -
- )} Close diff --git a/src/features/loadouts/components/findMatchingArmorSet.ts b/src/features/loadouts/components/findMatchingArmorSet.ts deleted file mode 100644 index 42c8153..0000000 --- a/src/features/loadouts/components/findMatchingArmorSet.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { RootState } from "../../../store"; -import { CharacterClass, FilteredPermutation, DestinyArmor, StatName } from "../../../types/d2l-types"; -import { filterPermutations } from "../../armor-optimization/filter-permutations"; -import { generatePermutations } from "../../armor-optimization/generate-permutations"; - - - -export interface DecodedLoadoutInfo { - selectedExoticItemHash: string; - selectedValues: { - [K in StatName]: number; - }; - statPriority: StatName[]; - characterClass: CharacterClass; -} - -const logArmorSet = (armorSet: DestinyArmor[]) => { - console.log("Armor Set Details:"); - armorSet.forEach((piece, index) => { - console.log(`Piece ${index + 1}: ${piece.name}`); - console.log(` Mobility: ${piece.mobility}, Resilience: ${piece.resilience}, Recovery: ${piece.recovery}`); - console.log(` Discipline: ${piece.discipline}, Intellect: ${piece.intellect}, Strength: ${piece.strength}`); - }); -}; - -export const findMatchingArmorSet = ( - decodedLoadout: DecodedLoadoutInfo, - state: RootState -): FilteredPermutation | null => { - console.log("Starting findMatchingArmorSet with decoded loadout:", decodedLoadout); - - const matchingCharacter = state.profile.profileData.characters.find( - char => char.class.toLowerCase() === decodedLoadout.characterClass.toLowerCase() - ); - - if (!matchingCharacter) { - console.error('No matching character found'); - return null; - } - - console.log("Generating permutations..."); - const permutations = generatePermutations(matchingCharacter.armor, decodedLoadout.selectedExoticItemHash); - console.log(`Generated ${permutations.length} permutations`); - - // Initialize all stats to 100 - let allStats: { [K in StatName]: number } = { - mobility: 100, - resilience: 100, - recovery: 100, - discipline: 100, - intellect: 100, - strength: 100 - }; - - // Start with only the highest priority stat - let currentThresholds: { [K in StatName]: number } = { - mobility: 0, - resilience: 0, - recovery: 0, - discipline: 0, - intellect: 0, - strength: 0 - }; - - for (let priorityIndex = 0; priorityIndex < decodedLoadout.statPriority.length; priorityIndex++) { - const currentStat = decodedLoadout.statPriority[priorityIndex]; - currentThresholds[currentStat] = 100; - - console.log(`\nConsidering stat: ${currentStat}`); - console.log("Current thresholds:", currentThresholds); - - let found = false; - while (!found) { - console.log("Filtering permutations..."); - const filteredPermutations = filterPermutations(permutations, currentThresholds); - console.log(`Found ${filteredPermutations.length} matching permutations`); - - if (filteredPermutations.length > 0) { - found = true; - if (priorityIndex === decodedLoadout.statPriority.length - 1) { - // If we're on the last stat and found a match, we're done - console.log("Match found! Returning best matching permutation."); - console.log("Matched Permutation Details:"); - logArmorSet(filteredPermutations[0].permutation); - console.log("Mods Array:", filteredPermutations[0].modsArray); - return filteredPermutations[0]; - } - } else { - // Reduce the threshold of the current stat - currentThresholds[currentStat] = Math.max(0, currentThresholds[currentStat] - 10); - console.log(`Reduced ${currentStat} threshold to ${currentThresholds[currentStat]}`); - - if (currentThresholds[currentStat] === 0) { - // If we've reduced the current stat to 0 and still no match, move to the next stat - break; - } - } - } - - if (!found) { - console.log(`Could not find a match considering up to ${currentStat}`); - break; - } - } - - return null; -}; \ No newline at end of file diff --git a/src/features/loadouts/types/index.ts b/src/features/loadouts/types/index.ts index c735638..665af4b 100644 --- a/src/features/loadouts/types/index.ts +++ b/src/features/loadouts/types/index.ts @@ -19,6 +19,7 @@ export type SharedLoadoutDto = { legs: number[]; }; subclass: { + damageType: number; super: number; aspects: number[]; fragments: number[]; diff --git a/src/features/loadouts/util/loadout-encoder.ts b/src/features/loadouts/util/loadout-encoder.ts index 84625b3..5f727bf 100644 --- a/src/features/loadouts/util/loadout-encoder.ts +++ b/src/features/loadouts/util/loadout-encoder.ts @@ -64,6 +64,7 @@ export function encodeLoadout(loadout: SharedLoadoutDto): string { // Encode subclass const encodedSubclass = [ + encodeNumber(subclass.damageType), encodeNumber(subclass.super), subclass.aspects.map(encodeNumber).join(','), subclass.fragments.map(encodeNumber).join(','), @@ -117,13 +118,14 @@ export function decodeLoadout(encoded: string): SharedLoadoutDto { const subclassParts = encodedSubclass.split('|'); const subclass = { - super: decodeNumber(subclassParts[0]), - aspects: subclassParts[1].split(',').map(decodeNumber), - fragments: subclassParts[2].split(',').map(decodeNumber), - classAbility: decodeNumber(subclassParts[3]), - meleeAbility: decodeNumber(subclassParts[4]), - movementAbility: decodeNumber(subclassParts[5]), - grenade: decodeNumber(subclassParts[6]), + damageType: decodeNumber(subclassParts[0]), + super: decodeNumber(subclassParts[1]), + aspects: subclassParts[2].split(',').map(decodeNumber), + fragments: subclassParts[3].split(',').map(decodeNumber), + classAbility: decodeNumber(subclassParts[4]), + meleeAbility: decodeNumber(subclassParts[5]), + movementAbility: decodeNumber(subclassParts[6]), + grenade: decodeNumber(subclassParts[7]), }; const selectedExoticItemHash = decodeNumber(encodedExotic).toString(); diff --git a/src/features/loadouts/util/loadout-utils.ts b/src/features/loadouts/util/loadout-utils.ts index cfa1fd1..8b732a4 100644 --- a/src/features/loadouts/util/loadout-utils.ts +++ b/src/features/loadouts/util/loadout-utils.ts @@ -1,9 +1,14 @@ import { ARMOR_ARRAY, DAMAGE_TYPE } from '../../../lib/bungie_api/constants'; import { snapShotLoadoutRequest } from '../../../lib/bungie_api/requests'; +import { db } from '../../../store/db'; import { + armor, armorMods, + Character, DestinyArmor, + FilteredPermutation, Loadout, + StatName, Subclass, SubclassConfig, } from '../../../types/d2l-types'; @@ -14,6 +19,9 @@ import { ManifestPlug, ManifestStatPlug, } from '../../../types/manifest-types'; +import { filterPermutations } from '../../armor-optimization/filter-permutations'; +import { generatePermutations } from '../../armor-optimization/generate-permutations'; +import { DecodedLoadoutInfo } from '../components/findMatchingArmorSet'; import { EquipResult, setState } from '../types'; import { ArmorEquipper } from './armor-equipper'; import { SubclassEquipper } from './subclass-equipper'; diff --git a/src/types/d2l-types.ts b/src/types/d2l-types.ts index 09cde12..8918f21 100644 --- a/src/types/d2l-types.ts +++ b/src/types/d2l-types.ts @@ -12,7 +12,13 @@ export type CharacterClass = 'warlock' | 'hunter' | 'titan' | ''; export type DamageType = 1 | 2 | 3 | 4 | 5 | 6 | 7; -export type StatName = 'mobility' | 'resilience' | 'recovery' | 'discipline' | 'intellect' | 'strength'; +export type StatName = + | 'mobility' + | 'resilience' + | 'recovery' + | 'discipline' + | 'intellect' + | 'strength'; export type armor = | ARMOR.HELMET @@ -96,6 +102,15 @@ export type Loadout = { subclassConfig: SubclassConfig; }; +export interface DecodedLoadoutData { + selectedExoticItemHash: string; + selectedValues: { + [K in StatName]: number; + }; + statPriority: StatName[]; + characterClass: CharacterClass; +} + export type ProfileData = { characters: Character[]; }; From 9d97d234f50793c02df8f8fc81a34cc2dcc4f9b5 Mon Sep 17 00:00:00 2001 From: dragoni7 Date: Fri, 6 Sep 2024 14:13:24 -0700 Subject: [PATCH 3/7] renaming fixes --- src/app/routes/Landing.tsx | 2 +- src/app/routes/index.tsx | 2 +- src/features/auth/BungieLogin.tsx | 2 +- src/lib/bungie_api/{Authorization.tsx => a.tsx} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename src/lib/bungie_api/{Authorization.tsx => a.tsx} (100%) diff --git a/src/app/routes/Landing.tsx b/src/app/routes/Landing.tsx index bc88178..0ea0f69 100644 --- a/src/app/routes/Landing.tsx +++ b/src/app/routes/Landing.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react'; import { useNavigate } from 'react-router'; import BungieLogin from '../../features/auth/BungieLogin'; import { regenerateTokens } from '../../lib/bungie_api/token-services'; -import { isAuthenticated } from '../../lib/bungie_api/Authorization'; +import { isAuthenticated } from '../../lib/bungie_api/a'; import { Container, Grid, Paper, Box, Typography } from '@mui/material'; import pyramidBackground from '../../assets/pyramid.jpg'; import FeatureSlider from '../../components/FeatureSlider'; diff --git a/src/app/routes/index.tsx b/src/app/routes/index.tsx index a05debc..dddc548 100644 --- a/src/app/routes/index.tsx +++ b/src/app/routes/index.tsx @@ -1,6 +1,6 @@ import { createBrowserRouter } from 'react-router-dom'; import { Dashboard } from './Dashboard'; -import { ProtectedRoute } from '../../lib/bungie_api/Authorization'; +import { ProtectedRoute } from '../../lib/bungie_api/a'; export const createRouter = () => { return createBrowserRouter([ diff --git a/src/features/auth/BungieLogin.tsx b/src/features/auth/BungieLogin.tsx index ff745f8..9b84052 100644 --- a/src/features/auth/BungieLogin.tsx +++ b/src/features/auth/BungieLogin.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { authenticate } from '../../lib/bungie_api/Authorization'; +import { authenticate } from '../../lib/bungie_api/a'; import { Button } from '@mui/material'; const BungieLogin: React.FC = () => { diff --git a/src/lib/bungie_api/Authorization.tsx b/src/lib/bungie_api/a.tsx similarity index 100% rename from src/lib/bungie_api/Authorization.tsx rename to src/lib/bungie_api/a.tsx From 63e6a12a9886d10f840fe85aff996b8c59ae53b1 Mon Sep 17 00:00:00 2001 From: dragoni7 Date: Fri, 6 Sep 2024 14:13:49 -0700 Subject: [PATCH 4/7] rename authorization --- src/app/routes/Landing.tsx | 2 +- src/app/routes/index.tsx | 2 +- src/features/auth/BungieLogin.tsx | 2 +- src/lib/bungie_api/{a.tsx => authorization.tsx} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename src/lib/bungie_api/{a.tsx => authorization.tsx} (100%) diff --git a/src/app/routes/Landing.tsx b/src/app/routes/Landing.tsx index 0ea0f69..988ee4d 100644 --- a/src/app/routes/Landing.tsx +++ b/src/app/routes/Landing.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react'; import { useNavigate } from 'react-router'; import BungieLogin from '../../features/auth/BungieLogin'; import { regenerateTokens } from '../../lib/bungie_api/token-services'; -import { isAuthenticated } from '../../lib/bungie_api/a'; +import { isAuthenticated } from '../../lib/bungie_api/authorization'; import { Container, Grid, Paper, Box, Typography } from '@mui/material'; import pyramidBackground from '../../assets/pyramid.jpg'; import FeatureSlider from '../../components/FeatureSlider'; diff --git a/src/app/routes/index.tsx b/src/app/routes/index.tsx index dddc548..84b56e9 100644 --- a/src/app/routes/index.tsx +++ b/src/app/routes/index.tsx @@ -1,6 +1,6 @@ import { createBrowserRouter } from 'react-router-dom'; import { Dashboard } from './Dashboard'; -import { ProtectedRoute } from '../../lib/bungie_api/a'; +import { ProtectedRoute } from '../../lib/bungie_api/authorization'; export const createRouter = () => { return createBrowserRouter([ diff --git a/src/features/auth/BungieLogin.tsx b/src/features/auth/BungieLogin.tsx index 9b84052..79ef478 100644 --- a/src/features/auth/BungieLogin.tsx +++ b/src/features/auth/BungieLogin.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { authenticate } from '../../lib/bungie_api/a'; +import { authenticate } from '../../lib/bungie_api/authorization'; import { Button } from '@mui/material'; const BungieLogin: React.FC = () => { diff --git a/src/lib/bungie_api/a.tsx b/src/lib/bungie_api/authorization.tsx similarity index 100% rename from src/lib/bungie_api/a.tsx rename to src/lib/bungie_api/authorization.tsx From e39604264d63049256dc78fb15e3d391c23b9e38 Mon Sep 17 00:00:00 2001 From: dragoni7 Date: Fri, 6 Sep 2024 14:15:51 -0700 Subject: [PATCH 5/7] clean up stats table --- .../armor-optimization/StatsTable.tsx | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/features/armor-optimization/StatsTable.tsx b/src/features/armor-optimization/StatsTable.tsx index 53563a9..926f5a4 100644 --- a/src/features/armor-optimization/StatsTable.tsx +++ b/src/features/armor-optimization/StatsTable.tsx @@ -8,7 +8,6 @@ import { useDispatch } from 'react-redux'; import ArmorIcon from '../../components/ArmorIcon'; import { STAT_MOD_HASHES, STATS } from '../../lib/bungie_api/constants'; import { db } from '../../store/db'; -import { ManifestArmorStatMod } from '../../types/manifest-types'; interface StatsTableProps { permutations: FilteredPermutation[]; @@ -112,7 +111,6 @@ const TableFooter = styled(Typography)(({ theme }) => ({ })); const StatsTable: React.FC = ({ permutations, onPermutationClick }) => { - const dispatch = useDispatch(); const [currentPage, setCurrentPage] = useState(0); const itemsPerPage = 4; @@ -151,18 +149,12 @@ const StatsTable: React.FC = ({ permutations, onPermutationClic }, []); const statIcons: Record = { - mobility: - 'https://www.bungie.net/common/destiny2_content/icons/e26e0e93a9daf4fdd21bf64eb9246340.png', - resilience: - 'https://www.bungie.net/common/destiny2_content/icons/202ecc1c6febeb6b97dafc856e863140.png', - recovery: - 'https://www.bungie.net/common/destiny2_content/icons/128eee4ee7fc127851ab32eac6ca91cf.png', - discipline: - 'https://www.bungie.net/common/destiny2_content/icons/79be2d4adef6a19203f7385e5c63b45b.png', - intellect: - 'https://www.bungie.net/common/destiny2_content/icons/d1c154469670e9a592c9d4cbdcae5764.png', - strength: - 'https://www.bungie.net/common/destiny2_content/icons/ea5af04ccd6a3470a44fd7bb0f66e2f7.png', + mobility: 'src/assets/mob.png', + resilience: 'src/assets/res.png', + recovery: 'src/assets/rec.png', + discipline: 'src/assets/disc.png', + intellect: 'src/assets/int.png', + strength: 'src/assets/str.png', }; const formatArmorStats = (armor: DestinyArmor) => { From 52e150f16ef4617044be629fdcc9da186bd49d90 Mon Sep 17 00:00:00 2001 From: dragoni7 Date: Fri, 6 Sep 2024 14:17:34 -0700 Subject: [PATCH 6/7] Remove redudant objects --- .../armor-optimization/NumberBoxes.tsx | 18 +++----- .../subclass/AbilitiesModification.tsx | 41 +------------------ 2 files changed, 7 insertions(+), 52 deletions(-) diff --git a/src/features/armor-optimization/NumberBoxes.tsx b/src/features/armor-optimization/NumberBoxes.tsx index e88d542..05aadd2 100644 --- a/src/features/armor-optimization/NumberBoxes.tsx +++ b/src/features/armor-optimization/NumberBoxes.tsx @@ -53,18 +53,12 @@ const StatIcon = styled('img')({ }); const statIcons: Record = { - mobility: - 'https://www.bungie.net/common/destiny2_content/icons/e26e0e93a9daf4fdd21bf64eb9246340.png', - resilience: - 'https://www.bungie.net/common/destiny2_content/icons/202ecc1c6febeb6b97dafc856e863140.png', - recovery: - 'https://www.bungie.net/common/destiny2_content/icons/128eee4ee7fc127851ab32eac6ca91cf.png', - discipline: - 'https://www.bungie.net/common/destiny2_content/icons/79be2d4adef6a19203f7385e5c63b45b.png', - intellect: - 'https://www.bungie.net/common/destiny2_content/icons/d1c154469670e9a592c9d4cbdcae5764.png', - strength: - 'https://www.bungie.net/common/destiny2_content/icons/ea5af04ccd6a3470a44fd7bb0f66e2f7.png', + mobility: 'src/assets/mob.png', + resilience: 'src/assets/res.png', + recovery: 'src/assets/rec.png', + discipline: 'src/assets/disc.png', + intellect: 'src/assets/int.png', + strength: 'src/assets/str.png', }; const NumberBoxes: React.FC = () => { diff --git a/src/features/subclass/AbilitiesModification.tsx b/src/features/subclass/AbilitiesModification.tsx index 7757f32..40dec83 100644 --- a/src/features/subclass/AbilitiesModification.tsx +++ b/src/features/subclass/AbilitiesModification.tsx @@ -13,51 +13,12 @@ import { ManifestStatPlug, } from '../../types/manifest-types'; import { DamageType, SubclassConfig } from '../../types/d2l-types'; +import { EMPTY_ASPECT, EMPTY_FRAGMENT } from '../../lib/bungie_api/constants'; interface AbilitiesModificationProps { subclass: SubclassConfig; } -export const EMPTY_MANIFEST_PLUG: ManifestPlug = { - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - itemHash: 0, - name: '', - icon: '', -}; - -export const EMPTY_ASPECT: ManifestAspect = { - energyCapacity: 0, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - itemHash: 0, - name: '', - icon: '', -}; - -export const EMPTY_FRAGMENT: ManifestStatPlug = { - mobilityMod: 0, - resilienceMod: 0, - recoveryMod: 0, - disciplineMod: 0, - intellectMod: 0, - strengthMod: 0, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - itemHash: 0, - name: '', - icon: '', -}; - const subclassTypeMap: { [key in DamageType]: string } = { 1: 'PRISMATIC', 2: 'ARC', From bd168dc9738a8680ad4e3dc52b94a8f26342d173 Mon Sep 17 00:00:00 2001 From: dragoni7 Date: Fri, 6 Sep 2024 14:33:24 -0700 Subject: [PATCH 7/7] fixed merge conflicts --- src/app/routes/Dashboard.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/routes/Dashboard.tsx b/src/app/routes/Dashboard.tsx index 23ff7ce..88a76be 100644 --- a/src/app/routes/Dashboard.tsx +++ b/src/app/routes/Dashboard.tsx @@ -42,7 +42,10 @@ import greyBackground from '../../assets/grey.png'; import ExoticSelector from '../../features/armor-optimization/ExoticSelector'; import { DAMAGE_TYPE } from '../../lib/bungie_api/constants'; import { decodeLoadout } from '../../features/loadouts/util/loadout-encoder'; -import { updateSelectedExoticItemHash } from '../../store/DashboardReducer'; +import { + updateSelectedExoticClassCombo, + updateSelectedExoticItemHash, +} from '../../store/DashboardReducer'; import { ManifestArmorStatMod, ManifestExoticArmor } from '../../types/manifest-types'; import { SharedLoadoutDto } from '../../features/loadouts/types';