React conversion DONE!
This commit is contained in:
parent
09b90cf445
commit
0157ea3122
13
Makefile
Normal file
13
Makefile
Normal 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)
|
14
README.md
14
README.md
|
@ -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/)
|
177
simmadome/package-lock.json
generated
177
simmadome/package-lock.json
generated
|
@ -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",
|
||||||
|
|
|
@ -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 |
|
@ -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"
|
|
||||||
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
|
<title>⚾ The Simmadome</title>
|
||||||
work correctly both with client-side routing and a non-root public URL.
|
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
<meta property="og:title" content="Watch at the Simmadome" />
|
||||||
-->
|
<meta property="og:description" content="The Simsim: Your players, your teams, your games." />
|
||||||
<title>React App</title>
|
<meta name="twitter:card" content="summary">
|
||||||
|
<meta name="twitter:site" content="@SIBR_XVI">
|
||||||
</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 |
|
@ -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"
|
|
||||||
}
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
7
simmadome/src/GamePage.css
Normal file
7
simmadome/src/GamePage.css
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#game_container {
|
||||||
|
margin-top: 3rem;
|
||||||
|
margin-left: 1rem;
|
||||||
|
margin-right: 1rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
24
simmadome/src/GamePage.tsx
Normal file
24
simmadome/src/GamePage.tsx
Normal 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;
|
|
@ -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" : ""}
|
|
||||||
onClick={() => props.onSelectNewFilter(innerprops.filter)}>
|
|
||||||
{innerprops.title}
|
{innerprops.title}
|
||||||
</button>
|
</Link>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,28 +109,26 @@ function Grid(props: { gameList: GameList }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//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 };
|
|
39
simmadome/src/GamesUtil.tsx
Normal file
39
simmadome/src/GamesUtil.tsx
Normal 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 };
|
|
@ -65,5 +65,8 @@ h2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
img.emoji {
|
img.emoji {
|
||||||
height: 14px;
|
height: 1em;
|
||||||
|
width: 1em;
|
||||||
|
margin: 0 .05em 0 .1em;
|
||||||
|
vertical-align: -0.1em;
|
||||||
}
|
}
|
|
@ -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>
|
||||||
|
<Router>
|
||||||
<Header />
|
<Header />
|
||||||
<App filter={null} gameId={null}/>
|
<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">
|
||||||
|
|
Loading…
Reference in New Issue
Block a user