/// import * as ko from "knockout"; import * as moment from "moment"; import { Game } from "../api/game"; import { TournamentBetStructure, TournamentPrizeStructure } from "../api/information"; import { Tournament, TournamentDefinition, TournamentPlayerDefinition, TournamentPlayerStatus, TournamentTableDefinition } from "../api/tournament"; import { App } from "../app"; import * as authManager from "../authmanager"; import { debugSettings } from "../debugsettings"; import { _ } from "../languagemanager"; import * as metadataManager from "../metadatamanager"; import { SimplePopup } from "../popups/simplepopup"; import { reloadManager } from "../services"; import { AccountManager } from "../services/accountManager"; import { tableManager } from "../table/tablemanager"; import * as timeService from "../timeservice"; import { PageBase } from "../ui/pagebase"; declare var apiHost: string; declare var host: string; declare var app: App; interface TournamentTablePlayerView { id: number; login: string; } interface TournamentTableListView { id: number; name: string; isClosed: boolean; selected: KnockoutObservable; players: TournamentTablePlayerView[]; } export class TournamentLobbyPage extends PageBase { public loading: KnockoutObservable; public tournamentId = 0; public tournamentData: KnockoutObservable; public tournamentCaption: KnockoutComputed; public authenticated: KnockoutComputed; public tablesAvailable: KnockoutComputed; public lateRegistrationAllowed: KnockoutComputed; public stackInformation: KnockoutComputed; public participantsInformation: KnockoutComputed; public betLevelInformation: KnockoutComputed; public parentView: string; public currentView: KnockoutObservable; public getBetStructure: KnockoutComputed; public playersSortOrder = ko.observable("asc"); public playersColumnOrder = ko.observable("Login"); public getTournamentPlayers: KnockoutComputed; public tablesData: KnockoutObservable; public getSelectedTablePlayers: KnockoutComputed; public couldRegister: KnockoutComputed; public couldUnregister: KnockoutComputed; public couldContinueGame: KnockoutComputed; public couldView: KnockoutComputed; public currentTime: KnockoutComputed; public duration: KnockoutComputed; public lateRegistrationLeft: KnockoutComputed; public lateRegistrationRunning: KnockoutComputed; public totalPrize: KnockoutComputed; public prizesCount: KnockoutComputed; public scrollTrigger: KnockoutComputed; constructor() { super(); const self = this; this.tournamentData = ko.observable(null); this.tablesData = ko.observableArray([]); this.tournamentData.subscribe(function(data) { if (data == null) { self.tablesData([]); return; } const result = []; for (let i = 0; i < data.TournamentTables.length; i++) { const table = data.TournamentTables[i]; const tableModel: TournamentTableListView = { id: table.TableId, name: table.TableName, isClosed: table.IsClosed, selected: ko.observable(false), players: [], }; if (!table.IsClosed) { table.Players.forEach(function(item) { tableModel.players.push({ id: item.PlayerId, login: item.PlayerName }); }); } result.push(tableModel); } if (result.length > 0) { result[0].selected(true); } self.tablesData(result); }); this.loading = ko.observable(true); this.currentView = ko.observable(1); authManager.authenticated.subscribe(function(newValue) { self.refreshTournament(); }); this.lateRegistrationAllowed = ko.computed(function() { const data = self.tournamentData(); if (data === null) { return false; } return data.RegistrationEndDate > data.StartDate; }, this); this.totalPrize = ko.computed(function() { const tdata = self.tournamentData(); if (tdata === null) { return null; } return tdata.PrizeAmount + (tdata.CollectedPrizeAmount || 0); }, this); this.stackInformation = ko.computed(function() { const data = self.tournamentData(); if (data === null) { return null; } const activePlayers = data.TournamentPlayers.filter(function(item) { return item.Status === 2; }); let maxStack = 0; let minStack = 10000000000; let stackSum = 0; activePlayers.forEach(function(item) { const stack = item.Stack === null ? 0 : item.Stack; if (stack > maxStack) { maxStack = stack; } if (stack < minStack) { minStack = stack; } stackSum += stackSum; }); const avgStack = stackSum / activePlayers.length; const result = _("tournamentLobby.stackInformation") .replace("#max", maxStack.toString()) .replace("#min", minStack.toString()) .replace("#avg", avgStack.toFixed(0)); return result; }, this); this.participantsInformation = ko.computed(function() { const data = self.tournamentData(); if (data == null) { return null; } const activePlayers = data.TournamentPlayers.filter(function(item) { return item.Status === 2; }); return _("tournamentLobby.participantsInformation") .replace("#joined", data.JoinedPlayers.toString()) .replace("#tablesCount", data.TournamentTables.length.toString()) .replace("#playersCount", activePlayers.length.toString()); }, this); this.betLevelInformation = ko.computed(function() { const data = self.tournamentData(); if (data == null) { return null; } if (data.BetLevel == null) { return null; } const betStructure = metadataManager.bets[data.WellKnownBetStructure] .sort((a, b) => a.Level - b.Level); // Values in the betStructure is starting from 1, be caution. // Different methods could produce different indexation. const betLevel = betStructure.length < data.BetLevel ? betStructure.length : data.BetLevel; const currentBets = betStructure.filter((bs) => bs.Level === betLevel)[0]; if (currentBets.Ante !== null && currentBets.Ante !== 0) { return _("tournamentLobby.betStructureWithAnte") .replace("#sb", currentBets.SmallBlind.toString()) .replace("#bb", currentBets.BigBlind.toString()) .replace("#ante", currentBets.Ante.toString()); } return _("tournamentLobby.betStructureWithoutAnte") .replace("#sb", currentBets.SmallBlind.toString()) .replace("#bb", currentBets.BigBlind.toString()); }, this); this.getBetStructure = ko.computed(function() { const data = self.tournamentData(); if (data == null) { return []; } const prizeStructure = metadataManager.bets[data.WellKnownBetStructure]; const sortedPrizes = prizeStructure.sort(function(a, b) { return a.Level > b.Level ? 1 : (a.Level < b.Level ? -1 : 0); }); return sortedPrizes; }, this); this.getTournamentPlayers = ko.computed(() => { const tdata = self.tournamentData(); if (tdata === null || tdata === undefined) { return null; } let players = tdata.TournamentPlayers; const modifier = self.playersSortOrder() === "asc" ? 1 : -1; const sortByName = (a: TournamentPlayerDefinition, b: TournamentPlayerDefinition) => { return a.PlayerName > b.PlayerName ? modifier : a.PlayerName < b.PlayerName ? -modifier : 0; }; const sortByPlace = (a: TournamentPlayerDefinition, b: TournamentPlayerDefinition) => { const aPrize = a.Prize === null ? 0 : a.Prize; const bPrize = b.Prize === null ? 0 : b.Prize; return aPrize > bPrize ? modifier : aPrize < bPrize ? -modifier : sortByName(a, b); }; const sortByStack = (a: TournamentPlayerDefinition, b: TournamentPlayerDefinition) => { if (a.Prize === null && b.Prize === null) { const aStack = a.Stack === null ? 0 : a.Stack; const bStack = b.Stack === null ? 0 : b.Stack; return aStack > bStack ? modifier : aStack < bStack ? -modifier : -sortByPlace(a, b); } else { if (a.Prize === null) { return modifier; } if (b.Prize === null) { return -modifier; } return -sortByPlace(a, b); } }; if (self.playersColumnOrder() === "Login") { players = players.sort(sortByName); } if (self.playersColumnOrder() === "Stack") { players = players.sort(sortByStack); } if (self.playersColumnOrder() === "Prize") { players = players.sort(sortByPlace); } return players; }, this); this.authenticated = ko.computed(function() { const value = authManager.authenticated(); return value; }, this); this.tablesAvailable = ko.computed(function() { const data = self.tournamentData(); return data != null && data.TournamentTables != null && data.TournamentTables.length > 0; }, this); this.tournamentCaption = ko.computed(function() { const data = self.tournamentData(); if (data === null) { return; } if (debugSettings.lobby.useTournamentNameForTournamentCaption) { return data.TournamentName; } return _("tournamentLobby.caption") .replace("#number", data.TournamentId.toString()); }, this); this.getSelectedTablePlayers = ko.computed(function() { const selectedTables = self.tablesData().filter(function(item) { return item.selected(); }); if (selectedTables.length < 1) { return []; } return selectedTables[0].players; }, this); this.couldRegister = ko.computed(function() { if (!self.authenticated()) { return false; } const tdata = self.tournamentData(); if (tdata === null) { return false; } return !tdata.IsRegistered && (tdata.Status === TournamentStatus.RegistrationStarted || tdata.Status === TournamentStatus.LateRegistration); }, this); this.couldUnregister = ko.computed(function() { if (!self.authenticated()) { return false; } const tdata = self.tournamentData(); if (tdata === null) { return false; } return tdata.IsRegistered && tdata.Status === TournamentStatus.RegistrationStarted; }, this); this.couldContinueGame = ko.computed(function() { if (!self.authenticated()) { return false; } const tdata = self.tournamentData(); if (tdata === null) { return false; } const tplayer = tdata.TournamentPlayers.filter(function(item) { return item.PlayerId === authManager.loginId(); }); if (tplayer.length === 0 || tplayer[0].Status !== TournamentPlayerStatus.Playing) { return false; } return tdata.Status === TournamentStatus.LateRegistration || tdata.Status === TournamentStatus.Started; }, this); this.couldView = ko.computed(function() { if (!self.authenticated()) { return false; } const tdata = self.tournamentData(); if (tdata === null) { return false; } const tplayer = tdata.TournamentPlayers.filter(function(item) { return item.PlayerId === authManager.loginId(); }); if (tplayer.length === 0) { return tdata.Status === TournamentStatus.Started; } if (tplayer[0].Status !== TournamentPlayerStatus.Playing) { return tdata.Status === TournamentStatus.LateRegistration || tdata.Status === TournamentStatus.Started; } return false; }, this); this.currentTime = ko.pureComputed(function() { return timeService.currentTime(); }, this); this.duration = ko.pureComputed(function() { const tdata = self.tournamentData(); if (tdata == null) { return ""; } const startDate = moment(tdata.StartDate); const currentMoment = moment().add(timeService.timeDiff, "ms"); const duration = moment.duration(currentMoment.diff(startDate)); const m = duration.minutes(); return duration.hours() + _("common.hours") + _("common.timeseparator") + (m < 10 ? "0" + m : "" + m) + _("common.minutes"); }, this); this.lateRegistrationLeft = ko.pureComputed(function() { const tdata = self.tournamentData(); if (tdata == null) { return ""; } const registrationEndDate = moment(tdata.RegistrationEndDate); const currentMoment = moment().add(timeService.timeDiff, "ms"); const duration = moment.duration(currentMoment.diff(registrationEndDate)); const m = duration.minutes(); return duration.hours() + _("common.hours") + _("common.timeseparator") + (m < 10 ? "0" + m : "" + m) + _("common.minutes"); }, this); this.lateRegistrationRunning = ko.pureComputed(function() { const tdata = self.tournamentData(); if (tdata == null) { return false; } const registrationEndDate = moment(tdata.RegistrationEndDate); const currentMoment = moment().add(timeService.timeDiff, "ms"); if (currentMoment.diff(registrationEndDate)) { return false; } return true; }, this); this.prizesCount = ko.computed(function() { const data = self.tournamentData(); if (data === null) { return null; } const currentPlayers = data.JoinedPlayers; const prizeStructure = metadataManager.prizes[data.WellKnownPrizeStructure]; const sortedPrizes = prizeStructure.sort(function(a, b) { return a.MaxPlayer > b.MaxPlayer ? 1 : (a.MaxPlayer < b.MaxPlayer ? -1 : 0); }); const filteredPrizes = sortedPrizes.filter(function(a) { return a.MaxPlayer >= currentPlayers; }); let currentPrize: TournamentPrizeStructure; if (filteredPrizes.length === 0) { currentPrize = sortedPrizes[0]; } else { currentPrize = filteredPrizes[0]; } return currentPrize.PrizeLevel.length; }, this); let scrollTriggerCounter = 0; this.scrollTrigger = ko.computed(function() { self.loading(); self.currentView(); return scrollTriggerCounter++; }, this); } public deactivate() { super.deactivate(); if (PageBlock.useDoubleView) { app.lobbyPageBlock.lobbyPage.filterLocked(false); } } public activate() { super.activate(); this.refreshTournament(); if (PageBlock.useDoubleView) { app.lobbyPageBlock.lobbyPage.filterLocked(true); } reloadManager.setReloadCallback(() => this.refreshTournament()); } public selectTable(table: TournamentTableListView) { const tables = this.tablesData(); tables.forEach(function(item) { item.selected(false); }); table.selected(true); this.tablesData(tables); } public setTournament(tournamentId: number, parentView: string): void { this.tournamentId = tournamentId || 0; this.parentView = parentView; } public async refreshTournament(): Promise> { if (this.tournamentId === 0) { return Promise.resolve({ Status: "Ok", Data: null }); } const self = this; const tournamentApi = new Tournament(host); this.loading(true); try { const data = await tournamentApi.getTournament(this.tournamentId); if (data.Status === "Ok") { const tournamentData = data.Data; self.log("Informaton about tournament ", self.tournamentId, " received: ", data.Data); self.log(tournamentData.TournamentName); self.tournamentData(tournamentData); if (tournamentData.Status === TournamentStatus.Started || tournamentData.Status === TournamentStatus.LateRegistration) { // Connect to the tournament so player could monitor game on it tableManager.selectTournament(tournamentData, true); } if (tournamentData.Status === TournamentStatus.Started || tournamentData.Status === TournamentStatus.LateRegistration) { self.setDefaultSortOrder("Stack"); } else if (tournamentData.Status === TournamentStatus.Completed) { self.setDefaultSortOrder("Prize"); } else { self.setDefaultSortOrder("Login"); } } self.loading(false); } catch (e) { self.log("Failed to get information about tournament " + self.tournamentId); self.loading(false); } } public back() { app.lobbyPageBlock.showSecondary(this.parentView); } public async register() { const self = this; const manager = new AccountManager(); self.loading(true); try { const data = await manager.getAccount(); self.loading(false); if (data.Status === "Ok") { const personalAccountData = data.Data; let balance = 0; const tournament = self.tournamentData(); if (tournament.CurrencyId === 1) { balance = personalAccountData.RealMoney; } else { balance = personalAccountData.GameMoney; } await self.promptRegister(balance); } else { SimplePopup.display(_("tournamentLobby.registrationSuccess"), _("errors." + data.Status)); } } catch (e) { self.loading(false); SimplePopup.display(_("tournamentLobby.registrationSuccess"), _("tournamentLobby.registrationError")); } } /** * Initiates cancelling registration in the tournament. */ public async unregister() { const self = this; const tournament = self.tournamentData(); const name = tournament.TournamentName; const operationTitle = _("tournamentLobby.registrationCancelled"); await app.promptAsync( _("tournamentLobby.tournamentRegistrationCancelPromptCaption"), [_("tournamentLobby.tournamentRegistrationCancelPrompt").replace("#name", name)]); self.loading(true); const tournamentApi = new Tournament(host); try { const data = await tournamentApi.cancelRegistration(self.tournamentId); self.loading(false); if (data.Status === "Ok") { self.log("Registration cancelled"); tableManager.removeTournamentById(self.tournamentId); SimplePopup.display(operationTitle, _("tournamentLobby.registrationCancelledCompleteSuccess")); try { await self.refreshTournament(); } catch (e) { SimplePopup.display(operationTitle, _("tournamentLobby.registrationCancelError")); } } else { SimplePopup.display(operationTitle, _("errors." + data.Status)); } } catch (e) { SimplePopup.display(operationTitle, _("tournamentLobby.registrationCancelError")); } } public selectView(view: number) { this.currentView(view); } public viewGame() { const tablesView = 2; if (this.currentView() !== tablesView) { this.currentView(tablesView); } else { const selectedTable = this.tablesData().filter((tablePage) => { return tablePage.selected(); }); if (selectedTable.length !== 0) { const table = selectedTable[0]; if (!table.isClosed) { this.showTournamentTable(selectedTable[0].id); } } } } public continueGame() { const tournamentView = tableManager.getTournamentById(this.tournamentId); this.showTournamentTable(tournamentView.currentTableId); } public showTable(table: TournamentTableDefinition) { if (!this.couldView() && this.couldContinueGame()) { return; } this.showTournamentTable(table.TableId); } public applyPlayersSort(sortColumn: string) { if (this.playersColumnOrder() === sortColumn) { this.playersSortOrder(this.playersSortOrder() === "asc" ? "desc" : "asc"); return; } this.playersColumnOrder(sortColumn); switch (sortColumn) { case "Login": this.playersSortOrder("asc"); break; case "Stack": this.playersSortOrder("desc"); break; case "Prize": this.playersSortOrder("asc"); break; default: this.playersSortOrder("asc"); break; } } public setDefaultSortOrder(sortColumn: string) { this.playersColumnOrder(sortColumn); switch (sortColumn) { case "Login": this.playersSortOrder("asc"); break; case "Stack": this.playersSortOrder("desc"); break; case "Prize": this.playersSortOrder("asc"); break; default: this.playersSortOrder("asc"); break; } } /** * Shows already opened tournament table, or load new one if needed. * @param tableId Number Id of the tournament table to show. */ private async showTournamentTable(tableId: number) { const self = this; const tableView = tableManager.getTableById(tableId); if (tableView === null) { this.loading(true); const tdata = this.tournamentData(); const api = new Game(host); const currentTournamentTitle = _("tournamentLobby.caption", { number: tdata.TournamentId }); try { const data = await api.getTableById(tableId); if (data.Status === "Ok") { const tapi = new Tournament(host); try { const tournamentData = await tapi.getTournament(tdata.TournamentId); if (data.Status === "Ok") { self.loading(false); tableManager.selectTournament(tournamentData.Data, false); tableManager.selectTable(data.Data, true); const currentTable = tableManager.getTableById(tableId); if (currentTable != null) { currentTable.tournament(tableManager.getTournamentById(tdata.TournamentId)); app.showSubPage("tables"); } } else { self.loading(false); SimplePopup.display(currentTournamentTitle, _("errors." + data.Status)); } } catch (e) { self.loading(false); self.log("Could not get tournament information for tournament #" + tdata.TournamentId); SimplePopup.display(currentTournamentTitle, _("tournamentLobby.showTableError")); } } else { self.loading(false); SimplePopup.display(currentTournamentTitle, _("errors." + data.Status)); } } catch (e) { self.loading(false); self.log("Could not get table information for table #" + tableId); SimplePopup.display(currentTournamentTitle, _("tournamentLobby.showTableError")); } } else { tableManager.selectById(tableId); app.showSubPage("tables"); } } private async promptRegister(currentBalance: number) { const self = this; const tournament = self.tournamentData(); const name = tournament.TournamentName; const joinAmount = tournament.JoinFee + tournament.BuyIn; /* tslint:disable:no-string-literal */ const numericTextBinding = ko.bindingHandlers["numericText"] as any; /* tslint:enable:no-string-literal */ const joinAmountString = numericTextBinding.withCommas(joinAmount.toFixed(0)); if (currentBalance < joinAmount) { await SimplePopup.display( _("tournamentLobby.tournamentRegistrationPromptCaption"), _("tournamentLobby.insufficientFunds")); return; } const balanceString = numericTextBinding.withCommas(currentBalance.toFixed(0)); app.okcancelPopup.customStyle("popup-container-left"); await app.promptAsync( _("tournamentLobby.tournamentRegistrationPromptCaption"), [ _("tournamentLobby.tournamentRegistrationPrompt", { name }), _("tournamentLobby.tournamentRegistrationAmount").replace("#amount", joinAmountString), _("tournamentLobby.tournamentRegistrationPromptBalance").replace("#amount", balanceString), ]); self.loading(true); const tournamentApi = new Tournament(host); try { const data = await tournamentApi.register(self.tournamentId); self.loading(false); if (data.Status === "Ok") { tableManager.openTournamentById(self.tournamentId); self.log("Registration success"); SimplePopup.display( _("tournamentLobby.registrationSuccess"), _("tournamentLobby.registrationCompleteSuccess")); try { await self.refreshTournament(); } catch (e) { SimplePopup.display( _("tournamentLobby.registrationSuccess"), _("tournamentLobby.registrationError")); } } else { SimplePopup.display(_("tournamentLobby.registrationSuccess"), _("errors." + data.Status)); } } catch (e) { self.loading(false); SimplePopup.display(_("tournamentLobby.registrationSuccess"), _("tournamentLobby.registrationError")); } } private log(message: string, ...params: any[]) { if (debugSettings.lobby.trace) { console.log(message, params); } } }