diff --git a/package.json b/package.json index 9636d4c9..2ba8d21c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tof-tools", - "version": "3.1.0", + "version": "3.1.1", "private": true, "scripts": { "dev": "next dev", diff --git a/src/features/GearOCRModal.tsx b/src/features/GearOCRModal.tsx index 77823953..796ee71f 100644 --- a/src/features/GearOCRModal.tsx +++ b/src/features/GearOCRModal.tsx @@ -147,10 +147,7 @@ export const GearOCRModal = ({ { - tempGearState.type = gearType; - }} - showGearOCRButton={false} + disableGearTypeChange /> )} diff --git a/src/features/GearPiece.tsx b/src/features/GearPiece.tsx index 2d2b1e8f..7a955d4a 100644 --- a/src/features/GearPiece.tsx +++ b/src/features/GearPiece.tsx @@ -14,7 +14,7 @@ import type { ReactNode } from 'react'; import { GearTypeIcon } from '../components/GearTypeIcon/GearTypeIcon'; import { GearTypeSelector } from '../components/GearTypeSelector/GearTypeSelector'; import type { CoreElementalType } from '../constants/elemental-type'; -import { Gear } from '../models/gear'; +import type { Gear } from '../models/gear'; import type { GearType } from '../models/gear-type'; import { getPossibleRandomStatTypes } from '../models/gear-type'; import { RandomStat } from '../models/random-stat'; @@ -29,7 +29,7 @@ import { EmptyStatEditor, StatEditor } from './StatEditor'; export interface GearPieceProps { gearSnap: Gear; gearState: Gear; - showGearOCRButton?: boolean; + showGearOCRButton?: { onGearChangeFromOCR: (gearFromOCR: Gear) => void }; disableGearTypeChange?: boolean; onGearTypeChange?: (gearType: GearType) => void; showSaveGearButton?: Pick; @@ -97,7 +97,7 @@ export const GearPiece = ({ {showGearOCRButton && ( { - Gear.copy(replacementGear, gearState); + showGearOCRButton.onGearChangeFromOCR(replacementGear); }} enforceGearType={ disableGearTypeChange ? gearSnap.type.id : undefined diff --git a/src/features/gear-comparer/LoadoutGear.tsx b/src/features/gear-comparer/LoadoutGear.tsx index e632c5a1..a9931067 100644 --- a/src/features/gear-comparer/LoadoutGear.tsx +++ b/src/features/gear-comparer/LoadoutGear.tsx @@ -25,7 +25,12 @@ export function LoadoutGear() { onGearTypeChange={(gearType) => { gearComparerState.selectedGearTypeId = gearType.id; }} - showGearOCRButton + showGearOCRButton={{ + onGearChangeFromOCR(gearFromOCR) { + gearComparerState.selectedLoadout.gearSet.setGear(gearFromOCR); + gearComparerState.selectedGearTypeId = gearFromOCR.type.id; + }, + }} showStatSummary={elementalType} maxTitanStatsContent={ gearSnap.stars !== 5 && diff --git a/src/features/gear-comparer/ReplacementGear.tsx b/src/features/gear-comparer/ReplacementGear.tsx index 89ca2f9a..5d300bfc 100644 --- a/src/features/gear-comparer/ReplacementGear.tsx +++ b/src/features/gear-comparer/ReplacementGear.tsx @@ -26,7 +26,12 @@ export function ReplacementGear() { onGearTypeChange={(gearType) => { gearComparerState.selectedGearTypeId = gearType.id; }} - showGearOCRButton + showGearOCRButton={{ + onGearChangeFromOCR(gearFromOCR) { + gearComparerState.replacementGearGearSet.setGear(gearFromOCR); + gearComparerState.selectedGearTypeId = gearFromOCR.type.id; + }, + }} showSaveGearButton={{ targetLoadout: gearComparerState.selectedLoadout }} showStatSummary={elementalType} maxTitanStatsContent={ diff --git a/src/features/loadouts/LoadoutGearSet.tsx b/src/features/loadouts/LoadoutGearSet.tsx index 61010402..f7160d9b 100644 --- a/src/features/loadouts/LoadoutGearSet.tsx +++ b/src/features/loadouts/LoadoutGearSet.tsx @@ -31,7 +31,13 @@ export function LoadoutGearSet() { { public get type() { return this._type; } - public set type(type: GearType) { - this._type = type; - this.resetRandomStats(); - } public get stars() { return this._stars; @@ -418,9 +414,10 @@ export class Gear implements Persistable { }; } - // Copy all gear properties over except for the id + /** Copy all gear properties over except for the id, as long as the gear's type is the same */ public static copy(from: Gear, to: Gear) { - to.type = from.type; + if (from.type.id !== to.type.id) return; + to.stars = from.stars; to.randomStats = []; @@ -467,7 +464,7 @@ export class Gear implements Persistable { const gearType = gearTypesLookup.byId[typeId]; this._id = id; - this.type = gearType; + this._type = gearType; this.stars = stars; this.isAugmented = !!isAugmented; this.isTitan = !!isTitan; diff --git a/src/states/gear-comparer.ts b/src/states/gear-comparer.ts index bc8b26b6..ef1ef641 100644 --- a/src/states/gear-comparer.ts +++ b/src/states/gear-comparer.ts @@ -39,6 +39,10 @@ export class GearComparerState implements Persistable { return this.selectedLoadout.getGearValue(this.selectedGearTypeId); } + public get replacementGearGearSet(): GearSet { + return this._replacementGearGearSet; + } + public get replacementGear(): Gear { return this._replacementGearGearSet.getGearByType(this.selectedGearTypeId); } diff --git a/src/states/migrations/state-migrations.ts b/src/states/migrations/state-migrations.ts index 04f9cb0d..89abdc5c 100644 --- a/src/states/migrations/state-migrations.ts +++ b/src/states/migrations/state-migrations.ts @@ -1,5 +1,6 @@ import { initializeChangelogState } from './v1/initialize-changelog-state'; import { migrateTeamsGearSetsStatsToLoadouts } from './v1/migrate-teams-gear-sets-stats-to-loadouts'; +import { fixGearSetsGearTypeData } from './v2/fix-gear-sets-gear-type-data'; export const stateMigrations: StateMigration[] = [ { @@ -9,6 +10,12 @@ export const stateMigrations: StateMigration[] = [ initializeChangelogState(); }, }, + { + version: 2, + migrate: () => { + fixGearSetsGearTypeData(); + }, + }, ]; interface StateMigration { diff --git a/src/states/migrations/v2/fix-gear-sets-gear-type-data.ts b/src/states/migrations/v2/fix-gear-sets-gear-type-data.ts new file mode 100644 index 00000000..23293f92 --- /dev/null +++ b/src/states/migrations/v2/fix-gear-sets-gear-type-data.ts @@ -0,0 +1,66 @@ +import { nanoid } from 'nanoid'; + +import type { GearName } from '../../../constants/gear-types'; +import type { GearSetDtoV2 } from '../../../models/gear-set'; +import type { GearComparerStateDto } from '../../gear-comparer'; +import type { LoadoutsStateDto } from '../../loadouts'; + +/** Data fix for potential issue with gear sets gear type data that may have been introduced in 3.0.0. + * + * A gear set's structure looks something like this: + * { gearsByTypeId: { Helmet: { id: ..., type: 'Helmet', randomStats: ... }, Eyepiece: { id: ..., type: 'Eyepiece', randomStats: ... } }} + * + * In gear comparer's gear OCR feature, a gear's type can be overwritten, causing an issue where a gear of a different type can be in a gearsByTypeId slot of another type. + * + * e.g. { gearsByTypeId: { Helmet: { id: ..., type: 'Eyepiece', randomStats: ... }, Eyepiece: { id: ..., type: 'Eyepiece', randomStats: ... } }} + * + * Fix this by identifying the incorrect gears and resetting them to an empty gear of the correct type */ +export function fixGearSetsGearTypeData() { + const loadoutsStateJson = localStorage.getItem('loadouts'); + const gearComparerStateJson = localStorage.getItem('gearComparer'); + + const loadoutsState = loadoutsStateJson + ? (JSON.parse(loadoutsStateJson) as LoadoutsStateDto) + : undefined; + const gearComparerState = gearComparerStateJson + ? (JSON.parse(gearComparerStateJson) as GearComparerStateDto) + : undefined; + + const gearSets: GearSetDtoV2[] = []; + if (loadoutsState) { + gearSets.push( + ...loadoutsState.loadoutList.map( + (loadoutItem) => loadoutItem.loadout.gearSet + ) + ); + } + if (gearComparerState) { + gearSets.push(gearComparerState.replacementGearGearSet); + } + + gearSets.forEach((gearSet) => { + const gearsByTypeId = gearSet.gearsByTypeId; + Object.keys(gearsByTypeId).forEach((gearTypeId) => { + const gear = gearsByTypeId[gearTypeId as GearName]; + if (gear?.typeId !== gearTypeId) { + gearsByTypeId[gearTypeId as GearName] = { + id: nanoid(), + typeId: gearTypeId as GearName, + stars: 0, + randomStats: [undefined, undefined, undefined, undefined], + augmentStats: [], + isAugmented: false, + isTitan: false, + version: 1, + }; + } + }); + }); + + if (loadoutsState) { + localStorage.setItem('loadouts', JSON.stringify(loadoutsState)); + } + if (gearComparerState) { + localStorage.setItem('gearComparer', JSON.stringify(gearComparerState)); + } +}