import React, { useEffect, useState, useRef, useCallback, useMemo } from 'react'; import { StyleSheet, TouchableOpacity, ActivityIndicator, FlatList, ScrollView, Image, TextInput, Platform, useWindowDimensions, RefreshControl } from 'react-native'; import { View, Text, useTheme } from '@bettoredge/styles'; import { Ionicons } from '@expo/vector-icons'; import type { CalcuttaEscrowProps } from '@bettoredge/types'; import { useCalcuttaAuction } from '../hooks/useCalcuttaAuction'; import { useCalcuttaCompetition } from '../hooks/useCalcuttaCompetition'; import { useCalcuttaEscrow } from '../hooks/useCalcuttaEscrow'; import { useCalcuttaBid } from '../hooks/useCalcuttaBid'; import { useCalcuttaSocket, CalcuttaPresence, CalcuttaPresencePlayer } from '../hooks/useCalcuttaSocket'; import { useCalcuttaItemImages } from '../hooks/useCalcuttaItemImages'; import { useCalcuttaPlayers } from '../hooks/useCalcuttaPlayers'; import { formatCurrency, getStatusLabel, resolveItemImageUrl } from '../helpers/formatting'; import { CalcuttaAuctionItem } from './CalcuttaAuctionItem'; import { CalcuttaEscrow } from './CalcuttaEscrow'; import { SealedBidAuction } from './sealed/SealedBidAuction'; import { AuctionPausedOverlay } from './AuctionPausedOverlay'; import { ItemSoldCelebration } from './ItemSoldCelebration'; import { AuctionCompleteOverlay } from './AuctionCompleteOverlay'; export interface CalcuttaAuctionProps { calcutta_competition_id: string; player_id?: string; onClose?: () => void; access_token?: string; device_id?: string; /** @deprecated Use access_token/device_id instead */ getSocket?: () => WebSocket | null; /** @deprecated Use access_token/device_id instead */ getAccessToken?: () => string | undefined; /** @deprecated Use access_token/device_id instead */ getDeviceId?: () => string | undefined; player_data?: { username: string; profile_pic?: string }; onPauseAuction?: () => Promise | void; onResumeAuction?: () => Promise | void; onNextItem?: () => Promise | void; player_balance?: number; onDepositFunds?: (amount: number) => void; onManage?: () => void; onJoin?: () => void | Promise; onLeave?: () => void | Promise; onRefresh?: () => void | Promise; onAuctionClosed?: () => void; } // Inner component for live auction (original layout) const LiveAuction: React.FC = ({ calcutta_competition_id, player_id, onClose, access_token, device_id, player_data, onPauseAuction, onResumeAuction, onNextItem, player_balance, onDepositFunds, onRefresh, onAuctionClosed: onAuctionClosedProp, }) => { const { theme } = useTheme(); const { width: windowWidth } = useWindowDimensions(); const [containerWidth, setContainerWidth] = useState(0); const measuredWidth = containerWidth || windowWidth; const isDesktop = Platform.OS === 'web' && measuredWidth >= 700; const [refreshing, setRefreshing] = useState(false); const { loading, competition, items, bids, my_bids, paused, escrow_summaries, total_escrow_balance, refresh: refreshAuction, stopPolling, handleBidUpdate, handleItemActive, handleItemSold, handleItemUnsold, handleAuctionPaused, handleAuctionResumed, handleAuctionClosed, } = useCalcuttaAuction(calcutta_competition_id, 5000, player_id); const { rounds, payout_rules } = useCalcuttaCompetition(calcutta_competition_id); const { escrow, fetchEscrow, updateEscrowLocally } = useCalcuttaEscrow(calcutta_competition_id); const { placeBid, loading: bidLoading } = useCalcuttaBid(); const { images: itemImages } = useCalcuttaItemImages(items); const isAdmin = player_id != null && competition?.admin_id === player_id; const handleRefresh = async () => { setRefreshing(true); try { await Promise.all([refreshAuction(), fetchEscrow(), onRefresh?.()]); } catch {} finally { setRefreshing(false); } }; // Auction complete overlay const [showAuctionComplete, setShowAuctionComplete] = useState(false); const wrappedHandleAuctionClosed = useCallback((data: any) => { handleAuctionClosed(data); // Show complete overlay after a short delay (let last item celebration finish) setTimeout(() => setShowAuctionComplete(true), 1000); onAuctionClosedProp?.(); }, [handleAuctionClosed, onAuctionClosedProp]); // Celebration modal state const [celebrationData, setCelebrationData] = useState<{ item?: any; winnerName?: string; winnerProfilePic?: string; winningBid?: number; isMe?: boolean; nextItem?: any; nextItemDeadline?: string; itemImage?: string; } | null>(null); // Wrap socket handlers to intercept item sold/active for celebration const wrappedHandleItemSold = useCallback((data: any) => { handleItemSold(data); // Find next pending item to preview const nextPending = items.find(i => i.status === 'pending' && i.calcutta_auction_item_id !== data.item.calcutta_auction_item_id ); setCelebrationData({ item: data.item, winnerName: data.winning_player_id === player_id ? 'You' : undefined, winningBid: data.winning_bid?.bid_amount ?? data.item.winning_bid, isMe: data.winning_player_id === player_id, nextItem: nextPending, itemImage: itemImages[data.item.item_id]?.url, }); }, [handleItemSold, items, player_id, itemImages]); const wrappedHandleItemUnsold = useCallback((data: any) => { handleItemUnsold(data); const nextPending = items.find(i => i.status === 'pending' && i.calcutta_auction_item_id !== data.item.calcutta_auction_item_id ); setCelebrationData({ item: data.item, winnerName: undefined, winningBid: 0, isMe: false, nextItem: nextPending, }); }, [handleItemUnsold, items]); // Socket connection for live auctions const { connected, presence, broadcast } = useCalcuttaSocket( calcutta_competition_id, access_token, device_id, player_data || { username: '' }, { onBidUpdate: (data: any) => { handleBidUpdate(data); // Update escrow from socket — check if our player's escrow is in the update if (player_id && data.escrow_updates?.[player_id]) { updateEscrowLocally(data.escrow_updates[player_id]); } }, onItemActive: handleItemActive, onItemSold: wrappedHandleItemSold, onItemUnsold: wrappedHandleItemUnsold, onAuctionPaused: handleAuctionPaused, onAuctionResumed: handleAuctionResumed, onAuctionClosed: wrappedHandleAuctionClosed, onRefresh: () => { refreshAuction(); }, }, ); // Resolve player profiles for bidders, winners, and presence const bidderIds = useMemo(() => { const ids = new Set(bids.map(b => b.player_id).filter(Boolean)); items.forEach(i => { if (i.winning_player_id) ids.add(i.winning_player_id); }); (presence?.players || []).forEach(p => { if (p.player_id) ids.add(p.player_id); }); return [...ids]; }, [bids, items, presence?.players]); const { players: bidderProfiles } = useCalcuttaPlayers(bidderIds); // Initial escrow fetch on mount useEffect(() => { fetchEscrow(); }, []); const handlePlaceBid = async (calcutta_auction_item_id: string, amount: number) => { const bid = await placeBid(calcutta_auction_item_id, amount); // Refresh own auction state + escrow immediately refreshAuction(); fetchEscrow(); // Broadcast to other clients via direct WS relay if (bid) { broadcast({ type: 'CALCUTTA_REFRESH', reason: 'bid' }); } }; const [escrowExpanded, setEscrowExpanded] = useState(true); const [hasAutoCollapsed, setHasAutoCollapsed] = useState(false); // Auto-collapse escrow panel once funds are loaded useEffect(() => { if (!hasAutoCollapsed && escrow && Number(escrow.escrow_balance) > 0) { setEscrowExpanded(false); setHasAutoCollapsed(true); } }, [escrow, hasAutoCollapsed]); // Presence bar visibility const [showPresenceList, setShowPresenceList] = useState(false); const [infoTab, setInfoTab] = useState<'items' | 'players' | 'escrow' | 'info' | 'completed_desktop' | null>('items'); const [selectedPlayerId, setSelectedPlayerId] = useState(null); const livePot = useMemo(() => items.filter((i: any) => i.status === 'sold').reduce((sum: number, i: any) => sum + Number(i.winning_bid || 0), 0) || Number(competition?.total_pot) || 0, [items, competition?.total_pot] ); if (loading && !competition) { return ( ); } if (!competition) { return ( Auction not found ); } const activeItem = items.find(i => i.calcutta_auction_item_id === competition.current_auction_item_id); const activeMyBid = activeItem ? my_bids.find(b => b.calcutta_auction_item_id === activeItem.calcutta_auction_item_id && b.bid_status === 'active') : undefined; // Resolve highest bidder for active item const activeHighestBid = activeItem ? bids.filter(b => b.calcutta_auction_item_id === activeItem.calcutta_auction_item_id && b.bid_status === 'active') .sort((a, b) => Number(b.bid_amount) - Number(a.bid_amount))[0] : undefined; const activeHighestBidder = activeHighestBid ? { username: activeHighestBid.player_id === player_id ? player_data?.username : bidderProfiles[activeHighestBid.player_id]?.username || presence.players.find(p => p.player_id === activeHighestBid.player_id)?.username, profile_pic: activeHighestBid.player_id === player_id ? player_data?.profile_pic : bidderProfiles[activeHighestBid.player_id]?.profile_pic, is_me: activeHighestBid.player_id === player_id, } : undefined; const pendingItems = items.filter(i => i.status === 'pending' && i.calcutta_auction_item_id !== competition.current_auction_item_id); const completedItems = items.filter(i => ['sold', 'unsold'].includes(i.status)); // ═══ SHARED RENDER HELPERS ═══ const renderItemRow = (item: any) => { const myBid = my_bids.find((b: any) => b.calcutta_auction_item_id === item.calcutta_auction_item_id && (item.status === 'sold' || item.status === 'unsold' ? true : b.bid_status === 'active')); const itemWinner = item.winning_player_id ? (() => { const wp = bidderProfiles[item.winning_player_id]; return wp ? { username: wp.username || wp.show_name, profile_pic: wp.profile_pic, is_me: item.winning_player_id === player_id } : item.winning_player_id === player_id ? { username: player_data?.username, profile_pic: player_data?.profile_pic, is_me: true } : undefined; })() : undefined; return ( handlePlaceBid(item.calcutta_auction_item_id, amount)} is_active_item={false} is_paused={paused} itemImage={itemImages[item.item_id]} winner={itemWinner} /> ); }; const renderPotBar = () => ( Pot {formatCurrency(livePot, competition.market_type)} Total Escrow {formatCurrency(total_escrow_balance, competition.market_type)} Items Sold {completedItems.filter(i => i.status === 'sold').length}/{items.length} ); const renderAdminControls = () => { if (!isAdmin || competition.auction_status !== 'in_progress' || paused) return null; return ( {onPauseAuction && ( { await onPauseAuction?.(); refreshAuction(); broadcast({ type: 'CALCUTTA_REFRESH', reason: 'paused' }); }} > Pause )} {onNextItem && ( { await onNextItem?.(); refreshAuction(); fetchEscrow(); broadcast({ type: 'CALCUTTA_REFRESH', reason: 'next_item' }); }} > Next Item )} ); }; const renderActiveItem = () => { if (!activeItem) return null; return ( handlePlaceBid(activeItem.calcutta_auction_item_id, amount)} is_active_item is_paused={paused} itemImage={itemImages[activeItem.item_id]} highest_bidder={activeHighestBidder} /> ); }; const renderPlayersContent = () => ( {(presence?.players || []).length > 0 ? (presence?.players || []).map((p: any) => { const profile = bidderProfiles[p.player_id]; const profilePic = profile?.profile_pic || p.profile_pic; const username = profile?.username || p.username || 'Player'; const isMe = p.player_id === player_id; const isSelected = selectedPlayerId === p.player_id; const wonItems = items.filter((i: any) => i.winning_player_id === p.player_id && i.status === 'sold'); return ( setSelectedPlayerId(isSelected ? null : p.player_id)} activeOpacity={0.7}> {profilePic ? ( ) : ( {username.charAt(0).toUpperCase()} )} {isMe ? 'You' : username} {wonItems.length > 0 ? `${wonItems.length} item${wonItems.length !== 1 ? 's' : ''} won` : 'No items'} {escrow_summaries[p.player_id] ? ` · ${formatCurrency(escrow_summaries[p.player_id].escrow_balance, competition.market_type)} avail` : ''} {wonItems.length > 0 && } {isSelected && wonItems.length > 0 && ( {wonItems.map((wi: any) => ( {wi.item_name} {formatCurrency(wi.winning_bid, competition.market_type)} ))} )} ); }) : ( No players connected )} ); const renderEscrowContent = () => ( fetchEscrow()} /> ); const renderInfoContent = () => { const livePot = items.filter((it: any) => it.status === 'sold').reduce((sum: number, it: any) => sum + Number(it.winning_bid || 0), 0) || Number(competition.total_pot) || 0; return ( {!isDesktop && competition.image?.url && ( )} {isDesktop && competition.image?.url && ( {competition.competition_name} {competition.competition_description ? {competition.competition_description} : null} )} {!(isDesktop && competition.image?.url) && ( <> {competition.competition_name} {competition.competition_description ? {competition.competition_description} : null} )} Auction Settings {[ ['Type', competition.auction_type === 'live' ? 'Live Auction' : competition.auction_type === 'sealed_bid' ? 'Sealed Bid' : 'Sweepstakes'], ['Entry Fee', Number(competition.entry_fee) > 0 ? formatCurrency(competition.entry_fee, competition.market_type) : 'Free'], Number(competition.min_bid) > 0 ? ['Min Bid', formatCurrency(competition.min_bid, competition.market_type)] : null, Number(competition.bid_increment) > 0 ? ['Bid Increment', formatCurrency(competition.bid_increment, competition.market_type)] : null, Number(competition.item_timer_seconds) > 0 ? ['Timer per Item', `${competition.item_timer_seconds}s`] : null, Number(competition.bid_extension_seconds) > 0 ? ['Bid Extension', `${competition.bid_extension_seconds}s`] : null, Number(competition.max_escrow) > 0 ? ['Budget Cap', formatCurrency(competition.max_escrow, competition.market_type)] : null, Number(competition.min_spend_pct) > 0 ? ['Min Spend', `${competition.min_spend_pct}%`] : null, ['Items', `${items.length}`], ].filter(Boolean).map((row: any, i) => ( {row[0]} {row[1]} ))} {payout_rules.length > 0 && ( Payouts LIVE Pot: {formatCurrency(livePot, competition.market_type)} {payout_rules.map((rule: any, i: number) => { const round = rounds.find((r: any) => r.round_number === rule.round_number); const amount = livePot > 0 ? (Number(rule.payout_pct) / 100) * livePot : 0; return ( {rule.description || round?.round_name || `Round ${rule.round_number}`} {rule.payout_pct}% {formatCurrency(amount, competition.market_type)} ); })} )} ); }; // ═══ RENDER ═══ return ( setContainerWidth(e.nativeEvent.layout.width)}> {/* Overlays */} { await onResumeAuction?.(); refreshAuction(); broadcast({ type: 'CALCUTTA_REFRESH', reason: 'resumed' }); }} /> setCelebrationData(null)} /> i.status === 'sold').reduce((sum, i) => sum + Number(i.winning_bid || 0), 0)} marketType={competition.market_type} myItemsWon={items.filter(i => i.status === 'sold' && i.winning_player_id === player_id)} myTotalSpent={items.filter(i => i.status === 'sold' && i.winning_player_id === player_id).reduce((sum, i) => sum + Number(i.winning_bid || 0), 0)} leaderboard={(() => { const soldItems = items.filter(i => i.status === 'sold' && i.winning_player_id); const playerMap: Record = {}; soldItems.forEach(i => { if (!playerMap[i.winning_player_id!]) playerMap[i.winning_player_id!] = { items_won: 0, total_spent: 0 }; playerMap[i.winning_player_id!].items_won++; playerMap[i.winning_player_id!].total_spent += Number(i.winning_bid || 0); }); return Object.entries(playerMap) .map(([pid, data]) => ({ player_id: pid, username: bidderProfiles[pid]?.username || bidderProfiles[pid]?.show_name, profile_pic: bidderProfiles[pid]?.profile_pic, is_me: pid === player_id, ...data, })) .sort((a, b) => b.total_spent - a.total_spent); })()} onDismiss={() => { setShowAuctionComplete(false); refreshAuction(); }} /> {isDesktop ? ( /* ═══════════════════════════════════════ DESKTOP LAYOUT Top bar: escrow summary + admin controls Full width: Active item Below: [Items (paginated)] [Players] [Info] ═══════════════════════════════════════ */ }> {/* Top bar: escrow + admin */} setInfoTab(infoTab === 'escrow' ? null : 'escrow')} activeOpacity={0.7} > {escrow ? formatCurrency(escrow.escrow_balance, competition.market_type) : '$0.00'} {escrow && Number(escrow.committed_balance) > 0 && ( ({formatCurrency(escrow.committed_balance, competition.market_type)} held) )} {/* Escrow popover */} {infoTab === 'escrow' && ( {renderEscrowContent()} )} {renderAdminControls()} {renderPotBar()} {/* Row 1: Active item + Up Next (same height) */} {/* Active item */} {renderActiveItem() || ( No active item )} {/* Up Next — matches active item height */} Up Next ({pendingItems.length}) {pendingItems.length > 0 ? pendingItems.map(item => renderItemRow(item)) : ( No items in queue )} {/* Row 2: Completed + Players + Info */} {/* Completed */} Completed ({completedItems.length}) {completedItems.length > 0 ? completedItems.map(item => renderItemRow(item)) : ( No items sold yet )} {/* Players */} Players {(presence?.count || 0) > 0 && ( {presence.count} LIVE )} {renderPlayersContent()} {/* Info + Payouts */} {renderInfoContent()} ) : ( /* ═══════════════════════════════════════ MOBILE LAYOUT — tabs ═══════════════════════════════════════ */ { if (infoTab !== 'items') return []; const rows: { type: string; item?: any; label?: string; key: string }[] = []; if (pendingItems.length > 0) { rows.push({ type: 'header', label: `Up Next (${pendingItems.length})`, key: 'h-pending' }); pendingItems.forEach(i => rows.push({ type: 'item', item: i, key: i.calcutta_auction_item_id })); } if (completedItems.length > 0) { rows.push({ type: 'header', label: `Completed (${completedItems.length})`, key: 'h-completed' }); completedItems.forEach(i => rows.push({ type: 'item', item: i, key: i.calcutta_auction_item_id })); } if (rows.length === 0) { rows.push({ type: 'empty', key: 'empty' }); } return rows; })()} keyExtractor={row => row.key} keyboardShouldPersistTaps="handled" contentContainerStyle={{ paddingBottom: 40 }} style={{ backgroundColor: theme.colors.surface.base }} refreshControl={} extraData={`${infoTab}-${items.map(i => `${i.calcutta_auction_item_id}:${i.status}:${i.current_bid}`).join(',')}-${my_bids.length}-${paused}-${escrow?.escrow_balance}`} ListHeaderComponent={ {renderAdminControls()} {renderPotBar()} {renderActiveItem()} {/* Tabs */} {([ { key: 'items' as const, icon: 'list-outline', label: `Items (${items.length})` }, { key: 'players' as const, icon: 'people-outline', label: (presence?.count || 0) > 0 ? `${presence.count} Live` : 'Players' }, { key: 'escrow' as const, icon: 'wallet-outline', label: escrow ? formatCurrency(escrow.escrow_balance, competition.market_type) : '$0.00' }, { key: 'info' as const, icon: 'information-circle-outline', label: 'Info' }, ]).map(tab => { const active = infoTab === tab.key; return ( setInfoTab(infoTab === tab.key ? null : tab.key)} activeOpacity={0.7} > {tab.label} ); })} {infoTab === 'players' && ( {renderPlayersContent()} )} {infoTab === 'escrow' && ( {renderEscrowContent()} )} {infoTab === 'info' && ( {renderInfoContent()} )} } renderItem={({ item: row }) => { if (row.type === 'header') { return ( {row.label} ); } if (row.type === 'empty') { return No auction items available; } return renderItemRow(row.item); }} /> )} ); }; // Sweepstakes: no auction, show the player's assigned item and prize structure const SweepstakesWaiting: React.FC = ({ calcutta_competition_id, player_id, onClose, onManage, onRefresh, competition, }) => { const { theme } = useTheme(); const { items, rounds, participants, item_results, refresh } = useCalcuttaCompetition(calcutta_competition_id); const { images: itemImages } = useCalcuttaItemImages(items); const isAdmin = player_id != null && competition?.admin_id == player_id; const [refreshing, setRefreshing] = useState(false); const handleRefresh = async () => { setRefreshing(true); try { await Promise.all([refresh(), onRefresh?.()]); } catch {} finally { setRefreshing(false); } }; // Build player lookup for owner usernames const ownerIds = items.filter(i => i.winning_player_id).map(i => i.winning_player_id!); const { players: enrichedPlayers } = useCalcuttaPlayers(ownerIds); const [searchQuery, setSearchQuery] = useState(''); const [expandedRound, setExpandedRound] = useState(null); const myItem = items.find(i => i.winning_player_id == player_id); const myItems = items.filter(i => i.winning_player_id == player_id).sort((a, b) => Number(b.winning_bid || 0) - Number(a.winning_bid || 0)); const isSweepstakes = competition.auction_type === 'sweepstakes'; const sortedRounds = [...(rounds ?? [])].sort((a, b) => a.round_number - b.round_number); const filteredItems = useMemo(() => { if (!searchQuery.trim()) return items; const q = searchQuery.toLowerCase().trim(); return items.filter(item => { if (item.item_name.toLowerCase().includes(q)) return true; if (item.winning_player_id) { const owner = enrichedPlayers[item.winning_player_id]; const ownerName = (owner?.username || owner?.show_name || '').toLowerCase(); if (ownerName.includes(q)) return true; } return false; }); }, [items, searchQuery, enrichedPlayers]); const statusLabel = competition.status === 'inprogress' ? 'In Progress' : competition.status === 'closed' ? 'Completed' : 'Scheduled'; const statusColor = competition.status === 'inprogress' ? '#8B5CF6' : competition.status === 'closed' ? '#6B7280' : '#10B981'; return ( }> {/* Hero with embedded title */} {competition.image?.url ? ( ) : ( )} {/* Dark overlay for text readability */} {/* Top bar: status badge + manage */} {statusLabel} {isAdmin && onManage && ( Manage )} {/* Title overlay */} {competition.competition_name} {competition.auction_type === 'sweepstakes' ? 'Sweepstakes' : competition.auction_type === 'live' ? 'Live Auction' : 'Sealed Bid'} {' · '}{statusLabel} {/* Assigned item */} {myItems.length > 0 && ( {isSweepstakes ? 'Your Item' : `Your Items (${myItems.length})`} {myItems.map(mi => { const imgUrl = resolveItemImageUrl(mi.item_image) || itemImages[mi.item_id]?.url; return ( {imgUrl ? ( ) : ( )} {mi.item_name} {mi.seed != null && #{mi.seed}} {Number(mi.winning_bid) > 0 && ( {formatCurrency(mi.winning_bid, competition.market_type)} )} ); })} )} {/* Stats */} Players {participants.length} Items {items.length} Rounds {rounds.length} {/* Rounds with results */} {sortedRounds.length > 0 && ( Rounds {sortedRounds.map((round: any) => { const isExpanded = expandedRound === round.calcutta_round_id; const roundResults = item_results.filter(r => r.round_number === round.round_number); const advancedIds = roundResults.filter(r => r.result === 'advanced' || r.result === 'won').map(r => r.calcutta_auction_item_id); const eliminatedIds = roundResults.filter(r => r.result === 'eliminated').map(r => r.calcutta_auction_item_id); const advancedItems = items.filter(i => advancedIds.includes(i.calcutta_auction_item_id)); const eliminatedItems = items.filter(i => eliminatedIds.includes(i.calcutta_auction_item_id)); const hasResults = roundResults.length > 0; const statusColor = round.status === 'closed' ? theme.colors.status.success : round.status === 'inprogress' ? theme.colors.primary.default : theme.colors.text.tertiary; return ( setExpandedRound(isExpanded ? null : round.calcutta_round_id)} activeOpacity={0.7} style={{ flexDirection: 'row', alignItems: 'center', paddingVertical: 12 }} > {round.round_name} {round.status === 'closed' ? 'Resolved' : round.status === 'inprogress' ? 'In Progress' : 'Pending'} {hasResults && ( {advancedItems.length} advanced )} {round.prize_description && ( {round.prize_description} )} {isExpanded && ( {!hasResults && ( No results yet )} {advancedItems.length > 0 && ( <> Advanced ({advancedItems.length}) {advancedItems.map(item => { const imgUrl = resolveItemImageUrl(item.item_image) || itemImages[item.item_id]?.url; const isMe = item.winning_player_id == player_id; return ( {imgUrl ? ( ) : ( )} {item.item_name} {isMe && YOU} ); })} )} {eliminatedItems.length > 0 && ( <> Eliminated ({eliminatedItems.length}) {eliminatedItems.map(item => { const imgUrl = resolveItemImageUrl(item.item_image) || itemImages[item.item_id]?.url; const isMe = item.winning_player_id == player_id; return ( {imgUrl ? ( ) : ( )} {item.item_name} {isMe && YOU} ); })} )} )} ); })} )} {/* All teams list */} {items.length > 0 && ( All Items { if (Platform.OS === 'web' && e?.target?.scrollIntoView) { setTimeout(() => e.target.scrollIntoView({ behavior: 'smooth', block: 'center' }), 300); } }} /> {filteredItems.map((item) => { const isMyItem = item.winning_player_id == player_id; const isEliminated = item.status === 'eliminated'; const imageUrl = resolveItemImageUrl(item.item_image) || itemImages[item.item_id]?.url; const owner = item.winning_player_id ? enrichedPlayers[item.winning_player_id] : undefined; const ownerName = owner?.username || owner?.show_name; return ( {imageUrl ? ( ) : ( )} {item.item_name} {ownerName && ( {isMyItem ? 'You' : ownerName} )} {!item.winning_player_id && item.status !== 'unsold' && ( Unclaimed )} {item.status === 'unsold' && ( No owner )} {item.seed != null && #{item.seed}} {isMyItem && YOU} ); })} )} {!myItem && !isAdmin && ( Waiting for the tournament to begin )} ); }; // Main exported component — routes to sealed bid or live layout export const CalcuttaAuction: React.FC = (props) => { const { theme } = useTheme(); // Peek at auction type to decide which layout to render. // Use useCalcuttaCompetition for a single fetch (no polling). const { loading, competition } = useCalcuttaCompetition(props.calcutta_competition_id); if (loading && !competition) { return ( ); } if (!competition) { return ( Auction not found ); } if (competition.auction_type === 'sealed_bid') { return ; } return ; }; const styles = StyleSheet.create({ container: { flex: 1, }, loadingContainer: { flex: 1, alignItems: 'center', justifyContent: 'center', padding: 40, }, adminButton: { height: 32, borderRadius: 16, alignItems: 'center', justifyContent: 'center', paddingHorizontal: 12, }, infoTabBtn: { flex: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', height: 38, borderRadius: 10, borderWidth: 1, }, infoTabContent: { marginHorizontal: 12, marginTop: 8, padding: 14, borderRadius: 12, borderWidth: 1, }, presenceAvatar: { width: 24, height: 24, borderRadius: 12, alignItems: 'center', justifyContent: 'center', }, desktopPanel: { borderRadius: 12, borderWidth: 1, padding: 14, }, panelTitle: { textTransform: 'uppercase', letterSpacing: 1, fontSize: 10, lineHeight: 13, }, infoRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 6, }, emptyText: { textAlign: 'center', padding: 20, }, });