diff --git a/src/App.js b/src/App.js
index f5afa39..9e93684 100644
--- a/src/App.js
+++ b/src/App.js
@@ -8,25 +8,29 @@ import Game from "./routes/Game";
import About from "./routes/About";
import FAQ from "./routes/FAQ";
+import { AuthProvider } from "./contexts/AuthContext";
+
function App() {
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
}
diff --git a/src/components/Modal.js b/src/components/Modal.js
index 1b9bd00..94b9eda 100644
--- a/src/components/Modal.js
+++ b/src/components/Modal.js
@@ -2,7 +2,13 @@ import React from "react";
import Modal from "react-modal";
import { useThemeUI, Close } from "theme-ui";
-function StyledModal({ isOpen, onRequestClose, children, ...props }) {
+function StyledModal({
+ isOpen,
+ onRequestClose,
+ children,
+ allowClose,
+ ...props
+}) {
const { theme } = useThemeUI();
return (
@@ -25,13 +31,19 @@ function StyledModal({ isOpen, onRequestClose, children, ...props }) {
{...props}
>
{children}
-
+ {allowClose && (
+
+ )}
);
}
+StyledModal.defaultProps = {
+ allowClose: true,
+};
+
export default StyledModal;
diff --git a/src/contexts/AuthContext.js b/src/contexts/AuthContext.js
new file mode 100644
index 0000000..ace0b95
--- /dev/null
+++ b/src/contexts/AuthContext.js
@@ -0,0 +1,25 @@
+import React, { useState, useEffect } from "react";
+
+const AuthContext = React.createContext();
+
+export function AuthProvider({ children }) {
+ const [password, setPassword] = useState(
+ sessionStorage.getItem("auth") || ""
+ );
+
+ useEffect(() => {
+ sessionStorage.setItem("auth", password);
+ }, [password]);
+
+ const [authenticationStatus, setAuthenticationStatus] = useState("unknown");
+
+ const value = {
+ password,
+ setPassword,
+ authenticationStatus,
+ setAuthenticationStatus,
+ };
+ return {children};
+}
+
+export default AuthContext;
diff --git a/src/helpers/useSession.js b/src/helpers/useSession.js
index caf67cf..0def7a4 100644
--- a/src/helpers/useSession.js
+++ b/src/helpers/useSession.js
@@ -1,9 +1,11 @@
-import { useEffect, useState } from "react";
+import { useEffect, useState, useContext } from "react";
import io from "socket.io-client";
import { omit } from "../helpers/shared";
import Peer from "../helpers/Peer";
+import AuthContext from "../contexts/AuthContext";
+
const socket = io("https://broker.owlbear.rodeo");
function useSession(
@@ -15,9 +17,11 @@ function useSession(
onPeerTrackRemoved,
onPeerError
) {
+ const { password, setAuthenticationStatus } = useContext(AuthContext);
+
useEffect(() => {
- socket.emit("join party", partyId);
- }, [partyId]);
+ socket.emit("join party", partyId, password);
+ }, [partyId, password]);
const [peers, setPeers] = useState({});
@@ -148,6 +152,7 @@ function useSession(
const sync = index === 0;
addPeer(id, true, sync);
}
+ setAuthenticationStatus("authenticated");
}
function handleSignal(data) {
@@ -157,17 +162,23 @@ function useSession(
}
}
+ function handleAuthError() {
+ setAuthenticationStatus("unauthenticated");
+ }
+
socket.on("party member joined", handlePartyMemberJoined);
socket.on("party member left", handlePartyMemberLeft);
socket.on("joined party", handleJoinedParty);
socket.on("signal", handleSignal);
+ socket.on("auth error", handleAuthError);
return () => {
socket.removeListener("party member joined", handlePartyMemberJoined);
socket.removeListener("party member left", handlePartyMemberLeft);
socket.removeListener("joined party", handleJoinedParty);
socket.removeListener("signal", handleSignal);
+ socket.removeListener("auth error", handleAuthError);
};
- }, [peers]);
+ }, [peers, setAuthenticationStatus]);
return { peers, socket };
}
diff --git a/src/modals/AuthModal.js b/src/modals/AuthModal.js
new file mode 100644
index 0000000..e97b0a8
--- /dev/null
+++ b/src/modals/AuthModal.js
@@ -0,0 +1,52 @@
+import React, { useState, useContext, useRef } from "react";
+import { Box, Input, Button, Label, Flex } from "theme-ui";
+
+import AuthContext from "../contexts/AuthContext";
+
+import Modal from "../components/Modal";
+
+function AuthModal({ isOpen }) {
+ const { password, setPassword, setAuthenticationStatus } = useContext(
+ AuthContext
+ );
+ const [tmpPassword, setTempPassword] = useState(password);
+
+ function handleChange(event) {
+ setTempPassword(event.target.value);
+ }
+
+ function handleSubmit(event) {
+ event.preventDefault();
+ setAuthenticationStatus("unknown");
+ setPassword(tmpPassword);
+ }
+
+ const inputRef = useRef();
+ function focusInput() {
+ inputRef.current && inputRef.current.focus();
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default AuthModal;
diff --git a/src/modals/JoinModal.js b/src/modals/JoinModal.js
index c28d655..32d677b 100644
--- a/src/modals/JoinModal.js
+++ b/src/modals/JoinModal.js
@@ -48,7 +48,11 @@ function JoinModal({ isOpen, onRequestClose }) {
onChange={handleChange}
ref={inputRef}
/>
-
+
+
+
diff --git a/src/modals/StartModal.js b/src/modals/StartModal.js
new file mode 100644
index 0000000..91d274c
--- /dev/null
+++ b/src/modals/StartModal.js
@@ -0,0 +1,73 @@
+import React, { useState, useContext } from "react";
+import { Box, Label, Input, Button, Flex, Checkbox } from "theme-ui";
+import { useHistory } from "react-router-dom";
+import shortid from "shortid";
+
+import AuthContext from "../contexts/AuthContext";
+
+import Modal from "../components/Modal";
+
+function StartModal({ isOpen, onRequestClose }) {
+ let history = useHistory();
+ const { password, setPassword } = useContext(AuthContext);
+
+ function handlePasswordChange(event) {
+ setPassword(event.target.value);
+ }
+
+ const [usePassword, setUsePassword] = useState(true);
+ function handleUsePasswordChange(event) {
+ setUsePassword(event.target.checked);
+ }
+
+ function handleSubmit(event) {
+ event.preventDefault();
+ if (!usePassword) {
+ setPassword("");
+ }
+ history.push(`/game/${shortid.generate()}`);
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default StartModal;
diff --git a/src/routes/Game.js b/src/routes/Game.js
index 5f125f7..7ac4d95 100644
--- a/src/routes/Game.js
+++ b/src/routes/Game.js
@@ -1,9 +1,14 @@
-import React, { useState, useRef, useEffect, useCallback } from "react";
+import React, {
+ useState,
+ useRef,
+ useEffect,
+ useCallback,
+ useContext,
+} from "react";
import { Flex, Box, Text, Link } from "theme-ui";
import { useParams } from "react-router-dom";
import { omit, isStreamStopped } from "../helpers/shared";
-
import useSession from "../helpers/useSession";
import { getRandomMonster } from "../helpers/monsters";
@@ -12,8 +17,13 @@ import Tokens from "../components/Tokens";
import Map from "../components/Map";
import Banner from "../components/Banner";
+import AuthModal from "../modals/AuthModal";
+
+import AuthContext from "../contexts/AuthContext";
+
function Game() {
const { id: gameId } = useParams();
+ const { authenticationStatus } = useContext(AuthContext);
const { peers, socket } = useSession(
gameId,
@@ -238,6 +248,7 @@ function Game() {
+
>
);
}
diff --git a/src/routes/Home.js b/src/routes/Home.js
index 7cd6158..2e9d967 100644
--- a/src/routes/Home.js
+++ b/src/routes/Home.js
@@ -1,22 +1,25 @@
-import React, { useState } from "react";
+import React, { useState, useEffect, useContext } from "react";
import { Flex, Button, Image, Text } from "theme-ui";
-import shortid from "shortid";
-import { useHistory } from "react-router-dom";
import Footer from "../components/Footer";
+import StartModal from "../modals/StartModal";
import JoinModal from "../modals/JoinModal";
+import AuthContext from "../contexts/AuthContext";
+
import owlington from "../images/Owlington.png";
function Home() {
- let history = useHistory();
- function handleStartGame() {
- history.push(`/game/${shortid.generate()}`);
- }
-
+ const [isStartModalOpen, setIsStartModalOpen] = useState(false);
const [isJoinModalOpen, setIsJoinModalOpen] = useState(false);
+ // Reset password on visiting home
+ const { setPassword } = useContext(AuthContext);
+ useEffect(() => {
+ setPassword("");
+ }, [setPassword]);
+
return (
-