Skip to content

Commit a84d387

Browse files
author
hiukim
committed
part 4 - account system
1 parent 3a504cb commit a84d387

File tree

7 files changed

+156
-16
lines changed

7 files changed

+156
-16
lines changed

.meteor/packages

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ [email protected] # Enable ECMAScript2015+ syntax in app code
2020
[email protected] # Publish all data to the clients (for prototyping)
2121
[email protected] # Allow all DB writes from clients (for prototyping)
2222
react-meteor-data
23+
accounts-password

imports/api/collections/games.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,39 @@
11
import { Mongo } from 'meteor/mongo';
22

33
export default Games = new Mongo.Collection('games');
4+
5+
_.extend(Games, {
6+
newGame() {
7+
let gameDoc = {
8+
board: [[null, null, null], [null, null, null], [null, null, null]],
9+
players: []
10+
};
11+
let gameId = Games.insert(gameDoc); // insert a new game document into the collection
12+
return gameId;
13+
},
14+
15+
joinGame(gameId, user) {
16+
console.log("gameId; ", gameId, user);
17+
let game = Games.findOne(gameId);
18+
if (game.players.length === 2) {
19+
throw "game is full";
20+
}
21+
game.players.push({
22+
userId: user._id,
23+
username: user.username
24+
});
25+
Games.update(game._id, {
26+
$set: {players: game.players}
27+
});
28+
},
29+
30+
leaveGame(gameId, user) {
31+
let game = Games.findOne(gameId);
32+
game.players = _.reject(game.players, (player) => {
33+
return player.userId === user._id;
34+
});
35+
Games.update(game._id, {
36+
$set: {players: game.players}
37+
});
38+
}
39+
});

imports/ui/App.jsx

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { createContainer } from 'meteor/react-meteor-data';
33
import Games from '../api/collections/games.js';
44
import GameList from './GameList.jsx';
55
import GameBoard from './GameBoard.jsx';
6+
import LoginForm from './LoginForm.jsx';
67

78
class App extends Component {
89
constructor(props) {
@@ -12,40 +13,51 @@ class App extends Component {
1213
}
1314
}
1415

15-
handleEnterGame(gameId) {
16+
handleEnterGame(gameId) {
1617
this.setState({selectedGameId: gameId});
1718
}
1819

19-
handleBackToGameList() {
20+
handleBackToGameList() {
2021
this.setState({selectedGameId: null});
2122
}
2223

23-
selectedGame() {
24+
selectedGame() {
2425
let selectedGame = _.find(this.props.games, (game) => {
2526
return game._id === this.state.selectedGameId;
2627
});
2728
return selectedGame;
2829
}
2930

30-
render() {
31-
if (this.state.selectedGameId === null) {
31+
render() {
32+
if (!this.props.user) {
33+
return (
34+
<div>
35+
<LoginForm/>
36+
</div>
37+
)
38+
}
39+
40+
if (this.state.selectedGameId === null) {
3241
return (
3342
<GameList
3443
games={this.props.games}
35-
enterGameHandler={this.handleEnterGame.bind(this)}/>
44+
enterGameHandler={this.handleEnterGame.bind(this)}
45+
user={this.props.user}/>
3646
)
3747
} else {
3848
return (
3949
<GameBoard
4050
game={this.selectedGame()}
41-
backToGameListHandler={this.handleBackToGameList.bind(this)}/>
51+
backToGameListHandler={this.handleBackToGameList.bind(this)}
52+
user={this.props.user}/>
4253
)
4354
}
4455
}
4556
}
4657

4758
export default createContainer(() => {
4859
return {
60+
user: Meteor.user(),
4961
games: Games.find().fetch()
5062
};
5163
}, App);

imports/ui/GameBoard.jsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ export default class GameBoard extends Component {
1616
handleCellClick(row, col) {
1717
let currentPlayer = this.currentPlayer();
1818
let game = this.props.game;
19+
20+
if (game.players[currentPlayer].userId !== this.props.user._id) return;
21+
1922
game.board[row][col] = currentPlayer;
2023
Games.update(game._id, {
2124
$set: {board: game.board}

imports/ui/GameList.jsx

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,76 @@ import Games from '../api/collections/games.js';
33

44
export default class GameList extends Component {
55
handleNewGame() {
6-
let gameDoc = {
7-
board: [[null, null, null], [null, null, null], [null, null, null]]
8-
};
9-
Games.insert(gameDoc); // insert a new game document into the collection
6+
Games.newGame(this.props.user);
7+
}
8+
9+
handleLeaveGame(gameId) {
10+
Games.leaveGame(gameId, this.props.user);
11+
}
12+
13+
handleJoinGame(gameId) {
14+
Games.joinGame(gameId, this.props.user);
1015
}
1116

1217
handleEnterGame(gameId) {
1318
this.props.enterGameHandler(gameId);
1419
}
1520

21+
myCurrentGameId() {
22+
// find game where the user is currently in
23+
let game = _.find(this.props.games, (game) => {
24+
return _.find(game.players, (player) => {
25+
return player.userId === this.props.user._id;
26+
}) !== undefined;
27+
});
28+
if (game === undefined) return null;
29+
return game._id;
30+
}
31+
32+
renderPlayers(game) {
33+
let player1 = game.players.length > 0? game.players[0].username: '';
34+
let player2 = game.players.length > 1? game.players[1].username: '';
35+
return (
36+
<span>[{player1}] vs [{player2}]</span>
37+
)
38+
}
39+
1640
render() {
1741
return (
1842
<div>
1943
<div>
20-
<button onClick={this.handleNewGame.bind(this)}>New Game</button>
21-
</div>
22-
23-
<div>
2444
<h1>List of games</h1>
2545
{this.props.games.map((game, index) => {
2646
return (
2747
<div key={game._id}>
2848
<span>Game {index+1}</span>
29-
<button onClick={this.handleEnterGame.bind(this, game._id)}>Enter</button>
49+
{this.renderPlayers(game)}
50+
51+
{/* can leave only if user is in the game, and the game is not started */}
52+
{this.myCurrentGameId() === game._id && game.players.length < 2? (
53+
<button onClick={this.handleLeaveGame.bind(this, game._id)}>Leave</button>
54+
): null}
55+
56+
{/* can join only if user is not in any game, and the game is not started */}
57+
{this.myCurrentGameId() === null && game.players.length < 2? (
58+
<button onClick={this.handleJoinGame.bind(this, game._id)}>Join</button>
59+
): null}
60+
61+
{/* can enter only if the game is started */}
62+
{game.players.length === 2? (
63+
<button onClick={this.handleEnterGame.bind(this, game._id)}>Enter</button>
64+
): null}
3065
</div>
3166
)
3267
})}
3368
</div>
69+
70+
{/* Only show new game button if player is not in any room */}
71+
{this.myCurrentGameId() === null? (
72+
<div>
73+
<button onClick={this.handleNewGame.bind(this)}>New Game</button>
74+
</div>
75+
): null}
3476
</div>
3577
)
3678
}

imports/ui/LoginForm.jsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { Random } from 'meteor/random'
2+
import React, { Component } from 'react';
3+
4+
export default class LoginForm extends Component {
5+
constructor(props) {
6+
super(props);
7+
this.state = {
8+
username: '',
9+
}
10+
}
11+
12+
handleUsernameChange(e) {
13+
this.setState({username: e.target.value});
14+
}
15+
16+
handleSubmit(e) {
17+
e.preventDefault();
18+
19+
let username = this.state.username.trim();
20+
if (username === '') return;
21+
Accounts.createUser({
22+
username: username,
23+
password: Random.secret()
24+
});
25+
}
26+
27+
render() {
28+
return (
29+
<form name="login-form" onSubmit={this.handleSubmit.bind(this)}>
30+
<h1>Login</h1>
31+
<input type="text" onChange={this.handleUsernameChange.bind(this)} placeholder="Enter your name"/>
32+
<input type="submit" value="Login"/>
33+
</form>
34+
)
35+
}
36+
}

0 commit comments

Comments
 (0)