feat: Refine session clear to preserve UI preferences while resetting game state and standardize image cache paths to full and crop subdirectories.
Some checks failed
Build and Deploy / build (push) Failing after 1m0s

This commit is contained in:
2025-12-18 20:41:01 +01:00
parent bc5eda5e2a
commit 49080d8233
11 changed files with 130 additions and 33 deletions

View File

@@ -454,24 +454,27 @@ export const CubeManager: React.FC<CubeManagerProps> = ({ packs, setPacks, avail
localStorage.removeItem('generatedPacks');
localStorage.removeItem('availableLands');
// 3. Reset Local State
// 3. Reset Local State to Defaults
// This will trigger the useEffect hooks to update localStorage accordingly
setInputText('');
setRawScryfallData(null);
setProcessedData(null);
setSelectedSets([]);
setSearchTerm(''); // Clear search
// 4. Clear Local Persistence
localStorage.removeItem('cube_inputText');
localStorage.removeItem('cube_rawScryfallData');
localStorage.removeItem('cube_selectedSets');
localStorage.removeItem('cube_viewMode');
localStorage.removeItem('cube_gameTypeFilter');
// We can optionally clear source mode, or leave it. Let's leave it for UX continuity or clear it?
// Let's clear it to full reset.
// localStorage.removeItem('cube_sourceMode');
setFilters({
ignoreBasicLands: false,
ignoreCommander: false,
ignoreTokens: false
});
// 5. Reset UI Filters/Views to defaults
setViewMode('list');
setGenSettings({
mode: 'mixed',
rarityMode: 'peasant'
});
setSourceMode('upload');
setNumBoxes(1);
setGameTypeFilter('all');
showToast("Session cleared successfully.", "success");

View File

@@ -107,10 +107,10 @@ export class PackGeneratorService {
layout: layout,
colors: cardData.colors || [],
image: useLocalImages
? `${window.location.origin}/cards/images/${cardData.set}/art_full/${cardData.id}.jpg`
? `${window.location.origin}/cards/images/${cardData.set}/full/${cardData.id}.jpg`
: (cardData.image_uris?.normal || cardData.card_faces?.[0]?.image_uris?.normal || ''),
imageArtCrop: useLocalImages
? `${window.location.origin}/cards/images/${cardData.set}/art_crop/${cardData.id}.jpg`
? `${window.location.origin}/cards/images/${cardData.set}/crop/${cardData.id}.jpg`
: (cardData.image_uris?.art_crop || cardData.card_faces?.[0]?.image_uris?.art_crop || ''),
set: cardData.set_name,
setCode: cardData.set,

View File

@@ -116,6 +116,16 @@ app.get('/api/sets', async (_req: Request, res: Response) => {
app.get('/api/sets/:code/cards', async (req: Request, res: Response) => {
try {
const cards = await scryfallService.fetchSetCards(req.params.code);
// Implicitly cache images for these cards so local URLs work
if (cards.length > 0) {
console.log(`[API] Triggering image cache for set ${req.params.code} (${cards.length} potential images)...`);
// We await this to ensure images are ready before user views them,
// although it might slow down the "Fetching..." phase.
// Given the user requirement "upon downloading metadata, also ... must be cached", we wait.
await cardService.cacheImages(cards);
}
res.json(cards);
} catch (e: any) {
res.status(500).json({ error: e.message });
@@ -131,6 +141,12 @@ app.post('/api/cards/parse', async (req: Request, res: Response) => {
const uniqueIds = identifiers.map(id => id.type === 'id' ? { id: id.value } : { name: id.value });
const uniqueCards = await scryfallService.fetchCollection(uniqueIds);
// Cache Images for the resolved cards
if (uniqueCards.length > 0) {
console.log(`[API] Triggering image cache for parsed lists (${uniqueCards.length} unique cards)...`);
await cardService.cacheImages(uniqueCards);
}
// Expand
const expanded: any[] = [];
const cardMap = new Map();

View File

@@ -9,17 +9,11 @@ const __dirname = path.dirname(__filename);
const CARDS_DIR = path.join(__dirname, '../public/cards');
export class CardService {
private imagesDir: string;
// Remove imagesDir property as we use CARDS_DIR directly
private metadataDir: string;
constructor() {
this.imagesDir = path.join(CARDS_DIR, 'images');
this.metadataDir = path.join(CARDS_DIR, 'metadata');
// Directory creation is handled by FileStorageManager on write for Local,
// and not needed for Redis.
// Migration logic removed as it's FS specific and one-time.
// If we need migration to Redis, it should be a separate script.
}
async cacheImages(cards: any[]): Promise<number> {
@@ -54,9 +48,9 @@ export class CardService {
const tasks: Promise<void>[] = [];
// Task 1: Normal Image (art_full)
// Task 1: Normal Image (full)
if (imageUrl) {
const filePath = path.join(this.imagesDir, setCode, 'art_full', `${uuid}.jpg`);
const filePath = path.join(CARDS_DIR, 'images', setCode, 'full', `${uuid}.jpg`);
tasks.push((async () => {
if (await fileStorageManager.exists(filePath)) return;
try {
@@ -65,19 +59,19 @@ export class CardService {
const buffer = await response.arrayBuffer();
await fileStorageManager.saveFile(filePath, Buffer.from(buffer));
downloadedCount++;
console.log(`Cached art_full: ${setCode}/${uuid}.jpg`);
console.log(`Cached full: ${setCode}/${uuid}.jpg`);
} else {
console.error(`Failed to download art_full ${imageUrl}: ${response.statusText}`);
console.error(`Failed to download full ${imageUrl}: ${response.statusText}`);
}
} catch (err) {
console.error(`Error downloading art_full for ${uuid}:`, err);
console.error(`Error downloading full for ${uuid}:`, err);
}
})());
}
// Task 2: Art Crop (art_crop)
// Task 2: Art Crop (crop)
if (cropUrl) {
const cropPath = path.join(this.imagesDir, setCode, 'art_crop', `${uuid}.jpg`);
const cropPath = path.join(CARDS_DIR, 'images', setCode, 'crop', `${uuid}.jpg`);
tasks.push((async () => {
if (await fileStorageManager.exists(cropPath)) return;
try {
@@ -85,12 +79,12 @@ export class CardService {
if (response.ok) {
const buffer = await response.arrayBuffer();
await fileStorageManager.saveFile(cropPath, Buffer.from(buffer));
console.log(`Cached art_crop: ${setCode}/${uuid}.jpg`);
console.log(`Cached crop: ${setCode}/${uuid}.jpg`);
} else {
console.error(`Failed to download art_crop ${cropUrl}: ${response.statusText}`);
console.error(`Failed to download crop ${cropUrl}: ${response.statusText}`);
}
} catch (err) {
console.error(`Error downloading art_crop for ${uuid}:`, err);
console.error(`Error downloading crop for ${uuid}:`, err);
}
})());
}

View File

@@ -98,8 +98,8 @@ export class PackGeneratorService {
typeLine: typeLine,
layout: layout,
colors: cardData.colors || [],
image: cardData.image_uris?.normal || cardData.card_faces?.[0]?.image_uris?.normal || '',
imageArtCrop: cardData.image_uris?.art_crop || cardData.card_faces?.[0]?.image_uris?.art_crop || '',
image: `/cards/images/${cardData.set}/full/${cardData.id}.jpg`,
imageArtCrop: `/cards/images/${cardData.set}/crop/${cardData.id}.jpg`,
set: cardData.set_name,
setCode: cardData.set,
setType: setType,