React conversion DONE!

This commit is contained in:
Elijah Steres 2021-01-08 00:29:39 -05:00
parent 09b90cf445
commit 0157ea3122
18 changed files with 349 additions and 121 deletions

13
Makefile Normal file
View File

@ -0,0 +1,13 @@
SOURCES = $(wildcard ./simmadome/src/*) $(wildcard ./simmadome/public/*)
OUTPUTS = $(wildcard ./simmadome/build/*)
.PHONY: run frontend
run: $(OUTPUTS)
python3 the_prestige.py
frontend:
(cd simmadome && npm run build)
$(OUTPUTS): $(SOURCES)
(cd simmadome && npm run build)

View File

@ -23,26 +23,26 @@ accepting pull requests, check the issues for to-dos.
- then another blank line seperating your batters and your pitchers. - then another blank line seperating your batters and your pitchers.
- the final lines are the names of the pitchers in your rotation, rotations can contain any number of pitchers between 1 and 8. - the final lines are the names of the pitchers in your rotation, rotations can contain any number of pitchers between 1 and 8.
- if you did it correctly, you'll get a team embed with a prompt to confirm. hit the 👍 and your team will be saved! - if you did it correctly, you'll get a team embed with a prompt to confirm. hit the 👍 and your team will be saved!
- m;deleteteam [teamname] (requires team ownership) - m;deleteteam [teamname] \(requires team ownership)
- allows you to delete the team with the provided name. you'll get an embed with a confirmation to prevent accidental deletions. hit the 👍 and your team will be deleted. - allows you to delete the team with the provided name. you'll get an embed with a confirmation to prevent accidental deletions. hit the 👍 and your team will be deleted.
- m;import - m;import
- imports an onomancer collection as a new team. you can use the new onomancer simsim setting to ensure compatibility. similarly to saveteam, you'll get a team embed with a prompt to confirm, hit the 👍 and your team will be saved! - imports an onomancer collection as a new team. you can use the new onomancer simsim setting to ensure compatibility. similarly to saveteam, you'll get a team embed with a prompt to confirm, hit the 👍 and your team will be saved!
#### editing (all of these commands require ownership and exact spelling of the team name): #### editing (all of these commands require ownership and exact spelling of the team name):
- m;addplayer batter/pitcher [team name] [player name] - m;addplayer batter/pitcher [team name] \[player name]
- adds a new player to the end of your team, either in the lineup or the rotation depending on which version you use. use addplayer batter or addplayer pitcher at the top of a list with entries separated by new lines: - adds a new player to the end of your team, either in the lineup or the rotation depending on which version you use. use addplayer batter or addplayer pitcher at the top of a list with entries separated by new lines:
- the name of the team you want to add the player to. - the name of the team you want to add the player to.
- the name of the player you want to add to the team. - the name of the player you want to add to the team.
- m;moveplayer [team name] [player name] [new lineup/rotation position number] - m;moveplayer [team name] \[player name] [new lineup/rotation position number]
- moves a player within your lineup or rotation. if you want to instead move a player from your rotation to your lineup or vice versa, use m;swapsection instead. use this command at the top of a list with entries separated by new lines: - moves a player within your lineup or rotation. if you want to instead move a player from your rotation to your lineup or vice versa, use m;swapsection instead. use this command at the top of a list with entries separated by new lines:
- the name of the team you want to move the player on. - the name of the team you want to move the player on.
- the name of the player you want to move. - the name of the player you want to move.
- the position you want to move them too, indexed with 1 being the first position of the lineup or rotation. all players below the specified position in the lineup or rotation will be pushed down. - the position you want to move them too, indexed with 1 being the first position of the lineup or rotation. all players below the specified position in the lineup or rotation will be pushed down.
- m;swapsection [team name] [player name] - m;swapsection [team name] \[player name]
- swaps a player from your lineup to the end of your rotation or your rotation to the end of your lineup. use this command at the top of a list with entries separated by new lines: - swaps a player from your lineup to the end of your rotation or your rotation to the end of your lineup. use this command at the top of a list with entries separated by new lines:
- the name of the team you want to swap the player on. - the name of the team you want to swap the player on.
- the name of the player you want to swap. - the name of the player you want to swap.
- m;removeplayer [team name] [player name] - m;removeplayer [team name] \[player name]
- removes a player from your team. if there are multiple copies of the same player on a team this will only delete the first one. use this command at the top of a list with entries separated by new lines: - removes a player from your team. if there are multiple copies of the same player on a team this will only delete the first one. use this command at the top of a list with entries separated by new lines:
- the name of the team you want to remove the player from. - the name of the team you want to remove the player from.
- the name of the player you want to remove. - the name of the player you want to remove.
@ -96,3 +96,7 @@ these folks are helping me a *ton* via patreon, and i cannot possibly thank them
- Chris Denmark - Chris Denmark
- Astrid Bek - Astrid Bek
- Kameleon - Kameleon
## Attribution
Twemoji is copyright 2020 Twitter, Inc and other contributors; code licensed under [the MIT License](http://opensource.org/licenses/MIT), graphics licensed under [CC-BY 4.0](https://creativecommons.org/licenses/by/4.0/)

View File

@ -2214,6 +2214,11 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/history": {
"version": "4.7.8",
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.8.tgz",
"integrity": "sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA=="
},
"@types/html-minifier-terser": { "@types/html-minifier-terser": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz",
@ -2311,6 +2316,25 @@
"@types/react": "^16" "@types/react": "^16"
} }
}, },
"@types/react-router": {
"version": "5.1.10",
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.10.tgz",
"integrity": "sha512-yu11Hu16CfGvvBWc7wluRlxbwfuSlY0snEntbbOTvfgMvyO6uLaEpAbnVOntr+9TNIpR++OOlPkmDcJPxOXRaQ==",
"requires": {
"@types/history": "*",
"@types/react": "*"
}
},
"@types/react-router-dom": {
"version": "5.1.7",
"resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.7.tgz",
"integrity": "sha512-D5mHD6TbdV/DNHYsnwBTv+y73ei+mMjrkGrla86HthE4/PVvL1J94Bu3qABU+COXzpL23T1EZapVVpwHuBXiUg==",
"requires": {
"@types/history": "*",
"@types/react": "*",
"@types/react-router": "*"
}
},
"@types/resolve": { "@types/resolve": {
"version": "0.0.8", "version": "0.0.8",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz",
@ -2347,6 +2371,11 @@
"@types/jest": "*" "@types/jest": "*"
} }
}, },
"@types/twemoji": {
"version": "12.1.1",
"resolved": "https://registry.npmjs.org/@types/twemoji/-/twemoji-12.1.1.tgz",
"integrity": "sha512-dW1B1WHTfrWmEzXb/tp8xsZqQHAyMB9JwLwbBqkIQVzmNUI02R7lJqxUpKFM114ygNZHKA1r74oPugCAiYHt1A=="
},
"@types/uglify-js": { "@types/uglify-js": {
"version": "3.11.1", "version": "3.11.1",
"resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.11.1.tgz", "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.11.1.tgz",
@ -7080,6 +7109,19 @@
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
}, },
"history": {
"version": "4.10.1",
"resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
"integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
"requires": {
"@babel/runtime": "^7.1.2",
"loose-envify": "^1.2.0",
"resolve-pathname": "^3.0.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0",
"value-equal": "^1.0.1"
}
},
"hmac-drbg": { "hmac-drbg": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
@ -7090,6 +7132,14 @@
"minimalistic-crypto-utils": "^1.0.1" "minimalistic-crypto-utils": "^1.0.1"
} }
}, },
"hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"requires": {
"react-is": "^16.7.0"
}
},
"hoopy": { "hoopy": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
@ -9946,6 +9996,15 @@
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
"integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==" "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="
}, },
"mini-create-react-context": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz",
"integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==",
"requires": {
"@babel/runtime": "^7.12.1",
"tiny-warning": "^1.0.3"
}
},
"mini-css-extract-plugin": { "mini-css-extract-plugin": {
"version": "0.11.3", "version": "0.11.3",
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.11.3.tgz", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.11.3.tgz",
@ -12389,6 +12448,52 @@
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz",
"integrity": "sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg==" "integrity": "sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg=="
}, },
"react-router": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz",
"integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==",
"requires": {
"@babel/runtime": "^7.1.2",
"history": "^4.9.0",
"hoist-non-react-statics": "^3.1.0",
"loose-envify": "^1.3.1",
"mini-create-react-context": "^0.4.0",
"path-to-regexp": "^1.7.0",
"prop-types": "^15.6.2",
"react-is": "^16.6.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0"
},
"dependencies": {
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
"path-to-regexp": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
"integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
"requires": {
"isarray": "0.0.1"
}
}
}
},
"react-router-dom": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz",
"integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==",
"requires": {
"@babel/runtime": "^7.1.2",
"history": "^4.9.0",
"loose-envify": "^1.3.1",
"prop-types": "^15.6.2",
"react-router": "5.2.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0"
}
},
"react-scripts": { "react-scripts": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-4.0.1.tgz", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-4.0.1.tgz",
@ -12845,6 +12950,11 @@
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
}, },
"resolve-pathname": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz",
"integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng=="
},
"resolve-url": { "resolve-url": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
@ -14543,6 +14653,16 @@
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
}, },
"tiny-invariant": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz",
"integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw=="
},
"tiny-warning": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
},
"tmpl": { "tmpl": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz",
@ -14680,6 +14800,58 @@
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
}, },
"twemoji": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/twemoji/-/twemoji-13.0.1.tgz",
"integrity": "sha512-mrTBq+XpCLM4zm76NJOjLHoQNV9mHdBt3Cba/T5lS1rxn8ArwpqE47mqTocupNlkvcLxoeZJjYSUW0DU5ZwqZg==",
"requires": {
"fs-extra": "^8.0.1",
"jsonfile": "^5.0.0",
"twemoji-parser": "13.0.0",
"universalify": "^0.1.2"
},
"dependencies": {
"fs-extra": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"requires": {
"graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
},
"dependencies": {
"jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
"requires": {
"graceful-fs": "^4.1.6"
}
}
}
},
"jsonfile": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-5.0.0.tgz",
"integrity": "sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w==",
"requires": {
"graceful-fs": "^4.1.6",
"universalify": "^0.1.2"
}
},
"universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
}
}
},
"twemoji-parser": {
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/twemoji-parser/-/twemoji-parser-13.0.0.tgz",
"integrity": "sha512-zMaGdskpH8yKjT2RSE/HwE340R4Fm+fbie4AaqjDa4H/l07YUmAvxkSfNl6awVWNRRQ0zdzLQ8SAJZuY5MgstQ=="
},
"type": { "type": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
@ -15018,6 +15190,11 @@
"spdx-expression-parse": "^3.0.0" "spdx-expression-parse": "^3.0.0"
} }
}, },
"value-equal": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
"integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
},
"vary": { "vary": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",

View File

@ -11,11 +11,17 @@
"@types/node": "^12.19.12", "@types/node": "^12.19.12",
"@types/react": "^16.14.2", "@types/react": "^16.14.2",
"@types/react-dom": "^16.9.10", "@types/react-dom": "^16.9.10",
"@types/react-router": "^5.1.10",
"@types/react-router-dom": "^5.1.7",
"@types/socket.io-client": "^1.4.34", "@types/socket.io-client": "^1.4.34",
"@types/twemoji": "^12.1.1",
"react": "^17.0.1", "react": "^17.0.1",
"react-dom": "^17.0.1", "react-dom": "^17.0.1",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.1", "react-scripts": "4.0.1",
"socket.io-client": "^3.0.5", "socket.io-client": "^3.0.5",
"twemoji": "^13.0.1",
"typescript": "^4.1.3", "typescript": "^4.1.3",
"web-vitals": "^0.2.4" "web-vitals": "^0.2.4"
}, },

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -5,26 +5,13 @@
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<meta
name="description" <title>⚾ The Simmadome</title>
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will <meta property="og:title" content="Watch at the Simmadome" />
work correctly both with client-side routing and a non-root public URL. <meta property="og:description" content="The Simsim: Your players, your teams, your games." />
Learn how to configure a non-root public URL by running `npm run build`. <meta name="twitter:card" content="summary">
--> <meta name="twitter:site" content="@SIBR_XVI">
<title>React App</title>
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -1,25 +0,0 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@ -169,7 +169,7 @@
border-radius: 4px; border-radius: 4px;
align-items: center; align-items: center;
display: flex; display: flex;
justify-content: start; justify-content: flex-start;
} }
.update_emoji { .update_emoji {

View File

@ -1,4 +1,6 @@
import { GameState } from './App'; import { GameState } from './GamesUtil';
import twemoji from 'twemoji';
import React, { useRef, useLayoutEffect } from 'react';
import './Game.css'; import './Game.css';
import base_filled from './img/base_filled.png'; import base_filled from './img/base_filled.png';
import base_empty from './img/base_empty.png'; import base_empty from './img/base_empty.png';
@ -6,9 +8,16 @@ import out_filled from './img/out_out.png';
import out_empty from './img/out_in.png'; import out_empty from './img/out_in.png';
function Game(props: {gameId: string, state : GameState}) { function Game(props: {gameId: string, state : GameState}) {
let self: React.MutableRefObject<HTMLDivElement | null> = useRef(null);
useLayoutEffect(() => {
if (self.current) {
twemoji.parse(self.current);
}
})
let state = props.state; let state = props.state;
return ( return (
<div className="game"> <div className="game" ref={self}>
<div className="header"> <div className="header">
<div className="inning">Inning: {state.display_top_of_inning ? "🔼" : "🔽"} {state.display_inning}/{state.max_innings}</div> <div className="inning">Inning: {state.display_top_of_inning ? "🔼" : "🔽"} {state.display_inning}/{state.max_innings}</div>
<div className="title">{state.title}</div> <div className="title">{state.title}</div>

View File

@ -0,0 +1,7 @@
#game_container {
margin-top: 3rem;
margin-left: 1rem;
margin-right: 1rem;
display: flex;
justify-content: space-around;
}

View File

@ -0,0 +1,24 @@
import React, {useState} from 'react';
import {GameState, useListener} from './GamesUtil';
import './GamePage.css';
import Game from './Game';
function GamePage() {
let searchparams = new URLSearchParams(window.location.search);
let gameId = searchparams.get('id');
let [games, setGames] = useState(new Array<[string, GameState]>());
useListener((newGames) => setGames(newGames));
let game = games.find((game) => game[0] === gameId)
return (
<div id="game_container">
{ game ?
<Game gameId={game[0]} state={game[1]}/> :
"The game you're looking for either doesn't exist or has already ended."
}
</div>
);
}
export default GamePage;

View File

@ -1,67 +1,47 @@
import React, {useState, useRef, useEffect, useLayoutEffect} from 'react'; import React, {useState, useRef, useEffect, useLayoutEffect} from 'react';
import io from 'socket.io-client'; import {GameState, GameList, useListener} from './GamesUtil';
import './App.css'; import {Link} from 'react-router-dom';
import './GamesPage.css';
import Game from './Game'; import Game from './Game';
interface GameState { function GamesPage() {
bases: (string | null)[]; let [search, setSearch] = useState(window.location.search);
outs: number; useEffect(() => {
display_top_of_inning: boolean setSearch(window.location.search);
display_inning: number }, [window.location.search])
max_innings: number
title: string
weather_emoji: string
weather_text: string
away_name: string
away_score: number
home_name: string
home_score: number
pitcher: string
batter: string
update_emoji: string
update_text: string
is_league: boolean
leagueoruser: string
}
type GameList = ([id: string, game: GameState] | null)[]; let searchparams = new URLSearchParams(search);
let filter = searchparams.get('league') ?? ""
function App(props: {filter: string | null, gameId: string | null}) {
let [games, setGames] = useState(new Array<[string, GameState]>()); let [games, setGames] = useState(new Array<[string, GameState]>());
let [filter, setFilter] = useState("");
useListener(setGames); useListener(setGames);
let filters: string[] = []; let filters = useRef(filter !== "" ? [filter] : []);
games.forEach((game, id) => { if (game[1].is_league && !filters.includes(game[1].leagueoruser)) { filters.push(game[1].leagueoruser) }}); 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(new Array<(string | null)>()); let gameList = useRef(new Array<(string | null)>());
let filterGames = games.filter((game, i) => filter === "" || game[1].leagueoruser === filter); let filterGames = games.filter((game, i) => filter === "" || game[1].leagueoruser === filter);
updateList(gameList.current, filterGames); updateList(gameList.current, filterGames, searchparams.get('gameId'));
return ( return (
<div className="App"> <>
<Filters filterList={filters} selectedFilter={filter} onSelectNewFilter={(filter: string) => {gameList.current = []; setFilter(filter)}}/> <Filters filterList={filters.current} selectedFilter={filter} />
<Grid gameList={gameList.current.map((val) => val !== null ? filterGames.find((game) => game[0] === val) as [string, GameState] : null )}/> <Grid gameList={gameList.current.map((val) => val !== null ? filterGames.find((game) => game[0] === val) as [string, GameState] : null )}/>
<Footer has_games={filterGames.length > 0}/> <Footer has_games={filterGames.length > 0}/>
</div> </>
); );
} }
// App Utils
// connects to the given url (or host if none) and waits for state updates
const useListener = (onUpdate: (update: [string, GameState][]) => void, url: string | null = null) => {
useEffect(() => {
let socket = url ? io(url) : io();
socket.on('connect', () => socket.emit('recieved', {}));
socket.on('states_update', onUpdate);
return () => {socket.disconnect()};
}, [url])
}
// adds and removes games from list to keep it up to date, without relocating games already in place // adds and removes games from list to keep it up to date, without relocating games already in place
function updateList(gameList: (string | null)[], games: [string, GameState][]) { function updateList(gameList: (string | null)[], games: [string, GameState][], firstGame: string | null) {
// insert firstGame into first slot, if necessary
if (firstGame !== null && games.find((game) => game[0] === firstGame)) {
if (gameList.includes(firstGame)) {
gameList[gameList.indexOf(firstGame)] = null;
}
gameList[0] = firstGame;
}
//remove games no longer present //remove games no longer present
for (let i = 0; i < gameList.length; i ++) { for (let i = 0; i < gameList.length; i ++) {
@ -88,14 +68,15 @@ function updateList(gameList: (string | null)[], games: [string, GameState][]) {
} }
} }
function Filters (props: {filterList: string[], selectedFilter: string, onSelectNewFilter: (newFilter: string) => void}) { function Filters (props: {filterList: string[], selectedFilter: string}) {
function Filter(innerprops: {title: string, filter:string} ) { function Filter(innerprops: {title: string, filter:string} ) {
let search = new URLSearchParams();
search.append('league', innerprops.filter);
return ( return (
<button className="filter" <Link to={innerprops.filter !== "" ? "/?" + search.toString() : "/"} className="filter" id={innerprops.filter === props.selectedFilter ? "selected_filter" : ""}>
id={innerprops.filter === props.selectedFilter ? "selected_filter" : ""} {innerprops.title}
onClick={() => props.onSelectNewFilter(innerprops.filter)}> </Link>
{innerprops.title}
</button>
); );
} }
@ -127,29 +108,27 @@ function Grid(props: { gameList: GameList }) {
return 3; return 3;
} }
} }
//set num cols after page loads
useLayoutEffect(() => { useLayoutEffect(() => {
setNumcols(getCols()); setNumcols(getCols());
}, []) }, [])
//set num cols on page resize
useEffect(() => { useEffect(() => {
window.addEventListener('resize', (event) => { window.addEventListener('resize', (event) => {
setNumcols(getCols()); setNumcols(getCols());
}) })
}) })
let emptyKey = 0;
let slots = newList.map((game) => {
if (game) {
return <Game gameId={game[0]} state={game[1]} key={game[0]}/>
} else {
return <div className="emptyslot"/>
}
})
return ( return (
<section className="container" id="container" ref={self}> <section className="container" id="container" ref={self}>
{slots.map((elem) => <div className="slot_container">{elem}</div>)} {newList.map((game) => (
<div className="slot_container" key={game ? game[0] : emptyKey++}>
{game ? <Game gameId={game[0]} state={game[1]}/> : <div className="emptyslot"/>}
</div>
))}
</section> </section>
); );
} }
@ -163,5 +142,4 @@ function Footer(props: { has_games: boolean }) {
); );
} }
export default App; export default GamesPage;
export type { GameState };

View File

@ -0,0 +1,39 @@
import {useLayoutEffect} from 'react';
import io from 'socket.io-client';
interface GameState {
bases: (string | null)[];
outs: number;
display_top_of_inning: boolean
display_inning: number
max_innings: number
title: string
weather_emoji: string
weather_text: string
away_name: string
away_score: number
home_name: string
home_score: number
pitcher: string
batter: string
update_emoji: string
update_text: string
is_league: boolean
leagueoruser: string
}
type GameList = ([id: string, game: GameState] | null)[];
// connects to the given url (or host if none) and waits for state updates
const useListener = (onUpdate: (update: [string, GameState][]) => void, url: string | null = null) => {
useLayoutEffect(() => {
let socket = url ? io(url) : io();
socket.on('connect', () => socket.emit('recieved', {}));
socket.on('states_update', onUpdate);
return () => {socket.disconnect()};
}, [url])
}
export { useListener };
export type { GameState, GameList };

View File

@ -65,5 +65,8 @@ h2 {
} }
img.emoji { img.emoji {
height: 14px; height: 1em;
width: 1em;
margin: 0 .05em 0 .1em;
vertical-align: -0.1em;
} }

View File

@ -1,19 +1,25 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import './index.css'; import './index.css';
import App from './App'; import GamesPage from './GamesPage';
import GamePage from './GamePage';
import discordlogo from "./img/discord.png"; import discordlogo from "./img/discord.png";
import reportWebVitals from './reportWebVitals'; import reportWebVitals from './reportWebVitals';
ReactDOM.render( ReactDOM.render(
<React.StrictMode> <React.StrictMode>
<Header /> <Router>
<App filter={null} gameId={null}/> <Header />
<Switch>
<Route path="/game" component={() => <GamePage key={window.location.search}/>}/>
<Route path="/" component={() => <GamesPage />}/>
</Switch>
</Router>
</React.StrictMode>, </React.StrictMode>,
document.getElementById('root') document.getElementById('root')
); );
function Header() { function Header() {
return ( return (
<div id="header"> <div id="header">