fix(inventory): address code review findings
Some checks failed
Deploy to Production / test (push) Failing after 31s
Some checks failed
Deploy to Production / test (push) Failing after 31s
- Replace setTimeout race in use-item flow with explicit Back button - Fix collector end handler to re-render current view instead of blanking - Add appendUseBackButton helper to attach navigation to use results - Remove unused isInventoryInteraction import - Fix rarity test type assertions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
|||||||
getEmptyInventoryMessage,
|
getEmptyInventoryMessage,
|
||||||
getItemDetailMessage,
|
getItemDetailMessage,
|
||||||
getDiscardConfirmMessage,
|
getDiscardConfirmMessage,
|
||||||
|
appendUseBackButton,
|
||||||
sortInventoryItems,
|
sortInventoryItems,
|
||||||
ITEMS_PER_PAGE,
|
ITEMS_PER_PAGE,
|
||||||
type InventoryEntry,
|
type InventoryEntry,
|
||||||
@@ -15,7 +16,6 @@ import {
|
|||||||
import { getLootboxResultMessage } from "@/modules/inventory/inventory.view";
|
import { getLootboxResultMessage } from "@/modules/inventory/inventory.view";
|
||||||
import {
|
import {
|
||||||
parseInventoryCustomId,
|
parseInventoryCustomId,
|
||||||
isInventoryInteraction,
|
|
||||||
executeItemUse,
|
executeItemUse,
|
||||||
} from "@/modules/inventory/inventory.interaction";
|
} from "@/modules/inventory/inventory.interaction";
|
||||||
import { UserError } from "@shared/lib/errors";
|
import { UserError } from "@shared/lib/errors";
|
||||||
@@ -192,31 +192,7 @@ async function setupCollector(
|
|||||||
try {
|
try {
|
||||||
const result = await executeItemUse(i, viewerId, selectedItemId);
|
const result = await executeItemUse(i, viewerId, selectedItemId);
|
||||||
const message = getLootboxResultMessage(result.results, result.item);
|
const message = getLootboxResultMessage(result.results, result.item);
|
||||||
await interaction.editReply(message as any);
|
await interaction.editReply(appendUseBackButton(message, viewerId) as any);
|
||||||
|
|
||||||
// After showing result, wait briefly then return to detail or list
|
|
||||||
setTimeout(async () => {
|
|
||||||
try {
|
|
||||||
const freshEntries = await inventoryService.getInventory(ownerId);
|
|
||||||
const freshSorted = sortInventoryItems(freshEntries as InventoryEntry[]);
|
|
||||||
const freshEntry = freshSorted.find(e => e.item.id === selectedItemId);
|
|
||||||
|
|
||||||
if (freshEntry) {
|
|
||||||
await interaction.editReply(
|
|
||||||
getItemDetailMessage(freshEntry, viewerId, ownerId)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
selectedItemId = null;
|
|
||||||
if (freshSorted.length === 0) {
|
|
||||||
await interaction.editReply(getEmptyInventoryMessage(username));
|
|
||||||
} else {
|
|
||||||
await interaction.editReply(
|
|
||||||
getInventoryListMessage(freshSorted, username, currentPage, viewerId, ownerId)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {}
|
|
||||||
}, 3000);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof UserError) {
|
if (error instanceof UserError) {
|
||||||
await interaction.editReply({
|
await interaction.editReply({
|
||||||
@@ -231,6 +207,27 @@ async function setupCollector(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case "use_back": {
|
||||||
|
// Return from use result to detail or list
|
||||||
|
if (!selectedItemId) break;
|
||||||
|
const entry = sorted.find(e => e.item.id === selectedItemId);
|
||||||
|
if (entry) {
|
||||||
|
await interaction.editReply(
|
||||||
|
getItemDetailMessage(entry, viewerId, ownerId)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
selectedItemId = null;
|
||||||
|
if (sorted.length === 0) {
|
||||||
|
await interaction.editReply(getEmptyInventoryMessage(username));
|
||||||
|
} else {
|
||||||
|
await interaction.editReply(
|
||||||
|
getInventoryListMessage(sorted, username, currentPage, viewerId, ownerId)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "discard": {
|
case "discard": {
|
||||||
if (viewerId !== ownerId || !selectedItemId) break;
|
if (viewerId !== ownerId || !selectedItemId) break;
|
||||||
const entry = sorted.find(e => e.item.id === selectedItemId);
|
const entry = sorted.find(e => e.item.id === selectedItemId);
|
||||||
@@ -293,7 +290,33 @@ async function setupCollector(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
collector.on("end", () => {
|
collector.on("end", async () => {
|
||||||
interaction.editReply({ components: [] }).catch(() => {});
|
try {
|
||||||
|
// Re-render current view as static (no interactive components)
|
||||||
|
const entries = await inventoryService.getInventory(ownerId);
|
||||||
|
const sorted = sortInventoryItems(entries as InventoryEntry[]);
|
||||||
|
|
||||||
|
if (selectedItemId) {
|
||||||
|
const entry = sorted.find(e => e.item.id === selectedItemId);
|
||||||
|
if (entry) {
|
||||||
|
// Show detail view without action buttons
|
||||||
|
const msg = getItemDetailMessage(entry, viewerId, ownerId);
|
||||||
|
// Replace components with empty to remove buttons but keep container content
|
||||||
|
await interaction.editReply(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sorted.length === 0) {
|
||||||
|
await interaction.editReply(getEmptyInventoryMessage(username));
|
||||||
|
} else {
|
||||||
|
await interaction.editReply(
|
||||||
|
getInventoryListMessage(sorted, username, currentPage, viewerId, ownerId)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// If re-rendering fails, at least try to clear gracefully
|
||||||
|
interaction.editReply({ components: [] }).catch(() => {});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -289,6 +289,34 @@ export function getDiscardConfirmMessage(entry: InventoryEntry, viewerId: string
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps a use-item result message with a Back button so the user
|
||||||
|
* can return to the inventory after seeing the effect result.
|
||||||
|
*/
|
||||||
|
export function appendUseBackButton(message: any, viewerId: string): any {
|
||||||
|
const backRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setCustomId(`inv_use_back_${viewerId}`)
|
||||||
|
.setLabel("◀ Back to Inventory")
|
||||||
|
.setStyle(ButtonStyle.Primary)
|
||||||
|
);
|
||||||
|
|
||||||
|
// If CV2 message with components array, append to the first container
|
||||||
|
if (message.components && message.flags === MessageFlags.IsComponentsV2) {
|
||||||
|
const container = message.components[0];
|
||||||
|
if (container?.addActionRowComponents) {
|
||||||
|
container.addActionRowComponents(backRow);
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Embed-based fallback — add as a regular component row
|
||||||
|
return {
|
||||||
|
...message,
|
||||||
|
components: [...(message.components || []), backRow],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves an item URL (icon or image) for use in CV2 components.
|
* Resolves an item URL (icon or image) for use in CV2 components.
|
||||||
* Handles both local assets and remote URLs.
|
* Handles both local assets and remote URLs.
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ describe("getRarityConfig", () => {
|
|||||||
|
|
||||||
it("falls back to Common for unknown rarity", () => {
|
it("falls back to Common for unknown rarity", () => {
|
||||||
const result = getRarityConfig("LEGENDARY");
|
const result = getRarityConfig("LEGENDARY");
|
||||||
expect(result).toEqual(RARITY_CONFIG["C"]);
|
expect(result).toEqual(RARITY_CONFIG["C"]!);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("falls back to Common for null/undefined input", () => {
|
it("falls back to Common for null/undefined input", () => {
|
||||||
expect(getRarityConfig(null as any)).toEqual(RARITY_CONFIG["C"]);
|
expect(getRarityConfig(null as any)).toEqual(RARITY_CONFIG["C"]!);
|
||||||
expect(getRarityConfig(undefined as any)).toEqual(RARITY_CONFIG["C"]);
|
expect(getRarityConfig(undefined as any)).toEqual(RARITY_CONFIG["C"]!);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user