import React, { useEffect, useMemo, useRef, useState } from "react"; import { Alert, ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View, } from "react-native"; import { useNavigation, useRoute } from "@react-navigation/native"; import type { RouteProp } from "@react-navigation/native"; import type { NativeStackNavigationProp } from "@react-navigation/native-stack"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import BrandLockup from "../components/BrandLockup"; import { useI18n } from "../i18n"; import type { RootStackParamList } from "../navigation/types"; import { useSession } from "../state/session-context"; import type { SessionPreview } from "../shared/types"; import { useTheme, type AppTheme } from "../theme"; export default function AgencyJoinScreen() { const navigation = useNavigation>(); const route = useRoute>(); const manager = useSession(); const { t } = useI18n(); const theme = useTheme(); const styles = useMemo(() => createStyles(theme), [theme]); const placeholderColor = theme.colors.placeholder; const handledLinkRef = useRef(null); const insets = useSafeAreaInsets(); const [joinCode, setJoinCode] = useState(""); const [joinStep, setJoinStep] = useState<"code" | "choice">("code"); const [joinPreview, setJoinPreview] = useState(null); const [joinName, setJoinName] = useState(""); const [takeoverName, setTakeoverName] = useState(""); const [takeoverDummyId, setTakeoverDummyId] = useState(""); const [showDummyOptions, setShowDummyOptions] = useState(false); const [takeoverToken, setTakeoverToken] = useState(null); const [takeoverWaiting, setTakeoverWaiting] = useState(false); const dummyOptions = useMemo( () => joinPreview?.players.filter((player) => player.isDummy) ?? [], [joinPreview], ); const storedPlayer = joinPreview?.players.find((player) => player.id === manager.playerId); const takeoverDisabled = storedPlayer?.connected === true; async function resolvePreview(code: string) { const preview = await manager.fetchSessionPreview(code); if (!preview) { Alert.alert(t("entry.alert.sessionNotFound")); return null; } setJoinPreview(preview); setJoinStep("choice"); return preview; } async function handleJoinPreview() { const normalized = joinCode.trim().toUpperCase(); if (!normalized) { Alert.alert(t("entry.alert.enterCode")); return; } await resolvePreview(normalized); } useEffect(() => { const raw = route.params?.gameId; if (typeof raw !== "string") { return; } const normalized = raw.trim().toUpperCase(); if (!normalized || handledLinkRef.current === normalized) { return; } handledLinkRef.current = normalized; setJoinCode(normalized); setJoinStep("code"); setJoinPreview(null); setJoinName(""); setTakeoverName(""); setTakeoverDummyId(""); setTakeoverToken(null); setTakeoverWaiting(false); void resolvePreview(normalized); }, [route.params?.gameId]); async function handleJoinNew() { if (!joinPreview) return; const data = await manager.joinSession(joinPreview.code, joinName.trim()); if (data) { navigation.replace("Lobby"); } } async function handleTakeover() { if (!joinPreview) return; if (!takeoverDummyId) { Alert.alert(t("entry.alert.selectDummy")); return; } setTakeoverWaiting(true); const selectedDummy = joinPreview.players.find((player) => player.id === takeoverDummyId); const fallbackName = takeoverName.trim() || selectedDummy?.name || ""; const token = await manager.requestTakeoverToken( joinPreview.code, takeoverDummyId, fallbackName, ); if (!token) { setTakeoverWaiting(false); if (manager.error) { Alert.alert(manager.error); } return; } setTakeoverToken(token); } useEffect(() => { if (joinStep === "code" || !joinPreview) { setShowDummyOptions(false); setTakeoverToken(null); setTakeoverWaiting(false); } }, [joinStep, joinPreview]); useEffect(() => { if (!takeoverToken || !joinPreview) return; let cancelled = false; let timeout: ReturnType | null = null; const poll = async () => { const data = await manager.claimTakeover(joinPreview.code, takeoverToken); if (cancelled) return; if (data) { setTakeoverWaiting(false); setTakeoverToken(null); navigation.replace("Lobby"); return; } timeout = setTimeout(poll, 2000); }; void poll(); return () => { cancelled = true; if (timeout) clearTimeout(timeout); }; }, [joinPreview, takeoverToken, manager, navigation]); return ( {t("entry.joinStepTitle")} {t("entry.joinStepSubtitle")} {t("entry.joinTitle")} {t("entry.joinDescription")} { setJoinCode(value.toUpperCase()); if (joinStep === "choice") { setJoinStep("code"); setJoinPreview(null); setJoinName(""); setTakeoverName(""); setTakeoverDummyId(""); setShowDummyOptions(false); } }} /> {t("common.continue")} {joinStep === "choice" && joinPreview ? ( <> {t("entry.previewLabel")} {t("entry.agencyCodeValue", { code: joinPreview.code })} {t("entry.previewCustomers", { count: joinPreview.players.length })} {t("entry.newPlayer")} {t("entry.newCustomerDescription")} {t("entry.joinAsCustomer")} {t("entry.takeoverTitle")} {t("entry.recoverCustomerDescription")} {takeoverDisabled ? ( {t("entry.alreadyConnected")} ) : takeoverWaiting ? ( {t("entry.takeoverPending")} { setTakeoverToken(null); setTakeoverWaiting(false); }} > {t("common.cancel")} ) : ( <> { if (dummyOptions.length === 0) return; setShowDummyOptions((prev) => !prev); }} > {dummyOptions.find((player) => player.id === takeoverDummyId)?.name ? `${dummyOptions.find((player) => player.id === takeoverDummyId)?.name} ยท ${takeoverDummyId}` : t("entry.selectDummy")} {showDummyOptions && dummyOptions.length > 0 ? ( {dummyOptions.map((player) => ( { setTakeoverDummyId(player.id); setShowDummyOptions(false); }} > {player.name} {player.id} ))} ) : null} {t("entry.requestTakeover")} )} {!takeoverDisabled && dummyOptions.length === 0 ? ( {t("entry.noDummies")} ) : null} ) : null} navigation.replace("AgencyCreate")} > {t("entry.linkOpenNew")} ); } const createStyles = (theme: AppTheme) => StyleSheet.create({ scroll: { flex: 1, backgroundColor: theme.colors.background, }, container: { gap: 18, backgroundColor: theme.colors.background, }, hero: { borderRadius: 28, padding: 22, gap: 12, backgroundColor: theme.colors.brandSurface, borderWidth: 1, borderColor: theme.colors.brandSurfaceAlt, }, heroTitle: { color: theme.colors.brandText, fontSize: 28, fontWeight: "800", letterSpacing: -0.8, }, heroBody: { color: theme.colors.brandTextMuted, fontSize: 15, lineHeight: 22, }, card: { backgroundColor: theme.colors.surface, borderRadius: 24, padding: 20, gap: 12, borderWidth: 1, borderColor: theme.colors.border, }, cardTitle: { color: theme.colors.text, fontSize: 22, fontWeight: "700", letterSpacing: -0.4, }, cardSubtitle: { color: theme.colors.textMuted, fontSize: 14, lineHeight: 21, }, input: { borderWidth: 1, borderColor: theme.colors.border, backgroundColor: theme.colors.inputBackground, color: theme.colors.inputText, borderRadius: 16, paddingHorizontal: 14, paddingVertical: 13, }, button: { backgroundColor: theme.colors.primary, paddingVertical: 14, borderRadius: 999, alignItems: "center", }, buttonText: { color: theme.colors.primaryText, fontWeight: "700", }, previewCard: { borderRadius: 24, padding: 20, gap: 8, backgroundColor: theme.colors.accentSurface, borderWidth: 1, borderColor: theme.colors.borderMuted, }, previewEyebrow: { color: theme.colors.accent, fontSize: 12, fontWeight: "700", letterSpacing: 1.1, textTransform: "uppercase", }, previewTitle: { color: theme.colors.text, fontSize: 22, fontWeight: "800", letterSpacing: -0.5, }, previewBody: { color: theme.colors.textMuted, }, choiceCard: { backgroundColor: theme.colors.surface, borderRadius: 24, padding: 20, gap: 12, borderWidth: 1, borderColor: theme.colors.border, }, choiceTitle: { fontSize: 18, fontWeight: "700", color: theme.colors.text, }, choiceBody: { color: theme.colors.textMuted, lineHeight: 20, }, dropdown: { gap: 6, }, dropdownButton: { borderWidth: 1, borderColor: theme.colors.border, backgroundColor: theme.colors.inputBackground, borderRadius: 16, paddingHorizontal: 14, paddingVertical: 13, }, dropdownText: { color: theme.colors.inputText, fontWeight: "600", }, dropdownList: { borderWidth: 1, borderColor: theme.colors.border, borderRadius: 16, backgroundColor: theme.colors.surface, overflow: "hidden", }, pendingBox: { gap: 10, backgroundColor: theme.colors.surfaceAlt, borderRadius: 16, padding: 14, borderWidth: 1, borderColor: theme.colors.borderMuted, }, dropdownItem: { paddingHorizontal: 14, paddingVertical: 12, borderBottomWidth: 1, borderBottomColor: theme.colors.borderMuted, }, dropdownItemActive: { backgroundColor: theme.colors.accentSurface, }, dropdownItemText: { fontWeight: "600", color: theme.colors.text, }, dropdownItemMeta: { color: theme.colors.textMuted, fontSize: 12, }, buttonSecondary: { backgroundColor: theme.colors.secondary, paddingVertical: 14, borderRadius: 999, alignItems: "center", }, buttonSecondaryText: { color: theme.colors.secondaryText, fontWeight: "700", }, helper: { fontSize: 12, color: theme.colors.textMuted, }, linkButton: { paddingVertical: 12, alignItems: "center", }, linkText: { color: theme.colors.textMuted, fontWeight: "600", }, });