#24-directory-structure #26
@ -1,31 +1,34 @@
 | 
			
		||||
import './App.css';
 | 
			
		||||
import React, { useReducer } from 'react'
 | 
			
		||||
import { BrowserRouter, Routes, Route } from "react-router-dom"
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { BrowserRouter, Routes, Route } from "react-router-dom";
 | 
			
		||||
 | 
			
		||||
import Header from "./container/Header"
 | 
			
		||||
import Leaderboard from "./container/Leaderboard"
 | 
			
		||||
import Game from "./components/Game"
 | 
			
		||||
import About from "./components/About"
 | 
			
		||||
 | 
			
		||||
import Polling from './flux/polling';
 | 
			
		||||
import User from './flux/user';
 | 
			
		||||
import usePollingReducer from './reducer/polling';
 | 
			
		||||
import useUserReducer from './reducer/user';
 | 
			
		||||
import useLeaderboardReducer from './reducer/leaderboard';
 | 
			
		||||
 | 
			
		||||
import useLeaderboardApi from './api/leaderboard';
 | 
			
		||||
import useUserApi from './api/user';
 | 
			
		||||
 | 
			
		||||
function App() {
 | 
			
		||||
  const pollingFlux = useReducer(Polling.reducer, Polling.initialState);
 | 
			
		||||
  const userFlux = useReducer(User.reducer, User.initialState);
 | 
			
		||||
  const pollingReducer = usePollingReducer();
 | 
			
		||||
  const userReducer = useUserReducer();
 | 
			
		||||
  const leaderboardReducer = useLeaderboardReducer();
 | 
			
		||||
  
 | 
			
		||||
  const leaderboardApi = useLeaderboardApi(pollingFlux);
 | 
			
		||||
  const userApi = useUserApi(userFlux);
 | 
			
		||||
  const leaderboardApi = useLeaderboardApi(leaderboardReducer);
 | 
			
		||||
  const userApi = useUserApi(userReducer);
 | 
			
		||||
  
 | 
			
		||||
  const leaderboard = leaderboardApi.get();
 | 
			
		||||
  const leaderboard = leaderboardApi.get(pollingReducer);
 | 
			
		||||
  const user = userApi.get();
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="App" >
 | 
			
		||||
      <BrowserRouter>
 | 
			
		||||
        <Header pollingFlux={pollingFlux} />
 | 
			
		||||
        <Header pollingReducer={pollingReducer} />
 | 
			
		||||
        <Routes>
 | 
			
		||||
          {/* https://stackoverflow.com/questions/40541994/multiple-path-names-for-a-same-component-in-react-router */}
 | 
			
		||||
          <Route path="/game" element={<Game />} />
 | 
			
		||||
 | 
			
		||||
@ -2,19 +2,26 @@ import usePolling from "../util/Polling"
 | 
			
		||||
 | 
			
		||||
const uri = '/api/leaderboard';
 | 
			
		||||
 | 
			
		||||
export default function useLeaderboardApi([polling, dispatchPolling]) {
 | 
			
		||||
export default function useLeaderboardApi(leaderboardReducer) {
 | 
			
		||||
    const [leaderboard, dispatchLeaderboaed] = leaderboardReducer;
 | 
			
		||||
 | 
			
		||||
    const useGet = (pollingReducer) => {
 | 
			
		||||
        const [polling, dispatchPolling] = pollingReducer;
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
        const [table, isFetching] = usePolling(uri, mode);
 | 
			
		||||
 | 
			
		||||
        if (polling.leaderboard !== isFetching) {
 | 
			
		||||
            dispatchPolling({ type: 'next', leaderboard: isFetching });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (leaderboard.table !== table) {
 | 
			
		||||
            dispatchLeaderboaed({ type: 'next', table });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return leaderboard;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,8 +4,8 @@ import { NavLink } from "react-router-dom";
 | 
			
		||||
import OnlineToggle from '../components/OnlineToggle';
 | 
			
		||||
import Wobler from '../components/Wobler';
 | 
			
		||||
 | 
			
		||||
export default function Header({ pollingFlux }) {
 | 
			
		||||
  const [polling, dispatchPolling] = pollingFlux;
 | 
			
		||||
export default function Header({ pollingReducer }) {
 | 
			
		||||
  const [polling, dispatchPolling] = pollingReducer;
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className='Header'>
 | 
			
		||||
@ -33,4 +33,4 @@ export default function Header({ pollingFlux }) {
 | 
			
		||||
      </nav>
 | 
			
		||||
    </div>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@ -4,14 +4,15 @@ import Loading from '../components/Loading';
 | 
			
		||||
 | 
			
		||||
export default function Leaderboard({ leaderboard, user }) {
 | 
			
		||||
 | 
			
		||||
  if (leaderboard == null)
 | 
			
		||||
  const table = leaderboard?.table;
 | 
			
		||||
  if (!table)
 | 
			
		||||
    return <Loading />
 | 
			
		||||
 | 
			
		||||
  const isCurrentUser = (playerName) =>
 | 
			
		||||
    user.isCurrentUser(playerName) === true ? true : null;
 | 
			
		||||
    user?.isCurrentUser(playerName) === true ? true : null;
 | 
			
		||||
 | 
			
		||||
  const tableRows = Object.keys(leaderboard).map(playerName => {
 | 
			
		||||
    var rank = leaderboard[playerName];
 | 
			
		||||
  const tableRows = Object.keys(table).map(playerName => {
 | 
			
		||||
    var rank = table[playerName];
 | 
			
		||||
 | 
			
		||||
    return <tr key={playerName} className={isCurrentUser(playerName) && 'currentuser'}>
 | 
			
		||||
      <td>{playerName}</td>
 | 
			
		||||
 | 
			
		||||
@ -1,28 +0,0 @@
 | 
			
		||||
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;
 | 
			
		||||
							
								
								
									
										21
									
								
								webapp/src/reducer/leaderboard.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								webapp/src/reducer/leaderboard.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
			
		||||
import { useReducer } from 'react';
 | 
			
		||||
import { nextState } from '../util/StateHelper';
 | 
			
		||||
 | 
			
		||||
export const leaderboardInitialState = {
 | 
			
		||||
  table: null,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function leaderboardReducer(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(leaderboardReducer, leaderboardInitialState);
 | 
			
		||||
}
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
import { useLocalStorage } from '../util/PersistentStorage'
 | 
			
		||||
import { useReducer } from 'react';
 | 
			
		||||
import { useLocalStorage } from '../util/PersistentStorage';
 | 
			
		||||
import { nextState } from '../util/StateHelper';
 | 
			
		||||
 | 
			
		||||
const Persistent = (() => {
 | 
			
		||||
@ -33,9 +34,6 @@ export function pollingReducer(curntState, action) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Polling = { 
 | 
			
		||||
  reducer: pollingReducer, 
 | 
			
		||||
  initialState: pollingInitialState 
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default Polling
 | 
			
		||||
export default function usePollingReducer() {
 | 
			
		||||
  return useReducer(pollingReducer, pollingInitialState);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								webapp/src/reducer/user.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								webapp/src/reducer/user.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
			
		||||
import { useReducer } from 'react';
 | 
			
		||||
import { localeCompare } from '../util/Locale';
 | 
			
		||||
import { nextState } 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 nextState(state, action);
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
      throw Error('Unknown action.type', action.type);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default function useUserReducer() {
 | 
			
		||||
  return useReducer(userReducer, userInitialState);
 | 
			
		||||
}
 | 
			
		||||
@ -47,7 +47,7 @@ export default function usePolling(url, mode) {
 | 
			
		||||
    }, [url, mode, isFetching, cache, fetchData, delayID]);
 | 
			
		||||
 | 
			
		||||
    return [
 | 
			
		||||
        cache, // API response
 | 
			
		||||
        cache, // API responce
 | 
			
		||||
        isFetching  // true / false
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user