236 lines
6.8 KiB
TypeScript
236 lines
6.8 KiB
TypeScript
|
|
import React, { useEffect, useMemo, useState } from "react";
|
||
|
|
import {
|
||
|
|
FlatList,
|
||
|
|
Platform,
|
||
|
|
StyleSheet,
|
||
|
|
Text,
|
||
|
|
TextInput,
|
||
|
|
TouchableOpacity,
|
||
|
|
View,
|
||
|
|
} from "react-native";
|
||
|
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||
|
|
import { useNavigation } from "@react-navigation/native";
|
||
|
|
import type { NativeStackNavigationProp } from "@react-navigation/native-stack";
|
||
|
|
import type { RootStackParamList } from "../navigation/types";
|
||
|
|
import { useSession } from "../state/session-context";
|
||
|
|
import { useI18n } from "../i18n";
|
||
|
|
import { useTheme } from "../theme";
|
||
|
|
import type { AppTheme } from "../theme";
|
||
|
|
import ExitGameButton from "../components/ExitGameButton";
|
||
|
|
|
||
|
|
export default function LobbyScreen() {
|
||
|
|
const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||
|
|
const manager = useSession();
|
||
|
|
const { t } = useI18n();
|
||
|
|
const theme = useTheme();
|
||
|
|
const styles = useMemo(() => createStyles(theme), [theme]);
|
||
|
|
const placeholderColor = theme.colors.placeholder;
|
||
|
|
const [dummyName, setDummyName] = useState("");
|
||
|
|
const [dummyBalance, setDummyBalance] = useState("1500");
|
||
|
|
const insets = useSafeAreaInsets();
|
||
|
|
const topInset = insets.top || (Platform.OS === "ios" ? 44 : 0);
|
||
|
|
const containerStyle = useMemo(
|
||
|
|
() => [
|
||
|
|
styles.container,
|
||
|
|
{
|
||
|
|
paddingTop: topInset + 20,
|
||
|
|
paddingBottom: insets.bottom + 20,
|
||
|
|
paddingLeft: insets.left + 20,
|
||
|
|
paddingRight: insets.right + 20,
|
||
|
|
},
|
||
|
|
],
|
||
|
|
[
|
||
|
|
styles.container,
|
||
|
|
topInset,
|
||
|
|
insets.bottom,
|
||
|
|
insets.left,
|
||
|
|
insets.right,
|
||
|
|
],
|
||
|
|
);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (!manager.session || !manager.me) return;
|
||
|
|
if (manager.session.status === "active") {
|
||
|
|
navigation.replace(manager.isBanker ? "BankerTabs" : "PlayerTabs");
|
||
|
|
}
|
||
|
|
}, [manager.session, manager.me, manager.isBanker, navigation]);
|
||
|
|
|
||
|
|
if (!manager.session || !manager.me) {
|
||
|
|
return (
|
||
|
|
<View style={containerStyle}>
|
||
|
|
<Text style={styles.title}>{t("common.loadingLobby")}</Text>
|
||
|
|
{manager.error ? <Text style={styles.helper}>{manager.error}</Text> : null}
|
||
|
|
<ExitGameButton mode="full" />
|
||
|
|
</View>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
const canStart = manager.isBanker && manager.session.status === "lobby";
|
||
|
|
|
||
|
|
return (
|
||
|
|
<View style={containerStyle}>
|
||
|
|
<Text style={styles.title}>{t("lobby.title")}</Text>
|
||
|
|
<Text style={styles.subtitle}>{t("lobby.code", { code: manager.session.code })}</Text>
|
||
|
|
|
||
|
|
<FlatList
|
||
|
|
data={manager.session.players}
|
||
|
|
keyExtractor={(item) => item.id}
|
||
|
|
contentContainerStyle={styles.list}
|
||
|
|
renderItem={({ item }) => (
|
||
|
|
<View style={styles.listItem}>
|
||
|
|
<View>
|
||
|
|
<Text style={styles.playerName}>{item.name}</Text>
|
||
|
|
<Text style={styles.playerMeta}>
|
||
|
|
{item.role === "banker" ? t("common.banker") : t("common.player")}{" "}
|
||
|
|
{item.isDummy ? `- ${t("common.dummy")}` : ""}
|
||
|
|
</Text>
|
||
|
|
</View>
|
||
|
|
<Text style={styles.playerMeta}>
|
||
|
|
{item.connected ? t("common.online") : t("common.offline")}
|
||
|
|
</Text>
|
||
|
|
</View>
|
||
|
|
)}
|
||
|
|
/>
|
||
|
|
|
||
|
|
{manager.isBanker && manager.session.status === "lobby" && (
|
||
|
|
<View style={styles.card}>
|
||
|
|
<Text style={styles.cardTitle}>{t("lobby.addDummyTitle")}</Text>
|
||
|
|
<Text style={styles.helper}>{t("lobby.addDummySubtitle")}</Text>
|
||
|
|
<TextInput
|
||
|
|
style={styles.input}
|
||
|
|
placeholder={t("lobby.enterDummyName")}
|
||
|
|
placeholderTextColor={placeholderColor}
|
||
|
|
value={dummyName}
|
||
|
|
onChangeText={setDummyName}
|
||
|
|
/>
|
||
|
|
<TextInput
|
||
|
|
style={styles.input}
|
||
|
|
placeholder={t("banker.tools.startingBalance")}
|
||
|
|
placeholderTextColor={placeholderColor}
|
||
|
|
value={dummyBalance}
|
||
|
|
onChangeText={setDummyBalance}
|
||
|
|
keyboardType="number-pad"
|
||
|
|
/>
|
||
|
|
<TouchableOpacity
|
||
|
|
style={styles.buttonSecondary}
|
||
|
|
onPress={() => {
|
||
|
|
manager.sendMessage({
|
||
|
|
type: "banker_create_dummy",
|
||
|
|
sessionId: manager.sessionId,
|
||
|
|
bankerId: manager.me?.id,
|
||
|
|
name: dummyName,
|
||
|
|
balance: Number(dummyBalance) || undefined,
|
||
|
|
});
|
||
|
|
setDummyName("");
|
||
|
|
setDummyBalance("1500");
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<Text style={styles.buttonSecondaryText}>{t("lobby.addDummyButton")}</Text>
|
||
|
|
</TouchableOpacity>
|
||
|
|
</View>
|
||
|
|
)}
|
||
|
|
|
||
|
|
{canStart && (
|
||
|
|
<TouchableOpacity
|
||
|
|
style={styles.button}
|
||
|
|
onPress={() =>
|
||
|
|
manager.sendMessage({
|
||
|
|
type: "banker_start",
|
||
|
|
sessionId: manager.sessionId,
|
||
|
|
bankerId: manager.me?.id,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
>
|
||
|
|
<Text style={styles.buttonText}>{t("lobby.startGame")}</Text>
|
||
|
|
</TouchableOpacity>
|
||
|
|
)}
|
||
|
|
|
||
|
|
<ExitGameButton mode="full" />
|
||
|
|
</View>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
const createStyles = (theme: AppTheme) =>
|
||
|
|
StyleSheet.create({
|
||
|
|
container: {
|
||
|
|
flex: 1,
|
||
|
|
paddingHorizontal: 0,
|
||
|
|
paddingBottom: 0,
|
||
|
|
gap: 12,
|
||
|
|
backgroundColor: theme.colors.background,
|
||
|
|
},
|
||
|
|
title: {
|
||
|
|
fontSize: 24,
|
||
|
|
fontWeight: "700",
|
||
|
|
color: theme.colors.text,
|
||
|
|
},
|
||
|
|
subtitle: {
|
||
|
|
color: theme.colors.textMuted,
|
||
|
|
},
|
||
|
|
list: {
|
||
|
|
gap: 10,
|
||
|
|
paddingBottom: 20,
|
||
|
|
},
|
||
|
|
listItem: {
|
||
|
|
backgroundColor: theme.colors.surface,
|
||
|
|
borderRadius: 12,
|
||
|
|
padding: 12,
|
||
|
|
flexDirection: "row",
|
||
|
|
justifyContent: "space-between",
|
||
|
|
alignItems: "center",
|
||
|
|
},
|
||
|
|
playerName: {
|
||
|
|
fontWeight: "600",
|
||
|
|
color: theme.colors.text,
|
||
|
|
},
|
||
|
|
playerMeta: {
|
||
|
|
fontSize: 12,
|
||
|
|
color: theme.colors.textMuted,
|
||
|
|
},
|
||
|
|
card: {
|
||
|
|
backgroundColor: theme.colors.surface,
|
||
|
|
borderRadius: 16,
|
||
|
|
padding: 16,
|
||
|
|
gap: 10,
|
||
|
|
borderWidth: 1,
|
||
|
|
borderColor: theme.colors.borderMuted,
|
||
|
|
},
|
||
|
|
cardTitle: {
|
||
|
|
fontWeight: "600",
|
||
|
|
color: theme.colors.text,
|
||
|
|
},
|
||
|
|
helper: {
|
||
|
|
color: theme.colors.textMuted,
|
||
|
|
fontSize: 12,
|
||
|
|
},
|
||
|
|
input: {
|
||
|
|
borderWidth: 1,
|
||
|
|
borderColor: theme.colors.border,
|
||
|
|
backgroundColor: theme.colors.inputBackground,
|
||
|
|
color: theme.colors.inputText,
|
||
|
|
borderRadius: 12,
|
||
|
|
paddingHorizontal: 12,
|
||
|
|
paddingVertical: 10,
|
||
|
|
},
|
||
|
|
button: {
|
||
|
|
backgroundColor: theme.colors.primary,
|
||
|
|
paddingVertical: 14,
|
||
|
|
borderRadius: 999,
|
||
|
|
alignItems: "center",
|
||
|
|
},
|
||
|
|
buttonText: {
|
||
|
|
color: theme.colors.primaryText,
|
||
|
|
fontWeight: "600",
|
||
|
|
},
|
||
|
|
buttonSecondary: {
|
||
|
|
backgroundColor: theme.colors.secondary,
|
||
|
|
paddingVertical: 12,
|
||
|
|
borderRadius: 999,
|
||
|
|
alignItems: "center",
|
||
|
|
},
|
||
|
|
buttonSecondaryText: {
|
||
|
|
color: theme.colors.secondaryText,
|
||
|
|
fontWeight: "600",
|
||
|
|
},
|
||
|
|
});
|