import { useEffect, useMemo, useRef, useState } from 'react';
import { Animated, Pressable, StyleSheet, Text, useWindowDimensions, View } from 'react-native';
import { LinearGradient } from 'expo-linear-gradient';
import { BlurView } from 'expo-blur';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { WS_URL } from '../api';
import { colors, fonts } from '../theme';
import CasinoButton from '../components/CasinoButton';
import Chip from '../components/Chip';
import DealerArea from '../components/DealerArea';
import Seat from '../components/Seat';
import TableBackground from '../components/TableBackground';
import TableMarkings from '../components/TableMarkings';
export default function TableScreen({ token, tableId, user, onLeave }) {
const [table, setTable] = useState(null);
const [balance, setBalance] = useState(user.balance);
const [message, setMessage] = useState('');
const [betAmount, setBetAmount] = useState(10);
const [turnSeconds, setTurnSeconds] = useState(null);
const wsRef = useRef(null);
const pulse = useRef(new Animated.Value(0)).current;
const insets = useSafeAreaInsets();
const { width, height } = useWindowDimensions();
const isPortrait = height >= width;
useEffect(() => {
const ws = new WebSocket(WS_URL);
wsRef.current = ws;
ws.onopen = () => {
ws.send(JSON.stringify({ type: 'hello', token }));
ws.send(JSON.stringify({ type: 'join', tableId }));
};
ws.onmessage = (event) => {
const payload = JSON.parse(event.data);
if (payload.type === 'table_state') {
setTable(payload.table);
if (payload.table.minBet) {
setBetAmount((prev) => {
const clamped = Math.max(payload.table.minBet, Math.min(prev, payload.table.maxBet));
return clamped;
});
}
}
if (payload.type === 'balance') {
setBalance(payload.balance);
}
if (payload.type === 'error') {
setMessage(payload.message);
setTimeout(() => setMessage(''), 2400);
}
};
ws.onclose = () => {
wsRef.current = null;
};
return () => {
if (ws.readyState === 1) {
ws.send(JSON.stringify({ type: 'leave' }));
}
ws.close();
};
}, [tableId, token]);
const mySeat = useMemo(() => table?.seats?.find((seat) => seat.isYou), [table]);
const isMyTurn = table?.phase === 'playing' && table?.currentSeatIndex === mySeat?.index;
const bettingLocked = ['playing', 'dealer', 'payout'].includes(table?.phase);
useEffect(() => {
if (!table?.turnEndsAt || !isMyTurn) {
setTurnSeconds(null);
return;
}
const update = () => {
const msLeft = Math.max(0, table.turnEndsAt - Date.now());
setTurnSeconds(Math.ceil(msLeft / 1000));
};
update();
const interval = setInterval(update, 250);
return () => clearInterval(interval);
}, [table?.turnEndsAt, isMyTurn]);
useEffect(() => {
if (!isMyTurn) {
pulse.stopAnimation();
pulse.setValue(0);
return;
}
Animated.loop(
Animated.sequence([
Animated.timing(pulse, { toValue: 1, duration: 700, useNativeDriver: true }),
Animated.timing(pulse, { toValue: 0, duration: 700, useNativeDriver: true })
])
).start();
}, [isMyTurn, pulse]);
const pulseStyle = {
transform: [
{
scale: pulse.interpolate({
inputRange: [0, 1],
outputRange: [1, 1.04]
})
}
],
opacity: pulse.interpolate({ inputRange: [0, 1], outputRange: [0.85, 1] })
};
const send = (payload) => {
if (wsRef.current?.readyState === 1) {
wsRef.current.send(JSON.stringify(payload));
}
};
const adjustBet = (delta) => {
if (!table) {
return;
}
setBetAmount((prev) => {
const next = prev + delta;
return Math.max(table.minBet, Math.min(table.maxBet, next));
});
};
const seats = table?.seats || [];
if (isPortrait) {
return (
Fordítsd el a telefont
A blackjack asztal fekvő nézetben működik jól.
);
}
return (
Asztal {tableId}
Egyenleg: {balance} Ft
{seats.map((seat) => (
))}
Tét
adjustBet(-10)} style={[styles.betAdjust, bettingLocked && styles.betAdjustDisabled]} disabled={bettingLocked}>
-
adjustBet(10)} style={[styles.betAdjust, bettingLocked && styles.betAdjustDisabled]} disabled={bettingLocked}>
+
send({ type: 'bet', amount: betAmount })} variant="gold" disabled={bettingLocked} />
send({ type: 'ready' })} variant="green" disabled={bettingLocked} />
{isMyTurn && turnSeconds !== null ? (
Idő: {turnSeconds} mp
) : null}
send({ type: 'action', action: 'hit' })} variant="gold" disabled={!isMyTurn} />
send({ type: 'action', action: 'stand' })} variant="gold" disabled={!isMyTurn} />
send({ type: 'action', action: 'double' })} variant="gold" disabled={!isMyTurn} />
{message ? {message} : null}
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingHorizontal: 12
},
topBar: {
paddingHorizontal: 6,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center'
},
tableTitle: {
color: colors.goldBright,
fontSize: 20,
fontFamily: fonts.display,
letterSpacing: 2
},
balance: {
color: colors.muted,
marginTop: 4,
fontFamily: fonts.mono
},
tableWrap: {
flex: 1,
justifyContent: 'flex-start',
alignItems: 'center',
marginTop: 8,
marginBottom: 8,
width: '100%'
},
tableSurface: {
flex: 1,
width: '100%',
padding: 0
},
tableInner: {
flex: 1,
position: 'relative'
},
dealerArea: {
alignItems: 'center',
paddingTop: 8
},
tableContent: {
flex: 1,
justifyContent: 'space-between',
paddingBottom: 8
},
seatLayer: {
flex: 1,
position: 'relative'
},
seatSpot: {
position: 'absolute',
width: 96,
transform: [{ translateX: -48 }]
},
controlsOverlay: {
position: 'absolute',
left: 12,
right: 12,
bottom: 14,
gap: 10
},
blurPanel: {
borderRadius: 18,
paddingVertical: 10,
paddingHorizontal: 12,
backgroundColor: 'rgba(10, 18, 14, 0.45)',
borderWidth: 1,
borderColor: 'rgba(255,255,255,0.12)',
overflow: 'hidden'
},
betRow: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
gap: 8
},
betControls: {
flexDirection: 'row',
alignItems: 'center',
gap: 6
},
betAdjust: {
width: 32,
height: 32,
borderRadius: 16,
backgroundColor: 'rgba(255,255,255,0.12)',
alignItems: 'center',
justifyContent: 'center'
},
betAdjustText: {
color: colors.text,
fontSize: 18,
fontWeight: '700'
},
betAdjustDisabled: {
opacity: 0.4
},
actionRow: {
flexDirection: 'row',
justifyContent: 'space-between',
gap: 10
},
sectionLabel: {
color: colors.text,
fontSize: 14,
fontFamily: fonts.body
},
message: {
color: colors.goldBright,
textAlign: 'center',
marginTop: 8,
fontFamily: fonts.body
},
timer: {
color: colors.goldBright,
textAlign: 'center',
marginBottom: 6,
fontFamily: fonts.mono
},
rotateWrap: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 24
},
rotateTitle: {
color: colors.goldBright,
fontFamily: fonts.display,
fontSize: 24,
letterSpacing: 2,
textAlign: 'center'
},
rotateSubtitle: {
color: colors.muted,
fontFamily: fonts.body,
fontSize: 14,
marginTop: 12,
textAlign: 'center'
}
});
const seatPositions = {
0: { left: '8%', top: '62%' },
1: { left: '22%', top: '55%' },
2: { left: '36%', top: '50%' },
3: { left: '50%', top: '48%' },
4: { left: '64%', top: '50%' },
5: { left: '78%', top: '55%' },
6: { left: '92%', top: '62%' }
};