import React, { useState, useEffect } from 'react'; import Axios from 'axios'; import { View, StyleSheet, Text, Platform } from 'react-native'; import * as Haptics from 'expo-haptics'; import NetInfo from '@react-native-community/netinfo'; import { MaterialCommunityIcons } from '@expo/vector-icons'; import { colors, urls, calcFromHeight } from '../lib/constants'; import { firestore, getConnectedPlayers } from '../lib/firebaseUtils'; import PlayerMenu from '../components/online/PlayerMenu'; import withSpinner from '../components/withSpinner'; import GameLoader from '../components/online/GameLoader'; import { useDispatch, useSelector } from 'react-redux'; import { selectLobbyId } from '../redux/game/game.selectors'; import { setLobbyId, setPlayerId } from '../redux/game/game.actions'; import { selectHaptics, selectTheme, } from '../redux/settings/settings.selectors'; import { ThemeMode } from '../types/Theme'; import { handleError } from '../lib/handleError'; import { GridNumber, GridString } from '../types/Game'; import { useDimensions } from '@react-native-community/hooks'; import { showToast } from '../lib/toast'; // Wrapping gamecanvas and playermenu in the spinner HOC component const PlayerMenuWithSpinner = withSpinner(PlayerMenu); const OnlineMultiplayer: React.FC = () => { const [textInput, setTextInput] = useState(''); const [gridSize, setGridSize] = useState<GridNumber>(3); const [connected, setConnected] = useState(false); const theme = useSelector(selectTheme); const hapticsEnabled = useSelector(selectHaptics); const lobbyId = useSelector(selectLobbyId); const dispatch = useDispatch(); useEffect(() => { let unsubscribe: any; if (Platform.OS !== 'web') { unsubscribe = NetInfo.addEventListener(state => { setConnected(state.isConnected); }); } return () => { if (Platform.OS !== 'web') { unsubscribe(); } }; }, []); const { height } = useDimensions().window; const styles = getStyleSheet(theme, height); const [loading, setLoading] = useState(false); const handleNewGame = async () => { setLoading(true); try { const response = await Axios({ method: 'POST', url: `${urls.gameUrl}/new`, data: { gameSize: gridSize }, }); const { data } = response; dispatch(setPlayerId(0)); dispatch(setLobbyId(data.lobbyId)); } catch (err) { handleError(err); } setLoading(false); }; const handleJoinGame = async () => { try { // Fetching lobby from text input const snapshot = await firestore .collection('lobbies') .doc(textInput) .get(); // Checking if lobby exists if (!snapshot.exists) { if (Platform.OS === 'ios' && hapticsEnabled) { Haptics.notificationAsync('error' as any); } showToast('This lobby does not exist...'); return; } const players = snapshot?.data()?.players; const connectedPlayers = getConnectedPlayers(players); const playerId = players[0].connected ? 1 : players[1].connected ? 0 : 0; if (connectedPlayers.length >= 2) { if (Platform.OS === 'ios' && hapticsEnabled) { Haptics.notificationAsync('error' as any); } showToast('Lobby is full...'); return; } dispatch(setPlayerId(playerId)); dispatch(setLobbyId(textInput)); if (Platform.OS === 'ios' && hapticsEnabled) Haptics.notificationAsync('success' as any); } catch (err) { handleError(err); } }; const handleInputChange = (text: string) => setTextInput(text); const handleGridSizeChange = (value: GridString) => { if (value) { if (Platform.OS === 'ios' && hapticsEnabled) Haptics.selectionAsync(); setGridSize(Number(value) as GridNumber); } }; if (connected || Platform.OS === 'web') { return ( <View style={styles.container}> {lobbyId ? ( <GameLoader styles={styles} /> ) : ( //No nested if, loading state passed directly to component <PlayerMenuWithSpinner msg="Connecting to game server" loading={loading} {...{ setTextInput, styles, textInput, gridSize, setGridSize, handleGridSizeChange, handleInputChange, handleNewGame, handleJoinGame, }} /> //No nested if, loading state passed directly to component )} </View> ); } else { return ( <View style={styles.container}> <MaterialCommunityIcons color={colors[theme].text} name="wifi-strength-alert-outline" size={30} /> <Text style={styles.text}> Please check your{'\n'}network connection! </Text> </View> ); } }; const getStyleSheet = (theme: ThemeMode, height: number) => { return StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: colors[theme].bg, }, joinText: { color: colors[theme].text, marginTop: calcFromHeight(15, height), fontSize: 20, textAlign: 'center', fontWeight: '500', }, text: { color: colors[theme].text, margin: calcFromHeight(8, height), fontSize: 20, textAlign: 'center', fontWeight: '500', }, lobbyId: { color: colors[theme].text, marginTop: calcFromHeight(15, height), fontSize: 20, textAlign: 'center', fontWeight: 'bold', }, infoText: { color: colors[theme].warning, margin: calcFromHeight(5, height), fontSize: 15, textAlign: 'center', fontWeight: 'bold', }, button: { width: 200, margin: calcFromHeight(8, height), backgroundColor: colors[theme].main, }, buttonGroup: { backgroundColor: colors[theme].main, }, buttonGroupSelected: { backgroundColor: colors[theme].text, }, quitButton: { margin: calcFromHeight(8, height), marginBottom: calcFromHeight(20, height), backgroundColor: colors[theme].main, }, input: { color: 'white', textAlign: 'center', backgroundColor: colors[theme].disabledButton, height: 40, width: 200, margin: calcFromHeight(8, height), borderRadius: 5, borderColor: colors[theme].main, fontSize: 20, }, }); }; export default OnlineMultiplayer;