Compare commits
No commits in common. "04f0b86527445f7032c544db405e53a368ac3340" and "f5b09e2123de56d153186324894ef93a8fb8626e" have entirely different histories.
04f0b86527
...
f5b09e2123
@ -9,9 +9,7 @@ import About from "./components/About"
|
|||||||
import Games from './container/Games';
|
import Games from './container/Games';
|
||||||
import Leaderboard from './container/Leaderboard';
|
import Leaderboard from './container/Leaderboard';
|
||||||
|
|
||||||
import useConfigReducer from './reducer/config';
|
import usePollingReducer from './reducer/polling';
|
||||||
import useUserReducer from './reducer/user';
|
|
||||||
import useLeaderboardReducer from './reducer/leaderboard';
|
|
||||||
import useGamesReducer from './reducer/games';
|
import useGamesReducer from './reducer/games';
|
||||||
|
|
||||||
import useUserApi from './api/user';
|
import useUserApi from './api/user';
|
||||||
@ -19,40 +17,35 @@ import useLeaderboardApi from './api/leaderboard';
|
|||||||
import useGamesApi from './api/games';
|
import useGamesApi from './api/games';
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const [config, dispatcConfig] = useConfigReducer();
|
const pollingReducer = usePollingReducer();
|
||||||
|
|
||||||
const user = useUserApi(useUserReducer()).getUser();
|
const leaderboard = useLeaderboardApi().poll(pollingReducer);
|
||||||
const leaderboard = useLeaderboardApi(useLeaderboardReducer(), config).pollTable();
|
const user = useUserApi().get();
|
||||||
|
|
||||||
|
const [games, dispatchGames] = useGamesReducer();
|
||||||
|
const gamesApi = useGamesApi(dispatchGames);
|
||||||
|
gamesApi.list(pollingReducer);
|
||||||
|
|
||||||
const players = {
|
const players = {
|
||||||
leaderboard,
|
leaderboard,
|
||||||
isCurrentUser: (playerName) => user?.isCurrentUser(playerName) === true ? true : null
|
isCurrentUser: (playerName) => user?.isCurrentUser(playerName) === true ? true : null
|
||||||
};
|
};
|
||||||
|
|
||||||
const gamesReducer = useGamesReducer();
|
|
||||||
const gamesApi = useGamesApi(gamesReducer, config);
|
|
||||||
const games = gamesApi.pollGamesList();
|
|
||||||
|
|
||||||
const isPolling = {
|
|
||||||
games: games.isPollingGamesList,
|
|
||||||
leaderboard: leaderboard.isPollingTable
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Header configReducer={[config, dispatcConfig]} isPolling={isPolling}/>
|
<Header pollingReducer={pollingReducer} />
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path='/' element={<About />} />
|
<Route path='/' element={<About />} />
|
||||||
<Route path='/about' element={<About />} />
|
<Route path='/about' element={<About />} />
|
||||||
<Route path='/games/*' element={<Games context={{ gamesReducer, gamesApi }} players={players} />} />
|
<Route path='/games/*' element={<Games context={{ games, dispatchGames, gamesApi }} players={players} />} />
|
||||||
<Route path='/leaderboard' element={<Leaderboard players={players} />} />
|
<Route path='/leaderboard' element={<Leaderboard players={players} />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Header({ configReducer, isPolling }) {
|
function Header({ pollingReducer }) {
|
||||||
const [config, dispatcConfig] = configReducer;
|
const [polling, dispatchPolling] = pollingReducer;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='Header'>
|
<div className='Header'>
|
||||||
@ -61,8 +54,8 @@ function Header({ configReducer, isPolling }) {
|
|||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<OnlineToggle
|
<OnlineToggle
|
||||||
isOnline={config.online}
|
isOnline={polling.enabled}
|
||||||
onClick={() => dispatcConfig({ type: 'toggleOnline' })}
|
onClick={() => dispatchPolling({ type: 'toggleOnOff' })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
@ -71,11 +64,11 @@ function Header({ configReducer, isPolling }) {
|
|||||||
</NavLink>
|
</NavLink>
|
||||||
|
|
||||||
<NavLink to='/games'>
|
<NavLink to='/games'>
|
||||||
<Wobler text="Games" dance={isPolling.games} />
|
<Wobler text="Games" dance={polling.games} />
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|
||||||
<NavLink to='/leaderboard'>
|
<NavLink to='/leaderboard'>
|
||||||
<Wobler text='Leaderboard' dance={isPolling.leaderboard} />
|
<Wobler text='Leaderboard' dance={polling.leaderboard} />
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,28 +1,26 @@
|
|||||||
import { usePolling, doPushing } from '../hook/api';
|
import usePolling from '../hook/Polling';
|
||||||
|
|
||||||
export default function useGamesApi(gamesReducer, config) {
|
export default function useGamesApi(dispatchGames) {
|
||||||
const [games, dispatchGames] = gamesReducer;
|
|
||||||
|
|
||||||
const usePollingGamesList = () => {
|
const useList = (pollingReducer) => {
|
||||||
const onSuccess = (gamesList) => {
|
const [polling, dispatchPolling] = pollingReducer;
|
||||||
dispatchGames({ type: 'next', gamesList });
|
|
||||||
|
const onResponce = (json) => {
|
||||||
|
dispatchGames({ type: 'next', list: json });
|
||||||
}
|
}
|
||||||
|
|
||||||
const isPollingGamesList = usePolling('/api/games', onSuccess, config.intervalMode(30));
|
const mode = (polling.enabled === true)
|
||||||
if (games.isPollingGamesList !== isPollingGamesList) {
|
? { interval_sec: 30 } // fetch gamesList every half a minue
|
||||||
dispatchGames({ type: 'next', isPollingGamesList });
|
: { interval_stop: true } // user has fliped OfflineToggel
|
||||||
}
|
|
||||||
|
|
||||||
return games;
|
const isPolling = usePolling('/api/games', onResponce, mode);
|
||||||
}
|
|
||||||
|
|
||||||
|
if (isPolling !== polling.games) {
|
||||||
|
dispatchPolling({ type: 'next', games: isPolling });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pollGamesList: usePollingGamesList,
|
list: useList
|
||||||
|
|
||||||
pushNewGame: (reqParams) => doPushing('/api/gameproposal', 'POST', reqParams, {
|
|
||||||
onPushing: (isPushingNewGame) => dispatchGames({ type: 'next', isPushingNewGame }),
|
|
||||||
onSuccess: (game) => dispatchGames({ type: 'next', gamesList: [...games.gamesList, game] })
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,22 +1,26 @@
|
|||||||
import { usePolling } from "../hook/api";
|
import { useState } from "react";
|
||||||
|
import usePolling from "../hook/Polling";
|
||||||
|
|
||||||
export default function useLeaderboardApi(leaderboardReducer, config) {
|
export default function useLeaderboardApi() {
|
||||||
const [leaderboard, dispatchLeaderboard] = leaderboardReducer;
|
const [leaderboard, setLeaderboard] = useState(null);
|
||||||
|
|
||||||
const usePollingTable = () => {
|
const usePoll = (pollingReducer) => {
|
||||||
const onSuccess = (table) => {
|
const [polling, dispatchPolling] = pollingReducer;
|
||||||
dispatchLeaderboard({ type: 'next', table });
|
|
||||||
}
|
|
||||||
|
|
||||||
const isPollingTable = usePolling('/api/leaderboard', onSuccess, config.intervalMode(300));
|
const mode = (polling.enabled === true)
|
||||||
if (leaderboard.isPollingTable !== isPollingTable) {
|
? { interval_sec: 300 } // update leaderbord stats every 5 min
|
||||||
dispatchLeaderboard({ type: 'next', isPollingTable });
|
: { interval_stop: true } // user has fliped OfflineToggel
|
||||||
|
|
||||||
|
const isPolling = usePolling('/api/leaderboard', setLeaderboard, mode);
|
||||||
|
|
||||||
|
if (isPolling !== polling.leaderboard) {
|
||||||
|
dispatchPolling({ type: 'next', leaderboard: isPolling });
|
||||||
}
|
}
|
||||||
|
|
||||||
return leaderboard;
|
return leaderboard;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pollTable: usePollingTable
|
poll: usePoll
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,19 +1,20 @@
|
|||||||
import { usePolling } from "../hook/api";
|
import usePolling from "../hook/Polling";
|
||||||
|
import useUserReducer from "../reducer/user";
|
||||||
|
|
||||||
export default function useUserApi(userReducer) {
|
export default function useUserApi() {
|
||||||
const [user, dispatchUser] = userReducer;
|
const [user, dispatchUser] = useUserReducer();
|
||||||
|
|
||||||
const useGetUser = () => {
|
const useGet = () => {
|
||||||
const onSuccess = (userJson) => {
|
const onResponce = (json) => {
|
||||||
dispatchUser({ type: "parse", userJson });
|
dispatchUser({ type: "parse", json });
|
||||||
}
|
}
|
||||||
|
|
||||||
usePolling('/api/user', onSuccess); // <<-- fetch once
|
usePolling('/api/user', onResponce); // <<-- fetch once
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getUser: useGetUser
|
get: useGet
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -97,19 +97,16 @@
|
|||||||
margin: 2px;
|
margin: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ActionPanel .Create.ready /* , */ /* OR .game-action.busy */
|
.ActionPanel .Create:hover, /* OR */
|
||||||
|
.game-action.busy
|
||||||
{
|
{
|
||||||
background-color:#00b0ff60;
|
background-color:#00b0ff60;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ActionPanel .Create.ready:hover {
|
.ActionPanel .Create.enabled:active {
|
||||||
background-color:#00b0ffa0;
|
background-color:#00b0ffa0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ActionPanel .Create.ready:active {
|
|
||||||
background-color:#00b0fff0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ActionPanel .Cancel:hover,
|
.ActionPanel .Cancel:hover,
|
||||||
.ActionPanel .Reject:hover {
|
.ActionPanel .Reject:hover {
|
||||||
background-color:#ff000030
|
background-color:#ff000030
|
||||||
|
@ -19,8 +19,7 @@ import GameBoard from './games/GameBoard';
|
|||||||
|
|
||||||
import './Games.css';
|
import './Games.css';
|
||||||
|
|
||||||
export default function Games({ context: { gamesReducer, gamesApi }, players }) {
|
export default function Games({ context: { games, dispatchGames, gamesApi }, players }) {
|
||||||
const [games, dispatchGames] = gamesReducer;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GamesContext.Provider value={games} >
|
<GamesContext.Provider value={games} >
|
||||||
@ -30,7 +29,7 @@ export default function Games({ context: { gamesReducer, gamesApi }, players })
|
|||||||
<ViewProvider players={players} dispatchGames={dispatchGames} />
|
<ViewProvider players={players} dispatchGames={dispatchGames} />
|
||||||
</div>
|
</div>
|
||||||
<div className='right-side'>
|
<div className='right-side'>
|
||||||
<ActionPanel players={players} gamesApi={gamesApi} />
|
<ActionPanel />
|
||||||
<GameBoard />
|
<GameBoard />
|
||||||
{/*
|
{/*
|
||||||
<GameMessage />
|
<GameMessage />
|
||||||
@ -81,15 +80,11 @@ function ViewProvider({ players, dispatchGames }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ActionPanel({ players, gamesApi }) {
|
function ActionPanel() {
|
||||||
return (
|
return (
|
||||||
<div className='ActionPanel'>
|
<div className='ActionPanel'>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path='new' element={
|
<Route path='new' element={<Create />} />
|
||||||
<Create isCurrentUser={players.isCurrentUser}
|
|
||||||
pushNewGame={(reqParams) => gamesApi.pushNewGame(reqParams)}
|
|
||||||
/>
|
|
||||||
} />
|
|
||||||
<Route path='proposal' element={[<Accept key={1} />, <Reject key={2} />, <Cancel key={3} />]} />
|
<Route path='proposal' element={[<Accept key={1} />, <Reject key={2} />, <Cancel key={3} />]} />
|
||||||
<Route path='active' element={[<DrawReq key={1} />, <DrawAcq key={2} />, <Surrender key={3} />]} />
|
<Route path='active' element={[<DrawReq key={1} />, <DrawAcq key={2} />, <Surrender key={3} />]} />
|
||||||
<Route path='archive' element={[<Backward key={1} />, <Forward key={2} />]} />
|
<Route path='archive' element={[<Backward key={1} />, <Forward key={2} />]} />
|
||||||
|
@ -4,12 +4,13 @@ import Loading from '../components/Loading';
|
|||||||
|
|
||||||
export default function Leaderboard({ players }) {
|
export default function Leaderboard({ players }) {
|
||||||
|
|
||||||
const table = players.leaderboard.table;
|
const leaderboard = players.leaderboard;
|
||||||
if (table == null)
|
|
||||||
|
if (leaderboard == null)
|
||||||
return <Loading />
|
return <Loading />
|
||||||
|
|
||||||
const tableRows = Object.keys(table).map(playerName => {
|
const tableRows = Object.keys(leaderboard).map(playerName => {
|
||||||
var rank = table[playerName];
|
var rank = leaderboard[playerName];
|
||||||
|
|
||||||
return <tr key={playerName} className={players.isCurrentUser(playerName) && 'currentuser'}>
|
return <tr key={playerName} className={players.isCurrentUser(playerName) && 'currentuser'}>
|
||||||
<td>{playerName}</td>
|
<td>{playerName}</td>
|
||||||
|
@ -1,66 +1,6 @@
|
|||||||
import React, { useContext } from 'react';
|
import React from 'react';
|
||||||
import { GamesContext } from '../../../context/games';
|
|
||||||
import Wobler from '../../../components/Wobler';
|
|
||||||
import { Color } from '../../../components/Checkers';
|
|
||||||
|
|
||||||
|
export default function Create() {
|
||||||
|
|
||||||
export default function Create({ isCurrentUser, pushNewGame }) {
|
return <button className='Create'>Create</button>
|
||||||
const games = useContext(GamesContext);
|
|
||||||
|
|
||||||
const hasPlayers = checkPlayers(games.newGame);
|
|
||||||
const hasCurrentUser = checkCurrentUser(games.newGame, isCurrentUser);
|
|
||||||
|
|
||||||
const prepareNewGameRequest = () => {
|
|
||||||
if (!hasPlayers)
|
|
||||||
return alert("Black and White players must be selected for the game");
|
|
||||||
|
|
||||||
if (!hasCurrentUser)
|
|
||||||
return alert("You must be one of the selected players");
|
|
||||||
|
|
||||||
if (games.isPushingNewGame)
|
|
||||||
return; // current request is still being processed
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Prepare & send NewGame request
|
|
||||||
*/
|
|
||||||
const [opponentName, opponentColor] = getOpponent(games.newGame, isCurrentUser);
|
|
||||||
|
|
||||||
const reqParams = {
|
|
||||||
opponentName,
|
|
||||||
opponentColor,
|
|
||||||
board: null, // default board configuration
|
|
||||||
message: 'default NewGame req message'
|
|
||||||
}
|
|
||||||
|
|
||||||
pushNewGame(reqParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button className={'Create' + (hasPlayers && hasCurrentUser ? ' ready' : '')}
|
|
||||||
onClick={prepareNewGameRequest}
|
|
||||||
>
|
|
||||||
<Wobler text="Create" dance={games.isPushingNewGame} />
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkPlayers({ whitePlayer, blackPlayer }) {
|
|
||||||
return whitePlayer && blackPlayer
|
|
||||||
&& whitePlayer !== blackPlayer;
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkCurrentUser({ whitePlayer, blackPlayer }, isCurrentUser) {
|
|
||||||
return isCurrentUser(whitePlayer) || isCurrentUser(blackPlayer);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getOpponent({whitePlayer, blackPlayer}, isCurrentUser) {
|
|
||||||
if (isCurrentUser(whitePlayer)) {
|
|
||||||
return [blackPlayer, Color.black];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isCurrentUser(blackPlayer)) {
|
|
||||||
return [whitePlayer, Color.white];
|
|
||||||
}
|
|
||||||
|
|
||||||
return ['', ''];
|
|
||||||
}
|
}
|
@ -7,14 +7,14 @@ import Loading from '../../../components/Loading';
|
|||||||
|
|
||||||
export default function GameSelector({ yours, opponents, onClick }) {
|
export default function GameSelector({ yours, opponents, onClick }) {
|
||||||
|
|
||||||
const gamesList = useContext(GamesContext).gamesList;
|
const games = useContext(GamesContext);
|
||||||
if (gamesList === null)
|
if (games.list === null)
|
||||||
return <Loading />
|
return <Loading />
|
||||||
|
|
||||||
const yoursList = gamesList.filter(game => game.status === yours)
|
const yoursList = games.list.filter(game => game.status === yours)
|
||||||
.map(game => <Selectable game={game} key={game.uuid} onClick={onClick} />)
|
.map(game => <Selectable game={game} key={game.uuid} onClick={onClick} />)
|
||||||
|
|
||||||
const opponentsList = gamesList.filter(game => game.status === opponents)
|
const opponentsList = games.list.filter(game => game.status === opponents)
|
||||||
.map(game => <Selectable game={game} key={game.uuid} onClick={onClick} />)
|
.map(game => <Selectable game={game} key={game.uuid} onClick={onClick} />)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -11,9 +11,9 @@ export default function NewGame({ players, onSelectPlayer }) {
|
|||||||
/*
|
/*
|
||||||
* Name options
|
* Name options
|
||||||
*/
|
*/
|
||||||
const nameOptions = !players.leaderboard.table
|
const nameOptions = !players.leaderboard
|
||||||
? [<option key='loading' value='…'>…loading</option>]
|
? [<option key='loading' value='…'>…loading</option>]
|
||||||
: Object.keys(players.leaderboard.table).map(playerName =>
|
: Object.keys(players.leaderboard).map(playerName =>
|
||||||
<option key={playerName} value={playerName}>
|
<option key={playerName} value={playerName}>
|
||||||
{players.isCurrentUser(playerName) ? 'You' : playerName}
|
{players.isCurrentUser(playerName) ? 'You' : playerName}
|
||||||
</option>)
|
</option>)
|
||||||
|
@ -8,7 +8,7 @@ import { useState, useRef, useCallback, useEffect, } from "react"
|
|||||||
- interval_stop
|
- interval_stop
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function usePolling(uri, onSuccess, mode = null) {
|
export default function usePolling(uri, onResponce, mode = null) {
|
||||||
const [isPolling, setPolling] = useState(false);
|
const [isPolling, setPolling] = useState(false);
|
||||||
|
|
||||||
const initialPollRef = useRef(true);
|
const initialPollRef = useRef(true);
|
||||||
@ -17,12 +17,13 @@ export function usePolling(uri, onSuccess, mode = null) {
|
|||||||
const intervalTimerIdRef = useRef(null);
|
const intervalTimerIdRef = useRef(null);
|
||||||
const intervalTimerId = intervalTimerIdRef.current;
|
const intervalTimerId = intervalTimerIdRef.current;
|
||||||
|
|
||||||
|
|
||||||
const pollData = useCallback(() => {
|
const pollData = useCallback(() => {
|
||||||
setPolling(true);
|
setPolling(true);
|
||||||
initialPollRef.current = false;
|
initialPollRef.current = false;
|
||||||
|
|
||||||
fetch(uri)
|
fetch(uri)
|
||||||
.then((response) => {
|
.then((responce) => {
|
||||||
setPolling(false);
|
setPolling(false);
|
||||||
|
|
||||||
if (typeof mode?.interval_sec === 'number') {
|
if (typeof mode?.interval_sec === 'number') {
|
||||||
@ -30,15 +31,15 @@ export function usePolling(uri, onSuccess, mode = null) {
|
|||||||
intervalTimerIdRef.current = setTimeout(pollData, mode.interval_sec * 1000);
|
intervalTimerIdRef.current = setTimeout(pollData, mode.interval_sec * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.json();
|
return responce.json();
|
||||||
})
|
})
|
||||||
.then((json) => {
|
.then((json) => {
|
||||||
onSuccess(json);
|
onResponce(json);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.warn(err.message);
|
console.warn(err.message);
|
||||||
})
|
})
|
||||||
}, [uri, mode, onSuccess, initialPollRef, intervalTimerIdRef]);
|
}, [uri, mode, onResponce, initialPollRef, intervalTimerIdRef]);
|
||||||
|
|
||||||
const stopPollInterval = useCallback(() => {
|
const stopPollInterval = useCallback(() => {
|
||||||
console.log("Cancel scheduled fetch for", uri);
|
console.log("Cancel scheduled fetch for", uri);
|
||||||
@ -58,28 +59,3 @@ export function usePolling(uri, onSuccess, mode = null) {
|
|||||||
|
|
||||||
return isPolling;
|
return isPolling;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function doPushing(uri, method, data, { onSuccess, onPushing }) {
|
|
||||||
if (onPushing)
|
|
||||||
onPushing(true);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(uri, {
|
|
||||||
method,
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify(data), // body data type must match "Content-Type" header
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok)
|
|
||||||
throw new Error(`Error! status: ${response.status}`);
|
|
||||||
|
|
||||||
if (onSuccess)
|
|
||||||
onSuccess(await response.json());
|
|
||||||
// } catch (err) {
|
|
||||||
} finally {
|
|
||||||
if (onPushing)
|
|
||||||
onPushing(false);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
import { useReducer } from 'react';
|
|
||||||
import { namedLocalStorage } from '../util/PersistentStorage';
|
|
||||||
import { nextState } from '../util/StateHelper';
|
|
||||||
|
|
||||||
const Persistent = (() => {
|
|
||||||
const [getOnline, setOnline] = namedLocalStorage('config.online', true);
|
|
||||||
|
|
||||||
return {
|
|
||||||
getOnline,
|
|
||||||
setOnline
|
|
||||||
}
|
|
||||||
})(); // <<--- Execute
|
|
||||||
|
|
||||||
const initialState = {
|
|
||||||
online: Persistent.getOnline() === 'true',
|
|
||||||
|
|
||||||
intervalMode
|
|
||||||
};
|
|
||||||
|
|
||||||
function dispatch(state, action) {
|
|
||||||
switch (action.type) {
|
|
||||||
|
|
||||||
case 'toggleOnline': return {
|
|
||||||
...state,
|
|
||||||
online: Persistent.setOnline(!state.online)
|
|
||||||
};
|
|
||||||
|
|
||||||
case 'next':
|
|
||||||
return nextState(state, action);
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw Error('ConfigReducer: unknown action.type', action.type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function useConfigReducer() {
|
|
||||||
return useReducer(dispatch, initialState);
|
|
||||||
}
|
|
||||||
|
|
||||||
function intervalMode(interval_sec) {
|
|
||||||
return (this.online === true)
|
|
||||||
? { interval_sec } // fetch from API every interval_sec
|
|
||||||
: { interval_stop: true } // user has fliped OfflineToggel
|
|
||||||
}
|
|
@ -1,20 +1,16 @@
|
|||||||
import { useReducer } from 'react';
|
import { useReducer } from 'react';
|
||||||
import { nextState } from '../util/StateHelper';
|
import { nextState } from '../util/StateHelper';
|
||||||
|
|
||||||
const initialState = {
|
export const gamesInitialState = {
|
||||||
gamesList: null,
|
list: null,
|
||||||
|
|
||||||
newGame: {
|
newGame: {
|
||||||
whitePlayer: '',
|
whitePlayer: '',
|
||||||
blackPlayer: ''
|
blackPlayer: ''
|
||||||
},
|
}
|
||||||
|
|
||||||
// Network
|
|
||||||
isPollingGamesList: false,
|
|
||||||
isPushingNewGame: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function reducer(state, action) {
|
export function gamesReducer(state, action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
|
||||||
case 'next':
|
case 'next':
|
||||||
@ -26,5 +22,5 @@ function reducer(state, action) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function useGamesReducer() {
|
export default function useGamesReducer() {
|
||||||
return useReducer(reducer, initialState);
|
return useReducer(gamesReducer, gamesInitialState);
|
||||||
}
|
}
|
@ -1,24 +0,0 @@
|
|||||||
import { useReducer } from 'react';
|
|
||||||
import { nextState } from '../util/StateHelper';
|
|
||||||
|
|
||||||
const initialState = {
|
|
||||||
table: null,
|
|
||||||
|
|
||||||
// Network
|
|
||||||
isPollingTable: false
|
|
||||||
};
|
|
||||||
|
|
||||||
function reducer(state, action) {
|
|
||||||
switch (action.type) {
|
|
||||||
|
|
||||||
case 'next':
|
|
||||||
return nextState(state, action);
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw Error('LeaderboardReducer: unknown action.type', action.type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function useLeaderboardReducer() {
|
|
||||||
return useReducer(reducer, initialState);
|
|
||||||
}
|
|
39
webapp/src/reducer/polling.js
Normal file
39
webapp/src/reducer/polling.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { useReducer } from 'react';
|
||||||
|
import { namedLocalStorage } from '../util/PersistentStorage';
|
||||||
|
import { nextState } from '../util/StateHelper';
|
||||||
|
|
||||||
|
const Persistent = (() => {
|
||||||
|
const [getEnabled, setEnabled] = namedLocalStorage('polling.enabled', true);
|
||||||
|
|
||||||
|
return {
|
||||||
|
getEnabled,
|
||||||
|
setEnabled
|
||||||
|
}
|
||||||
|
})(); // <<--- Execute
|
||||||
|
|
||||||
|
export const pollingInitialState = {
|
||||||
|
enabled: Persistent.getEnabled() === 'true',
|
||||||
|
|
||||||
|
games: false,
|
||||||
|
leaderboard: false
|
||||||
|
};
|
||||||
|
|
||||||
|
export function pollingReducer(curntState, action) {
|
||||||
|
switch (action.type) {
|
||||||
|
|
||||||
|
case 'toggleOnOff': return {
|
||||||
|
...curntState,
|
||||||
|
enabled: Persistent.setEnabled(!curntState.enabled)
|
||||||
|
};
|
||||||
|
|
||||||
|
case 'next':
|
||||||
|
return nextState(curntState, action);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw Error('Unknown action.type:' + action.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function usePollingReducer() {
|
||||||
|
return useReducer(pollingReducer, pollingInitialState);
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
import { useReducer } from 'react';
|
import { useReducer } from 'react';
|
||||||
import { localeCompare } from '../util/Locale';
|
import { localeCompare } from '../util/Locale';
|
||||||
|
|
||||||
const initialState = {
|
export const userInitialState = {
|
||||||
username: '',
|
username: '',
|
||||||
|
|
||||||
isCurrentUser: function (otherUsername) {
|
isCurrentUser: function (otherUsername) {
|
||||||
@ -9,13 +9,13 @@ const initialState = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function reducer(state, action) {
|
export function userReducer(state, action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
|
||||||
case 'parse':
|
case 'parse':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
username: action.userJson.username
|
username: action.json.username
|
||||||
};
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -24,5 +24,5 @@ function reducer(state, action) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function useUserReducer() {
|
export default function useUserReducer() {
|
||||||
return useReducer(reducer, initialState);
|
return useReducer(userReducer, userInitialState);
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user