Added authentication handling
This commit is contained in:
parent
5dc6a4ed32
commit
2ceec9cfec
36
src/App.js
36
src/App.js
@ -8,25 +8,29 @@ import Game from "./routes/Game";
|
|||||||
import About from "./routes/About";
|
import About from "./routes/About";
|
||||||
import FAQ from "./routes/FAQ";
|
import FAQ from "./routes/FAQ";
|
||||||
|
|
||||||
|
import { AuthProvider } from "./contexts/AuthContext";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<Router>
|
<AuthProvider>
|
||||||
<Switch>
|
<Router>
|
||||||
<Route path="/about">
|
<Switch>
|
||||||
<About />
|
<Route path="/about">
|
||||||
</Route>
|
<About />
|
||||||
<Route path="/faq">
|
</Route>
|
||||||
<FAQ />
|
<Route path="/faq">
|
||||||
</Route>
|
<FAQ />
|
||||||
<Route path="/game/:id">
|
</Route>
|
||||||
<Game />
|
<Route path="/game/:id">
|
||||||
</Route>
|
<Game />
|
||||||
<Route path="/">
|
</Route>
|
||||||
<Home />
|
<Route path="/">
|
||||||
</Route>
|
<Home />
|
||||||
</Switch>
|
</Route>
|
||||||
</Router>
|
</Switch>
|
||||||
|
</Router>
|
||||||
|
</AuthProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,13 @@ import React from "react";
|
|||||||
import Modal from "react-modal";
|
import Modal from "react-modal";
|
||||||
import { useThemeUI, Close } from "theme-ui";
|
import { useThemeUI, Close } from "theme-ui";
|
||||||
|
|
||||||
function StyledModal({ isOpen, onRequestClose, children, ...props }) {
|
function StyledModal({
|
||||||
|
isOpen,
|
||||||
|
onRequestClose,
|
||||||
|
children,
|
||||||
|
allowClose,
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
const { theme } = useThemeUI();
|
const { theme } = useThemeUI();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -25,13 +31,19 @@ function StyledModal({ isOpen, onRequestClose, children, ...props }) {
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<Close
|
{allowClose && (
|
||||||
m={0}
|
<Close
|
||||||
sx={{ position: "absolute", top: 0, right: 0 }}
|
m={0}
|
||||||
onClick={onRequestClose}
|
sx={{ position: "absolute", top: 0, right: 0 }}
|
||||||
/>
|
onClick={onRequestClose}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StyledModal.defaultProps = {
|
||||||
|
allowClose: true,
|
||||||
|
};
|
||||||
|
|
||||||
export default StyledModal;
|
export default StyledModal;
|
||||||
|
25
src/contexts/AuthContext.js
Normal file
25
src/contexts/AuthContext.js
Normal file
@ -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 <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AuthContext;
|
@ -1,9 +1,11 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState, useContext } from "react";
|
||||||
import io from "socket.io-client";
|
import io from "socket.io-client";
|
||||||
|
|
||||||
import { omit } from "../helpers/shared";
|
import { omit } from "../helpers/shared";
|
||||||
import Peer from "../helpers/Peer";
|
import Peer from "../helpers/Peer";
|
||||||
|
|
||||||
|
import AuthContext from "../contexts/AuthContext";
|
||||||
|
|
||||||
const socket = io("https://broker.owlbear.rodeo");
|
const socket = io("https://broker.owlbear.rodeo");
|
||||||
|
|
||||||
function useSession(
|
function useSession(
|
||||||
@ -15,9 +17,11 @@ function useSession(
|
|||||||
onPeerTrackRemoved,
|
onPeerTrackRemoved,
|
||||||
onPeerError
|
onPeerError
|
||||||
) {
|
) {
|
||||||
|
const { password, setAuthenticationStatus } = useContext(AuthContext);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
socket.emit("join party", partyId);
|
socket.emit("join party", partyId, password);
|
||||||
}, [partyId]);
|
}, [partyId, password]);
|
||||||
|
|
||||||
const [peers, setPeers] = useState({});
|
const [peers, setPeers] = useState({});
|
||||||
|
|
||||||
@ -148,6 +152,7 @@ function useSession(
|
|||||||
const sync = index === 0;
|
const sync = index === 0;
|
||||||
addPeer(id, true, sync);
|
addPeer(id, true, sync);
|
||||||
}
|
}
|
||||||
|
setAuthenticationStatus("authenticated");
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSignal(data) {
|
function handleSignal(data) {
|
||||||
@ -157,17 +162,23 @@ function useSession(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleAuthError() {
|
||||||
|
setAuthenticationStatus("unauthenticated");
|
||||||
|
}
|
||||||
|
|
||||||
socket.on("party member joined", handlePartyMemberJoined);
|
socket.on("party member joined", handlePartyMemberJoined);
|
||||||
socket.on("party member left", handlePartyMemberLeft);
|
socket.on("party member left", handlePartyMemberLeft);
|
||||||
socket.on("joined party", handleJoinedParty);
|
socket.on("joined party", handleJoinedParty);
|
||||||
socket.on("signal", handleSignal);
|
socket.on("signal", handleSignal);
|
||||||
|
socket.on("auth error", handleAuthError);
|
||||||
return () => {
|
return () => {
|
||||||
socket.removeListener("party member joined", handlePartyMemberJoined);
|
socket.removeListener("party member joined", handlePartyMemberJoined);
|
||||||
socket.removeListener("party member left", handlePartyMemberLeft);
|
socket.removeListener("party member left", handlePartyMemberLeft);
|
||||||
socket.removeListener("joined party", handleJoinedParty);
|
socket.removeListener("joined party", handleJoinedParty);
|
||||||
socket.removeListener("signal", handleSignal);
|
socket.removeListener("signal", handleSignal);
|
||||||
|
socket.removeListener("auth error", handleAuthError);
|
||||||
};
|
};
|
||||||
}, [peers]);
|
}, [peers, setAuthenticationStatus]);
|
||||||
|
|
||||||
return { peers, socket };
|
return { peers, socket };
|
||||||
}
|
}
|
||||||
|
52
src/modals/AuthModal.js
Normal file
52
src/modals/AuthModal.js
Normal file
@ -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 (
|
||||||
|
<Modal isOpen={isOpen} allowClose={false} onAfterOpen={focusInput}>
|
||||||
|
<Box as="form" onSubmit={handleSubmit}>
|
||||||
|
<Label py={2} htmlFor="password">
|
||||||
|
Enter password
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="password"
|
||||||
|
value={tmpPassword}
|
||||||
|
onChange={handleChange}
|
||||||
|
ref={inputRef}
|
||||||
|
autoComplete="off"
|
||||||
|
/>
|
||||||
|
<Flex py={2}>
|
||||||
|
<Button sx={{ flexGrow: 1 }} disabled={!tmpPassword}>
|
||||||
|
Join
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AuthModal;
|
@ -48,7 +48,11 @@ function JoinModal({ isOpen, onRequestClose }) {
|
|||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
/>
|
/>
|
||||||
<Button disabled={!gameId}>Join › (ノ◕ヮ◕)ノ*:・゚✧</Button>
|
<Flex>
|
||||||
|
<Button sx={{ flexGrow: 1 }} disabled={!gameId}>
|
||||||
|
Join › (ノ◕ヮ◕)ノ*:・゚✧
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
73
src/modals/StartModal.js
Normal file
73
src/modals/StartModal.js
Normal file
@ -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 (
|
||||||
|
<Modal isOpen={isOpen} onRequestClose={onRequestClose}>
|
||||||
|
<Flex
|
||||||
|
sx={{
|
||||||
|
flexDirection: "column",
|
||||||
|
justifyContent: "center",
|
||||||
|
maxWidth: "300px",
|
||||||
|
flexGrow: 1,
|
||||||
|
}}
|
||||||
|
m={2}
|
||||||
|
>
|
||||||
|
<Box as="form" onSubmit={handleSubmit}>
|
||||||
|
<Label htmlFor="password">Password</Label>
|
||||||
|
<Input
|
||||||
|
my={1}
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
value={usePassword ? password : ""}
|
||||||
|
onChange={handlePasswordChange}
|
||||||
|
disabled={!usePassword}
|
||||||
|
autoComplete="off"
|
||||||
|
/>
|
||||||
|
<Box>
|
||||||
|
<Label mb={3}>
|
||||||
|
<Checkbox
|
||||||
|
checked={usePassword}
|
||||||
|
onChange={handleUsePasswordChange}
|
||||||
|
/>
|
||||||
|
Use password
|
||||||
|
</Label>
|
||||||
|
</Box>
|
||||||
|
<Flex>
|
||||||
|
<Button sx={{ flexGrow: 1 }} disabled={!password && usePassword}>
|
||||||
|
Start
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StartModal;
|
@ -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 { Flex, Box, Text, Link } from "theme-ui";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
|
||||||
import { omit, isStreamStopped } from "../helpers/shared";
|
import { omit, isStreamStopped } from "../helpers/shared";
|
||||||
|
|
||||||
import useSession from "../helpers/useSession";
|
import useSession from "../helpers/useSession";
|
||||||
import { getRandomMonster } from "../helpers/monsters";
|
import { getRandomMonster } from "../helpers/monsters";
|
||||||
|
|
||||||
@ -12,8 +17,13 @@ import Tokens from "../components/Tokens";
|
|||||||
import Map from "../components/Map";
|
import Map from "../components/Map";
|
||||||
import Banner from "../components/Banner";
|
import Banner from "../components/Banner";
|
||||||
|
|
||||||
|
import AuthModal from "../modals/AuthModal";
|
||||||
|
|
||||||
|
import AuthContext from "../contexts/AuthContext";
|
||||||
|
|
||||||
function Game() {
|
function Game() {
|
||||||
const { id: gameId } = useParams();
|
const { id: gameId } = useParams();
|
||||||
|
const { authenticationStatus } = useContext(AuthContext);
|
||||||
|
|
||||||
const { peers, socket } = useSession(
|
const { peers, socket } = useSession(
|
||||||
gameId,
|
gameId,
|
||||||
@ -238,6 +248,7 @@ function Game() {
|
|||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Banner>
|
</Banner>
|
||||||
|
<AuthModal isOpen={authenticationStatus === "unauthenticated"} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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 { Flex, Button, Image, Text } from "theme-ui";
|
||||||
import shortid from "shortid";
|
|
||||||
import { useHistory } from "react-router-dom";
|
|
||||||
|
|
||||||
import Footer from "../components/Footer";
|
import Footer from "../components/Footer";
|
||||||
|
|
||||||
|
import StartModal from "../modals/StartModal";
|
||||||
import JoinModal from "../modals/JoinModal";
|
import JoinModal from "../modals/JoinModal";
|
||||||
|
|
||||||
|
import AuthContext from "../contexts/AuthContext";
|
||||||
|
|
||||||
import owlington from "../images/Owlington.png";
|
import owlington from "../images/Owlington.png";
|
||||||
|
|
||||||
function Home() {
|
function Home() {
|
||||||
let history = useHistory();
|
const [isStartModalOpen, setIsStartModalOpen] = useState(false);
|
||||||
function handleStartGame() {
|
|
||||||
history.push(`/game/${shortid.generate()}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const [isJoinModalOpen, setIsJoinModalOpen] = useState(false);
|
const [isJoinModalOpen, setIsJoinModalOpen] = useState(false);
|
||||||
|
|
||||||
|
// Reset password on visiting home
|
||||||
|
const { setPassword } = useContext(AuthContext);
|
||||||
|
useEffect(() => {
|
||||||
|
setPassword("");
|
||||||
|
}, [setPassword]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
sx={{
|
sx={{
|
||||||
@ -39,7 +42,7 @@ function Home() {
|
|||||||
Owlbear Rodeo
|
Owlbear Rodeo
|
||||||
</Text>
|
</Text>
|
||||||
<Image src={owlington} m={2} />
|
<Image src={owlington} m={2} />
|
||||||
<Button m={2} onClick={handleStartGame}>
|
<Button m={2} onClick={() => setIsStartModalOpen(true)}>
|
||||||
Start Game
|
Start Game
|
||||||
</Button>
|
</Button>
|
||||||
<Button m={2} onClick={() => setIsJoinModalOpen(true)}>
|
<Button m={2} onClick={() => setIsJoinModalOpen(true)}>
|
||||||
@ -52,6 +55,10 @@ function Home() {
|
|||||||
isOpen={isJoinModalOpen}
|
isOpen={isJoinModalOpen}
|
||||||
onRequestClose={() => setIsJoinModalOpen(false)}
|
onRequestClose={() => setIsJoinModalOpen(false)}
|
||||||
/>
|
/>
|
||||||
|
<StartModal
|
||||||
|
isOpen={isStartModalOpen}
|
||||||
|
onRequestClose={() => setIsStartModalOpen(false)}
|
||||||
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Footer />
|
<Footer />
|
||||||
</Flex>
|
</Flex>
|
||||||
|
Loading…
Reference in New Issue
Block a user