diff --git a/components/game-form.tsx b/components/game-form.tsx index 1787ae6..7e2337a 100644 --- a/components/game-form.tsx +++ b/components/game-form.tsx @@ -2,7 +2,7 @@ import { MouseEvent, useContext, useState } from "react"; import axios from "axios"; import { DataContext } from "../pages"; -import { Game, Team } from "../domain/Game"; +import { Team } from "../domain/Game"; import Image from "next/image"; import { PlayerId } from "../domain/Player"; import { uniq } from "lodash"; @@ -24,7 +24,7 @@ function GameForm() { const delta = isComplete && leaderboard.getGameDelta( - new Game("", new Date(), winnerTeam, loserTeam), + { id: "", createdAt: Date.now(), winnerTeam, loserTeam }, leaderboard.getRankedPlayers() ); @@ -78,9 +78,9 @@ function GameForm() { {isAdding ? ( <>
-

Winner

+

Winner

{delta &&

Δ {delta}

} -

Loser

+

Loser

diff --git a/domain/Game.ts b/domain/Game.ts index 18e9cd0..1308dbb 100644 --- a/domain/Game.ts +++ b/domain/Game.ts @@ -1,26 +1,12 @@ -import { uniq } from "lodash"; - import { PlayerId } from "./Player"; export type Team = [PlayerId, PlayerId]; export type GameId = string; +type Timestamp = number; -export class Game implements IGame { - constructor( - public readonly id: GameId, - public readonly createdAt: Date, - public readonly winnerTeam: Team, - public readonly loserTeam: Team - ) { - if (uniq([...winnerTeam, ...loserTeam]).length !== 4) { - throw new Error("There must be four unique players."); - } - } -} - -export interface IGame { +export interface Game { id: GameId; - createdAt: Date; + createdAt: Timestamp; winnerTeam: Team; loserTeam: Team; } diff --git a/domain/Leaderboard.ts b/domain/Leaderboard.ts index 597fa89..a44b5ce 100644 --- a/domain/Leaderboard.ts +++ b/domain/Leaderboard.ts @@ -1,11 +1,11 @@ -import { Game, IGame } from "./Game"; -import { IPlayer, Player } from "./Player"; +import { Game } from "./Game"; +import { Player } from "./Player"; -export interface RatedPlayer extends IPlayer { +export interface RatedPlayer extends Player { rating: number; } -export interface RatedGame extends IGame { +export interface RatedGame extends Game { delta: number; } @@ -22,8 +22,8 @@ export class Leaderboard { })); this.games - .filter((game) => +game.createdAt < +date) - .sort((a, b) => +a.createdAt - +b.createdAt) + .filter((game) => game.createdAt < +date) + .sort((a, b) => a.createdAt - b.createdAt) .forEach((game) => { ratedPlayers = this.applyGame(game, ratedPlayers); }); @@ -36,8 +36,8 @@ export class Leaderboard { (player) => ({ ...player, rating: 1500 }) ); - const { games, players } = this.games - .sort((a, b) => +a.createdAt - +b.createdAt) + const { games } = this.games + .sort((a, b) => a.createdAt - b.createdAt) .reduce<{ games: RatedGame[]; players: RatedPlayer[] }>( (curr, game) => { const ratedPlayers = this.applyGame(game, curr.players); diff --git a/domain/Player.ts b/domain/Player.ts index 9f97227..245cc35 100644 --- a/domain/Player.ts +++ b/domain/Player.ts @@ -52,16 +52,7 @@ export type PlayerAnimal = | "hedgehog" | "kangaroo"; -export class Player implements IPlayer { - constructor( - public readonly id: PlayerId, - public readonly name: string, - public readonly animal: PlayerAnimal, - public readonly isRetired: boolean - ) {} -} - -export interface IPlayer { +export interface Player { id: PlayerId; name: string; animal: PlayerAnimal; diff --git a/pages/index.tsx b/pages/index.tsx index b1502ea..f93c4b4 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -6,9 +6,9 @@ import dynamic from "next/dynamic"; import Button from "../components/button"; import GameForm from "../components/game-form"; import GameList from "../components/game-list"; -import { IGame } from "../domain/Game"; +import { Game } from "../domain/Game"; import { Leaderboard } from "../domain/Leaderboard"; -import { IPlayer, PlayerId } from "../domain/Player"; +import { Player, PlayerId } from "../domain/Player"; import { UpstashGameRepository } from "../repository/UpstashGameRepository"; import { UpstashPlayerRepository } from "../repository/UpstashPlayerRepository"; import Card from "../components/card"; @@ -20,14 +20,9 @@ const PlayerList = dynamic(() => import("../components/player-list"), { suspense: true, }); -const Home: NextPage<{ players: string; games: string }> = (props) => { - const [players, setPlayers] = useState(JSON.parse(props.players)); - const [games, setGames] = useState( - JSON.parse(props.games).map((game: any) => ({ - ...game, - createdAt: new Date(game.createdAt), - })) - ); +const Home: NextPage<{ players: Player[]; games: Game[] }> = (props) => { + const [players, setPlayers] = useState(props.players); + const [games, setGames] = useState(props.games); const [tab, setTab] = useState<"games" | "players">("games"); function fetchPlayers() { @@ -35,14 +30,7 @@ const Home: NextPage<{ players: string; games: string }> = (props) => { } function fetchGames() { - axios("/api/games").then(({ data }) => - setGames( - data.map((game: IGame) => ({ - ...game, - createdAt: new Date(game.createdAt), - })) - ) - ); + axios("/api/games").then(({ data }) => setGames(data)); } function getPlayer(id: PlayerId) { @@ -120,10 +108,10 @@ const Home: NextPage<{ players: string; games: string }> = (props) => { }; export const DataContext = createContext<{ - players: IPlayer[]; + players: Player[]; refreshPlayers: VoidFunction; - getPlayer: (id: PlayerId) => IPlayer; - games: IGame[]; + getPlayer: (id: PlayerId) => Player; + games: Game[]; refreshGames: VoidFunction; leaderboard: Leaderboard; }>({ @@ -146,12 +134,7 @@ export async function getServerSideProps() { playerRepository.listAll(), ]); - return { - props: { - games: JSON.stringify(games), - players: JSON.stringify(players), - }, - }; + return { props: { games, players } }; } export default Home; diff --git a/repository/UpstashGameRepository.ts b/repository/UpstashGameRepository.ts index 16e5992..952bc94 100644 --- a/repository/UpstashGameRepository.ts +++ b/repository/UpstashGameRepository.ts @@ -1,7 +1,7 @@ import { Redis } from "@upstash/redis"; import { v4 as uuid } from "uuid"; -import { Game, IGame, Team } from "../domain/Game"; +import { Game, GameId, Team } from "../domain/Game"; const { set, del, mget, rpush, lrem, lrange } = Redis.fromEnv(); @@ -9,13 +9,18 @@ const GAME_LIST_KEY = "games"; export class UpstashGameRepository { public async create(winnerTeam: Team, loserTeam: Team) { - const game = new Game(uuid(), new Date(), winnerTeam, loserTeam); + const game: Game = { + id: uuid(), + createdAt: Date.now(), + winnerTeam, + loserTeam, + }; await set(this.getGameKey(game.id), JSON.stringify(game)); await rpush(GAME_LIST_KEY, game.id); } - public async delete(gameId: IGame["id"]) { + public async delete(gameId: GameId) { await del(this.getGameKey(gameId)); await lrem(GAME_LIST_KEY, 0, gameId); } @@ -24,15 +29,10 @@ export class UpstashGameRepository { const gameIds = await lrange(GAME_LIST_KEY, 0, -1); const keys = gameIds.map(this.getGameKey); - const games = await mget(keys[0], ...keys.slice(1)); - - return games.map((data) => { - const { id, createdAt, winnerTeam, loserTeam } = data as IGame; - return new Game(id, new Date(createdAt), winnerTeam, loserTeam); - }); + return await mget(keys[0], ...keys.slice(1)); } - private getGameKey(gameId: IGame["id"]) { + private getGameKey(gameId: GameId) { return `GAME#${gameId}`; } } diff --git a/repository/UpstashPlayerRepository.ts b/repository/UpstashPlayerRepository.ts index 64ccaa0..183cbae 100644 --- a/repository/UpstashPlayerRepository.ts +++ b/repository/UpstashPlayerRepository.ts @@ -1,7 +1,7 @@ import { Redis } from "@upstash/redis"; import { v4 as uuid } from "uuid"; -import { IPlayer, Player, PlayerAnimal } from "../domain/Player"; +import { Player, PlayerAnimal, PlayerId } from "../domain/Player"; const { set, mget, del, rpush, lrem, lrange } = Redis.fromEnv(); @@ -9,17 +9,22 @@ const PLAYER_LIST_KEY = "players"; export class UpstashPlayerRepository { public async create(name: string, animal: PlayerAnimal) { - const player = new Player(uuid(), name, animal, false); + const player: Player = { + id: uuid(), + name, + animal, + isRetired: false, + }; await set(this.getPlayerKey(player.id), JSON.stringify(player)); await rpush(PLAYER_LIST_KEY, player.id); } - public async update(player: IPlayer) { + public async update(player: Player) { await set(this.getPlayerKey(player.id), JSON.stringify(player)); } - public async delete(playerId: IPlayer["id"]) { + public async delete(playerId: PlayerId) { await del(this.getPlayerKey(playerId)); await lrem(PLAYER_LIST_KEY, 0, playerId); } @@ -28,15 +33,10 @@ export class UpstashPlayerRepository { const playerIds = await lrange(PLAYER_LIST_KEY, 0, -1); const keys = playerIds.map(this.getPlayerKey); - const players = await mget(keys[0], ...keys.slice(1)); - - return players.map((data) => { - const { id, name, animal, isRetired } = data as IPlayer; - return new Player(id, name, animal, Boolean(isRetired)); - }); + return await mget(keys[0], ...keys.slice(1)); } - private getPlayerKey(playerId: IPlayer["id"]) { + private getPlayerKey(playerId: PlayerId) { return `PLAYER#${playerId}`; } }