Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v3.23.2 #197

Merged
merged 6 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 32 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
</button>

<button type="button" class="btn btn-info" tooltip="Deploy game resources"
(click)="handleDeployGameResources()">
[disabled]="!results?.teams?.items?.length" (click)="handleConfirmDeployGameResources()">
<fa-icon [icon]="fa.computer"></fa-icon>
</button>

Expand Down Expand Up @@ -63,7 +63,7 @@
</select>

<select class="form-control ml-2" [(ngModel)]="filterSettings.sessionStatus" (change)="load()">
<option [ngValue]="undefined" (change)="load()">[Any status]</option>
<option [ngValue]="undefined">[Any status]</option>
<option value="complete">Finished</option>
<option value="playing">Playing now</option>
<option value="notStarted">Not Started</option>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { ToastService } from '@/utility/services/toast.service';
import { AdminEnrollTeamModalComponent } from '../../admin-enroll-team-modal/admin-enroll-team-modal.component';
import { ManageManualChallengeBonusesModalComponent } from '../../manage-manual-challenge-bonuses-modal/manage-manual-challenge-bonuses-modal.component';
import { SimpleEntity } from '@/api/models';
import { GameCenterTeamsAdvancementFilter, GameCenterTeamSessionStatus, GameCenterTeamsResults, GameCenterTeamsSort } from '../game-center.models';
import { GameCenterTeamsAdvancementFilter, GameCenterTeamSessionStatus, GameCenterTeamsResults, GameCenterTeamsResultsTeam, GameCenterTeamsSort } from '../game-center.models';
import { UnsubscriberService } from '@/services/unsubscriber.service';
import { unique } from '@/../tools/tools';
import { ScoreboardTeamDetailModalComponent } from '@/scoreboard/components/scoreboard-team-detail-modal/scoreboard-team-detail-modal.component';
Expand Down Expand Up @@ -90,49 +90,61 @@ export class GameCenterTeamsComponent implements OnInit {
await this.load(this.game?.id);
}

protected async handleDeployGameResources() {
if (!this.results || !this.gameId)
protected async handleConfirmDeployGameResources() {
const teams = this.resolveSelectedTeams();
const nowish = this.nowService.nowToMsEpoch();

if (!teams.length) {
this.modalService.openConfirm({
title: "No eligible teams",
bodyContent: "There are no teams eligible for deployment."
});

return;
}

const teamIds = this.selectedTeamIds.length ? this.selectedTeamIds : this.results.teams.items.map(t => t.id);
const invalidTeamNames: string[] = [];
const validTeamIds: string[] = [];
const eligibleTeams: GameCenterTeamsResultsTeam[] = [];
const ineligibleTeams: GameCenterTeamsResultsTeam[] = [];

const nowish = this.nowService.nowToMsEpoch();
for (const team of this.results.teams.items) {
if (this.selectedTeamIds.length && this.selectedTeamIds.indexOf(team.id) < 0)
continue;

if (team.session.end && team.session.end < nowish)
invalidTeamNames.push(team.name);
else
validTeamIds.push(team.id);
for (const team of teams) {
if (team.session.end && team.session.end < nowish) {
ineligibleTeams.push(team);
}
else {
eligibleTeams.push(team);
}
}

if (!validTeamIds.length) {
if (ineligibleTeams.length) {
this.modalService.openConfirm({
bodyContent: "All selected teams have finished their sessions, so no resources can be deployed for them.",
hideCancel: true,
title: "All teams finished",
subtitle: this.game?.name
title: "Ineligible teams",
subtitle: this.game?.name,
bodyContent: "Some teams are ineligible to have resources deployed because they've already finished their sessions. Unselect them to proceed.\n\n" + ineligibleTeams
.map(t => ` - ${t.name || "_(no name)_"}`)
.join('\n\n'),
renderBodyAsMarkdown: true
});

return;
}

let appendInvalidTeamsClause = "";
if (invalidTeamNames.length) {
appendInvalidTeamsClause = `\n\nSessions for some teams have ended, so their resources won't be deployed:\n\n${invalidTeamNames.map(tId => `- ${tId}\n`)}`;
}
await this.handleDeployGameResources();
}

private async handleDeployGameResources() {
const teams = this.resolveSelectedTeams();

// let appendInvalidTeamsClause = "";
// if (invalidTeamNames.length) {
// appendInvalidTeamsClause = `\n\nSessions for some teams have ended, so their resources won't be deployed:\n\n${invalidTeamNames.map(tId => `- ${tId}\n`)}`;
// }

this.modalService.openConfirm({
bodyContent: `Are you sure you want to deploy resources for ${validTeamIds.length} teams?${appendInvalidTeamsClause}`,
// bodyContent: `Are you sure you want to deploy resources for ${validTeamIds.length} teams?${appendInvalidTeamsClause}`,
bodyContent: `Are you sure you want to deploy resources for ${teams.length} teams?`,
onConfirm: async () => {
if (!validTeamIds.length)
return;

await this.gameService.deployResources(this.gameId!, validTeamIds);
this.toastService.showMessage(`Deploying resources for **${validTeamIds.length} ${this.game?.isTeamGame ? "team" : "player"}(s)**.`);
await this.gameService.deployResources(this.gameId!, teams.map(t => t.id));
this.toastService.showMessage(`Deploying resources for **${teams.length} ${this.game?.isTeamGame ? "team" : "player"}(s)**.`);
this.selectedTeamIds = [];
},
renderBodyAsMarkdown: true,
Expand Down Expand Up @@ -260,20 +272,30 @@ export class GameCenterTeamsComponent implements OnInit {
}

protected async load(gameId?: string) {
if (!gameId) {
return;
}

if (!this.game || this.game.id != gameId) {
if (gameId && this.game?.id !== gameId) {
this.game = await firstValueFrom(this.gameService.retrieve(gameId));
}

if (!gameId && !this.game?.id)
return;

gameId = this.game?.id;
this.gameId = gameId;

this.isLoading = true;
this.results = await this.adminService.getGameCenterTeams(gameId, this.filterSettings);
this.results = await this.adminService.getGameCenterTeams(gameId!, this.filterSettings);
this.isLoading = false;
this.updateFilterConfig();
}

private resolveSelectedTeams() {
if (!this.results)
return [];

const hasSelection = this.selectedTeamIds.length;
return this.results.teams.items.filter(t => !hasSelection || this.selectedTeamIds.indexOf(t.id) >= 0);
}

private updateFilterConfig() {
this.localStorageClient.add(StorageKey.GameCenterTeamsFilterSettings, this.filterSettings);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
import { BehaviorSubject, combineLatest, Observable, of, timer } from 'rxjs';
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { BehaviorSubject, combineLatest, firstValueFrom, Observable, of, timer } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { calculateCountdown, TimeWindow } from '../../../api/player-models';
import { fa } from '@/services/font-awesome.service';
import { HubState, NotificationService } from '../../../services/notification.service';
import { ChallengesService } from '@/api/challenges.service';
import { BoardService } from '@/api/board.service';

export interface GameboardPerformanceSummaryViewModel {
interface GameboardPerformanceSummaryViewModel {
player: {
id: string;
teamId: string;
Expand All @@ -25,28 +27,55 @@ export interface GameboardPerformanceSummaryViewModel {
styleUrls: ['./gameboard-performance-summary.component.scss']
})
export class GameboardPerformanceSummaryComponent implements OnInit, OnChanges {
@Input() ctx?: GameboardPerformanceSummaryViewModel;
@Output() onRefreshRequest = new EventEmitter<string>();
@Input() playerId?: string;

countdown$?: Observable<number | undefined>;
protected ctx?: GameboardPerformanceSummaryViewModel;
isCountdownOver = false;
hubState$: BehaviorSubject<HubState>;
protected fa = fa;

constructor(
hubService: NotificationService) {
challengesService: ChallengesService,
hubService: NotificationService,
private boardService: BoardService) {
challengesService.challengeGraded$.subscribe(async c => {
if (this.ctx && c.teamId === this.ctx.player.teamId) {
await this.load();
}
});
this.hubState$ = hubService.state$;
}

ngOnInit(): void {
this.updateCountdown();
this.load();
}

ngOnChanges(changes: SimpleChanges): void {
this.updateCountdown();
this.load();
}

private updateCountdown() {
private async load() {
if (!this.playerId)
return;

const boardInfo = await firstValueFrom(this.boardService.load(this.playerId));
this.ctx = {
player: {
id: this.playerId,
session: boardInfo.session,
teamId: boardInfo.teamId,
scoring: {
partialCount: boardInfo.partialCount,
correctCount: boardInfo.correctCount,
rank: boardInfo.rank,
score: boardInfo.score,

}
}
};

// update the countdown
this.countdown$ = combineLatest([
timer(0, 1000),
of(this.ctx)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ export class GameChallengeSpecQueryModel {
const params: Params = cloneNonNullAndDefinedProperties(model);

this.routerService.updateQueryParams({
parameters: params
parameters: params,
resetParams: [this.challengeSpecIdParamName, this.gameIdParamName]
});
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<div #timelineContainer></div>
<div *ngIf="timelineViewModel; else noTimeline" class="team-event-horizon-component pb-3">
<div class="text-center text-muted fs-08 mt-1">Click any timeline event to copy to your clipboard as markdown</div>
<div class="text-center text-muted fs-08 mt-1">
<strong>Pro tip:</strong>
Click any timeline event to copy to your clipboard as markdown
</div>
<div class="controls-container d-flex justify-content-center align-content-center mt-3">
<div class="btn-group btn-group-toggle" role="group" aria-label="Event type selection group"
data-toggle="buttons">
Expand Down
Loading
Loading