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