feat: enhance CasinoButton, Chip, and TableBackground components; add size prop for responsive styling and update TableScreen controls visibility logic
This commit is contained in:
@@ -8,18 +8,19 @@ const gradients = {
|
||||
green: ['#39c377', '#1f7a44']
|
||||
};
|
||||
|
||||
export default function CasinoButton({ label, onPress, variant = 'gold', disabled }) {
|
||||
export default function CasinoButton({ label, onPress, variant = 'gold', disabled, size = 'default' }) {
|
||||
const textColor = variant === 'gold' ? '#2b1d0b' : '#f7f2e6';
|
||||
const isSmall = size === 'small';
|
||||
return (
|
||||
<Pressable onPress={onPress} disabled={disabled} style={styles.wrapper}>
|
||||
<LinearGradient
|
||||
colors={gradients[variant] || gradients.gold}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 1 }}
|
||||
style={[styles.button, disabled && styles.disabled]}
|
||||
style={[styles.button, isSmall && styles.buttonSmall, disabled && styles.disabled]}
|
||||
>
|
||||
<View style={styles.inner}>
|
||||
<Text style={[styles.text, { color: textColor }]}>{label}</Text>
|
||||
<Text style={[styles.text, isSmall && styles.textSmall, { color: textColor }]}>{label}</Text>
|
||||
</View>
|
||||
</LinearGradient>
|
||||
</Pressable>
|
||||
@@ -40,6 +41,10 @@ const styles = StyleSheet.create({
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(255,255,255,0.2)'
|
||||
},
|
||||
buttonSmall: {
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 16
|
||||
},
|
||||
inner: {
|
||||
alignItems: 'center'
|
||||
},
|
||||
@@ -49,6 +54,10 @@ const styles = StyleSheet.create({
|
||||
letterSpacing: 1,
|
||||
textTransform: 'uppercase'
|
||||
},
|
||||
textSmall: {
|
||||
fontSize: 13,
|
||||
letterSpacing: 0.8
|
||||
},
|
||||
disabled: {
|
||||
opacity: 0.5
|
||||
}
|
||||
|
||||
@@ -7,11 +7,12 @@ const chipColors = {
|
||||
green: colors.chipGreen
|
||||
};
|
||||
|
||||
export default function Chip({ label, color = 'blue' }) {
|
||||
export default function Chip({ label, color = 'blue', size = 'default' }) {
|
||||
const isSmall = size === 'small';
|
||||
return (
|
||||
<View style={[styles.chip, { backgroundColor: chipColors[color] || colors.chipBlue }]}>
|
||||
<View style={[styles.chip, isSmall && styles.chipSmall, { backgroundColor: chipColors[color] || colors.chipBlue }]}>
|
||||
<View style={styles.inner}>
|
||||
<Text style={styles.text}>{label}</Text>
|
||||
<Text style={[styles.text, isSmall && styles.textSmall]}>{label}</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
@@ -27,6 +28,11 @@ const styles = StyleSheet.create({
|
||||
borderWidth: 3,
|
||||
borderColor: '#f2f1e8'
|
||||
},
|
||||
chipSmall: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: 20
|
||||
},
|
||||
inner: {
|
||||
width: 28,
|
||||
height: 28,
|
||||
@@ -41,5 +47,8 @@ const styles = StyleSheet.create({
|
||||
fontWeight: '700',
|
||||
fontSize: 12,
|
||||
fontFamily: fonts.mono
|
||||
},
|
||||
textSmall: {
|
||||
fontSize: 11
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,11 +2,11 @@ import { StyleSheet, View } from 'react-native';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import { colors } from '../theme';
|
||||
|
||||
export default function TableBackground({ children, style }) {
|
||||
export default function TableBackground({ children, style, showRing = true }) {
|
||||
return (
|
||||
<View style={[styles.wrapper, style]}>
|
||||
<LinearGradient
|
||||
colors={[colors.tableFeltDark, '#0a3a2a']}
|
||||
colors={[colors.tableFeltDark, colors.tableFelt]}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 1 }}
|
||||
style={styles.edge}
|
||||
@@ -17,7 +17,7 @@ export default function TableBackground({ children, style }) {
|
||||
end={{ x: 1, y: 1 }}
|
||||
style={styles.felt}
|
||||
>
|
||||
<View style={styles.innerRing} />
|
||||
{showRing ? <View style={styles.innerRing} /> : null}
|
||||
{children}
|
||||
</LinearGradient>
|
||||
</LinearGradient>
|
||||
@@ -33,32 +33,23 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
edge: {
|
||||
flex: 1,
|
||||
borderTopLeftRadius: 12,
|
||||
borderTopRightRadius: 12,
|
||||
borderBottomLeftRadius: 280,
|
||||
borderBottomRightRadius: 280,
|
||||
padding: 4
|
||||
borderRadius: 18,
|
||||
padding: 6
|
||||
},
|
||||
felt: {
|
||||
flex: 1,
|
||||
borderTopLeftRadius: 8,
|
||||
borderTopRightRadius: 8,
|
||||
borderBottomLeftRadius: 260,
|
||||
borderBottomRightRadius: 260,
|
||||
padding: 12,
|
||||
borderRadius: 16,
|
||||
padding: 14,
|
||||
overflow: 'hidden'
|
||||
},
|
||||
innerRing: {
|
||||
position: 'absolute',
|
||||
top: 16,
|
||||
bottom: 16,
|
||||
left: 16,
|
||||
right: 16,
|
||||
borderWidth: 2,
|
||||
top: 12,
|
||||
bottom: 12,
|
||||
left: 12,
|
||||
right: 12,
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(255,255,255,0.15)',
|
||||
borderTopLeftRadius: 6,
|
||||
borderTopRightRadius: 6,
|
||||
borderBottomLeftRadius: 230,
|
||||
borderBottomRightRadius: 230
|
||||
borderRadius: 14
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useCallback, 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';
|
||||
@@ -18,7 +18,9 @@ export default function TableScreen({ token, tableId, user, onLeave }) {
|
||||
const [message, setMessage] = useState('');
|
||||
const [betAmount, setBetAmount] = useState(10);
|
||||
const [turnSeconds, setTurnSeconds] = useState(null);
|
||||
const [controlsVisible, setControlsVisible] = useState(true);
|
||||
const wsRef = useRef(null);
|
||||
const hideTimerRef = useRef(null);
|
||||
const pulse = useRef(new Animated.Value(0)).current;
|
||||
const insets = useSafeAreaInsets();
|
||||
const { width, height } = useWindowDimensions();
|
||||
@@ -69,6 +71,32 @@ export default function TableScreen({ token, tableId, user, onLeave }) {
|
||||
const isMyTurn = table?.phase === 'playing' && table?.currentSeatIndex === mySeat?.index;
|
||||
const bettingLocked = ['playing', 'dealer', 'payout'].includes(table?.phase);
|
||||
|
||||
const scheduleHide = useCallback(() => {
|
||||
if (hideTimerRef.current) {
|
||||
clearTimeout(hideTimerRef.current);
|
||||
}
|
||||
hideTimerRef.current = setTimeout(() => {
|
||||
setControlsVisible(false);
|
||||
}, 5000);
|
||||
}, []);
|
||||
|
||||
const showControls = useCallback(() => {
|
||||
setControlsVisible(true);
|
||||
scheduleHide();
|
||||
}, [scheduleHide]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isMyTurn) {
|
||||
setControlsVisible(true);
|
||||
}
|
||||
scheduleHide();
|
||||
return () => {
|
||||
if (hideTimerRef.current) {
|
||||
clearTimeout(hideTimerRef.current);
|
||||
}
|
||||
};
|
||||
}, [isMyTurn, scheduleHide]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!table?.turnEndsAt || !isMyTurn) {
|
||||
setTurnSeconds(null);
|
||||
@@ -114,6 +142,7 @@ export default function TableScreen({ token, tableId, user, onLeave }) {
|
||||
|
||||
const send = (payload) => {
|
||||
if (wsRef.current?.readyState === 1) {
|
||||
showControls();
|
||||
wsRef.current.send(JSON.stringify(payload));
|
||||
}
|
||||
};
|
||||
@@ -122,6 +151,7 @@ export default function TableScreen({ token, tableId, user, onLeave }) {
|
||||
if (!table) {
|
||||
return;
|
||||
}
|
||||
showControls();
|
||||
setBetAmount((prev) => {
|
||||
const next = prev + delta;
|
||||
return Math.max(table.minBet, Math.min(table.maxBet, next));
|
||||
@@ -173,7 +203,7 @@ export default function TableScreen({ token, tableId, user, onLeave }) {
|
||||
</View>
|
||||
|
||||
<View style={styles.tableWrap}>
|
||||
<TableBackground style={styles.tableSurface}>
|
||||
<TableBackground style={styles.tableSurface} showRing={false}>
|
||||
<View style={styles.tableInner}>
|
||||
<TableMarkings />
|
||||
<View style={styles.tableContent}>
|
||||
@@ -189,6 +219,7 @@ export default function TableScreen({ token, tableId, user, onLeave }) {
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{controlsVisible ? (
|
||||
<View style={styles.controlsOverlay}>
|
||||
<BlurView intensity={28} tint="dark" style={styles.blurPanel}>
|
||||
<View style={styles.betRow}>
|
||||
@@ -197,13 +228,13 @@ export default function TableScreen({ token, tableId, user, onLeave }) {
|
||||
<Pressable onPress={() => adjustBet(-10)} style={[styles.betAdjust, bettingLocked && styles.betAdjustDisabled]} disabled={bettingLocked}>
|
||||
<Text style={styles.betAdjustText}>-</Text>
|
||||
</Pressable>
|
||||
<Chip label={`${betAmount}`} color="red" />
|
||||
<Chip label={`${betAmount}`} color="red" size="small" />
|
||||
<Pressable onPress={() => adjustBet(10)} style={[styles.betAdjust, bettingLocked && styles.betAdjustDisabled]} disabled={bettingLocked}>
|
||||
<Text style={styles.betAdjustText}>+</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
<CasinoButton label="Tét" onPress={() => send({ type: 'bet', amount: betAmount })} variant="gold" disabled={bettingLocked} />
|
||||
<CasinoButton label="Kész" onPress={() => send({ type: 'ready' })} variant="green" disabled={bettingLocked} />
|
||||
<CasinoButton label="Tét" onPress={() => send({ type: 'bet', amount: betAmount })} variant="gold" size="small" disabled={bettingLocked} />
|
||||
<CasinoButton label="Kész" onPress={() => send({ type: 'ready' })} variant="green" size="small" disabled={bettingLocked} />
|
||||
</View>
|
||||
</BlurView>
|
||||
|
||||
@@ -212,12 +243,15 @@ export default function TableScreen({ token, tableId, user, onLeave }) {
|
||||
<Text style={styles.timer}>Idő: {turnSeconds} mp</Text>
|
||||
) : null}
|
||||
<Animated.View style={[styles.actionRow, isMyTurn && pulseStyle]}>
|
||||
<CasinoButton label="Hit" onPress={() => send({ type: 'action', action: 'hit' })} variant="gold" disabled={!isMyTurn} />
|
||||
<CasinoButton label="Stand" onPress={() => send({ type: 'action', action: 'stand' })} variant="gold" disabled={!isMyTurn} />
|
||||
<CasinoButton label="Double" onPress={() => send({ type: 'action', action: 'double' })} variant="gold" disabled={!isMyTurn} />
|
||||
<CasinoButton label="Hit" onPress={() => send({ type: 'action', action: 'hit' })} variant="gold" size="small" disabled={!isMyTurn} />
|
||||
<CasinoButton label="Stand" onPress={() => send({ type: 'action', action: 'stand' })} variant="gold" size="small" disabled={!isMyTurn} />
|
||||
<CasinoButton label="Double" onPress={() => send({ type: 'action', action: 'double' })} variant="gold" size="small" disabled={!isMyTurn} />
|
||||
</Animated.View>
|
||||
</BlurView>
|
||||
</View>
|
||||
) : (
|
||||
<Pressable style={styles.controlsReveal} onPress={showControls} />
|
||||
)}
|
||||
|
||||
{message ? <Text style={styles.message}>{message}</Text> : null}
|
||||
</View>
|
||||
@@ -273,7 +307,7 @@ const styles = StyleSheet.create({
|
||||
tableContent: {
|
||||
flex: 1,
|
||||
justifyContent: 'space-between',
|
||||
paddingBottom: 120
|
||||
paddingBottom: 140
|
||||
},
|
||||
seatLayer: {
|
||||
flex: 1,
|
||||
@@ -281,20 +315,23 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
seatSpot: {
|
||||
position: 'absolute',
|
||||
width: 92,
|
||||
transform: [{ translateX: -46 }]
|
||||
width: 90,
|
||||
transform: [{ translateX: -45 }]
|
||||
},
|
||||
controlsOverlay: {
|
||||
position: 'absolute',
|
||||
left: 18,
|
||||
right: 18,
|
||||
bottom: 12,
|
||||
left: 20,
|
||||
right: 20,
|
||||
bottom: 10,
|
||||
gap: 10
|
||||
},
|
||||
controlsReveal: {
|
||||
...StyleSheet.absoluteFillObject
|
||||
},
|
||||
blurPanel: {
|
||||
borderRadius: 18,
|
||||
paddingVertical: 10,
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 10,
|
||||
backgroundColor: 'rgba(10, 18, 14, 0.45)',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(255,255,255,0.12)',
|
||||
@@ -312,16 +349,16 @@ const styles = StyleSheet.create({
|
||||
gap: 6
|
||||
},
|
||||
betAdjust: {
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: 16,
|
||||
width: 28,
|
||||
height: 28,
|
||||
borderRadius: 14,
|
||||
backgroundColor: 'rgba(255,255,255,0.12)',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
betAdjustText: {
|
||||
color: colors.text,
|
||||
fontSize: 18,
|
||||
fontSize: 16,
|
||||
fontWeight: '700'
|
||||
},
|
||||
betAdjustDisabled: {
|
||||
@@ -346,7 +383,7 @@ const styles = StyleSheet.create({
|
||||
timer: {
|
||||
color: colors.goldBright,
|
||||
textAlign: 'center',
|
||||
marginBottom: 6,
|
||||
marginBottom: 4,
|
||||
fontFamily: fonts.mono
|
||||
},
|
||||
rotateWrap: {
|
||||
|
||||
Reference in New Issue
Block a user