diff --git a/src/app/armory/Armory.tsx b/src/app/armory/Armory.tsx
index 319c1b97fa..4c28fc72f7 100644
--- a/src/app/armory/Armory.tsx
+++ b/src/app/armory/Armory.tsx
@@ -6,7 +6,7 @@ import { DestinyTooltipText } from 'app/dim-ui/DestinyTooltipText';
import ElementIcon from 'app/dim-ui/ElementIcon';
import { t } from 'app/i18next-t';
import ItemIcon from 'app/inventory/ItemIcon';
-import { allItemsSelector, bucketsSelector } from 'app/inventory/selectors';
+import { allItemsSelector, createItemContextSelector } from 'app/inventory/selectors';
import { makeFakeItem } from 'app/inventory/store/d2-item-factory';
import {
applySocketOverrides,
@@ -51,14 +51,14 @@ export default function Armory({
}) {
const dispatch = useThunkDispatch();
const defs = useD2Definitions()!;
- const buckets = useSelector(bucketsSelector)!;
const allItems = useSelector(allItemsSelector);
const isPhonePortrait = useIsPhonePortrait();
const [socketOverrides, onPlugClicked] = useSocketOverrides();
+ const createItemContext = useSelector(createItemContextSelector);
const itemDef = defs.InventoryItem.get(itemHash);
- const itemWithoutSockets = makeFakeItem(defs, buckets, undefined, itemHash);
+ const itemWithoutSockets = makeFakeItem(createItemContext, itemHash);
if (!itemWithoutSockets) {
return (
@@ -68,12 +68,12 @@ export default function Armory({
);
}
- // We apply socket overrides *twice* - once to set the original sockets, then to apply the user's chosen overrides
- const item = applySocketOverrides(
- defs,
- applySocketOverrides(defs, itemWithoutSockets, realItemSockets),
- socketOverrides
- );
+ const item = applySocketOverrides(createItemContext, itemWithoutSockets, {
+ // Start with the item's current sockets
+ ...realItemSockets,
+ // Then apply whatever the user chose in the Armory UI
+ ...socketOverrides,
+ });
const storeItems = allItems.filter((i) => i.hash === itemHash);
@@ -208,7 +208,7 @@ export default function Armory({
{itemDef.setData?.itemList && (
{itemDef.setData.itemList.map((h) => {
- const stepItem = makeFakeItem(defs, buckets, undefined, h.itemHash);
+ const stepItem = makeFakeItem(createItemContext, h.itemHash);
return (
stepItem && (
- {
let items = rawCompareItems;
- if (defs) {
- if (doAssumeWeaponMasterworks) {
- items = items.map((i) => {
- const y2MasterworkSocket = i.sockets?.allSockets.find(
- (socket) => socket.socketDefinition.socketTypeHash === weaponMasterworkY2SocketTypeHash
+ if (doAssumeWeaponMasterworks) {
+ items = items.map((i) => {
+ const y2MasterworkSocket = i.sockets?.allSockets.find(
+ (socket) => socket.socketDefinition.socketTypeHash === weaponMasterworkY2SocketTypeHash
+ );
+ const plugSet = y2MasterworkSocket?.plugSet;
+ const plugged = y2MasterworkSocket?.plugged;
+ if (plugSet && plugged) {
+ const fullMasterworkPlug = _.maxBy(
+ plugSet.plugs.filter(
+ (p) => p.plugDef.plug.plugCategoryHash === plugged.plugDef.plug.plugCategoryHash
+ ),
+ (plugOption) => plugOption.plugDef.investmentStats[0]?.value
);
- const plugSet = y2MasterworkSocket?.plugSet;
- const plugged = y2MasterworkSocket?.plugged;
- if (plugSet && plugged) {
- const fullMasterworkPlug = _.maxBy(
- plugSet.plugs.filter(
- (p) => p.plugDef.plug.plugCategoryHash === plugged.plugDef.plug.plugCategoryHash
- ),
- (plugOption) => plugOption.plugDef.investmentStats[0]?.value
- );
- if (fullMasterworkPlug) {
- return applySocketOverrides(defs, i, {
- [y2MasterworkSocket.socketIndex]: fullMasterworkPlug.plugDef.hash,
- });
- }
+ if (fullMasterworkPlug) {
+ return applySocketOverrides(createItemContext, i, {
+ [y2MasterworkSocket.socketIndex]: fullMasterworkPlug.plugDef.hash,
+ });
}
- return i;
- });
- }
- items = items.map((i) => applySocketOverrides(defs, i, socketOverrides[i.id]));
+ }
+ return i;
+ });
}
+ items = items.map((i) => applySocketOverrides(createItemContext, i, socketOverrides[i.id]));
return items;
- }, [defs, doAssumeWeaponMasterworks, rawCompareItems, socketOverrides]);
+ }, [createItemContext, doAssumeWeaponMasterworks, rawCompareItems, socketOverrides]);
const cancel = useCallback(() => {
dispatch(endCompareSession());
diff --git a/src/app/destiny1/loadout-drawer/D1LoadoutDrawer.tsx b/src/app/destiny1/loadout-drawer/D1LoadoutDrawer.tsx
index 8f5c3b3afb..94973a2b48 100644
--- a/src/app/destiny1/loadout-drawer/D1LoadoutDrawer.tsx
+++ b/src/app/destiny1/loadout-drawer/D1LoadoutDrawer.tsx
@@ -4,7 +4,7 @@ import Sheet from 'app/dim-ui/Sheet';
import { t } from 'app/i18next-t';
import { DimItem } from 'app/inventory/item-types';
import ItemIcon from 'app/inventory/ItemIcon';
-import { allItemsSelector, bucketsSelector } from 'app/inventory/selectors';
+import { allItemsSelector, createItemContextSelector } from 'app/inventory/selectors';
import { showItemPicker } from 'app/item-picker/item-picker';
import { deleteLoadout, updateLoadout } from 'app/loadout-drawer/actions';
import {
@@ -57,16 +57,16 @@ export default function D1LoadoutDrawer({
const defs = useD1Definitions()!;
const allItems = useSelector(allItemsSelector);
- const buckets = useSelector(bucketsSelector)!;
const [showingItemPicker, setShowingItemPicker] = useState(false);
const [loadout, setLoadout] = useState(initialLoadout);
+ const createItemContext = useSelector(createItemContextSelector);
const loadoutItems = loadout?.items;
// Turn loadout items into real DimItems
const [items, warnitems] = useMemo(
- () => getItemsFromLoadoutItems(loadoutItems, defs, storeId, buckets, allItems),
- [loadoutItems, defs, storeId, buckets, allItems]
+ () => getItemsFromLoadoutItems(createItemContext, loadoutItems, storeId, allItems),
+ [createItemContext, loadoutItems, storeId, allItems]
);
const onAddItem = useCallback(
diff --git a/src/app/inventory/actions.ts b/src/app/inventory/actions.ts
index 4dd5c7ffd1..3a3856cea9 100644
--- a/src/app/inventory/actions.ts
+++ b/src/app/inventory/actions.ts
@@ -1,6 +1,5 @@
import { DestinyAccount } from 'app/accounts/destiny-account';
import { currentAccountSelector } from 'app/accounts/selectors';
-import { D2ManifestDefinitions } from 'app/destiny2/d2-definitions';
import { apiPermissionGrantedSelector } from 'app/dim-api/selectors';
import { t } from 'app/i18next-t';
import { showNotification } from 'app/notifications/notifications';
@@ -14,9 +13,9 @@ import {
} from 'bungie-api-ts/destiny2';
import { createAction } from 'typesafe-actions';
import { TagCommand, TagValue } from './dim-item-info';
-import { InventoryBuckets } from './inventory-buckets';
import { DimItem } from './item-types';
import { AccountCurrency, DimCharacterStat, DimStore } from './store-types';
+import { CreateItemContext } from './store/d2-item-factory';
/**
* Update the computed/massaged state of inventory, plus account-wide info like currencies.
@@ -73,8 +72,7 @@ export const itemMoved = createAction('inventory/MOVE_ITEM')<{
export const awaItemChanged = createAction('inventory/AWA_CHANGE')<{
item: DimItem | null;
changes: DestinyItemChangeResponse;
- defs: D2ManifestDefinitions;
- buckets: InventoryBuckets;
+ createItemContext: CreateItemContext;
}>();
/*
diff --git a/src/app/inventory/advanced-write-actions.ts b/src/app/inventory/advanced-write-actions.ts
index cc2f754182..6449b82158 100644
--- a/src/app/inventory/advanced-write-actions.ts
+++ b/src/app/inventory/advanced-write-actions.ts
@@ -27,8 +27,8 @@ import { showNotification } from '../notifications/notifications';
import { awaItemChanged } from './actions';
import { DimItem, DimSocket } from './item-types';
import {
+ createItemContextSelector,
currentStoreSelector,
- d2BucketsSelector,
profileResponseSelector,
storesSelector,
} from './selectors';
@@ -238,17 +238,15 @@ async function awaInsertSocketPlug(
*/
function refreshItemAfterAWA(changes: DestinyItemChangeResponse): ThunkResult {
return async (dispatch, getState) => {
- const defs = d2ManifestSelector(getState())!;
- const buckets = d2BucketsSelector(getState())!;
+ const createItemContext = createItemContextSelector(getState());
const stores = storesSelector(getState());
- const newItem = makeItemSingle(defs, buckets, changes.item, stores);
+ const newItem = makeItemSingle(createItemContext, changes.item, stores);
dispatch(
awaItemChanged({
item: newItem,
changes,
- defs: d2ManifestSelector(getState())!,
- buckets: d2BucketsSelector(getState())!,
+ createItemContext,
})
);
};
diff --git a/src/app/inventory/d2-stores.ts b/src/app/inventory/d2-stores.ts
index 266e51003e..74c3e762f8 100644
--- a/src/app/inventory/d2-stores.ts
+++ b/src/app/inventory/d2-stores.ts
@@ -5,6 +5,7 @@ import { DestinyAccount } from 'app/accounts/destiny-account';
import { getPlatforms } from 'app/accounts/platforms';
import { currentAccountSelector } from 'app/accounts/selectors';
import { loadClarity } from 'app/clarity/descriptions/loadDescriptions';
+import { settingSelector } from 'app/dim-api/selectors';
import { t } from 'app/i18next-t';
import { maxLightItemSet } from 'app/loadout-drawer/auto-loadouts';
import { d2ManifestSelector, manifestSelector } from 'app/manifest/selectors';
@@ -41,7 +42,6 @@ import {
} from './actions';
import { ArtifactXP } from './ArtifactXP';
import { cleanInfos } from './dim-item-info';
-import { InventoryBuckets } from './inventory-buckets';
import { DimItem } from './item-types';
import { ItemPowerSet } from './ItemPowerSet';
import { d2BucketsSelector, storesLoadedSelector, storesSelector } from './selectors';
@@ -51,7 +51,7 @@ import {
getCharacterStatsData as getD1CharacterStatsData,
hasAffectingClassified,
} from './store/character-utils';
-import { processItems } from './store/d2-item-factory';
+import { CreateItemContext, processItems } from './store/d2-item-factory';
import { getCharacterStatsData, makeCharacter, makeVault } from './store/d2-store-factory';
import { resetItemIndexGenerator } from './store/item-index';
import { getArtifactBonus } from './stores-helpers';
@@ -258,7 +258,7 @@ function loadStoresData(account: DestinyAccount): ThunkResult
- processCharacter(defs, buckets, characterId, profileInfo, lastPlayedDate)
+ const characters = Object.keys(profileResponse.characters.data).map((characterId) =>
+ processCharacter(createItemContext, characterId, lastPlayedDate)
);
processSpan?.finish();
@@ -374,7 +383,7 @@ export function buildStores(
const allItems = stores.flatMap((s) => s.items);
const bucketsWithClassifieds = getBucketsWithClassifiedItems(allItems);
- const characterProgress = getCharacterProgressions(profileInfo);
+ const characterProgress = getCharacterProgressions(profileResponse);
for (const s of stores) {
updateBasePower(
@@ -384,7 +393,7 @@ export function buildStores(
characterProgress,
// optional chaining here accounts for an edge-case, possible, but type-unadvertised,
// missing artifact power bonus. please keep this here.
- profileInfo.profileProgression?.data?.seasonalArtifact?.powerBonusProgression
+ profileResponse.profileProgression?.data?.seasonalArtifact?.powerBonusProgression
?.progressionHash,
bucketsWithClassifieds
);
@@ -412,22 +421,16 @@ function processCurrencies(profileInfo: DestinyProfileResponse, defs: D2Manifest
* Process a single character from its raw form to a DIM store, with all the items.
*/
function processCharacter(
- defs: D2ManifestDefinitions,
- buckets: InventoryBuckets,
+ createItemContext: CreateItemContext,
characterId: string,
- profileInfo: DestinyProfileResponse,
lastPlayedDate: Date
): DimStore {
- const character = profileInfo.characters.data![characterId];
- const characterInventory = profileInfo.characterInventories.data?.[characterId]?.items || [];
- const profileInventory = profileInfo.profileInventory.data?.items || [];
- const characterEquipment = profileInfo.characterEquipment.data?.[characterId]?.items || [];
- const profileRecords = profileInfo.profileRecords?.data;
- const itemComponents = profileInfo.itemComponents;
-
- const characterProgressions = getCharacterProgressions(profileInfo, characterId);
- const uninstancedItemObjectives = characterProgressions?.uninstancedItemObjectives;
- const uninstancedItemPerks = characterProgressions?.uninstancedItemPerks;
+ const { defs, buckets, profileResponse } = createItemContext;
+ const character = profileResponse.characters.data![characterId];
+ const characterInventory = profileResponse.characterInventories.data?.[characterId]?.items || [];
+ const profileInventory = profileResponse.profileInventory.data?.items || [];
+ const characterEquipment = profileResponse.characterEquipment.data?.[characterId]?.items || [];
+ const profileRecords = profileResponse.profileRecords?.data;
const store = makeCharacter(defs, character, lastPlayedDate, profileRecords);
@@ -444,30 +447,15 @@ function processCharacter(
}
}
- const processedItems = processItems(
- defs,
- buckets,
- store,
- items,
- itemComponents,
- uninstancedItemObjectives,
- profileRecords,
- uninstancedItemPerks
- );
- store.items = processedItems;
+ store.items = processItems(createItemContext, store, items);
return store;
}
-function processVault(
- defs: D2ManifestDefinitions,
- buckets: InventoryBuckets,
- profileInfo: DestinyProfileResponse
-): DimStore {
- const profileInventory = profileInfo.profileInventory.data
- ? profileInfo.profileInventory.data.items
+function processVault(createItemContext: CreateItemContext): DimStore {
+ const { buckets, profileResponse } = createItemContext;
+ const profileInventory = profileResponse.profileInventory.data
+ ? profileResponse.profileInventory.data.items
: [];
- const profileRecords = profileInfo.profileRecords?.data; // Not present in the initial load
- const itemComponents = profileInfo.itemComponents;
const store = makeVault();
@@ -480,17 +468,7 @@ function processVault(
}
}
- const processedItems = processItems(
- defs,
- buckets,
- store,
- items,
- itemComponents,
- undefined,
- profileRecords
- );
- store.items = processedItems;
-
+ store.items = processItems(createItemContext, store, items);
return store;
}
diff --git a/src/app/inventory/reducer.ts b/src/app/inventory/reducer.ts
index bc7078a8bf..bc924db872 100644
--- a/src/app/inventory/reducer.ts
+++ b/src/app/inventory/reducer.ts
@@ -1,4 +1,3 @@
-import { D2ManifestDefinitions } from 'app/destiny2/d2-definitions';
import { warnLog } from 'app/utils/log';
import {
DestinyItemChangeResponse,
@@ -14,10 +13,9 @@ import { ActionType, getType } from 'typesafe-actions';
import { setCurrentAccount } from '../accounts/actions';
import type { AccountsAction } from '../accounts/reducer';
import * as actions from './actions';
-import { InventoryBuckets } from './inventory-buckets';
import { DimItem } from './item-types';
import { AccountCurrency, DimStore } from './store-types';
-import { makeItem } from './store/d2-item-factory';
+import { CreateItemContext, makeItem } from './store/d2-item-factory';
import { createItemIndex } from './store/item-index';
import { findItemsByBucket, getCurrentStore, getStore, getVault } from './stores-helpers';
@@ -101,8 +99,8 @@ export const inventory: Reducer awaItemChanged(draft, changes, item, defs, buckets));
+ const { changes, item, createItemContext } = action.payload;
+ return produce(state, (draft) => awaItemChanged(draft, changes, item, createItemContext));
}
case getType(actions.error):
@@ -434,9 +432,10 @@ function awaItemChanged(
draft: Draft,
changes: DestinyItemChangeResponse,
item: DimItem | null,
- defs: D2ManifestDefinitions,
- buckets: InventoryBuckets
+ createItemContext: CreateItemContext
) {
+ const { defs, buckets } = createItemContext;
+
// Replace item
if (!item) {
warnLog('awaChange', 'No item produced from change');
@@ -527,7 +526,7 @@ function awaItemChanged(
currency.quantity = Math.min(max, currency.quantity + addedItemComponent.quantity);
} else if (addedItemComponent.itemInstanceId) {
const addedOwner = getSource(addedItemComponent);
- const addedItem = makeItem(defs, buckets, undefined, addedItemComponent, addedOwner);
+ const addedItem = makeItem(createItemContext, addedItemComponent, addedOwner);
if (addedItem) {
addItem(addedOwner, addedItem);
}
@@ -539,7 +538,7 @@ function awaItemChanged(
(i) => i.amount
);
let addAmount = addedItemComponent.quantity;
- const addedItem = makeItem(defs, buckets, undefined, addedItemComponent, target);
+ const addedItem = makeItem(createItemContext, addedItemComponent, target);
if (!addedItem) {
continue;
}
diff --git a/src/app/inventory/selectors.ts b/src/app/inventory/selectors.ts
index 92025438ff..aeb0adce72 100644
--- a/src/app/inventory/selectors.ts
+++ b/src/app/inventory/selectors.ts
@@ -1,6 +1,6 @@
import { ItemHashTag } from '@destinyitemmanager/dim-api-types';
import { destinyVersionSelector } from 'app/accounts/selectors';
-import { currentProfileSelector, settingsSelector } from 'app/dim-api/selectors';
+import { currentProfileSelector, settingSelector, settingsSelector } from 'app/dim-api/selectors';
import { d2ManifestSelector } from 'app/manifest/selectors';
import { RootState } from 'app/store/types';
import { emptyObject, emptySet } from 'app/utils/empty';
@@ -15,6 +15,7 @@ import { characterSortImportanceSelector, characterSortSelector } from '../setti
import { getTag, ItemInfos } from './dim-item-info';
import { DimItem } from './item-types';
import { collectNotesHashtags } from './note-hashtags';
+import { CreateItemContext } from './store/d2-item-factory';
import { getCurrentStore, getVault } from './stores-helpers';
/** All stores, unsorted. */
@@ -166,6 +167,22 @@ export const craftingMaterialCountsSelector = createSelector(
}
);
+/**
+ * All the dependencies for item creation. Don't use this before profile is loaded...
+ */
+export const createItemContextSelector = createSelector(
+ d2ManifestSelector,
+ profileResponseSelector,
+ bucketsSelector,
+ (state: RootState) => settingSelector('customTotalStatsByClass')(state),
+ (defs, profileResponse, buckets, customTotalStatsByClass): CreateItemContext => ({
+ defs: defs!,
+ buckets: buckets!,
+ profileResponse: profileResponse!,
+ customTotalStatsByClass,
+ })
+);
+
const STORE_SPECIFIC_OWNERSHIP_BUCKETS = [
// Emblems cannot be transferred between characters and if one character owns an emblem,
// other characters don't really own it. Also affects vendor claimability.
diff --git a/src/app/inventory/store/d2-item-factory.ts b/src/app/inventory/store/d2-item-factory.ts
index 21a2203917..5ce7c9a6bf 100644
--- a/src/app/inventory/store/d2-item-factory.ts
+++ b/src/app/inventory/store/d2-item-factory.ts
@@ -15,7 +15,6 @@ import {
BucketCategory,
ComponentPrivacySetting,
DestinyAmmunitionType,
- DestinyCharacterProgressionComponent,
DestinyClass,
DestinyInventoryItemDefinition,
DestinyItemComponent,
@@ -25,8 +24,7 @@ import {
DestinyItemSubType,
DestinyItemTooltipNotification,
DestinyObjectiveProgress,
- DestinyPerkReference,
- DestinyProfileRecordsComponent,
+ DestinyProfileResponse,
DictionaryComponentResponse,
ItemBindStatus,
ItemLocation,
@@ -66,41 +64,18 @@ const collectiblesByItemHash = memoizeOne(
/**
* Process an entire list of items into DIM items.
- * @param owner the ID of the owning store.
- * @param items a list of "raw" items from the Destiny API
- * @param previousItems a set of item IDs representing the previous store's items
- * @param newItems a set of item IDs representing the previous list of new items
- * @return a promise for the list of items
*/
export function processItems(
- defs: D2ManifestDefinitions,
- buckets: InventoryBuckets,
+ context: CreateItemContext,
owner: DimStore,
- items: DestinyItemComponent[],
- itemComponents: DestinyItemComponentSetOfint64,
- uninstancedItemObjectives?: DestinyCharacterProgressionComponent['uninstancedItemObjectives'],
- profileRecords?: DestinyProfileRecordsComponent,
- uninstancedItemPerks?: DestinyCharacterProgressionComponent['uninstancedItemPerks']
+ items: DestinyItemComponent[]
): DimItem[] {
const result: DimItem[] = [];
for (const item of items) {
let createdItem: DimItem | null = null;
-
- const itemUninstancedObjectives = uninstancedItemObjectives?.[item.itemHash];
- const itemUninstancedPerks = uninstancedItemPerks?.[item.itemHash]?.perks;
-
try {
- createdItem = makeItem(
- defs,
- buckets,
- itemComponents,
- item,
- owner,
- itemUninstancedObjectives,
- itemUninstancedPerks,
- profileRecords
- );
+ createdItem = makeItem(context, item, owner);
} catch (e) {
errorLog('d2-stores', 'Error processing item', item, e);
reportException('Processing Dim item', e);
@@ -119,7 +94,7 @@ export function processItems(
// an exception occurred, or the item lacks a definition
// not all of these should cause the store to consider itself hadErrors.
// dummies and invisible items are not a big deal
-
+ const defs = context.defs;
const bucketDef = defs.InventoryBucket[item.bucketHash];
// if it's a named, non-invisible bucket, it may be a problem that the item wasn't generated
if (
@@ -155,22 +130,17 @@ const getClassTypeNameLocalized = _.memoize((type: DestinyClass, defs: D2Manifes
/** Make a "fake" item from other information - used for Collectibles, etc. */
export function makeFakeItem(
- defs: D2ManifestDefinitions,
- buckets: InventoryBuckets,
- itemComponents: DestinyItemComponentSetOfint64 | undefined,
+ context: CreateItemContext,
itemHash: number,
- itemInstanceId?: string,
- quantity?: number,
- profileRecords?: DestinyProfileRecordsComponent,
- allowWishList?: boolean
+ itemInstanceId = '0',
+ quantity = 1,
+ allowWishList = false
): DimItem | null {
const item = makeItem(
- defs,
- buckets,
- itemComponents,
+ context,
{
itemHash,
- itemInstanceId: itemInstanceId ?? '0',
+ itemInstanceId: itemInstanceId,
quantity: quantity ?? 1,
bindStatus: ItemBindStatus.NotBound,
location: ItemLocation.Vendor,
@@ -182,12 +152,9 @@ export function makeFakeItem(
isWrapper: false,
tooltipNotificationIndexes: [],
metricObjective: {} as DestinyObjectiveProgress,
- versionNumber: defs.InventoryItem.get(itemHash)?.quality?.currentVersion,
+ versionNumber: context.defs.InventoryItem.get(itemHash)?.quality?.currentVersion,
},
- undefined,
- undefined,
- undefined,
- profileRecords
+ undefined
);
if (item && !allowWishList) {
@@ -201,18 +168,19 @@ export function makeFakeItem(
* We can use this item to refresh a single item in the store from this response.
*/
export function makeItemSingle(
- defs: D2ManifestDefinitions,
- buckets: InventoryBuckets,
- item: DestinyItemResponse,
+ context: CreateItemContext,
+ itemResponse: DestinyItemResponse,
stores: DimStore[]
): DimItem | null {
- if (!item.item.data) {
+ if (!itemResponse.item.data) {
return null;
}
- const owner = item.characterId ? stores.find((s) => s.id === item.characterId) : getVault(stores);
+ const owner = itemResponse.characterId
+ ? stores.find((s) => s.id === itemResponse.characterId)
+ : getVault(stores);
- const itemId = item.item.data.itemInstanceId;
+ const itemId = itemResponse.item.data.itemInstanceId;
// Convert a single component response into a dictionary component response
const empty = { privacy: ComponentPrivacySetting.Public, data: {} };
@@ -220,54 +188,61 @@ export function makeItemSingle(
? (v) => (v ? { privacy: v.privacy, data: v.data ? { [itemId]: v.data } : {} } : empty)
: () => empty;
+ // We'll override our item components with these ones
+ const itemComponents = {
+ instances: m(itemResponse.instance),
+ perks: m(itemResponse.perks),
+ renderData: m(itemResponse.renderData),
+ stats: m(itemResponse.stats),
+ sockets: m(itemResponse.sockets),
+ reusablePlugs: m(itemResponse.reusablePlugs),
+ plugObjectives: m(itemResponse.plugObjectives),
+ talentGrids: m(itemResponse.talentGrid),
+ plugStates: empty,
+ objectives: m(itemResponse.objectives),
+ };
+
// Make it look like a full response
- return makeItem(
- defs,
- buckets,
- {
- instances: m(item.instance),
- perks: m(item.perks),
- renderData: m(item.renderData),
- stats: m(item.stats),
- sockets: m(item.sockets),
- reusablePlugs: m(item.reusablePlugs),
- plugObjectives: m(item.plugObjectives),
- talentGrids: m(item.talentGrid),
- plugStates: empty,
- objectives: m(item.objectives),
- },
- item.item.data,
- owner
- );
+ return makeItem({ ...context, itemComponents }, itemResponse.item.data, owner);
+}
+
+/**
+ * Stuff that's required to create a DimItem.
+ */
+export interface CreateItemContext {
+ defs: D2ManifestDefinitions;
+ buckets: InventoryBuckets;
+ profileResponse: DestinyProfileResponse;
+ customTotalStatsByClass: {
+ [key: number]: number[];
+ };
+ /**
+ * Sometimes comes from the profile response, but also sometimes from vendors response or mocked out.
+ * If not present, use the one from profileInfo.
+ */
+ itemComponents?: DestinyItemComponentSetOfint64;
}
/**
* Process a single raw item into a DIM item.
- * @param defs the manifest definitions
- * @param buckets the bucket definitions
- * @param previousItems a set of item IDs representing the previous store's items
- * @param newItems a set of item IDs representing the previous list of new items
- * @param item "raw" item from the Destiny API
- * @param owner the ID of the owning store
- * @param uninstancedItemObjectives the owning character's dictionary of uninstanced objectives
*/
-// TODO: extract individual item components first!
export function makeItem(
- defs: D2ManifestDefinitions,
- buckets: InventoryBuckets,
- itemComponents: DestinyItemComponentSetOfint64 | undefined,
+ { defs, buckets, itemComponents, customTotalStatsByClass, profileResponse }: CreateItemContext,
item: DestinyItemComponent,
- owner: DimStore | undefined,
- /** this item's uninstanced objectives */
- itemUninstancedObjectives?: DestinyObjectiveProgress[],
- /** this item's uninstanced perks */
- itemUninstancedPerks?: DestinyPerkReference[],
- profileRecords?: DestinyProfileRecordsComponent
+ /** the ID of the owning store - can be undefined for fake collections items */
+ owner: DimStore | undefined
): DimItem | null {
+ itemComponents ??= profileResponse.itemComponents;
+
const itemDef = defs.InventoryItem.get(item.itemHash);
+ // Fish relevant data out of the profile.
+ const profileRecords = profileResponse.profileRecords?.data;
+ const characterProgressions =
+ owner && !owner?.isVault ? profileResponse.characterProgressions?.data?.[owner.id] : undefined;
+
const itemInstanceData: Partial = item.itemInstanceId
- ? itemComponents?.instances.data?.[item.itemInstanceId ?? ''] ?? emptyObject()
+ ? itemComponents?.instances.data?.[item.itemInstanceId] ?? emptyObject()
: emptyObject();
// Missing definition?
@@ -625,7 +600,7 @@ export function makeItem(
}
try {
- createdItem.stats = buildStats(defs, createdItem, itemDef);
+ createdItem.stats = buildStats(defs, createdItem, customTotalStatsByClass, itemDef);
} catch (e) {
errorLog('d2-stores', `Error building stats for ${createdItem.name}`, item, itemDef, e);
reportException('Stats', e, { itemHash: item.itemHash });
@@ -635,6 +610,8 @@ export function makeItem(
const itemInstancedObjectives = item.itemInstanceId
? itemComponents?.objectives?.data?.[item.itemInstanceId]?.objectives
: undefined;
+ const uninstancedItemObjectives = characterProgressions?.uninstancedItemObjectives;
+ const itemUninstancedObjectives = uninstancedItemObjectives?.[item.itemHash];
createdItem.objectives = buildObjectives(
itemDef,
@@ -647,6 +624,8 @@ export function makeItem(
}
if (itemDef.perks?.length) {
+ const uninstancedItemPerks = characterProgressions?.uninstancedItemPerks;
+ const itemUninstancedPerks = uninstancedItemPerks?.[item.itemHash]?.perks;
const perks = itemDef.perks.filter(
(p, i) =>
p.perkVisibility === ItemPerkVisibility.Visible &&
diff --git a/src/app/inventory/store/override-sockets.ts b/src/app/inventory/store/override-sockets.ts
index 489a254ec7..48a03a6317 100644
--- a/src/app/inventory/store/override-sockets.ts
+++ b/src/app/inventory/store/override-sockets.ts
@@ -1,10 +1,10 @@
-import { D2ManifestDefinitions } from 'app/destiny2/d2-definitions';
import { DEFAULT_ORNAMENTS } from 'app/search/d2-known-values';
import { errorLog } from 'app/utils/log';
import produce from 'immer';
import _ from 'lodash';
import { useCallback, useState } from 'react';
import { DimItem, DimPlug, DimSocket } from '../item-types';
+import { CreateItemContext } from './d2-item-factory';
import { buildDefinedPlug } from './sockets';
import { buildStats } from './stats';
@@ -20,9 +20,10 @@ export interface SocketOverrides {
* Transform an item into a new item whose properties (mostly stats) reflect the chosen socket overrides.
*/
export function applySocketOverrides(
- defs: D2ManifestDefinitions,
+ // We don't need everything here but I'm assuming over time we'll want to plumb more stuff into stats calculations?
+ { defs, customTotalStatsByClass }: CreateItemContext,
item: DimItem,
- socketOverrides?: SocketOverrides
+ socketOverrides: SocketOverrides | undefined
): DimItem {
if (!socketOverrides || _.isEmpty(socketOverrides) || !item.sockets) {
return item;
@@ -103,7 +104,7 @@ export function applySocketOverrides(
};
// Recalculate the entire item's stats from scratch given the new plugs
- updatedItem.stats = buildStats(defs, updatedItem);
+ updatedItem.stats = buildStats(defs, updatedItem, customTotalStatsByClass);
return updatedItem;
}
diff --git a/src/app/inventory/store/stats.ts b/src/app/inventory/store/stats.ts
index 434f5c2a7e..3eac23a310 100644
--- a/src/app/inventory/store/stats.ts
+++ b/src/app/inventory/store/stats.ts
@@ -1,5 +1,4 @@
import { D2ManifestDefinitions } from 'app/destiny2/d2-definitions';
-import { settingsSelector } from 'app/dim-api/selectors';
import { t } from 'app/i18next-t';
import { D1ItemCategoryHashes } from 'app/search/d1-known-values';
import { armorStats, CUSTOM_TOTAL_STAT_HASH, TOTAL_STAT_HASH } from 'app/search/d2-known-values';
@@ -18,7 +17,6 @@ import {
} from 'bungie-api-ts/destiny2';
import { BucketHashes, ItemCategoryHashes, StatHashes } from 'data/d2/generated-enums';
import _ from 'lodash';
-import reduxStore from '../../store/store';
import { socketContainsIntrinsicPlug } from '../../utils/socket-utils';
import { DimItem, DimPlug, DimSocket, DimStat } from '../item-types';
@@ -105,6 +103,9 @@ interface StatLookup {
export function buildStats(
defs: D2ManifestDefinitions,
createdItem: DimItem,
+ customTotalStatsByClass: {
+ [key: number]: number[];
+ },
itemDef = defs.InventoryItem.get(createdItem.hash)
) {
if (!itemDef.stats?.statGroupHash) {
@@ -151,7 +152,7 @@ export function buildStats(
investmentStats.push(tStat);
const cStat =
createdItem.bucket.hash !== BucketHashes.ClassArmor &&
- customStat(investmentStats, createdItem.classType);
+ customStat(investmentStats, customTotalStatsByClass, createdItem.classType);
if (cStat) {
investmentStats.push(cStat);
}
@@ -520,10 +521,14 @@ const customStatTemplate = _.once(() => ({
isConditionallyActive: false,
}));
-function customStat(stats: DimStat[], destinyClass: DestinyClass): DimStat | undefined {
- const customStatDef = settingsSelector(reduxStore.getState()).customTotalStatsByClass[
- destinyClass
- ];
+function customStat(
+ stats: DimStat[],
+ customTotalStatsByClass: {
+ [key: number]: number[];
+ },
+ destinyClass: DestinyClass
+): DimStat | undefined {
+ const customStatDef = customTotalStatsByClass[destinyClass];
if (!customStatDef || customStatDef.length === 0 || customStatDef.length === 6) {
return undefined;
diff --git a/src/app/item-popup/ItemDetails.tsx b/src/app/item-popup/ItemDetails.tsx
index 09580f846e..e968089e98 100644
--- a/src/app/item-popup/ItemDetails.tsx
+++ b/src/app/item-popup/ItemDetails.tsx
@@ -4,7 +4,7 @@ import { WeaponCatalystInfo } from 'app/dim-ui/WeaponCatalystInfo';
import { WeaponCraftedInfo } from 'app/dim-ui/WeaponCraftedInfo';
import { WeaponDeepsightInfo } from 'app/dim-ui/WeaponDeepsightInfo';
import { t } from 'app/i18next-t';
-import { storesSelector } from 'app/inventory/selectors';
+import { createItemContextSelector, storesSelector } from 'app/inventory/selectors';
import { isTrialsPassage } from 'app/inventory/store/objectives';
import { applySocketOverrides, useSocketOverrides } from 'app/inventory/store/override-sockets';
import { getStore } from 'app/inventory/stores-helpers';
@@ -44,9 +44,10 @@ export default function ItemDetails({
extraInfo?: ItemPopupExtraInfo;
}) {
const defs = useDefinitions()!;
+ const createItemContext = useSelector(createItemContextSelector);
const [socketOverrides, onPlugClicked, resetSocketOverrides] = useSocketOverrides();
const item = defs.isDestiny2()
- ? applySocketOverrides(defs, originalItem, socketOverrides)
+ ? applySocketOverrides(createItemContext, originalItem, socketOverrides)
: originalItem;
const modTypeIcon = item.itemCategoryHashes.includes(ItemCategoryHashes.ArmorMods)
? helmetIcon
diff --git a/src/app/item-popup/ItemPopupContainer.tsx b/src/app/item-popup/ItemPopupContainer.tsx
index d11e7fdb18..1be95da602 100644
--- a/src/app/item-popup/ItemPopupContainer.tsx
+++ b/src/app/item-popup/ItemPopupContainer.tsx
@@ -1,6 +1,6 @@
import { useHotkey } from 'app/hotkeys/useHotkey';
import { t } from 'app/i18next-t';
-import { sortedStoresSelector } from 'app/inventory/selectors';
+import { createItemContextSelector, sortedStoresSelector } from 'app/inventory/selectors';
import { DimStore } from 'app/inventory/store-types';
import { applySocketOverrides } from 'app/inventory/store/override-sockets';
import { useD2Definitions } from 'app/manifest/selectors';
@@ -23,6 +23,7 @@ interface Props {
export default function ItemPopupContainer({ boundarySelector }: Props) {
const stores = useSelector(sortedStoresSelector);
const defs = useD2Definitions();
+ const createItemContext = useSelector(createItemContextSelector);
const currentItem = useSubscription(showItemPopup$);
@@ -39,7 +40,7 @@ export default function ItemPopupContainer({ boundarySelector }: Props) {
let item = currentItem?.item && maybeFindItem(currentItem.item, stores);
// Apply socket overrides to customize the item (e.g. from a loadout)
if (item && defs && currentItem?.extraInfo?.socketOverrides) {
- item = applySocketOverrides(defs, item, currentItem.extraInfo.socketOverrides);
+ item = applySocketOverrides(createItemContext, item, currentItem.extraInfo.socketOverrides);
}
if (!currentItem || !item) {
diff --git a/src/app/loadout-builder/generated-sets/CompareLoadoutsDrawer.tsx b/src/app/loadout-builder/generated-sets/CompareLoadoutsDrawer.tsx
index 8e78d3988c..4cca39f320 100644
--- a/src/app/loadout-builder/generated-sets/CompareLoadoutsDrawer.tsx
+++ b/src/app/loadout-builder/generated-sets/CompareLoadoutsDrawer.tsx
@@ -1,18 +1,16 @@
import { LoadoutParameters } from '@destinyitemmanager/dim-api-types';
-import { D2ManifestDefinitions } from 'app/destiny2/d2-definitions';
import Select from 'app/dim-ui/Select';
import Sheet from 'app/dim-ui/Sheet';
import { t } from 'app/i18next-t';
-import { InventoryBuckets } from 'app/inventory/inventory-buckets';
import { DimItem } from 'app/inventory/item-types';
-import { allItemsSelector, bucketsSelector } from 'app/inventory/selectors';
+import { allItemsSelector, createItemContextSelector } from 'app/inventory/selectors';
import { DimStore } from 'app/inventory/store-types';
+import { CreateItemContext } from 'app/inventory/store/d2-item-factory';
import { updateLoadout } from 'app/loadout-drawer/actions';
import { getItemsFromLoadoutItems } from 'app/loadout-drawer/loadout-item-conversion';
import { Loadout, ResolvedLoadoutItem } from 'app/loadout-drawer/loadout-types';
import { convertToLoadoutItem } from 'app/loadout-drawer/loadout-utils';
import LoadoutView from 'app/loadout/LoadoutView';
-import { useD2Definitions } from 'app/manifest/selectors';
import { useThunkDispatch } from 'app/store/thunk-dispatch';
import { DestinyClass } from 'bungie-api-ts/destiny2';
import clsx from 'clsx';
@@ -57,11 +55,10 @@ function chooseInitialLoadout(
* replaced with `subclass`, and the given `params` and `notes`.
*/
function createLoadoutUsingLOItems(
- defs: D2ManifestDefinitions,
+ createItemContext: CreateItemContext,
allItems: DimItem[],
autoMods: number[],
storeId: string | undefined,
- buckets: InventoryBuckets,
setItems: DimItem[],
subclass: ResolvedLoadoutItem | undefined,
loadout: Loadout | undefined,
@@ -71,10 +68,9 @@ function createLoadoutUsingLOItems(
return produce(loadout, (draftLoadout) => {
if (draftLoadout) {
const [resolvedItems, warnItems] = getItemsFromLoadoutItems(
+ createItemContext,
draftLoadout.items,
- defs,
storeId,
- buckets,
allItems
);
const newItems = setItems.map((item) => convertToLoadoutItem(item, true));
@@ -125,27 +121,25 @@ export default function CompareLoadoutsDrawer({
onClose,
}: Props) {
const dispatch = useThunkDispatch();
- const defs = useD2Definitions()!;
- const useableLoadouts = loadouts.filter((l) => l.classType === classType);
+ const usableLoadouts = loadouts.filter((l) => l.classType === classType);
const setItems = set.armor.map((items) => items[0]);
const [selectedLoadout, setSelectedLoadout] = useState(() =>
- chooseInitialLoadout(setItems, useableLoadouts, initialLoadoutId)
+ chooseInitialLoadout(setItems, usableLoadouts, initialLoadoutId)
);
const allItems = useSelector(allItemsSelector);
- const buckets = useSelector(bucketsSelector)!;
+ const createItemContext = useSelector(createItemContextSelector);
// This probably isn't needed but I am being cautious as it iterates over the stores.
const generatedLoadout = useMemo(
() =>
createLoadoutUsingLOItems(
- defs,
+ createItemContext,
allItems,
set.statMods,
selectedStore.id,
- buckets,
setItems,
subclass,
selectedLoadout,
@@ -153,11 +147,10 @@ export default function CompareLoadoutsDrawer({
notes
),
[
- defs,
+ createItemContext,
allItems,
set.statMods,
selectedStore.id,
- buckets,
setItems,
subclass,
selectedLoadout,
@@ -188,12 +181,12 @@ export default function CompareLoadoutsDrawer({
const loadoutOptions = useMemo(
() =>
- useableLoadouts.map((l) => ({
+ usableLoadouts.map((l) => ({
key: l.id,
value: l,
content: l.name,
})),
- [useableLoadouts]
+ [usableLoadouts]
);
// This is likely never to happen but since it is disconnected to the button its here for safety.
diff --git a/src/app/loadout-drawer/loadout-item-conversion.ts b/src/app/loadout-drawer/loadout-item-conversion.ts
index 5401119317..48bd0ff702 100644
--- a/src/app/loadout-drawer/loadout-item-conversion.ts
+++ b/src/app/loadout-drawer/loadout-item-conversion.ts
@@ -1,8 +1,5 @@
-import { D1ManifestDefinitions } from 'app/destiny1/d1-definitions';
-import { D2ManifestDefinitions } from 'app/destiny2/d2-definitions';
-import { InventoryBuckets } from 'app/inventory/inventory-buckets';
import { makeFakeItem as makeFakeD1Item } from 'app/inventory/store/d1-item-factory';
-import { makeFakeItem } from 'app/inventory/store/d2-item-factory';
+import { CreateItemContext, makeFakeItem } from 'app/inventory/store/d2-item-factory';
import { applySocketOverrides } from 'app/inventory/store/override-sockets';
import { emptyArray } from 'app/utils/empty';
import { warnLog } from 'app/utils/log';
@@ -21,10 +18,9 @@ export function generateMissingLoadoutItemId() {
* are returned as warnitems.
*/
export function getItemsFromLoadoutItems(
+ createItemContext: CreateItemContext,
loadoutItems: LoadoutItem[] | undefined,
- defs: D1ManifestDefinitions | D2ManifestDefinitions,
storeId: string | undefined,
- buckets: InventoryBuckets,
allItems: DimItem[],
modsByBucket?: {
[bucketHash: number]: number[] | undefined;
@@ -34,6 +30,8 @@ export function getItemsFromLoadoutItems(
return [emptyArray(), emptyArray()];
}
+ const { defs, buckets } = createItemContext;
+
const items: ResolvedLoadoutItem[] = [];
const warnitems: ResolvedLoadoutItem[] = [];
for (const loadoutItem of loadoutItems) {
@@ -54,7 +52,9 @@ export function getItemsFromLoadoutItems(
}
// Apply socket overrides so the item appears as it should be configured in the loadout
- const overriddenItem = defs.isDestiny2() ? applySocketOverrides(defs, item, overrides) : item;
+ const overriddenItem = defs.isDestiny2()
+ ? applySocketOverrides(createItemContext, item, overrides)
+ : item;
items.push({
item: overriddenItem,
@@ -66,7 +66,7 @@ export function getItemsFromLoadoutItems(
});
} else {
const fakeItem: DimItem | null = defs.isDestiny2()
- ? makeFakeItem(defs, buckets, undefined, loadoutItem.hash)
+ ? makeFakeItem(createItemContext, loadoutItem.hash)
: makeFakeD1Item(defs, buckets, loadoutItem.hash);
if (fakeItem) {
fakeItem.id = generateMissingLoadoutItemId();
diff --git a/src/app/loadout/LoadoutView.tsx b/src/app/loadout/LoadoutView.tsx
index c3387663d1..cf7e2ad141 100644
--- a/src/app/loadout/LoadoutView.tsx
+++ b/src/app/loadout/LoadoutView.tsx
@@ -1,12 +1,11 @@
-import { D2ManifestDefinitions } from 'app/destiny2/d2-definitions';
import { AlertIcon } from 'app/dim-ui/AlertIcon';
import ClassIcon from 'app/dim-ui/ClassIcon';
import ColorDestinySymbols from 'app/dim-ui/destiny-symbols/ColorDestinySymbols';
import { t } from 'app/i18next-t';
-import { InventoryBuckets } from 'app/inventory/inventory-buckets';
import { DimItem } from 'app/inventory/item-types';
-import { allItemsSelector, bucketsSelector } from 'app/inventory/selectors';
+import { allItemsSelector, createItemContextSelector } from 'app/inventory/selectors';
import { DimStore } from 'app/inventory/store-types';
+import { CreateItemContext } from 'app/inventory/store/d2-item-factory';
import { getItemsFromLoadoutItems } from 'app/loadout-drawer/loadout-item-conversion';
import { Loadout, LoadoutItem, ResolvedLoadoutItem } from 'app/loadout-drawer/loadout-types';
import { getLight, getModsFromLoadout } from 'app/loadout-drawer/loadout-utils';
@@ -26,10 +25,9 @@ import LoadoutSubclassSection from './loadout-ui/LoadoutSubclassSection';
import styles from './LoadoutView.m.scss';
export function getItemsAndSubclassFromLoadout(
+ createItemContext: CreateItemContext,
loadoutItems: LoadoutItem[],
store: DimStore,
- defs: D2ManifestDefinitions,
- buckets: InventoryBuckets,
allItems: DimItem[],
modsByBucket?: {
[bucketHash: number]: number[] | undefined;
@@ -40,10 +38,9 @@ export function getItemsAndSubclassFromLoadout(
warnitems: ResolvedLoadoutItem[]
] {
let [items, warnitems] = getItemsFromLoadoutItems(
+ createItemContext,
loadoutItems,
- defs,
store.id,
- buckets,
allItems,
modsByBucket
);
@@ -81,8 +78,8 @@ export default function LoadoutView({
hideShowModPlacements?: boolean;
}) {
const defs = useD2Definitions()!;
- const buckets = useSelector(bucketsSelector)!;
const allItems = useSelector(allItemsSelector);
+ const createItemContext = useSelector(createItemContextSelector);
const missingSockets =
loadout.name === t('Loadouts.FromEquipped') && allItems.some((i) => i.missingSockets);
const isPhonePortrait = useIsPhonePortrait();
@@ -95,8 +92,14 @@ export default function LoadoutView({
// Turn loadout items into real DimItems, filtering out unequippable items
const [items, subclass, warnitems] = useMemo(
() =>
- getItemsAndSubclassFromLoadout(loadout.items, store, defs, buckets, allItems, modsByBucket),
- [loadout.items, defs, buckets, allItems, store, modsByBucket]
+ getItemsAndSubclassFromLoadout(
+ createItemContext,
+ loadout.items,
+ store,
+ allItems,
+ modsByBucket
+ ),
+ [createItemContext, loadout.items, store, allItems, modsByBucket]
);
const allMods = useMemo(() => getModsFromLoadout(defs, loadout), [defs, loadout]);
diff --git a/src/app/loadout/loadout-edit/LoadoutEdit.tsx b/src/app/loadout/loadout-edit/LoadoutEdit.tsx
index 701c7b3df8..c3c6b37dfd 100644
--- a/src/app/loadout/loadout-edit/LoadoutEdit.tsx
+++ b/src/app/loadout/loadout-edit/LoadoutEdit.tsx
@@ -3,7 +3,7 @@ import { D2ManifestDefinitions } from 'app/destiny2/d2-definitions';
import { t } from 'app/i18next-t';
import { InventoryBucket } from 'app/inventory/inventory-buckets';
import { DimItem, PluggableInventoryItemDefinition } from 'app/inventory/item-types';
-import { allItemsSelector, bucketsSelector } from 'app/inventory/selectors';
+import { allItemsSelector, createItemContextSelector } from 'app/inventory/selectors';
import { DimStore } from 'app/inventory/store-types';
import {
applySocketOverrides,
@@ -58,10 +58,10 @@ export default function LoadoutEdit({
onClickWarnItem: (resolvedItem: ResolvedLoadoutItem) => void;
}) {
const defs = useD2Definitions()!;
- const buckets = useSelector(bucketsSelector)!;
const allItems = useSelector(allItemsSelector);
const missingSockets = allItems.some((i) => i.missingSockets);
const [plugDrawerOpen, setPlugDrawerOpen] = useState(false);
+ const createItemContext = useSelector(createItemContextSelector);
// TODO: filter down by usable mods?
const modsByBucket: {
@@ -71,8 +71,14 @@ export default function LoadoutEdit({
// Turn loadout items into real DimItems, filtering out unequippable items
const [items, subclass, warnitems] = useMemo(
() =>
- getItemsAndSubclassFromLoadout(loadout.items, store, defs, buckets, allItems, modsByBucket),
- [loadout.items, defs, buckets, allItems, store, modsByBucket]
+ getItemsAndSubclassFromLoadout(
+ createItemContext,
+ loadout.items,
+ store,
+ allItems,
+ modsByBucket
+ ),
+ [createItemContext, loadout.items, store, allItems, modsByBucket]
);
const allMods = useMemo(() => getModsFromLoadout(defs, loadout), [defs, loadout]);
diff --git a/src/app/loadout/mod-assignment-drawer/selectors.ts b/src/app/loadout/mod-assignment-drawer/selectors.ts
index 3536eca882..cab55f16f0 100644
--- a/src/app/loadout/mod-assignment-drawer/selectors.ts
+++ b/src/app/loadout/mod-assignment-drawer/selectors.ts
@@ -1,10 +1,13 @@
import { DimItem } from 'app/inventory/item-types';
-import { allItemsSelector, bucketsSelector, sortedStoresSelector } from 'app/inventory/selectors';
+import {
+ allItemsSelector,
+ createItemContextSelector,
+ sortedStoresSelector,
+} from 'app/inventory/selectors';
import { getCurrentStore, getStore } from 'app/inventory/stores-helpers';
import { LockableBucketHashes } from 'app/loadout-builder/types';
import { getItemsFromLoadoutItems } from 'app/loadout-drawer/loadout-item-conversion';
import { Loadout, ResolvedLoadoutItem } from 'app/loadout-drawer/loadout-types';
-import { manifestSelector } from 'app/manifest/selectors';
import { RootState } from 'app/store/types';
import { BucketHashes } from 'data/d2/generated-enums';
import _ from 'lodash';
@@ -37,15 +40,13 @@ export function useEquippedLoadoutArmorAndSubclass(
storeToHydrateFrom?.items.filter((item) => item.equipped && item.bucket.inArmor) ?? [];
const classType = storeToHydrateFrom?.classType ?? loadout.classType;
const allItems = allItemsSelector(state);
- const defs = manifestSelector(state)!;
- const buckets = bucketsSelector(state)!;
+ const createItemContext = createItemContextSelector(state);
const modsByBucket = loadout.parameters?.modsByBucket;
const [loadoutItems] = getItemsFromLoadoutItems(
+ createItemContext,
loadout.items.filter((i) => i.equip),
- defs,
storeId,
- buckets,
allItems,
modsByBucket
);
diff --git a/src/app/organizer/ItemTable.tsx b/src/app/organizer/ItemTable.tsx
index 8a82fc2d43..013bff8bec 100644
--- a/src/app/organizer/ItemTable.tsx
+++ b/src/app/organizer/ItemTable.tsx
@@ -8,6 +8,7 @@ import { bulkLockItems, bulkTagItems } from 'app/inventory/bulk-actions';
import { DimItem } from 'app/inventory/item-types';
import {
allItemsSelector,
+ createItemContextSelector,
itemInfosSelector,
newItemsSelector,
storesSelector,
@@ -101,12 +102,14 @@ export default function ItemTable({ categories }: { categories: ItemCategoryTree
const wishList = useSelector(wishListFunctionSelector);
const hasWishList = useSelector(hasWishListSelector);
const enabledColumns = useSelector(settingSelector(columnSetting(itemType)));
- const customTotalStatsByClass = useSelector(settingSelector('customTotalStatsByClass'));
+ const createItemContext = useSelector(createItemContextSelector);
const loadoutsByItem = useSelector(loadoutsByItemSelector);
const newItems = useSelector(newItemsSelector);
const destinyVersion = useSelector(destinyVersionSelector);
const dispatch = useThunkDispatch();
+ const { customTotalStatsByClass } = createItemContext;
+
const classCategoryHash =
categories.map((n) => n.itemCategoryHash).find((hash) => hash in categoryToClass) ?? 999;
const classIfAny: DestinyClass = categoryToClass[classCategoryHash] ?? DestinyClass.Unknown;
@@ -135,9 +138,11 @@ export default function ItemTable({ categories }: { categories: ItemCategoryTree
const items = useMemo(
() =>
defs
- ? originalItems.map((item) => applySocketOverrides(defs, item, socketOverrides[item.id]))
+ ? originalItems.map((item) =>
+ applySocketOverrides(createItemContext, item, socketOverrides[item.id])
+ )
: originalItems,
- [defs, originalItems, socketOverrides]
+ [createItemContext, defs, originalItems, socketOverrides]
);
// Build a list of all the stats relevant to this set of items
diff --git a/src/app/progress/Progress.tsx b/src/app/progress/Progress.tsx
index 7586281639..0594e45798 100644
--- a/src/app/progress/Progress.tsx
+++ b/src/app/progress/Progress.tsx
@@ -178,8 +178,6 @@ export default function Progress({ account }: { account: DestinyAccount }) {
)}
diff --git a/src/app/progress/SeasonalChallenges.tsx b/src/app/progress/SeasonalChallenges.tsx
index e502ca1b95..072e0c99ef 100644
--- a/src/app/progress/SeasonalChallenges.tsx
+++ b/src/app/progress/SeasonalChallenges.tsx
@@ -1,18 +1,13 @@
import { trackedTriumphsSelector } from 'app/dim-api/selectors';
import CollapsibleTitle from 'app/dim-ui/CollapsibleTitle';
-import { InventoryBuckets } from 'app/inventory/inventory-buckets';
+import { createItemContextSelector } from 'app/inventory/selectors';
import { DimStore } from 'app/inventory/store-types';
-import { useD2Definitions } from 'app/manifest/selectors';
import {
DimPresentationNode,
DimRecord,
toPresentationNodeTree,
} from 'app/records/presentation-nodes';
-import {
- DestinyPresentationNodeDefinition,
- DestinyProfileResponse,
- DestinyRecordState,
-} from 'bungie-api-ts/destiny2';
+import { DestinyPresentationNodeDefinition, DestinyRecordState } from 'bungie-api-ts/destiny2';
import seasonalChallengesInfo from 'data/d2/seasonal-challenges.json';
import { useSelector } from 'react-redux';
import { recordToPursuitItem } from './milestone-items';
@@ -24,19 +19,13 @@ import { PursuitsGroup } from './Pursuits';
export default function SeasonalChallenges({
seasonalChallengesPresentationNode,
store,
- buckets,
- profileResponse,
}: {
seasonalChallengesPresentationNode: DestinyPresentationNodeDefinition;
store: DimStore;
- buckets: InventoryBuckets;
- profileResponse: DestinyProfileResponse;
}) {
- const defs = useD2Definitions()!;
+ const createItemContext = useSelector(createItemContextSelector);
const nodeTree = toPresentationNodeTree(
- defs,
- buckets,
- profileResponse,
+ createItemContext,
seasonalChallengesPresentationNode.hash
);
@@ -54,7 +43,7 @@ export default function SeasonalChallenges({
.map((r) =>
recordToPursuitItem(
r,
- buckets,
+ createItemContext.buckets,
store,
seasonalChallengesPresentationNode.displayProperties.name,
trackedRecords.includes(r.recordDef.hash)
diff --git a/src/app/progress/selectors.ts b/src/app/progress/selectors.ts
index 5d7008ec38..81caea386a 100644
--- a/src/app/progress/selectors.ts
+++ b/src/app/progress/selectors.ts
@@ -9,10 +9,8 @@ export const getCharacterProgressions = (
characterId?: string
) => {
// try to fill in missing character ID with a valid value
- characterId =
- characterId ||
- (profileResponse?.characterProgressions?.data
- ? Object.keys(profileResponse.characterProgressions.data)[0]
- : '');
+ characterId ??= profileResponse?.characterProgressions?.data
+ ? Object.keys(profileResponse.characterProgressions.data)[0]
+ : '';
return profileResponse?.characterProgressions?.data?.[characterId];
};
diff --git a/src/app/records/PlugSet.tsx b/src/app/records/PlugSet.tsx
index dc66966443..7f2571b440 100644
--- a/src/app/records/PlugSet.tsx
+++ b/src/app/records/PlugSet.tsx
@@ -1,4 +1,5 @@
import { DimItem } from 'app/inventory/item-types';
+import { createItemContextSelector } from 'app/inventory/selectors';
import { makeFakeItem } from 'app/inventory/store/d2-item-factory';
import { useD2Definitions } from 'app/manifest/selectors';
import { percent } from 'app/shell/formatters';
@@ -6,8 +7,8 @@ import { chainComparator, compareBy } from 'app/utils/comparators';
import { VendorItemDisplay } from 'app/vendors/VendorItemComponent';
import clsx from 'clsx';
import _ from 'lodash';
+import { useSelector } from 'react-redux';
import BungieImage from '../dim-ui/BungieImage';
-import { InventoryBuckets } from '../inventory/inventory-buckets';
import { AppIcon, collapseIcon, expandIcon } from '../shell/icons';
import { count } from '../utils/util';
@@ -17,7 +18,6 @@ const plugSetOrder = chainComparator(
);
interface Props {
- buckets: InventoryBuckets;
plugSetCollection: {
hash: number;
displayItem: number;
@@ -31,7 +31,6 @@ interface Props {
* A single plug set.
*/
export default function PlugSet({
- buckets,
plugSetCollection,
unlockedItems,
path,
@@ -40,9 +39,10 @@ export default function PlugSet({
const defs = useD2Definitions()!;
const plugSetHash = plugSetCollection.hash;
const plugSetDef = defs.PlugSet.get(plugSetHash);
+ const createItemContext = useSelector(createItemContextSelector);
const plugSetItems = _.compact(
- plugSetDef.reusablePlugItems.map((i) => makeFakeItem(defs, buckets, undefined, i.plugItemHash))
+ plugSetDef.reusablePlugItems.map((i) => makeFakeItem(createItemContext, i.plugItemHash))
);
plugSetItems.sort(plugSetOrder);
diff --git a/src/app/records/PresentationNodeRoot.tsx b/src/app/records/PresentationNodeRoot.tsx
index 800ab17a6a..63833835c1 100644
--- a/src/app/records/PresentationNodeRoot.tsx
+++ b/src/app/records/PresentationNodeRoot.tsx
@@ -1,7 +1,9 @@
+import { createItemContextSelector } from 'app/inventory/selectors';
import { useD2Definitions } from 'app/manifest/selectors';
import { ItemFilter } from 'app/search/filter-types';
import { DestinyProfileResponse } from 'bungie-api-ts/destiny2';
import { useMemo, useState } from 'react';
+import { useSelector } from 'react-redux';
import { InventoryBuckets } from '../inventory/inventory-buckets';
import PlugSet from './PlugSet';
import { unlockedItemsForCharacterOrProfilePlugSet } from './plugset-helpers';
@@ -41,6 +43,7 @@ export default function PresentationNodeRoot({
overrideName,
completedRecordsHidden,
}: Props) {
+ const createItemContext = useSelector(createItemContextSelector);
const defs = useD2Definitions()!;
const [nodePath, setNodePath] = useState([]);
@@ -58,8 +61,8 @@ export default function PresentationNodeRoot({
}
const nodeTree = useMemo(
- () => toPresentationNodeTree(defs, buckets, profileResponse, presentationNodeHash),
- [defs, buckets, profileResponse, presentationNodeHash]
+ () => toPresentationNodeTree(createItemContext, presentationNodeHash),
+ [presentationNodeHash, createItemContext]
);
// console.log(nodeTree);
@@ -111,7 +114,6 @@ export default function PresentationNodeRoot({
plugSetCollections.map((plugSetCollection) => (
{
const collectibleDef = defs.Collectible.get(collectibleHash);
@@ -302,15 +290,7 @@ function toCollectibles(
) {
return null;
}
- const item = makeFakeItem(
- defs,
- buckets,
- profileResponse.itemComponents,
- collectibleDef.itemHash,
- undefined,
- undefined,
- profileResponse.profileRecords.data
- );
+ const item = makeFakeItem(createItemContext, collectibleDef.itemHash);
if (!item) {
return null;
}
@@ -360,31 +340,27 @@ export function toRecord(
}
function toCraftables(
- defs: D2ManifestDefinitions,
- buckets: InventoryBuckets,
- profileResponse: DestinyProfileResponse,
+ createItemContext: CreateItemContext,
craftableChildren: DestinyPresentationNodeCraftableChildEntry[]
): DimCraftable[] {
return _.compact(
_.sortBy(craftableChildren, (c) => c.nodeDisplayPriority).map((c) =>
- toCraftable(defs, buckets, profileResponse, c.craftableItemHash)
+ toCraftable(createItemContext, c.craftableItemHash)
)
);
}
function toCraftable(
- defs: D2ManifestDefinitions,
- buckets: InventoryBuckets,
- profileResponse: DestinyProfileResponse,
+ createItemContext: CreateItemContext,
itemHash: number
): DimCraftable | undefined {
- const item = makeFakeItem(defs, buckets, profileResponse.itemComponents, itemHash);
+ const item = makeFakeItem(createItemContext, itemHash);
if (!item) {
return;
}
- const info = getCraftableInfo(item.hash, profileResponse);
+ const info = getCraftableInfo(item.hash, createItemContext.profileResponse);
if (!info?.visible) {
return;
}
diff --git a/src/app/vendors/SingleVendor.tsx b/src/app/vendors/SingleVendor.tsx
index e7904dc01f..92658c4eae 100644
--- a/src/app/vendors/SingleVendor.tsx
+++ b/src/app/vendors/SingleVendor.tsx
@@ -13,7 +13,12 @@ import { useLocation, useParams } from 'react-router';
import { DestinyAccount } from '../accounts/destiny-account';
import Countdown from '../dim-ui/Countdown';
import ErrorBoundary from '../dim-ui/ErrorBoundary';
-import { bucketsSelector, profileResponseSelector, storesSelector } from '../inventory/selectors';
+import {
+ bucketsSelector,
+ createItemContextSelector,
+ profileResponseSelector,
+ storesSelector,
+} from '../inventory/selectors';
import { loadingTracker } from '../shell/loading-tracker';
import { refresh$ } from '../shell/refresh-events';
import { loadAllVendors } from './actions';
@@ -35,6 +40,7 @@ export default function SingleVendor({ account }: { account: DestinyAccount }) {
const profileResponse = useSelector(profileResponseSelector);
const vendors = useSelector(vendorsByCharacterSelector);
const defs = useD2Definitions();
+ const createItemContext = useSelector(createItemContextSelector);
const dispatch = useThunkDispatch();
// TODO: get for all characters, or let people select a character? This is a hack
@@ -109,14 +115,10 @@ export default function SingleVendor({ account }: { account: DestinyAccount }) {
// TODO: there's a cool background image but I'm not sure how to use it
const d2Vendor = toVendor(
+ { ...createItemContext, itemComponents: vendorResponse?.itemComponents[vendorHash] },
vendorHash,
- defs,
- buckets,
- profileResponse,
vendor,
- account,
characterId,
- vendorResponse?.itemComponents[vendorHash],
vendorResponse?.sales.data?.[vendorHash]?.saleItems
);
diff --git a/src/app/vendors/d2-vendors.test.ts b/src/app/vendors/d2-vendors.test.ts
index 0194ae853b..5913332549 100644
--- a/src/app/vendors/d2-vendors.test.ts
+++ b/src/app/vendors/d2-vendors.test.ts
@@ -1,4 +1,3 @@
-import { DestinyAccount } from 'app/accounts/destiny-account';
import { getBuckets } from 'app/destiny2/d2-buckets';
import { getTestDefinitions, getTestProfile, getTestVendors } from 'testing/test-utils';
import { D2VendorGroup, toVendorGroups } from './d2-vendors';
@@ -8,18 +7,18 @@ async function getTestVendorGroups() {
const profileResponse = getTestProfile();
const vendorsResponse = getTestVendors();
const buckets = getBuckets(defs);
- const account: DestinyAccount = {
- displayName: '',
- originalPlatformType: 2,
- platformLabel: '',
- membershipId: '',
- destinyVersion: 1,
- platforms: [],
- lastPlayed: new Date(),
- };
const characterId = Object.keys(profileResponse.characters.data!)[0];
- return toVendorGroups(vendorsResponse, profileResponse, defs, buckets, account, characterId);
+ return toVendorGroups(
+ {
+ defs,
+ buckets,
+ profileResponse,
+ customTotalStatsByClass: {},
+ },
+ vendorsResponse,
+ characterId
+ );
}
function* allSaleItems(vendorGroups: D2VendorGroup[]) {
diff --git a/src/app/vendors/d2-vendors.ts b/src/app/vendors/d2-vendors.ts
index 1a8f27039c..5ff6da530f 100644
--- a/src/app/vendors/d2-vendors.ts
+++ b/src/app/vendors/d2-vendors.ts
@@ -1,16 +1,11 @@
-import { DestinyAccount } from 'app/accounts/destiny-account';
-import { D2ManifestDefinitions } from 'app/destiny2/d2-definitions';
-import { InventoryBuckets } from 'app/inventory/inventory-buckets';
+import { CreateItemContext } from 'app/inventory/store/d2-item-factory';
import { VENDORS } from 'app/search/d2-known-values';
import { ItemFilter } from 'app/search/filter-types';
import {
- BungieMembershipType,
DestinyCollectibleState,
DestinyDestinationDefinition,
DestinyInventoryItemDefinition,
- DestinyItemComponentSetOfint32,
DestinyPlaceDefinition,
- DestinyProfileResponse,
DestinyVendorComponent,
DestinyVendorDefinition,
DestinyVendorGroupDefinition,
@@ -37,17 +32,16 @@ export interface D2Vendor {
const vendorOrder = [VENDORS.SPIDER, VENDORS.ADA_TRANSMOG, VENDORS.BANSHEE, VENDORS.EVERVERSE];
export function toVendorGroups(
+ context: CreateItemContext,
vendorsResponse: DestinyVendorsResponse,
- profileResponse: DestinyProfileResponse,
- defs: D2ManifestDefinitions,
- buckets: InventoryBuckets,
- account: DestinyAccount,
characterId: string
): D2VendorGroup[] {
if (!vendorsResponse.vendorGroups.data) {
return [];
}
+ const { defs } = context;
+
return _.sortBy(
Object.values(vendorsResponse.vendorGroups.data.groups).map((group) => {
const groupDef = defs.VendorGroup.get(group.vendorGroupHash);
@@ -58,14 +52,11 @@ export function toVendorGroups(
group.vendorHashes
.map((vendorHash) =>
toVendor(
+ // Override the item components from the profile with this vendor's item components
+ { ...context, itemComponents: vendorsResponse.itemComponents[vendorHash] },
vendorHash,
- defs,
- buckets,
- profileResponse,
vendorsResponse.vendors.data?.[vendorHash],
- account,
characterId,
- vendorsResponse.itemComponents[vendorHash],
vendorsResponse.sales.data?.[vendorHash]?.saleItems
)
)
@@ -83,36 +74,24 @@ export function toVendorGroups(
}
export function toVendor(
+ context: CreateItemContext,
vendorHash: number,
- defs: D2ManifestDefinitions,
- buckets: InventoryBuckets,
- profileResponse: DestinyProfileResponse | undefined,
vendor: DestinyVendorComponent | undefined,
- account: DestinyAccount,
characterId: string,
- itemComponents: DestinyItemComponentSetOfint32 | undefined,
sales:
| {
[key: string]: DestinyVendorSaleItemComponent;
}
| undefined
): D2Vendor | undefined {
+ const { defs } = context;
const vendorDef = defs.Vendor.get(vendorHash);
if (!vendorDef) {
return undefined;
}
- const vendorItems = getVendorItems(
- account,
- defs,
- buckets,
- vendorDef,
- profileResponse,
- characterId,
- itemComponents,
- sales
- );
+ const vendorItems = getVendorItems(context, vendorDef, characterId, sales);
const destinationDef =
typeof vendor?.vendorLocationIndex === 'number' && vendor.vendorLocationIndex >= 0
@@ -143,13 +122,9 @@ export function toVendor(
}
function getVendorItems(
- account: DestinyAccount,
- defs: D2ManifestDefinitions,
- buckets: InventoryBuckets,
+ context: CreateItemContext,
vendorDef: DestinyVendorDefinition,
- profileResponse: DestinyProfileResponse | undefined,
characterId: string,
- itemComponents: DestinyItemComponentSetOfint32 | undefined,
sales:
| {
[key: string]: DestinyVendorSaleItemComponent;
@@ -159,28 +134,13 @@ function getVendorItems(
if (sales) {
const components = Object.values(sales);
return components.map((component) =>
- vendorItemForSaleItem(
- defs,
- buckets,
- vendorDef,
- profileResponse,
- component,
- characterId,
- itemComponents
- )
+ vendorItemForSaleItem(context, vendorDef, component, characterId)
);
} else if (vendorDef.returnWithVendorRequest) {
// If the sales should come from the server, don't show anything until we have them
return [];
} else {
- return vendorDef.itemList
- .filter(
- (i) =>
- !i.exclusivity ||
- i.exclusivity === BungieMembershipType.All ||
- i.exclusivity === account.originalPlatformType
- )
- .map((i) => vendorItemForDefinitionItem(defs, buckets, i, profileResponse, characterId));
+ return vendorDef.itemList.map((i) => vendorItemForDefinitionItem(context, i, characterId));
}
}
diff --git a/src/app/vendors/selectors.ts b/src/app/vendors/selectors.ts
index 8da5cec506..cfca8037c5 100644
--- a/src/app/vendors/selectors.ts
+++ b/src/app/vendors/selectors.ts
@@ -1,13 +1,10 @@
-import { currentAccountSelector } from 'app/accounts/selectors';
import {
- bucketsSelector,
+ createItemContextSelector,
ownedItemsSelector,
ownedUncollectiblePlugsSelector,
- profileResponseSelector,
sortedStoresSelector,
} from 'app/inventory/selectors';
import { getCurrentStore } from 'app/inventory/stores-helpers';
-import { d2ManifestSelector } from 'app/manifest/selectors';
import { RootState } from 'app/store/types';
import { emptyArray } from 'app/utils/empty';
import { currySelector } from 'app/utils/redux-utils';
@@ -20,32 +17,17 @@ export const vendorsByCharacterSelector = (state: RootState) => state.vendors.ve
* returns a character's vendors and their sale items
*/
export const nonCurriedVendorGroupsForCharacterSelector = createSelector(
- d2ManifestSelector,
+ createItemContextSelector,
vendorsByCharacterSelector,
- bucketsSelector,
- currentAccountSelector,
- profileResponseSelector,
// get character ID from props not state
- (state: any, characterId: string | undefined) =>
+ (state: RootState, characterId: string | undefined) =>
characterId || getCurrentStore(sortedStoresSelector(state))?.id,
- (defs, vendors, buckets, currentAccount, profileResponse, selectedStoreId) => {
+ (context, vendors, selectedStoreId) => {
const vendorData = selectedStoreId ? vendors[selectedStoreId] : undefined;
const vendorsResponse = vendorData?.vendorsResponse;
- return vendorsResponse &&
- defs &&
- buckets &&
- currentAccount &&
- selectedStoreId &&
- profileResponse
- ? toVendorGroups(
- vendorsResponse,
- profileResponse,
- defs,
- buckets,
- currentAccount,
- selectedStoreId
- )
+ return vendorsResponse && vendorData && selectedStoreId
+ ? toVendorGroups(context, vendorsResponse, selectedStoreId)
: emptyArray();
}
);
diff --git a/src/app/vendors/vendor-item.ts b/src/app/vendors/vendor-item.ts
index 5e4db4496f..1174a0a640 100644
--- a/src/app/vendors/vendor-item.ts
+++ b/src/app/vendors/vendor-item.ts
@@ -1,9 +1,9 @@
import { VENDORS } from 'app/search/d2-known-values';
+import { emptyArray } from 'app/utils/empty';
import {
DestinyCollectibleState,
DestinyDisplayPropertiesDefinition,
DestinyInventoryItemDefinition,
- DestinyItemComponentSetOfint32,
DestinyItemQuantity,
DestinyProfileResponse,
DestinyVendorDefinition,
@@ -12,10 +12,8 @@ import {
DestinyVendorSaleItemComponent,
} from 'bungie-api-ts/destiny2';
import { BucketHashes } from 'data/d2/generated-enums';
-import { D2ManifestDefinitions } from '../destiny2/d2-definitions';
-import { InventoryBuckets } from '../inventory/inventory-buckets';
import { DimItem } from '../inventory/item-types';
-import { makeFakeItem } from '../inventory/store/d2-item-factory';
+import { CreateItemContext, makeFakeItem } from '../inventory/store/d2-item-factory';
/**
* This represents an item inside a vendor.
@@ -59,18 +57,17 @@ function getCollectibleState(
}
function makeVendorItem(
- defs: D2ManifestDefinitions,
- buckets: InventoryBuckets,
- profileResponse: DestinyProfileResponse | undefined,
+ context: CreateItemContext,
itemHash: number,
failureStrings: string[],
vendorHash: number,
- vendorItemDef: DestinyVendorItemDefinition | undefined,
+ vendorItemDef: DestinyVendorItemDefinition,
saleItem: DestinyVendorSaleItemComponent | undefined,
- itemComponents: DestinyItemComponentSetOfint32 | undefined,
// the character to whom this item is being offered
characterId: string
): VendorItem {
+ const { defs, profileResponse } = context;
+
const inventoryItem = defs.InventoryItem.get(itemHash);
const key = saleItem ? saleItem.vendorItemIndex : inventoryItem.hash;
const vendorItem: VendorItem = {
@@ -86,14 +83,11 @@ function makeVendorItem(
previewVendorHash: inventoryItem.preview?.previewVendorHash,
collectibleState: getCollectibleState(inventoryItem, profileResponse, characterId),
item: makeFakeItem(
- defs,
- buckets,
- itemComponents,
+ context,
itemHash,
// For sale items the item ID needs to be the vendor item index, since that's how we look up item components for perks
key.toString(),
vendorItemDef ? vendorItemDef.quantity : 1,
- profileResponse?.profileRecords.data,
// vendor items are wish list enabled!
true
),
@@ -136,31 +130,25 @@ function makeVendorItem(
* of that copy they're selling
*/
export function vendorItemForSaleItem(
- defs: D2ManifestDefinitions,
- buckets: InventoryBuckets,
+ context: CreateItemContext,
vendorDef: DestinyVendorDefinition,
- profileResponse: DestinyProfileResponse | undefined,
saleItem: DestinyVendorSaleItemComponent,
- // all DIM vendor calls are character-specific. any sale item should have an associated character.
- characterId: string,
- itemComponents: DestinyItemComponentSetOfint32 | undefined
+ /** all DIM vendor calls are character-specific. any sale item should have an associated character. */
+ characterId: string
): VendorItem {
const vendorItemDef = vendorDef.itemList[saleItem.vendorItemIndex];
const failureStrings =
- saleItem && vendorDef
+ saleItem && vendorDef && saleItem.failureIndexes
? (saleItem.failureIndexes || []).map((i) => vendorDef.failureStrings[i])
- : [];
+ : emptyArray();
return makeVendorItem(
- defs,
- buckets,
- profileResponse,
+ context,
saleItem.itemHash,
failureStrings,
vendorDef.hash,
vendorItemDef,
saleItem,
- itemComponents,
characterId
);
}
@@ -170,22 +158,17 @@ export function vendorItemForSaleItem(
* some vendors are set up so statically, that they have no data in the live Vendors response
*/
export function vendorItemForDefinitionItem(
- defs: D2ManifestDefinitions,
- buckets: InventoryBuckets,
+ context: CreateItemContext,
vendorItemDef: DestinyVendorItemDefinition,
- profileResponse: DestinyProfileResponse | undefined,
characterId: string
): VendorItem {
return makeVendorItem(
- defs,
- buckets,
- profileResponse,
+ context,
vendorItemDef.itemHash,
[],
0,
vendorItemDef,
undefined,
- undefined,
characterId
);
}
diff --git a/src/testing/test-utils.ts b/src/testing/test-utils.ts
index 29ffc1fc87..6178738857 100644
--- a/src/testing/test-utils.ts
+++ b/src/testing/test-utils.ts
@@ -87,6 +87,11 @@ export const getTestVendors = () => (vendors as any).Response as DestinyVendorsR
export const getTestStores = _.once(async () => {
const manifest = await getTestDefinitions();
- const stores = buildStores(manifest, getBuckets(manifest), getTestProfile());
+ const stores = buildStores({
+ defs: manifest,
+ buckets: getBuckets(manifest),
+ profileResponse: getTestProfile(),
+ customTotalStatsByClass: {},
+ });
return stores;
});