From c2a58683da08927cc3afe9d2170f4e2ddd24e8e7 Mon Sep 17 00:00:00 2001 From: Nigel Breslaw Date: Sat, 20 Apr 2024 12:28:16 +0300 Subject: [PATCH] refactor(transfer): Add transferBundle (#1341) This prepares for the equip system that will need multiple items to transfer to achieve an unequip action followed by any moves. --- native/app/screens/BottomSheet.tsx | 8 +- native/app/transfer/TransferLogic.ts | 177 ++++++++++++++++++--------- 2 files changed, 123 insertions(+), 62 deletions(-) diff --git a/native/app/screens/BottomSheet.tsx b/native/app/screens/BottomSheet.tsx index 9d34c5b76..1769a655c 100644 --- a/native/app/screens/BottomSheet.tsx +++ b/native/app/screens/BottomSheet.tsx @@ -1,7 +1,7 @@ import type { DestinyItem } from "@/app/bungie/Types.ts"; import { itemTypeDisplayName, itemsDefinition } from "@/app/store/Definitions.ts"; import { useGGStore } from "@/app/store/GGStore.ts"; -import { processTransferItem } from "@/app/transfer/TransferLogic.ts"; +import { startTransfer } from "@/app/transfer/TransferLogic.ts"; import { GLOBAL_INVENTORY_NAMES, VAULT_CHARACTER_ID } from "@/app/utilities/Constants.ts"; import type { NavigationProp, RouteProp } from "@react-navigation/native"; import { Image } from "expo-image"; @@ -279,8 +279,8 @@ export default function BottomSheet({ } }, []); - function startTransfer(targetId: string, quantity = 1, equipOnTarget = false) { - processTransferItem(targetId, destinyItem, quantity, equipOnTarget); + function transfer(targetId: string, quantity = 1, equipOnTarget = false) { + startTransfer(targetId, destinyItem, quantity, equipOnTarget); } return ( @@ -391,7 +391,7 @@ export default function BottomSheet({ } }} destinyItem={destinyItem} - startTransfer={startTransfer} + startTransfer={transfer} currentCharacterId={characterId} /> diff --git a/native/app/transfer/TransferLogic.ts b/native/app/transfer/TransferLogic.ts index 84c8134ad..775ce86cc 100644 --- a/native/app/transfer/TransferLogic.ts +++ b/native/app/transfer/TransferLogic.ts @@ -25,6 +25,17 @@ const responseSchema = object({ ThrottleSeconds: optional(number()), }); +export type TransferBundle = { + // The main item to be transferred + primaryItem: TransferItem; + // Other items that first need transferring e.g. unquip items + otherItem: TransferItem | null; + // Track failed transfers to see if the transfer should be retried. + fails: number; + // Changed to true when all transfers have been completed + completed: boolean; +}; + export type TransferItem = { destinyItem: DestinyItem; finalTargetId: string; @@ -32,42 +43,65 @@ export type TransferItem = { equipOnTarget: boolean; }; -export async function processTransferItem( +function createTransferBundle( toCharacterId: string, destinyItem: DestinyItem, quantityToMove = 1, equipOnTarget = false, -) { - if (DEBUG_TRANSFER) { - console.log( - "processTransferItem()", - "to guardian:", - DestinyClass[useGGStore.getState().guardians[toCharacterId]?.data.classType ?? 3], - "quantity:", +): TransferBundle { + return { + primaryItem: { + destinyItem, + finalTargetId: toCharacterId, quantityToMove, - "equipOnTarget:", equipOnTarget, - ); - } - - const transferItem: TransferItem = { - destinyItem, - finalTargetId: toCharacterId, - equipOnTarget, - quantityToMove, + }, + otherItem: null, + fails: 0, + completed: false, }; +} + +export function startTransfer( + toCharacterId: string, + destinyItem: DestinyItem, + quantityToMove = 1, + equipOnTarget = false, +) { + const transferBundle = createTransferBundle(toCharacterId, destinyItem, quantityToMove, equipOnTarget); + processTransfer(transferBundle); +} + +function showTransferSuccess(transferBundle: TransferBundle) { + const itemDefinition = itemsDefinition[transferBundle.primaryItem.destinyItem.itemHash]; + const successMessage = `${itemDefinition?.n} has been transferred${ + transferBundle.primaryItem.equipOnTarget ? " and equipped." : "." + }`; + console.info(successMessage); + useGGStore.getState().showSnackBar(successMessage); +} + +export async function processTransfer(transferBundle: TransferBundle) { + if (DEBUG_TRANSFER) { + console.log("processTransferItem()", transferBundle); + } // Is transfer complete? - if (hasSuccessfullyTransferred(transferItem)) { - const itemDefinition = itemsDefinition[transferItem.destinyItem.itemHash]; - const successMessage = `${itemDefinition?.n} has been transferred${ - transferItem.equipOnTarget ? " and equipped." : "." - }`; - console.info(successMessage); - useGGStore.getState().showSnackBar(successMessage); + const checkedPackage = hasSuccessfullyTransferred(transferBundle); + if (checkedPackage.completed) { + showTransferSuccess(transferBundle); return; } + let transferItem: TransferItem; + + // Is there other items to transfer first? + if (transferBundle.otherItem) { + transferItem = transferBundle.otherItem; + } else { + transferItem = transferBundle.primaryItem; + } + // Is the item currently in lost items? if (transferItem.destinyItem.bucketHash === SectionBuckets.LostItem) { const result = await pullFromPostmaster(transferItem); @@ -76,12 +110,14 @@ export async function processTransferItem( if (parsedResult.output.ErrorStatus === "Success") { // Update the UI and get a transformed item to continue the transfer const transformedItem = useGGStore.getState().pullFromPostmaster(result[1]); + // spread transformedItem into transferItem.destinyItem + transferItem.destinyItem = { ...transformedItem }; // Send the item on its way - processTransferItem(toCharacterId, transformedItem, quantityToMove, equipOnTarget); + processTransfer(transferBundle); return; } - console.error("Failed 2", parsedResult.output); + console.log("Failed 2", parsedResult.output); useGGStore.getState().showSnackBar(`Failed to transfer item ${parsedResult.output.Message} `); return; } @@ -89,14 +125,6 @@ export async function processTransferItem( return; } - if (transferItem.destinyItem.equipped) { - // First unquip it with another item - // const unequip = await unequipItem(transferItem.destinyItem); - // System.log(inventoryItem.name + " needs to be unequipped to move") - useGGStore.getState().showSnackBar("Unequip has not been implemented yet"); - return; - } - const reachedTarget = hasReachedTarget(transferItem); if (reachedTarget) { // This is only possible if the item needs to equipped @@ -108,12 +136,14 @@ export async function processTransferItem( const parsedResult = safeParse(responseSchema, result[0]); if (parsedResult.success) { if (parsedResult.output.ErrorStatus === "Success") { - processTransferItem(toCharacterId, result[1], quantityToMove, equipOnTarget); + transferItem.destinyItem = { ...result[1] }; + processTransfer(transferBundle); useGGStore.getState().equipItem(result[1]); + return; } console.error("Failed 1", parsedResult.output); - useGGStore.getState().showSnackBar(`Failed to transfer item ${parsedResult.output.Message} `); + useGGStore.getState().showSnackBar(`Failed to equip item ${parsedResult.output.Message} `); return; } console.error("Failed to equip item and failed to parse response"); @@ -123,30 +153,41 @@ export async function processTransferItem( useGGStore.getState().showSnackBar("Failed to equip item"); } } else { - try { - const result = await moveItem(transferItem); - const parsedResult = safeParse(responseSchema, result[0]); - - if (parsedResult.success) { - if (parsedResult.output.ErrorStatus === "Success") { - processTransferItem(toCharacterId, result[1], quantityToMove, equipOnTarget); - useGGStore.getState().moveItem(result[1]); + if (transferItem.destinyItem.equipped) { + try { + // First unquip it with another item + // const unequip = await unequipItem(transferItem.destinyItem); + // System.log(inventoryItem.name + " needs to be unequipped to move") + useGGStore.getState().showSnackBar("Unequip has not been implemented yet"); + return; + } catch {} + } else { + try { + const result = await moveItem(transferItem); + const parsedResult = safeParse(responseSchema, result[0]); + + if (parsedResult.success) { + if (parsedResult.output.ErrorStatus === "Success") { + transferItem.destinyItem = { ...result[1] }; + processTransfer(transferBundle); + useGGStore.getState().moveItem(result[1]); + return; + } + console.error("Failed 3", parsedResult.output); + useGGStore.getState().showSnackBar(`Failed to transfer item ${parsedResult.output.Message} `); return; } - console.error("Failed 3", parsedResult.output); - useGGStore.getState().showSnackBar(`Failed to transfer item ${parsedResult.output.Message} `); - return; + console.error("Failed to parse response", result[0]); + useGGStore.getState().showSnackBar("Failed to move item and Failed to parse response"); + } catch (e) { + console.error("Failed to move item", e); + useGGStore.getState().showSnackBar("Failed to move item"); } - console.error("Failed to parse response", result[0]); - useGGStore.getState().showSnackBar("Failed to move item and Failed to parse response"); - } catch (e) { - console.error("Failed to move item", e); - useGGStore.getState().showSnackBar("Failed to move item"); } - } - if (DEBUG_TRANSFER) { - console.log("transferItem got here..."); + if (DEBUG_TRANSFER) { + console.log("transferItem got here..."); + } } } @@ -184,17 +225,37 @@ function exoticAlreadyEquipped(destinyItem: DestinyItem): boolean { return false; } -function hasSuccessfullyTransferred(item: TransferItem) { +function hasSuccessfullyTransferred(transferBundle: TransferBundle) { + if (transferBundle.otherItem) { + if (itemSuccessfullyTransferred(transferBundle.otherItem)) { + transferBundle.otherItem = null; + } + } + + // if the array is not empty then return false + if (transferBundle.otherItem) { + return transferBundle; + } + + if (itemSuccessfullyTransferred(transferBundle.primaryItem)) { + transferBundle.completed = true; + return transferBundle; + } + + return transferBundle; +} + +function itemSuccessfullyTransferred(item: TransferItem) { const reachedTarget = item.destinyItem.characterId === item.finalTargetId; const inCorrectEquipState = item.destinyItem.equipped === item.equipOnTarget; - const lostItem = item.destinyItem.bucketHash === 215593132; + const lostItem = item.destinyItem.bucketHash === SectionBuckets.LostItem; return reachedTarget && inCorrectEquipState && !lostItem; } function hasReachedTarget(item: TransferItem) { const reachedTarget = item.destinyItem.characterId === item.finalTargetId; - const lostItem = item.destinyItem.bucketHash === 215593132; + const lostItem = item.destinyItem.bucketHash === SectionBuckets.LostItem; return reachedTarget && !lostItem; }