import React, { useState, useEffect, useRef, useCallback } from 'react'; import { View, Text, TouchableOpacity, ActivityIndicator, Modal, Animated, StyleSheet, KeyboardAvoidingView, Platform, } from 'react-native'; import { useDubsTheme } from '../theme'; import { useDubs } from '../../provider'; import { useEnterArcadePool } from '../../hooks/useEnterArcadePool'; import type { EnterArcadePoolMutationResult } from '../../hooks/useEnterArcadePool'; import type { ArcadePool, ArcadePoolStats } from '../../types'; export interface EnterArcadePoolSheetProps { visible: boolean; onDismiss: () => void; pool: ArcadePool; stats?: ArcadePoolStats | null; mode?: 'join' | 'rejoin'; onSuccess?: (result: EnterArcadePoolMutationResult) => void; onError?: (error: Error) => void; } const STATUS_LABELS: Record = { building: 'Building transaction...', signing: 'Approve in wallet...', confirming: 'Confirming...', success: 'Joined!', }; export function EnterArcadePoolSheet({ visible, onDismiss, pool, stats, mode = 'join', onSuccess, onError, }: EnterArcadePoolSheetProps) { const t = useDubsTheme(); const { wallet } = useDubs(); const mutation = useEnterArcadePool(); const overlayOpacity = useRef(new Animated.Value(0)).current; useEffect(() => { Animated.timing(overlayOpacity, { toValue: visible ? 1 : 0, duration: 250, useNativeDriver: true, }).start(); }, [visible, overlayOpacity]); // Reset on open useEffect(() => { if (visible) { mutation.reset(); } }, [visible]); // eslint-disable-line react-hooks/exhaustive-deps // Auto-dismiss on success useEffect(() => { if (mutation.status === 'success' && mutation.data) { onSuccess?.(mutation.data); const timer = setTimeout(() => { onDismiss(); }, 1500); return () => clearTimeout(timer); } }, [mutation.status, mutation.data]); // eslint-disable-line react-hooks/exhaustive-deps // Report errors useEffect(() => { if (mutation.status === 'error' && mutation.error) { onError?.(mutation.error); } }, [mutation.status, mutation.error]); // eslint-disable-line react-hooks/exhaustive-deps const buyInSol = (pool.buy_in_lamports / 1_000_000_000).toFixed(4); const totalPlayers = stats?.total_entries ?? 0; const totalBuyIns = pool.total_entries ?? totalPlayers; const topScore = stats?.top_score ?? 0; const potSol = ((pool.buy_in_lamports * Number(totalBuyIns)) / 1_000_000_000).toFixed(4); const isMutating = mutation.status !== 'idle' && mutation.status !== 'success' && mutation.status !== 'error'; const canJoin = !isMutating && mutation.status !== 'success'; const handleJoin = useCallback(async () => { if (!wallet.publicKey) return; try { await mutation.execute(pool.id); } catch { // Error captured in mutation state } }, [wallet.publicKey, mutation.execute, pool.id]); // eslint-disable-line react-hooks/exhaustive-deps const statusLabel = STATUS_LABELS[mutation.status] || ''; const isRejoin = mode === 'rejoin'; const headerTitle = isRejoin ? 'Play Again' : 'Join Pool'; const ctaLabel = isRejoin ? `Play Again \u2014 ${buyInSol} SOL` : `Join Pool \u2014 ${buyInSol} SOL`; const successLabel = isRejoin ? 'Lives refilled!' : 'Joined!'; return ( {/* Drag handle */} {/* Header */} {headerTitle} {'\u2715'} {/* Pool name */} {pool.name} {/* Summary Card */} Buy-in {buyInSol} SOL Players in {totalPlayers} Current pot {potSol} SOL Lives {pool.max_lives} {topScore > 0 && ( <> Top score {topScore} )} {/* Error Display */} {mutation.error && ( {mutation.error.message} )} {/* CTA Button */} {isMutating ? ( {statusLabel} ) : mutation.status === 'success' ? ( {successLabel} ) : ( {ctaLabel} )} ); } const styles = StyleSheet.create({ overlay: { ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(0,0,0,0.5)', }, overlayTap: { flex: 1 }, keyboardView: { flex: 1, justifyContent: 'flex-end' }, sheetPositioner: { justifyContent: 'flex-end' }, sheet: { borderTopLeftRadius: 24, borderTopRightRadius: 24, paddingHorizontal: 20, paddingBottom: 40, }, handleRow: { alignItems: 'center', paddingTop: 10, paddingBottom: 8 }, handle: { width: 36, height: 4, borderRadius: 2, opacity: 0.4 }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingVertical: 12, }, headerTitle: { fontSize: 20, fontWeight: '700' }, closeButton: { fontSize: 20, padding: 4 }, poolName: { fontSize: 15, fontWeight: '600', marginBottom: 8 }, summaryCard: { marginTop: 12, borderRadius: 16, borderWidth: 1, overflow: 'hidden', }, summaryRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 16, paddingVertical: 14, }, summaryLabel: { fontSize: 14 }, summaryValue: { fontSize: 15, fontWeight: '700' }, summarySep: { height: 1, marginHorizontal: 16 }, errorBox: { marginTop: 16, borderRadius: 12, borderWidth: 1, padding: 12, }, errorText: { fontSize: 13, fontWeight: '500' }, ctaButton: { marginTop: 20, height: 56, borderRadius: 14, justifyContent: 'center', alignItems: 'center', }, ctaText: { color: '#FFFFFF', fontSize: 16, fontWeight: '700' }, ctaLoading: { flexDirection: 'row', alignItems: 'center', gap: 10 }, });