CardTable.js (36847B) [raw]
1 import React from 'react'; 2 import PropTypes from 'prop-types'; 3 import _ from 'lodash'; 4 import {Button, Grid, Row, Column, OverflowMenu, OverflowMenuItem, Tooltip} from 'carbon-components-react'; 5 import {Logout32, Redo32, Package32, View16, ViewOff16, Information16} from '@carbon/icons-react'; 6 import SeatPicker from './SeatPicker'; 7 import MainHand from './MainHand'; 8 import HiddenHand from './HiddenHand'; 9 import TrumpPicker from './TrumpPicker'; 10 import ChatPanel from './ChatPanel'; 11 12 const trumpPlacement = ['me', 'left', 'partner', 'right']; 13 const suit = { 14 H: 'Hearts', 15 D: 'Diamonds', 16 S: 'Spades', 17 C: 'Clubs' 18 } 19 20 export default class CardTable extends React.Component { 21 22 constructor(props) { 23 super(props); 24 this.state = { 25 playerNames: [], 26 mySeat: -1, 27 myCards: [], 28 myHandInfo: 'mhi', 29 myTurnInfo: 'mti', 30 phase: 'lobby', 31 leftName: '', 32 leftSeat: -1, 33 leftHandInfo: '', 34 leftTurnInfo: '', 35 partnerName: '', 36 partnerSeat: -1, 37 partnerHandInfo: '', 38 partnerTurnInfo: '', 39 rightName: '', 40 rightSeat: -1, 41 rightHandInfo: '', 42 rightTurnInfo: '', 43 trump: '', 44 trumpPlace: '', 45 trumpNom: '', 46 turnSeat: -1, 47 dealSeat: -1, 48 table: [], 49 handLengths: [], 50 score: [0, 0], 51 trickWinner:'', 52 tricks:[], 53 spectators: [], 54 bannerMsg: '', 55 innerWinMsg: '', 56 onlyAlone: false, 57 noPick: false, 58 noPass: false, 59 amSpectator: false, 60 latestPost: '', 61 showErrors: true, 62 hard_order: true, 63 hard_pick: true, 64 stick_dealer: false 65 }; 66 }; 67 68 componentDidMount () { 69 const {name, tableName, client, firstMsg} = this.props; 70 client.onmessage = (event) => this.processResponse(event); 71 const welcomeMsg = 'Welcome to the ' + tableName + ' table, ' + name + '!'; 72 if (firstMsg) { 73 this.processPlayerChange(firstMsg); 74 } 75 this.setState({ 76 bannerMsg: welcomeMsg 77 }); 78 }; 79 80 componentDidUpdate (prevProps) { 81 const {firstMsg} = this.props; 82 if (firstMsg && !prevProps.firstMsg) { 83 this.processPlayerChange(firstMsg); 84 } 85 } 86 87 processResponse = (event) => { 88 const { playerNames} = this.state; 89 let msg = JSON.parse(event.data); 90 if ('pong' != msg.msg_type) { 91 console.log(msg); 92 } 93 if ('game_state' == msg.msg_type) { 94 this.setState({ 95 spectators: msg.game.spectators 96 }); 97 if (msg.game.phase != 'lobby') { 98 if (!_.isEqual(playerNames, msg.game.players)){ 99 this.processPlayerChange(msg); 100 } else { 101 this.processResponseSwitch(msg); 102 } 103 } else { 104 this.processLobby(msg); 105 } 106 } else if ('chat' == msg.msg_type) { 107 this.processChat(msg); 108 } else if ('error' == msg.msg_type) { 109 this.processError(msg); 110 } 111 }; 112 113 processError = msg => { 114 if (msg.msg) { 115 const post = '<<Error: ' + msg.msg; 116 this.setState({ 117 latestPost: post 118 }); 119 } 120 }; 121 122 processChat = msg => { 123 let post = msg.msg; 124 if (post && post != '') { 125 this.setState({ 126 latestPost: post 127 }); 128 } 129 } 130 131 processResponseSwitch = msg => { 132 switch (msg.game.phase) { 133 case 'lobby': 134 this.processLobby(msg); 135 break; 136 case 'vote': 137 this.processVote(msg); 138 break; 139 case 'dealer_swap': 140 this.processSwap(msg); 141 break; 142 case 'play': 143 this.processPlay(msg); 144 break; 145 case 'pause': 146 this.processPause(msg); 147 break; 148 case 'end': 149 this.processEnd(msg); 150 break; 151 default: 152 break; 153 } 154 } 155 156 processPlayerChange = msg => { 157 const {amSpectator} = this.state; 158 console.log('Player update!!'); 159 const newSpectator = msg.is_spectator > 0; 160 if (!amSpectator && !newSpectator) { 161 // I'm already a player, update the name set and continue 162 this.setState({ 163 playerNames: msg.game.players 164 }, () => { 165 this.processResponseSwitch(msg); 166 }); 167 } else if (!amSpectator && newSpectator) { 168 // I was playing and just stood up - reset me to spectator mode 169 this.processFirstMessage(msg); 170 } else if (amSpectator && !newSpectator) { 171 // I was spectator and just took a seat 172 this.processFirstMessage(msg); 173 } else { 174 // was and still am spectator - update names and continue 175 this.setState({ 176 playerNames: msg.game.players, 177 }, () => { 178 this.processResponseSwitch(msg); 179 }); 180 } 181 182 } 183 184 processFirstMessage = msg => { 185 console.log('processFirstMessage, msg:\n', msg); 186 // in cases of forceRejoin, the firstMsg could be lobby, vote, play 187 // for cases of ordinary game join, will be lobby 188 if (msg && msg.game) { 189 this.setState({ 190 spectators: msg.game.spectators 191 }); 192 switch (msg.game.phase) { 193 case 'lobby': 194 this.processLobby(msg); 195 break; 196 case 'vote': 197 this.processFirstMsgVote(msg); 198 break; 199 case 'play': 200 this.processFirstMsgPlay(msg); 201 break; 202 case 'end': 203 this.processFirstMsgEnd(msg); 204 break; 205 } 206 } 207 //TODO force correct score (for all the rejoin cases) 208 } 209 210 // NOTE the processFirstMsgXyz functions are needed to handle the case 211 // of a user dropping connection and doing a force-rejoin back to the 212 // same table. 213 // In normal game play, firstMsg will be a 'lobby', and none of the other 214 // processFirst* variants will be called 215 216 processFirstMsgEnd = msg => { 217 // timer use in these processFirst* is to allow sequence of setState to complete 218 this.initMySeat(msg); 219 setTimeout(this.gameStartSetup, 300, msg); 220 setTimeout(this.handStartSetup, 600, msg); 221 setTimeout(this.processEnd, 900, msg); 222 } 223 224 processFirstMsgPlay = msg => { 225 this.initMySeat(msg); 226 setTimeout(this.gameStartSetup, 300, msg); 227 setTimeout(this.handStartSetup, 600, msg); 228 setTimeout(this.processPlay, 900, msg); 229 // this.gameStartSetup(msg); 230 // this.handStartSetup(msg); 231 // this.processPlay(msg); 232 } 233 234 processFirstMsgVote = msg => { 235 this.initMySeat(msg); 236 setTimeout(this.gameStartSetup, 300, msg); 237 setTimeout(this.processVote, 600, msg); 238 // this.gameStartSetup(msg); 239 // this.processVote(msg); 240 } 241 242 // OK to let processLobby run twice on first lobby msg, which 243 // may happen if CardTable already mounted on join_table 244 processLobby = (msg) => { 245 this.initMySeat(msg); 246 this.setState({ 247 phase: 'lobby', 248 score: [0,0] 249 }); 250 }; 251 252 initMySeat = msg => { 253 const {name} = this.props; 254 const newSpec = msg.is_spectator > 0; 255 if (msg.game.players) { 256 const plAr = msg.game.players; 257 let mySeat = plAr.findIndex( x => x == name ); 258 console.log('initMySeat.mySeat=', mySeat); 259 if (newSpec && msg.game.phase != 'lobby'){ 260 mySeat = 3; 261 }; 262 this.setState({ 263 playerNames: plAr, 264 mySeat: mySeat, 265 amSpectator: newSpec, 266 hard_order: msg.settings.hard_order, 267 hard_pick: msg.settings.hard_pick, 268 stick_dealer: msg.settings.stick_dealer 269 }); 270 }; 271 } 272 273 haveSuit = (hand, trumpNom) => { 274 const targetSuit = trumpNom.substring(1); 275 for (let i=0; i < hand.length; i++) { 276 if (hand[i].substring(1) == targetSuit) { 277 console.log('targetSuit:' + targetSuit + ' matched by ' + hand[i]); 278 return true; 279 } 280 } 281 return false; 282 } 283 284 processVote = (msg) => { 285 if (this.state.phase == 'lobby') { 286 this.gameStartSetup(msg); 287 } else if (this.state.phase == 'pause' || 288 (this.state.phase == 'vote2' && msg.game.pass_count == 0)) { 289 // second condition is for all players pass trump twice 290 this.trumpStartSetup(msg); 291 } 292 const {leftSeat, rightSeat, partnerSeat, mySeat, dealSeat, hard_order, hard_pick, stick_dealer} = this.state; 293 const vote1phase = msg.game.pass_count < 4; 294 const phaseString = vote1phase ? 'vote' : 'vote2'; 295 const onlyAlone = hard_order && vote1phase && dealSeat == partnerSeat; 296 const noPass = stick_dealer && !vote1phase && (dealSeat == mySeat); 297 const noPick = hard_pick && vote1phase && (dealSeat == mySeat) && !this.haveSuit(msg.hand, msg.game.trump_nominee); 298 let turnInfo = ['', '', '', '']; 299 turnInfo[msg.game.turn] = 'trump?'; 300 this.setState({ 301 phase: phaseString, 302 myCards: msg.hand, 303 turnSeat: msg.game.turn, 304 onlyAlone: onlyAlone, 305 noPick: noPick, 306 noPass: noPass, 307 handLengths: msg.game.hand_lengths, 308 leftTurnInfo: turnInfo[leftSeat], 309 rightTurnInfo: turnInfo[rightSeat], 310 partnerTurnInfo: turnInfo[partnerSeat], 311 myTurnInfo: turnInfo[mySeat] 312 }); 313 }; 314 315 processSwap = msg => { 316 const {leftSeat, rightSeat, partnerSeat, mySeat, dealSeat} = this.state; 317 let turnInfo = ['', '', '', '']; 318 turnInfo[dealSeat] = 'Swapping...'; 319 this.setState({ 320 phase: 'swap', 321 leftTurnInfo: turnInfo[leftSeat], 322 rightTurnInfo: turnInfo[rightSeat], 323 partnerTurnInfo: turnInfo[partnerSeat], 324 myTurnInfo: turnInfo[mySeat] 325 }); 326 } 327 328 trickMsg = (num, action) => { 329 let retVal; 330 if (num == 0) { 331 retVal = action ? 'action' : ''; 332 } else { 333 retVal = num == 1 ? '1 trick' : (num + ' tricks'); 334 if (action) { 335 retVal = retVal + ', action'; 336 } 337 } 338 return retVal; 339 } 340 341 processPlay = msg => { 342 const {leftSeat, rightSeat, partnerSeat, mySeat, dealSeat, phase} = this.state; 343 let turnInfo = []; 344 if (phase == 'vote' || phase == 'vote2' || phase == 'swap') { 345 this.handStartSetup(msg); 346 } 347 for (let i = 0; i < 4; i++) { 348 const tricks = msg.game.tricks[i]; 349 let tInf = this.trickMsg(tricks, (msg.game.turn == i)); 350 turnInfo.push(tInf); 351 } 352 this.setState({ 353 phase: 'play', 354 myCards: msg.hand, 355 table: msg.game.table, 356 tricks: msg.game.tricks, 357 trickWinner: '', 358 handLengths: msg.game.hand_lengths, 359 leftTurnInfo: turnInfo[leftSeat], 360 rightTurnInfo: turnInfo[rightSeat], 361 partnerTurnInfo: turnInfo[partnerSeat], 362 myTurnInfo: turnInfo[mySeat] 363 }); 364 365 } 366 367 processPause = msg => { 368 let trickWinIndex = -1; 369 let trickWinner = ''; 370 for (let i = 0; i < 4; i++){ 371 if (msg.game.tricks[i] != this.state.tricks[i]){ 372 trickWinIndex = i; 373 break; 374 } 375 } 376 if (trickWinIndex > -1) { 377 //must be?? sanity check 378 trickWinner = this.state.playerNames[trickWinIndex] + ' takes the trick!' 379 } 380 this.setState ({ 381 table: msg.game.table, 382 handLengths: msg.game.hand_lengths, 383 myCards: msg.hand, 384 tricks: msg.game.tricks, 385 phase: 'pause', 386 trickWinner: trickWinner 387 }) 388 } 389 390 processEnd = msg => { 391 const {playerNames, leftSeat, partnerSeat, rightSeat, amSpectator} = this.state; 392 // arrangeScore[0] us, [1] them 393 const finalScore = this.arrangeScore(msg.game.score); 394 const myName = amSpectator ? playerNames[3] : 'You'; 395 const winMsg = finalScore[0] > finalScore[1] ? 396 myName + ' and ' + playerNames[partnerSeat] + ' win the game!!' : 397 playerNames[leftSeat] + ' and ' + playerNames[rightSeat] + ' win this one...'; 398 const innerWin = amSpectator ? 'Game Over...' : 399 finalScore[0] > finalScore[1] ? 'You Win!!' : 'You lost...'; 400 this.setState({ 401 phase: 'end', 402 score: finalScore, 403 bannerMsg: winMsg, 404 innerWinMsg: innerWin 405 }); 406 } 407 408 handStartSetup = msg => { 409 const {leftSeat, rightSeat, partnerSeat, mySeat, dealSeat} = this.state; 410 let handInfo = ['', '', '', '']; 411 const caller = msg.game.out_player > -1 ? 'Alone' : 'Caller'; 412 const callSeat = msg.game.caller; 413 if (callSeat == dealSeat) { 414 handInfo[dealSeat] = 'Dealer, ' + caller; 415 } else { 416 handInfo[dealSeat] = 'Dealer'; 417 handInfo[callSeat] = caller; 418 } 419 // this.state.score is always [{us}, {them}] 420 let score = this.arrangeScore(msg.game.score); 421 this.setState({ 422 trump: msg.game.trump, 423 table: msg.game.table, 424 score: score, 425 handLengths: msg.game.hand_lengths, 426 leftHandInfo: handInfo[leftSeat], 427 rightHandInfo: handInfo[rightSeat], 428 partnerHandInfo: handInfo[partnerSeat], 429 myHandInfo: handInfo[mySeat] 430 }); 431 432 } 433 434 arrangeScore = (msgScore) => { 435 // this.state.score is always [{us}, {them}] 436 const { mySeat } = this.state; 437 let score = []; 438 if (mySeat % 2 == 0){ 439 // we're evens 440 score[0] = msgScore[0]; 441 score[1] = msgScore[1]; 442 } else { 443 score[0] = msgScore[1]; 444 score[1] = msgScore[0]; 445 } 446 return score; 447 } 448 449 trumpStartSetup = (msg) => { 450 const {leftSeat, rightSeat, partnerSeat, mySeat, dealSeat} = this.state; 451 let handInfo = ['', '', '', '']; 452 const newDeal = msg.game.dealer; 453 handInfo[newDeal] = 'Dealer'; 454 let tpIndex = msg.game.dealer - mySeat; 455 tpIndex = (tpIndex < 0) ? tpIndex + 4 : tpIndex; 456 const trumpPlace = trumpPlacement[tpIndex]; 457 // this.state.score is always [{us}, {them}] 458 let score = this.arrangeScore(msg.game.score); 459 this.setState({ 460 trump: '', 461 trumpPlace: trumpPlace, 462 dealSeat: newDeal, 463 score: score, 464 trickWinner: '', 465 trumpNom: msg.game.trump_nominee, 466 leftHandInfo: handInfo[leftSeat], 467 rightHandInfo: handInfo[rightSeat], 468 partnerHandInfo: handInfo[partnerSeat], 469 myHandInfo: handInfo[mySeat] 470 }); 471 472 } 473 474 gameStartSetup = (msg) => { 475 const { playerNames, mySeat } = this.state; 476 let handInfo = [' ', ' ', ' ', ' ']; 477 let turnInfo = [' ', ' ', ' ', ' ']; 478 handInfo[msg.game.dealer] = 'Dealer'; 479 turnInfo[msg.game.turn] = 'trump?'; 480 const leftSeat = (mySeat + 1) % 4; 481 const partnerSeat = (mySeat + 2) % 4; 482 const rightSeat = (mySeat + 3) % 4; 483 let tpIndex = msg.game.dealer - mySeat; 484 tpIndex = (tpIndex < 0) ? tpIndex + 4 : tpIndex; 485 const trumpPlace = trumpPlacement[tpIndex]; 486 this.setState ({ 487 leftName: playerNames[leftSeat], 488 leftSeat: leftSeat, 489 leftHandInfo: handInfo[leftSeat], 490 leftTurnInfo: turnInfo[leftSeat], 491 partnerName: playerNames[partnerSeat], 492 partnerSeat: partnerSeat, 493 partnerHandInfo: handInfo[partnerSeat], 494 partnerTurnInfo: turnInfo[partnerSeat], 495 rightName: playerNames[rightSeat], 496 rightSeat: rightSeat, 497 rightHandInfo: handInfo[rightSeat], 498 rightTurnInfo: turnInfo[rightSeat], 499 myHandInfo : handInfo[mySeat], 500 myTurnInfo : turnInfo[mySeat], 501 trumpPlace : trumpPlace, 502 trumpNom: msg.game.trump_nominee, 503 dealSeat: msg.game.dealer 504 }); 505 }; 506 507 sendSit = (index) => { 508 const { client } = this.props; 509 client.send(JSON.stringify({ 510 action:'take_seat', 511 seat: index 512 })); 513 }; 514 515 sendStand = () => { 516 const { client } = this.props; 517 client.send(JSON.stringify({ 518 action:'stand_up' 519 })); 520 }; 521 522 sendStart = (startDealer) => { 523 this.props.client.send(JSON.stringify({ 524 action: 'start_game', start_seat: startDealer 525 })); 526 // console.log('start game, dealer = ', startDealer); 527 }; 528 529 sendExit = () => { 530 this.props.exitTable(); 531 }; 532 533 sendVote = (voteObject) => { 534 const voteString = JSON.stringify(voteObject); 535 // console.log('sendVote:', voteString); 536 this.props.client.send(voteString); 537 }; 538 539 sendCard = (index) => { 540 const {phase, myCards} = this.state; 541 // console.log('card click ', myCards[index]); 542 if (phase == 'swap') { 543 this.props.client.send(JSON.stringify({ 544 action:'dealer_swap', card: myCards[index] 545 })); 546 } else if (phase == 'play') { 547 this.props.client.send(JSON.stringify({ 548 action:'play_card', card: myCards[index] 549 })); 550 } 551 }; 552 553 sendRestart = () => { 554 this.props.client.send(JSON.stringify({ 555 action:'restart_game' 556 })); 557 } 558 559 sendChat = post => { 560 console.log(post); 561 if (post && post != ''){ 562 this.props.client.send(JSON.stringify({ 563 action: 'chat', 564 msg: post 565 })); 566 } 567 // XX TODO remove this debug action when chat stable 568 if ('chat10' == post){ 569 for (let i=0; i < 10; i++){ 570 const msg='this is chat10 test-chat-' + i; 571 this.props.client.send(JSON.stringify({ 572 action: 'chat', 573 msg: msg 574 })); 575 } 576 } 577 } 578 579 toggleErrorDisplay = () => { 580 // NOTE this trick requires that .err__post is the first selector of all, 581 // so requires it first in our .scss, even before imports 582 const { showErrors } = this.state; 583 let stylesheet = document.styleSheets[0]; 584 const nextShowErrors = !showErrors; 585 const rule = nextShowErrors ? 'inherit' : 'none'; 586 stylesheet.cssRules[0].style.display = rule; 587 this.setState({ 588 showErrors: nextShowErrors 589 }) 590 console.log('toggleErrorDisplay'); 591 } 592 593 genGameOver = () => { 594 const {innerWinMsg, amSpectator} = this.state; 595 let retVal = []; 596 const instMsg = amSpectator ? 'You can take a seat if one becomes empty, or you can return to the lobby...' 597 : 'You can play again at this table, or return to the lobby to change your table or player name...'; 598 retVal.push( 599 <div className="gover__outer" key="gom1"> 600 <div className="gover__inwin">{innerWinMsg}</div> 601 <div className="gover__inst"> 602 {instMsg} 603 </div> 604 <div className="gover__buttons"> 605 <Button 606 className="exit2__button" 607 kind="secondary" 608 onClick={this.sendExit} 609 renderIcon={Logout32} 610 >Exit to Lobby</Button> 611 {!amSpectator && ( 612 <Button 613 className="repeat__button" 614 kind="primary" 615 onClick={()=>this.sendRestart()} 616 renderIcon={Redo32} 617 >Play Again!!</Button> 618 619 )} 620 </div> 621 </div> 622 ); 623 return retVal; 624 } 625 626 genTrick = () => { 627 let retVal = []; 628 const { table, mySeat, leftSeat, rightSeat, partnerSeat } = this.state; 629 const myCard = table[mySeat]; 630 const leftCard = table[leftSeat]; 631 const partnerCard = table[partnerSeat]; 632 const rightCard = table[rightSeat]; 633 const mySrc = 'cards/' + myCard + '.svg'; 634 const leftSrc = 'cards/' + leftCard + '.svg'; 635 const partnerSrc = 'cards/' + partnerCard + '.svg'; 636 const rightSrc = 'cards/' + rightCard + '.svg'; 637 let midClass = 'mid__trick'; 638 if (myCard && partnerCard) { 639 midClass += ' both'; 640 } else if (myCard) { 641 midClass += ' me'; 642 } else if (partnerCard) { 643 midClass += ' partner'; 644 } 645 retVal.push ( 646 <div className="trick__outer" key="gt1"> 647 <Grid className="trick__grid"> 648 <Row className="trick__row"> 649 <Column> 650 <div className="left__trick"> 651 {leftCard && ( 652 <div className="trick__div"> 653 <img className="trick__card" src={leftSrc} /> 654 </div> 655 )} 656 </div> 657 </Column> 658 <Column> 659 <div className={midClass}> 660 {partnerCard && ( 661 <div className="trick__div"> 662 <img className="trick__card" src={partnerSrc} /> 663 </div> 664 )} 665 {myCard && ( 666 <div className="trick__div"> 667 <img className="trick__card" src={mySrc} /> 668 </div> 669 )} 670 </div> 671 </Column> 672 <Column> 673 <div className="right__trick"> 674 {rightCard && ( 675 <div className="trick__div"> 676 <img className="trick__card" src={rightSrc} /> 677 </div> 678 )} 679 </div> 680 </Column> 681 </Row> 682 </Grid> 683 </div> 684 ); 685 return retVal; 686 } 687 688 genNameDisplay = seatNum => { 689 let retVal = ''; 690 const { playerNames, mySeat, myHandInfo, amSpectator } = this.state; 691 let seatName = playerNames[seatNum]; 692 if (seatNum == mySeat) { 693 seatName = amSpectator ? seatName : 'You'; 694 if (seatName != 'Empty'){ 695 seatName += ': ' + myHandInfo; 696 } 697 } 698 if (seatName != 'Empty') { 699 retVal = (<div>{seatName}</div>); 700 } else { 701 retVal = ( 702 <div>Empty 703 {amSpectator && ( 704 <Button 705 className="sit__button" 706 kind="ghost" 707 onClick={()=>{this.sendSit(seatNum)}} 708 renderIcon={Package32}>Choose seat</Button> 709 )} 710 </div>); 711 } 712 return retVal; 713 } 714 715 genSpecMsgObj = () => { 716 const {spectators} = this.state; 717 let retVal = {} 718 if (!spectators || spectators.length == 0) { 719 retVal.title = 'No Spectators'; 720 retVal.list = null; 721 } else { 722 retVal.title = (spectators.length == 1) ? '1 Spectator:' : spectators.length + ' Spectators:'; 723 retVal.list = spectators[0]; 724 for (let i=1; i < spectators.length; i++) { 725 retVal.list += ', ' + spectators[i]; 726 }; 727 } 728 return retVal; 729 } 730 731 render () { 732 const { playerNames, mySeat, phase, myCards, myTurnInfo, amSpectator, 733 partnerHandInfo, partnerTurnInfo, partnerSeat, leftTurnInfo, leftHandInfo, leftSeat, 734 rightHandInfo, rightTurnInfo, rightSeat, trumpPlace, trumpNom, turnSeat, spectators, 735 dealSeat, trump, handLengths, score, trickWinner, bannerMsg, noPick, noPass, onlyAlone, 736 hard_pick, hard_order, stick_dealer, latestPost, showErrors } = this.state; 737 const {tableName} = this.props; 738 const showSeatPicker = phase == 'lobby'; 739 const showGameOver = phase == 'end'; 740 const showTrump = (phase == 'vote') || (phase == 'vote2') || (phase == 'swap'); 741 const showTrumpPicker = showTrump && (turnSeat == mySeat); 742 const showSwap = (phase == 'swap') && (dealSeat == mySeat); 743 const showBottomInfo = !showSeatPicker && (amSpectator || (!showTrumpPicker && !showSwap)); 744 const tcp = "trump__holder " + trumpPlace; 745 const trumpImage = (phase != 'vote2') ? 'cards/' + trumpNom + '.svg' : 'cards/1B.svg'; 746 const trumpMsg = phase == 'play' ? suit[trump] + ' are trump' : ''; 747 const trickDisplay = (phase == 'play' || phase == 'pause') ? this.genTrick() : []; 748 const gameOverDisplay = (phase == 'end') ? this.genGameOver() : []; 749 const usLabel = amSpectator ? playerNames[1] + ' & ' + playerNames[3] + ': ' : 'Us: '; 750 const themLabel = amSpectator ? playerNames[0] + ' & ' + playerNames[2] + ': ' : 'Them: '; 751 const hasSpec = spectators.length > 0; 752 const specMsgObj = this.genSpecMsgObj(); 753 const toggleErrorMsg = showErrors ? 'Hide errors in chat' : 'Show errors in chat'; 754 return ( 755 <div id="table" className="table__main"> 756 <Grid className="og"> 757 <Row className="og__row"> 758 <Column className="og__left" md={5}> 759 <Grid className="inner__left"> 760 {(showSeatPicker || showGameOver) && ( 761 <Row className="table__header"> 762 <h3 className="banner">{bannerMsg}</h3> 763 </Row> 764 )} 765 <Row className="table__top"> 766 <Column className="tt__left" md={3}> 767 {!showSeatPicker && ( 768 <div className="menu__holder"> 769 <OverflowMenu> 770 <OverflowMenuItem 771 itemText="Stand" 772 disabled={amSpectator} 773 onClick={this.sendStand} 774 /> 775 <OverflowMenuItem 776 itemText="Exit to Lobby" 777 onClick={this.sendExit} 778 /> 779 <OverflowMenuItem 780 itemText={toggleErrorMsg} 781 onClick={this.toggleErrorDisplay} 782 /> 783 </OverflowMenu> 784 </div> 785 )} 786 </Column> 787 <Column className="tt__center" md={5}> 788 {!showSeatPicker && ( 789 <div className="partner__stack"> 790 <div className="player__name">{this.genNameDisplay(partnerSeat)}</div> 791 <div className="partner__info"> 792 <div className="play__hinfo">{partnerHandInfo}</div> 793 <div className="play__tinfo">{partnerTurnInfo}</div> 794 </div> 795 <HiddenHand 796 numCards={handLengths[partnerSeat]} /> 797 </div>)} 798 </Column> 799 </Row> 800 <Row className="table__mid"> 801 <Column className="tm__left" md={3}> 802 {!showSeatPicker && ( 803 <div className="vert__stack"> 804 <div className="player__name">{this.genNameDisplay(leftSeat)}</div> 805 <div className="play__hinfo">{leftHandInfo}</div> 806 <div className="play__tinfo">{leftTurnInfo}</div> 807 <HiddenHand 808 numCards={handLengths[leftSeat]} /> 809 </div>)} 810 </Column> 811 <Column className="tm__center" md={5}> 812 {showSeatPicker && ( 813 <SeatPicker 814 names={playerNames} 815 handleSit={this.sendSit} 816 handleStand={this.sendStand} 817 mySeat={mySeat} 818 amSpectator={amSpectator} 819 handleStart={this.sendStart} 820 />)} 821 { showTrump && ( 822 <div className="trump__outer"> 823 <div className={tcp}> 824 <img className="trump__card" src={trumpImage} /> 825 </div> 826 </div> 827 )} 828 { (phase=='play' || phase=='pause') && ( 829 <div className="trick__holder">{trickDisplay}</div> 830 )} 831 { showGameOver && ( 832 <div className="gameOver__holder"> 833 {gameOverDisplay} 834 </div> 835 )} 836 </Column> 837 </Row> 838 <Row className="table__bot"> 839 <Column className="tb__left" md={3}> 840 {showBottomInfo && ( 841 <div className="my__stack"> 842 <div className="my__hinfo">{this.genNameDisplay(mySeat)}</div> 843 <div className="my__tinfo">{myTurnInfo}</div> 844 <div className="my__tinfo">{trumpMsg}</div> 845 </div> 846 )} 847 {showTrumpPicker && !amSpectator && ( 848 <TrumpPicker 849 trumpCard={trumpNom} 850 phaseTwo={phase == 'vote2'} 851 myDeal={dealSeat == mySeat} 852 onlyAlone={onlyAlone} 853 noPick={noPick} 854 noPass={noPass} 855 handleVote={this.sendVote} /> 856 )} 857 {showSwap && !amSpectator && ( 858 <div className="my__stack"> 859 <div className="my_tinfo">Click a card to discard:</div> 860 </div> 861 )} 862 </Column> 863 <Column className="tb__center" md={5}> 864 {!showSeatPicker && !amSpectator && ( 865 <MainHand 866 cards={myCards} 867 cardClick={this.sendCard} 868 /> 869 )} 870 {!showSeatPicker && amSpectator && ( 871 <HiddenHand 872 numCards={handLengths[mySeat]} /> 873 )} 874 </Column> 875 </Row> 876 </Grid> 877 </Column> 878 <Column className="og__right" md={3}> 879 <Grid className="inner_right"> 880 <Row className="tt__right" > 881 {phase == 'lobby' && ( 882 <div className="exit__row"> 883 <Button 884 className="leave__button" 885 kind="ghost" 886 onClick={this.sendExit} 887 renderIcon={Logout32}>Exit</Button> 888 </div> 889 )} 890 {(phase != 'lobby') && ( 891 <div className="score__holder"> 892 <div className="us__score">{usLabel}{score[0]}</div> 893 <div className="them__score">{themLabel}{score[1]}</div> 894 <div className="trick__win">{trickWinner}</div> 895 <div className="info__buttons"> 896 <Tooltip 897 direction="left" 898 renderIcon={Information16} 899 > 900 <div className="table__info"> 901 <div className="ti__name">{tableName}</div> 902 <div className="ti__opt">HardOrder: {hard_order ? 'on' : 'off'}</div> 903 <div className="ti__opt">HardPick: {hard_pick ? 'on' : 'off'}</div> 904 <div className="ti__opt">StickDealer: {stick_dealer ? 'on' : 'off'}</div> 905 </div> 906 </Tooltip> 907 <Tooltip 908 direction="left" 909 renderIcon={hasSpec? View16 : ViewOff16} 910 > 911 <div className="spec__info"> 912 <div className="spec__title"> 913 {specMsgObj.title} 914 </div> 915 {(specMsgObj.list != null) && ( 916 <div className="spec__list"> 917 {specMsgObj.list} 918 </div> 919 )} 920 </div> 921 </Tooltip> 922 </div> 923 </div> 924 )} 925 </Row> 926 <Row className="tm__right"> 927 {!showSeatPicker && ( 928 <div className="vert__stack right__stack"> 929 <div className="player__name">{this.genNameDisplay(rightSeat)}</div> 930 <div className="play__hinfo">{rightHandInfo}</div> 931 <div className="play__tinfo">{rightTurnInfo}</div> 932 <HiddenHand 933 numCards={handLengths[rightSeat]} /> 934 </div>)} 935 </Row> 936 <Row className="tb__right"> 937 <ChatPanel 938 receivedChat={latestPost} 939 sendChat={this.sendChat} /> 940 </Row> 941 </Grid> 942 </Column> 943 </Row> 944 </Grid> 945 </div> 946 ); 947 }; 948 } 949 950 CardTable.propTypes = { 951 exitTable: PropTypes.func, 952 name: PropTypes.string, 953 tableName: PropTypes.string, 954 firstMsg: PropTypes.object, 955 client: PropTypes.object 956 }