diff --git a/webapp/src/App.js b/webapp/src/App.js
index 04ad760..dd7a425 100644
--- a/webapp/src/App.js
+++ b/webapp/src/App.js
@@ -8,9 +8,14 @@ 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';
function App() {
const [polling, dispatchPolling] = useReducer(Polling.reducer, Polling.initialState)
+
+ const leaderboard = LeaderboardApi(polling, dispatchPolling).get();
+ // const user = UserApi(polling, dispatchPolling).get();
return
@@ -22,7 +27,7 @@ function App() {
} />
} />
} />
- } />
+ } />
} />
diff --git a/webapp/src/api/leaderboard.js b/webapp/src/api/leaderboard.js
new file mode 100644
index 0000000..993932c
--- /dev/null
+++ b/webapp/src/api/leaderboard.js
@@ -0,0 +1,24 @@
+import usePolling from "../util/Polling"
+
+const uri = '/api/leaderboard';
+
+export function LeaderboardApi(polling, dispatchPolling) {
+
+ const useGet = () => {
+ const mode = (polling.enabled === true)
+ ? { interval_sec: 300 } // update leaderbord stats every 5 min
+ : { interval_stop: true } // user has fliped OfflineToggel
+
+ const [leaderboard, isFetching] = usePolling(uri, mode);
+
+ if (polling.leaderboard !== isFetching) {
+ dispatchPolling({ type: 'setLeaderboard', value: isFetching });
+ }
+
+ return leaderboard;
+ }
+
+ return {
+ get: useGet
+ }
+}
\ No newline at end of file
diff --git a/webapp/src/components/Leaderboard/index.jsx b/webapp/src/components/Leaderboard/index.jsx
index 7d893fb..2dd542d 100644
--- a/webapp/src/components/Leaderboard/index.jsx
+++ b/webapp/src/components/Leaderboard/index.jsx
@@ -1,31 +1,30 @@
import React from "react"
import './index.css';
-import { AppData } from "../../context/data"
-export default function Leaderboard() {
- const [data] = React.useContext(AppData)
+export default function Leaderboard({ leaderboard }) {
- if (data.leaderboard == null)
+ if (leaderboard == null)
return
Loading...
-// var listItems = Object.keys(data).map(playerName => {
-// var rank = data[playerName];
-//
-// return
-// {playerName}: played {rank.gamesPlayed}, won {rank.gamesWon}, draw {rank.gamesDraw}
-//
-// });
-// return
;
+ // var listItems = Object.keys(data).map(playerName => {
+ // var rank = data[playerName];
+ //
+ // return
+ // {playerName}: played {rank.gamesPlayed}, won {rank.gamesWon}, draw {rank.gamesDraw}
+ //
+ // });
+ // return
;
- const tableRows = Object.keys(data.leaderboard).map(playerName => {
- var rank = data.leaderboard[playerName];
+ const tableRows = Object.keys(leaderboard).map(playerName => {
+ var rank = leaderboard[playerName];
- return
+ // TODO tr: className={data.isCurrentUser(playerName) && 'username'}
+ return
{playerName} |
{rank.gamesPlayed} |
{rank.gamesWon} |
{rank.gamesDraw} |
-
+
});
return
@@ -39,8 +38,8 @@ export default function Leaderboard() {
- { tableRows }
+ {tableRows}
-
+
};
diff --git a/webapp/src/reducer/polling.js b/webapp/src/reducer/polling.js
index 89ebf9a..88573a3 100644
--- a/webapp/src/reducer/polling.js
+++ b/webapp/src/reducer/polling.js
@@ -1,4 +1,4 @@
-import { useLocalStorage } from './util/PersistentStorage'
+import { useLocalStorage } from '../util/PersistentStorage'
const Persistent = (() => {
const [getEnabled, setEnabled] = useLocalStorage('polling.enabled', true);
@@ -26,18 +26,18 @@ export function pollingReducer(state, action) {
enabled: Persistent.setEnabled(!state.enabled)
};
- case 'games': return {
+ case 'setGames': return {
...state,
games: action.value
};
- case 'leaderboard': return {
+ case 'setLeaderboard': return {
...state,
leaderboard: action.value
};
default:
- throw Error('Unknown action:' + action.type);
+ throw Error('Unknown action.type:' + action.type);
}
}
diff --git a/webapp/src/util/Polling.js b/webapp/src/util/Polling.js
new file mode 100644
index 0000000..ed3fac6
--- /dev/null
+++ b/webapp/src/util/Polling.js
@@ -0,0 +1,53 @@
+import { useState, useCallback, useEffect, } from "react"
+
+/*
+ - uri: string
+ - mode:
+ - null - default, return cashed value while polling fresh one from server)
+ - interval_sec
+ - interval_stop
+*/
+
+export default function usePolling(url, mode) {
+ const [cache, setCache] = useState(null);
+ const [isFetching, setFetching] = useState(false);
+ const [delayID, setDelayID] = useState(null);
+
+ const fetchData = useCallback(() => {
+ setDelayID(null);
+ setFetching(true);
+
+ fetch(url)
+ .then((response) => response.json())
+ .then((freshData) => {
+ setCache(freshData);
+ setFetching(false);
+ })
+ .catch((err) => {
+ console.warn(err.message);
+ setFetching(false);
+ })
+ }, [url])
+
+ useEffect(() => {
+ if (cache === null && isFetching === false) {
+ fetchData();
+ }
+
+ if (mode?.interval_sec && delayID === null) {
+ const timeoutID = setTimeout(fetchData, mode.interval_sec * 1000)
+ setDelayID(timeoutID)
+ console.log("Fetch '" + url + "' scheduled in " + mode.interval_sec + " sec")
+ }
+ else if (mode?.interval_stop) {
+ clearTimeout(delayID); // cancel already scheduled fetch
+ setDelayID(null);
+ }
+
+ }, [url, mode, isFetching, cache, fetchData, delayID]);
+
+ return [
+ cache, // API response
+ isFetching // true / false
+ ]
+}