Compare commits

..

620 commits

Author SHA1 Message Date
Sorrel
08745443ca
Create LICENSE 2021-04-14 20:31:26 -04:00
Sorrel
177d3a4c42
Merge pull request #14 from sorrelbri/dependabot/npm_and_yarn/lodash-4.17.19
Bump lodash from 4.17.15 to 4.17.19
2020-07-20 13:13:50 -04:00
dependabot[bot]
1d804694b7
Bump lodash from 4.17.15 to 4.17.19
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-20 17:07:39 +00:00
Sorrel
a40253a4fd
Merge pull request #13 from sorrelbri/dependabot/npm_and_yarn/packages/server/lodash-4.17.19
Bump lodash from 4.17.15 to 4.17.19 in /packages/server
2020-07-20 13:05:21 -04:00
Sorrel
1c470ffec0
Merge pull request #15 from sorrelbri/patch/game-service
patch Game service
2020-07-20 12:35:15 -04:00
Sorrel
27cf281670
patch Game service
Bug where player passes were submitted before `game` object was assigned within `Game.makeMove` patched
2020-07-20 12:20:47 -04:00
dependabot[bot]
0fad98a1d0
Bump lodash from 4.17.15 to 4.17.19 in /packages/server
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-19 12:20:31 +00:00
Sorrel
14a94be59e
Merge pull request #12 from sorrelbri/game_record_tree
Game add checkMove hook
2020-07-07 17:30:20 -07:00
sorrelbri
0f3e7942ef refactor Game.makeMove to check legality only if checkMove has not yet been called 2020-07-07 17:08:02 -07:00
sorrelbri
cfec49845b add checkMove hook to Game 2020-07-07 16:52:45 -07:00
Sorrel
2a84644e12
Merge pull request #11 from sorrelbri/game_record_tree
Game Service plays through and ends finished games
2020-07-01 15:06:06 -07:00
Sorrel
e1a6cd9a44
patch rewrite
original written with optional chaining, changed to `&&` operations
2020-07-01 15:01:12 -07:00
Sorrel
a3420c1152
patch to fix gameData is undefined 2020-07-01 14:56:07 -07:00
sorrelbri
c4192f2e33 add support for ending game from game record to game service 2020-07-01 14:32:03 -07:00
sorrelbri
cbf9d461ba adjust endGame query to modify record with win information 2020-07-01 11:42:16 -07:00
sorrelbri
5eca128110 add placement boolean to Move migration, add placement and prior_move selects to move query 2020-07-01 10:54:32 -07:00
Sorrel
dd0289439c
Merge pull request #9 from sorrelbri/guest_account
Guest account
2020-06-27 13:21:27 -07:00
sorrelbri
9cae5ff185 style Continue as Guest button, patch account display on NavBar 2020-06-27 13:05:38 -07:00
sorrelbri
0ec92dd5e9 add random password to guest 2020-06-26 23:33:56 -07:00
sorrelbri
ff3d6c2c24 add service to generate guest account on server 2020-06-26 23:22:17 -07:00
sorrelbri
1e6050a486 add FE service to post to auth/guest 2020-06-26 17:55:52 -07:00
sorrelbri
2f1174c21b stub guest creation on server 2020-06-26 17:35:13 -07:00
Sorrel
ccbeddfa40
Merge pull request #8 from sorrelbri/patch_game_end_bug
patch game end bug
2020-06-26 16:33:33 -07:00
sorrelbri
f8779ae887 patch game end bug, update_board socket message always returns data in consistent format 2020-06-26 16:24:55 -07:00
Sorrel
a9be49c38e
Merge pull request #7 from sorrelbri/game_record
add game record view to game page menu
2020-06-26 13:31:27 -07:00
sorrelbri
5e33e6439e add print game record option to menu, prints only canvas elements 2020-06-26 13:15:07 -07:00
sorrelbri
ea951bee66 add game record section to readme 2020-06-26 11:50:26 -07:00
Sorrel
d7c53e7c7f
Merge pull request #6 from sorrelbri/game_record
add menu component to game page that displays game record
2020-06-25 22:52:55 -07:00
sorrelbri
a290ec532d add game record display overflow canvas for moves made at previously played points 2020-06-25 19:44:51 -07:00
sorrelbri
9ed4fc0a39 draw game record on canvas when menu is opened 2020-06-24 16:53:31 -07:00
sorrelbri
62fe6dfcb4 add click handler and basic style to menu on Game 2020-06-21 18:47:00 -07:00
sorrelbri
3955f940e2 stub Menu, add showMenu value to Game 2020-06-21 18:20:03 -07:00
sorrelbri
f14c0ce087 style Kifu to hover 'show menu' text 2020-06-21 17:34:03 -07:00
Sorrel
5eedd6b2e9
Merge pull request #5 from sorrelbri/game_record
Add Kifu to Game UI
2020-06-21 16:53:26 -07:00
sorrelbri
d803f55f5b remove props from Kifu 2020-06-21 16:33:53 -07:00
sorrelbri
35c51c567f style kifu 2020-06-21 16:32:24 -07:00
sorrelbri
8414d13949 pass Kifu to appropriate PlayerArea 2020-06-21 16:02:45 -07:00
sorrelbri
aed378c021 stub Kifu component 2020-06-21 15:00:11 -07:00
sorrelbri
4b507cce96 patch game meta to include score in tests 2020-06-20 17:23:13 -07:00
sorrelbri
a8119bb194 remove dot data from game end state 2020-06-20 17:05:39 -07:00
sorrelbri
692a8b1400 add endGame function to FE 2020-06-20 16:56:03 -07:00
sorrelbri
52762475f7 add socket send game_end message from FE 2020-06-20 16:25:09 -07:00
sorrelbri
0db13d2913 add socket message for toggling territory 2020-06-17 11:59:21 -07:00
sorrelbri
b1d43b5b02 display end game territory dot 2020-06-16 22:29:52 -07:00
sorrelbri
e40fa63274 update readme with game state details 2020-06-13 16:02:55 -07:00
sorrelbri
a3aecfeadc revert https patch 2020-06-08 14:13:32 -07:00
sorrelbri
0b2bfad12f add server side https redirect 2020-06-08 14:00:45 -07:00
sorrelbri
72b93f8ff1 patch secure protocol redirect 2020-06-08 13:13:36 -07:00
sorrelbri
18027ec1ef patch gameServices tests 2020-06-07 17:27:16 -07:00
sorrelbri
be8cb70431 connect FE submit pass to BE game service 2020-06-07 17:06:26 -07:00
sorrelbri
249789944b patch game_resign in games reducer 2020-06-07 15:10:18 -07:00
sorrelbri
17b2e2c31f connect FE submit resign to BE game service 2020-06-07 13:47:09 -07:00
sorrelbri
77ced54c6f patch useContext dependencies in Game component 2020-06-06 17:08:40 -07:00
sorrelbri
97242a2235 add render for hoshi points 2020-06-06 16:54:48 -07:00
sorrelbri
f010d5d9ee add highlight for current turn to PlayerArea bowl 2020-06-06 16:22:14 -07:00
sorrelbri
6359089e91 display player data in PlayerArea component 2020-06-06 16:04:32 -07:00
sorrelbri
49873aa706 patch endGame scoring bug 2020-06-05 22:54:19 -07:00
sorrelbri
12f9847a0e add toggleTerritory logic to Game 2020-06-04 23:47:25 -07:00
sorrelbri
09b346064f Merge branch 'master' into game_logic 2020-06-04 22:42:56 -07:00
sorrelbri
84085dc8b2 add screenshots and stub new sections of readme 2020-06-02 23:49:57 -07:00
sorrelbri
934b1b7b2d add endGame to count territory 2020-06-02 00:23:21 -07:00
sorrelbri
7c2bf6416b stub toggleTerritory and endGame methods on Game 2020-06-01 22:53:02 -07:00
sorrelbri
d1f3459516 add determineLife to Game.endGame, refactor joinEmptyPoints to avoid removing empty points from adjoining groups' liberties 2020-06-01 17:12:28 -07:00
sorrelbri
1b3dbd870a add determineTerritory to Game.endGame for empty points 2020-06-01 15:58:43 -07:00
sorrelbri
19a5282b73 add joinEmptyPoints to endGame method on Game 2020-05-31 23:27:38 -07:00
sorrelbri
55a282b5c8 update seed for honinbo game 2020-05-30 22:37:51 -07:00
sorrelbri
796bb7aad9 patch neighboring liberty bug 2020-05-30 21:44:52 -07:00
sorrelbri
8fa6e207ed add styling for ko and legal move on hover 2020-05-30 17:07:27 -07:00
sorrelbri
d54d43c42c add cache confirmation check to Game Service 2020-05-30 16:43:41 -07:00
sorrelbri
181554124b decouple gameService from db 2020-05-30 16:01:13 -07:00
Sorrel Bri
3eabf4191a stub endGame function, add pass counter on Game 2020-05-15 22:00:08 -07:00
Sorrel Bri
9b419a416b stub submitPass on Game 2020-05-14 23:21:19 -07:00
Sorrel Bri
e41d889177 patch failing react tests 2020-05-14 21:04:29 -07:00
Sorrel Bri
2922be86e1 Merge branch 'game_logic' 2020-05-14 21:01:54 -07:00
Sorrel Bri
7eb592a397 patch lerna bootstrap 2020-05-14 21:01:43 -07:00
Sorrel Bri
572a1dcf19 patch App and ActionError components to cleanup effect 2020-05-14 21:01:04 -07:00
Sorrel Bri
ef0e53756e add submitResign to Game 2020-05-14 20:59:49 -07:00
Sorrel Bri
9f18c01839 patch missing node-sass version 2020-05-14 19:30:58 -07:00
Sorrel Bri
8cdefa98c1 add seed example game 2020-05-14 19:26:41 -07:00
Sorrel Bri
475db4e812 patch trace capture bug in Game 2020-05-12 16:47:17 -07:00
Sorrel Bri
73ba2bb237 patch trace capture bug in Game 2020-05-12 16:34:11 -07:00
Sorrel Bri
a324ece0af add move persistence to gameService 2020-05-11 18:18:42 -07:00
Sorrel Bri
2e613daa1b patch seeds without hardcoded ids to ensure auto-increment 2020-05-10 17:08:13 -07:00
Sorrel Bri
8e25106be6 update knex and pg 2020-05-10 13:29:14 -07:00
Sorrel Bri
c43df2cd5f update node-sass 2020-05-10 12:48:35 -07:00
Sorrel Bri
72d31cca5b update bcrypt version 2020-05-10 12:04:58 -07:00
Sorrel Bri
54fd676656 patch implicit meta prop passed to Point object 2020-05-04 23:47:46 -07:00
Sorrel Bri
155cca9110 patch useEffect dependencies on Game page 2020-05-04 23:44:38 -07:00
Sorrel Bri
7aed5b7bf9 connect new Game module to service and update frontend connections 2020-05-04 23:22:50 -07:00
Sorrel Bri
a02576532d refactor server side socket for connection with new Game service 2020-05-02 23:22:57 -07:00
Sorrel Bri
b8eb3770d9 deprecate Game.v1 2020-05-02 23:00:20 -07:00
Sorrel Bri
a99426e03f Game.returnToMove(x) where x >= 0 returns game state at move x 2020-05-02 22:04:22 -07:00
Sorrel Bri
d51e3f72f4 init Game.returnToMove(x) where x < 0 rewinds x number of moves 2020-05-02 21:25:54 -07:00
Sorrel Bri
bdeb9c9d86 init Game.returnToMove 2020-05-02 21:11:54 -07:00
Sorrel Bri
88e51fdbf9 add clearKo to Game called after makeMove is verified as legal 2020-05-02 20:30:28 -07:00
Sorrel Bri
2237e344c1 patch ko bug preventing snapback 2020-05-02 20:18:21 -07:00
Sorrel Bri
5ad997d276 add ko check to makeCaptures 2020-05-02 01:35:41 -07:00
Sorrel Bri
9646656f7a patch checkLegal logic for capturing Set to support snapback 2020-05-01 22:58:59 -07:00
Sorrel Bri
1142c1d448 refactor Point.capturing to Set to handle adjascent stones in same group 2020-04-30 23:45:19 -07:00
Sorrel Bri
e9d94d1fad green all capture tests 2020-04-30 23:12:11 -07:00
Sorrel Bri
1a1e5121d7 refactor Game.v2 to remove side effects 2020-04-30 23:01:30 -07:00
Sorrel Bri
a5fbeea929 add makeCaptures to Point 2020-04-29 22:24:27 -07:00
Sorrel Bri
13a882d212 add checkCaptures to Point, called during makeMove 2020-04-29 21:02:01 -07:00
Sorrel Bri
96af52823d add logic to prevent move at group's final liberty 2020-04-26 17:45:35 -07:00
Sorrel Bri
9164ee5987 add support for group joining moves at points with no liberties 2020-04-26 00:13:25 -07:00
Sorrel Bri
4a90b933a7 add logic for legal check that prevents moves at points with no liberties 2020-04-24 00:16:31 -07:00
Sorrel Bri
089783c82d add setLiberties to Point and refactor groups object to include liberties for each group 2020-04-23 00:24:12 -07:00
Sorrel Bri
e553601af7 add join group logic to Point, triggered by Game.makeMove 2020-04-22 23:17:46 -07:00
Sorrel Bri
b1f29f3d2d refactor getNeighbors in initBoard for array index lookup instead of boardState.find(...) 2020-04-20 17:39:52 -07:00
Sorrel Bri
bdfb6ebe85 add getNeighbors to initBoard() allowing Points to directly reference neighbors 2020-04-20 17:34:49 -07:00
Sorrel Bri
a54d4bf7e4 stub makeMove in Game v2 API 2020-04-19 22:58:26 -07:00
Sorrel Bri
37db281d08 add getMeta to Game API 2020-04-19 22:26:04 -07:00
Sorrel Bri
41e6e662e9 initialize board for all board sizes and handicap amounts 2020-04-19 15:02:09 -07:00
Sorrel Bri
8fb1b80cb6 add initGame to v2 Game logic service 2020-04-18 20:04:00 -07:00
Sorrel Bri
e53a8f4f2a Merge branch 'game_logic' 2020-04-17 17:17:18 -07:00
Sorrel Bri
7408e409aa patch userValidator middleware to remove deprecated sanitize function 2020-04-17 17:14:31 -07:00
Sorrel Bri
c533c7837c patch failing frontend tests 2020-04-17 00:01:04 -07:00
Sorrel Bri
aa2c9084e5 readd react and react-dom to ci pipeline 2020-04-16 23:39:29 -07:00
Sorrel Bri
dde97e2a1f patch lerna bootstrap command 2020-04-16 23:19:39 -07:00
Sorrel Bri
2c39fe1156 patch Game logic neighboring stone bug 2020-04-16 20:28:40 -07:00
Sorrel Bri
a1f7c45536 update readme with setup 2020-04-15 23:16:00 -07:00
Sorrel Bri
f2c0ce62fe patch link underline styles 2020-04-13 18:58:55 -07:00
Sorrel Bri
176c087599 style RoomDetail component 2020-04-12 22:54:20 -07:00
Sorrel Bri
0dbf0ed844 patch to Game button style 2020-04-12 14:50:03 -07:00
Sorrel Bri
a35cad24df patch link style 2020-04-12 14:22:10 -07:00
Sorrel Bri
c03ee1020d stub RoomDetail component 2020-04-12 00:19:28 -07:00
Sorrel Bri
c54dcb41a8 stub RoomDetail component 2020-04-12 00:19:21 -07:00
sorrelbri
a9243840a6 refactor Room button styles to remove cruft and clean up 2020-04-11 21:43:48 -07:00
sorrelbri
a9025aca8f style small Room button 2020-04-11 21:39:09 -07:00
sorrelbri
0368325199 refactor to remove crufty renderOpenGame function from Game Button 2020-04-10 20:25:11 -07:00
sorrelbri
bf836d50d7 refactor to remove crufty renderOpenGame function from Game Button 2020-04-10 20:19:58 -07:00
sorrelbri
16868aea1a refactor to remove crufty renderOpenGame function from Game Button 2020-04-10 20:05:42 -07:00
sorrelbri
8a60d82832 patch styling of game room drop shadows, refactor GameButton for open games 2020-04-10 20:03:27 -07:00
sorrelbri
a69b48d238 clean unused package.json scripts 2020-04-10 19:10:34 -07:00
sorrelbri
f8451ce62d clean unused package.json scripts 2020-04-10 19:09:43 -07:00
sorrelbri
d628a8dd48 add react and react-dom to play-node-go package.json 2020-03-30 11:37:48 -07:00
sorrelbri
3768297f7f add react and react-dom to play-node-go package.json 2020-03-30 11:33:31 -07:00
sorrelbri
30f65b85b3 add react and react-dom to play-node-go package.json 2020-03-30 11:33:07 -07:00
sorrelbri
f1d480c203 push build in frontend deploy 2020-03-30 09:26:24 -07:00
sorrelbri
d5fe96e0e9 fix vulnerabilities in play-node-go dependencies 2020-03-30 00:24:00 -07:00
sorrelbri
69c59683e4 fix deploy bug in circleci 2020-03-29 23:32:12 -07:00
sorrelbri
46f82e658c add subtree to deploy commands in circleci 2020-03-29 23:30:14 -07:00
sorrelbri
6a17b6b872 collapse steps in circleci 2020-03-29 23:26:09 -07:00
sorrelbri
eb7adf37e1 add machine-enabled to deploy step 2020-03-29 23:23:51 -07:00
sorrelbri
c1a9fe7204 add circleci env variables to deploy step 2020-03-29 23:22:43 -07:00
sorrelbri
4d71c8fdaf add audit-level flag to ci to bypass low-level dev-dependency issues 2020-03-29 20:56:16 -07:00
sorrelbri
7dffc9dbc7 fix minimist version 2020-03-29 20:45:20 -07:00
sorrelbri
d6cfda9d8b add deployment to ci pipeline 2020-03-29 20:27:38 -07:00
sorrelbri
3789401918 raise react dependencies to root package.json 2020-02-10 22:23:44 -08:00
sorrelbri
45cbe56564 raise react dependencies to root package.json 2020-02-10 22:21:31 -08:00
sorrelbri
5bd9ca1de3 undo previous commit - readd npm i react & react-dom to circleci/config 2020-02-10 22:17:11 -08:00
sorrelbri
e42a300e36 remove npm i react & react-dom 2020-02-10 22:12:52 -08:00
sorrelbri
316396f218 remove psql references from circleci config and test suite 2020-02-10 22:09:07 -08:00
sorrelbri
8f18d0685c remove psql references from circleci config and test suite 2020-02-10 22:07:35 -08:00
sorrelbri
7b63a2f76d remove psql references from circleci config and test suite 2020-02-10 22:05:33 -08:00
sorrelbri
7481178abd config psql in circleci config 2020-02-08 17:26:10 -08:00
sorrelbri
fbea9767e3 config psql in circleci config 2020-02-08 17:24:16 -08:00
sorrelbri
3c34b6846a config psql in circleci config 2020-02-08 17:21:13 -08:00
sorrelbri
a9dbf359fc config psql in circleci config 2020-02-08 16:10:53 -08:00
sorrelbri
d29e488354 config psql in circleci config 2020-02-07 20:44:27 -08:00
sorrelbri
22907dc7c5 config psql in circleci config 2020-02-07 20:43:17 -08:00
sorrelbri
5589b1463e config psql in circleci config 2020-02-07 20:42:04 -08:00
sorrelbri
3d20606999 config psql in circleci config 2020-02-07 20:40:32 -08:00
sorrelbri
d79151c707 config psql in circleci config 2020-02-07 20:38:01 -08:00
sorrelbri
68f8f4bf50 config psql in circleci config 2020-02-07 19:21:19 -08:00
sorrelbri
b044bb57d2 add postgres image to circleci config 2020-02-07 18:35:43 -08:00
sorrelbri
8b1ef3dbff add react and react-dom install to circleci config 2020-02-07 18:22:32 -08:00
sorrelbri
6ce03c3449 patch reducer compile warnings 2020-02-07 18:15:22 -08:00
sorrelbri
3e2a956634 patch reducer compile warnings 2020-02-07 18:03:28 -08:00
sorrelbri
401076cd50 patch reducer compile warnings 2020-02-07 17:59:28 -08:00
sorrelbri
24a8a4e991 patch reducer compile warnings 2020-02-07 17:55:25 -08:00
sorrelbri
3f15e48eef remove flow from app 2020-02-07 17:51:09 -08:00
sorrelbri
bfdad6e3aa add build step to circleci config 2020-02-07 17:41:28 -08:00
sorrelbri
c3842a0589 move flow to dev dependencies 2020-02-07 16:23:58 -08:00
sorrelbri
bee61a1f31 add @babel/preset-flow config options all equals true 2020-02-07 16:16:47 -08:00
sorrelbri
2c5a0534d7 patch React compilation warnings and useEffect dependencies 2020-02-07 16:11:01 -08:00
sorrelbri
4c7fa1512d patch React compilation warnings and useEffect dependencies 2020-02-07 16:01:54 -08:00
sorrelbri
9cac33cef3 update node image in circleci config 2020-02-06 11:17:02 -08:00
sorrelbri
8b26417827 revert to npm i and add lerna script to paackage.json 2020-02-06 11:13:55 -08:00
sorrelbri
97c4f11348 revert to npm i and add lerna script to paackage.json 2020-02-06 11:12:40 -08:00
sorrelbri
ebf33c7337 revert to npm i and add lerna script to paackage.json 2020-02-06 11:05:56 -08:00
sorrelbri
4bbe50cb57 revert to npm i and add lerna script to paackage.json 2020-02-06 11:01:21 -08:00
sorrelbri
d1cece19d9 revert to npm i and add lerna script to paackage.json 2020-02-06 10:59:52 -08:00
sorrelbri
b766cdb59a revert to npm i and add lerna script to paackage.json 2020-02-06 10:56:29 -08:00
sorrelbri
0dc9458b93 revert to npm i and add lerna script to paackage.json 2020-02-06 10:55:16 -08:00
sorrelbri
89eab6b4d8 change npm i to lerna bootstrap 2020-02-06 10:50:25 -08:00
sorrelbri
d5a1afaa02 update ci config from npm scripts to lerna scripts 2020-02-05 19:13:07 -08:00
sorrelbri
4b609b4e28 update ci config from npm scripts to lerna scripts 2020-02-05 19:11:43 -08:00
sorrelbri
d1f09a5494 restore circleci config with create_concatenated_package_lock command 2020-02-05 17:25:14 -08:00
sorrelbri
0c93989644 restore circleci config with create_concatenated_package_lock command 2020-02-05 17:23:42 -08:00
sorrelbri
e050f295ae collect dev dependencies in root package.json 2020-02-05 17:14:16 -08:00
sorrelbri
29f3385ba8 collect dev dependencies in root package.json 2020-02-05 17:07:08 -08:00
sorrelbri
e69aa9b326 package server and play-node-go 2020-02-05 16:54:09 -08:00
Sorrel Bri
7223d98c36 init lerna 2020-02-05 16:45:28 -08:00
Sorrel Bri
497db97161 stub nav form styles and fix board span bug 2020-02-05 16:45:28 -08:00
Sorrel Bri
a29431e67d decoupled show Auth form logic from Home sidebar for use in other sidebars on Auth errors 2020-02-05 16:45:27 -08:00
Sorrel Bri
72fb0ae243 style player area on game screen 2020-02-05 16:45:27 -08:00
Sorrel Bri
72a533a285 stub render board state from make_move 2020-02-05 16:45:27 -08:00
Sorrel Bri
9b82236283 stub make move socket message to add move to game 2020-02-05 16:45:27 -08:00
Sorrel Bri
23cd485cbb refactor make move to serve game meta data with game record 2020-02-05 16:45:27 -08:00
Sorrel Bri
76f29cf9d3 check legality of move prior to posting 2020-02-05 16:45:27 -08:00
Sorrel Bri
be294f669c stub make move to game service 2020-02-05 16:45:27 -08:00
Sorrel Bri
95724c39db stub game services to store active games 2020-02-05 16:45:27 -08:00
Sorrel Bri
4851f31744 stub board on game page 2020-02-05 16:45:26 -08:00
Sorrel Bri
e0830bed5c stub Game components 2020-02-05 16:45:26 -08:00
Sorrel Bri
f37def2688 add seed moves 2020-02-05 16:45:26 -08:00
Sorrel Bri
fb9bc0d8cf stub game connection via socket room 2020-02-05 16:45:26 -08:00
Sorrel Bri
fd72f135dd stub game connection via socket room 2020-02-05 16:45:26 -08:00
Sorrel Bri
d6ec6c3163 patch to cleanup reducer directory filenames 2020-02-05 16:45:26 -08:00
Sorrel Bri
7bca0e9a04 stub default falsey values for initState function to prevent crash on 500 2020-02-05 16:45:25 -08:00
Sorrel Bri
ffda6a8164 create Loading component with pinwheel ko animation 2020-02-05 16:45:25 -08:00
Sorrel Bri
45b3604fb2 add table styling to game buttons in game room 2020-02-05 16:45:25 -08:00
Sorrel Bri
3195a73644 refactor all jsx to BEM 2020-02-05 16:45:25 -08:00
Sorrel Bri
6e0ec94676 debug room socket connect dispatch 2020-02-05 16:45:25 -08:00
Sorrel Bri
a0d721ab9a refactor sockets to be launched with namespace by components 2020-02-05 16:45:25 -08:00
Sorrel Bri
61b13ab97a refacor sockets on server and client to utilize dispatch for socket meessages 2020-02-05 16:45:25 -08:00
Sorrel Bri
86a75d146e stub socket connection to game id namespace 2020-02-05 16:45:25 -08:00
Sorrel Bri
a4a9a72ece stub socket connection to game id namespace 2020-02-05 16:45:25 -08:00
Sorrel Bri
026af700df stub games/:id endpoint to join game socket room 2020-02-05 16:45:24 -08:00
Sorrel Bri
b8681e8ef5 refactor join_game_request to require unique users 2020-02-05 16:45:24 -08:00
Sorrel Bri
11855c57d4 connect join_game_request socket at client and server 2020-02-05 16:45:24 -08:00
Sorrel Bri
6757553785 patch rooms/:id failure to return multiple games 2020-02-05 16:45:24 -08:00
Sorrel Bri
59b5d31c35 patch no-query bug in rooms/ endpoint 2020-02-05 16:45:24 -08:00
Sorrel Bri
8589976264 refactor api rooms/:id endpoint to deliver current room without table join 2020-02-05 16:45:24 -08:00
Sorrel Bri
775e1fc050 stub room display with messages and game link 2020-02-05 16:45:24 -08:00
Sorrel Bri
38014ac497 dispatch all room data to state upon rooms/:id 2020-02-05 16:45:24 -08:00
Sorrel Bri
f94f4fd2c0 refactor api room endpoint to return unjoined game room data 2020-02-05 16:45:23 -08:00
Sorrel Bri
695d1ea2b1 add SET_MESSAGES to reducer 2020-02-05 16:45:23 -08:00
Sorrel Bri
6467004a89 refactor services and reducers to parse JSON before data hits dispatch 2020-02-05 16:45:23 -08:00
Sorrel Bri
83d3abf188 add client routing to rooms/:id, move socket(rooms/roomId) connect from Home page to Room page 2020-02-05 16:45:23 -08:00
Sorrel Bri
661fc74df2 serve room with message data from api/v1/rooms/:id 2020-02-05 16:45:23 -08:00
Sorrel Bri
aa0c850aab serve room with joined game data from api/v1/rooms/:id 2020-02-05 16:45:23 -08:00
Sorrel Bri
c001b939d1 stub room socket connection (client + server) 2020-02-05 16:45:23 -08:00
Sorrel Bri
e8867b9634 add message table with migration 2020-02-05 16:45:23 -08:00
Sorrel Bri
464a8ffbf4 add move table with migration 2020-02-05 16:45:22 -08:00
Sorrel Bri
f3d21e0444 add game table with seed and migration 2020-02-05 16:45:22 -08:00
Sorrel Bri
522782e3a5 add time_setting table with seed and migration 2020-02-05 16:45:22 -08:00
Sorrel Bri
d9edea315a patch remove unnecessary socket.io cookie 2020-02-05 16:45:22 -08:00
Sorrel Bri
3e8e99a816 Merge branch 'gameroom_endpoint' 2020-02-05 16:45:22 -08:00
Sorrel Bri
1ef3adc3d7 patch https cookies in production 2020-02-05 16:45:22 -08:00
Sorrel Bri
0fb6a7bfb8 patch https cookies in production 2020-02-05 16:45:22 -08:00
Sorrel Bri
05b7f338ff hook SET_ROOMS into rooms Service 2020-02-05 16:45:22 -08:00
Sorrel Bri
8ad488c3ce add SET_ROOMS dispatch 2020-02-05 16:45:22 -08:00
Sorrel Bri
6114c20e2c serve all public rooms at rooms index 2020-02-05 16:45:22 -08:00
Sorrel Bri
762c4d93a8 stub rooms index route and controller 2020-02-05 16:45:22 -08:00
Sorrel Bri
47856773eb add room migration and simple seeds 2020-02-05 16:45:21 -08:00
Sorrel Bri
d706a9d137 serve basic page at api endpoint to redirect user 2020-02-05 16:45:21 -08:00
Sorrel Bri
6669d3f16b patch component references in pages/ 2020-02-05 16:45:21 -08:00
Sorrel Bri
cb3fb31b24 patch reference to MainWrapper component 2020-02-05 16:45:21 -08:00
Sorrel Bri
b06b490d6f rename RoomArchive components to differentiate from NewsArchive 2020-02-05 16:45:21 -08:00
Sorrel Bri
9dc91de5ed stub home and rooms sidebar components 2020-02-05 16:45:21 -08:00
Sorrel Bri
54abeb65fc patch to hide password input on login form 2020-02-05 16:45:21 -08:00
Sorrel Bri
00757ac2d2 patch frontend api endpoint in config 2020-02-05 16:45:21 -08:00
Sorrel Bri
2f9cb987ba add manual set Access Control Allow Origin header 2020-02-05 16:45:21 -08:00
Sorrel Bri
891ba5ab2f patch CORS 2020-02-05 16:45:21 -08:00
Sorrel Bri
188cd0b966 patch config for production 2020-02-05 16:45:20 -08:00
Sorrel Bri
6f86007632 patch config for production 2020-02-05 16:45:20 -08:00
Sorrel Bri
a9a10c1adb add login hook to frontend 2020-02-05 16:45:20 -08:00
Sorrel Bri
18d351d698 add validation for username and email on signup 2020-02-05 16:45:20 -08:00
Sorrel Bri
2c9aca4856 add client side password confirmation check 2020-02-05 16:45:20 -08:00
Sorrel Bri
b8b7890e4f refactor auth and api/index to ensure token verified and sent to dispatch ADD_USER 2020-02-05 16:45:20 -08:00
Sorrel Bri
465a3342f2 refactor auth and api/index to ensure token verified and sent to dispatch ADD_USER 2020-02-05 16:45:20 -08:00
Sorrel Bri
5fa43f8b7e refactor to fetch to ensure proper cookie storage 2020-02-05 16:45:20 -08:00
Sorrel Bri
080ab8a994 serve user from verified jwt 2020-02-05 16:45:20 -08:00
Sorrel Bri
97efe2314b add FormError component to display auth errors 2020-02-05 16:45:19 -08:00
Sorrel Bri
969e44e8e5 hook reducer into signup post response 2020-02-05 16:45:19 -08:00
Sorrel Bri
e4d4652d1b patch server production env allowed origin 2020-02-05 16:45:19 -08:00
Sorrel Bri
42d10a55cd patch signup post for existing user to return 409 2020-02-05 16:45:19 -08:00
Sorrel Bri
9b02705a91 hook reducer into auth/signup request service 2020-02-05 16:45:19 -08:00
Sorrel Bri
78ba0a035e stub styling of side and main content 2020-02-05 16:45:19 -08:00
Sorrel Bri
9014564180 stub sidebar component to display auth form 2020-02-05 16:45:19 -08:00
Sorrel Bri
fee04ec269 stub tests for page components 2020-02-05 16:45:18 -08:00
Sorrel Bri
34959d8e44 debug password hashing function 2020-02-05 16:45:18 -08:00
Sorrel Bri
36c75e9126 stub frontend requests to auth endpoints 2020-02-05 16:45:18 -08:00
Sorrel Bri
1e706fe20f add validation and sanitization for auth/. posts 2020-02-05 16:45:18 -08:00
Sorrel Bri
f35bc165fd patch to remove timeout from login test 2020-02-05 16:45:18 -08:00
Sorrel Bri
98fdf6926c add login verification of user 2020-02-05 16:45:17 -08:00
Sorrel Bri
30c0f9ad97 patch knexfile name to run migrations 2020-02-05 16:45:17 -08:00
Sorrel Bri
8af3685cba add password hash to new user 2020-02-05 16:45:17 -08:00
Sorrel Bri
9a0045d99b stub new user sign up 2020-02-05 16:45:17 -08:00
Sorrel Bri
7b4b48050d add sign jwt function and hook to auth/signup 2020-02-05 16:45:17 -08:00
Sorrel Bri
a629225fae stub auth routes and controllers 2020-02-05 16:45:17 -08:00
Sorrel Bri
cd1273e645 debug migrations path 2020-02-05 16:45:17 -08:00
Sorrel Bri
3a5125893b debug produciton db connection 2020-02-05 16:45:17 -08:00
Sorrel Bri
458876a2b4 add migrate command to server Procfile 2020-02-05 16:45:17 -08:00
Sorrel Bri
1661b3fe65 add create user table migration 2020-02-05 16:45:17 -08:00
Sorrel Bri
413cf63192 deploy subdirectories successfully 2020-02-05 16:45:16 -08:00
Sorrel Bri
d7cf6c44eb return README to root directory 2020-02-05 16:45:16 -08:00
Sorrel Bri
7ef212643d move react module into subdirectory 2020-02-05 16:45:16 -08:00
Sorrel Bri
18d744a7a5 confirm successful deployment 2020-02-05 16:45:16 -08:00
Sorrel Bri
b62458218b add runtime env for production enironment 2020-02-05 16:45:16 -08:00
Sorrel Bri
173bb8766c add production endpoints to react config 2020-02-05 16:45:16 -08:00
Sorrel Bri
48546c9448 change server Procfile to npm start 2020-02-05 16:45:16 -08:00
Sorrel Bri
5977c686a5 remove path from server Procfile 2020-02-05 16:45:16 -08:00
Sorrel Bri
d2fe74ffec add path to server Procfile 2020-02-05 16:45:16 -08:00
Sorrel Bri
f3208819c8 add node to server Procfile 2020-02-05 16:45:16 -08:00
Sorrel Bri
07070439f1 empty react Procfile 2020-02-05 16:45:16 -08:00
Sorrel Bri
cd22eb89e1 add run to Procfile 2020-02-05 16:45:15 -08:00
Sorrel Bri
328bf9128f remove npm from start command 2020-02-05 16:45:15 -08:00
Sorrel Bri
56fbc34ba3 redeploy with namespaced scripts 2020-02-05 16:45:15 -08:00
Sorrel Bri
65bffa11a1 add Procfiles for deploy 2020-02-05 16:45:15 -08:00
Sorrel Bri
e42118d6aa config flow 2020-02-05 16:45:15 -08:00
Sorrel Bri
cc6b00b72b patch page function tests, import pages in App 2020-02-05 16:45:15 -08:00
Sorrel Bri
2ae07a1a99 stub page components 2020-02-05 16:45:15 -08:00
Sorrel Bri
24c4130f7f stub mocha/chai for testing server 2020-02-05 16:45:15 -08:00
Sorrel Bri
6a373a1172 stub react pages structure 2020-02-05 16:45:15 -08:00
Sorrel Bri
34dbdaab7b stub middleware (including socket), controllers architecture 2020-02-05 16:45:15 -08:00
Sorrel Bri
64dc036296 connect socket 2020-02-05 16:45:15 -08:00
Sorrel Bri
aeb64cb6a5 connect socket 2020-02-05 16:45:14 -08:00
Sorrel Bri
840fae2bf7 connect to db with knex 2020-02-05 16:45:14 -08:00
Sorrel Bri
e83ca4a1b5 restrict CORS to env defined origin 2020-02-05 16:45:14 -08:00
Sorrel Bri
ea878c9057 configure simple CORS 2020-02-05 16:45:14 -08:00
Sorrel Bri
b2d9eaca0d stub basic connection 2020-02-05 16:45:14 -08:00
Sorrel Bri
dc7b895dd6 add react frontend structure 2020-02-05 16:45:14 -08:00
Sorrel Bri
edf520ba79 remove views/, add controllers/ 2020-02-05 16:45:14 -08:00
Sorrel Bri
3ca3e28d45 git init 2020-02-05 16:45:13 -08:00
Sorrel Bri
baa843feeb init lerna 2020-02-05 16:45:13 -08:00
Sorrel Bri
d087671cf2 stub nav form styles and fix board span bug 2020-02-05 16:45:13 -08:00
Sorrel Bri
d58eb95c76 decoupled show Auth form logic from Home sidebar for use in other sidebars on Auth errors 2020-02-05 16:45:13 -08:00
Sorrel Bri
eb5df6069c style player area on game screen 2020-02-05 16:45:13 -08:00
Sorrel Bri
ca98733365 stub render board state from make_move 2020-02-05 16:45:13 -08:00
Sorrel Bri
2d04f40bd6 stub make move socket message to add move to game 2020-02-05 16:45:13 -08:00
Sorrel Bri
8310307460 refactor make move to serve game meta data with game record 2020-02-05 16:45:13 -08:00
Sorrel Bri
d2d34ff658 check legality of move prior to posting 2020-02-05 16:45:13 -08:00
Sorrel Bri
b38637d3c8 stub make move to game service 2020-02-05 16:45:13 -08:00
Sorrel Bri
d83a58dcb4 stub game services to store active games 2020-02-05 16:45:12 -08:00
Sorrel Bri
b294e009d3 stub board on game page 2020-02-05 16:45:12 -08:00
Sorrel Bri
9a20746b78 stub Game components 2020-02-05 16:45:12 -08:00
Sorrel Bri
3ecb5889c1 add seed moves 2020-02-05 16:45:12 -08:00
Sorrel Bri
5651e1fa4c stub game connection via socket room 2020-02-05 16:45:12 -08:00
Sorrel Bri
e02746cb67 stub game connection via socket room 2020-02-05 16:45:12 -08:00
Sorrel Bri
6a5ae9cc71 patch to cleanup reducer directory filenames 2020-02-05 16:45:12 -08:00
Sorrel Bri
638e57dd29 stub default falsey values for initState function to prevent crash on 500 2020-02-05 16:45:12 -08:00
Sorrel Bri
9f5842703b create Loading component with pinwheel ko animation 2020-02-05 16:45:12 -08:00
Sorrel Bri
a58c2ed04a add table styling to game buttons in game room 2020-02-05 16:45:12 -08:00
Sorrel Bri
c853cb2ad8 refactor all jsx to BEM 2020-02-05 16:45:11 -08:00
Sorrel Bri
e1515db88c debug room socket connect dispatch 2020-02-05 16:45:11 -08:00
Sorrel Bri
16468fb907 refactor sockets to be launched with namespace by components 2020-02-05 16:45:11 -08:00
Sorrel Bri
6f8b68a354 refacor sockets on server and client to utilize dispatch for socket meessages 2020-02-05 16:45:11 -08:00
Sorrel Bri
7f1386f7a7 stub socket connection to game id namespace 2020-02-05 16:45:11 -08:00
Sorrel Bri
c033e3558a stub socket connection to game id namespace 2020-02-05 16:45:11 -08:00
Sorrel Bri
07af4e7be5 stub games/:id endpoint to join game socket room 2020-02-05 16:45:11 -08:00
Sorrel Bri
d7c024e286 refactor join_game_request to require unique users 2020-02-05 16:45:11 -08:00
Sorrel Bri
a3180b0bde connect join_game_request socket at client and server 2020-02-05 16:45:11 -08:00
Sorrel Bri
f6d4ed49ca patch rooms/:id failure to return multiple games 2020-02-05 16:45:10 -08:00
Sorrel Bri
e3ce466bb4 patch no-query bug in rooms/ endpoint 2020-02-05 16:45:10 -08:00
Sorrel Bri
34c3b34ada refactor api rooms/:id endpoint to deliver current room without table join 2020-02-05 16:45:10 -08:00
Sorrel Bri
c21807adc8 stub room display with messages and game link 2020-02-05 16:45:10 -08:00
Sorrel Bri
9c3fbb9572 dispatch all room data to state upon rooms/:id 2020-02-05 16:45:10 -08:00
Sorrel Bri
40dbe487e1 refactor api room endpoint to return unjoined game room data 2020-02-05 16:45:10 -08:00
Sorrel Bri
ebd76baac6 add SET_MESSAGES to reducer 2020-02-05 16:45:10 -08:00
Sorrel Bri
95e54df9fd refactor services and reducers to parse JSON before data hits dispatch 2020-02-05 16:45:10 -08:00
Sorrel Bri
16adb58cb0 add client routing to rooms/:id, move socket(rooms/roomId) connect from Home page to Room page 2020-02-05 16:45:09 -08:00
Sorrel Bri
007a5c96d6 serve room with message data from api/v1/rooms/:id 2020-02-05 16:45:09 -08:00
Sorrel Bri
b3d05639ad serve room with joined game data from api/v1/rooms/:id 2020-02-05 16:45:09 -08:00
Sorrel Bri
5867c0ddd9 stub room socket connection (client + server) 2020-02-05 16:45:09 -08:00
Sorrel Bri
603201d45c add message table with migration 2020-02-05 16:45:09 -08:00
Sorrel Bri
33141284eb add move table with migration 2020-02-05 16:45:09 -08:00
Sorrel Bri
3f8d898f43 add game table with seed and migration 2020-02-05 16:45:09 -08:00
Sorrel Bri
99a616fe37 add time_setting table with seed and migration 2020-02-05 16:45:09 -08:00
Sorrel Bri
5addf9ace0 patch remove unnecessary socket.io cookie 2020-02-05 16:45:09 -08:00
Sorrel Bri
8aa84d4abe Merge branch 'gameroom_endpoint' 2020-02-05 16:45:09 -08:00
Sorrel Bri
d3b3028adf patch https cookies in production 2020-02-05 16:45:09 -08:00
Sorrel Bri
9729d15ff2 patch https cookies in production 2020-02-05 16:45:08 -08:00
Sorrel Bri
cba61a78e9 hook SET_ROOMS into rooms Service 2020-02-05 16:45:08 -08:00
Sorrel Bri
5a7f3bc177 add SET_ROOMS dispatch 2020-02-05 16:45:08 -08:00
Sorrel Bri
36eeb8202b serve all public rooms at rooms index 2020-02-05 16:45:08 -08:00
Sorrel Bri
acbc9c901b stub rooms index route and controller 2020-02-05 16:45:08 -08:00
Sorrel Bri
a30d4f3a45 add room migration and simple seeds 2020-02-05 16:45:08 -08:00
Sorrel Bri
8bd52fd993 serve basic page at api endpoint to redirect user 2020-02-05 16:45:08 -08:00
Sorrel Bri
8933e25156 patch component references in pages/ 2020-02-05 16:45:08 -08:00
Sorrel Bri
2dc17a9588 patch reference to MainWrapper component 2020-02-05 16:45:08 -08:00
Sorrel Bri
230dadd3d2 rename RoomArchive components to differentiate from NewsArchive 2020-02-05 16:45:07 -08:00
Sorrel Bri
101531e727 stub home and rooms sidebar components 2020-02-05 16:45:07 -08:00
Sorrel Bri
c0aff888f5 patch to hide password input on login form 2020-02-05 16:45:07 -08:00
Sorrel Bri
68fedb4d2f patch frontend api endpoint in config 2020-02-05 16:45:07 -08:00
Sorrel Bri
b69edb9182 add manual set Access Control Allow Origin header 2020-02-05 16:45:07 -08:00
Sorrel Bri
ca43baf181 patch CORS 2020-02-05 16:45:07 -08:00
Sorrel Bri
3cbba5d072 patch config for production 2020-02-05 16:45:07 -08:00
Sorrel Bri
b1cf43d84b patch config for production 2020-02-05 16:45:07 -08:00
Sorrel Bri
d20c070c02 add login hook to frontend 2020-02-05 16:45:07 -08:00
Sorrel Bri
3b868cec4e add validation for username and email on signup 2020-02-05 16:45:06 -08:00
Sorrel Bri
5fbfc28024 add client side password confirmation check 2020-02-05 16:45:06 -08:00
Sorrel Bri
e90abc283a refactor auth and api/index to ensure token verified and sent to dispatch ADD_USER 2020-02-05 16:45:06 -08:00
Sorrel Bri
5c8da62edb refactor auth and api/index to ensure token verified and sent to dispatch ADD_USER 2020-02-05 16:45:06 -08:00
Sorrel Bri
d77216d762 refactor to fetch to ensure proper cookie storage 2020-02-05 16:45:06 -08:00
Sorrel Bri
1ee4c28d4c serve user from verified jwt 2020-02-05 16:45:06 -08:00
Sorrel Bri
c2a0ad2ff2 add FormError component to display auth errors 2020-02-05 16:45:06 -08:00
Sorrel Bri
8c6e38ec66 hook reducer into signup post response 2020-02-05 16:45:05 -08:00
Sorrel Bri
c905dd05b8 patch server production env allowed origin 2020-02-05 16:45:05 -08:00
Sorrel Bri
e0a336bd35 patch signup post for existing user to return 409 2020-02-05 16:45:05 -08:00
Sorrel Bri
0e2fe08666 hook reducer into auth/signup request service 2020-02-05 16:45:05 -08:00
Sorrel Bri
9d10ed4fe2 stub styling of side and main content 2020-02-05 16:45:05 -08:00
Sorrel Bri
3068b94c5b stub sidebar component to display auth form 2020-02-05 16:45:05 -08:00
Sorrel Bri
8d3991ef55 stub tests for page components 2020-02-05 16:45:05 -08:00
Sorrel Bri
16d5c97e3f debug password hashing function 2020-02-05 16:45:05 -08:00
Sorrel Bri
fea8c9035f stub frontend requests to auth endpoints 2020-02-05 16:45:04 -08:00
Sorrel Bri
02d408adba add validation and sanitization for auth/. posts 2020-02-05 16:45:04 -08:00
Sorrel Bri
add9e49bb6 patch to remove timeout from login test 2020-02-05 16:45:04 -08:00
Sorrel Bri
6033a99223 add login verification of user 2020-02-05 16:45:04 -08:00
Sorrel Bri
c480d986d2 patch knexfile name to run migrations 2020-02-05 16:45:04 -08:00
Sorrel Bri
52b31b7fe2 add password hash to new user 2020-02-05 16:45:04 -08:00
Sorrel Bri
89eca4a5c1 stub new user sign up 2020-02-05 16:45:04 -08:00
Sorrel Bri
323ea5d24c add sign jwt function and hook to auth/signup 2020-02-05 16:45:04 -08:00
Sorrel Bri
85ebffcdcf stub auth routes and controllers 2020-02-05 16:45:03 -08:00
Sorrel Bri
118989a9cc debug migrations path 2020-02-05 16:45:03 -08:00
Sorrel Bri
a451144581 debug produciton db connection 2020-02-05 16:45:03 -08:00
Sorrel Bri
52e6271738 add migrate command to server Procfile 2020-02-05 16:45:03 -08:00
Sorrel Bri
55104ba3ff add create user table migration 2020-02-05 16:45:03 -08:00
Sorrel Bri
d5d7121a71 deploy subdirectories successfully 2020-02-05 16:45:03 -08:00
Sorrel Bri
5adf8b4690 return README to root directory 2020-02-05 16:45:03 -08:00
Sorrel Bri
5f6889439b move react module into subdirectory 2020-02-05 16:45:03 -08:00
Sorrel Bri
6011354d67 confirm successful deployment 2020-02-05 16:45:03 -08:00
Sorrel Bri
f6b52000f3 add runtime env for production enironment 2020-02-05 16:45:03 -08:00
Sorrel Bri
3d81aeda5e add production endpoints to react config 2020-02-05 16:45:03 -08:00
Sorrel Bri
9ea54db3e2 change server Procfile to npm start 2020-02-05 16:45:02 -08:00
Sorrel Bri
962d0f9bcf remove path from server Procfile 2020-02-05 16:45:02 -08:00
Sorrel Bri
ebe945245c add path to server Procfile 2020-02-05 16:45:02 -08:00
Sorrel Bri
94c6a97265 add node to server Procfile 2020-02-05 16:45:02 -08:00
Sorrel Bri
489fde4972 empty react Procfile 2020-02-05 16:45:02 -08:00
Sorrel Bri
b91f468810 add run to Procfile 2020-02-05 16:45:02 -08:00
Sorrel Bri
2b74813c14 remove npm from start command 2020-02-05 16:45:02 -08:00
Sorrel Bri
2f65341daa redeploy with namespaced scripts 2020-02-05 16:45:02 -08:00
Sorrel Bri
0356002022 add Procfiles for deploy 2020-02-05 16:45:02 -08:00
Sorrel Bri
ba1211d0d6 config flow 2020-02-05 16:45:02 -08:00
Sorrel Bri
538e911b21 patch page function tests, import pages in App 2020-02-05 16:45:02 -08:00
Sorrel Bri
7d4ffc4cc0 stub page components 2020-02-05 16:45:02 -08:00
Sorrel Bri
101def1cac stub mocha/chai for testing server 2020-02-05 16:45:01 -08:00
Sorrel Bri
fa9b049e58 stub react pages structure 2020-02-05 16:45:01 -08:00
Sorrel Bri
9f0875f483 stub middleware (including socket), controllers architecture 2020-02-05 16:45:01 -08:00
Sorrel Bri
397617eb34 connect socket 2020-02-05 16:45:01 -08:00
Sorrel Bri
395e2b1d86 connect socket 2020-02-05 16:45:01 -08:00
Sorrel Bri
90e078fa86 connect to db with knex 2020-02-05 16:45:01 -08:00
Sorrel Bri
8a203cb096 restrict CORS to env defined origin 2020-02-05 16:45:01 -08:00
Sorrel Bri
c94c64b106 configure simple CORS 2020-02-05 16:45:01 -08:00
Sorrel Bri
39cdef0b02 stub basic connection 2020-02-05 16:45:01 -08:00
Sorrel Bri
709220e19e add react frontend structure 2020-02-05 16:45:01 -08:00
Sorrel Bri
8af1ea67d3 remove views/, add controllers/ 2020-02-05 16:45:00 -08:00
Sorrel Bri
384056b3bb git init 2020-02-05 16:45:00 -08:00
Sorrel Bri
20a20cd7be init lerna 2020-02-05 16:44:38 -08:00
Sorrel Bri
3943d08ab6 stub nav form styles and fix board span bug 2020-02-05 16:44:37 -08:00
Sorrel Bri
08e76b33d0 decoupled show Auth form logic from Home sidebar for use in other sidebars on Auth errors 2020-02-05 16:44:37 -08:00
Sorrel Bri
94c40e6d07 style player area on game screen 2020-02-05 16:44:37 -08:00
Sorrel Bri
0e5c18907d stub render board state from make_move 2020-02-05 16:44:37 -08:00
Sorrel Bri
485771b8f2 stub make move socket message to add move to game 2020-02-05 16:44:37 -08:00
Sorrel Bri
293a574d2b refactor make move to serve game meta data with game record 2020-02-05 16:44:37 -08:00
Sorrel Bri
167fee8175 check legality of move prior to posting 2020-02-05 16:44:37 -08:00
Sorrel Bri
ad8a3eaff2 stub make move to game service 2020-02-05 16:44:37 -08:00
Sorrel Bri
5981daf86a stub game services to store active games 2020-02-05 16:44:37 -08:00
Sorrel Bri
cb56a98d27 stub board on game page 2020-02-05 16:44:37 -08:00
Sorrel Bri
607a4e13bf stub Game components 2020-02-05 16:44:37 -08:00
Sorrel Bri
e870d45f90 add seed moves 2020-02-05 16:44:37 -08:00
Sorrel Bri
1d8cabf5ee stub game connection via socket room 2020-02-05 16:44:37 -08:00
Sorrel Bri
857e9e0fd5 stub game connection via socket room 2020-02-05 16:44:37 -08:00
Sorrel Bri
1f89dbc481 patch to cleanup reducer directory filenames 2020-02-05 16:44:36 -08:00
Sorrel Bri
ba8a89ee6a stub default falsey values for initState function to prevent crash on 500 2020-02-05 16:44:36 -08:00
Sorrel Bri
025aee43c8 create Loading component with pinwheel ko animation 2020-02-05 16:44:36 -08:00
Sorrel Bri
261b5c5bbe add table styling to game buttons in game room 2020-02-05 16:44:36 -08:00
Sorrel Bri
ebb7ceb466 refactor all jsx to BEM 2020-02-05 16:44:36 -08:00
Sorrel Bri
79daaf1461 debug room socket connect dispatch 2020-02-05 16:44:36 -08:00
Sorrel Bri
e2537df697 refactor sockets to be launched with namespace by components 2020-02-05 16:44:36 -08:00
Sorrel Bri
0b78c78a0a refacor sockets on server and client to utilize dispatch for socket meessages 2020-02-05 16:44:36 -08:00
Sorrel Bri
f6b6134d5b stub socket connection to game id namespace 2020-02-05 16:44:36 -08:00
Sorrel Bri
67f0449963 stub socket connection to game id namespace 2020-02-05 16:44:36 -08:00
Sorrel Bri
5a2d5648e0 stub games/:id endpoint to join game socket room 2020-02-05 16:44:36 -08:00
Sorrel Bri
f0c0b9a0f6 refactor join_game_request to require unique users 2020-02-05 16:44:36 -08:00
Sorrel Bri
eeca5480ec connect join_game_request socket at client and server 2020-02-05 16:44:35 -08:00
Sorrel Bri
d882fe9bcf patch rooms/:id failure to return multiple games 2020-02-05 16:44:35 -08:00
Sorrel Bri
6aecd7d48e patch no-query bug in rooms/ endpoint 2020-02-05 16:44:35 -08:00
Sorrel Bri
523bec7446 refactor api rooms/:id endpoint to deliver current room without table join 2020-02-05 16:44:35 -08:00
Sorrel Bri
6eaf272d5d stub room display with messages and game link 2020-02-05 16:44:35 -08:00
Sorrel Bri
3ad0c9da52 dispatch all room data to state upon rooms/:id 2020-02-05 16:44:35 -08:00
Sorrel Bri
a33fea6ba6 refactor api room endpoint to return unjoined game room data 2020-02-05 16:44:35 -08:00
Sorrel Bri
41d64135e5 add SET_MESSAGES to reducer 2020-02-05 16:44:35 -08:00
Sorrel Bri
ef165b0cff refactor services and reducers to parse JSON before data hits dispatch 2020-02-05 16:44:35 -08:00
Sorrel Bri
55b1a9bce0 add client routing to rooms/:id, move socket(rooms/roomId) connect from Home page to Room page 2020-02-05 16:44:35 -08:00
Sorrel Bri
63a9233b15 serve room with message data from api/v1/rooms/:id 2020-02-05 16:44:35 -08:00
Sorrel Bri
c992bf4b4f serve room with joined game data from api/v1/rooms/:id 2020-02-05 16:44:35 -08:00
Sorrel Bri
6c0d1fbc7c stub room socket connection (client + server) 2020-02-05 16:44:34 -08:00
Sorrel Bri
ebb23863dd add message table with migration 2020-02-05 16:44:34 -08:00
Sorrel Bri
0f507ef316 add move table with migration 2020-02-05 16:44:34 -08:00
Sorrel Bri
51bd3fb832 add game table with seed and migration 2020-02-05 16:44:34 -08:00
Sorrel Bri
76cfbf54c3 add time_setting table with seed and migration 2020-02-05 16:44:34 -08:00
Sorrel Bri
d6c850cae1 patch remove unnecessary socket.io cookie 2020-02-05 16:44:34 -08:00
Sorrel Bri
e1e7768c08 Merge branch 'gameroom_endpoint' 2020-02-05 16:44:34 -08:00
Sorrel Bri
a8750afa7e patch https cookies in production 2020-02-05 16:44:34 -08:00
Sorrel Bri
65bbeb71dd patch https cookies in production 2020-02-05 16:44:34 -08:00
Sorrel Bri
a5206c965c hook SET_ROOMS into rooms Service 2020-02-05 16:44:34 -08:00
Sorrel Bri
1b7f802013 add SET_ROOMS dispatch 2020-02-05 16:44:34 -08:00
Sorrel Bri
d81be0b595 serve all public rooms at rooms index 2020-02-05 16:44:34 -08:00
Sorrel Bri
a6105973f3 stub rooms index route and controller 2020-02-05 16:44:34 -08:00
Sorrel Bri
1cd1dc87a9 add room migration and simple seeds 2020-02-05 16:44:33 -08:00
Sorrel Bri
43556e97aa serve basic page at api endpoint to redirect user 2020-02-05 16:44:33 -08:00
Sorrel Bri
e1e45f679b patch component references in pages/ 2020-02-05 16:44:33 -08:00
Sorrel Bri
8622b5747f patch reference to MainWrapper component 2020-02-05 16:44:33 -08:00
Sorrel Bri
ba3fb61f8b rename RoomArchive components to differentiate from NewsArchive 2020-02-05 16:44:33 -08:00
Sorrel Bri
59a1ade90a stub home and rooms sidebar components 2020-02-05 16:44:33 -08:00
Sorrel Bri
575cf4a77a patch to hide password input on login form 2020-02-05 16:44:33 -08:00
Sorrel Bri
e0bc32078b patch frontend api endpoint in config 2020-02-05 16:44:33 -08:00
Sorrel Bri
63b95b0604 add manual set Access Control Allow Origin header 2020-02-05 16:44:33 -08:00
Sorrel Bri
4960a5f60d patch CORS 2020-02-05 16:44:33 -08:00
Sorrel Bri
23daa25ca9 patch config for production 2020-02-05 16:44:33 -08:00
Sorrel Bri
ffef145527 patch config for production 2020-02-05 16:44:33 -08:00
Sorrel Bri
78ad0b18ec add login hook to frontend 2020-02-05 16:44:33 -08:00
Sorrel Bri
2a4d48d26c add validation for username and email on signup 2020-02-05 16:44:33 -08:00
Sorrel Bri
d04656762e add client side password confirmation check 2020-02-05 16:44:32 -08:00
Sorrel Bri
9837814edb refactor auth and api/index to ensure token verified and sent to dispatch ADD_USER 2020-02-05 16:44:32 -08:00
Sorrel Bri
d10774c161 refactor auth and api/index to ensure token verified and sent to dispatch ADD_USER 2020-02-05 16:44:32 -08:00
Sorrel Bri
ff2e1e82b2 refactor to fetch to ensure proper cookie storage 2020-02-05 16:44:32 -08:00
Sorrel Bri
ca6773c18d serve user from verified jwt 2020-02-05 16:44:32 -08:00
Sorrel Bri
ad26f1af3a add FormError component to display auth errors 2020-02-05 16:44:32 -08:00
Sorrel Bri
0131183237 hook reducer into signup post response 2020-02-05 16:44:32 -08:00
Sorrel Bri
44636cf4f5 patch server production env allowed origin 2020-02-05 16:44:32 -08:00
Sorrel Bri
0ba2e2acdc patch signup post for existing user to return 409 2020-02-05 16:44:32 -08:00
Sorrel Bri
918b2ac5cb hook reducer into auth/signup request service 2020-02-05 16:44:32 -08:00
Sorrel Bri
285d9b7a46 stub styling of side and main content 2020-02-05 16:44:32 -08:00
Sorrel Bri
16c10d9038 stub sidebar component to display auth form 2020-02-05 16:44:31 -08:00
Sorrel Bri
3f2417dd4d stub tests for page components 2020-02-05 16:44:31 -08:00
Sorrel Bri
86b23bdf44 debug password hashing function 2020-02-05 16:44:31 -08:00
Sorrel Bri
a7f1a16ba2 stub frontend requests to auth endpoints 2020-02-05 16:44:31 -08:00
Sorrel Bri
923879e249 add validation and sanitization for auth/. posts 2020-02-05 16:44:31 -08:00
Sorrel Bri
a5bee4129b patch to remove timeout from login test 2020-02-05 16:44:31 -08:00
Sorrel Bri
68ae8615f3 add login verification of user 2020-02-05 16:44:31 -08:00
Sorrel Bri
4f5c9e11a3 patch knexfile name to run migrations 2020-02-05 16:44:31 -08:00
Sorrel Bri
73ed98622c add password hash to new user 2020-02-05 16:44:31 -08:00
Sorrel Bri
e7c2bf38cf stub new user sign up 2020-02-05 16:44:31 -08:00
Sorrel Bri
f26b8ece56 add sign jwt function and hook to auth/signup 2020-02-05 16:44:31 -08:00
Sorrel Bri
6fef68d9ba stub auth routes and controllers 2020-02-05 16:44:31 -08:00
Sorrel Bri
af01d85945 debug migrations path 2020-02-05 16:44:30 -08:00
Sorrel Bri
4aed7bbd81 debug produciton db connection 2020-02-05 16:44:30 -08:00
Sorrel Bri
c59f7df174 add migrate command to server Procfile 2020-02-05 16:44:30 -08:00
Sorrel Bri
83633509c2 add create user table migration 2020-02-05 16:44:30 -08:00
Sorrel Bri
5c747139be deploy subdirectories successfully 2020-02-05 16:44:30 -08:00
Sorrel Bri
ef5ca1f2dd return README to root directory 2020-02-05 16:44:30 -08:00
Sorrel Bri
148808979b move react module into subdirectory 2020-02-05 16:44:30 -08:00
Sorrel Bri
c056770a70 confirm successful deployment 2020-02-05 16:44:30 -08:00
Sorrel Bri
3e10f1a03c add runtime env for production enironment 2020-02-05 16:44:30 -08:00
Sorrel Bri
8912db80d7 add production endpoints to react config 2020-02-05 16:44:30 -08:00
Sorrel Bri
dbfe5c13d0 change server Procfile to npm start 2020-02-05 16:44:30 -08:00
Sorrel Bri
cbbacc0ada remove path from server Procfile 2020-02-05 16:44:30 -08:00
Sorrel Bri
ffff70229a add path to server Procfile 2020-02-05 16:44:30 -08:00
Sorrel Bri
0c433d54df add node to server Procfile 2020-02-05 16:44:29 -08:00
Sorrel Bri
8813b72ad2 empty react Procfile 2020-02-05 16:44:29 -08:00
Sorrel Bri
cb8a308872 add run to Procfile 2020-02-05 16:44:29 -08:00
Sorrel Bri
83bd12b222 remove npm from start command 2020-02-05 16:44:29 -08:00
Sorrel Bri
c0d96ffdc8 redeploy with namespaced scripts 2020-02-05 16:44:29 -08:00
Sorrel Bri
06d1b3c63b add Procfiles for deploy 2020-02-05 16:44:29 -08:00
Sorrel Bri
cbaae4fd2c config flow 2020-02-05 16:44:29 -08:00
Sorrel Bri
6486176d8f patch page function tests, import pages in App 2020-02-05 16:44:29 -08:00
Sorrel Bri
8e94b6d83c stub page components 2020-02-05 16:44:29 -08:00
Sorrel Bri
c0be13db8e stub mocha/chai for testing server 2020-02-05 16:44:29 -08:00
Sorrel Bri
8416a83757 stub react pages structure 2020-02-05 16:44:29 -08:00
Sorrel Bri
30eca7029d stub middleware (including socket), controllers architecture 2020-02-05 16:44:29 -08:00
Sorrel Bri
fb30588566 connect socket 2020-02-05 16:44:29 -08:00
Sorrel Bri
6171fa85c5 connect socket 2020-02-05 16:44:29 -08:00
Sorrel Bri
c14a76e996 connect to db with knex 2020-02-05 16:44:28 -08:00
Sorrel Bri
bbdc63f61c restrict CORS to env defined origin 2020-02-05 16:44:28 -08:00
Sorrel Bri
ae0ee5e7ef configure simple CORS 2020-02-05 16:44:28 -08:00
Sorrel Bri
f17911f852 stub basic connection 2020-02-05 16:44:28 -08:00
Sorrel Bri
c8f7a7097c add react frontend structure 2020-02-05 16:44:28 -08:00
Sorrel Bri
5cb00b90f6 remove views/, add controllers/ 2020-02-05 16:44:28 -08:00
Sorrel Bri
ebedfae532 git init 2020-02-05 16:44:28 -08:00
Sorrel Bri
ca4b7aba04 init lerna 2020-02-05 15:29:36 -08:00
Sorrel Bri
64dfe54953 stub nav form styles and fix board span bug 2020-02-04 22:52:07 -08:00
Sorrel Bri
d0d4f2429c decoupled show Auth form logic from Home sidebar for use in other sidebars on Auth errors 2020-02-04 19:21:31 -08:00
Sorrel Bri
8999b3ab3b style player area on game screen 2020-02-01 20:48:26 -08:00
Sorrel Bri
ddc7ac8548 stub render board state from make_move 2020-01-31 22:22:43 -08:00
Sorrel Bri
279f27349f stub make move socket message to add move to game 2020-01-31 20:28:39 -08:00
Sorrel Bri
4336666eef refactor make move to serve game meta data with game record 2020-01-30 23:35:39 -08:00
Sorrel Bri
999a8cf041 check legality of move prior to posting 2020-01-30 23:24:40 -08:00
Sorrel Bri
4a06a6939f stub make move to game service 2020-01-30 22:50:34 -08:00
Sorrel Bri
63afa73570 stub game services to store active games 2020-01-30 21:03:27 -08:00
Sorrel Bri
3f283b47d8 stub board on game page 2020-01-30 15:56:50 -08:00
Sorrel Bri
5ef4d390ff stub Game components 2020-01-30 00:30:56 -08:00
Sorrel Bri
3aeadb48cd add seed moves 2020-01-29 23:44:06 -08:00
Sorrel Bri
09e90c97ab stub game connection via socket room 2020-01-29 23:22:50 -08:00
Sorrel Bri
899d32c9cf stub game connection via socket room 2020-01-29 17:01:46 -08:00
Sorrel Bri
8ec5b8f773 patch to cleanup reducer directory filenames 2020-01-29 13:28:49 -08:00
Sorrel Bri
0ac8ca002b stub default falsey values for initState function to prevent crash on 500 2020-01-29 13:00:00 -08:00
Sorrel Bri
572c4241f9 create Loading component with pinwheel ko animation 2020-01-29 00:17:32 -08:00
Sorrel Bri
0f02cc4cfc add table styling to game buttons in game room 2020-01-28 22:20:56 -08:00
Sorrel Bri
b45abe5559 refactor all jsx to BEM 2020-01-28 17:03:17 -08:00
Sorrel Bri
d7c0477c88 debug room socket connect dispatch 2020-01-28 14:33:54 -08:00
Sorrel Bri
976293f099 refactor sockets to be launched with namespace by components 2020-01-27 22:40:03 -08:00
Sorrel Bri
33a8877597 refacor sockets on server and client to utilize dispatch for socket meessages 2020-01-25 16:50:18 -08:00
Sorrel Bri
dbd4575128 stub socket connection to game id namespace 2020-01-24 15:51:15 -08:00
Sorrel Bri
34a5fa8d6f stub socket connection to game id namespace 2020-01-24 15:50:47 -08:00
Sorrel Bri
f4b8e79a3a stub games/:id endpoint to join game socket room 2020-01-24 00:07:40 -08:00
Sorrel Bri
4a960c784b refactor join_game_request to require unique users 2020-01-23 21:41:25 -08:00
Sorrel Bri
680bb41337 connect join_game_request socket at client and server 2020-01-23 21:25:08 -08:00
Sorrel Bri
f015f4242e patch rooms/:id failure to return multiple games 2020-01-23 16:14:38 -08:00
Sorrel Bri
015be6ca6b patch no-query bug in rooms/ endpoint 2020-01-23 16:04:40 -08:00
Sorrel Bri
0a66d3f93c refactor api rooms/:id endpoint to deliver current room without table join 2020-01-23 14:46:49 -08:00
Sorrel Bri
abfc0c7c94 stub room display with messages and game link 2020-01-23 14:45:48 -08:00
Sorrel Bri
1863cc96a2 dispatch all room data to state upon rooms/:id 2020-01-22 16:40:56 -08:00
Sorrel Bri
e6f286c743 refactor api room endpoint to return unjoined game room data 2020-01-22 16:30:28 -08:00
Sorrel Bri
c3eb7fe697 add SET_MESSAGES to reducer 2020-01-22 15:41:08 -08:00
Sorrel Bri
754d17890e refactor services and reducers to parse JSON before data hits dispatch 2020-01-22 15:16:43 -08:00
Sorrel Bri
833d8fa1dd add client routing to rooms/:id, move socket(rooms/roomId) connect from Home page to Room page 2020-01-22 11:43:36 -08:00
Sorrel Bri
067ce69cbb serve room with message data from api/v1/rooms/:id 2020-01-21 17:02:54 -08:00
Sorrel Bri
c1d269625b serve room with joined game data from api/v1/rooms/:id 2020-01-21 16:01:35 -08:00
Sorrel Bri
c323968404 stub room socket connection (client + server) 2020-01-21 14:33:40 -08:00
Sorrel Bri
5205035d7c add message table with migration 2020-01-20 23:36:52 -08:00
Sorrel Bri
a10453a848 add move table with migration 2020-01-20 23:19:22 -08:00
Sorrel Bri
d0f26d9a6c add game table with seed and migration 2020-01-20 23:05:55 -08:00
Sorrel Bri
63f1b335e9 add time_setting table with seed and migration 2020-01-20 22:39:28 -08:00
Sorrel Bri
2e76c58f88 patch remove unnecessary socket.io cookie 2020-01-20 13:47:02 -08:00
Sorrel Bri
cd0499cf72 Merge branch 'gameroom_endpoint' 2020-01-20 13:22:18 -08:00
Sorrel Bri
3e82a2c410 render stubbed RoomButton component on Home 2020-01-20 13:21:52 -08:00
Sorrel Bri
3275fc18d6 change landing page to news if user not logged in 2020-01-20 12:16:46 -08:00
Sorrel Bri
34abfa9d29 patch https cookies in production 2020-01-18 23:53:42 -08:00
Sorrel Bri
082cf29470 patch https cookies in production 2020-01-18 23:02:33 -08:00
Sorrel Bri
eb86556f2a hook SET_ROOMS into rooms Service 2020-01-18 16:40:57 -08:00
Sorrel Bri
03648fcf3f add SET_ROOMS dispatch 2020-01-18 16:17:23 -08:00
Sorrel Bri
048f518114 serve all public rooms at rooms index 2020-01-18 15:49:35 -08:00
Sorrel Bri
493a1f3e82 stub rooms index route and controller 2020-01-18 15:42:45 -08:00
Sorrel Bri
e6fb148bdc add room migration and simple seeds 2020-01-18 14:15:44 -08:00
Sorrel Bri
e4f84275b4 serve basic page at api endpoint to redirect user 2020-01-18 13:24:00 -08:00
Sorrel Bri
1d0111567c patch component references in pages/ 2020-01-18 13:06:28 -08:00
Sorrel Bri
c6ec8210dc patch reference to MainWrapper component 2020-01-18 13:02:03 -08:00
228 changed files with 21260 additions and 5186 deletions

55
.circleci/config.yml Normal file
View file

@ -0,0 +1,55 @@
# JavaScript Node CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-javascript/ for more details
#
version: 2
commands:
create_concatenated_package_lock:
description: "Concatenate all package-lock.json files recognized by lerna.js into single file. File is used as checksum source for part of caching key."
parameters:
filename:
type: string
steps:
- run:
name: Combine package-lock.json files to single file
command: npx lerna list -p -a | awk -F packages '{printf "\"packages%s/package-lock.json\" ", $2}' | xargs cat > << parameters.filename >>
jobs:
build:
docker:
# specify the version you desire here
- image: circleci/node:12.6
working_directory: ~/node-go
steps:
- checkout
# - run: sudo apt-get install postgresql-client-11.4
# - run: pg_ctl -D /var/lib/postgresql/data -l logfile start
# - run: psql CREATE DATABASE circle_test
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "package-lock.json" }}
# fallback to using the latest cache if no exact match is found
- run: npm install
- run: npm run bootstrap
- save_cache:
paths:
- node_modules
key:
v1-dependencies-{{ checksum "package-lock.json" }}
- run: npm test
- run: npm install react
- run: npm install react-dom
# ! temporary fix for deprecated package: minimist
- run: npm audit --audit-level=moderate
# DEPLOY
- run: git subtree push --prefix packages/server https://heroku:$HEROKU_API_KEY@git.heroku.com/$HK_API.git master
- run: git subtree push --prefix packages/play-node-go https://heroku:$HEROKU_API_KEY@git.heroku.com/$HK_PLAY.git master

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Sorrel
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

171
README.md
View file

@ -1 +1,170 @@
[Visit site](https://play-node-go.herokuapp.com/) # Node Go
A browser application to play Go in real time.
[The project in it's current state](https://play-node-go.herokuapp.com/)
[Client only prototype](https://sorrelbri.github.io/browser-go-proto/)
![Screenshot of an in-progress game of Go.](public/game-in-progress.png)
[About Go](#the-game-of-go)
[Technical Challenges](#technical-challenges)
[Setup For Development](#setup)
[Known Bugs](#known-bugs)
[Roadmap](#roadmap)
[Features](#features)
[Tech](#built-with)
---
## The Game of Go
Go is a 2 player abstract strategy game of perfect information.
Players take turns placing playing pieces called stones on the intersections of a gridded board. This board is usually a square 19 points across. Stones remain on the points at which they are placed unless they are captured by the opposing player. Capture occurs when a stone or group of stones no longer has any adjascent empty points.
Play ends when both players agree that they have exhausted all advantageous moves. Scoring is determined by counting and comparing the area controlled by either player.
For a more detailed explanation of the rules, please see [my previous illustrated explanation of the game of go](https://github.com/sorrelbri/browser-go-proto#the-game-of-go) or the [American Go Association's Concise Rules of Go.](https://www.usgo.org/aga-concise-rules-go)
---
## Technical Challenges
### Modeling Game State
A go board typically consists of 361 points which can exist in a number of states. Points can influence the state of points that are orthogonal neighbors. This relationship can be thought of as an undirected graph, with each point being a vertex typically of degree 4. Special cases include 'edge' points, whose degree is 3 or, in the case of corner points, 2.
Many of the methods that manage the state of the game and of the board, make use of this graph representation. Groups are contiguous points with the same color stone which are important in determining the life or death of stones on the board. When a player makes a move, (provided that move is legal,) the point at which the move is made will utilize a breadth-first graph traversal calling a `joinGroup` method on each point with the same color stone.
Adjacent points without stones are very important to the state of a point as well. These are known as liberties, and so long as a group of stones has at least one point with at least one liberty, that group remains alive and on the board. Therefore, the `joinGroup` method also utilizes a depth first traversal to mark all of the liberties of a group. Both the stones and the liberties of the group are memoized on the Game object.
![Image of Game logic](public/game-logic.png)
### Game Records
For the moment, game records are modeled as a list where each move has the type `{player: <string: player color>, pos: {x: <integer>, y: <integer>}}`.
The database `move` table contains additional information `number`, `game_record`, and `prior_move` in addition to a foreign key for a `game` row. `number` represents the move number in a game record (for now this corresponds to list position), `game_record` is a boolean representing whether the move was 'official' or is an alternate move used in study, and `prior_move` is a reference to another `move` row allowing for the construction of a tree model for the game (see [Expanding this representation](#expanding-this-representation), below.)
There is a backend service that processes this list of moves into a current board state ([Modeling Game State](#modeling-game-state).) On the frontend, users have the option of expanding a menu to view the move order in the format below.
![Game Record: Black and white 19x19 grid with moves represented as circles filled in with stone color and marked with move number. Below the grid is an overflow area for moves made at previously played points in the format /[new move number and color at old move number and color/]](public/game-record.png)
This is a customary representation of game records printed in Go literature. A frontend service processes the list of moves and plots each move onto a `<canvas>` element drawn to resemble the grid lines on a board, with moves that are placed at prior points plotted on an additional `<canvas>` element below.
#### Expanding this representation
This representation will be expanded when support for alternate game paths is added. Alternate game paths will allow users to study completed games by playing out the consequences of moves not in the completed game. This feature will require a tree structure for the game record where moves on the main line are referred with a `main` property on each move node and alternate moves with any number of alternate references.
### Caching multiple in-progress games
![Image of Game module in context](public/game-module.png)
### Partitioning Game Rooms
Finding a game starts with joining a game room.
![Image of Home Screen with 'main' game room](public/home-screen.png)
Watch an in progress game, join a game, or study a historic game.
![Image of Room Screen with multiple games in various states](public/room-screen.png)
---
## Setup
### Local Repo
```sh
$ git clone https://github.com/sorrelbri/node-go.git
```
### Install Deps
```sh
$ npm run bootstrap
```
Runs lerna `bootstrap` command installing dependencies for project, development and package dependencies
### Initialize Database
#### Download PostgreSQL
To verify PostgreSQL installation:
```sh
$ psql -V
```
Node Go API was built with version 11.4.
[See documentation for Postgres download.](https://www.postgresql.org/download/)
#### Create Databases
```sh
$ psql
# psql(11.4)
```
```sql
CREATE DATABASE node-go;
CREATE DATABASE node-go-test; # testing database
```
### Configure Environment
```sh
$ touch packages/server/.env
```
```
# .env
NODE_ENV=development
PORT=# set development port for API
REACT_ADDRESS=http://localhost:3000 # default
PG_CONNECTION_STRING=postgresql://localhost:5432/node-go
PG_CONNECTION_STRING_TEST=postgresql://localhost:5432/node-go-test
JWT_SECRET=# generate a secret key for JWT signing algorithm
TEST_SECRET=# same as above, for test environment
SALT_ROUNDS=# set number of salt rounds for bcrypt hashing function
DOMAIN=localhost
USER_ONE_PASSWORD=# credentials for testing with
USER_ONE_EMAIL=# same as above
```
### Smoke test
```sh
$ lerna run test
```
### Run Database Migrations
```sh
$ cd packages/server; npm run migrate; npm run seed
```
### Running in development
```sh
$ cd packages/server
$ npm start # or if you have nodemon
$ nodemon
```
```sh
$ cd packages/play-node-go
$ npm start
```
---
## Known Bugs
- game end logic not implemented on front end yet
- no authorization for game moves
- websocket connections may remain open, pooling prevents runaway leaks, but tests may hang
---
## Roadmap
### 6/20
1. Frontend implementation of game end logic
2. Auth for games
3. Game request creation
### 7/20
1. Generate game records
2. Implement chat
3. Implement study mode
---
## Features
- [x] Realtime play
- [x] Account authentication
- [ ] Chat
- [ ] Study mode
- [ ] Multiple game settings
- [ ] Customizable board size
- [ ] Download games in .sgf format
---
## Built with
- [Express](https://expressjs.com)
- [React](https://reactjs.org)
- [PostgreSQL](https://postgresql.org)
- [Socket.io](https://socket.io)
- [Sass](https://sass-lang.com)
### Management & Deployment
- Lerna
- CircleCI

6
lerna.json Normal file
View file

@ -0,0 +1,6 @@
{
"packages": [
"packages/*"
],
"version": "independent"
}

8014
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

18
package.json Normal file
View file

@ -0,0 +1,18 @@
{
"name": "root",
"private": true,
"scripts": {
"test": "lerna run test",
"bootstrap": "lerna bootstrap --hoist"
},
"devDependencies": {
"@babel/preset-flow": "^7.8.3",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.4.0",
"chai": "^4.2.0",
"chai-http": "^4.3.0",
"lerna": "^3.20.2",
"mocha": "^7.0.0",
"react-test-renderer": "^16.12.0"
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,22 +1,21 @@
{ {
"name": "react-boilerplate", "name": "play-node-go",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@mars/heroku-js-runtime-env": "^3.0.2", "@mars/heroku-js-runtime-env": "^3.0.2",
"@testing-library/user-event": "^7.1.2", "@testing-library/user-event": "^7.1.2",
"flow-bin": "^0.114.0", "node-sass": "^4.14.1",
"node-sass": "^4.13.0", "react": "^16.13.1",
"react": "^16.12.0", "react-dom": "^16.13.1",
"react-dom": "^16.12.0",
"react-router-dom": "^5.1.2", "react-router-dom": "^5.1.2",
"react-scripts": "3.3.0", "react-scripts": "^3.4.0",
"socket.io-client": "^2.3.0" "socket.io-client": "^2.3.0"
}, },
"scripts": { "scripts": {
"start": "REACT_APP_ENVIRONMENT='development' react-scripts start", "start": "REACT_APP_ENVIRONMENT='development' react-scripts start",
"build": "react-scripts build", "build": "react-scripts build",
"test": "react-scripts test", "test": "CI=true react-scripts test",
"eject": "react-scripts eject", "eject": "react-scripts eject",
"flow": "./node_modules/.bin/flow", "flow": "./node_modules/.bin/flow",
"predeploy": "REACT_APP_ENVIRONMENT=production npm run build", "predeploy": "REACT_APP_ENVIRONMENT=production npm run build",
@ -36,10 +35,5 @@
"last 1 firefox version", "last 1 firefox version",
"last 1 safari version" "last 1 safari version"
] ]
},
"devDependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.4.0",
"react-test-renderer": "^16.12.0"
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

View file

@ -11,6 +11,7 @@
/> />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%/reset.css"> <link rel="stylesheet" type="text/css" href="%PUBLIC_URL%/reset.css">
<link href="https://fonts.googleapis.com/css?family=Oswald&display=swap" rel="stylesheet">
<title>Node Go</title> <title>Node Go</title>
</head> </head>
<body> <body>

View file

@ -1,3 +1,8 @@
a, a:link, a:visited, a:focus, a:active {
color: unset;
text-decoration: none;
}
/* http://meyerweb.com/eric/tools/css/reset/ /* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126 v2.0 | 20110126
License: none (public domain) License: none (public domain)
@ -46,3 +51,8 @@ table {
border-collapse: collapse; border-collapse: collapse;
border-spacing: 0; border-spacing: 0;
} }
button {
background: none;
border: none;
}

View file

@ -0,0 +1,21 @@
@mixin fullspan {
height: 100%;
width: 100%;
}
@mixin flexAround($direction) {
display: flex;
flex-direction: $direction;
align-items: center;
justify-content: space-around;
}
@mixin gameViewLabel {
color: rgb(255,240,230);
background-color: rgba(0,0,0,0.7);
padding: 0.25em;
}
@function place-below($grid-column, $grid-row) {
@return $grid-row + 1 + '/' + $grid-column + 2 + '/ span 1 / span 1';
}

View file

@ -0,0 +1,43 @@
$dev-colors: (
"a": #34dc90,
"b": #9034dc,
"c": #dc9034,
"d": #dc34dc,
"e": #34dcdc,
"f": #dcdc34
);
/* Responsive Design */
$break-points: (
"500": "only screen and (min-width: 500px)",
"570": "only screen and (min-width: 570px)",
"590": "only screen and (min-width: 590px)",
"700": "only screen and (min-width: 700px)",
"900": "only screen and (min-width: 900px)"
);
$colors: (
"error": #aa3333,
"nav_bar": #6f3e68,
"nav_link": #dae1b7,
"sidebar": #3c013f,
"sidebar_link": #f2ce3d,
"main": #95acae,
"home": #444444
);
$backgrounds: (
"game_room": radial-gradient(farthest-corner at 55% 40%, rgb(189, 131, 100) 0%, rgb(175, 113, 80) 65%, rgb(150, 90, 65) 90%, rgb(125, 65, 40) 100%),
);
/* Game Room Components */
$small-room: (
"roof": linear-gradient(rgb(69, 36, 19), rgb(31, 22, 18)),
"body": rgb(174, 169, 120),
"door": radial-gradient(15vh 20vh at top left, #662413,#992413 15%, #cc3423),
"door-handle": radial-gradient(circle at 40% 30%, rgb(247, 235, 183), rgb(213, 222, 41) 30%, rgb(121, 108, 7)),
"window-x": linear-gradient(rgba(67, 149, 159, 0.4), rgba(196, 249, 255, 0.4) 50%, rgba(67, 149, 159, 0.4) 50%, rgba(196, 249, 255, 0.4)),
"window-y": linear-gradient(to right, rgba(67, 149, 159, 0.5), rgba(196, 249, 255, 0.5) 50%, rgba(67, 149, 159, 0.5) 50%, rgba(196, 249, 255, 0.5)),
"frame": rgb(69, 36, 19),
"sign": rgb(188, 127, 95)
);

View file

@ -0,0 +1,82 @@
import React, { useEffect, useReducer } from 'react';
import { Switch, Route, BrowserRouter as Router } from 'react-router-dom';
import MainWrapper from './pages/Layout/MainWrapper/MainWrapper';
import { stateReducer } from './reducers/reducer';
import { initState } from './reducers/init/reducer.init';
import indexServices from './services/api/indexServices';
import './App.scss';
function App() {
const [ state, dispatch ] = useReducer(
stateReducer,
{},
initState
);
useEffect(() => {
const fetchIndexAPI = async () => {
const response = await indexServices.indexService();
if (response) {
const action = {
type: 'INDEX',
message: 'SET_USER',
body: response
}
dispatch(action)
}
}
fetchIndexAPI();
}, [ ])
useEffect(() => {
const socketConnect = () => {
if (state.connect.type) return;
dispatch({type:'SOCKET', message: 'LAUNCH', body:{nsp:'', dispatch}});
}
socketConnect();
return () => dispatch({type: 'SOCKET', message: 'DISCONNECT', body: {}});
}, [ state.connect ])
return (
<Router>
<div data-testid="App" className="App">
<Switch>
<Route path="/account">
<MainWrapper page="account" state={state} dispatch={dispatch}/>
</Route>
<Route path="/rooms/:id">
<MainWrapper page="room" state={state} dispatch={dispatch}/>
</Route>
<Route path="/games/:id">
<MainWrapper page="game" state={state} dispatch={dispatch}/>
</Route>
<Route path="/news">
<MainWrapper page="news" state={state} dispatch={dispatch}/>
</Route>
<Route path="/home">
<MainWrapper page="home" state={state} dispatch={dispatch}/>
</Route>
<Route path="/">
{state.user.username
? <MainWrapper page="home" state={state} dispatch={dispatch}/>
: <MainWrapper page="news" state={state} dispatch={dispatch}/>}
</Route>
</Switch>
<p className="App__socket-flag">{state.connect.type ? '✓' : ' ⃠'}</p>
</div>
</Router>
);
}
export default App;

View file

@ -0,0 +1,115 @@
import React from 'react';
import { Link } from 'react-router-dom';
import './Game.scss';
const GameButton = (props) => {
const { game, user } = props;
const setGameDisplayData = () => {
const gameData = {
playerBlack: game.playerBlack,
playerBlackRank: game.playerBlackRank,
gameId: game.id,
}
if (game.open) {
gameData.gameLinkText = 'Request to Join Game';
gameData.playerWhite = '';
gameData.playerWhiteRank = 'could be you!';
}
if (!game.open) {
gameData.playerWhite = game.playerWhite;
gameData.playerWhiteRank = game.playerWhiteRank;
gameData.gameLinkText = game.winType ? 'Study Game'
: user ? 'Rejoin Game' : 'Watch Game'
}
return gameData;
}
// TODO add to open game link
// const requestJoinGame = () => {
// console.log(`request to Join Game ${game.id}!`)
// const requestAction = {
// type: 'GAMES',
// message: 'JOIN_REQUEST',
// body: {id: game.id}
// }
// dispatch(requestAction);
// }
const renderGame = () => {
const {
gameLinkText,
playerBlack,
playerBlackRank,
gameId,
playerWhite,
playerWhiteRank
} = setGameDisplayData();
return (
<>
<div className="GameButton__seat GameButton__seat--black">
</div>
<div className="GameButton__table">
<div className="GameButton__table__meta">
<div
className="GameButton__player-data GameButton__player-data--black"
>
<span
className="GameButton__player-data__name GameButton__player-data__name--black"
>{playerBlack}</span>
<span
className="GameButton__player-data__rank GameButton__player-data__rank"
>{playerBlackRank}</span>
</div>
<Link
className="GameButton__link"
to={`/games/${gameId}`}
>{gameLinkText}</Link>
<div
className={`GameButton__player-data GameButton__player-data--white ${
playerWhite ? '' : 'GameButton__player-data--invisible'
}`
}
>
<span
className="GameButton__player-data__name GameButton__player-data__name--white"
>{playerWhite}</span>
<span
className="GameButton__player-data__rank GameButton__player-data__rank--white"
>{playerWhiteRank}</span>
</div>
</div>
<div className="GameButton__table__image">
<div className="table__player-area table__player-area--black">
<div className="table__player-bowl table__player-bowl--black"></div>
</div>
<div className="table__game-board">
<div className="table__game-board--grid"></div>
</div>
<div className="table__player-area table__player-area--white">
<div className="table__player-bowl table__player-bowl--white"></div>
</div>
</div>
</div>
<div className="GameButton__seat GameButton__seat--white">
</div>
</>
)
}
return (
<div className="GameButton" data-testid="GameButton">
{renderGame()}
</div>
);
}
export default GameButton;

View file

@ -0,0 +1,139 @@
@import '../../../../public/stylesheets/partials/mixins';
div.GameButton {
align-items: stretch;
display: flex;
flex-flow: column nowrap;
height: 20vh;
justify-content: space-around;
margin: 1vw;
width: 20vh;
}
div.GameButton__seat {
background: linear-gradient(190deg, rgb(232, 78, 78), rgb(172, 18, 18) 40%, rgb(100, 0, 0));
height: 10%;
margin: 0 auto;
width: 50%;
&.GameButton__seat--black {
border: rgba(83, 53, 35, 0.81);
border-radius: 5vw 5vw 0 0;
}
&.GameButton__seat--white {
border: transparent;
border-radius: 0 0 5vw 5vw;
}
}
div.GameButton__table {
box-shadow: -2vmin 4vmin 2vmin rgba(83, 53, 35, 0.81);
height: 80%;
margin: 0;
width: 100%;
}
div.GameButton__table__meta {
display: flex;
flex-flow: column nowrap;
height: 100%;
justify-content: space-between;
width: 100%;
z-index: 2;
}
div.GameButton__table__meta * {
z-index: 2;
}
div.GameButton__table__image {
align-items: center;
background: radial-gradient(
farthest-corner at 55% 40%,
rgb(150, 200, 220) 0%, rgb(97, 166, 194) 65%, rgb(70,100,120) 100%
);
border-top: 0.1vw solid rgb(150, 200, 220);
border-right: 0.05vw solid rgb(97, 166, 194);
border-radius: 0.3vw;
display: flex;
flex-flow: column nowrap;
height: 100%;
justify-content: space-between;
position: relative;
top: -100%;
width: 100%;
z-index: 1;
div.table__game-board {
align-items: center;
background: radial-gradient(
farthest-corner at 55% 40%,
rgb(244, 230, 120) 0%, rgb(234, 178, 78) 65%, rgb(200, 160, 90) 90%, rgb(200, 140, 90) 100%
);
box-shadow: -0.5vh 1vh 2vh rgba(30,30,30, 0.7);
height: 60%;
justify-content: center;
width: 60%;
display: flex;
div.table__game-board--grid {
background:
repeating-linear-gradient(0deg, #000 0%, transparent 0.75%, transparent 4.25%, #000 5%),
repeating-linear-gradient(90deg, #000 0%, transparent 0.75%, transparent 4.25%, #000 5%);
height: 94%;
width: 94%;
}
}
}
div.table__player-area {
height: 20%;
width: 60%;
div.table__player-bowl {
background: radial-gradient(
farthest-corner at 48% 54%,
rgb(30, 5, 0) 0%, rgb(30, 5, 0) 2%, rgb(30, 5, 0) 32%, rgb(0,0,0)35%, rgb(116,48,17) 48%,
rgb(140, 60, 40) 52%, rgb(100, 40, 5) 55%, rgb(116, 48, 17) 58%, rgb(140,60,40) 65%,
rgb(100, 40, 5) 80%, rgb(80, 20, 0) 90%
);
border-radius: 50%;
box-shadow: -0.5vh 1vh 2vh rgba(30,30,30, 0.7);
height: 100%;
&--white {
margin: auto auto auto 75%;
}
&--black {
margin: auto 75% auto auto;
}
}
}
div.GameButton__player-data {
@include gameViewLabel;
display: flex;
justify-content: space-around;
max-width: 100%;
&.GameButton__player-data--invisible {
visibility: hidden;
}
&.GameButton__player-data--white {
margin: 0 5vw 1vw 0.5vw;
}
&.GameButton__player-data--black {
margin: 1vw 0.5vw 0 5vw;
}
}
a.GameButton__link {
@include gameViewLabel;
margin: 0 auto;
}

View file

@ -0,0 +1,22 @@
import React from 'react';
import { render } from '@testing-library/react';
import { BrowserRouter as Router } from 'react-router-dom';
import Game from './Game';
const gameData = {
id: 1,
name: "main",
description: "A general place to play Go",
language: "EN"
}
test('renders GameButton without crashing', () => {
const { getByTestId } = render(
<Router>
<Game game={gameData} />
</Router>
);
const GameButton = getByTestId('GameButton');
expect(GameButton).toBeInTheDocument();
});

View file

@ -0,0 +1,34 @@
import React from "react";
import authServices from "../../../services/authServices";
const Guest = ({ dispatch }) => {
const handleClick = async (e) => {
e.preventDefault();
// dispatch to guest endpoint
const guestResponse = await authServices.guestService();
if (guestResponse.errors) {
const authError = guestResponse.errors[0].auth;
return dispatch({
type: "ERR",
message: "AUTH_ERROR",
body: { authError },
});
}
return dispatch({
type: "AUTH",
message: "GUEST",
body: guestResponse,
});
};
return (
<>
<button className="nav__section__button" onClick={handleClick}>
Continue As Guest
</button>
</>
);
};
export default Guest;

View file

@ -0,0 +1,18 @@
import React from 'react';
import './Library.scss';
const LibraryButton = () => {
return (
<div
className="nav__section"
>
<p
className="nav__section__label"
>
Library
</p>
</div>
);
}
export default LibraryButton;

View file

@ -0,0 +1,18 @@
import React from 'react';
import './NewGame.scss';
const NewGameButton = () => {
return (
<div
className="nav__section"
>
<p
className="nav__section__label"
>
New Game
</p>
</div>
);
}
export default NewGameButton;

View file

@ -0,0 +1,18 @@
import React from 'react';
import './NewRoom.scss';
const NewRoomButton = () => {
return (
<div
className="nav__section"
>
<p
className="nav__section__label"
>
New Room
</p>
</div>
);
}
export default NewRoomButton;

View file

@ -0,0 +1,71 @@
import React from 'react';
import { Link } from 'react-router-dom';
import RoomDetail from '../../Display/RoomDetail/RoomDetail';
import './Room.scss';
const RoomButton = ({room, roomDetail, showRoomDetail}) => {
const smallRoom = (
<div className="small-room">
<div className="small-room__roof">
<div className="small-room__roof-top small-room__roof-top--left"></div>
<div className="small-room__roof--left"></div>
<div className="small-room__roof-top small-room__roof-top--right"></div>
<div className="small-room__roof--right"></div>
</div>
<div className="small-room__body">
<div className="small-room__trim small-room__trim--left"></div>
<div className="small-room__sign">
<div className="small-room__sign__stone small-room__sign__stone--black"></div>
<div className="small-room__sign__stone small-room__sign__stone--white"></div>
<div className="small-room__sign__stone small-room__sign__stone--white"></div>
<div className="small-room__sign__stone small-room__sign__stone--black"></div>
</div>
<div className="small-room__window small-room__window--left">
<div className="small-room__window-frame small-room__window-frame--top"></div>
<div className="small-room__window-frame small-room__window-frame--left"></div>
<div className="small-room__window-frame small-room__window-frame--middle"></div>
<div className="small-room__window-frame small-room__window-frame--center"></div>
<div className="small-room__window-frame small-room__window-frame--right"></div>
<div className="small-room__window-frame small-room__window-frame--bottom"></div>
</div>
<div className="small-room__door">
<div className="small-room__door-frame small-room__door-frame--top"></div>
<div className="small-room__door-frame small-room__door-frame--left"></div>
<div className="small-room__door-handle"></div>
<div className="small-room__door-frame small-room__door-frame--right"></div>
</div>
<div className="small-room__window small-room__window--right">
<div className="small-room__window-frame small-room__window-frame--top"></div>
<div className="small-room__window-frame small-room__window-frame--left"></div>
<div className="small-room__window-frame small-room__window-frame--middle"></div>
<div className="small-room__window-frame small-room__window-frame--center"></div>
<div className="small-room__window-frame small-room__window-frame--right"></div>
<div className="small-room__window-frame small-room__window-frame--bottom"></div>
</div>
<div className="small-room__trim small-room__trim--bottom-left"></div>
<div className="small-room__trim small-room__trim--right"></div>
<div className="small-room__trim small-room__trim--bottom-right"></div>
</div>
</div>
)
return (
<>
<div className="RoomButton" data-testid="RoomButton">
<h4 className="RoomButton__room-link RoomButton__room-link--action">
<Link to={`/rooms/${room.id}`}>Join {room.name}</Link>
</h4>
<h4
className="RoomButton__room-link RoomButton__room-link--info --link"
onClick={e => showRoomDetail(room.id)}
>
?
</h4>
{smallRoom}
</div>
{roomDetail ? <RoomDetail room={room} /> : <></>}
</>
);
}
export default RoomButton;

View file

@ -0,0 +1,227 @@
@import '../../../../public/stylesheets/partials/mixins';
@import '../../../../public/stylesheets/partials/variables';
div.RoomButton {
display: grid;
grid-template-columns: [left] 1fr [right];
grid-template-rows: [top] 1fr [middle] 4fr [bottom];
height: 20vh;
padding: 1vw;
width: 20vh;
.RoomButton__room-link {
@include gameViewLabel;
align-self: self-end;
grid-area: top/left/middle/right;
max-width: fit-content;
z-index: 1;
&--info {
justify-self: end;
}
}
div.small-room {
display: grid;
grid-area: top/left/bottom/right;
grid-template-rows: [sky-start] 4vh [roof-start] 5vh [body-start] 1fr [body-end];
height: 100%;
width: 100%;
.small-room__roof {
display: grid;
grid-row: roof-start / body-start;
grid-template-columns: [left] 10vh [middle] 10vh [right];
grid-template-rows: 1fr;
height: 100%;
max-width: 20vh;
.small-room__roof-top {
background: map-get($small-room, "roof");
box-shadow: 0 0.35vh 0.75vh map-get($colors, "home");
grid-row: 1 / 2;
height: 0.7vh;
position: relative;
top: 3vh;
width: 12vh;
&--left {
grid-column: left / middle;
left: -1.2vh;
transform: rotate(-26deg);
}
&--right {
grid-column: middle / right;
left: -0.8vh;
transform: rotate(26deg);
}
}
&--left {
border-right: map-get($small-room, "body") solid 10vh;
border-top: map-get($colors, "home") solid 5vh;
grid-column: left / middle;
height: 0;
}
&--right {
border-left: map-get($small-room, "body") solid 10vh;
border-top: map-get($colors, "home") solid 5vh;
grid-column: middle / right;
height: 0;
}
}
.small-room__body {
background-color: map-get($small-room, "body");
display: grid;
grid-row: body-start / body-end;
grid-template-areas:
"trim-left gap-left top-left gap-mid-left top-mid gap-mid-right top-right gap-right trim-right"
"trim-left gap-left top-left gap-mid-left top-mid gap-mid-right top-right gap-right trim-right"
"trim-left gap-left window--left gap-mid-left door gap-mid-right window--right gap-right trim-right"
"trim-left trim--bottom-left trim--bottom-left trim--bottom-left door trim--bottom-right trim--bottom-right trim--bottom-right trim-right";
grid-template-columns: 0.5vh 1vh 2fr 1vh 3fr 1vh 2fr 1vh 0.5vh;
grid-template-rows: 1fr 1vh 5fr 0.75vh;
justify-self: center;
width: 80%;
}
.small-room__trim {
background-color: map-get($small-room, "frame");
height: 100%;
width: 100%;
&--left {
grid-area: trim-left;
justify-self: start;
}
&--right {
grid-area: trim-right;
justify-self: end;
}
&--bottom-left {
grid-area: trim--bottom-left;
}
&--bottom-right {
grid-area: trim--bottom-right;
}
}
.small-room__sign {
background-color: map-get($small-room, "sign");
display: grid;
gap: 0.1vh;
grid-area: top-mid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
height: 2vh;
justify-self: center;
padding: 0.1vh;
width: 2vh;
.small-room__sign__stone {
border-radius: 100%;
&--white {
background-color: #FFF;
}
&--black {
background-color: #000;
}
}
}
.small-room__window {
align-self: center;
background: map-get($small-room, "window-x"), map-get($small-room, "window-y");
display: grid;
grid-template-columns: 1fr 5fr 1fr 5fr 1fr;
grid-template-rows: 1fr 5fr 1fr 5fr 1fr;
height: 60%;
width: 100%;
&--left {
grid-area: window--left;
}
&--right {
grid-area: window--right;
}
.small-room__window-frame {
background-color: map-get($small-room, "frame");
height: 100%;
width: 100%;
&--top {
grid-area: 1/1/2/6;
}
&--center {
grid-area: 3/1/4/6;
}
&--bottom {
grid-area: 5/1/6/6;
}
&--left {
grid-area: 1/1/6/2;
}
&--middle {
grid-area: 1/3/6/4;
}
&--right {
grid-area: 1/5/6/6;
}
}
}
.small-room__door {
grid-area: door;
background: map-get($small-room, "door");
display: grid;
grid-template-columns: 0.5vh 1fr 0.5vh;
grid-template-rows: 0.5vh 1fr;
height: 100%;
width: 100%;
.small-room__door-handle {
align-self: center;
background: map-get($small-room, "door-handle");
border-radius: 50%;
box-shadow: 0.1vh 0.25vh 0.4vh rgba(30, 30, 30, 0.7);
grid-area: 2/2/3/3;
height: 0.75vh;
justify-self: end;
margin: 0.5vh 0.5vh 0 0;
width: 0.75vh;
}
.small-room__door-frame {
background-color: map-get($small-room, "frame");
height: 100%;
width: 100%;
&--top {
grid-area: 1/1/2/3;
}
&--left {
grid-area: 1/1/3/2;
}
&--left {
grid-area: 1/3/3/4;
}
}
}
}
}

View file

@ -0,0 +1,32 @@
import React from 'react';
import { render } from '@testing-library/react';
import { BrowserRouter as Router } from 'react-router-dom';
import Room from './Room';
const roomData = {
id: 1,
name: "main",
description: "A general place to play Go",
language: "EN"
}
test('renders RoomButton without crashing', () => {
const { getByTestId } = render(
<Router>
<Room room={roomData} />
</Router>
);
const RoomButton = getByTestId('RoomButton');
expect(RoomButton).toBeInTheDocument();
});
test('renders RoomButton with Room name', () => {
const { getByTestId } = render(
<Router>
<Room room={roomData} />
</Router>
);
const RoomButton = getByTestId('RoomButton');
expect(RoomButton).toHaveTextContent('main');
});

View file

@ -0,0 +1,18 @@
import React from 'react';
import './RoomArchive.scss';
const RoomArchiveButton = () => {
return (
<div
className="nav__section"
>
<p
className="nav__section__label"
>
Room Archive
</p>
</div>
);
}
export default RoomArchiveButton;

View file

@ -0,0 +1,9 @@
import React from 'react';
const Development = () => {
return (<>
<p>This Feature is in Development :)</p>
</>);
}
export default Development;

View file

@ -0,0 +1,80 @@
import React from 'react';
import './Loading.scss';
const Loading = () => {
return (
<div
className="Loading"
>
<div
className="point"
id="a-1"
></div>
<div
className="point"
id="a-2"
></div>
<div
className="point"
id="a-3"
></div>
<div
className="point"
id="a-4"
></div>
<div
className="point"
id="b-1"
></div>
<div
className="point"
id="b-2"
></div>
<div
className="point"
id="b-3"
></div>
<div
className="point"
id="b-4"
></div>
<div
className="point"
id="c-1"
></div>
<div
className="point"
id="c-2"
></div>
<div
className="point"
id="c-3"
></div>
<div
className="point"
id="c-4"
></div>
<div
className="point"
id="d-1"
></div>
<div
className="point"
id="d-2"
></div>
<div
className="point"
id="d-3"
></div>
<div
className="point"
id="d-4"
></div>
</div>
);
}
export default Loading;

View file

@ -0,0 +1,186 @@
div.Loading {
background-color: rgb(234, 178, 78);
display: flex;
flex-flow: row wrap;
height: 6vw;
left: 47vw;
position: absolute;
top: 45vh;
width: 6vw;
div.point {
height: 1vw;
width: 1vw;
border-radius: 50%;
padding: 0.25vw;
}
#a-1 {
animation-name: white-stone--corner;
animation-duration: 4s;
animation-delay: -2.75s;
animation-iteration-count: infinite;
}
#a-2 {
background-color: black;
animation-name: black-stone;
animation-duration: 4s;
animation-delay: -1s;
animation-iteration-count: infinite;
}
#a-3 {
background-color: white;
animation-name: white-stone;
animation-duration: 4s;
animation-delay: -2.25s;
animation-iteration-count: infinite;
}
#a-4 {
animation-name: black-stone--corner;
animation-duration: 4s;
animation-delay: 0s;
animation-iteration-count: infinite;
}
#b-1 {
background-color: black;
animation-name: black-stone;
animation-duration: 4s;
animation-delay: -1.5s;
animation-iteration-count: infinite;
}
#b-2 {
animation-name: white-stone--center;
animation-duration: 4s;
animation-delay: -3.25s;
animation-iteration-count: infinite;
}
#b-3 {
background-color: black;
animation-name: black-stone--center;
animation-duration: 4s;
animation-delay: -.5s;
animation-iteration-count: infinite;
}
#b-4 {
background-color: white;
animation-name: white-stone;
animation-duration: 4s;
animation-delay: -1.75s;
animation-iteration-count: infinite;
}
#c-1 {
animation-name: white-stone;
animation-duration: 4s;
animation-delay: -3.75s;
animation-iteration-count: infinite;
}
#c-2 {
background-color: black;
animation-name: black-stone--center;
animation-duration: 4s;
animation-delay: -2.5s;
animation-iteration-count: infinite;
}
#c-3 {
background-color: white;
animation-name: white-stone--center;
animation-duration: 4s;
animation-delay: -1.25s;
animation-iteration-count: infinite;
}
#c-4 {
animation-name: black-stone;
animation-duration: 4s;
animation-delay: -3.5s;
animation-iteration-count: infinite;
}
#d-1 {
background-color: black;
animation-name: black-stone--corner;
animation-duration: 4s;
animation-delay: -2s;
animation-iteration-count: infinite;
}
#d-2 {
background-color: white;
animation-name: white-stone;
animation-duration: 4s;
animation-delay: -.25s;
animation-iteration-count: infinite;
}
#d-3 {
animation-name: black-stone;
animation-duration: 4s;
animation-delay: -3s;
animation-iteration-count: infinite;
}
#d-4 {
background-color: white;
animation-name: white-stone--corner;
animation-duration: 4s;
animation-delay: -.75s;
animation-iteration-count: infinite;
}
}
@keyframes black-stone {
0% {background: black;}
67.5% {background: black;}
70% {background: repeating-linear-gradient(0deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%),
repeating-linear-gradient(90deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%);}
99.5% {background: repeating-linear-gradient(0deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%),
repeating-linear-gradient(90deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%);}
}
@keyframes white-stone {
0% {background: white;}
60% {background: white;}
62.5% {background: repeating-linear-gradient(0deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%),
repeating-linear-gradient(90deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%);}
92.5% {background: repeating-linear-gradient(0deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%),
repeating-linear-gradient(90deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%);}
95% {background: white;}
}
@keyframes black-stone--corner {
0% {background: black;}
52.5% {background: black;};
55% {background: repeating-linear-gradient(0deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%),
repeating-linear-gradient(90deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%);};
99% {background: repeating-linear-gradient(0deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%),
repeating-linear-gradient(90deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%);};
}
@keyframes white-stone--corner {
0% {background: white;}
47.5% {background: white;};
50% {background: repeating-linear-gradient(0deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%),
repeating-linear-gradient(90deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%);};
95% {background: repeating-linear-gradient(0deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%),
repeating-linear-gradient(90deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%);};
97.5% {background: white;}
}
@keyframes black-stone--center {
0% {background: black;}
75.5% {background: black;};
77.5% {background: repeating-linear-gradient(0deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%),
repeating-linear-gradient(90deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%);};
99% {background: repeating-linear-gradient(0deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%),
repeating-linear-gradient(90deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%);};
}
@keyframes white-stone--center {
0% {background: white;}
72.5% {background: white;};
75% {background: repeating-linear-gradient(0deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%),
repeating-linear-gradient(90deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%);};
97.5% {background: repeating-linear-gradient(0deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%),
repeating-linear-gradient(90deg, transparent 0%, transparent 45%, black 50%, transparent 55%, transparent 100%);};
100% {background: white;}
}

View file

@ -0,0 +1,18 @@
import React from 'react';
import './Message.scss';
const Message = (props) => {
const messageData = props.message;
return (
<div
className="Message"
data-testid="Message"
>
<p>{messageData.content}</p>
<p>{messageData.username}</p>
<p>{messageData.admin ? 'admin' : <></>}</p>
</div>
);
}
export default Message;

View file

@ -0,0 +1,16 @@
import React from 'react';
import { render } from '@testing-library/react';
import Message from './Message';
const messageData = [
{
"content": "Hey! Welcome to the general room!", "username": "userOne", "admin": true
}
];
test('renders Message without crashing', () => {
const { getByTestId } = render( <Message message={messageData} /> );
const messageDiv = getByTestId('Message');
expect(messageDiv).toBeInTheDocument();
});

View file

@ -0,0 +1,25 @@
import React from 'react';
import './RoomDetail.scss';
const RoomDetail = ({room}) => {
return (
<div className="RoomDetail">
<div className="RoomDetail__arrow"></div>
<div className="RoomDetail__data">
<div className="RoomDetail__heading">
<h3 className="RoomDetail__room-name">{room.name}</h3>
<div className="RoomDetail__room-lang"></div>
<p className="RoomDetail__room-description">{room.description}</p>
</div>
<div className="RoomDetail__current">
<h4 className="RoomDetail__current-header">Current:</h4>
<p className="RoomDetail__current-players"></p>
<p className="RoomDetail__current-games"></p>
<p className="RoomDetail__current-rank"></p>
</div>
</div>
</div>
);
}
export default RoomDetail;

View file

@ -0,0 +1,32 @@
@import '../../../../public/stylesheets/partials/mixins';
@import '../../../../public/stylesheets/partials/variables';
div.RoomDetail {
display: grid;
grid-template-columns: [left] 1fr [right];
grid-template-rows: [top] 1fr [middle] 9fr [bottom];
height: 20vh;
padding: 1vw;
width: 20vh;
div.RoomDetail__arrow {
border-top: map-get($colors, 'home') solid 2vh;
border-left: white solid 1.5vh;
height: 0;
}
div.RoomDetail__data {
background-color: white;
height: 90%;
width: 100%;
padding: 1vh;
display: grid;
grid-template-rows: 2fr 3fr;
.RoomDetail__heading {
display: flex;
flex-flow: row wrap;
align-items: flex-start;
}
}
}

View file

@ -0,0 +1,16 @@
import React from 'react';
import './ActionError.scss';
const ActionError = (props) => {
const errorMessage = props.error || '';
return (
<span
className="error error--action"
data-testid="ActionError"
>
{errorMessage}
</span>
);
}
export default ActionError;

View file

@ -0,0 +1,17 @@
import React from 'react';
import { render } from '@testing-library/react';
import ActionError from './ActionError';
test('renders ActionError without crashing', () => {
const { getByTestId } = render(<ActionError />);
const ActionErrorSpan = getByTestId('ActionError');
expect(ActionErrorSpan).toBeInTheDocument();
});
test('renders ActionError with error message', () => {
const errorMessage = "User already exists!";
const { getByTestId } = render(<ActionError error={errorMessage}/>);
const ActionErrorSpan = getByTestId('ActionError');
expect(ActionErrorSpan).toHaveTextContent(errorMessage);
})

View file

@ -4,7 +4,10 @@ import './FormError.scss';
const FormError = (props) => { const FormError = (props) => {
const errorMessage = props.error; const errorMessage = props.error;
return ( return (
<span data-testid="FormError" className="FormError"> <span
className="error error--form"
data-testid="FormError"
>
{errorMessage} {errorMessage}
</span> </span>
); );

View file

@ -0,0 +1,5 @@
@import '../../../../public/stylesheets/partials/variables';
span.FormError {
color: map-get($colors, "error");;
}

View file

@ -3,7 +3,7 @@ import { render } from '@testing-library/react';
import FormError from './FormError'; import FormError from './FormError';
test('renders FormError without crashing', () => { test('renders FormError without crashing', () => {
const { getByTestId } = render(<FormError />); const { getByTestId } = render(<FormError error={''}/>);
const FormErrorSpan = getByTestId('FormError'); const FormErrorSpan = getByTestId('FormError');
expect(FormErrorSpan).toBeInTheDocument(); expect(FormErrorSpan).toBeInTheDocument();
}); });

View file

@ -0,0 +1,49 @@
import React, { useState } from "react";
import Login from "../Login/Login";
import Signup from "../Signup/Signup";
import Guest from "../../Button/Guest/Guest";
const Auth = (props) => {
const [showForm, setShowForm] = useState("login");
const { state, dispatch } = props;
return (
<>
<div
className="nav__section nav__section--auth"
onClick={() => {
setShowForm("login");
}}
>
<p className="nav__section__label">Login</p>
{showForm === "login" ? (
<Login dispatch={dispatch} state={state} />
) : (
<></>
)}
</div>
<div
className="nav__section nav__section--auth"
onClick={() => {
setShowForm("signup");
}}
>
<p className="nav__section__label">Signup</p>
{showForm === "signup" ? (
<Signup dispatch={dispatch} state={state} />
) : (
<></>
)}
</div>
<div className="nav__section nav__section--auth">
<Guest dispatch={dispatch} />
</div>
</>
);
};
export default Auth;

View file

@ -0,0 +1,18 @@
import React from 'react';
import './FindGame.scss';
const FindGameForm = () => {
return (
<div
className="nav__section"
>
<p
className="nav__section__label"
>
Find Game
</p>
</div>
);
}
export default FindGameForm;

View file

@ -0,0 +1,18 @@
import React from 'react';
import './FindRoom.scss';
const FindRoomForm = () => {
return (
<div
className="nav__section"
>
<p
className="nav__section__label"
>
Find Room
</p>
</div>
);
}
export default FindRoomForm;

View file

@ -36,9 +36,8 @@ const Login = (props) => {
username, username,
password password
}) })
const parsedResponse = JSON.parse(loginResponse); if (loginResponse.errors) {
if (parsedResponse.errors) { const authError = loginResponse.errors
const authError = parsedResponse.errors
return props.dispatch({ return props.dispatch({
...errorDispatchAction, ...errorDispatchAction,
body: { authError } body: { authError }
@ -48,7 +47,7 @@ const Login = (props) => {
return props.dispatch({ return props.dispatch({
type: 'AUTH', type: 'AUTH',
message: 'LOGIN', message: 'LOGIN',
body: parsedResponse body: loginResponse
}) })
} }
@ -69,29 +68,42 @@ const Login = (props) => {
<div className="Login" data-testid="Login"> <div className="Login" data-testid="Login">
{formError(props.state.errors)} {formError(props.state.errors)}
<form <form
className="Login__form"
data-testid="Login__form" data-testid="Login__form"
onSubmit={e => handleSubmit(e)} onSubmit={e => handleSubmit(e)}
> >
<label htmlFor="username-input">Username:</label> <label
className="form__label form__label--username"
htmlFor="username-input"
>Username:</label>
<input <input
name="username" name="username"
id="username-input" id="username-input"
className="form__input form__input--username"
type="text" type="text"
onChange={e => setUsername(e.target.value)} onChange={e => setUsername(e.target.value)}
default="username" default="username"
/> />
<label htmlFor="password-input">Password:</label> <label
className="form__label form__label--password"
htmlFor="password-input"
>Password:</label>
<input <input
name="password" name="password"
id="password-input" id="password-input"
className="form__input form__input--password"
type="password" type="password"
onChange={e => setPassword(e.target.value)} onChange={e => setPassword(e.target.value)}
default="" default=""
/> />
<input type="submit" value="Login!" /> <input
className="form__submit"
type="submit"
value="Login!"
/>
</form> </form>
</div> </div>

View file

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import Login from './Login'; import Login from './Login';
import { initState } from '../../reducers/init/stateReducer.init'; import { initState } from '../../../reducers/init/reducer.init';
test('renders Login without crashing', () => { test('renders Login without crashing', () => {
const state = initState(); const state = initState();

View file

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react'; import React, { useState } from 'react';
import './Signup.scss'; import './Signup.scss';
import authServices from '../../../services/authServices'; import authServices from '../../../services/authServices';
import FormError from '../../Error/FormError/FormError'; import FormError from '../../Error/FormError/FormError';
@ -22,7 +22,7 @@ const Signup = (props) => {
}) })
} }
if (password.length < 8) { if (password.length < minimumPasswordLength) {
return props.dispatch({ return props.dispatch({
...errorDispatchAction, ...errorDispatchAction,
body: { authError: 'Password must be at least 8 characters'} body: { authError: 'Password must be at least 8 characters'}
@ -53,10 +53,9 @@ const Signup = (props) => {
password, password,
confirmPassword confirmPassword
}) })
const parsedResponse = JSON.parse(signupResponse)
if (parsedResponse.errors) { if (signupResponse.errors) {
const authError = parsedResponse.errors[0].auth const authError = signupResponse.errors[0].auth
return props.dispatch({ return props.dispatch({
...errorDispatchAction, ...errorDispatchAction,
body: { authError } body: { authError }
@ -66,7 +65,7 @@ const Signup = (props) => {
return props.dispatch({ return props.dispatch({
type: 'AUTH', type: 'AUTH',
message: 'SIGNUP', message: 'SIGNUP',
body: parsedResponse body: signupResponse
}) })
} }
@ -88,39 +87,56 @@ const Signup = (props) => {
<div className="Signup" data-testid="Signup"> <div className="Signup" data-testid="Signup">
{formError(props.state.errors)} {formError(props.state.errors)}
<form <form
className="Signup__form"
data-testid="Signup__form" data-testid="Signup__form"
onSubmit={e => handleSubmit(e)} onSubmit={e => handleSubmit(e)}
> >
<label htmlFor="username-input">Username:</label> <label
className="form__label form__label--username"
htmlFor="username-input"
>Username:</label>
<input <input
name="username" className="form__input form__input--username"
id="username-input"
type="text"
onChange={e => setUsername(e.target.value)}
default="username" default="username"
/> id="username-input"
name="username"
<label htmlFor="email-input">Email:</label> onChange={e => setUsername(e.target.value)}
<input
name="email"
id="email-input"
type="text" type="text"
onChange={e => setEmail(e.target.value)} />
<label
className="form__label form__label--email"
htmlFor="email-input"
>Email:</label>
<input
className="form__input form__input--email"
default="email" default="email"
id="email-input"
name="email"
onChange={e => setEmail(e.target.value)}
type="text"
/> />
<label htmlFor="password-input">Password:</label> <label
className="form__label form__label--password"
htmlFor="password-input"
>Password:</label>
<input <input
name="password" className="form__input form__input--password"
id="password-input"
type="password"
onChange={e => setPassword(e.target.value)}
default="" default=""
id="password-input"
name="password"
onChange={e => setPassword(e.target.value)}
type="password"
/> />
<label htmlFor="confirmPassword-input">Confirm password:</label> <label
className="form__label form__label--password"
htmlFor="confirmPassword-input"
>Confirm password:</label>
<input <input
className="form__input form__input--password"
name="confirmPassword" name="confirmPassword"
id="confirmPassword-input" id="confirmPassword-input"
type="password" type="password"
@ -128,7 +144,11 @@ const Signup = (props) => {
default="" default=""
/> />
<input type="submit" value="Create Account!"/> <input
className="form__submit"
type="submit"
value="Create Account!"
/>
</form> </form>
</div> </div>

View file

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import Signup from './Signup'; import Signup from './Signup';
import { initState } from '../../reducers/init/stateReducer.init'; import { initState } from '../../../reducers/init/reducer.init';
test('renders Signup without crashing', () => { test('renders Signup without crashing', () => {
const { getByTestId } = render(<Signup state={initState()}/>); const { getByTestId } = render(<Signup state={initState()}/>);

View file

@ -0,0 +1,74 @@
import React from "react";
import "./Board.scss";
import Point from "../Point/Point";
const Board = (props) => {
const { game, user, dispatch, board, meta } = props;
const sizeFlag = `Game__board--size-${game.boardSize}`;
const hoshiPoints = {
9: { "3-3": true, "7-7": true, "3-7": true, "7-3": true },
13: {
"7-7": true,
"10-7": true,
"7-4": true,
"7-10": true,
"4-7": true,
"4-4": true,
"10-10": true,
"4-10": true,
"10-4": true,
},
19: {
"10-10": true,
"16-10": true,
"10-4": true,
"10-16": true,
"4-10": true,
"4-4": true,
"16-16": true,
"4-16": true,
"16-4": true,
},
};
const isHoshi = (posX, posY) =>
hoshiPoints[game.boardSize][`${posX}-${posY}`];
const renderPoints = (boardSize) => {
let i = 0,
boardPoints = [];
while (i < boardSize * boardSize) {
const posX = Math.floor(i / boardSize) + 1;
const posY = (i % boardSize) + 1;
const pointData = board[`${posX}-${posY}`];
const dotData =
meta && meta.turn === 0 && !meta.winner && meta.territory
? meta.territory[`${posX}-${posY}`]
: game.turn || meta?.turn;
boardPoints.push(
<Point
key={`${posX}-${posY}`}
posX={posX}
posY={posY}
pointData={pointData}
dotData={dotData}
dispatch={dispatch}
user={user}
meta={meta}
hoshi={isHoshi(posX, posY)}
{...props}
/>
);
i++;
}
return boardPoints;
};
return (
<div className={`Game__board ${sizeFlag}`}>
{game.id ? renderPoints(game.boardSize) : <></>}
</div>
);
};
export default Board;

View file

@ -0,0 +1,61 @@
@import '../../../../public/stylesheets/partials/mixins';
@import '../../../../public/stylesheets/partials/variables';
div.Game__board {
// @include fullspan;
background: radial-gradient(farthest-corner at 55% 40%, rgb(244, 230, 120) 0%, rgb(234, 178, 78) 65%, rgb(200, 160, 90) 90%, rgb(200, 140, 90) 100%);
background-size: cover;
box-shadow: -2vmin 4vmin 3vmin rgba(145, 92, 23, 0.5);
display: grid;
// flex: 1;
margin: 0 auto;
z-index: 1;
&--size-9 {
grid-template-columns: repeat(9, 1fr);
grid-template-rows: repeat(9, 1fr);
div.board__point {
width: 9vmin;
height: 9vmin;
@media #{map-get($break-points, "570")} {
height: 7.5vh;
width: 7.5vh;
}
}
}
&--size-13 {
grid-template-columns: repeat(13, 1fr);
grid-template-rows: repeat(13, 1fr);
div.board__point {
width: 7vmin;
height: 7vmin;
@media #{map-get($break-points, "570")} {
height: 5vh;
width: 5vh;
}
}
}
&--size-19 {
grid-template-columns: repeat(19, 1fr);
grid-template-rows: repeat(19, 1fr);
div.board__point {
width: 5vmin;
height: 5vmin;
@media #{map-get($break-points, "590")} {
height: 2.6vw;
width: 2.6vw;
max-width: 3.5vh;
max-height: 3.5vh;
}
}
}
}

View file

@ -0,0 +1,12 @@
import React from 'react';
import './GameTree.scss';
const GameTree = () => {
return (
<div className="GameTree">
</div>
);
}
export default GameTree;

View file

@ -0,0 +1,15 @@
import React from "react";
import "./Kifu.scss";
const Kifu = ({ clickKifu }) => {
return (
<div className="Kifu">
<p className="Kifu__show-menu" onClick={clickKifu}>
Show Menu?
</p>
<div className="Kifu__board"></div>
</div>
);
};
export default Kifu;

View file

@ -0,0 +1,31 @@
@import '../../../../public/stylesheets/partials/mixins';
div.Kifu {
order: 0;
height: 10vh;
width: 8vh;
background-color: #FFF;
transform: rotate(-20deg);
p.Kifu__show-menu {
display: none;
margin: 3vh auto;
position: absolute;
transform: rotate(20deg);
width: fit-content;
}
&:hover {
p.Kifu__show-menu {
@include gameViewLabel;
cursor: pointer;
display: inline-block;
}
}
div.Kifu__board {
border: 0.25vh solid #444;
width: 5vh;
height: 5vh;
margin: 1vh 2vh 2vh auto;
}
}

View file

@ -0,0 +1,194 @@
import React, { useEffect, useRef } from "react";
import "./Menu.scss";
const Menu = ({ showMenu, clickClose, ...props }) => {
const { active, meta } = props.state; // active.game.boardSize, meta.gameRecord
const boardSize = active.game.boardSize;
const handleBackgroundClick = (e) => {
if (e.target.className === "Game__Menu-container") clickClose();
};
const canvasRef = useRef();
const overflowRef = useRef();
const drawGameRecord = () => {
return (
<>
<canvas ref={canvasRef} />
<canvas ref={overflowRef} />
</>
);
};
const clickPrint = () => {
const iframe = document.createElement("iframe");
const body = document.getElementsByTagName("body")[0];
const record = document.getElementById("record");
body.appendChild(iframe);
// move game record to iframe and print iframe
iframe.contentDocument.body.appendChild(canvasRef.current);
iframe.contentDocument.body.appendChild(overflowRef.current);
iframe.contentWindow.print();
// move game record back to menu and remove iframe
record.appendChild(canvasRef.current);
record.appendChild(overflowRef.current);
body.removeChild(iframe);
};
// draw canvases
useEffect(() => {
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
const scale = Math.min(window.innerWidth * 0.75, 500);
canvas.height = scale;
canvas.width = scale;
const space = scale / boardSize;
const offset = space / 2;
for (let i = 0; i < boardSize; i++) {
const start = i * space + offset;
const end = scale - offset;
ctx.beginPath();
ctx.moveTo(start, offset);
ctx.lineTo(start, end);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(offset, start);
ctx.lineTo(end, start);
ctx.closePath();
ctx.stroke();
}
if (!meta?.gameRecord) return;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
const { overflow } = meta.gameRecord.reduce(
(dict, { player, pos }, index) => {
const past = dict[`${pos.x}-${pos.y}`];
if (past) {
// overflow: [ { move:#, player:'color', subsequentMoves: [ { move: #, player: 'color' } ] } ]
if (dict.overflow) {
const indexOfPrior = dict.overflow.findIndex(
({ move }) => move === past
);
if (indexOfPrior !== -1) {
// if multiple past moves at this point exist
dict.overflow[indexOfPrior].subsequentMoves.push({
move: index + 1,
player,
});
return dict;
}
// if a second move at this point has not yet been encountered
// prior move will be black if no active handicap and move is odd or if active handicap and move is even
const playerPrior =
(active.handicap && !(past % 2)) || past % 2 ? "black" : "white";
return {
...dict,
overflow: [
...dict.overflow,
{
move: past,
player: playerPrior,
subsequentMoves: [{ move: index + 1, player }],
},
],
};
}
// if no move has yet been encountered at a previously made move
return {
...dict,
overflow: [
{ move: past, subsequentMoves: [{ move: index + 1, player }] },
],
};
}
ctx.beginPath();
ctx.arc(
(pos.y - 1) * space + offset,
(pos.x - 1) * space + offset,
offset * 0.95,
0,
Math.PI * 2,
true
);
ctx.stroke();
ctx.fillStyle = player === "white" ? "#fff" : "#000";
ctx.fill();
ctx.fillStyle = player === "white" ? "#000" : "#fff";
ctx.fillText(
index + 1,
(pos.y - 1) * space + offset,
(pos.x - 1) * space + offset
);
return { ...dict, [`${pos.x}-${pos.y}`]: index + 1 };
},
{}
);
if (!overflow?.length) return;
// Draw Overflow Moves (moves made at prior points)
const canvas2 = overflowRef.current;
const ctx2 = canvas2.getContext("2d");
canvas2.width = scale;
canvas2.height = space * overflow.length;
ctx2.textAlign = "center";
ctx2.textBaseline = "middle";
overflow.forEach(({ move, subsequentMoves, player }, index) => {
subsequentMoves.forEach(({ player, move }, subIndex) => {
ctx2.beginPath();
ctx2.arc(
subIndex * space + offset,
index * space + offset,
offset * 0.95,
0,
Math.PI * 2,
true
);
ctx2.stroke();
ctx2.fillStyle = player === "white" ? "#fff" : "#000";
ctx2.fill();
ctx2.fillStyle = player === "white" ? "#000" : "#fff";
ctx2.fillText(move, subIndex * space + offset, index * space + offset);
});
ctx2.fillStyle = "#000";
ctx2.fillText(
"at",
subsequentMoves.length * space + offset,
index * space + offset
);
ctx2.fillStyle = player === "white" ? "#fff" : "#000";
ctx2.beginPath();
ctx2.arc(
(subsequentMoves.length + 1) * space + offset,
index * space + offset,
offset * 0.95,
0,
Math.PI * 2,
true
);
ctx2.fill();
ctx2.stroke();
ctx2.fillStyle = player === "white" ? "#000" : "#fff";
ctx2.fillText(
move,
(subsequentMoves.length + 1) * space + offset,
index * space + offset
);
});
}, [showMenu, meta, active.handicap, boardSize]);
return (
<div
className={`Game__Menu-container${showMenu ? "" : "--hidden"}`}
onClick={(e) => handleBackgroundClick(e)}
>
<div className="Game__Menu-container__Menu">
<button onClick={clickClose}>X</button>
<button onClick={clickPrint}>Print Record</button>
<div id="record" className="Game__Menu__game-record-container">
{drawGameRecord()}
</div>
</div>
</div>
);
};
export default Menu;

View file

@ -0,0 +1,29 @@
div.Game__Menu-container {
&--hidden {
display: none;
}
background: rgba(0,0,0,0.5);
display: flex;
flex-flow: column nowrap;
justify-content: center;
align-items: center;
height: 100vh;
position: absolute;
width: 100vw;
z-index: 2;
.Game__Menu-container__Menu {
background: #eef;
max-height: 90vh;
max-width: 80vw;
padding: 2vh;
overflow: scroll;
}
div.Game__Menu__game-record-container {
display: flex;
flex-flow: column nowrap;
align-items: center;
}
}

View file

@ -0,0 +1,11 @@
import React from 'react';
import './MessageContainer.scss';
const MessageContainer = () => {
return (
<>
</>
);
}
export default MessageContainer;

View file

@ -0,0 +1,66 @@
import React from "react";
import "./PlayerArea.scss";
const PlayerArea = ({
handleResignClick,
handlePassClick,
playerMeta,
turn,
Kifu,
}) => {
const { stones, player, rank, captures } = playerMeta;
const isTurn =
(stones === "black" && turn === 1) || (stones === "white" && turn === -1);
const bowlAttributes = () => {
if (isTurn || turn === 0)
return {
"data-turn": true,
onClick: () => handlePassClick(stones),
};
return null;
};
const bowlText = () => {
if (isTurn) return "Pass?";
if (turn === 0) return "End Game?";
};
return (
<div className={`player-container player-container--${stones}`}>
<div
className={`player-container__bowl player-container__bowl--${stones}`}
{...bowlAttributes()}
>
<p>{bowlText()}</p>
</div>
{Kifu}
<div
className={`player-container__name-space player-container__name-space--${stones}`}
>
<h4>
{playerMeta
? `${player || stones} ${rank || "?"}`
: "Waiting for player"}
</h4>
<div
className={`player-container__caps-space player-container__caps-space__${stones}`}
>
<p
className={`player-container__resign-message player-container__resign-message--${stones}`}
{...(isTurn ? { onClick: () => handleResignClick(stones) } : null)}
>
Resign?
</p>
<p
className={`player-container__caps-counter player-container__caps-counter--${stones}`}
>
{playerMeta ? captures : "Captures go here"}
</p>
</div>
</div>
</div>
);
};
export default PlayerArea;

View file

@ -0,0 +1,102 @@
@import '../../../../public/stylesheets/partials/variables';
@import '../../../../public/stylesheets/partials/mixins';
div.player-container {
align-items: flex-end;
display: flex;
flex: 5;
height: 9vmin;
justify-content: space-around;
margin: 1em;
width: 100%;
&:last-child {
align-items: flex-start;
flex-direction: row-reverse;
justify-self: flex-end;
}
@media #{map-get($break-points, "500")} {
height: 14vh;
}
div.player-container__bowl {
align-items: center;
background: radial-gradient(farthest-corner at 48% 54%, rgba(30, 5, 0, 0.25) 0%, rgba(30, 5, 0, 0.45) 2%, rgba(30, 5, 0, 0.75) 32%, rgba(0,0,0,0.85)35%, rgb(116,48,17) 48%, rgb(140, 60, 40) 52%, rgb(100, 40, 5) 55%, rgb(116, 48, 17) 58%, rgb(140,60,40) 65%, rgb(100, 40, 5) 80%, rgb(80, 20, 0) 90%);
background-color: rgb(116, 48, 17);
border-radius: 50%;
box-shadow: -1vmin 2vmin 1.5vmin rgba(83, 53, 35, 0.61);
display: flex;
height: 15vh;
justify-content: center;
margin: 4vh;
order: -1;
width: 15vh;
@media #{map-get($break-points, "570")} {
height: 10vh;
margin: 3vh;
width: 10vh;
}
&[data-turn]:hover p {
background-color: rgba(0,0,0,0.3);
color: #FFF;
cursor: grab;
display: block;
padding: .5em;
}
&[data-turn] {
// highlight for turn
box-shadow: 0 0 3vh 3vh rgb(255, 175, 2);
& + .player-container__name-space .player-container__caps-space:hover :first-child {
background-color: rgba(0,0,0,0.7);
cursor: grab;
display: block;
padding: .5em;
position: absolute;
}
}
p {
display: none;
}
}
}
div.player-container__caps-space {
align-items: center;
background: radial-gradient(farthest-side at 49% 52%, rgb(150, 75, 50) 0%, rgb(116,48,17) 35%, rgb(116,48,17) 64%, rgb(80, 20, 0) 65%, rgb(175, 140, 95) 70%, rgb(120, 50, 40) 80%, rgb(80, 20, 0) 95%, rgb(175, 140, 95) 100%);
border-radius: 50%;
box-shadow: -0.5vmin 1vmin 1vmin rgba(83, 53, 35, 0.61);
color: #FFF;
display: flex;
height: 10vh;
justify-content: center;
margin: 1vh;
width: 10vh;
@media #{map-get($break-points, "570")} {
height: 7vh;
margin: 2vh;
width: 7vh;
}
& :first-child {
display: none;
}
}
div.player-container__name-space {
align-items: center;
flex-direction: column;
display: flex;
order: 1;
h4 {
@include gameViewLabel;
font-size: 120%;
z-index: 1;
}
}

View file

@ -0,0 +1,96 @@
import React from "react";
import "./Point.scss";
const Point = (props) => {
const {
posX,
posY,
user,
game,
meta,
dispatch,
pointData,
dotData,
hoshi,
} = props;
const turn =
meta && meta.turn
? meta.turn > 0
? "black"
: "white"
: game.turn > 0
? "black"
: "white";
const stone = () => {
if (pointData === 1) return "black";
if (pointData === -1) return "white";
if (pointData === "k") return "ko";
return "none";
};
const xFlag = () => {
if (posX === 1) return `board__point--top`;
if (posX === game.boardSize) return `board__point--bottom`;
return "";
};
const yFlag = () => {
if (posY === 1) return `board__point--left`;
if (posY === game.boardSize) return `board__point--right`;
return "";
};
const clickHandle = (e) => {
if (meta?.turn === 0 && !meta?.winner) {
const action = {
type: "SOCKET",
message: "TOGGLE_TERRITORY",
body: { user, point: `${posX}-${posY}`, game, room: game.room },
};
return dispatch(action);
}
const action = {
type: "SOCKET",
message: "MAKE_MOVE",
body: {
user,
game,
room: game.room,
board: {},
move: { player: turn, pos: { x: posX, y: posY } },
},
};
dispatch(action);
};
const getDot = () => {
if (meta?.turn === 0) {
switch (dotData) {
case -1:
return "white";
case 1:
return "black";
case "d":
return "dame";
default:
return 0;
}
}
return dotData;
};
return (
<div
className={`board__point ${xFlag()} ${yFlag()}`}
onClick={(e) => clickHandle(e)}
>
<div
className={`board__point__stone ${hoshi ? "hoshi" : ""}`}
data-stone={stone()}
>
<div className="board__point__dot" data-dot={getDot()}></div>
</div>
</div>
);
};
export default Point;

View file

@ -0,0 +1,107 @@
div.board__point {
background: conic-gradient(#000 0%, rgba(0,0,0,0) 1%, rgba(0,0,0,0) 24%, #000 25%, rgba(0,0,0,0) 26%, rgba(0,0,0,0) 49%, #000 50%, rgba(0,0,0,0) 51%, rgba(0,0,0,0) 74%, #000 75%, rgba(0,0,0,0) 76%, rgba(0,0,0,0) 99%, #000 100%);
border-radius: 50% solid black;
color: black;
margin: auto;
padding: 0;
vertical-align: middle;
display: flex;
justify-content: center;
align-items: center;
}
div.board__point--top {
background: conic-gradient( rgba(0,0,0,0) 24%, #000 25%, rgba(0,0,0,0) 26%, rgba(0,0,0,0) 49%, #000 50%, rgba(0,0,0,0) 51%, rgba(0,0,0,0) 74%, #000 75%, rgba(0,0,0,0) 76%);
}
div.board__point--bottom {
background: conic-gradient(#000 0%, rgba(0,0,0,0) 1%, rgba(0,0,0,0) 24%, #000 25%, rgba(0,0,0,0) 26%, rgba(0,0,0,0) 74%, #000 75%, rgba(0,0,0,0) 76%, rgba(0,0,0,0) 99%, #000 100%);
}
div.board__point--left {
background: conic-gradient(#000 0%, rgba(0,0,0,0) 1%, rgba(0,0,0,0) 24%, #000 25%, rgba(0,0,0,0) 26%, rgba(0,0,0,0) 49%, #000 50%, rgba(0,0,0,0) 51%, rgba(0,0,0,0) 99%, #000 100%);
}
div.board__point--right {
background: conic-gradient(#000 0%, rgba(0,0,0,0) 1%, rgba(0,0,0,0) 49%, #000 50%, rgba(0,0,0,0) 51%, rgba(0,0,0,0) 74%, #000 75%, rgba(0,0,0,0) 76%, rgba(0,0,0,0) 99%, #000 100%);
}
div.board__point--top.board__point--left {
background: conic-gradient( rgba(0,0,0,0) 24%, #000 25%, rgba(0,0,0,0) 26%, rgba(0,0,0,0) 49%, #000 50%, rgba(0,0,0,0) 51%);
}
div.board__point--top.board__point--right {
background: conic-gradient( rgba(0,0,0,0) 49%, #000 50%, rgba(0,0,0,0) 51%, rgba(0,0,0,0) 74%, #000 75%, rgba(0,0,0,0) 76% );
}
div.board__point--bottom.board__point--left {
background: conic-gradient(#000 0%, rgba(0,0,0,0) 1%, rgba(0,0,0,0) 24%, #000 25%, rgba(0,0,0,0) 26%, rgba(0,0,0,0) 99%, #000 100%);
}
div.board__point--bottom.board__point--right {
background: conic-gradient(#000 0%, rgba(0,0,0,0) 1%, rgba(0,0,0,0) 74%, #000 75%, rgba(0,0,0,0) 76%, rgba(0,0,0,0) 99%, #000 100%);
}
div.board__point__stone {
width: 85%;
height: 85%;
border-radius: 50%;
vertical-align: middle;
display: flex;
flex-direction: column;
justify-content: center;
&.hoshi {
background: radial-gradient(circle farthest-corner at center, #000 0%, #000 14%, rgba(0,0,0,0) 15%);
z-index: 3;
}
}
div.board__point div.board__point__stone div.board__point__dot {
width: 35%;
height: 35%;
border-radius: 50%;
margin: auto;
vertical-align: middle;
}
div.board__point__stone{
&[data-stone="white"] {
background: radial-gradient(farthest-side at 55% 40%, white 0%, rgb(200,200,200) 65%, rgb(100,100,100) 90%, rgb(68, 50, 0) 100%);
box-shadow: -.25vmin .5vmin .5vmin rgba(145, 92, 23, 0.5);
}
&[data-stone="black"] {
background-color: black;
background: radial-gradient(farthest-side at 55% 40%, rgb(220,220,220) 0%, rgb(60,60,60) 45%, rgb(15,15,15) 90%, rgb(5, 5, 0) 100%);
box-shadow: -.25vmin .5vmin .5vmin rgba(145, 92, 23, 0.75);
}
&[data-stone="ko"] {
background-color: transparent;
border: .5vmin solid rgba(200,20,50,0.8);
border-radius: 0%;
height: 60%;
width: 60%;
}
}
div.board__point {
div.board__point__dot[data-dot="dame"] {
background: purple;
}
div.board__point__dot[data-dot="black"] {
background: black;
}
div.board__point__dot[data-dot="white"] {
background: white;
}
&:hover {
div.board__point__dot[data-dot="1"] {
background: black;
}
div.board__point__dot[data-dot="-1"] {
background: white;
}
}
}

View file

@ -0,0 +1,11 @@
import React from 'react';
import './Timer.scss';
const Timer = () => {
return (
<>
</>
);
}
export default Timer;

View file

@ -3,17 +3,17 @@
html * { html * {
margin: 0; margin: 0;
font-size: 14px;
@media #{map-get($break-points, "500")} { @media #{map-get($break-points, "500")} {
font-size: 14px; // font-size: 14px;
} }
} }
body { body {
margin: 0; margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", font-family: 'Oswald', sans-serif;
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", font-weight: 800;
sans-serif;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
box-sizing: border-box; box-sizing: border-box;
@ -32,11 +32,12 @@ body {
// ! dev settings // ! dev settings
aside { aside {
background-color: map-get($dev-colors, "e"); background-color: map-get($colors, "sidebar");
} }
main { main {
background-color: map-get($dev-colors, "d"); background-color: map-get($colors, "main");
font-weight: 100;
} }
} }
@ -44,3 +45,24 @@ code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace; monospace;
} }
h3 {
font-size: 140%;
font-weight: 600;
}
h4 {
font-size: 110%;
}
a:hover, a:active, {
text-decoration: underline;
}
.--link {
cursor: pointer;
&:hover {
text-decoration: underline;
}
}

View file

@ -0,0 +1,56 @@
import socketIOClient from "socket.io-client";
import config from "./config";
const launch = (nsp, dispatch) => {
const socket = socketIOClient(`${config.socketAddress}/${nsp}`);
socket.on("connected", () => {
dispatch({
type: "SOCKET",
message: "CONNECTED",
body: { nsp: socket.nsp },
});
});
socket.on("connect_error", (err) => {
dispatch({
type: "ERR",
message: "SOCKET_ERROR",
body: { socketError: err },
});
});
socket.on("error", (err) => {
dispatch({
type: "ERR",
message: "SOCKET_ERROR",
body: { socketError: err },
});
});
socket.on("room_connected", (data) => {
dispatch({ type: "ROOMS", message: "CONNECT_ROOM", body: data });
});
socket.on("new_user", (data) => {
dispatch({ type: "ROOMS", message: "NEW_USER", body: data });
});
socket.on("game_connected", (data) => {
dispatch({ type: "GAMES", message: "UPDATE_BOARD", body: data });
});
socket.on("update_board", (data) => {
dispatch({ type: "GAMES", message: "UPDATE_BOARD", body: data });
});
socket.on("game_resign", (data) => {
dispatch({ type: "GAMES", message: "GAME_RESIGN", body: data });
});
socket.on("end_game", (data) => {
dispatch({ type: "GAMES", message: "GAME_END", body: data });
});
return socket;
};
export { launch };

View file

@ -2,8 +2,8 @@ import React from 'react';
const AccountSidebar = () => { const AccountSidebar = () => {
return ( return (
<> <nav>
</> </nav>
); );
} }

View file

@ -0,0 +1,163 @@
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import gamesServices from "../../services/api/gamesServices";
import "./Game.scss";
import Logo from "../../components/Display/Logo/Logo";
import Board from "../../components/GameUI/Board/Board";
import PlayerArea from "../../components/GameUI/PlayerArea/PlayerArea";
import Kifu from "../../components/GameUI/Kifu/Kifu";
import Menu from "../../components/GameUI/Menu/Menu";
const Game = (props) => {
const { state, dispatch } = props;
const gameId = parseInt(useParams().id) || 0;
const [playerBlackMeta, setPlayerBlackMeta] = useState({});
const [playerWhiteMeta, setPlayerWhiteMeta] = useState({});
const [showMenu, setShowMenu] = useState(false);
const playerState = state?.meta?.playerState;
const game = state.active?.game;
useEffect(() => {
const fetchGameAPI = async () => {
const response = await gamesServices.getGameService(gameId);
if (response) {
const action = {
type: "GAMES",
message: "SET_ACTIVE",
body: response,
};
return dispatch(action);
}
};
fetchGameAPI();
}, [gameId, dispatch]);
useEffect(() => {
const roomSocketConnect = () => {
const game = state.active.game;
const user = state.user;
const action = {
type: "SOCKET",
message: "CONNECT_GAME",
body: { game, user, dispatch },
};
return dispatch(action);
};
roomSocketConnect();
}, [state.active.game, dispatch, state.user]);
useEffect(() => {
if (!game || !playerState) return;
const { playerBlack, playerBlackRank, playerWhite, playerWhiteRank } = game;
const { bCaptures, wCaptures } = playerState;
setPlayerBlackMeta({
player: playerBlack,
rank: playerBlackRank,
captures: bCaptures,
stones: "black",
});
setPlayerWhiteMeta({
player: playerWhite,
rank: playerWhiteRank,
captures: wCaptures,
stones: "white",
});
}, [playerState, game]);
const handleResignClick = (player) => {
const action = {
type: "SOCKET",
message: "RESIGN",
body: { game, player },
};
dispatch(action);
};
const handlePassClick = (player) => {
if (state?.meta && state?.meta?.winner) return;
if (state?.meta && state?.meta?.turn === 0) {
const action = {
type: "SOCKET",
message: "END_GAME",
body: { game, player },
};
return dispatch(action);
}
const action = {
type: "SOCKET",
message: "PASS",
body: { game, player },
};
dispatch(action);
};
return (
<div className="Game" data-testid="Game">
<Menu
showMenu={showMenu}
clickClose={() => setShowMenu(false)}
{...props}
/>
<div className="Game__meta-container">
<span className="Game__socket-flag">{state.socket ? "✓" : " ⃠"}</span>
<Logo />
{state?.meta?.winner ? (
<p>
{`winner: ${
state.meta.winner === 1
? playerBlackMeta?.player
: playerWhiteMeta?.player
}
`}
</p>
) : (
<></>
)}
<p>Timer</p>
<p>? Game Tree</p>
</div>
<div className="Game__board-container">
<PlayerArea
handleResignClick={handleResignClick}
handlePassClick={handlePassClick}
playerMeta={
state.user &&
playerBlackMeta.playerBlack &&
state.user === playerBlackMeta.playerBlack
? playerBlackMeta
: playerWhiteMeta
}
turn={state?.meta?.turn}
/>
<Board
dispatch={dispatch}
game={state.active.game}
meta={state.meta}
user={state.user}
board={state.board}
/>
<PlayerArea
handleResignClick={handleResignClick}
handlePassClick={handlePassClick}
playerMeta={
state.user &&
playerBlackMeta.playerWhite &&
state.user === playerWhiteMeta.playerWhite
? playerWhiteMeta
: playerBlackMeta
}
Kifu={<Kifu clickKifu={() => setShowMenu(true)} />}
turn={state?.meta?.turn}
/>
</div>
<div className="Game__message-container">
<p>Messages</p>
<p>Message Form</p>
</div>
</div>
);
};
export default Game;

View file

@ -0,0 +1,18 @@
div.Game {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
height: 100%;
width: 100%;
background: radial-gradient(
farthest-corner at 55% 40%,
rgb(150, 200, 220) 0%, rgb(97, 166, 194) 65%, rgb(70,100,120) 90%, rgb(40, 80, 90) 100%
);
}
div.Game__board-container {
display: flex;
flex-flow: column nowrap;
justify-content: space-evenly;
max-width: 50vw;
max-height: 100vh;
}

View file

@ -0,0 +1,16 @@
import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom'
import { render } from '@testing-library/react';
import { initState } from '../../reducers/init/reducer.init';
import Game from './Game';
const state = initState();
test('renders Game without crashing', () => {
const { getByTestId } = render(
<Router>
<Game state={state} dispatch={()=>{}}/>
</Router>
);
const GameDiv = getByTestId('Game');
expect(GameDiv).toBeInTheDocument();
});

View file

@ -0,0 +1,54 @@
import React, { useEffect, useState } from 'react';
import './Home.scss';
import roomsServices from '../../services/api/roomsServices';
import RoomButton from '../../components/Button/Room/Room';
import Loading from '../../components/Display/Loading/Loading';
const Home = props => {
const state = props.state || {};
const dispatch = props.dispatch;
const [ roomDetail, setRoomDetail ] = useState(1);
const showRoomDetail = id => roomDetail === id ? setRoomDetail(0) : setRoomDetail(id);
const renderRooms = () => {
const rooms = state.rooms || [];
if (rooms.length) {
return rooms.map(roomData => (
<RoomButton
key={`room-${roomData.id}`}
room={roomData}
roomDetail={roomDetail === roomData.id}
showRoomDetail={showRoomDetail}
/>
))
}
// TODO stub loader
return <Loading />
}
useEffect(() => {
const fetchRoomsAPI = async () => {
const response = await roomsServices.indexService();
if (response) {
const action = {
type: 'ROOMS',
message: 'SET_ROOMS',
body: response.rooms
}
return dispatch(action)
}
}
fetchRoomsAPI();
}, [ dispatch ])
return (
<div className="Home" data-testid="Home">
{renderRooms()}
</div>
);
}
export default Home;

View file

@ -0,0 +1,11 @@
@import '../../../public/stylesheets/partials/variables';
@import '../../../public/stylesheets/partials/mixins';
div.Home {
@include fullspan;
background-color: map-get($colors, "home");
display: grid;
grid-template-columns: repeat(auto-fill, 22vh);
grid-template-rows: repeat(auto-fill, minmax(22vh, 1fr));
overflow: scroll;
}

View file

@ -0,0 +1,30 @@
import React from 'react';
import Auth from '../../components/Form/Auth/Auth';
import NewRoomButton from '../../components/Button/NewRoom/NewRoom';
import FindRoomForm from '../../components/Form/FindRoom/FindRoom';
import LibraryButton from '../../components/Button/Library/Library';
const HomeSidebar = (props) => {
const { state, dispatch } = props;
const ifUser = <>
<FindRoomForm />
<NewRoomButton />
<LibraryButton />
</>
const ifNoUser = <Auth state={state} dispatch={dispatch} />
return (
<nav>
{
state.user.username
? ifUser
: ifNoUser
}
</nav>
);
}
export default HomeSidebar;

View file

@ -0,0 +1,54 @@
import React from 'react';
import './MainWrapper.scss';
import NavBar from '../NavBar/NavBar';
import Sidebar from '../Sidebar/Sidebar';
import Account from '../../Account/Account';
import Game from '../../Game/Game';
import Home from '../../Home/Home';
import News from '../../News/News';
import Room from '../../Room/Room';
const MainWrapper = (props) => {
const { state, page, dispatch } = props;
const setWrapperWithSidebarAndPage = () => {
if (page === 'game') return selectPage()
return (
<div className="main-wrapper" data-testid="main-wrapper">
<NavBar state={state}/>
<div className="content-wrapper">
<Sidebar page={page} state={state} dispatch={dispatch}/>
<main>
{selectPage()}
</main>
</div>
</div>
)
}
const selectPage = () =>{
switch (page) {
case 'account':
return <Account state={state} dispatch={dispatch}/>
case 'game':
return <Game state={state} dispatch={dispatch}/>
case 'home':
return <Home state={state} dispatch={dispatch}/>
case 'news':
return <News state={state} dispatch={dispatch}/>
case 'room':
return <Room state={state} dispatch={dispatch}/>
default:
return <></>
}
}
return (
<>
{ setWrapperWithSidebarAndPage() }
</>
);
}
export default MainWrapper;

View file

@ -1,4 +1,5 @@
@import '../../../public/stylesheets/partials/mixins'; @import '../../../../public/stylesheets/partials/mixins';
@import '../../../../public/stylesheets/partials/variables';
div.main-wrapper { div.main-wrapper {
display: flex; display: flex;
@ -27,25 +28,21 @@ div.main-wrapper {
} }
main { main {
display: flex; display: block;
justify-content: center; max-width: 80vw;
align-items: center; width: 100%;
flex-grow: 4; height: 100%;
} }
} }
} }
div.Game { button {
display: flex; color:map-get($colors, 'sidebar_link');
justify-content: space-between; cursor: pointer;
@include fullspan; font-family: inherit;
aside { font-size: 110%;
@include flexAround(column); font-weight: bold;
flex-grow: 1; margin-bottom: .5em;
} padding: 0;
text-decoration: none;
div.game-area {
@include flexAround(column);
flex-grow: 2;
}
} }

View file

@ -0,0 +1,36 @@
import React from "react";
import { Link } from "react-router-dom";
import "./NavBar.scss";
import Logo from "../../../components/Display/Logo/Logo";
const NavBar = ({ state }) => {
return (
<div className="NavBar" data-testid="NavBar">
<Link to="/home">
<div className="NavBar__logo">
<Logo />
</div>
</Link>
<Link to="/home">
<div className="NavBar__menu-item NavBar__home">
<p className="--link">Find a Game</p>
</div>
</Link>
<Link to="/news">
<div className="NavBar__menu-item NavBar__news">
<p className="--link">News</p>
</div>
</Link>
<Link to="/account">
<div className="NavBar__menu-item NavBar__account">
{state.user.username ? state.user.username : <></>}
</div>
</Link>
</div>
);
};
export default NavBar;

View file

@ -0,0 +1,22 @@
@import '../../../../public/stylesheets/partials/_variables.scss';
div.NavBar {
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
align-items: center;
background-color: map-get($colors, "nav_bar");
color: map-get($colors, "nav_link");
p {
font-size: 1.4em;
}
a {
text-decoration: none;
}
a{
color: inherit;
}
}

View file

@ -2,7 +2,7 @@ import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom'; import { BrowserRouter as Router } from 'react-router-dom';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import NavBar from './NavBar'; import NavBar from './NavBar';
import { initState } from '../../reducers/init/stateReducer.init'; import { initState } from '../../../reducers/init/reducer.init';
test('renders NavBar without crashing', () => { test('renders NavBar without crashing', () => {
const { getByTestId } = render( const { getByTestId } = render(

View file

@ -0,0 +1,33 @@
import React from 'react';
import './Sidebar.scss';
import AccountSidebar from '../../Account/AccountSidebar';
import HomeSidebar from '../../Home/HomeSidebar';
import NewsSidebar from '../../News/NewsSidebar';
import RoomSidebar from '../../Room/RoomSidebar';
const Sidebar = (props) => {
const {page, state, dispatch} = props
const displayComponent = () =>{
switch (page) {
case 'account':
return <AccountSidebar state={state} dispatch={dispatch}/>
case 'home':
return <HomeSidebar state={state} dispatch={dispatch}/>
case 'news':
return <NewsSidebar state={state} dispatch={dispatch}/>
case 'room':
return <RoomSidebar state={state} dispatch={dispatch}/>
default:
return <></>
}
}
return (
<aside className="Sidebar" data-testid="Sidebar">
{displayComponent()}
</aside>
);
}
export default Sidebar;

View file

@ -0,0 +1,41 @@
@import '../../../../public/stylesheets/partials/variables';
aside {
color: map-get($colors, "sidebar_link");
display: flex;
flex-flow: column nowrap;
nav {
align-items: stretch;
display: flex;
flex-flow: column nowrap;
}
h3 {
font-size: 130%;
margin: .2em auto;
text-transform: capitalize;
}
input {
display: block;
margin: .2em 0 .2em auto;
}
div.nav__section {
border: rgba(100,100,100,0.5) solid .25em;
margin: .5em;
padding: .75em .5em;
}
p.nav__section__label {
font-size: 110%;
margin-bottom: .5em;
text-decoration: none;
&:hover {
cursor: pointer;
text-decoration: underline;
}
}
}

Some files were not shown because too many files have changed in this diff Show more