+
+
+ props.dispatch({type: 'rename_division', name: e.target.value})
+ }/>
+
{props.showError ? divisionErr : ""}
+
+ {props.state.teams.map((team, i) => {
+ let showDeleted = props.showError && props.deletedTeams.includes(team.name)
+ return (<>
+
+
{team.name}
+
+
+
{showDeleted ? "This team was deleted" : ""}
+ >)
+ })}
+ { props.state.teams.length < MAX_TEAMS_PER_DIVISION ? <>
+
+ {
+ let params = new URLSearchParams({query: e.target.value, page_len: '5', page_num: '0'});
+ fetch("/api/teams/search?" + params.toString())
+ .then(response => response.json())
+ .then(data => setSearchResults(data));
+ setNewName(e.target.value);
+ }}/>
+
+ {searchResults.length > 0 && newName.length > 0 ?
+ (
+ {searchResults.map(result =>
+
{
+ props.dispatch({type:'add_team', name: result});
+ setNewName("");
+ if (newNameInput.current) {
+ newNameInput.current.focus();
+ }
+ }}>{result}
+ )}
+
):
+ null
+ }> :
+ null
+ }
+
+
{props.showError ? teamsErr : ""}
+
+ );
+}
+
+// LEAGUE OPTIONS
+
+function LeagueOptions(props: {state: LeagueOptionsState, dispatch: React.Dispatch, showError: boolean}) {
+ return (
+
+
+
+ props.dispatch({type: 'set_games_series', value: value})} showError={props.showError}/>
+
+ props.dispatch({type: 'set_top_postseason', value: value})} showError={props.showError}/>
+
+ props.dispatch({type: 'set_wildcards', value: value})} showError={props.showError}/>
+
+
+
+ props.dispatch({type: 'set_intra_division_series', value: value})} showError={props.showError}/>
+
+ props.dispatch({type: 'set_inter_division_series', value: value})} showError={props.showError}/>
+
+ props.dispatch({type: 'set_inter_league_series', value: value})} showError={props.showError}/>
+
+
+ );
+}
+
+function NumberInput(props: {title: string, value: string, setValue: (newVal: string) => void, showError: boolean, minValue? : number}) {
+ let minValue = 1;
+ if (props.minValue !== undefined) {
+ minValue = props.minValue
+ }
+ return (
+
+
{props.title}
+
props.setValue(e.target.value)}/>
+
{(isNaN(Number(props.value)) || Number(props.value) < minValue) && props.showError ? "Must be a number greater than " + minValue : ""}
+
+ );
+}
+
+export default CreateLeague;
\ No newline at end of file
diff --git a/static/css/game.css b/simmadome/src/Game.css
similarity index 89%
rename from static/css/game.css
rename to simmadome/src/Game.css
index c60f230..ca73b4d 100644
--- a/static/css/game.css
+++ b/simmadome/src/Game.css
@@ -1,13 +1,4 @@
-
-:root {
- --background-main: #2f3136; /*discord dark theme background-secondary - the same color as the embeds*/
- --background-secondary: #4f545c; /*discord's background-tertiary*/
- --background-accent: #4f545c; /*discord's background-accent*/
- --highlight: rgb(113, 54, 138); /*matteo purple™*/
-}
-
.game {
- align-self: stretch;
text-align: center;
display: flex;
flex-direction: column;
@@ -125,7 +116,7 @@
}
.score {
- background: var(--background-accent);
+ background: var(--background-secondary);
width: 40px;
min-width: 40px;
height: 40px;
@@ -170,7 +161,7 @@
border-radius: 4px;
align-items: center;
display: flex;
- justify-content: start;
+ justify-content: flex-start;
}
.update_emoji {
@@ -193,7 +184,7 @@
height: 60px;
}
-.base_2 {
+.field > .base {
margin-bottom: -25%
}
diff --git a/simmadome/src/Game.tsx b/simmadome/src/Game.tsx
new file mode 100644
index 0000000..af1a50d
--- /dev/null
+++ b/simmadome/src/Game.tsx
@@ -0,0 +1,85 @@
+import { GameState } from './GamesUtil';
+import twemoji from 'twemoji';
+import React, { useRef, useLayoutEffect } from 'react';
+import { Link } from 'react-router-dom';
+import './Game.css';
+import base_filled from './img/base_filled.png';
+import base_empty from './img/base_empty.png';
+import out_filled from './img/out_out.png';
+import out_empty from './img/out_in.png';
+
+function Game(props: {gameId: string, state : GameState}) {
+ let self: React.MutableRefObject = useRef(null);
+ useLayoutEffect(() => {
+ if (self.current) {
+ twemoji.parse(self.current);
+ }
+ })
+
+ let state = props.state;
+ return (
+
+
+
Inning: {state.display_top_of_inning ? "🔼" : "🔽"} {state.display_inning}/{state.max_innings}
+
{state.title}
+
{state.weather_emoji} {state.weather_text}
+
+
+
+
+
+
+
+
+
+
OUTS
+
+ {[1, 2].map((out) => )}
+
+
+
+
+
PITCHER
+
{state.pitcher}
+
BATTER
+
{state.batter}
+
+
+
{state.update_emoji}
+
{state.update_text}
+
+
+
+
{state.display_top_of_inning ? state.away_name : state.home_name} batting.
+
{state.leagueoruser} (share)
+
+
+ );
+}
+
+function Team(props: {name: string, score: number}) {
+ return (
+
+
{ props.name }
+
{ props.score }
+
+ );
+}
+
+function Base(props: {name: string | null}) {
+ return (
+
+ );
+}
+
+function Out(props: {thisOut: number, totalOuts: number}) {
+ return
;
+}
+
+export default Game;
\ No newline at end of file
diff --git a/simmadome/src/GamePage.css b/simmadome/src/GamePage.css
new file mode 100644
index 0000000..e07cd9a
--- /dev/null
+++ b/simmadome/src/GamePage.css
@@ -0,0 +1,37 @@
+#game_container {
+ margin-top: 3rem;
+ margin-left: 1rem;
+ margin-right: 1rem;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: space-around;
+}
+
+.history_box {
+ width: 100%;
+ margin-top: 3rem;
+ padding: 1rem;
+ padding-top: 0.5rem;
+ background: var(--background-main);
+ border-radius: 0.25rem;
+ width: 100%;
+ min-width: 32rem;
+ max-width: 44rem;
+ box-sizing: border-box;
+ border: 4px solid;
+ border-radius: 4px;
+ border-color: var(--highlight);
+ border-top: none;
+ border-right: none;
+ border-bottom: none;
+}
+
+.history_title {
+ font-size: 14pt;
+}
+
+.history_update {
+ height: 4rem;
+ margin: 0.5rem;
+}
\ No newline at end of file
diff --git a/simmadome/src/GamePage.tsx b/simmadome/src/GamePage.tsx
new file mode 100644
index 0000000..cdf2c31
--- /dev/null
+++ b/simmadome/src/GamePage.tsx
@@ -0,0 +1,62 @@
+import React, {useState, useRef, useLayoutEffect} from 'react';
+import twemoji from 'twemoji';
+import ReactRouter from 'react-router';
+import {GameState, useListener} from './GamesUtil';
+import './GamePage.css';
+import Game from './Game';
+import {getUID} from './util';
+
+function GamePage(props: ReactRouter.RouteComponentProps<{id: string}>) {
+ let [game, setGame] = useState<[string, GameState]|undefined>(undefined);
+ let history = useRef<[number, string, string][]>([]);
+
+ useListener((newGames) => {
+ let newGame = newGames.find((gamePair) => gamePair[0] === props.match.params.id);
+ setGame(newGame);
+ console.log(newGame);
+ if (newGame !== undefined && newGame[1].start_delay < 0 && newGame[1].end_delay > 8) {
+ history.current.unshift([getUID(), newGame[1].update_emoji, newGame[1].update_text]);
+ if (history.current.length > 8) {
+ history.current.pop();
+ }
+ }
+ });
+
+ if (game === undefined) {
+ return The game you're looking for either doesn't exist or has already ended.
+ }
+
+ return (
+
+
+ { history.current.length > 0 ?
+ :
+ null
+ }
+
+ );
+}
+
+function GameHistory(props: {history: [number, string, string][]}) {
+ let self = useRef(null);
+
+ useLayoutEffect(() => {
+ if (self.current) {
+ twemoji.parse(self.current);
+ }
+ })
+
+ return (
+
+
History
+ {props.history.map((update) => (
+
+
{update[1]}
+
{update[2]}
+
+ ))}
+
+ );
+}
+
+export default GamePage;
\ No newline at end of file
diff --git a/static/css/games_page.css b/simmadome/src/GamesPage.css
similarity index 90%
rename from static/css/games_page.css
rename to simmadome/src/GamesPage.css
index d8d9e02..8baef47 100644
--- a/static/css/games_page.css
+++ b/simmadome/src/GamesPage.css
@@ -5,10 +5,16 @@
grid-auto-flow: row;
}
+.slot_container {
+ display: flex;
+ justify-content: space-around;
+}
+
.emptyslot, .game {
min-height: 18.75rem;
- justify-self: center;
max-width: 44rem;
+ min-width: 32rem;
+ width: 100%;
}
#filters {
@@ -58,8 +64,6 @@
.emptyslot {
border: 2px dashed white;
border-radius: 15px;
- align-self: stretch;
- justify-self: stretch;
text-align: center;
color: white;
}
@@ -72,9 +76,4 @@
left: 50%;
transform: translate(-50%, 0);
}
-
- .emptyslot {
- border: none;
- min-height: 0px;
- }
}
diff --git a/simmadome/src/GamesPage.tsx b/simmadome/src/GamesPage.tsx
new file mode 100644
index 0000000..4fdaacb
--- /dev/null
+++ b/simmadome/src/GamesPage.tsx
@@ -0,0 +1,142 @@
+import React, {useState, useRef, useEffect, useLayoutEffect} from 'react';
+import {GameState, GameList, useListener} from './GamesUtil';
+import {Link} from 'react-router-dom';
+import './GamesPage.css';
+import Game from './Game';
+
+function GamesPage() {
+ let [search, setSearch] = useState(window.location.search);
+ useEffect(() => {
+ setSearch(window.location.search);
+ //eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [window.location.search])
+
+ let searchparams = new URLSearchParams(search);
+ let filter = searchparams.get('league') ?? ""
+
+ let [games, setGames] = useState<[string, GameState][]>([]);
+ useListener(setGames);
+
+ let filters = useRef(filter !== "" ? [filter] : []);
+ games.forEach((game) => { if (game[1].is_league && !filters.current.includes(game[1].leagueoruser)) { filters.current.push(game[1].leagueoruser) }});
+ filters.current = filters.current.filter((f) => games.find((game) => game && game[1].is_league && game[1].leagueoruser === f) || f === filter);
+
+ let gameList = useRef<(string | null)[]>([]);
+ let filterGames = games.filter((game, i) => filter === "" || game[1].leagueoruser === filter);
+ updateList(gameList.current, filterGames, searchparams.get('game'));
+
+ return (
+ <>
+
+ val !== null ? filterGames.find((game) => game[0] === val) as [string, GameState] : null )}/>
+