/** * Card detail modal - shows full card info with quantity controls. */ import React, { useState } from "react"; import { StyleSheet, View, Text, Image, Pressable, ScrollView, ActivityIndicator, Alert, Dimensions, Platform, } from "react-native"; import { StatusBar } from "expo-status-bar"; import { useLocalSearchParams, useRouter } from "expo-router"; import { FontAwesome } from "@expo/vector-icons"; import { useQuery, useMutation } from "convex/react"; import { api } from "../convex/_generated/api"; import { useCurrentUser } from "@/lib/hooks"; const { width: SCREEN_WIDTH } = Dimensions.get("window"); const CARD_WIDTH = SCREEN_WIDTH - 64; const CARD_HEIGHT = CARD_WIDTH * (88 / 63); export default function CardDetailModal() { const router = useRouter(); const params = useLocalSearchParams<{ collectionEntryId: string; cardId: string; }>(); // Get authenticated user const { user } = useCurrentUser(); const userId = user?._id ?? null; const [isUpdating, setIsUpdating] = useState(false); // Fetch card details const card = useQuery( api.cards.getByScryfallId, params.cardId ? { scryfallId: params.cardId } : "skip" ); // Fetch collection entry const collection = useQuery( api.collections.getByUser, userId ? { userId: userId as any } : "skip" ); const collectionEntry = collection?.find( (entry) => entry._id === params.collectionEntryId ); // Mutations const updateQuantity = useMutation(api.collections.updateQuantity); const removeCard = useMutation(api.collections.remove); const handleQuantityChange = async (delta: number) => { if (!collectionEntry) return; const newQuantity = collectionEntry.quantity + delta; if (newQuantity <= 0) { handleRemove(); return; } setIsUpdating(true); try { await updateQuantity({ entryId: collectionEntry._id, quantity: newQuantity, }); } catch (error) { console.error("Failed to update quantity:", error); Alert.alert("Error", "Failed to update quantity"); } finally { setIsUpdating(false); } }; const handleRemove = () => { Alert.alert( "Remove Card", `Remove ${card?.name || "this card"} from your collection?`, [ { text: "Cancel", style: "cancel" }, { text: "Remove", style: "destructive", onPress: async () => { if (!collectionEntry) return; setIsUpdating(true); try { await removeCard({ entryId: collectionEntry._id }); router.back(); } catch (error) { console.error("Failed to remove card:", error); Alert.alert("Error", "Failed to remove card"); setIsUpdating(false); } }, }, ] ); }; // Loading state if (!card || !collectionEntry) { return ( ); } return ( {/* Card image */} {card.imageUri ? ( ) : ( )} {/* Card info */} {card.name} Set {card.setCode.toUpperCase()} Number {card.collectorNumber} Rarity {card.rarity} {card.artist && ( {card.artist} )} {/* Quantity controls */} Quantity handleQuantityChange(-1)} disabled={isUpdating} > {isUpdating ? ( ) : ( {collectionEntry.quantity} )} handleQuantityChange(1)} disabled={isUpdating} > {collectionEntry.isFoil && ( Foil )} {/* Added date */} Added {new Date(collectionEntry.addedAt).toLocaleDateString()} {/* Actions */} Remove from Collection ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: "#1a1a1a", }, content: { paddingBottom: 40, }, loadingContainer: { flex: 1, justifyContent: "center", alignItems: "center", backgroundColor: "#1a1a1a", }, imageContainer: { alignItems: "center", paddingTop: 20, paddingBottom: 24, }, cardImage: { width: CARD_WIDTH, height: CARD_HEIGHT, borderRadius: 12, backgroundColor: "#2a2a2a", }, cardPlaceholder: { justifyContent: "center", alignItems: "center", }, infoContainer: { paddingHorizontal: 24, marginBottom: 24, }, cardName: { color: "#fff", fontSize: 24, fontWeight: "bold", textAlign: "center", marginBottom: 16, }, metaRow: { flexDirection: "row", justifyContent: "center", gap: 24, }, metaItem: { alignItems: "center", }, metaLabel: { color: "#666", fontSize: 12, textTransform: "uppercase", marginBottom: 4, }, metaValue: { color: "#fff", fontSize: 16, fontWeight: "600", }, artistRow: { flexDirection: "row", justifyContent: "center", alignItems: "center", gap: 8, marginTop: 16, }, artistText: { color: "#888", fontSize: 14, fontStyle: "italic", }, quantityContainer: { backgroundColor: "#2a2a2a", marginHorizontal: 24, padding: 20, borderRadius: 12, alignItems: "center", marginBottom: 16, }, quantityLabel: { color: "#888", fontSize: 12, textTransform: "uppercase", marginBottom: 12, }, quantityControls: { flexDirection: "row", alignItems: "center", gap: 20, }, quantityButton: { width: 44, height: 44, borderRadius: 22, backgroundColor: "#007AFF", justifyContent: "center", alignItems: "center", }, quantityDisplay: { width: 60, alignItems: "center", }, quantityValue: { color: "#fff", fontSize: 28, fontWeight: "bold", }, foilBadge: { flexDirection: "row", alignItems: "center", gap: 6, marginTop: 12, paddingHorizontal: 12, paddingVertical: 6, backgroundColor: "rgba(255,215,0,0.1)", borderRadius: 6, }, foilText: { color: "#FFD700", fontSize: 12, fontWeight: "600", }, addedDate: { color: "#666", fontSize: 12, textAlign: "center", marginBottom: 24, }, actions: { paddingHorizontal: 24, }, actionButton: { flexDirection: "row", justifyContent: "center", alignItems: "center", gap: 8, padding: 16, borderRadius: 12, }, removeButton: { backgroundColor: "rgba(255,107,107,0.1)", borderWidth: 1, borderColor: "rgba(255,107,107,0.3)", }, removeButtonText: { color: "#FF6B6B", fontSize: 16, fontWeight: "600", }, });