From 32380a4fa972debf237959c14e0b56dd55d50a79 Mon Sep 17 00:00:00 2001 From: dragoni7 Date: Thu, 19 Sep 2024 13:25:57 -0700 Subject: [PATCH] Added missing comments --- .prettierrc | 6 ++- src/app/routes/Dashboard.tsx | 2 +- .../armor-mods/components/ArmorConfig.tsx | 21 +++----- .../components/ArmorModSelector.tsx | 2 +- src/features/armor-mods/mod-utils.ts | 20 +++++++ .../generate-permutations.ts | 12 ----- .../loadouts/components/ShareLoadout.tsx | 19 +++---- src/features/loadouts/util/armor-equipper.ts | 11 ++++ src/features/loadouts/util/equipper.ts | 14 +++++ src/features/loadouts/util/loadout-encoder.ts | 31 ----------- src/features/loadouts/util/loadout-utils.ts | 41 ++++++++++++++ .../loadouts/util/subclass-equipper.ts | 22 ++++++++ src/hooks/use-artifice-mods.ts | 3 ++ src/hooks/use-stat-mods.ts | 3 ++ src/util/app-utils.ts | 9 ++++ src/util/profile-characters.ts | 54 ++++++++++++++++++- src/util/version-check.ts | 3 ++ 17 files changed, 199 insertions(+), 74 deletions(-) create mode 100644 src/util/app-utils.ts diff --git a/.prettierrc b/.prettierrc index 5ac85e2..6019970 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,4 +1,8 @@ { "printWidth": 100, - "singleQuote": true + "singleQuote": true, + + "importOrder": ["^components/(.*)$", "^[./]"], + "importOrderSeparation": true, + "importOrderSortSpecifiers": true } diff --git a/src/app/routes/Dashboard.tsx b/src/app/routes/Dashboard.tsx index 007b485..674bd3b 100644 --- a/src/app/routes/Dashboard.tsx +++ b/src/app/routes/Dashboard.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState, useMemo } from 'react'; -import { Box, styled } from '@mui/system'; +import { styled } from '@mui/system'; import { useDispatch, useSelector } from 'react-redux'; import { AppDispatch, RootState } from '../../store'; import { generatePermutations } from '../../features/armor-optimization/generate-permutations'; diff --git a/src/features/armor-mods/components/ArmorConfig.tsx b/src/features/armor-mods/components/ArmorConfig.tsx index 9550e3c..3ba1079 100644 --- a/src/features/armor-mods/components/ArmorConfig.tsx +++ b/src/features/armor-mods/components/ArmorConfig.tsx @@ -4,7 +4,7 @@ import ArmorIcon from '../../../components/ArmorIcon'; import { updateLoadoutArmorMods, updateRequiredStatMods } from '../../../store/LoadoutReducer'; import { armorMods, DestinyArmor } from '../../../types/d2l-types'; import ArmorModSelector from './ArmorModSelector'; -import { getModsBySlot } from '../mod-utils'; +import { calculateAvailableEnergy, getModsBySlot } from '../mod-utils'; import { ManifestArmorMod, ManifestArmorStatMod } from '../../../types/manifest-types'; import { Alert, Grid, Fade, Snackbar, SnackbarCloseReason, CircularProgress } from '@mui/material'; import { RootState, store } from '../../../store'; @@ -43,13 +43,6 @@ const ArmorConfig: React.FC = ({ armor, statMods, artificeMods ); }; - const calculateAvailableEnergy = (currentSlot: number) => { - const totalEnergyCost = selectedMods.reduce((total, mod, index) => { - return index !== currentSlot ? total + mod.energyCost : total; - }, 0); - return 10 - totalEnergyCost; // Assuming max energy is 10 - }; - const onSelectMod = async (mod: ManifestArmorMod | ManifestArmorStatMod, slot: number) => { if (selectedMods[slot].itemHash === mod.itemHash) return; @@ -69,7 +62,7 @@ const ArmorConfig: React.FC = ({ armor, statMods, artificeMods return; } - const availableEnergy = calculateAvailableEnergy(slot); + const availableEnergy = calculateAvailableEnergy(slot, selectedMods); if (mod.energyCost > availableEnergy) { setSnackPack((prev) => [ ...prev, @@ -155,7 +148,7 @@ const ArmorConfig: React.FC = ({ armor, statMods, artificeMods selected={selectedMods[0]} mods={statMods} onSelectMod={(mod: ManifestArmorMod | ManifestArmorStatMod) => onSelectMod(mod, 0)} - availableEnergy={calculateAvailableEnergy(0)} + availableEnergy={calculateAvailableEnergy(0, selectedMods)} /> @@ -163,7 +156,7 @@ const ArmorConfig: React.FC = ({ armor, statMods, artificeMods selected={selectedMods[1]} mods={armorMods} onSelectMod={(mod: ManifestArmorMod | ManifestArmorStatMod) => onSelectMod(mod, 1)} - availableEnergy={calculateAvailableEnergy(1)} + availableEnergy={calculateAvailableEnergy(1, selectedMods)} /> @@ -171,7 +164,7 @@ const ArmorConfig: React.FC = ({ armor, statMods, artificeMods selected={selectedMods[2]} mods={armorMods} onSelectMod={(mod: ManifestArmorMod | ManifestArmorStatMod) => onSelectMod(mod, 2)} - availableEnergy={calculateAvailableEnergy(2)} + availableEnergy={calculateAvailableEnergy(2, selectedMods)} /> @@ -179,7 +172,7 @@ const ArmorConfig: React.FC = ({ armor, statMods, artificeMods selected={selectedMods[3]} mods={armorMods} onSelectMod={(mod: ManifestArmorMod | ManifestArmorStatMod) => onSelectMod(mod, 3)} - availableEnergy={calculateAvailableEnergy(3)} + availableEnergy={calculateAvailableEnergy(3, selectedMods)} /> {armor.artifice === true ? ( @@ -190,7 +183,7 @@ const ArmorConfig: React.FC = ({ armor, statMods, artificeMods onSelectMod={(mod: ManifestArmorMod | ManifestArmorStatMod) => onSelectMod(mod, 4) } - availableEnergy={calculateAvailableEnergy(4)} + availableEnergy={calculateAvailableEnergy(4, selectedMods)} /> ) : ( diff --git a/src/features/armor-mods/components/ArmorModSelector.tsx b/src/features/armor-mods/components/ArmorModSelector.tsx index c87a815..328b19e 100644 --- a/src/features/armor-mods/components/ArmorModSelector.tsx +++ b/src/features/armor-mods/components/ArmorModSelector.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useRef, useState } from 'react'; import { Box } from '@mui/system'; import { ManifestArmorMod, ManifestArmorStatMod } from '../../../types/manifest-types'; -import { Tooltip, IconButton, styled } from '@mui/material'; +import { IconButton, styled } from '@mui/material'; import ChevronLeftIcon from '@mui/icons-material/ChevronLeft'; import ChevronRightIcon from '@mui/icons-material/ChevronRight'; import HoverCard from '../../../components/HoverCard'; diff --git a/src/features/armor-mods/mod-utils.ts b/src/features/armor-mods/mod-utils.ts index aa0efb9..73ac1b1 100644 --- a/src/features/armor-mods/mod-utils.ts +++ b/src/features/armor-mods/mod-utils.ts @@ -11,6 +11,10 @@ import { armorMods } from '../../types/d2l-types'; import { Dispatch, UnknownAction } from 'redux'; import { updateLoadoutArmorMods, updateRequiredStatMods } from '../../store/LoadoutReducer'; +/** + * Get armor mods by armor slot + * @returns armor mods array + */ export async function getModsBySlot(slot: string): Promise { const slotMods = await db.manifestArmorModDef .where('category') @@ -32,6 +36,9 @@ export async function getModsBySlot(slot: string): Promise { return slotMods; } +/** + * Auto equips a mod into the loadout config + */ export function autoEquipStatMod( mod: ManifestArmorStatMod, dispatch: Dispatch @@ -65,3 +72,16 @@ export function autoEquipStatMod( return false; } + +/** + * Calculates the available energy out of 10 with equipped mods. + */ +export function calculateAvailableEnergy( + currentSlot: number, + selectedMods: (ManifestArmorMod | ManifestArmorStatMod)[] +): number { + const totalEnergyCost = selectedMods.reduce((total, mod, index) => { + return index !== currentSlot ? total + mod.energyCost : total; + }, 0); + return 10 - totalEnergyCost; // Assuming max energy is 10 +} diff --git a/src/features/armor-optimization/generate-permutations.ts b/src/features/armor-optimization/generate-permutations.ts index 497d8f0..45d3e8e 100644 --- a/src/features/armor-optimization/generate-permutations.ts +++ b/src/features/armor-optimization/generate-permutations.ts @@ -149,18 +149,6 @@ export function generatePermutations( const totalSum = Object.values(totalStats).reduce((a, b) => a + b, 0); - const baseStats = modifiedPermutation.reduce( - (sum, item) => - sum + - item.mobility + - item.resilience + - item.recovery + - item.discipline + - item.intellect + - item.strength, - 0 - ); - if (heap.size() < 30000) { heap.push(modifiedPermutation); } else { diff --git a/src/features/loadouts/components/ShareLoadout.tsx b/src/features/loadouts/components/ShareLoadout.tsx index 41ea17d..bdb701b 100644 --- a/src/features/loadouts/components/ShareLoadout.tsx +++ b/src/features/loadouts/components/ShareLoadout.tsx @@ -1,13 +1,11 @@ import React, { useState } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; -import { RootState, AppDispatch } from '../../../store/index'; +import { useSelector } from 'react-redux'; +import { RootState } from '../../../store/index'; import { createSelector } from '@reduxjs/toolkit'; -import { encodeLoadout, decodeLoadout } from '../util/loadout-encoder'; +import { encodeLoadout } from '../util/loadout-encoder'; import { - Button, TextField, Box, - List, ListItem, ListItemIcon, ListItemText, @@ -26,6 +24,7 @@ import { CharacterClass, StatName } from '../../../types/d2l-types'; import { SharedLoadoutDto } from '../types'; import { D2LButton } from '../../../components/D2LButton'; import { statIcons } from '../../../util/constants'; +import { copyToClipBoard } from '../../../util/app-utils'; const StyledDialog = styled(Dialog)(({ theme }) => ({ '& .MuiDialog-paper': { @@ -183,6 +182,7 @@ const StatPriorityList: React.FC<{ ); }); + const ShareLoadout: React.FC = () => { const loadoutState = useSelector(selectLoadoutState); const selectedCharacterClass = useSelector(selectSelectedCharacterClass); @@ -231,13 +231,6 @@ const ShareLoadout: React.FC = () => { const encodedData = encodeLoadout(dataToShare); const shareableLink = `https://d2loadouts.com/?d=${encodedData}`; setShareLink(shareableLink); - console.log('Generated link:', shareableLink); - }; - - const copyToClipboard = () => { - navigator.clipboard.writeText(shareLink).then(() => { - alert('Link copied to clipboard!'); - }); }; return ( @@ -282,7 +275,7 @@ const ShareLoadout: React.FC = () => { }} /> - + copyToClipBoard(shareLink)} sx={{ mr: 1 }}> Copy Link diff --git a/src/features/loadouts/util/armor-equipper.ts b/src/features/loadouts/util/armor-equipper.ts index 8faeabd..ddafc8c 100644 --- a/src/features/loadouts/util/armor-equipper.ts +++ b/src/features/loadouts/util/armor-equipper.ts @@ -10,6 +10,9 @@ import { ManifestArmorStatMod, ManifestArmorMod } from '../../../types/manifest- import { STATUS } from '../constants'; import { Equipper } from './equipper'; +/** + * Builder for equipping armor and mods and creating equip result array + */ export class ArmorEquipper extends Equipper { private inventorySlots: { [key: string]: any[] }; @@ -64,6 +67,10 @@ export class ArmorEquipper extends Equipper { } } + /** + * Equips an armor piece on the set character, transferring items between vault and character if necessary + * @param armor the armor to equip + */ public async equipArmor(armor: DestinyArmor): Promise { const result = { status: STATUS.SUCCESS, @@ -121,6 +128,10 @@ export class ArmorEquipper extends Equipper { this.result.push(result); } + /** + * Equips armor mods onto an armor piece + * @param mods mods to equip + */ public async equipArmorMods( mods: [string, ManifestArmorMod | ManifestArmorStatMod][] ): Promise { diff --git a/src/features/loadouts/util/equipper.ts b/src/features/loadouts/util/equipper.ts index 9ef29aa..8d64501 100644 --- a/src/features/loadouts/util/equipper.ts +++ b/src/features/loadouts/util/equipper.ts @@ -1,5 +1,8 @@ import { EquipResult } from '../types'; +/** + * Base builder for equipping objects and building equip results + */ export abstract class Equipper { protected characterId: number; @@ -10,16 +13,27 @@ export abstract class Equipper { this.result = []; } + /** + * Set the equipping character + * @param characterId character to equip on + */ public setCharacter(characterId: number): void { this.characterId = characterId; } + /** + * Gets the equip results + * @returns the equip result array + */ public getResult(): EquipResult[] { const temp = this.result; this.reset(); return temp; } + /** + * Resets the results + */ public reset(): void { this.result = []; } diff --git a/src/features/loadouts/util/loadout-encoder.ts b/src/features/loadouts/util/loadout-encoder.ts index 5f727bf..fd4e74a 100644 --- a/src/features/loadouts/util/loadout-encoder.ts +++ b/src/features/loadouts/util/loadout-encoder.ts @@ -1,37 +1,6 @@ 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 - * 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. - * - * Encoding a simple `LoadoutData` object: - * ``` - * const loadout = { - * mods: { helmet: [1], gauntlets: [2], chest: [3], legs: [4] }, - * subclass: { super: 5, aspects: [6], fragments: [7], classAbility: 8, - * meleeAbility: 9, movementAbility: 10, grenade: 11 }, - * selectedExoticItemHash: '12', - * selectedValues: { mobility: 13, resilience: 14 }, - * statPriority: ['mobility', 'resilience'], - * characterClass: 'Titan' - * }; - * ``` - * `encodeLoadout(loadout)` might return: "1|2|3|4~5|6|7|8|9|A|B~C~mob:D,res:E~mobres~Titan". - * Decoding this string with `decodeLoadout` returns the original `LoadoutData` object. - */ - // We'll use a custom base64 encoding to make the strings URL-safe const base64Chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_'; diff --git a/src/features/loadouts/util/loadout-utils.ts b/src/features/loadouts/util/loadout-utils.ts index cfa1fd1..846f822 100644 --- a/src/features/loadouts/util/loadout-utils.ts +++ b/src/features/loadouts/util/loadout-utils.ts @@ -18,6 +18,14 @@ import { EquipResult, setState } from '../types'; import { ArmorEquipper } from './armor-equipper'; import { SubclassEquipper } from './subclass-equipper'; +/** + * Saves a loadout from equipped items to Destiny 2 + * @param characterId character for the loadout + * @param colorHash loadout color identifier hash + * @param iconHash loadout icon identifier hash + * @param loadoutIndex loadout index to replace + * @param nameHash loadout name identifierhash + */ export async function createInGameLoadout( characterId: string, colorHash: number, @@ -28,6 +36,13 @@ export async function createInGameLoadout( await snapShotLoadoutRequest(characterId, colorHash, iconHash, loadoutIndex, nameHash); } +/** + * Equips a loadout, handling armor first then subclass, setting state as it goes. + * @param loadout the loadout to equip + * @param setProcessing the setState for processing object + * @param setEquipStep the setState for equipping item + * @param setResults the setState for results + */ export async function equipLoadout( loadout: Loadout, setProcessing: setState, @@ -71,6 +86,17 @@ export async function equipLoadout( ); } +/** + * Equips armor piece and it's mods, processing exotics last + * @param i armor index + * @param loadout loadout to equip + * @param equipper armor equipper to use + * @param tempEquipped temp equipped array + * @param tempResults temp results array + * @param setProcessing the setState for processing object + * @param setEquipStep the setState for equipping item + * @param setResults the setState for results + */ async function processArmor( i: number, loadout: Loadout, @@ -119,12 +145,27 @@ async function processArmor( } } +/** + * Sorts a mod dicationary + * @param mods mods to sort + * @returns sorted mods array + */ function sortMods(mods: { [key: number]: ManifestArmorMod | ManifestArmorStatMod; }): [string, ManifestArmorMod | ManifestArmorStatMod][] { return Object.entries(mods).sort(([, a], [, b]) => b.energyCost - a.energyCost); } +/** + * Equips subclass config + * @param subclassConfig subclass config to equip + * @param equipper subclass equipper to use + * @param tempEquipped temp equipped array + * @param tempResults temp results array + * @param setProcessing the setState for processing object + * @param setEquipStep the setState for equipping item + * @param setResults the setState for results + */ async function processSubclass( subclassConfig: SubclassConfig, equipper: SubclassEquipper, diff --git a/src/features/loadouts/util/subclass-equipper.ts b/src/features/loadouts/util/subclass-equipper.ts index 8699533..cbf355d 100644 --- a/src/features/loadouts/util/subclass-equipper.ts +++ b/src/features/loadouts/util/subclass-equipper.ts @@ -5,7 +5,14 @@ import { ManifestAspect, ManifestPlug, ManifestStatPlug } from '../../../types/m import { STATUS } from '../constants'; import { Equipper } from './equipper'; +/** + * Builder for equipping subclasses and mods and creating equip result array + */ export class SubclassEquipper extends Equipper { + /** + * Equips a subclass + * @param subclass subclass to equip + */ public async equipSubclass(subclass: Subclass) { const result = { status: STATUS.SUCCESS, @@ -27,6 +34,11 @@ export class SubclassEquipper extends Equipper { this.result.push(result); } + /** + * Equips an ability into a subclass + * @param ability subclass ability to equip + * @param socketArrayIndex index of ability + */ public async equipSubclassAbility(ability: ManifestPlug, socketArrayIndex: number) { const subclass = this.result[0].subject; @@ -60,6 +72,11 @@ export class SubclassEquipper extends Equipper { this.result.push(result); } + /** + * Equips a subclass aspect + * @param aspect aspect to equip + * @param socketArrayIndex aspect index + */ public async equipSubclassAspect(aspect: ManifestAspect, socketArrayIndex: number) { const subclass = this.result[0].subject; @@ -93,6 +110,11 @@ export class SubclassEquipper extends Equipper { this.result.push(result); } + /** + * Equips a subclass fragment + * @param fragment fragment to equip + * @param socketArrayIndex fragment index + */ public async equipSubclassFragments(fragment: ManifestStatPlug, socketArrayIndex: number) { const subclass = this.result[0].subject; diff --git a/src/hooks/use-artifice-mods.ts b/src/hooks/use-artifice-mods.ts index 751184c..7bc2faa 100644 --- a/src/hooks/use-artifice-mods.ts +++ b/src/hooks/use-artifice-mods.ts @@ -3,6 +3,9 @@ import { PLUG_CATEGORY_HASH } from '../lib/bungie_api/constants'; import { db } from '../store/db'; import { ManifestArmorStatMod } from '../types/manifest-types'; +/** + * Uses artifice mods found in the local database + */ export default function useArtificeMods() { const [artificeMods, setArtificeMods] = useState([]); diff --git a/src/hooks/use-stat-mods.ts b/src/hooks/use-stat-mods.ts index d9cdaea..a77d800 100644 --- a/src/hooks/use-stat-mods.ts +++ b/src/hooks/use-stat-mods.ts @@ -3,6 +3,9 @@ import { ManifestArmorStatMod } from '../types/manifest-types'; import { PLUG_CATEGORY_HASH } from '../lib/bungie_api/constants'; import { db } from '../store/db'; +/** + * Use stat mods found in the local database + */ export default function useStatMods() { const [statMods, setStatMods] = useState([]); diff --git a/src/util/app-utils.ts b/src/util/app-utils.ts new file mode 100644 index 0000000..f4fd57c --- /dev/null +++ b/src/util/app-utils.ts @@ -0,0 +1,9 @@ +/** + * Copies a string to the clipboard + * @param data string to copy + */ +export function copyToClipBoard(data: string) { + navigator.clipboard.writeText(data).then(() => { + alert('Link copied to clipboard!'); + }); +} diff --git a/src/util/profile-characters.ts b/src/util/profile-characters.ts index fcc30ad..218298b 100644 --- a/src/util/profile-characters.ts +++ b/src/util/profile-characters.ts @@ -28,12 +28,20 @@ import { } from '../types/d2l-types'; import { updateProfileCharacters } from '../store/ProfileReducer'; +/** + * Updates the character data + * @param dispatch + */ export async function refreshProfileCharacters(dispatch: Dispatch) { const profileCharacters = await getProfileData(); dispatch(updateProfileCharacters(profileCharacters)); } +/** + * Gets the logged in user's character data from profile + * @returns the logged in user's characters + */ export async function getProfileData(): Promise { const profileCharacters: Character[] = []; @@ -216,6 +224,12 @@ export async function getProfileData(): Promise { return profileCharacters; } +/** + * Gets a character's loadouts + * @param loadoutResponse loadout api call response + * @param characterId character to get loadouts from + * @returns loadouts array + */ export function getCharacterLoadoutsFromResponse( loadoutResponse: any, characterId: number @@ -236,6 +250,12 @@ export function getCharacterLoadoutsFromResponse( return loadouts; } +/** + * Initializes a character from api profile response + * @param profileResponse bungie api profile response + * @param key character key + * @returns the created character + */ async function initCharacterFromResponse(profileResponse: any, key: string): Promise { const itemComponents = profileResponse.data.Response.itemComponents; const characterInventories = profileResponse.data.Response.characterInventories.data; @@ -434,6 +454,12 @@ async function initCharacterFromResponse(profileResponse: any, key: string): Pro return character; } +/** + * Creates a subclass config from api response + * @param item subclass + * @param itemComponents itemComponents api response + * @returns a new subclass config or null + */ async function buildSubclassConfig(item: any, itemComponents: any): Promise { const subclassQuery = db.manifestSubclass.where('itemHash').equals(item.itemHash); @@ -451,7 +477,7 @@ async function buildSubclassConfig(item: any, itemComponents: any): Promise void } = { [STAT_MOD_HASHES.INTELLECT_MOD]: (armor: DestinyArmor) => (armor.intellect = armor.intellect - 10), @@ -665,6 +712,11 @@ const modReverseDict: { [key: number]: (armor: DestinyArmor) => void } = { (armor.strength = armor.strength - 3), }; +/** + * Gets a string character class from a class hash + * @param classHash character class hash + * @returns character class string + */ function getCharacterClass(classHash: number) { switch (classHash) { case CLASS_HASH.TITAN: { diff --git a/src/util/version-check.ts b/src/util/version-check.ts index 654d74c..44e4e85 100644 --- a/src/util/version-check.ts +++ b/src/util/version-check.ts @@ -1,5 +1,8 @@ import packageJson from '../../package.json'; +/** + * Clears the local storage on app version update + */ export function handleVersionUpdate() { const storedVersion = localStorage.getItem('version'); if (packageJson.version !== storedVersion || storedVersion === null) {