Files
deckerr/src/services/api.ts

260 lines
6.9 KiB
TypeScript

import { Card } from '../types';
import { supabase } from '../lib/supabase';
const SCRYFALL_API = 'https://api.scryfall.com';
export const searchCards = async (query: string): Promise<Card[]> => {
const response = await fetch(`${SCRYFALL_API}/cards/search?q=${query}`);
const data = await response.json();
return data.data;
};
export const getRandomCards = async (count: number = 10): Promise<Card[]> => {
const cards: Card[] = [];
for (let i = 0; i < count; i++) {
const response = await fetch(`${SCRYFALL_API}/cards/random`);
const card = await response.json();
cards.push(card);
}
return cards;
};
export const getCardById = async (cardId: string): Promise<Card> => {
const response = await fetch(`${SCRYFALL_API}/cards/${cardId}`);
return await response.json();
};
const chunkArray = (array: string[], size: number): string[][] => {
const chunkedArray: string[][] = [];
for (let i = 0; i < array.length; i += size) {
chunkedArray.push(array.slice(i, i + size));
}
return chunkedArray;
};
export const getCardsByIds = async (cardIds: string[]): Promise<Card[]> => {
const chunkedCardIds = chunkArray(cardIds, 75);
let allCards: Card[] = [];
for (const chunk of chunkedCardIds) {
const response = await fetch(`${SCRYFALL_API}/cards/collection`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
identifiers: chunk.map((id) => ({ id })),
}),
});
const data = await response.json();
allCards = allCards.concat(data.data);
}
return allCards;
};
// Collection API functions
export const getUserCollection = async (userId: string): Promise<Map<string, number>> => {
const { data, error } = await supabase
.from('collections')
.select('card_id, quantity')
.eq('user_id', userId);
if (error) {
console.error('Error fetching user collection:', error);
throw error;
}
// Create a map of card_id to quantity for easy lookup
const collectionMap = new Map<string, number>();
data?.forEach((item) => {
collectionMap.set(item.card_id, item.quantity);
});
return collectionMap;
};
// Paginated collection API
export interface PaginatedCollectionResult {
items: Map<string, number>; // card_id -> quantity
totalCount: number;
hasMore: boolean;
}
// Get total collection value from database (lightweight query)
export const getCollectionTotalValue = async (userId: string): Promise<number> => {
const { data, error } = await supabase
.from('collections')
.select('price_usd, quantity')
.eq('user_id', userId);
if (error) {
console.error('Error fetching collection total value:', error);
return 0;
}
// Calculate total: sum of (price * quantity) for each card
const totalValue = data?.reduce((total, item) => {
const price = item.price_usd || 0;
const quantity = item.quantity || 0;
return total + (price * quantity);
}, 0) || 0;
return totalValue;
};
export const getUserCollectionPaginated = async (
userId: string,
pageSize: number = 50,
offset: number = 0
): Promise<PaginatedCollectionResult> => {
// First, get the total count
const { count: totalCount, error: countError } = await supabase
.from('collections')
.select('*', { count: 'exact', head: true })
.eq('user_id', userId);
if (countError) {
console.error('Error counting user collection:', countError);
throw countError;
}
// Then get the paginated data
const { data, error } = await supabase
.from('collections')
.select('card_id, quantity')
.eq('user_id', userId)
.order('created_at', { ascending: false })
.range(offset, offset + pageSize - 1);
if (error) {
console.error('Error fetching user collection:', error);
throw error;
}
// Create a map of card_id to quantity for easy lookup
const collectionMap = new Map<string, number>();
data?.forEach((item) => {
collectionMap.set(item.card_id, item.quantity);
});
return {
items: collectionMap,
totalCount: totalCount || 0,
hasMore: offset + pageSize < (totalCount || 0),
};
};
export const addCardToCollection = async (
userId: string,
cardId: string,
quantity: number = 1,
priceUsd: number = 0
): Promise<void> => {
// Check if card already exists in collection
const { data: existing, error: fetchError } = await supabase
.from('collections')
.select('id, quantity')
.eq('user_id', userId)
.eq('card_id', cardId)
.single();
if (fetchError && fetchError.code !== 'PGRST116') {
// PGRST116 is "not found" error, which is expected for new cards
throw fetchError;
}
if (existing) {
// Update existing card quantity and price
const { error: updateError } = await supabase
.from('collections')
.update({
quantity: existing.quantity + quantity,
price_usd: priceUsd,
updated_at: new Date().toISOString()
})
.eq('id', existing.id);
if (updateError) throw updateError;
} else {
// Insert new card
const { error: insertError } = await supabase
.from('collections')
.insert({
user_id: userId,
card_id: cardId,
quantity: quantity,
price_usd: priceUsd,
});
if (insertError) throw insertError;
}
};
export const addMultipleCardsToCollection = async (
userId: string,
cards: { cardId: string; quantity: number; priceUsd?: number }[]
): Promise<void> => {
// Fetch existing cards in collection
const cardIds = cards.map(c => c.cardId);
const { data: existingCards, error: fetchError } = await supabase
.from('collections')
.select('card_id, quantity, id')
.eq('user_id', userId)
.in('card_id', cardIds);
if (fetchError) throw fetchError;
const existingMap = new Map<string, { id: string; quantity: number }>();
existingCards?.forEach((item) => {
existingMap.set(item.card_id, { id: item.id, quantity: item.quantity });
});
const toInsert = [];
const toUpdate = [];
for (const card of cards) {
const existing = existingMap.get(card.cardId);
if (existing) {
toUpdate.push({
id: existing.id,
quantity: existing.quantity + card.quantity,
price_usd: card.priceUsd || 0,
updated_at: new Date().toISOString(),
});
} else {
toInsert.push({
user_id: userId,
card_id: card.cardId,
quantity: card.quantity,
price_usd: card.priceUsd || 0,
});
}
}
// Perform bulk operations
if (toInsert.length > 0) {
const { error: insertError } = await supabase
.from('collections')
.insert(toInsert);
if (insertError) throw insertError;
}
if (toUpdate.length > 0) {
for (const update of toUpdate) {
const { error: updateError } = await supabase
.from('collections')
.update({
quantity: update.quantity,
price_usd: update.price_usd,
updated_at: update.updated_at
})
.eq('id', update.id);
if (updateError) throw updateError;
}
}
};