Skip to content

Commit

Permalink
refactor: break up inventory data (#1351)
Browse files Browse the repository at this point in the history
Before the different pages were all grouped together. For example weaponsInventory was a single object in the state. This meant if one item changed all 4 pages were updated.

The new system breaks the pages to each character (guardians and vault). It then does an eqaulity check to ensure only characters with changed data are updated.

Previously transfers could cause the UI to update and take up to 1 second to redraw everything. It seems the largest updates are now 250ms.
  • Loading branch information
NigelBreslaw authored Apr 21, 2024
1 parent 55769d8 commit 20a0a5b
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 40 deletions.
5 changes: 4 additions & 1 deletion native/app/bungie/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {
GuardianRaceType,
ItemType,
} from "@/app/bungie/Hashes.ts";
import type { DamageType } from "@/app/inventory/Common.ts";
import type { DamageType, UISections } from "@/app/inventory/Common.ts";
import { array, boolean, isoTimestamp, merge, number, object, optional, record, string, unknown } from "valibot";
import type { Output } from "valibot";

Expand Down Expand Up @@ -434,4 +434,7 @@ export type GGCharacterUiData = {
secondarySpecial: string;
lastActiveCharacter: boolean;
ggCharacterType: GGCharacterType;
armorPageData: UISections[];
generalPageData: UISections[];
weaponsPageData: UISections[];
};
12 changes: 6 additions & 6 deletions native/app/inventory/Common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export const basePath = "https://www.bungie.net/Platform";
export const iconUrl = "https://www.bungie.net/common/destiny2_content/icons/";
export const screenshotUrl = "https://www.bungie.net/common/destiny2_content/screenshots/";

export enum InventoryPage {
export enum InventoryPageEnums {
Unknown = 0,
Weapons = 1,
Armor = 2,
Expand Down Expand Up @@ -153,18 +153,18 @@ export const equipSectionBuckets = [
SectionBuckets.Finisher,
];

export function getInventoryPage(bucket: number): InventoryPage {
export function getInventoryPage(bucket: number): InventoryPageEnums {
if (weaponsPageBuckets.includes(bucket)) {
return InventoryPage.Weapons;
return InventoryPageEnums.Weapons;
}
if (armorPageBuckets.includes(bucket)) {
return InventoryPage.Armor;
return InventoryPageEnums.Armor;
}
if (generalPageBuckets.includes(bucket)) {
return InventoryPage.General;
return InventoryPageEnums.General;
}
console.log("Unknown page", bucket);
return InventoryPage.Unknown;
return InventoryPageEnums.Unknown;
}

export enum DamageType {
Expand Down
5 changes: 2 additions & 3 deletions native/app/screens/ArmorPage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { InventoryPageEnums } from "@/app/inventory/Common.ts";
import InventoryPage from "@/app/screens/InventoryPage.tsx";
import { useGGStore } from "@/app/store/GGStore.ts";

export default function ArmorPage() {
const armorPageData = useGGStore((state) => state.armorPageData);
return <InventoryPage inventoryPageData={armorPageData} />;
return <InventoryPage inventoryPages={InventoryPageEnums.Armor} />;
}
5 changes: 2 additions & 3 deletions native/app/screens/GeneralPage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import InventoryPage from "@/app/screens/InventoryPage.tsx";
import { useGGStore } from "@/app/store/GGStore.ts";
import { InventoryPageEnums } from "@/app/inventory/Common.ts";

export default function GeneralPage() {
const generalPageData = useGGStore((state) => state.generalPageData);
return <InventoryPage inventoryPageData={generalPageData} />;
return <InventoryPage inventoryPages={InventoryPageEnums.General} />;
}
22 changes: 18 additions & 4 deletions native/app/screens/InventoryPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { UISections } from "@/app/inventory/Common.ts";
import type { GGCharacterUiData } from "@/app/bungie/Types.ts";
import { InventoryPageEnums, type UISections } from "@/app/inventory/Common.ts";
import { UiCellRenderItem } from "@/app/inventory/UiRowRenderItem.tsx";
import { calcCurrentListIndex } from "@/app/screens/Helpers.ts";
import { useGGStore } from "@/app/store/GGStore.ts";
Expand All @@ -11,8 +12,19 @@ import { ScrollView } from "react-native-gesture-handler";

const pageEstimatedFlashListItemSize = [130, 130, 130, 200];

function getData(ggCharacter: GGCharacterUiData, inventoryPage: InventoryPageEnums): UISections[] | undefined {
switch (inventoryPage) {
case InventoryPageEnums.Armor:
return ggCharacter.armorPageData;
case InventoryPageEnums.General:
return ggCharacter.generalPageData;
case InventoryPageEnums.Weapons:
return ggCharacter.weaponsPageData;
}
}

type InventoryPageProps = {
inventoryPageData: UISections[][];
inventoryPages: InventoryPageEnums;
};

const rootStyles = StyleSheet.create({
Expand Down Expand Up @@ -78,6 +90,8 @@ export default function InventoryPage(props: InventoryPageProps) {
const debouncedMove = debounce(listMoved, 40);
const debounceListIndex = debounce(calcCurrentListIndex, 40);

const mainData = useGGStore((state) => state.ggCharacters);

return (
<View style={rootStyles.root}>
<ScrollView
Expand All @@ -87,15 +101,15 @@ export default function InventoryPage(props: InventoryPageProps) {
onScroll={(e) => debounceListIndex(e.nativeEvent.contentOffset.x, HOME_WIDTH)}
ref={pagedScrollRef}
>
{props.inventoryPageData.map((list, index) => {
{mainData.map((character, index) => {
return (
// biome-ignore lint/suspicious/noArrayIndexKey: <Index is unique for each page in this case>
<View key={index} style={styles.page}>
<FlashList
ref={(ref) => {
listRefs.current[index] = ref;
}}
data={list}
data={getData(character, props.inventoryPages)}
renderItem={UiCellRenderItem}
keyExtractor={keyExtractor}
estimatedItemSize={pageEstimatedFlashListItemSize[index]}
Expand Down
5 changes: 2 additions & 3 deletions native/app/screens/WeaponsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { InventoryPageEnums } from "@/app/inventory/Common.ts";
import InventoryPage from "@/app/screens/InventoryPage.tsx";
import { useGGStore } from "@/app/store/GGStore.ts";

export default function WeaponsPage() {
const weaponsPageData = useGGStore((state) => state.weaponsPageData);
return <InventoryPage inventoryPageData={weaponsPageData} />;
return <InventoryPage inventoryPages={InventoryPageEnums.Weapons} />;
}
33 changes: 29 additions & 4 deletions native/app/store/AccountInventoryLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,47 @@ import {
} from "@/app/utilities/Constants.ts";
import { typeAndPowerSort } from "@/app/utilities/Helpers.ts";
import { create } from "mutative";
import { deepEqual } from "fast-equals";

// ------------------------------
// UI data creation
// ------------------------------

export function updateAllPages(get: AccountSliceGetter, set: AccountSliceSetter) {
const p1 = performance.now();
createUIData(get);
const p1 = performance.now();

const weaponsPageData = buildUIData(get, weaponsPageBuckets);
const ggCharacters = get().ggCharacters;

const armorPageData = buildUIData(get, armorPageBuckets);
const generalPageData = buildUIData(get, generalPageBuckets);
const p2 = performance.now();
console.log("built UI data for all pages", `${(p2 - p1).toFixed(4)} ms`);
set({ weaponsPageData, armorPageData, generalPageData });
console.log("buildUIData took:", `${(p2 - p1).toFixed(4)} ms`);

const updatedGGCharacters = create(ggCharacters, (draft) => {
let index = 0;
for (const ggCharacter of draft) {
const newWeaponsPageData = weaponsPageData[index];

if (newWeaponsPageData && !deepEqual(ggCharacter.weaponsPageData, newWeaponsPageData)) {
ggCharacter.weaponsPageData = newWeaponsPageData;
}
const newArmorPageData = armorPageData[index];
if (newArmorPageData && !deepEqual(ggCharacter.armorPageData, newArmorPageData)) {
ggCharacter.armorPageData = newArmorPageData;
}

const newGeneralPageData = generalPageData[index];
if (newGeneralPageData && !deepEqual(ggCharacter.generalPageData, newGeneralPageData)) {
ggCharacter.generalPageData = newGeneralPageData;
}
index++;
}
});
set({ ggCharacters: updatedGGCharacters });
const p3 = performance.now();
console.log("Rebuild UI took:", `${(p3 - p2).toFixed(4)} ms`);
console.log("rebuild UI took:", `${(p3 - p2).toFixed(4)} ms`);
}

function createUIData(get: AccountSliceGetter) {
Expand Down
6 changes: 6 additions & 0 deletions native/app/store/AccountLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ export function getCharactersAndVault(guardians: Record<string, Guardian>): GGCh
secondarySpecial,
lastActiveCharacter: false,
ggCharacterType: GGCharacterType.Vault,
armorPageData: [],
generalPageData: [],
weaponsPageData: [],
};
ggCharacters.push(vaultData);

Expand All @@ -68,6 +71,9 @@ function addCharacterDefinition(guardianData: GuardianData): GGCharacterUiData {
secondarySpecial: "",
lastActiveCharacter: false,
ggCharacterType: GGCharacterType.Guardian,
armorPageData: [],
generalPageData: [],
weaponsPageData: [],
};

return data;
Expand Down
14 changes: 1 addition & 13 deletions native/app/store/AccountSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,7 @@ import {
type ProfileData,
type VaultData,
} from "@/app/bungie/Types.ts";
import {
SectionBuckets,
characterBuckets,
iconUrl,
type DestinyItemIdentifier,
type UISections,
} from "@/app/inventory/Common.ts";
import { SectionBuckets, characterBuckets, iconUrl, type DestinyItemIdentifier } from "@/app/inventory/Common.ts";
import { findDestinyItem, getCharactersAndVault } from "@/app/store/AccountLogic.ts";
import {
bucketTypeHashArray,
Expand Down Expand Up @@ -57,9 +51,6 @@ export interface AccountSlice {
// dictates
ggCharacters: GGCharacterUiData[];

armorPageData: UISections[][];
generalPageData: UISections[][];
weaponsPageData: UISections[][];
selectedItem: DestinyItem | null;

responseMintedTimestamp: Date;
Expand Down Expand Up @@ -90,9 +81,6 @@ export const createAccountSlice: StateCreator<IStore, [], [], AccountSlice> = (s

ggCharacters: [],

armorPageData: [],
generalPageData: [],
weaponsPageData: [],
selectedItem: null,

responseMintedTimestamp: new Date(1977),
Expand Down
4 changes: 1 addition & 3 deletions native/app/store/AuthenticationSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,7 @@ export const createAuthenticationSlice: StateCreator<IStore, [], [], Authenticat
bungieUser: initialBungieUser,
authToken: null,
authenticated: "NO-AUTHENTICATION",
armorPageData: [],
generalPageData: [],
weaponsPageData: [],
ggCharacters: [],
responseMintedTimestamp: new Date(1977),
secondaryComponentsMintedTimestamp: new Date(1977),
});
Expand Down

0 comments on commit 20a0a5b

Please sign in to comment.