diff --git a/src/Friends/index.tsx b/src/Friends/index.tsx index 9ed5227..4f51fb3 100644 --- a/src/Friends/index.tsx +++ b/src/Friends/index.tsx @@ -1,5 +1,6 @@ // Import FirebaseAuth and firebase. -import React, { useState, useEffect, useCallback, ChangeEvent } from 'react'; +import { useState, useEffect, useCallback, useRef } from 'react'; +import type { ChangeEventHandler, KeyboardEventHandler } from 'react'; import * as firebaseui from 'firebaseui'; import StyledFirebaseAuth from '../StyledFirebaseAuth'; @@ -25,54 +26,121 @@ const uiConfig = { }, }; + +function createMatch(user1: string, user2: string) { + // Create new game record + const gameRef = firebase.database().ref('games').push(); + // Point match to game + const data = { + sort: Date.now(), + game: gameRef.key, + }; + firebase.database().ref(`matches/${user1}/${user2}/game`).set(data); + firebase.database().ref(`matches/${user2}/${user1}/game`).set(data); +} + +type UserData = { + name: string; + photoURL: string; + language: string; +} + export default function Login({ show }: { show: boolean }) { - const [chats, setChats] = useState([]); - const [selectedChat, setSelectedChat] = useState(null); - const [search, setSearch] = useState(''); + const [matches, setMatches] = useState([]); + const [selected, setSelected] = useState(null); + const searchRef = useRef(null); + const user = useAuth(); // Local signed-in state. + const [userData, setUserData] = useState(null); + const [newFriend, setNewFriend] = useState(null); + // onLogin useEffect(() => { - const fetchChats = async () => { - const chatRef = firebase.database().ref('chats'); - chatRef.on('value', (snapshot) => { - const chatArray: firebase.database.DataSnapshot[] = []; - snapshot.forEach((childSnapshot) => { - chatArray.push(childSnapshot); - }); - setChats(chatArray); + if (!user) return; + const matchesRef = firebase.database().ref(`matches/${user.uid}`).orderByChild('sort') + const userRef = firebase.database().ref(`users/${user.uid}`) + const subscriber = (snapshot: firebase.database.DataSnapshot) => { + const matchesArray: firebase.database.DataSnapshot[] = []; + snapshot.forEach((childSnapshot) => { + matchesArray.push(childSnapshot); }); + setMatches(matchesArray); }; + userRef.get().then(snapshot => { + let data = snapshot.val() as UserData; + if (!data) { + // Upload initial user data + data = { + name: user.displayName, + photoURL: user.photoURL, + language: navigator.language, + } as UserData; + console.log('Creating user', data); + userRef.set(data); + } + setUserData(data); + }); + matchesRef.on('value', subscriber); + return () => { + matchesRef.off('value', subscriber); + } + }, [user]); - fetchChats(); - }, []); + // Match Listener + useEffect(() => { + if (!user || !selected) return; + const matchesRef = firebase.database().ref(`games/${selected.val().game}`) + const subscriber = (snapshot: firebase.database.DataSnapshot) => { + const matchesArray: firebase.database.DataSnapshot[] = []; + snapshot.forEach((childSnapshot) => { + matchesArray.push(childSnapshot); + }); + setMatches(matchesArray); + }; + matchesRef.on('value', subscriber); + return () => matchesRef.off('value', subscriber); + }, [user, selected]); const handleChatClick = useCallback((chat: firebase.database.DataSnapshot) => { - setSelectedChat(chat); + setSelected(chat); }, []); - const onSearch = useCallback((event: ChangeEvent) => { - setSearch(event.currentTarget.value); - }, []); + const onSearch = useCallback(async event => { + if (!user || !searchRef.current?.value) return; + const search = searchRef.current.value + const friend = await firebase.database().ref(`users/${search}`).get() + if (friend.exists()) { + // user found + const match = await firebase.database().ref(`matches/${user.uid}/${friend.key}`).get() + if (!match.exists()) { + setNewFriend(friend.val() as UserData); + } + // createMatch(user.uid, snapshot.key as string); + } + }, [user]) as ChangeEventHandler; - const user = useAuth(); // Local signed-in state. if (!show) return null; + return user ? (

Matches

firebase.auth().signOut()}>Sign-out: {user.uid}
- +
    - {chats.map((chat: firebase.database.DataSnapshot) => ( + {newFriend && ( +
  • openMatch(newFriend)}>New Match: {newFriend}
  • + )} + {matches.map((chat: firebase.database.DataSnapshot) => (
  • handleChatClick(chat)}> - {chat.val().name} + {chat.val().name}: {chat.val().messages.length} messages
  • ))}
- {selectedChat && ( + {selected && (
-

{selectedChat.val().name}

- {selectedChat.val().messages.map((message: string, index: number) => ( +

{selected.val().name}

+ {selected.val().messages.map((message: string, index: number) => (

{message}

))}
diff --git a/src/Game/Account.css b/src/Game/Account.css deleted file mode 100644 index 64ddaef..0000000 --- a/src/Game/Account.css +++ /dev/null @@ -1,7 +0,0 @@ -#account { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - height: 100%; -} \ No newline at end of file diff --git a/src/Game/Account.jsx b/src/Game/Account.jsx deleted file mode 100644 index b54eefe..0000000 --- a/src/Game/Account.jsx +++ /dev/null @@ -1,63 +0,0 @@ -import 'react' -import './Account.css' - -// https://firebase.google.com/docs/auth/web/anonymous-auth?hl=en&authuser=0 - -import { getAuth, signInAnonymously } from "firebase/auth"; - -const auth = getAuth(); - -const firebaseConfig = { - apiKey: "AIzaSyD1-5s4lK7YyY5tW1oTg4F5xq4Cf1X8iM4", - authDomain: "mygame-9f4b0.firebaseapp.com", - projectId: "mygame-9f4b0", - storageBucket: "mygame-9f4b0.appspot.com", - messagingSenderId: "554878081473", - appId: "1:554878081473:web:9f0d9a1e0d3f7d5f3a1b9e" -}; - -firebase.initializeApp(firebaseConfig); - -const auth = firebase.auth(); -// const db = firebase.firestore(); - -function signIn(email, password) { - auth.signInWithEmailAndPassword(email, password) - .then((userCredential) => { - // Signed in - var user = userCredential.user; - // ... - }) - .catch((error) => { - var errorCode = error.code; - var errorMessage = error.message; - }); -} - -function signUp(email, password) { - auth.createUserWithEmailAndPassword(email, password) - .then((userCredential) => { - // Signed in - var user = userCredential.user; - // ... - }) - .catch((error) => { - var errorCode = error.code; - var errorMessage = error.message; - // .. - }); -} - -// auth.signOut() - -// auth.sendPasswordResetEmail(email) - - -export default function Account() { - return
-

Account

- Sign In - Sign Up - -
-} \ No newline at end of file diff --git a/src/MultiplayerContext.tsx b/src/MultiplayerContext.tsx new file mode 100644 index 0000000..41bc872 --- /dev/null +++ b/src/MultiplayerContext.tsx @@ -0,0 +1,51 @@ +import { createContext, useContext, useEffect, useState, ReactNode } from 'react'; +import firebase from 'firebase/compat/app'; +import 'firebase/compat/database'; +import { useAuth } from './AuthContext'; + +// React context for the auth state +export const MultiplayerContext = createContext({}); + +// Provider component for the auth context +export function MultiplayerProvider({ children }: { children: ReactNode}) { + const [value, setValue] = useState({}); + const authUser = useAuth(); // Local signed-in state. + + async function load(user: string) { + if (!authUser) return; + const database = firebase.database(); + const userSnapshot = await database.ref(`users/${user}`).get(); + if (!userSnapshot.exists()) { + console.error('User not found', user); + return; + } + const matchSnapshot = await database.ref(`matches/${authUser.uid}/${user}`).get(); + let gameSnapshot: firebase.database.DataSnapshot; + if (!matchSnapshot.exists()) { + // Create new game record + const gameRef = firebase.database().ref('games').push(); + // Point match to game + const data = { + sort: Date.now(), + game: gameRef.key, + }; + firebase.database().ref(`matches/${authUser.uid}/${user}/game`).set(data); + firebase.database().ref(`matches/${user}/${authUser.uid}/game`).set(data); + gameSnapshot = await gameRef.get(); + } else { + gameSnapshot = await database.ref(`games/${matchSnapshot.val().game}`).get(); + } + setValue({ userSnapshot, matchSnapshot, gameSnapshot, load }) + } + + return ( + + {children} + + ); +} + +// Hook to use the context +export function useMultiplayer() { + return useContext(MultiplayerContext); +} \ No newline at end of file