CardTable.js (36908B) [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 let anyEmpty = plAr.includes('Empty'); 259 if (newSpec && !anyEmpty){ 260 // console.log('all seats taken, initMySeat.newSpec, mySeat=3'); 261 mySeat = 3; 262 }; 263 this.setState({ 264 playerNames: plAr, 265 mySeat: mySeat, 266 amSpectator: newSpec, 267 hard_order: msg.settings.hard_order, 268 hard_pick: msg.settings.hard_pick, 269 stick_dealer: msg.settings.stick_dealer 270 }); 271 }; 272 } 273 274 haveSuit = (hand, trumpNom) => { 275 const targetSuit = trumpNom.substring(1); 276 for (let i=0; i < hand.length; i++) { 277 if (hand[i].substring(1) == targetSuit) { 278 console.log('targetSuit:' + targetSuit + ' matched by ' + hand[i]); 279 return true; 280 } 281 } 282 return false; 283 } 284 285 processVote = (msg) => { 286 if (this.state.phase == 'lobby') { 287 this.gameStartSetup(msg); 288 } else if (this.state.phase == 'pause' || 289 (this.state.phase == 'vote2' && msg.game.pass_count == 0)) { 290 // second condition is for all players pass trump twice 291 this.trumpStartSetup(msg); 292 } 293 const {leftSeat, rightSeat, partnerSeat, mySeat, dealSeat, hard_order, hard_pick, stick_dealer} = this.state; 294 const vote1phase = msg.game.pass_count < 4; 295 const phaseString = vote1phase ? 'vote' : 'vote2'; 296 const onlyAlone = hard_order && vote1phase && dealSeat == partnerSeat; 297 const noPass = stick_dealer && !vote1phase && (dealSeat == mySeat); 298 const noPick = hard_pick && vote1phase && (dealSeat == mySeat) && !this.haveSuit(msg.hand, msg.game.trump_nominee); 299 let turnInfo = ['', '', '', '']; 300 turnInfo[msg.game.turn] = 'trump?'; 301 this.setState({ 302 phase: phaseString, 303 myCards: msg.hand, 304 turnSeat: msg.game.turn, 305 onlyAlone: onlyAlone, 306 noPick: noPick, 307 noPass: noPass, 308 handLengths: msg.game.hand_lengths, 309 leftTurnInfo: turnInfo[leftSeat], 310 rightTurnInfo: turnInfo[rightSeat], 311 partnerTurnInfo: turnInfo[partnerSeat], 312 myTurnInfo: turnInfo[mySeat] 313 }); 314 }; 315 316 processSwap = msg => { 317 const {leftSeat, rightSeat, partnerSeat, mySeat, dealSeat} = this.state; 318 let turnInfo = ['', '', '', '']; 319 turnInfo[dealSeat] = 'Swapping...'; 320 this.setState({ 321 phase: 'swap', 322 leftTurnInfo: turnInfo[leftSeat], 323 rightTurnInfo: turnInfo[rightSeat], 324 partnerTurnInfo: turnInfo[partnerSeat], 325 myTurnInfo: turnInfo[mySeat] 326 }); 327 } 328 329 trickMsg = (num, action) => { 330 let retVal; 331 if (num == 0) { 332 retVal = action ? 'action' : ''; 333 } else { 334 retVal = num == 1 ? '1 trick' : (num + ' tricks'); 335 if (action) { 336 retVal = retVal + ', action'; 337 } 338 } 339 return retVal; 340 } 341 342 processPlay = msg => { 343 const {leftSeat, rightSeat, partnerSeat, mySeat, dealSeat, phase} = this.state; 344 let turnInfo = []; 345 if (phase == 'vote' || phase == 'vote2' || phase == 'swap') { 346 this.handStartSetup(msg); 347 } 348 for (let i = 0; i < 4; i++) { 349 const tricks = msg.game.tricks[i]; 350 let tInf = this.trickMsg(tricks, (msg.game.turn == i)); 351 turnInfo.push(tInf); 352 } 353 this.setState({ 354 phase: 'play', 355 myCards: msg.hand, 356 table: msg.game.table, 357 tricks: msg.game.tricks, 358 trickWinner: '', 359 handLengths: msg.game.hand_lengths, 360 leftTurnInfo: turnInfo[leftSeat], 361 rightTurnInfo: turnInfo[rightSeat], 362 partnerTurnInfo: turnInfo[partnerSeat], 363 myTurnInfo: turnInfo[mySeat] 364 }); 365 366 } 367 368 processPause = msg => { 369 let trickWinIndex = -1; 370 let trickWinner = ''; 371 for (let i = 0; i < 4; i++){ 372 if (msg.game.tricks[i] != this.state.tricks[i]){ 373 trickWinIndex = i; 374 break; 375 } 376 } 377 if (trickWinIndex > -1) { 378 //must be?? sanity check 379 trickWinner = this.state.playerNames[trickWinIndex] + ' takes the trick!' 380 } 381 this.setState ({ 382 table: msg.game.table, 383 handLengths: msg.game.hand_lengths, 384 myCards: msg.hand, 385 tricks: msg.game.tricks, 386 phase: 'pause', 387 trickWinner: trickWinner 388 }) 389 } 390 391 processEnd = msg => { 392 const {playerNames, leftSeat, partnerSeat, rightSeat, amSpectator} = this.state; 393 // arrangeScore[0] us, [1] them 394 const finalScore = this.arrangeScore(msg.game.score); 395 const myName = amSpectator ? playerNames[3] : 'You'; 396 const winMsg = finalScore[0] > finalScore[1] ? 397 myName + ' and ' + playerNames[partnerSeat] + ' win the game!!' : 398 playerNames[leftSeat] + ' and ' + playerNames[rightSeat] + ' win this one...'; 399 const innerWin = amSpectator ? 'Game Over...' : 400 finalScore[0] > finalScore[1] ? 'You Win!!' : 'You lost...'; 401 this.setState({ 402 phase: 'end', 403 score: finalScore, 404 bannerMsg: winMsg, 405 innerWinMsg: innerWin 406 }); 407 } 408 409 handStartSetup = msg => { 410 const {leftSeat, rightSeat, partnerSeat, mySeat, dealSeat} = this.state; 411 let handInfo = ['', '', '', '']; 412 const caller = msg.game.out_player > -1 ? 'Alone' : 'Caller'; 413 const callSeat = msg.game.caller; 414 if (callSeat == dealSeat) { 415 handInfo[dealSeat] = 'Dealer, ' + caller; 416 } else { 417 handInfo[dealSeat] = 'Dealer'; 418 handInfo[callSeat] = caller; 419 } 420 // this.state.score is always [{us}, {them}] 421 let score = this.arrangeScore(msg.game.score); 422 this.setState({ 423 trump: msg.game.trump, 424 table: msg.game.table, 425 score: score, 426 handLengths: msg.game.hand_lengths, 427 leftHandInfo: handInfo[leftSeat], 428 rightHandInfo: handInfo[rightSeat], 429 partnerHandInfo: handInfo[partnerSeat], 430 myHandInfo: handInfo[mySeat] 431 }); 432 433 } 434 435 arrangeScore = (msgScore) => { 436 // this.state.score is always [{us}, {them}] 437 const { mySeat } = this.state; 438 let score = []; 439 if (mySeat % 2 == 0){ 440 // we're evens 441 score[0] = msgScore[0]; 442 score[1] = msgScore[1]; 443 } else { 444 score[0] = msgScore[1]; 445 score[1] = msgScore[0]; 446 } 447 return score; 448 } 449 450 trumpStartSetup = (msg) => { 451 const {leftSeat, rightSeat, partnerSeat, mySeat, dealSeat} = this.state; 452 let handInfo = ['', '', '', '']; 453 const newDeal = msg.game.dealer; 454 handInfo[newDeal] = 'Dealer'; 455 let tpIndex = msg.game.dealer - mySeat; 456 tpIndex = (tpIndex < 0) ? tpIndex + 4 : tpIndex; 457 const trumpPlace = trumpPlacement[tpIndex]; 458 // this.state.score is always [{us}, {them}] 459 let score = this.arrangeScore(msg.game.score); 460 this.setState({ 461 trump: '', 462 trumpPlace: trumpPlace, 463 dealSeat: newDeal, 464 score: score, 465 trickWinner: '', 466 trumpNom: msg.game.trump_nominee, 467 leftHandInfo: handInfo[leftSeat], 468 rightHandInfo: handInfo[rightSeat], 469 partnerHandInfo: handInfo[partnerSeat], 470 myHandInfo: handInfo[mySeat] 471 }); 472 473 } 474 475 gameStartSetup = (msg) => { 476 const { playerNames, mySeat } = this.state; 477 let handInfo = [' ', ' ', ' ', ' ']; 478 let turnInfo = [' ', ' ', ' ', ' ']; 479 handInfo[msg.game.dealer] = 'Dealer'; 480 turnInfo[msg.game.turn] = 'trump?'; 481 const leftSeat = (mySeat + 1) % 4; 482 const partnerSeat = (mySeat + 2) % 4; 483 const rightSeat = (mySeat + 3) % 4; 484 let tpIndex = msg.game.dealer - mySeat; 485 tpIndex = (tpIndex < 0) ? tpIndex + 4 : tpIndex; 486 const trumpPlace = trumpPlacement[tpIndex]; 487 this.setState ({ 488 leftName: playerNames[leftSeat], 489 leftSeat: leftSeat, 490 leftHandInfo: handInfo[leftSeat], 491 leftTurnInfo: turnInfo[leftSeat], 492 partnerName: playerNames[partnerSeat], 493 partnerSeat: partnerSeat, 494 partnerHandInfo: handInfo[partnerSeat], 495 partnerTurnInfo: turnInfo[partnerSeat], 496 rightName: playerNames[rightSeat], 497 rightSeat: rightSeat, 498 rightHandInfo: handInfo[rightSeat], 499 rightTurnInfo: turnInfo[rightSeat], 500 myHandInfo : handInfo[mySeat], 501 myTurnInfo : turnInfo[mySeat], 502 trumpPlace : trumpPlace, 503 trumpNom: msg.game.trump_nominee, 504 dealSeat: msg.game.dealer 505 }); 506 }; 507 508 sendSit = (index) => { 509 const { client } = this.props; 510 client.send(JSON.stringify({ 511 action:'take_seat', 512 seat: index 513 })); 514 }; 515 516 sendStand = () => { 517 const { client } = this.props; 518 client.send(JSON.stringify({ 519 action:'stand_up' 520 })); 521 }; 522 523 sendStart = (startDealer) => { 524 this.props.client.send(JSON.stringify({ 525 action: 'start_game', start_seat: startDealer 526 })); 527 // console.log('start game, dealer = ', startDealer); 528 }; 529 530 sendExit = () => { 531 this.props.exitTable(); 532 }; 533 534 sendVote = (voteObject) => { 535 const voteString = JSON.stringify(voteObject); 536 // console.log('sendVote:', voteString); 537 this.props.client.send(voteString); 538 }; 539 540 sendCard = (index) => { 541 const {phase, myCards} = this.state; 542 // console.log('card click ', myCards[index]); 543 if (phase == 'swap') { 544 this.props.client.send(JSON.stringify({ 545 action:'dealer_swap', card: myCards[index] 546 })); 547 } else if (phase == 'play') { 548 this.props.client.send(JSON.stringify({ 549 action:'play_card', card: myCards[index] 550 })); 551 } 552 }; 553 554 sendRestart = () => { 555 this.props.client.send(JSON.stringify({ 556 action:'restart_game' 557 })); 558 } 559 560 sendChat = post => { 561 console.log(post); 562 if (post && post != ''){ 563 this.props.client.send(JSON.stringify({ 564 action: 'chat', 565 msg: post 566 })); 567 } 568 // XX TODO remove this debug action when chat stable 569 if ('chat10' == post){ 570 for (let i=0; i < 10; i++){ 571 const msg='this is chat10 test-chat-' + i; 572 this.props.client.send(JSON.stringify({ 573 action: 'chat', 574 msg: msg 575 })); 576 } 577 } 578 } 579 580 toggleErrorDisplay = () => { 581 // NOTE this trick requires that .err__post is the first selector of all, 582 // so requires it first in our .scss, even before imports 583 const { showErrors } = this.state; 584 let stylesheet = document.styleSheets[0]; 585 const nextShowErrors = !showErrors; 586 const rule = nextShowErrors ? 'inherit' : 'none'; 587 stylesheet.cssRules[0].style.display = rule; 588 this.setState({ 589 showErrors: nextShowErrors 590 }) 591 console.log('toggleErrorDisplay'); 592 } 593 594 genGameOver = () => { 595 const {innerWinMsg, amSpectator} = this.state; 596 let retVal = []; 597 const instMsg = amSpectator ? 'You can take a seat if one becomes empty, or you can return to the lobby...' 598 : 'You can play again at this table, or return to the lobby to change your table or player name...'; 599 retVal.push( 600 <div className="gover__outer" key="gom1"> 601 <div className="gover__inwin">{innerWinMsg}</div> 602 <div className="gover__inst"> 603 {instMsg} 604 </div> 605 <div className="gover__buttons"> 606 <Button 607 className="exit2__button" 608 kind="secondary" 609 onClick={this.sendExit} 610 renderIcon={Logout32} 611 >Exit to Lobby</Button> 612 {!amSpectator && ( 613 <Button 614 className="repeat__button" 615 kind="primary" 616 onClick={()=>this.sendRestart()} 617 renderIcon={Redo32} 618 >Play Again!!</Button> 619 620 )} 621 </div> 622 </div> 623 ); 624 return retVal; 625 } 626 627 genTrick = () => { 628 let retVal = []; 629 const { table, mySeat, leftSeat, rightSeat, partnerSeat } = this.state; 630 const myCard = table[mySeat]; 631 const leftCard = table[leftSeat]; 632 const partnerCard = table[partnerSeat]; 633 const rightCard = table[rightSeat]; 634 const mySrc = 'cards/' + myCard + '.svg'; 635 const leftSrc = 'cards/' + leftCard + '.svg'; 636 const partnerSrc = 'cards/' + partnerCard + '.svg'; 637 const rightSrc = 'cards/' + rightCard + '.svg'; 638 let midClass = 'mid__trick'; 639 if (myCard && partnerCard) { 640 midClass += ' both'; 641 } else if (myCard) { 642 midClass += ' me'; 643 } else if (partnerCard) { 644 midClass += ' partner'; 645 } 646 retVal.push ( 647 <div className="trick__outer" key="gt1"> 648 <Grid className="trick__grid"> 649 <Row className="trick__row"> 650 <Column> 651 <div className="left__trick"> 652 {leftCard && ( 653 <div className="trick__div"> 654 <img className="trick__card" src={leftSrc} /> 655 </div> 656 )} 657 </div> 658 </Column> 659 <Column> 660 <div className={midClass}> 661 {partnerCard && ( 662 <div className="trick__div"> 663 <img className="trick__card" src={partnerSrc} /> 664 </div> 665 )} 666 {myCard && ( 667 <div className="trick__div"> 668 <img className="trick__card" src={mySrc} /> 669 </div> 670 )} 671 </div> 672 </Column> 673 <Column> 674 <div className="right__trick"> 675 {rightCard && ( 676 <div className="trick__div"> 677 <img className="trick__card" src={rightSrc} /> 678 </div> 679 )} 680 </div> 681 </Column> 682 </Row> 683 </Grid> 684 </div> 685 ); 686 return retVal; 687 } 688 689 genNameDisplay = seatNum => { 690 let retVal = ''; 691 const { playerNames, mySeat, myHandInfo, amSpectator } = this.state; 692 let seatName = playerNames[seatNum]; 693 if (seatNum == mySeat) { 694 seatName = amSpectator ? seatName : 'You'; 695 if (seatName != 'Empty'){ 696 seatName += ': ' + myHandInfo; 697 } 698 } 699 if (seatName != 'Empty') { 700 retVal = (<div>{seatName}</div>); 701 } else { 702 retVal = ( 703 <div>Empty 704 {amSpectator && ( 705 <Button 706 className="sit__button" 707 kind="ghost" 708 onClick={()=>{this.sendSit(seatNum)}} 709 renderIcon={Package32}>Choose seat</Button> 710 )} 711 </div>); 712 } 713 return retVal; 714 } 715 716 genSpecMsgObj = () => { 717 const {spectators} = this.state; 718 let retVal = {} 719 if (!spectators || spectators.length == 0) { 720 retVal.title = 'No Spectators'; 721 retVal.list = null; 722 } else { 723 retVal.title = (spectators.length == 1) ? '1 Spectator:' : spectators.length + ' Spectators:'; 724 retVal.list = spectators[0]; 725 for (let i=1; i < spectators.length; i++) { 726 retVal.list += ', ' + spectators[i]; 727 }; 728 } 729 return retVal; 730 } 731 732 render () { 733 const { playerNames, mySeat, phase, myCards, myTurnInfo, amSpectator, 734 partnerHandInfo, partnerTurnInfo, partnerSeat, leftTurnInfo, leftHandInfo, leftSeat, 735 rightHandInfo, rightTurnInfo, rightSeat, trumpPlace, trumpNom, turnSeat, spectators, 736 dealSeat, trump, handLengths, score, trickWinner, bannerMsg, noPick, noPass, onlyAlone, 737 hard_pick, hard_order, stick_dealer, latestPost, showErrors } = this.state; 738 const {tableName} = this.props; 739 const showSeatPicker = phase == 'lobby'; 740 const showGameOver = phase == 'end'; 741 const showTrump = (phase == 'vote') || (phase == 'vote2') || (phase == 'swap'); 742 const showTrumpPicker = showTrump && (turnSeat == mySeat); 743 const showSwap = (phase == 'swap') && (dealSeat == mySeat); 744 const showBottomInfo = !showSeatPicker && (amSpectator || (!showTrumpPicker && !showSwap)); 745 const tcp = "trump__holder " + trumpPlace; 746 const trumpImage = (phase != 'vote2') ? 'cards/' + trumpNom + '.svg' : 'cards/1B.svg'; 747 const trumpMsg = phase == 'play' ? suit[trump] + ' are trump' : ''; 748 const trickDisplay = (phase == 'play' || phase == 'pause') ? this.genTrick() : []; 749 const gameOverDisplay = (phase == 'end') ? this.genGameOver() : []; 750 const usLabel = amSpectator ? playerNames[1] + ' & ' + playerNames[3] + ': ' : 'Us: '; 751 const themLabel = amSpectator ? playerNames[0] + ' & ' + playerNames[2] + ': ' : 'Them: '; 752 const hasSpec = spectators.length > 0; 753 const specMsgObj = this.genSpecMsgObj(); 754 const toggleErrorMsg = showErrors ? 'Hide errors in chat' : 'Show errors in chat'; 755 return ( 756 <div id="table" className="table__main"> 757 <Grid className="og"> 758 <Row className="og__row"> 759 <Column className="og__left" md={5}> 760 <Grid className="inner__left"> 761 {(showSeatPicker || showGameOver) && ( 762 <Row className="table__header"> 763 <h3 className="banner">{bannerMsg}</h3> 764 </Row> 765 )} 766 <Row className="table__top"> 767 <Column className="tt__left" md={3}> 768 {!showSeatPicker && ( 769 <div className="menu__holder"> 770 <OverflowMenu> 771 <OverflowMenuItem 772 itemText="Stand" 773 disabled={amSpectator} 774 onClick={this.sendStand} 775 /> 776 <OverflowMenuItem 777 itemText="Exit to Lobby" 778 onClick={this.sendExit} 779 /> 780 <OverflowMenuItem 781 itemText={toggleErrorMsg} 782 onClick={this.toggleErrorDisplay} 783 /> 784 </OverflowMenu> 785 </div> 786 )} 787 </Column> 788 <Column className="tt__center" md={5}> 789 {!showSeatPicker && ( 790 <div className="partner__stack"> 791 <div className="player__name">{this.genNameDisplay(partnerSeat)}</div> 792 <div className="partner__info"> 793 <div className="play__hinfo">{partnerHandInfo}</div> 794 <div className="play__tinfo">{partnerTurnInfo}</div> 795 </div> 796 <HiddenHand 797 numCards={handLengths[partnerSeat]} /> 798 </div>)} 799 </Column> 800 </Row> 801 <Row className="table__mid"> 802 <Column className="tm__left" md={3}> 803 {!showSeatPicker && ( 804 <div className="vert__stack"> 805 <div className="player__name">{this.genNameDisplay(leftSeat)}</div> 806 <div className="play__hinfo">{leftHandInfo}</div> 807 <div className="play__tinfo">{leftTurnInfo}</div> 808 <HiddenHand 809 numCards={handLengths[leftSeat]} /> 810 </div>)} 811 </Column> 812 <Column className="tm__center" md={5}> 813 {showSeatPicker && ( 814 <SeatPicker 815 names={playerNames} 816 handleSit={this.sendSit} 817 handleStand={this.sendStand} 818 mySeat={mySeat} 819 amSpectator={amSpectator} 820 handleStart={this.sendStart} 821 />)} 822 { showTrump && ( 823 <div className="trump__outer"> 824 <div className={tcp}> 825 <img className="trump__card" src={trumpImage} /> 826 </div> 827 </div> 828 )} 829 { (phase=='play' || phase=='pause') && ( 830 <div className="trick__holder">{trickDisplay}</div> 831 )} 832 { showGameOver && ( 833 <div className="gameOver__holder"> 834 {gameOverDisplay} 835 </div> 836 )} 837 </Column> 838 </Row> 839 <Row className="table__bot"> 840 <Column className="tb__left" md={3}> 841 {showBottomInfo && ( 842 <div className="my__stack"> 843 <div className="my__hinfo">{this.genNameDisplay(mySeat)}</div> 844 <div className="my__tinfo">{myTurnInfo}</div> 845 <div className="my__tinfo">{trumpMsg}</div> 846 </div> 847 )} 848 {showTrumpPicker && !amSpectator && ( 849 <TrumpPicker 850 trumpCard={trumpNom} 851 phaseTwo={phase == 'vote2'} 852 myDeal={dealSeat == mySeat} 853 onlyAlone={onlyAlone} 854 noPick={noPick} 855 noPass={noPass} 856 handleVote={this.sendVote} /> 857 )} 858 {showSwap && !amSpectator && ( 859 <div className="my__stack"> 860 <div className="my_tinfo">Click a card to discard:</div> 861 </div> 862 )} 863 </Column> 864 <Column className="tb__center" md={5}> 865 {!showSeatPicker && !amSpectator && ( 866 <MainHand 867 cards={myCards} 868 cardClick={this.sendCard} 869 /> 870 )} 871 {!showSeatPicker && amSpectator && ( 872 <HiddenHand 873 numCards={handLengths[mySeat]} /> 874 )} 875 </Column> 876 </Row> 877 </Grid> 878 </Column> 879 <Column className="og__right" md={3}> 880 <Grid className="inner_right"> 881 <Row className="tt__right" > 882 {phase == 'lobby' && ( 883 <div className="exit__row"> 884 <Button 885 className="leave__button" 886 kind="ghost" 887 onClick={this.sendExit} 888 renderIcon={Logout32}>Exit</Button> 889 </div> 890 )} 891 {(phase != 'lobby') && ( 892 <div className="score__holder"> 893 <div className="us__score">{usLabel}{score[0]}</div> 894 <div className="them__score">{themLabel}{score[1]}</div> 895 <div className="trick__win">{trickWinner}</div> 896 <div className="info__buttons"> 897 <Tooltip 898 direction="left" 899 renderIcon={Information16} 900 > 901 <div className="table__info"> 902 <div className="ti__name">{tableName}</div> 903 <div className="ti__opt">HardOrder: {hard_order ? 'on' : 'off'}</div> 904 <div className="ti__opt">HardPick: {hard_pick ? 'on' : 'off'}</div> 905 <div className="ti__opt">StickDealer: {stick_dealer ? 'on' : 'off'}</div> 906 </div> 907 </Tooltip> 908 <Tooltip 909 direction="left" 910 renderIcon={hasSpec? View16 : ViewOff16} 911 > 912 <div className="spec__info"> 913 <div className="spec__title"> 914 {specMsgObj.title} 915 </div> 916 {(specMsgObj.list != null) && ( 917 <div className="spec__list"> 918 {specMsgObj.list} 919 </div> 920 )} 921 </div> 922 </Tooltip> 923 </div> 924 </div> 925 )} 926 </Row> 927 <Row className="tm__right"> 928 {!showSeatPicker && ( 929 <div className="vert__stack right__stack"> 930 <div className="player__name">{this.genNameDisplay(rightSeat)}</div> 931 <div className="play__hinfo">{rightHandInfo}</div> 932 <div className="play__tinfo">{rightTurnInfo}</div> 933 <HiddenHand 934 numCards={handLengths[rightSeat]} /> 935 </div>)} 936 </Row> 937 <Row className="tb__right"> 938 <ChatPanel 939 receivedChat={latestPost} 940 sendChat={this.sendChat} /> 941 </Row> 942 </Grid> 943 </Column> 944 </Row> 945 </Grid> 946 </div> 947 ); 948 }; 949 } 950 951 CardTable.propTypes = { 952 exitTable: PropTypes.func, 953 name: PropTypes.string, 954 tableName: PropTypes.string, 955 firstMsg: PropTypes.object, 956 client: PropTypes.object 957 }