From d92a3df32b002919a5a890b680e64bcc67cbe01b Mon Sep 17 00:00:00 2001 From: djmil Date: Tue, 7 Nov 2023 11:57:52 +0100 Subject: [PATCH] userFlux --- webapp/src/App.js | 51 ++++++++++++++----------- webapp/src/api/leaderboard.js | 2 +- webapp/src/api/user.js | 20 ++++++++++ webapp/src/container/Header.jsx | 3 +- webapp/src/container/Leaderboard.css | 2 +- webapp/src/container/Leaderboard.jsx | 17 +++------ webapp/src/{reducer => flux}/polling.js | 13 ++----- webapp/src/flux/user.js | 28 ++++++++++++++ webapp/src/util/Locale.js | 6 +++ webapp/src/util/StateHelper.js | 22 +++++++++++ 10 files changed, 118 insertions(+), 46 deletions(-) create mode 100644 webapp/src/api/user.js rename webapp/src/{reducer => flux}/polling.js (75%) create mode 100644 webapp/src/flux/user.js create mode 100644 webapp/src/util/Locale.js create mode 100644 webapp/src/util/StateHelper.js diff --git a/webapp/src/App.js b/webapp/src/App.js index d19d707..0b8f5a3 100644 --- a/webapp/src/App.js +++ b/webapp/src/App.js @@ -7,31 +7,38 @@ import Leaderboard from "./container/Leaderboard" import Game from "./components/Game" import About from "./components/About" -import Polling from './reducer/polling'; -import { LeaderboardApi } from './api/leaderboard'; -// import { UserApi } from './api/user'; +import Polling from './flux/polling'; +import User from './flux/user'; +import useLeaderboardApi from './api/leaderboard'; +import useUserApi from './api/user'; function App() { - const [polling, dispatchPolling] = useReducer(Polling.reducer, Polling.initialState) - - const leaderboard = LeaderboardApi(polling, dispatchPolling).get(); - // const user = UserApi(polling, dispatchPolling).get(); + const pollingFlux = useReducer(Polling.reducer, Polling.restoreState); + const userFlux = useReducer(User.reducer, User.initialState); + + const leaderboardApi = useLeaderboardApi(pollingFlux); + const userApi = useUserApi(userFlux); + + const leaderboard = leaderboardApi.get(); + const user = userApi.get(); - return
- -
- - {/* https://stackoverflow.com/questions/40541994/multiple-path-names-for-a-same-component-in-react-router */} - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - -
+ return ( +
+ +
+ + {/* https://stackoverflow.com/questions/40541994/multiple-path-names-for-a-same-component-in-react-router */} + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + +
+ ) } export default App; diff --git a/webapp/src/api/leaderboard.js b/webapp/src/api/leaderboard.js index 993932c..5b2e565 100644 --- a/webapp/src/api/leaderboard.js +++ b/webapp/src/api/leaderboard.js @@ -2,7 +2,7 @@ import usePolling from "../util/Polling" const uri = '/api/leaderboard'; -export function LeaderboardApi(polling, dispatchPolling) { +export default function useLeaderboardApi([polling, dispatchPolling]) { const useGet = () => { const mode = (polling.enabled === true) diff --git a/webapp/src/api/user.js b/webapp/src/api/user.js new file mode 100644 index 0000000..7f8606a --- /dev/null +++ b/webapp/src/api/user.js @@ -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 + } +} \ No newline at end of file diff --git a/webapp/src/container/Header.jsx b/webapp/src/container/Header.jsx index b6a446a..72f52bf 100644 --- a/webapp/src/container/Header.jsx +++ b/webapp/src/container/Header.jsx @@ -4,7 +4,8 @@ import { NavLink } from "react-router-dom"; import OnlineToggle from '../components/OnlineToggle'; import Wobler from '../components/Wobler'; -export default function Header({ polling, dispatchPolling }) { +export default function Header({ pollingFlux }) { + const [polling, dispatchPolling] = pollingFlux; return (
diff --git a/webapp/src/container/Leaderboard.css b/webapp/src/container/Leaderboard.css index 89964b1..4eeafb6 100644 --- a/webapp/src/container/Leaderboard.css +++ b/webapp/src/container/Leaderboard.css @@ -4,6 +4,6 @@ align-items: center; } -tr.username { +tr.currentuser { background-color:aliceblue; } diff --git a/webapp/src/container/Leaderboard.jsx b/webapp/src/container/Leaderboard.jsx index 4194048..44e338b 100644 --- a/webapp/src/container/Leaderboard.jsx +++ b/webapp/src/container/Leaderboard.jsx @@ -2,25 +2,18 @@ import './Leaderboard.css'; import React from "react" import Loading from '../components/Loading'; -export default function Leaderboard({ leaderboard }) { +export default function Leaderboard({ leaderboard, user }) { if (leaderboard == null) return - // var listItems = Object.keys(data).map(playerName => { - // var rank = data[playerName]; - // - // return
  • - // {playerName}: played {rank.gamesPlayed}, won {rank.gamesWon}, draw {rank.gamesDraw} - //
  • - // }); - // return
      {listItems}
    ; + const isCurrentUser = (playerName) => + user.isCurrentUser(playerName) === true ? true : null; const tableRows = Object.keys(leaderboard).map(playerName => { var rank = leaderboard[playerName]; - // TODO tr: className={data.isCurrentUser(playerName) && 'username'} - return + return {playerName} {rank.gamesPlayed} {rank.gamesWon} @@ -43,4 +36,4 @@ export default function Leaderboard({ leaderboard }) {
    -}; +}; \ No newline at end of file diff --git a/webapp/src/reducer/polling.js b/webapp/src/flux/polling.js similarity index 75% rename from webapp/src/reducer/polling.js rename to webapp/src/flux/polling.js index 88573a3..2f8cbd8 100644 --- a/webapp/src/reducer/polling.js +++ b/webapp/src/flux/polling.js @@ -7,18 +7,16 @@ const Persistent = (() => { getEnabled, setEnabled } -})(); // <<--- execute +})(); // <<--- Execute -export const pollingGetInitialState = () => { - return { +const restoreState = { enabled: Persistent.getEnabled() === 'true', games: false, leaderboard: false - } }; -export function pollingReducer(state, action) { +function reducer(state, action) { switch (action.type) { case 'toggleOnOff': return { @@ -41,9 +39,6 @@ export function pollingReducer(state, action) { } } -const Polling = { - initialState: pollingGetInitialState(), // <<--- execute - reducer: pollingReducer -} +const Polling = { reducer, restoreState } export default Polling \ No newline at end of file diff --git a/webapp/src/flux/user.js b/webapp/src/flux/user.js new file mode 100644 index 0000000..0d286cd --- /dev/null +++ b/webapp/src/flux/user.js @@ -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; \ No newline at end of file diff --git a/webapp/src/util/Locale.js b/webapp/src/util/Locale.js new file mode 100644 index 0000000..42e0b83 --- /dev/null +++ b/webapp/src/util/Locale.js @@ -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; +} \ No newline at end of file diff --git a/webapp/src/util/StateHelper.js b/webapp/src/util/StateHelper.js new file mode 100644 index 0000000..1cda2df --- /dev/null +++ b/webapp/src/util/StateHelper.js @@ -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; \ No newline at end of file