Initial commit

This commit is contained in:
2026-04-15 11:51:49 -07:00
commit 18dd8703e1
197 changed files with 45676 additions and 0 deletions

217
Source/Game.js Normal file
View File

@@ -0,0 +1,217 @@
import React from "react";
import io from "socket.io-client"
import pako from "pako";
import * as Act from "./interface/Actions";
import Store from "./interface/Store";
import Players from "./Players"
import WinStats from "./WinStats"
import SerStats from "./SeriesStats";
import settings from "../settings";
import retImg from "./Images/goback.png"
import bp1Img from "./Images/spkron.png"
import bp2Img from "./Images/spkroff.png"
export default class Game extends React.Component {
constructor() {
super()
this.state = {
auth: Store.getToken(),
players: [], hand: [], turn: [], status: 0, atr: 0,
live: false, msg: '', noise: true,
myturn: false, coin: 0, avail: [],
}
this.color = ['grey', 'red', 'green', 'blue']
this.card = []
this.hand = []
this.connect = this.connect.bind(this)
this.boardCard = this.boardCard.bind(this)
this.dealtCard = this.dealtCard.bind(this)
this.player = this.player.bind(this)
this.status = this.status.bind(this)
this.myturn = this.myturn.bind(this)
this.onDragStart = this.onDragStart.bind(this)
this.onDragOver = this.onDragOver.bind(this)
this.onDrop = this.onDrop.bind(this)
}
componentDidMount() {
this.socket = io.connect(settings.server.baseSite,
{ query: 'token=' + this.state.auth + '&game=' + this.props.game })
this.socket.on('connect', this.connect)
this.socket.on('boardCard', this.boardCard)
this.socket.on('dealtCard', this.dealtCard)
this.socket.on('player', this.player)
this.socket.on('status', this.status)
this.socket.on('myturn', this.myturn)
this.socket.emit('reqBoardCards', { id: 'BRDCRD' })
}
componentWillUnmount() {
this.socket.emit('disconnect', { id: 'PLYERS', act: 0, soc: '' })
this.socket.disconnect()
}
connect() {
this.socket.emit('reqPlayers', { id: 'PLYERS', act: 1 })
}
boardCard(data) {
const binData = new Uint8Array(data);
const obj = JSON.parse(pako.inflate(binData, { to: 'string' }));
const node = this.card[obj.crd]
node.innerHTML = obj.img;
}
dealtCard(data) {
const binData = new Uint8Array(data);
const obj = JSON.parse(pako.inflate(binData, { to: 'string' }));
const node = this.hand[obj.inx]
node.innerHTML = obj.img;
let hand = this.state.hand
hand[obj.inx] = { dlr: obj.dlr }
this.setState({ hand: hand })
}
player(data) {
let obj = data
this.setState({ players: obj.players, live: obj.live })
if (obj.live === true) {
this.socket.emit('reqDealtHand', { id: 'DLTHND' })
} else {
for (let i = 0; i < this.hand.length; i++) {
const node = this.hand[i]
while (node.firstChild) node.removeChild(node.firstChild);
}
}
}
status(data) {
let obj = data
this.setState({ turn: obj.turn, msg: obj.msg, atr: obj.atr, status: Number(obj.typ) })
if (obj.typ === 1) {
Act.apiCall('service', 'GMSTAT', { match: this.props.game })
this.setState({ myturn: false, coin: 0 })
} else if (obj.typ === 2) {
this.socket.emit('reqDealtHand', { id: 'DLTHND' })
this.setState({status: 0})
}
}
myturn(data) {
let obj = data
this.setState({ myturn: true, coin: obj.coin, avail: obj.avail })
if (this.state.noise) this.beep()
}
newgame() {
this.socket.emit('reqNewGame', { id: 'GAMREQ' })
}
leave() {
this.props.xout()
}
beep() {
var snd = new Audio("data:audio/wav;base64,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qJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3kuZGUAAAAAAAAAACU=");
snd.play();
}
onDragStart(ev, id) {
ev.dataTransfer.setData("id", id);
let inx = Number(id.substr(1));
let dlr = this.state.hand[inx].dlr
for (let i = 0; i < this.state.avail.length; i++) {
if (this.state.avail[i].dlr === dlr) {
this.state.avail[i].p.map((x) => {
const node = this.card[x]
node.addEventListener('dragover', this.onDragOver);
// console.log("d:" + dlr + " i:" + i + " c:" + x)
})
}
}
}
onDragOver(ev) {
ev.preventDefault();
}
onDrop(ev, msg) {
let id = ev.dataTransfer.getData("id");
let inx = Number(id.substr(1));
//console.log("I:" + inx + " B:" + msg.substr(1) + " D:" + this.state.hand[inx].dlr)
this.socket.emit('PlayCard', { id: 'CRDPLY', crd: msg.substr(1), inx: inx, dlr: this.state.hand[inx].dlr })
this.setState({ myturn: false, coin: 0 })
for (let i = 0; i < this.card.length; i++) {
const node = this.card[i]
node.removeEventListener('dragover', this.onDragOver);
}
}
render() {
let dec = []
for (let i = 0; i < 100; i++) {
dec.push(<div className="card"
onDrop={(e) => this.onDrop(e, 'X' + i)}
ref={(component) => { this.card[i] = component; }} >
</div>)
}
let hnd = []
for (let i = 0; i < this.props.deal; i++) {
hnd.push(<div id="dealt" className="dealt">
{this.state.live && this.state.myturn === true
? <div className="coin"
draggable onDragStart={(e) => this.onDragStart(e, 'I' + i)}
style={{
backgroundColor: this.color[this.state.coin], backgroundClip: "content-box",
opacity: "0.7", border: "solid 3px " + this.color[this.state.coin]
}}>
</div>
: <div className="coin"
style={{ backgroundColor: "#f0f0f0", opacity: "0.7", border: "solid 3px grey" }}>
</div>
}
{this.state.status === 0
? <div className="card" style={{ backgroundImage: "url('FC.svg')", backgroundSize: "100%" }}
ref={(component) => { this.hand[i] = component; }} >
</div>
: null
}
</div>)
}
return (
<div className="flexCol">
<div className="flexRow flexSpace msg">
<div className="flexRow" onClick={this.leave.bind(this)}>
<img src={retImg} />return
</div>
<div style={{ color: this.color[this.state.atr] }}>
{this.state.msg}
</div>
<div className="flexRow">
<SerStats />
<img src={this.state.noise ? bp1Img : bp2Img}
onClick={(e) => this.setState({ noise: !this.state.noise })}
style={{ width: "16px", height: "16px", margin: "2px 7px" }} />
</div>
</div>
<div className="flexRow">
<Players players={this.state.players} turn={this.state.turn} live={this.state.live} />
<div id="board" className={this.state.myturn ? "flash" : ""}>
{dec}
</div>
{this.state.status === 1
? <WinStats newgame={this.newgame.bind(this)} game={this.props.game} />
: <div className="flexCol" style={{ marginTop: "8px" }}>
{hnd}
</div>
}
</div>
</div>
);
}
}

133
Source/GameSetup.js Normal file
View File

@@ -0,0 +1,133 @@
import React from "react";
import * as Act from "./interface/Actions";
import Store from "./interface/Store";
import Game from "./Game";
import NewMatch from "./NewMatch";
import NewGroup from "./NewGroup";
import delImg from "./Images/delete.png";
import plyImg from "./Images/play.png";
import rfhImg from "./Images/refresh.png";
export default class GameSetup extends React.Component {
constructor() {
super();
this.state = {
serlst: Store.getSeries(), grp: '', rsfh: true,
gamtag: '', deal: 0, flag: false, tab: 0
};
this.getSeries = this.getSeries.bind(this);
this.color = ['grey', 'red', 'green', 'blue']
}
componentDidMount() {
Store.on("series", this.getSeries)
}
componentWillUnmount() {
Store.removeListener("series", this.getSeries)
}
dltSeries(inx, e) {
let tag = this.state.serlst[inx].Tag
Act.apiCall('service', 'SERDLT', { ser: tag });
}
getSeries() {
this.setState({ serlst: Store.getSeries(), rfsh: true })
}
joinGame(inx, e) {
let cond = true
let tag = this.state.serlst[inx].Tag
let hnd = this.state.serlst[inx].Deal
Act.apiCall('service', 'JOINGM', { match: tag });
this.setState({ gamtag: tag, deal: hnd, flag: cond })
}
refreshSeries() {
Act.apiCall('service', 'SERRFH', {});
this.setState({rfsh: false})
}
backButton() {
this.setState({ flag: false })
}
render() {
let series = this.state.serlst.map((g, i) => {
return <div className="flexRow" style={{ fontFamily: "Roboto", fontSize: "12px", borderBottom: "1px solid #f0f0f0" }}>
<img src={delImg}
onClick={this.dltSeries.bind(this, i)}
style={{ width: "16px", height: "16px", marginRight: "2px" }} />
<div className={g.live > 0 ? "flexRow hilit" : "flexRow"} onClick={this.joinGame.bind(this, i)} >
<div style={{ width: "165px" }}>{g.Desc}</div>
<div style={{ width: "125px" }}>{g.Group}</div>
<div style={{ width: "15px" }} >{g.played}</div>
<div style={{ width: "15px", textAlign: "center" }} >{g.live}</div>
<div style={{ alignContent: "flex-end" }} >
<img src={plyImg} />
</div>
</div>
</div>
})
return (
<div style={{ display: "flex", justifyContent: "center" }}>
{this.state.flag
? <Game game={this.state.gamtag} deal={this.state.deal} xout={this.backButton.bind(this)} />
: <div className="flexCol" style={{ display: "flex", justifyContent: "center", width: "375px" }}>
<div className="flexRow boxHdr">
<div className="flexCol" >
<div className="flexRow">
<div style={{ marginLeft: "135px" }}>Games In Motion</div>
<div style={{ marginLeft: "60px" }}>gme ply</div>
</div>
<div className="flexRow">
<div style={{ width: "18px" }}></div>
<div style={{ width: "165px" }}>Match</div>
<div style={{ width: "113px" }}>Group</div>
<div style={{ width: "18px" }}>#</div>
<div style={{ width: "15px" }}>#</div>
</div>
</div>
<div style={{ display: "flex", alignItems: "center", marginLeft: "5px" , width: "20px"}}
onClick={this.refreshSeries.bind(this)} >
{this.state.rfsh
? <img src={rfhImg} />
: null
}
</div>
</div>
<div className="flexCol" style={{
overflowY: "scroll", height: "150px",
border: "solid 1px grey",
marginBottom: "15px"
}}>
{series}
</div>
<div style={{ display: "flex", justifyContent: "center" }}>
<ul className="tabrow">
<li className={this.state.tab === 0 ? "tabsel" : ""}
onClick={(e) => this.setState({ tab: 0 })}>
New Match
</li>
<li className={this.state.tab === 1 ? "tabsel" : ""}
onClick={(e) => this.setState({ tab: 1 })}>
New Group
</li>
</ul>
</div>
<div className="gameBox" style={{ height: "165px" }}>
{this.state.tab === 0
? <NewMatch />
: <NewGroup />
}
</div>
<div className="boxTrl" style={{ color: "grey" }}>Code Creation by Grae Jones</div>
</div>
}
</div>
);
}
}

BIN
Source/Images/add.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 747 B

BIN
Source/Images/addD.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 B

BIN
Source/Images/addE.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 747 B

BIN
Source/Images/addsmlD.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 B

BIN
Source/Images/btnblu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

BIN
Source/Images/btngrn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 B

BIN
Source/Images/btnred.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 B

BIN
Source/Images/delete.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

BIN
Source/Images/goback.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="20 20 24 24"><path fill="#CAD1DA" d="M39.905 32h-1.718v-4.813C38.187 23.77 35.417 21 32 21c-3.418 0-6.188 2.77-6.188 6.187V32h-1.72c-.57 0-1.03.462-1.03 1.03v8.938c0 .57.46 1.03 1.03 1.03h15.813c.57 0 1.03-.46 1.03-1.03V33.03c.002-.568-.46-1.03-1.03-1.03zm-6.875 5.9v2.005c0 .57-.46 1.03-1.03 1.03-.57 0-1.032-.46-1.032-1.03V37.9c-.614-.356-1.03-1.014-1.03-1.775 0-1.14.922-2.063 2.062-2.063 1.14 0 2.063.922 2.063 2.063 0 .762-.418 1.42-1.033 1.776zm3.094-5.9h-8.25v-4.813c0-2.278 1.846-4.125 4.125-4.125 2.276 0 4.122 1.847 4.122 4.125V32z"/></svg>

After

Width:  |  Height:  |  Size: 622 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="96 61 24 24"><g fill="#CAD1DA"><path d="M107.986 72.983c1.52 0 2.815-.536 3.888-1.61 1.074-1.074 1.612-2.37 1.612-3.888 0-1.52-.538-2.815-1.612-3.89-1.073-1.074-2.37-1.61-3.888-1.61s-2.815.536-3.89 1.61c-1.073 1.074-1.61 2.37-1.61 3.89 0 1.518.537 2.814 1.61 3.888 1.076 1.074 2.372 1.61 3.89 1.61z"/><path d="M118.02 78.79c-.035-.48-.103-1.002-.202-1.56-.1-.56-.228-1.077-.38-1.554-.152-.478-.358-.944-.615-1.398-.258-.45-.554-.838-.89-1.16-.333-.318-.74-.574-1.223-.764-.482-.19-1.015-.288-1.598-.288-.085 0-.286.103-.602.308s-.67.436-1.066.688c-.397.254-.912.483-1.547.688s-1.272.307-1.913.307c-.64 0-1.277-.102-1.91-.307-.636-.205-1.15-.434-1.548-.688-.396-.252-.75-.482-1.066-.688s-.516-.308-.602-.308c-.582 0-1.114.096-1.597.288-.48.19-.89.445-1.22.765-.335.32-.63.704-.89 1.155-.26.454-.462.92-.615 1.398-.153.477-.28.995-.38 1.554-.1.557-.167 1.08-.2 1.56-.034.482-.05.98-.05 1.48 0 1.147.348 2.05 1.044 2.718.696.662 1.622.994 2.78.994h12.515c1.155 0 2.08-.333 2.778-.996.7-.667 1.05-1.57 1.05-2.718 0-.504-.017-1-.05-1.483z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
Source/Images/minus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
Source/Images/play.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 796 B

52
Source/Images/plusG.svg Normal file
View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<polygon style="fill:#CFF09E;" points="370.287,221.92 290.08,221.92 290.08,141.712 221.92,141.712 221.92,221.92 141.713,221.92
141.713,290.079 221.92,290.079 221.92,370.287 290.08,370.287 290.08,290.079 370.287,290.079 "/>
<g>
<path style="fill:#507C5C;" d="M290.08,384.789H221.92c-8.009,0-14.502-6.493-14.502-14.502v-65.706h-65.706
c-8.009,0-14.502-6.493-14.502-14.502V221.92c0-8.009,6.493-14.502,14.502-14.502h65.706v-65.706
c0-8.009,6.493-14.502,14.502-14.502h68.159c8.008,0,14.502,6.493,14.502,14.502v65.706h65.706c8.008,0,14.502,6.493,14.502,14.502
v68.159c0,8.009-6.494,14.502-14.502,14.502h-65.706v65.706C304.582,378.296,298.089,384.789,290.08,384.789z M236.422,355.785
h39.155v-65.706c0-8.009,6.494-14.502,14.502-14.502h65.706v-39.155H290.08c-8.008,0-14.502-6.493-14.502-14.502v-65.706h-39.155
v65.706c0,8.009-6.493,14.502-14.502,14.502h-65.706v39.155h65.706c8.009,0,14.502,6.493,14.502,14.502V355.785z"/>
<path style="fill:#507C5C;" d="M497.497,512H14.503c-8.009,0-14.502-6.493-14.502-14.502V14.502C0.001,6.493,6.493,0,14.503,0
h482.995c8.008,0,14.502,6.493,14.502,14.502v385.151c0,8.009-6.494,14.502-14.502,14.502s-14.502-6.493-14.502-14.502V29.004
H29.005v453.992h468.493c8.008,0,14.502,6.493,14.502,14.502S505.507,512,497.497,512z"/>
<path style="fill:#507C5C;" d="M427.065,441.568H84.935c-8.009,0-14.502-6.493-14.502-14.502V154.242
c0-8.009,6.493-14.502,14.502-14.502c8.009,0,14.502,6.493,14.502,14.502v258.323h313.127V99.436H84.935
c-8.009,0-14.502-6.493-14.502-14.502s6.493-14.502,14.502-14.502h342.131c8.008,0,14.502,6.493,14.502,14.502v342.132
C441.567,435.076,435.075,441.568,427.065,441.568z"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

52
Source/Images/plusY.svg Normal file
View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<polygon style="fill:#FEE187;" points="370.287,221.92 290.08,221.92 290.08,141.712 221.92,141.712 221.92,221.92 141.713,221.92
141.713,290.079 221.92,290.079 221.92,370.287 290.08,370.287 290.08,290.079 370.287,290.079 "/>
<g>
<path style="fill:#FFC61B;" d="M290.08,384.789H221.92c-8.009,0-14.502-6.493-14.502-14.502v-65.706h-65.706
c-8.009,0-14.502-6.493-14.502-14.502V221.92c0-8.009,6.493-14.502,14.502-14.502h65.706v-65.706
c0-8.009,6.493-14.502,14.502-14.502h68.159c8.008,0,14.502,6.493,14.502,14.502v65.706h65.706c8.008,0,14.502,6.493,14.502,14.502
v68.159c0,8.009-6.494,14.502-14.502,14.502h-65.706v65.706C304.582,378.296,298.089,384.789,290.08,384.789z M236.422,355.785
h39.155v-65.706c0-8.009,6.494-14.502,14.502-14.502h65.706v-39.155H290.08c-8.008,0-14.502-6.493-14.502-14.502v-65.706h-39.155
v65.706c0,8.009-6.493,14.502-14.502,14.502h-65.706v39.155h65.706c8.009,0,14.502,6.493,14.502,14.502V355.785z"/>
<path style="fill:#FFC61B;" d="M497.497,512H14.503c-8.009,0-14.502-6.493-14.502-14.502V14.502C0.001,6.493,6.493,0,14.503,0
h482.995c8.008,0,14.502,6.493,14.502,14.502v385.151c0,8.009-6.494,14.502-14.502,14.502s-14.502-6.493-14.502-14.502V29.004
H29.005v453.992h468.493c8.008,0,14.502,6.493,14.502,14.502S505.507,512,497.497,512z"/>
<path style="fill:#FFC61B;" d="M427.065,441.568H84.935c-8.009,0-14.502-6.493-14.502-14.502V154.242
c0-8.009,6.493-14.502,14.502-14.502c8.009,0,14.502,6.493,14.502,14.502v258.323h313.127V99.436H84.935
c-8.009,0-14.502-6.493-14.502-14.502s6.493-14.502,14.502-14.502h342.131c8.008,0,14.502,6.493,14.502,14.502v342.132
C441.567,435.076,435.075,441.568,427.065,441.568z"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
Source/Images/refresh.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 814 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 B

BIN
Source/Images/search.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
Source/Images/spkroff.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
Source/Images/spkron.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

47
Source/Index.js Normal file
View File

@@ -0,0 +1,47 @@
import React from "react";
import { render } from "react-dom";
import SignIn from "./SignIn";
import Games from "./GameSetup";
import Store from "./interface/Store"
import * as Act from "./interface/Actions";
import "./Style/style.css";
export default class Index extends React.Component {
constructor() {
super()
this.state = { accs: Store.getAccess() }
this.getAuth = this.getAuth.bind(this)
//this.leaving = this.leaving.bind(this)
}
componentDidMount() {
//window.addEventListener('beforeunload', this.leaving);
Store.on("authChange", this.getAuth)
}
componentWillUnmount() {
//window.removeEventListener('beforeunload', this.leaving);
Store.removeListener("authChange", this.getAuth)
}
//leaving() {
// Act.apiCall('logout', { });
//}
getAuth() {
this.setState({ accs: Store.getAccess() })
}
render() {
return (
<div>
{this.state.accs.status > 0
? <Games />
: <SignIn msg={this.state.accs.msg}/>
}
</div>
);
}
}
render(<Index />, document.getElementById('page'))

155
Source/NewGroup.js Normal file
View File

@@ -0,0 +1,155 @@
import React from "react";
import Store from "./interface/Store";
import * as Act from "./interface/Actions";
import addE from "./Images/addE.png"
import addD from "./Images/addD.png"
import del from "./Images/minus.png"
export default class NewGroup extends React.Component {
constructor() {
super()
this.state = { usrs: Store.getUsers(), grpnme: '', team: [], coin: 1, usrinx: -1, msg: '' }
this.getUsers = this.getUsers.bind(this)
this.color = ['grey', 'red', 'green', 'blue']
}
componentDidMount() {
Store.on("users", this.getUsers)
Act.apiCall('service', 'USRLST', {});
}
componentWillUnmount() {
Store.removeListener("users", this.getUsers)
}
getUsers() {
this.setState({ usrs: Store.getUsers(), usrinx: 0 })
}
addUser() {
let pers = this.state.usrs[this.state.usrinx]
let team = this.state.team
team.push({ tag: pers.tag, nme: pers.desc, coin: this.state.coin })
this.setState({ team: team })
}
newGroup() {
Act.apiCall('service', 'GRPNEW', {
desc: this.state.grpnme,
group: this.state.team
});
this.setState({ grpnme: '', team: [], usrinx: 0 })
}
teamOff(inx, e) {
let tm = this.state.team
tm.splice(inx, 1)
this.setState({ team: tm })
}
check(me) {
let msg = []
let i = this.state.team.length
if (i === 0) return ['create group']
let cn = [...new Set(this.state.team.map((ent) => ent.coin))];
let nm = [...new Set(this.state.team.map((ent) => ent.tag))];
if (this.state.grpnme.length < 4)
msg.push('group name required')
if (nm.indexOf(me) === -1)
msg.push('must be in list')
if ([1, 5, 7, 9, 11].indexOf(i) > -1)
msg.push('uneven players')
if (i !== nm.length)
msg.push('duplicate player')
if (i === 2 && cn.length !== 2)
msg.push('different tokens')
if (i > 2 && i % cn.length !== 0)
msg.push('token mismatch')
return msg
}
render() {
let me = ''
let usrlst = this.state.usrs.map((u, i) => {
if (u.me === 1) me = u.tag
return <option value={i}>{u.desc}</option>
})
let team = this.state.team
? this.state.team.map((d, i) => {
return <div className="flexRow flexSpace">
<div style={{
fontFamily: "Roboto", fontSize: "12px",
fontWeight: (d.me === 1 ? "bold" : "normal")
}}>{d.nme}</div>
<div className="flexRow">
<div className="cirsml" style={{ backgroundColor: this.color[d.coin], marginRight: "2px" }}></div>
<img src={del} onClick={this.teamOff.bind(this, i)} style={{ width: "12px" }} />
</div>
</div>
})
: <div>no memebers</div>
let msg = this.check(me);
return (
<div className="flexRow flexPad">
<div className="flexCol flexPad" style={{ height: "162px" }}>
<div className="box" style={{ marginTop: "5px" }} >
<div className="boxHdr">group name</div>
<input type="text" value={this.state.grpnme} style={{ width: "174px" }}
placeholder="...enter something here"
onChange={(e) => this.setState({ grpnme: e.target.value })} />
</div>
<div className="box">
<div className="flexRow flexSpace" style={{ borderBottom: '1px solid grey' }}>
<div className="flexCol">
<select value={this.state.usrinx} style={{ width: "156px" }}
onChange={(e) => this.setState({ usrinx: e.target.value })}>
{usrlst}
</select>
<div className="flexRow flexSpace flexAlign" style={{ height: "20px" }}>
<div style={{ fontFamily: "Roboto", fontSize: "12px" }}>select token</div>
<div className={this.state.coin === 1 ? "circle" : "cirsml"}
style={{ backgroundColor: "red" }}
onClick={() => this.setState({ coin: 1 })}></div>
<div className={this.state.coin === 2 ? "circle" : "cirsml"}
style={{ backgroundColor: "green" }}
onClick={() => this.setState({ coin: 2 })} ></div>
<div className={this.state.coin === 3 ? "circle" : "cirsml"}
style={{ backgroundColor: "blue", marginRight: "15px" }}
onClick={() => this.setState({ coin: 3 })} ></div>
</div>
</div>
<div style={{ display: "flex", alignItems: "center" }}>
<img src="addsml.png" onClick={this.addUser.bind(this)} style={{ width: "24px", height: "24px" }} />
</div>
</div>
<div className="flexCol" style={{ overflowY: "scroll", height: "60px" }}>
{team}
</div>
</div>
</div>
<div className="flexCol">
<img src="team.png" style={{ width: "100px" }} />
<div className="flexCenter" style={{ marginTop: "7px" }}>
{msg.length === 0
? <img onClick={this.newGroup.bind(this)} src={addE} />
: <img src={addD} />
}
</div>
<div className="flexCol" style={{ fontFamily: "OpenSans", fontSize: "12px" }} >
{msg.map((m) => { return <div>{m}</div> })}
</div>
</div>
</div>
)
}
}

115
Source/NewMatch.js Normal file
View File

@@ -0,0 +1,115 @@
import React from "react";
import Store from "./interface/Store";
import * as Act from "./interface/Actions";
import delImg from "./Images/delete.png";
import addE from "./Images/addE.png"
import addD from "./Images/addD.png"
export default class NewMatch extends React.Component {
constructor() {
super()
this.state = {
grplst: Store.getGroups(), grp: -1,
fndlst: Store.getFriends(), sernme:''
}
this.getGroups = this.getGroups.bind(this);
this.getFriends = this.getFriends.bind(this);
this.color = ['grey', 'red', 'green', 'blue']
}
componentDidMount() {
Store.on("groups", this.getGroups)
Store.on("friends", this.getFriends)
}
componentWillUnmount() {
Store.removeListener("groups", this.getGroups)
Store.removeListener("friends", this.getFriends)
}
getGroups() {
this.setState({ grplst: Store.getGroups() })
}
getFriends() {
this.setState({ fndlst: Store.getFriends() })
}
groupChange(e) {
if (e.target.value > -1) {
let tag = this.state.grplst[e.target.value].Tag
Act.apiCall('service', 'GRFRND', { grp: tag });
this.setState({ grp: e.target.value })
} else {
this.setState({ grp: e.target.value, fndlst: [] })
}
}
delGroup() {
let tag = this.state.grplst[this.state.grp].Tag
Act.apiCall('service', 'GRPDLT', { grp: tag });
this.setState({ grp: -1, sernme: '', fndlst: [] })
}
newSeries() {
if (this.state.grp > -1 && this.state.sernme.length > 3) {
let tag = this.state.grplst[this.state.grp].Tag
Act.apiCall('service', 'NEWSER', { grp: tag, ser: this.state.sernme });
this.setState({ grp: -1, sernme: '', fndlst: [] })
}
}
render() {
let groups = this.state.grplst.map((g,i) => {
return <option value={i}>{g.Desc}</option>
})
let friends = this.state.fndlst.map((f) => {
return <div className="flexRow flexSpace" style={{ fontFamily: "Roboto", fontSize: "12px" }}>
<div style={{ maringLeft: "5px" }}>{f.Desc}</div>
<div style={{
marginRight: "5px", width: "14px", height: "14px",
borderRadius: "7px", backgroundColor: this.color[f.Coin]
}}></div>
</div>
})
let flag = this.state.grp > -1 && this.state.sernme.length > 3 ? true : false;
return (
<div className="flexRow flexPad">
<div className="flexCol flexPad" style={{ height: "162px" }}>
<div className="box" style={{ marginTop: "5px" }} >
<div className="boxHdr">match title</div>
<input type="text" value={this.state.sernme} style={{ width: "174px" }}
placeholder="...enter something here"
onChange={(e) => this.setState({ sernme: e.target.value })} />
</div>
<div className="box">
<div className="flexRow flexSpace" style={{ borderBottom: '1px solid grey' }}>
<div style={{ width: "18px", height: "18px" }}>
{
this.state.grp > -1
? <img src={delImg} onClick={this.delGroup.bind(this)} />
: null
}
</div>
<select value={this.state.grp} style={{ width: "160px" }}
onChange={this.groupChange.bind(this)}>
<option value='-1'>[please select group]</option>
{groups}
</select>
</div>
<div className="flexCol" style={{ overflowY: "scroll", height: "80px" }}>
{friends}
</div>
</div>
</div>
<div className="flexCol">
<img src="match.png" />
<div className="flexCenter" style={{marginTop: "15px"}}>
<img onClick={this.newSeries.bind(this)} src={ flag ? addE : addD} />
</div>
</div>
</div>
)
}
}

72
Source/Players.js Normal file
View File

@@ -0,0 +1,72 @@
import React from "react";
import Store from "./interface/Store";
export default class Players extends React.Component {
constructor() {
super();
this.state = { plylst: Store.getPlayers() };
this.getPlayers = this.getPlayers.bind(this)
this.color = ['grey', 'red', 'green', 'blue']
}
componentDidMount() {
Store.on("playlist", this.getPlayers)
}
componentWillUnmount() {
Store.removeListener("playlist", this.getPlayers)
}
getPlayers() {
this.setState({ plylst: Store.getPlayers() })
}
render() {
const p = this.props.players
const t = this.props.turn
const plyrs = this.state.plylst.map((d, i) => {
return (
<div className="flexRow">
<div style={{ height: "94px", width: "66px", marginRight: "3px" }}>
{t.length ? <img src={t[i].crd + '.svg'} style={{ height: "91px" }} /> : null}
</div>
<div>
<div className="player" style={{ marginBottom: "3px" }}>
{p.length && p[i].live === true
? <div className="flexCol flexSpace plyrImg" style={{ backgroundImage: "url(" + d.Img + ")" }}>
<div className="flexEnd">
<div className="plyrCoin" style={{ backgroundColor: this.color[d.coin] }}>
</div>
</div>
<div className="flexCenter" >
{t.length && t[i].req
? <img src="checkmark.png" style={{ width: "54px", height: "54px" }} />
: <div></div>
}
</div>
</div>
: <div className="plyrImg plyBlnk">
<div className="flexEnd">
<div className="plyrCoin" style={{ backgroundColor: "#d0d0d0" }}>
</div>
</div>
<div>
</div>
</div>
}
<div className="flexCol flexCenter plyrName"
style={{ backgroundColor: t.length && t[i].turn === true ? '#FFFF66' : 'white' }} >
{d.Desc}
</div>
</div>
</div>
</div>
)
})
return (
<div className="flexCol" style={{ margin: "10px 5px" }}>
{plyrs}
</div>
);
}
}

39
Source/SeriesStats.js Normal file
View File

@@ -0,0 +1,39 @@
import React from "react";
import Store from "./interface/Store";
export default class SeriesStats extends React.Component {
constructor() {
super()
this.state = { stats: Store.getSeriesStats() }
this.getStats = this.getStats.bind(this)
this.color = ['#d0d0d0', '#ff3333', '#32cd32', '#4F94CD']
}
componentDidMount() {
Store.on("serstats", this.getStats)
}
componentWillUnmount() {
Store.removeListener("serstats", this.getStats)
}
getStats() {
this.setState({ stats: Store.getSeriesStats() })
}
render() {
return (
<div className="flexRow">
<div style={{ marginRight: "2px" }}>played</div>
<div className="flexRow flexAlign" style={{ borderTop: "1px solid grey", borderRight: "1px solid grey", borderBottom: "1px solid grey" }}>
<div style={{ minWidth: "25px", marginRight: "2px", height: "17px", backgroundColor: this.color[0] }}>
{this.state.stats.plyd}
</div>
<div style={{ color: "white", minWidth: "22px", height: "16px", backgroundColor: this.color[1] }}>{this.state.stats.c1}</div>
<div style={{ color: "white", minWidth: "22px", height: "16px", backgroundColor: this.color[2] }}>{this.state.stats.c2}</div>
<div style={{ color: "white", minWidth: "22px", height: "16px", backgroundColor: this.color[3], marginRight: "2px" }}>{this.state.stats.c3}</div>
</div>
</div>
);
}
}

74
Source/SignIn.js Normal file
View File

@@ -0,0 +1,74 @@
import React from "react";
import * as Act from "./interface/Actions";
import usrsvg from "./images/loginuser.svg";
import pswsvg from "./images/loginpas.svg";
export default class SignIn extends React.Component {
constructor() {
super();
this.state = { u: '', p: '', p1: '', p2: '' };
}
handleKeyPress(e) {
if (e.key === 'Enter') {
this.handleLogin();
}
}
handleLogin() {
Act.apiCall('login', 'ACCTKN', {
a: this.props.a, usr: this.state.u, psw: this.state.p,
p1: this.state.p1, p2: this.state.p2
});
}
render() {
return (
<div style={{ display: "flex", justifyContent: "center" }} >
<div className="flexCol " style={{
justifyContent: "center", alignItems: "center",
marginTop: "50px", width: "300px", height: "300px",
backgroundImage: 'url("signin.png")', backgroundSize: "100%"
}}>
{this.props.a !== -1
? <div>
<div className="LognInp">
<span><img src={usrsvg} /></span>
<input type="text" name="u" placeholder="Username" required="required"
value={this.state.u} onChange={(e) => { this.setState({ u: e.target.value }) }} />
</div>
<div className="LognInp">
<span><img src={pswsvg} /></span>
<input type="password" name="p1" placeholder="Password" required="required"
key={this.props.a} value={this.state.p}
onKeyPress={this.handleKeyPress.bind(this)}
onChange={(e) => { this.setState({ p: e.target.value }) }} />
</div>
</div>
:
<div>
<div className="LognInp">
<span><img src={pswsvg} /></span>
<input type="password" name="p1" placeholder="New Password" required="required"
value={this.state.p1} onChange={(e) => { this.setState({ p1: e.target.value }) }} />
</div>
<div className="LognInp">
<span><img src={pswsvg} /></span>
<input type="password" name="p2" placeholder="Reenter Password" required="required"
value={this.state.p2}
onChange={(e) => { this.setState({ p2: e.target.value }) }} />
</div>
</div>
}
<div className="LognInp" style={{ justifyContent: "flex-end" }}>
<input type="submit" value="enter"
onClick={this.handleLogin.bind(this)} style={{ width: "145px" }} />
</div>
<div style={{ fontFamily: "Roboto", fontSize: "13px", color: "#ff073a" }}>
{this.props.msg}
</div>
</div>
</div>
);
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Source/Style/blank.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

303
Source/Style/style.css Normal file
View File

@@ -0,0 +1,303 @@
html {
height: 100%;
}
body {
min-height: 100%;
}
@font-face {
font-family: 'Roboto';
src: url('../../Source/Style/Roboto-Regular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Lato';
src: url('../../Source/Style/Lato-Regular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'OpenSans';
src: url('../../Source/Style/OpenSansCondensed-Light.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Economica';
src: url('../../Source/Style/Economica-Bold.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@keyframes pulse {
0% {
transform: scale(1, 1);
}
50% {
transform: scale(1.1, 1.1);
}
100% {
transform: scale(1, 1);
}
}
.pulse {
animation: pulse 1s linear infinite;
}
@keyframes blink {
50% {
border-color: #ff0000;
}
}
.flash { animation: blink .5s step-end infinite alternate;
}
#board {
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
width: 562px;
line-height: 0;
padding: 2px;
border: solid 3px #4169e1;
border-radius: 5px;
}
#board .card {
height: 80px;
width: 56px;
}
#board .card svg {
pointer-events: none;
}
.flexRow {
display: flex;
flex-direction: row;
}
.flexCol {
display: flex;
flex-direction: column;
}
.flexCenter {
display: flex;
justify-content: center;
}
.flexAlign {
align-items: center;
}
.flexPad {
display: flex;
justify-content: space-around;
}
.flexSpace {
display: flex;
justify-content: space-between;
}
.flexEnd {
display: flex;
justify-content: flex-end;
}
.player {
width: 75px;
height: 90px;
border: 1px solid grey;
border-radius: 2px 2px 0 0;
}
.player .plyrImg {
height: 75px;
background-size: 100%;
}
.player .plyrCoin {
width: 18px;
height: 18px;
margin: 1px;
border-radius: 8px;
}
#dealt .card {
height: 94px;
width: 66px;
margin: 1px;
}
.dealt {
width: 125px;
display: flex;
flex-direction: row;
align-items: center;
padding: 1px;
}
.stats {
width: 125px;
}
.stats .card {
height: 70px;
width: 50px;
}
.coin {
width: 32px;
height: 32px;
border-radius: 18px;
background-clip: content-box;
margin: 0 10px 0 6px;
}
.msg {
text-align: center;
font-family: Roboto;
font-size: 14px;
border: solid 1px grey;
border-radius: 2px 2px 0 0;
margin: 2px 0 3px 0px;
}
.box {
border: 1px solid grey;
border-radius: 2px;
}
.boxHdr {
display: flex;
justify-content: center;
border-left: 1px solid grey;
border-top: 1px solid grey;
border-right: 1px solid grey;
border-radius: 2px 2px 0 0;
background-color: #F8F8F8;
font-family: Roboto;
font-size: 12px;
}
.boxTrl {
display: flex;
justify-content: center;
align-items: center;
border-left: 1px solid grey;
border-bottom: 1px solid grey;
border-right: 1px solid grey;
border-radius: 0 0 2px 2px;
background-color: #F8F8F8;
font-family: Roboto;
font-size: 12px;
}
.btn {
border: solid 1px grey;
border-radius: 4px;
font-family: Roboto;
font-size: 12px;
margin: 3px;
padding: 2px;
color: white;
background-color: #00B000;
display: block;
text-align: center;
}
.gameBox {
border-left: 1px solid grey;
border-right: 1px solid grey;
border-bottom: 1px solid grey;
}
.circle {
width: 14px;
height: 14px;
border-radius: 7px;
}
.cirsml {
width: 12px;
height: 12px;
border-radius: 6px;
}
.hilit {
background-color: yellow;
}
.plyrName {
width: 100%;
font-family: Economica;
font-size: 13px;
text-align: center;
display: inline-block;
overflow: hidden
}
.plyBlnk {
background-image: url(./blank.png);
}
.LognInp {
display: flex;
margin: 5px;
background-color: white;
padding: 1px;
}
.tabrow {
width: 100%;
text-align: center;
list-style: none;
margin: 0;
padding: 0;
line-height: 24px;
position: relative;
font-family: Roboto;
font-size: 13px;
}
.tabrow:after {
position: absolute;
content: "";
width: 100%;
bottom: 0;
left: 0;
border-bottom: 1px solid #AAA;
z-index: 1;
}
.tabrow:before {
z-index: 1;
}
.tabrow li {
width: 80px;
margin: 0 5px;
padding: 0 10px;
border: 1px solid #AAA;
border-radius: 3px 3px 0px 0px;
font-family: Lato;
background: #ECECEC;
display: inline-block;
position: relative;
z-index: 0;
}
.tabrow li.tabsel {
background: #FFF;
color: #000;
z-index: 2;
border-bottom-color: #FFF;
}

71
Source/WinStats.js Normal file
View File

@@ -0,0 +1,71 @@
import React from "react";
import Store from "./interface/Store";
export default class WinStats extends React.Component {
constructor() {
super()
this.state = { stats: Store.getGameStats(), hand: Store.getHand() }
this.getStats = this.getStats.bind(this)
this.color = ['charcoal', '#ff3333', '#32cd32', '#4F94CD']
}
componentDidMount() {
Store.on("gmestats", this.getStats)
}
componentWillUnmount() {
Store.removeListener("gmestats", this.getStats)
}
getStats() {
console.log('gmestats')
this.setState({ stats: Store.getGameStats(), hand: Store.getHand() })
}
newGame() {
// Act.apiCall('service', 'NEWGME', {match: this.props.game});
this.props.newgame()
}
render() {
const msg = this.state.stats.length > 0
? this.state.stats.map((m) => {
return <div className="flexRow flexSpace" style={{ color: this.color[m.clr] }}>
<div>{m.tag}</div>
<div>{m.msg}</div>
</div>
})
: <div></div>
const crds = this.state.hand.length > 0
? this.state.hand.map((c) => {
return <img src={c.crd + '.svg'} className="card" />
})
: <div></div>
return (
<div className="stats">
<div className="btn" onClick={this.newGame.bind(this)}>
play another game
</div>
<div className="flexCol" style={{ margin: "0 0 5px 5px" }}>
<div className="boxHdr" >
Game statistics
</div>
<div style={{ border: "solid 1px gray", fontFamily: "OpenSans", fontSize: "15px" }}>
{msg}
</div>
</div>
<div className="flexRow flexCenter" style={{ margin: "1px" }}>
<div className="flexCol">
{crds}
</div>
</div>
</div>
);
}
}

3
Source/dispatcher.js Normal file
View File

@@ -0,0 +1,3 @@
import { Dispatcher } from "flux";
export default new Dispatcher;

View File

@@ -0,0 +1,33 @@
import dispatcher from "../dispatcher";
import api from "axios";
var jsonReq = {
action: "?",
filters: []
};
export function logout() {
dispatcher.dispatch({ type: 'LOGOUT', data: { auth: 0, msg: 'logged out', token: '' } });
}
export function apiCall(route, action, filters) {
jsonReq.action = action;
jsonReq.filters = filters;
api.post('/api/' + route, jsonReq)
.then(function (resp) {
dispatcher.dispatch({
type: action,
data: resp.data
});
})
.catch(function (error) {
dispatcher.dispatch({
type: "API_ERR",
data: error
});
});
}
export function localCall(action, data) {
dispatcher.dispatch({ type: action, data: data });
}

141
Source/interface/Store.js Normal file
View File

@@ -0,0 +1,141 @@
import { EventEmitter } from "events";
import dispatcher from "../dispatcher";
class Store extends EventEmitter {
constructor() {
super()
this.access = { status: -91, msg: '' }
this.users = []
this.groups = []
this.friends = []
this.series = []
this.gmestats = []
this.serstats = {}
this.hand = []
this.msg = 'welcome'
this.players = []
this.color = ['grey', 'red', 'green', 'blue']
}
getAccess() { return this.access }
getStatus() { return this.access.status }
getGroups() { return this.groups }
getFriends() { return this.friends }
getUsers() { return this.users }
getSeries() { return this.series }
getGameStats() { return this.gmestats }
getSeriesStats() { return this.serstats }
getHand() { return this.hand }
getPlayers() { return this.players }
getMsg() { return this.msg }
getToken() { return this.access.token }
getCoin() { return this.color[this.coin] }
updAccToken(data) {
this.access = data
this.emit("authChange")
}
updGroups(data) {
this.groups = data.groups || []
this.emit("groups")
}
updUsers(data) {
this.users = data.users || []
this.emit("users")
}
updFriends(data) {
this.friends = data.friends || []
this.emit("friends")
}
updGameStats(data) {
this.gmestats = data.gmestats || []
this.hand = data.crds || []
this.emit("gmestats")
}
updSeries(data) {
this.series = data.series || []
this.emit("series")
}
updSeriesStats(data) {
this.serstats = data.serstats || {}
this.emit("serstats")
}
updPlayers(data) {
this.players = data.players || []
this.emit("playlist")
}
setError(error) {
this.access = { auth: -5, msg: error, token: '' }
}
handleActions(action) {
switch (action.type) {
case "ACCTKN": {
this.updAccToken(action.data)
this.updSeries(action.data)
this.updGroups(action.data)
break
}
case "GRPNEW":
case "GRPDLT": {
this.updGroups(action.data)
break;
}
case "GRFRND": {
this.updFriends(action.data)
break
}
case "USRLST": {
this.updUsers(action.data)
break
}
case "NEWSER": {
this.updSeries(action.data)
break
}
//case "NEWGAM": {
// this.updPlayers(action.data)
// break
//}
case "SERDLT":
case "SERRFH": {
this.updSeries(action.data)
break
}
case "LOGOUT": {
this.updAccToken(action.data)
break
}
case "GMSTAT": {
this.updSeriesStats(action.data)
this.updGameStats(action.data)
break
}
case "SRSTAT": {
this.updSeriesStats(action.data)
break
}
case "JOINGM": {
this.updPlayers(action.data)
this.updSeriesStats(action.data)
break
}
case "API_ERR": {
this.setError(action.error)
break
}
}
}
}
const store = new Store()
dispatcher.register(store.handleActions.bind(store))
export default store