feat: initialize mobile blackjack app with authentication and game features

This commit is contained in:
2025-12-20 23:10:06 +01:00
commit 1160c3a713
19 changed files with 1107 additions and 0 deletions

81
src/components/Card.js Normal file
View File

@@ -0,0 +1,81 @@
import { StyleSheet, Text, View } from 'react-native';
import { colors, fonts } from '../theme';
const suitSymbols = {
S: '♠',
H: '♥',
D: '♦',
C: '♣'
};
const suitColors = {
S: '#1c1c1c',
C: '#1c1c1c',
H: colors.red,
D: colors.red
};
export default function Card({ rank, suit, hidden }) {
if (hidden || rank === 'X') {
return (
<View style={[styles.card, styles.cardBack]}>
<View style={styles.backPattern} />
</View>
);
}
const symbol = suitSymbols[suit] || '?';
const color = suitColors[suit] || colors.text;
return (
<View style={styles.card}>
<Text style={[styles.corner, { color }]}>{rank}</Text>
<Text style={[styles.corner, { color }]}>{symbol}</Text>
<Text style={[styles.center, { color }]}>{symbol}</Text>
<View style={styles.cornerBottom}>
<Text style={[styles.corner, { color }]}>{rank}</Text>
<Text style={[styles.corner, { color }]}>{symbol}</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
card: {
width: 54,
height: 78,
borderRadius: 8,
backgroundColor: '#fdf8f0',
padding: 6,
marginRight: 6,
borderWidth: 1,
borderColor: '#d2c1a4',
justifyContent: 'space-between'
},
cardBack: {
backgroundColor: '#152d52',
borderColor: '#0d1e38',
alignItems: 'center',
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'
},
center: {
fontSize: 24,
fontFamily: fonts.display,
textAlign: 'center'
},
cornerBottom: {
transform: [{ rotate: '180deg' }]
}
});

View File

@@ -0,0 +1,55 @@
import { Pressable, StyleSheet, Text, View } from 'react-native';
import { LinearGradient } from 'expo-linear-gradient';
import { colors, fonts } from '../theme';
const gradients = {
gold: [colors.goldBright, colors.gold],
red: ['#f05a4f', colors.red],
green: ['#39c377', '#1f7a44']
};
export default function CasinoButton({ label, onPress, variant = 'gold', disabled }) {
const textColor = variant === 'gold' ? '#2b1d0b' : '#f7f2e6';
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]}
>
<View style={styles.inner}>
<Text style={[styles.text, { color: textColor }]}>{label}</Text>
</View>
</LinearGradient>
</Pressable>
);
}
const styles = StyleSheet.create({
wrapper: {
shadowColor: colors.shadow,
shadowOpacity: 0.4,
shadowRadius: 6,
shadowOffset: { width: 0, height: 4 }
},
button: {
borderRadius: 999,
paddingVertical: 12,
paddingHorizontal: 24,
borderWidth: 1,
borderColor: 'rgba(255,255,255,0.2)'
},
inner: {
alignItems: 'center'
},
text: {
fontSize: 16,
fontFamily: fonts.body,
letterSpacing: 1,
textTransform: 'uppercase'
},
disabled: {
opacity: 0.5
}
});

45
src/components/Chip.js Normal file
View File

@@ -0,0 +1,45 @@
import { StyleSheet, Text, View } from 'react-native';
import { colors, fonts } from '../theme';
const chipColors = {
blue: colors.chipBlue,
red: colors.chipRed,
green: colors.chipGreen
};
export default function Chip({ label, color = 'blue' }) {
return (
<View style={[styles.chip, { backgroundColor: chipColors[color] || colors.chipBlue }]}>
<View style={styles.inner}>
<Text style={styles.text}>{label}</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
chip: {
width: 48,
height: 48,
borderRadius: 24,
alignItems: 'center',
justifyContent: 'center',
borderWidth: 3,
borderColor: '#f2f1e8'
},
inner: {
width: 28,
height: 28,
borderRadius: 14,
borderWidth: 2,
borderColor: 'rgba(255,255,255,0.7)',
alignItems: 'center',
justifyContent: 'center'
},
text: {
color: '#f7f2e6',
fontWeight: '700',
fontSize: 12,
fontFamily: fonts.mono
}
});

View File

@@ -0,0 +1,33 @@
import { StyleSheet, Text, View } from 'react-native';
import Card from './Card';
import { colors, fonts } from '../theme';
export default function DealerArea({ hand }) {
return (
<View style={styles.wrapper}>
<Text style={styles.label}>Osztó</Text>
<View style={styles.hand}>
{hand.map((card, idx) => (
<Card key={`${card.rank}-${card.suit}-${idx}`} rank={card.rank} suit={card.suit} hidden={card.hidden} />
))}
</View>
</View>
);
}
const styles = StyleSheet.create({
wrapper: {
alignItems: 'center'
},
label: {
color: colors.goldBright,
letterSpacing: 2,
textTransform: 'uppercase',
fontSize: 12,
fontFamily: fonts.body
},
hand: {
flexDirection: 'row',
marginTop: 6
}
});

73
src/components/Seat.js Normal file
View File

@@ -0,0 +1,73 @@
import { StyleSheet, Text, View } from 'react-native';
import Card from './Card';
import { colors, fonts } from '../theme';
export default function Seat({ seat, highlight }) {
const isEmpty = !seat.username;
return (
<View style={[styles.seat, highlight && styles.highlight]}>
<Text style={styles.name}>{isEmpty ? 'Üres hely' : seat.username}</Text>
{!isEmpty && seat.bet > 0 && (
<Text style={styles.bet}>Tet: {seat.bet} Ft</Text>
)}
{!isEmpty && seat.hand?.length > 0 && (
<View style={styles.hand}>
{seat.hand.map((card, idx) => (
<Card key={`${card.rank}-${card.suit}-${idx}`} rank={card.rank} suit={card.suit} hidden={card.hidden} />
))}
</View>
)}
{!isEmpty && seat.result && (
<Text style={styles.result}>
{seat.result.outcome === 'win' && 'Nyereség'}
{seat.result.outcome === 'blackjack' && 'Blackjack!'}
{seat.result.outcome === 'push' && 'Döntetlen'}
{seat.result.outcome === 'bust' && 'Bukás'}
{seat.result.outcome === 'lose' && 'Vesztettél'}
{seat.result.outcome === 'left' && 'Kilépett'}
{seat.result.outcome === 'disconnect' && 'Eltűnt'}
{seat.result.outcome === 'moved' && 'Átült'}
</Text>
)}
</View>
);
}
const styles = StyleSheet.create({
seat: {
padding: 8,
borderRadius: 12,
backgroundColor: 'rgba(0,0,0,0.3)',
borderWidth: 1,
borderColor: 'rgba(255,255,255,0.1)'
},
highlight: {
borderColor: colors.goldBright,
shadowColor: colors.goldBright,
shadowOpacity: 0.6,
shadowRadius: 8
},
name: {
color: colors.text,
fontSize: 12,
fontFamily: fonts.body,
fontWeight: '600'
},
bet: {
color: colors.goldBright,
fontSize: 11,
marginTop: 2,
fontFamily: fonts.mono
},
hand: {
flexDirection: 'row',
marginTop: 4
},
result: {
marginTop: 4,
color: colors.muted,
fontSize: 10,
fontFamily: fonts.body
}
});

View File

@@ -0,0 +1,54 @@
import { StyleSheet, View } from 'react-native';
import { LinearGradient } from 'expo-linear-gradient';
import { colors } from '../theme';
export default function TableBackground({ children }) {
return (
<View style={styles.wrapper}>
<LinearGradient
colors={[colors.tableEdge, '#3e2a10']}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.edge}
>
<LinearGradient
colors={[colors.tableFelt, colors.tableFeltDark]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.felt}
>
<View style={styles.innerRing} />
{children}
</LinearGradient>
</LinearGradient>
</View>
);
}
const styles = StyleSheet.create({
wrapper: {
flex: 1,
padding: 12
},
edge: {
flex: 1,
borderRadius: 220,
padding: 10
},
felt: {
flex: 1,
borderRadius: 200,
padding: 20,
overflow: 'hidden'
},
innerRing: {
position: 'absolute',
top: 16,
bottom: 16,
left: 16,
right: 16,
borderWidth: 2,
borderColor: 'rgba(255,255,255,0.15)',
borderRadius: 180
}
});