diff --git a/src/components/Community.tsx b/src/components/Community.tsx index 297f4d2..ca8b33f 100644 --- a/src/components/Community.tsx +++ b/src/components/Community.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react'; -import { Search, Globe, Users, Eye, ArrowLeftRight, Loader2, Clock, History, UserPlus, UserMinus, Check, X, Send, Settings, Save, ChevronLeft } from 'lucide-react'; +import { Search, Globe, Users, Eye, ArrowLeftRight, Loader2, Clock, History, UserPlus, UserMinus, Check, X, Send, Settings, Save, ChevronLeft, RefreshCw, Plus, Minus } from 'lucide-react'; import { useAuth } from '../contexts/AuthContext'; import { useToast } from '../contexts/ToastContext'; import { supabase } from '../lib/supabase'; @@ -64,6 +64,9 @@ export default function Community() { const [loadingCollection, setLoadingCollection] = useState(false); const [showTradeCreator, setShowTradeCreator] = useState(false); const [userCollectionSearch, setUserCollectionSearch] = useState(''); + const [hoveredUserCard, setHoveredUserCard] = useState(null); + const [selectedUserCard, setSelectedUserCard] = useState(null); + const [userCardFaceIndex, setUserCardFaceIndex] = useState>(new Map()); // Friends state const [friendsSubTab, setFriendsSubTab] = useState('list'); @@ -224,6 +227,44 @@ export default function Community() { }; }, [user, selectedUser]); + // Helper function to check if a card has an actual back face + const isDoubleFaced = (card: Card) => { + const backFaceLayouts = ['transform', 'modal_dfc', 'double_faced_token', 'reversible_card']; + return card.card_faces && card.card_faces.length > 1 && backFaceLayouts.includes(card.layout); + }; + + // Helper function to get the current face index for a card + const getCurrentFaceIndex = (cardId: string) => { + return userCardFaceIndex.get(cardId) || 0; + }; + + // Helper function to get the image URI for a card + const getCardImageUri = (card: Card, faceIndex: number = 0) => { + if (isDoubleFaced(card) && card.card_faces) { + return card.card_faces[faceIndex]?.image_uris?.normal || card.card_faces[faceIndex]?.image_uris?.small; + } + return card.image_uris?.normal || card.image_uris?.small; + }; + + // Helper function to get the large image URI for hover preview + const getCardLargeImageUri = (card: Card, faceIndex: number = 0) => { + if (isDoubleFaced(card) && card.card_faces) { + return card.card_faces[faceIndex]?.image_uris?.large || card.card_faces[faceIndex]?.image_uris?.normal; + } + return card.image_uris?.large || card.image_uris?.normal; + }; + + // Toggle card face + const toggleCardFace = (cardId: string, totalFaces: number) => { + setUserCardFaceIndex(prev => { + const newMap = new Map(prev); + const currentIndex = prev.get(cardId) || 0; + const nextIndex = (currentIndex + 1) % totalFaces; + newMap.set(cardId, nextIndex); + return newMap; + }); + }; + const loadAllData = async () => { if (!user) return; setLoading(true); @@ -532,74 +573,266 @@ export default function Community() { ); return ( -
- {/* Header */} -
-
+
+
+ {/* Header with Back and Trade buttons */} +
-

{selectedUser.username}

+

{selectedUser.username}'s Collection

+ {/* Search input */} -
- - setUserCollectionSearch(e.target.value)} - placeholder="Search cards..." - className="w-full pl-9 pr-8 py-2 bg-gray-700 border border-gray-600 rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent" - /> - {userCollectionSearch && ( - +
+
+ + setUserCollectionSearch(e.target.value)} + placeholder="Search cards by name, type, or text..." + className="w-full pl-10 pr-4 py-2 bg-gray-800 border border-gray-700 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" + /> +
+
+ + {/* Collection */} +
+

+ {userCollectionSearch + ? `Found ${filteredUserCollection.length} card(s)` + : `Cards (${selectedUserCollection.length} unique, ${selectedUserCollection.reduce((acc, c) => acc + c.quantity, 0)} total)` + } +

+ + {loadingCollection ? ( +
+ +
+ ) : selectedUserCollection.length === 0 ? ( +
+

Empty collection

+
+ ) : filteredUserCollection.length === 0 ? ( +
+

No cards found

+

Try a different search term

+
+ ) : ( +
+ {filteredUserCollection.map(({ card, quantity }) => { + const currentFaceIndex = getCurrentFaceIndex(card.id); + const isMultiFaced = isDoubleFaced(card); + const displayName = isMultiFaced && card.card_faces + ? card.card_faces[currentFaceIndex]?.name || card.name + : card.name; + + return ( +
setHoveredUserCard(card)} + onMouseLeave={() => setHoveredUserCard(null)} + onClick={() => setSelectedUserCard({ card, quantity })} + > + {/* Card thumbnail */} +
+ {displayName} + {/* Quantity badge */} +
+ x{quantity} +
+ {/* Flip button for double-faced cards */} + {isMultiFaced && ( + + )} +
+ + {/* Card name below thumbnail */} +
+ {displayName} +
+
+ ); + })} +
)}
- {/* Collection Grid */} -
- {loadingCollection ? ( -
- -
- ) : selectedUserCollection.length === 0 ? ( -

Empty collection

- ) : filteredUserCollection.length === 0 ? ( -

No cards match "{userCollectionSearch}"

- ) : ( -
- {filteredUserCollection.map(({ card, quantity }) => ( -
+ {/* Hover Card Preview - desktop only, only show if no card is selected */} + {hoveredUserCard && !selectedUserCard && (() => { + const currentFaceIndex = getCurrentFaceIndex(hoveredUserCard.id); + const isMultiFaced = isDoubleFaced(hoveredUserCard); + const currentFace = isMultiFaced && hoveredUserCard.card_faces + ? hoveredUserCard.card_faces[currentFaceIndex] + : null; + + const displayName = currentFace?.name || hoveredUserCard.name; + const displayTypeLine = currentFace?.type_line || hoveredUserCard.type_line; + const displayOracleText = currentFace?.oracle_text || hoveredUserCard.oracle_text; + + return ( +
+
+
{card.name} - - x{quantity} - + {isMultiFaced && ( +
+ Face {currentFaceIndex + 1}/{hoveredUserCard.card_faces!.length} +
+ )}
- ))} +
+

{displayName}

+

{displayTypeLine}

+ {displayOracleText && ( +

+ {displayOracleText} +

+ )} + {hoveredUserCard.prices?.usd && ( +
+ ${hoveredUserCard.prices.usd} +
+ )} +
+
- )} -
+ ); + })()} + + {/* Card Detail Panel - slides in from right */} + {selectedUserCard && (() => { + const currentFaceIndex = getCurrentFaceIndex(selectedUserCard.card.id); + const isMultiFaced = isDoubleFaced(selectedUserCard.card); + const currentFace = isMultiFaced && selectedUserCard.card.card_faces + ? selectedUserCard.card.card_faces[currentFaceIndex] + : null; + + const displayName = currentFace?.name || selectedUserCard.card.name; + const displayTypeLine = currentFace?.type_line || selectedUserCard.card.type_line; + const displayOracleText = currentFace?.oracle_text || selectedUserCard.card.oracle_text; + + return ( + <> + {/* Backdrop */} +
setSelectedUserCard(null)} + /> + + {/* Sliding Panel */} +
+ {/* Close button */} + + +
+ {/* Card Image */} +
+ {displayName} + {isMultiFaced && ( + <> +
+ Face {currentFaceIndex + 1}/{selectedUserCard.card.card_faces!.length} +
+ + + )} +
+ + {/* Card Info */} +
+
+

{displayName}

+

{displayTypeLine}

+
+ + {displayOracleText && ( +
+

{displayOracleText}

+
+ )} + + {selectedUserCard.card.prices?.usd && ( +
+
+ ${selectedUserCard.card.prices.usd} each +
+
+ Total value: ${(parseFloat(selectedUserCard.card.prices.usd) * selectedUserCard.quantity).toFixed(2)} +
+
+ )} + + {/* Quantity Display */} +
+

Quantity in Collection

+
+
+
{selectedUserCard.quantity}
+
copies
+
+
+
+
+
+
+ + ); + })()} {showTradeCreator && (