userFlux
This commit is contained in:
parent
df60508d45
commit
d92a3df32b
@ -7,31 +7,38 @@ import Leaderboard from "./container/Leaderboard"
|
|||||||
import Game from "./components/Game"
|
import Game from "./components/Game"
|
||||||
import About from "./components/About"
|
import About from "./components/About"
|
||||||
|
|
||||||
import Polling from './reducer/polling';
|
import Polling from './flux/polling';
|
||||||
import { LeaderboardApi } from './api/leaderboard';
|
import User from './flux/user';
|
||||||
// import { UserApi } from './api/user';
|
import useLeaderboardApi from './api/leaderboard';
|
||||||
|
import useUserApi from './api/user';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [polling, dispatchPolling] = useReducer(Polling.reducer, Polling.initialState)
|
const pollingFlux = useReducer(Polling.reducer, Polling.restoreState);
|
||||||
|
const userFlux = useReducer(User.reducer, User.initialState);
|
||||||
|
|
||||||
const leaderboard = LeaderboardApi(polling, dispatchPolling).get();
|
const leaderboardApi = useLeaderboardApi(pollingFlux);
|
||||||
// const user = UserApi(polling, dispatchPolling).get();
|
const userApi = useUserApi(userFlux);
|
||||||
|
|
||||||
return <div className="App">
|
const leaderboard = leaderboardApi.get();
|
||||||
<BrowserRouter>
|
const user = userApi.get();
|
||||||
<Header polling={polling} dispatchPolling={dispatchPolling} />
|
|
||||||
<Routes>
|
return (
|
||||||
{/* https://stackoverflow.com/questions/40541994/multiple-path-names-for-a-same-component-in-react-router */}
|
<div className="App" >
|
||||||
<Route path="/game" element={<Game />} />
|
<BrowserRouter>
|
||||||
<Route path="/game/new" element={<Game />} />
|
<Header pollingFlux={pollingFlux} />
|
||||||
<Route path="/game/proposal" element={<Game />} />
|
<Routes>
|
||||||
<Route path="/game/active" element={<Game />} />
|
{/* https://stackoverflow.com/questions/40541994/multiple-path-names-for-a-same-component-in-react-router */}
|
||||||
<Route path="/game/archive" element={<Game />} />
|
<Route path="/game" element={<Game />} />
|
||||||
<Route path="/leaderboard" element={<Leaderboard leaderboard={leaderboard} />} />
|
<Route path="/game/new" element={<Game />} />
|
||||||
<Route path="/about" element={<About />} />
|
<Route path="/game/proposal" element={<Game />} />
|
||||||
</Routes>
|
<Route path="/game/active" element={<Game />} />
|
||||||
</BrowserRouter>
|
<Route path="/game/archive" element={<Game />} />
|
||||||
</div>
|
<Route path="/leaderboard" element={<Leaderboard leaderboard={leaderboard} user={user} />} />
|
||||||
|
<Route path="/about" element={<About />} />
|
||||||
|
</Routes>
|
||||||
|
</BrowserRouter>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
@ -2,7 +2,7 @@ import usePolling from "../util/Polling"
|
|||||||
|
|
||||||
const uri = '/api/leaderboard';
|
const uri = '/api/leaderboard';
|
||||||
|
|
||||||
export function LeaderboardApi(polling, dispatchPolling) {
|
export default function useLeaderboardApi([polling, dispatchPolling]) {
|
||||||
|
|
||||||
const useGet = () => {
|
const useGet = () => {
|
||||||
const mode = (polling.enabled === true)
|
const mode = (polling.enabled === true)
|
||||||
|
20
webapp/src/api/user.js
Normal file
20
webapp/src/api/user.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import usePolling from "../util/Polling"
|
||||||
|
|
||||||
|
const uri = '/api/user';
|
||||||
|
|
||||||
|
export default function useUserApi([user, dispatchUser]) {
|
||||||
|
|
||||||
|
const useGet = () => {
|
||||||
|
const [nextUser] = usePolling(uri);
|
||||||
|
|
||||||
|
if (typeof nextUser?.username === 'string' && nextUser.username !== user.username) {
|
||||||
|
dispatchUser({ type: "next", username: nextUser.username });
|
||||||
|
}
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
get: useGet
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,8 @@ import { NavLink } from "react-router-dom";
|
|||||||
import OnlineToggle from '../components/OnlineToggle';
|
import OnlineToggle from '../components/OnlineToggle';
|
||||||
import Wobler from '../components/Wobler';
|
import Wobler from '../components/Wobler';
|
||||||
|
|
||||||
export default function Header({ polling, dispatchPolling }) {
|
export default function Header({ pollingFlux }) {
|
||||||
|
const [polling, dispatchPolling] = pollingFlux;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='Header'>
|
<div className='Header'>
|
||||||
|
@ -4,6 +4,6 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
tr.username {
|
tr.currentuser {
|
||||||
background-color:aliceblue;
|
background-color:aliceblue;
|
||||||
}
|
}
|
||||||
|
@ -2,25 +2,18 @@ import './Leaderboard.css';
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import Loading from '../components/Loading';
|
import Loading from '../components/Loading';
|
||||||
|
|
||||||
export default function Leaderboard({ leaderboard }) {
|
export default function Leaderboard({ leaderboard, user }) {
|
||||||
|
|
||||||
if (leaderboard == null)
|
if (leaderboard == null)
|
||||||
return <Loading />
|
return <Loading />
|
||||||
|
|
||||||
// var listItems = Object.keys(data).map(playerName => {
|
const isCurrentUser = (playerName) =>
|
||||||
// var rank = data[playerName];
|
user.isCurrentUser(playerName) === true ? true : null;
|
||||||
//
|
|
||||||
// return <li key={playerName}>
|
|
||||||
// {playerName}: played {rank.gamesPlayed}, won {rank.gamesWon}, draw {rank.gamesDraw}
|
|
||||||
// </li>
|
|
||||||
// });
|
|
||||||
// return <ul>{listItems}</ul>;
|
|
||||||
|
|
||||||
const tableRows = Object.keys(leaderboard).map(playerName => {
|
const tableRows = Object.keys(leaderboard).map(playerName => {
|
||||||
var rank = leaderboard[playerName];
|
var rank = leaderboard[playerName];
|
||||||
|
|
||||||
// TODO tr: className={data.isCurrentUser(playerName) && 'username'}
|
return <tr key={playerName} className={isCurrentUser(playerName) && 'currentuser'}>
|
||||||
return <tr key={playerName} >
|
|
||||||
<td>{playerName}</td>
|
<td>{playerName}</td>
|
||||||
<td>{rank.gamesPlayed}</td>
|
<td>{rank.gamesPlayed}</td>
|
||||||
<td>{rank.gamesWon}</td>
|
<td>{rank.gamesWon}</td>
|
||||||
|
@ -7,18 +7,16 @@ const Persistent = (() => {
|
|||||||
getEnabled,
|
getEnabled,
|
||||||
setEnabled
|
setEnabled
|
||||||
}
|
}
|
||||||
})(); // <<--- execute
|
})(); // <<--- Execute
|
||||||
|
|
||||||
export const pollingGetInitialState = () => {
|
const restoreState = {
|
||||||
return {
|
|
||||||
enabled: Persistent.getEnabled() === 'true',
|
enabled: Persistent.getEnabled() === 'true',
|
||||||
|
|
||||||
games: false,
|
games: false,
|
||||||
leaderboard: false
|
leaderboard: false
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function pollingReducer(state, action) {
|
function reducer(state, action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
|
||||||
case 'toggleOnOff': return {
|
case 'toggleOnOff': return {
|
||||||
@ -41,9 +39,6 @@ export function pollingReducer(state, action) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Polling = {
|
const Polling = { reducer, restoreState }
|
||||||
initialState: pollingGetInitialState(), // <<--- execute
|
|
||||||
reducer: pollingReducer
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Polling
|
export default Polling
|
28
webapp/src/flux/user.js
Normal file
28
webapp/src/flux/user.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { localeCompare } from '../util/Locale'
|
||||||
|
import StateHelper from '../util/StateHelper';
|
||||||
|
|
||||||
|
export const userInitialState = {
|
||||||
|
username: '',
|
||||||
|
isCurrentUser: function (otherUsername) {
|
||||||
|
return localeCompare(this.username, otherUsername)
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export function userReducer(state, action) {
|
||||||
|
switch (action.type) {
|
||||||
|
|
||||||
|
case 'next':
|
||||||
|
return StateHelper.next(state, action);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw Error('Unknown action.type: ' + action.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const User = {
|
||||||
|
reducer: userReducer,
|
||||||
|
initialState: userInitialState
|
||||||
|
};
|
||||||
|
|
||||||
|
export default User;
|
6
webapp/src/util/Locale.js
Normal file
6
webapp/src/util/Locale.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export function localeCompare(a, b) {
|
||||||
|
// console.log(localeCompare, a, b);
|
||||||
|
return typeof a === 'string' && typeof b === 'string'
|
||||||
|
? a.localeCompare(b, undefined, { sensitivity: 'accent' }) === 0
|
||||||
|
: a === b;
|
||||||
|
}
|
22
webapp/src/util/StateHelper.js
Normal file
22
webapp/src/util/StateHelper.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
export function nextState(state, action) {
|
||||||
|
const nextState = { ...state };
|
||||||
|
|
||||||
|
Object.keys(action)
|
||||||
|
.slice(1) // skip first property i.e. 'next'
|
||||||
|
.forEach(key => {
|
||||||
|
if (Object.hasOwn(nextState, key)) {
|
||||||
|
console.log("next [", key, "] = ", action[key]);
|
||||||
|
nextState[key] = action[key];
|
||||||
|
} else {
|
||||||
|
console.warn("nextState: bad action property\n", key + ":", action[key]);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return nextState;
|
||||||
|
}
|
||||||
|
|
||||||
|
const StateHelper = {
|
||||||
|
next: nextState
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StateHelper;
|
Loading…
Reference in New Issue
Block a user