From 0c14bab2995eb44bf04c4f46926c1ae245bcbd24 Mon Sep 17 00:00:00 2001 From: b3ni15 Date: Sun, 21 Dec 2025 02:54:10 +0100 Subject: [PATCH] feat: enhance Card, CasinoButton, Chip, DealerArea, and TableScreen components; add animation, size adjustments, and improved UI elements for better user experience --- src/components/Card.js | 107 +++-- src/components/CasinoButton.js | 6 +- src/components/Chip.js | 24 +- src/components/DealerArea.js | 13 +- src/screens/TableScreen.js | 779 +++++++++++++++++++++++++++------ 5 files changed, 748 insertions(+), 181 deletions(-) diff --git a/src/components/Card.js b/src/components/Card.js index 1a610ac..ed57592 100644 --- a/src/components/Card.js +++ b/src/components/Card.js @@ -1,4 +1,5 @@ -import { StyleSheet, Text, View } from 'react-native'; +import { useEffect, useRef } from 'react'; +import { Animated, StyleSheet, Text, View } from 'react-native'; import { colors, fonts } from '../theme'; const suitSymbols = { @@ -15,12 +16,70 @@ const suitColors = { D: colors.red }; -export default function Card({ rank, suit, hidden }) { +export default function Card({ rank, suit, hidden, size = 'normal', animate = true, delay = 0 }) { + const scaleMap = { tiny: 0.78, small: 0.92, normal: 1.06, large: 1.18 }; + const scale = scaleMap[size] ?? 1; + const appear = useRef(new Animated.Value(animate ? 0 : 1)).current; + + useEffect(() => { + if (!animate) { + return; + } + Animated.timing(appear, { + toValue: 1, + duration: 260, + delay, + useNativeDriver: true + }).start(); + }, [animate, appear, delay]); + + const animatedStyle = animate + ? { + opacity: appear, + transform: [ + { + translateY: appear.interpolate({ + inputRange: [0, 1], + outputRange: [-12 * scale, 0] + }) + }, + { + scale: appear.interpolate({ + inputRange: [0, 1], + outputRange: [0.96, 1] + }) + } + ] + } + : null; + + const cardStyle = [ + styles.card, + { + width: 54 * scale, + height: 78 * scale, + borderRadius: 8 * scale, + padding: 6 * scale, + marginRight: 6 * scale + } + ]; + const cornerStyle = [styles.corner, { fontSize: 12 * scale, lineHeight: 13 * scale }]; + const centerStyle = [styles.center, { fontSize: 24 * scale, lineHeight: 26 * scale }]; + const backPatternStyle = [ + styles.backPattern, + { + width: 32 * scale, + height: 46 * scale, + borderRadius: 6 * scale, + borderWidth: Math.max(1, Math.round(2 * scale)) + } + ]; + if (hidden || rank === 'X') { return ( - - - + + + ); } @@ -28,29 +87,29 @@ export default function Card({ rank, suit, hidden }) { const color = suitColors[suit] || colors.text; return ( - - {rank} - {symbol} - {symbol} + + {rank} + {symbol} + {symbol} - {rank} - {symbol} + {rank} + {symbol} - + ); } const styles = StyleSheet.create({ card: { - width: 54, - height: 78, - borderRadius: 8, backgroundColor: '#fdf8f0', - padding: 6, - marginRight: 6, borderWidth: 1, borderColor: '#d2c1a4', - justifyContent: 'space-between' + justifyContent: 'space-between', + overflow: 'hidden', + shadowColor: '#000', + shadowOpacity: 0.18, + shadowRadius: 6, + shadowOffset: { width: 0, height: 3 } }, cardBack: { backgroundColor: '#152d52', @@ -59,21 +118,17 @@ const styles = StyleSheet.create({ justifyContent: 'center' }, backPattern: { - width: 32, - height: 46, - borderRadius: 6, - borderWidth: 2, borderColor: 'rgba(255,255,255,0.4)' }, corner: { - fontSize: 12, fontFamily: fonts.body, - fontWeight: '700' + fontWeight: '700', + includeFontPadding: false }, center: { - fontSize: 24, fontFamily: fonts.display, - textAlign: 'center' + textAlign: 'center', + includeFontPadding: false }, cornerBottom: { transform: [{ rotate: '180deg' }] diff --git a/src/components/CasinoButton.js b/src/components/CasinoButton.js index 14dc8de..2c35220 100644 --- a/src/components/CasinoButton.js +++ b/src/components/CasinoButton.js @@ -42,8 +42,8 @@ const styles = StyleSheet.create({ borderColor: 'rgba(255,255,255,0.2)' }, buttonSmall: { - paddingVertical: 8, - paddingHorizontal: 16 + paddingVertical: 9, + paddingHorizontal: 18 }, inner: { alignItems: 'center' @@ -56,7 +56,7 @@ const styles = StyleSheet.create({ }, textSmall: { fontSize: 13, - letterSpacing: 0.8 + letterSpacing: 0.7 }, disabled: { opacity: 0.5 diff --git a/src/components/Chip.js b/src/components/Chip.js index 71d8ab9..b221da8 100644 --- a/src/components/Chip.js +++ b/src/components/Chip.js @@ -11,7 +11,7 @@ export default function Chip({ label, color = 'blue', size = 'default' }) { const isSmall = size === 'small'; return ( - + {label} @@ -20,9 +20,9 @@ export default function Chip({ label, color = 'blue', size = 'default' }) { const styles = StyleSheet.create({ chip: { - width: 48, - height: 48, - borderRadius: 24, + width: 56, + height: 56, + borderRadius: 28, alignItems: 'center', justifyContent: 'center', borderWidth: 3, @@ -34,21 +34,27 @@ const styles = StyleSheet.create({ borderRadius: 20 }, inner: { - width: 28, - height: 28, - borderRadius: 14, + width: 32, + height: 32, + borderRadius: 16, borderWidth: 2, borderColor: 'rgba(255,255,255,0.7)', alignItems: 'center', justifyContent: 'center' }, + innerSmall: { + width: 22, + height: 22, + borderRadius: 11, + borderWidth: 1.5 + }, text: { color: '#f7f2e6', fontWeight: '700', - fontSize: 12, + fontSize: 13, fontFamily: fonts.mono }, textSmall: { - fontSize: 11 + fontSize: 10 } }); diff --git a/src/components/DealerArea.js b/src/components/DealerArea.js index 0f8c814..cfd6961 100644 --- a/src/components/DealerArea.js +++ b/src/components/DealerArea.js @@ -2,13 +2,20 @@ import { StyleSheet, Text, View } from 'react-native'; import Card from './Card'; import { colors, fonts } from '../theme'; -export default function DealerArea({ hand }) { +export default function DealerArea({ hand, cardSize = 'small', delayBase = 0 }) { return ( Osztó {hand.map((card, idx) => ( - @@ -23,7 +30,7 @@ const styles = StyleSheet.create({ color: colors.goldBright, letterSpacing: 2, textTransform: 'uppercase', - fontSize: 12, + fontSize: 13, fontFamily: fonts.body }, hand: { diff --git a/src/screens/TableScreen.js b/src/screens/TableScreen.js index 77b15e9..f1aade0 100644 --- a/src/screens/TableScreen.js +++ b/src/screens/TableScreen.js @@ -5,12 +5,46 @@ import { BlurView } from 'expo-blur'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { WS_URL } from '../api'; import { colors, fonts } from '../theme'; +import Card from '../components/Card'; 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'; + +const emptySeat = (index) => ({ + index, + userId: null, + username: '', + bet: 0, + hand: [], + result: null, + isYou: false +}); + +const getHandTotal = (hand) => { + if (!hand || hand.length === 0) { + return null; + } + let total = 0; + let aces = 0; + for (const card of hand) { + if (!card || card.rank === 'X') { + return null; + } + if (card.rank === 'A') { + total += 11; + aces += 1; + } else if (['J', 'Q', 'K'].includes(card.rank)) { + total += 10; + } else { + total += Number(card.rank); + } + } + while (total > 21 && aces > 0) { + total -= 10; + aces -= 1; + } + return total; +}; export default function TableScreen({ token, tableId, user, onLeave }) { const [table, setTable] = useState(null); @@ -18,9 +52,14 @@ export default function TableScreen({ token, tableId, user, onLeave }) { const [message, setMessage] = useState(''); const [betAmount, setBetAmount] = useState(10); const [turnSeconds, setTurnSeconds] = useState(null); + const [roundSeconds, setRoundSeconds] = useState(null); const [controlsVisible, setControlsVisible] = useState(true); + const [seatLayout, setSeatLayout] = useState(null); + const [winOverlay, setWinOverlay] = useState(null); + const [confettiKey, setConfettiKey] = useState(0); const wsRef = useRef(null); const hideTimerRef = useRef(null); + const lastWinRoundRef = useRef(null); const pulse = useRef(new Animated.Value(0)).current; const insets = useSafeAreaInsets(); const { width, height } = useWindowDimensions(); @@ -70,6 +109,8 @@ export default function TableScreen({ token, tableId, user, onLeave }) { 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); + const showBetControls = !bettingLocked; + const showActionControls = table?.phase === 'playing'; const scheduleHide = useCallback(() => { if (hideTimerRef.current) { @@ -113,6 +154,22 @@ export default function TableScreen({ token, tableId, user, onLeave }) { return () => clearInterval(interval); }, [table?.turnEndsAt, isMyTurn]); + useEffect(() => { + if (!table?.roundStartsAt || table?.phase !== 'betting') { + setRoundSeconds(null); + return; + } + + const update = () => { + const msLeft = Math.max(0, table.roundStartsAt - Date.now()); + setRoundSeconds(Math.ceil(msLeft / 1000)); + }; + + update(); + const interval = setInterval(update, 250); + return () => clearInterval(interval); + }, [table?.roundStartsAt, table?.phase]); + useEffect(() => { if (!isMyTurn) { pulse.stopAnimation(); @@ -128,6 +185,24 @@ export default function TableScreen({ token, tableId, user, onLeave }) { ).start(); }, [isMyTurn, pulse]); + useEffect(() => { + if (table?.phase !== 'payout' || !mySeat?.result) { + return; + } + + if (lastWinRoundRef.current === table.roundId) { + return; + } + + if (['win', 'blackjack'].includes(mySeat.result.outcome)) { + lastWinRoundRef.current = table.roundId; + setWinOverlay({ payout: mySeat.result.payout, outcome: mySeat.result.outcome }); + setConfettiKey((prev) => prev + 1); + const timer = setTimeout(() => setWinOverlay(null), 2600); + return () => clearTimeout(timer); + } + }, [table?.phase, table?.roundId, mySeat?.result]); + const pulseStyle = { transform: [ { @@ -159,29 +234,65 @@ export default function TableScreen({ token, tableId, user, onLeave }) { }; const seats = table?.seats || []; + const activePlayers = seats.filter((seat) => seat.userId).length; + const showRoundTimer = roundSeconds !== null && table?.phase === 'betting' && activePlayers > 1; - if (isPortrait) { - return ( - - - Fordítsd el a telefont - A blackjack asztal fekvő nézetben működik jól. - - - - - - ); - } + const uiScale = useMemo(() => { + const base = isPortrait ? Math.min(width / 390, height / 844) : Math.min(width / 844, height / 390); + return Math.max(0.88, Math.min(base, 1.15)); + }, [width, height, isPortrait]); + + const seatMetrics = useMemo(() => { + if (!seatLayout) { + return { + width: Math.round(150 * uiScale), + height: Math.round(110 * uiScale) + }; + } + const baseWidth = seatLayout.width * (isPortrait ? 0.24 : 0.18); + const clamped = Math.max(120, Math.min(baseWidth, 190)); + const widthValue = Math.round(clamped * uiScale); + return { + width: widthValue, + height: Math.round(widthValue * 0.78) + }; + }, [seatLayout, isPortrait, uiScale]); + + const seatAnchors = useMemo(() => { + if (isPortrait) { + return [ + { x: 0.12, y: 0.32 }, + { x: 0.88, y: 0.32 }, + { x: 0.12, y: 0.58 }, + { x: 0.88, y: 0.58 } + ]; + } + return [ + { x: 0.2, y: 0.26 }, + { x: 0.8, y: 0.26 }, + { x: 0.2, y: 0.64 }, + { x: 0.8, y: 0.64 } + ]; + }, [isPortrait]); + + const seatSlots = useMemo(() => { + return seatAnchors.map((_, idx) => seats[idx] ?? emptySeat(idx)); + }, [seatAnchors, seats]); + + const seatPositions = useMemo(() => { + if (!seatLayout) { + return []; + } + return seatAnchors.map((anchor) => ({ + left: seatLayout.width * anchor.x - seatMetrics.width / 2, + top: seatLayout.height * anchor.y - seatMetrics.height / 2 + })); + }, [seatLayout, seatAnchors, seatMetrics]); + + const seatCardSize = uiScale > 1.02 ? 'normal' : 'small'; + const playerCardSize = uiScale > 1 ? 'large' : 'normal'; + const myTotal = getHandTotal(mySeat?.hand); + const dealerDelayBase = seatSlots.length * 140; return ( Asztal {tableId} - Egyenleg: {balance} Ft + {balance} Ft - + - - - - + + + + + + + + - + - - {seats.map((seat) => ( - - - - ))} - - - {controlsVisible ? ( - - - - Tét - - adjustBet(-10)} style={[styles.betAdjust, bettingLocked && styles.betAdjustDisabled]} disabled={bettingLocked}> - - - - - adjustBet(10)} style={[styles.betAdjust, bettingLocked && styles.betAdjustDisabled]} disabled={bettingLocked}> - + - + setSeatLayout(event.nativeEvent.layout)}> + {seatSlots.map((seat, idx) => { + const pos = seatPositions[idx]; + if (!pos) { + return null; + } + const highlight = seat.userId && table?.currentSeatIndex === seat.index; + return ( + + - send({ type: 'bet', amount: betAmount })} variant="gold" size="small" disabled={bettingLocked} /> - send({ type: 'ready' })} variant="green" size="small" disabled={bettingLocked} /> - - - - - {isMyTurn && turnSeconds !== null ? ( - Idő: {turnSeconds} mp - ) : null} - - send({ type: 'action', action: 'hit' })} variant="gold" size="small" disabled={!isMyTurn} /> - send({ type: 'action', action: 'stand' })} variant="gold" size="small" disabled={!isMyTurn} /> - send({ type: 'action', action: 'double' })} variant="gold" size="small" disabled={!isMyTurn} /> - - + ); + })} - ) : ( - - )} - {message ? {message} : null} - - + + + {(mySeat?.hand || []).map((card, idx) => ( + + + ))} + + {myTotal !== null ? ( + Összeg: {myTotal} + ) : null} + {mySeat ? ( + Tét: {mySeat.bet || betAmount} Ft + ) : null} + + + {controlsVisible ? ( + + {showRoundTimer ? ( + Kezdés: {roundSeconds} mp + ) : null} + + {showBetControls ? ( + + + 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" size="small" disabled={bettingLocked} /> + send({ type: 'ready' })} variant="green" size="small" disabled={bettingLocked} /> + + + ) : null} + + {showActionControls ? ( + + + {isMyTurn ? 'HIT' : 'Várakozás'} + {isMyTurn && turnSeconds !== null ? ( + {turnSeconds} mp + ) : null} + + + send({ type: 'action', action: 'hit' })} variant="gold" size="small" disabled={!isMyTurn} /> + send({ type: 'action', action: 'stand' })} variant="gold" size="small" disabled={!isMyTurn} /> + send({ type: 'action', action: 'double' })} variant="gold" size="small" disabled={!isMyTurn} /> + + + ) : null} + + ) : ( + + )} + + {message ? {message} : null} + + + + + {winOverlay ? ( + + + + Nyertél! + +{winOverlay.payout} Ft + + + ) : null} ); } +function PortraitSeat({ seat, highlight, cardSize, scale, dealIndex }) { + const isEmpty = !seat.userId; + const hand = seat.hand || []; + const total = getHandTotal(hand); + const label = seat.userId ? (seat.isYou ? 'ÉN' : seat.username || 'Játékos') : 'Üres hely'; + + return ( + + {highlight ? : null} + + + + + + + + {label} + + {hand.length > 0 ? ( + + {hand.map((card, idx) => ( + + + ))} + + ) : null} + {total !== null ? Összeg: {total} : null} + + {seat.bet > 0 ? `${seat.bet} Ft` : 'TÉT'} + + {seat.result?.outcome === 'blackjack' ? ( + + BLACKJACK! + + ) : null} + + ); +} + +function ConfettiBurst({ trigger, width, height }) { + const pieces = useMemo(() => { + const palette = ['#f7d488', '#d94a3d', '#39c377', '#2f7dd3', '#f2f1e8']; + return Array.from({ length: 22 }, (_, idx) => { + return { + key: `${trigger}-${idx}`, + x: Math.random() * Math.max(0, width - 24), + delay: idx * 70, + rotate: Math.random() * 180, + drift: (Math.random() - 0.5) * 80, + size: 6 + Math.random() * 6, + color: palette[idx % palette.length], + anim: new Animated.Value(0) + }; + }); + }, [trigger, width]); + + useEffect(() => { + const animations = pieces.map((piece) => + Animated.timing(piece.anim, { + toValue: 1, + duration: 1600, + delay: piece.delay, + useNativeDriver: true + }) + ); + Animated.parallel(animations).start(); + }, [pieces]); + + return ( + + {pieces.map((piece) => ( + + ))} + + ); +} + const styles = StyleSheet.create({ container: { flex: 1, @@ -274,64 +590,215 @@ const styles = StyleSheet.create({ }, tableTitle: { color: colors.goldBright, - fontSize: 20, + fontSize: 22, fontFamily: fonts.display, - letterSpacing: 2 + letterSpacing: 1 }, balance: { color: colors.muted, - marginTop: 4, + marginTop: 2, fontFamily: fonts.mono }, tableWrap: { flex: 1, - justifyContent: 'flex-start', - alignItems: 'center', - marginTop: 8, + marginTop: 10, marginBottom: 8, width: '100%' }, tableSurface: { flex: 1, width: '100%', - padding: 0 + borderRadius: 26, + overflow: 'hidden' }, - tableInner: { - flex: 1, - position: 'relative' + tableGradient: { + flex: 1 }, - dealerArea: { - alignItems: 'center', - paddingTop: 8 + tableGlow: { + ...StyleSheet.absoluteFillObject, + backgroundColor: 'rgba(255,255,255,0.02)' + }, + tableArc: { + position: 'absolute', + left: '10%', + right: '10%', + borderRadius: 999, + borderWidth: 1, + borderColor: 'rgba(255,255,255,0.12)' + }, + tableArcTop: { + top: 26 + }, + tableArcMid: { + top: 130, + borderColor: 'rgba(255,255,255,0.08)' + }, + tableArcBottom: { + bottom: 200, + borderColor: 'rgba(255,255,255,0.1)' }, tableContent: { flex: 1, - justifyContent: 'space-between', - paddingBottom: 140 + paddingHorizontal: 16 + }, + dealerArea: { + alignItems: 'center', + marginTop: 6 }, seatLayer: { flex: 1, - position: 'relative' + position: 'relative', + marginTop: 6, + marginBottom: 6 }, seatSpot: { + position: 'absolute' + }, + seatCard: { + flex: 1, + alignItems: 'center', + justifyContent: 'flex-start' + }, + seatCardActive: { + shadowColor: colors.goldBright, + shadowOpacity: 0.6, + shadowRadius: 12 + }, + seatGlow: { position: 'absolute', - width: 90, - transform: [{ translateX: -45 }] + top: -8, + left: -8, + right: -8, + bottom: -8, + borderRadius: 999, + borderWidth: 1, + borderColor: 'rgba(255,214,138,0.6)' }, - controlsOverlay: { - position: 'absolute', - left: 20, - right: 20, - bottom: 10, - gap: 10 + avatarWrap: { + marginTop: 2, + marginBottom: 4 }, - controlsReveal: { - ...StyleSheet.absoluteFillObject + avatarRing: { + width: 38, + height: 38, + borderRadius: 19, + borderWidth: 1, + borderColor: 'rgba(255,255,255,0.35)', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: 'rgba(0,0,0,0.25)' }, - blurPanel: { - borderRadius: 18, - paddingVertical: 8, + avatarHead: { + width: 10, + height: 10, + borderRadius: 5, + borderWidth: 1, + borderColor: 'rgba(255,255,255,0.3)', + marginBottom: 2 + }, + avatarBody: { + width: 18, + height: 10, + borderRadius: 6, + borderWidth: 1, + borderColor: 'rgba(255,255,255,0.25)' + }, + seatName: { + color: colors.text, + fontFamily: fonts.body, + fontSize: 12, + marginBottom: 2 + }, + seatNameYou: { + fontWeight: '800', + color: colors.goldBright + }, + seatHand: { + flexDirection: 'row', + alignItems: 'center', + marginTop: 2 + }, + seatCardStack: { + alignItems: 'center', + justifyContent: 'center' + }, + seatTotal: { + marginTop: 3, + color: colors.muted, + fontSize: 11, + fontFamily: fonts.mono + }, + betPill: { + marginTop: 4, paddingHorizontal: 10, + paddingVertical: 4, + borderRadius: 10, + borderWidth: 1, + borderColor: colors.goldBright, + backgroundColor: 'rgba(0,0,0,0.25)' + }, + betPillEmpty: { + borderColor: 'rgba(255,255,255,0.18)' + }, + betPillText: { + color: colors.goldBright, + fontFamily: fonts.mono, + fontSize: 12 + }, + blackjackTag: { + marginTop: 4, + paddingHorizontal: 8, + paddingVertical: 2, + borderRadius: 8, + backgroundColor: 'rgba(0,0,0,0.45)', + borderWidth: 1, + borderColor: colors.gold + }, + blackjackText: { + color: colors.goldBright, + fontSize: 10, + fontFamily: fonts.body + }, + playerArea: { + alignItems: 'center', + marginTop: 4, + marginBottom: 4 + }, + playerHand: { + flexDirection: 'row', + alignItems: 'center' + }, + playerCard: { + alignItems: 'center', + justifyContent: 'center' + }, + playerTotal: { + marginTop: 6, + color: colors.muted, + fontFamily: fonts.mono, + fontSize: 12 + }, + playerBet: { + marginTop: 2, + color: colors.goldBright, + fontFamily: fonts.body, + fontSize: 14 + }, + controlsArea: { + marginTop: 4, + gap: 12, + paddingBottom: 6 + }, + roundTimer: { + textAlign: 'center', + color: colors.goldBright, + fontFamily: fonts.mono, + fontSize: 12 + }, + controlsPanel: { + borderRadius: 18, + paddingVertical: 10, + paddingHorizontal: 12, backgroundColor: 'rgba(10, 18, 14, 0.45)', borderWidth: 1, borderColor: 'rgba(255,255,255,0.12)', @@ -341,17 +808,18 @@ const styles = StyleSheet.create({ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', - gap: 8 + gap: 10, + flexWrap: 'wrap' }, betControls: { flexDirection: 'row', alignItems: 'center', - gap: 6 + gap: 8 }, betAdjust: { - width: 28, - height: 28, - borderRadius: 14, + width: 32, + height: 32, + borderRadius: 16, backgroundColor: 'rgba(255,255,255,0.12)', alignItems: 'center', justifyContent: 'center' @@ -364,6 +832,38 @@ const styles = StyleSheet.create({ betAdjustDisabled: { opacity: 0.4 }, + actionWrap: { + alignItems: 'center', + gap: 10 + }, + actionHint: { + minWidth: '80%', + borderRadius: 18, + paddingVertical: 10, + paddingHorizontal: 16, + borderWidth: 1, + borderColor: 'rgba(255,255,255,0.2)', + backgroundColor: 'rgba(0,0,0,0.25)', + alignItems: 'center' + }, + actionHintActive: { + borderColor: colors.goldBright, + shadowColor: colors.goldBright, + shadowOpacity: 0.4, + shadowRadius: 10 + }, + actionHintText: { + color: colors.goldBright, + fontFamily: fonts.display, + fontSize: 16, + letterSpacing: 2 + }, + turnTimer: { + marginTop: 4, + color: colors.muted, + fontFamily: fonts.mono, + fontSize: 12 + }, actionRow: { flexDirection: 'row', justifyContent: 'space-between', @@ -377,46 +877,45 @@ const styles = StyleSheet.create({ message: { color: colors.goldBright, textAlign: 'center', - marginTop: 8, + marginTop: 4, fontFamily: fonts.body }, - timer: { - color: colors.goldBright, - textAlign: 'center', - marginBottom: 4, - fontFamily: fonts.mono + controlsReveal: { + ...StyleSheet.absoluteFillObject }, - rotateWrap: { - flex: 1, - alignItems: 'center', + winOverlay: { + ...StyleSheet.absoluteFillObject, justifyContent: 'center', - paddingHorizontal: 24 + alignItems: 'center' }, - rotateTitle: { + winCard: { + paddingHorizontal: 24, + paddingVertical: 16, + borderRadius: 18, + backgroundColor: 'rgba(8, 18, 12, 0.85)', + borderWidth: 1, + borderColor: colors.goldBright + }, + winTitle: { color: colors.goldBright, fontFamily: fonts.display, - fontSize: 24, - letterSpacing: 2, + fontSize: 22, textAlign: 'center' }, - rotateSubtitle: { - color: colors.muted, - fontFamily: fonts.body, - fontSize: 14, - marginTop: 12, + winAmount: { + marginTop: 6, + color: colors.text, + fontFamily: fonts.mono, + fontSize: 18, textAlign: 'center' }, - rotateActions: { - marginTop: 20 + confettiLayer: { + ...StyleSheet.absoluteFillObject + }, + confettiPiece: { + position: 'absolute', + top: 0, + borderRadius: 3, + opacity: 0.9 } }); - -const seatPositions = { - 0: { left: '8%', top: '52%' }, - 1: { left: '22%', top: '45%' }, - 2: { left: '36%', top: '40%' }, - 3: { left: '50%', top: '38%' }, - 4: { left: '64%', top: '40%' }, - 5: { left: '78%', top: '45%' }, - 6: { left: '92%', top: '52%' } -};