This commit is contained in:
Kirill Ivlev 2024-11-21 00:19:07 +04:00
parent 6dd57b4025
commit ab63f62d4e
25 changed files with 501 additions and 159 deletions

150
package-lock.json generated
View file

@ -24,7 +24,7 @@
"i": "^0.3.7",
"jquery": "^3.6.0",
"npm": "^10.2.3",
"rxjs": "~6.6.0",
"rxjs": "^7.8.1",
"socket.io-client": "^4.2.0",
"tslib": "^2.3.0",
"zone.js": "~0.13.3"
@ -71,15 +71,6 @@
"yarn": ">= 1.13.0"
}
},
"node_modules/@angular-devkit/architect/node_modules/rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
"dev": true,
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/@angular-devkit/build-angular": {
"version": "16.2.9",
"resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-16.2.9.tgz",
@ -241,15 +232,6 @@
"vite": "^3.0.0 || ^4.0.0"
}
},
"node_modules/@angular-devkit/build-angular/node_modules/rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
"dev": true,
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/@angular-devkit/build-angular/node_modules/vite": {
"version": "4.4.7",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.7.tgz",
@ -324,15 +306,6 @@
"webpack-dev-server": "^4.0.0"
}
},
"node_modules/@angular-devkit/build-webpack/node_modules/rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
"dev": true,
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/@angular-devkit/core": {
"version": "16.2.9",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.2.9.tgz",
@ -360,15 +333,6 @@
}
}
},
"node_modules/@angular-devkit/core/node_modules/rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
"dev": true,
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/@angular-devkit/schematics": {
"version": "16.2.9",
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-16.2.9.tgz",
@ -387,15 +351,6 @@
"yarn": ">= 1.13.0"
}
},
"node_modules/@angular-devkit/schematics/node_modules/rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
"dev": true,
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/@angular/animations": {
"version": "16.2.12",
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-16.2.12.tgz",
@ -7331,15 +7286,6 @@
"node": ">=8"
}
},
"node_modules/inquirer/node_modules/rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
"dev": true,
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/inquirer/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@ -13424,21 +13370,14 @@
}
},
"node_modules/rxjs": {
"version": "6.6.7",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
"integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
"license": "Apache-2.0",
"dependencies": {
"tslib": "^1.9.0"
},
"engines": {
"npm": ">=2.0.0"
"tslib": "^2.1.0"
}
},
"node_modules/rxjs/node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@ -15627,17 +15566,6 @@
"requires": {
"@angular-devkit/core": "16.2.9",
"rxjs": "7.8.1"
},
"dependencies": {
"rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
"dev": true,
"requires": {
"tslib": "^2.1.0"
}
}
}
},
"@angular-devkit/build-angular": {
@ -15738,15 +15666,6 @@
"dev": true,
"requires": {}
},
"rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
"dev": true,
"requires": {
"tslib": "^2.1.0"
}
},
"vite": {
"version": "4.4.7",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.7.tgz",
@ -15769,17 +15688,6 @@
"requires": {
"@angular-devkit/architect": "0.1602.9",
"rxjs": "7.8.1"
},
"dependencies": {
"rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
"dev": true,
"requires": {
"tslib": "^2.1.0"
}
}
}
},
"@angular-devkit/core": {
@ -15794,17 +15702,6 @@
"picomatch": "2.3.1",
"rxjs": "7.8.1",
"source-map": "0.7.4"
},
"dependencies": {
"rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
"dev": true,
"requires": {
"tslib": "^2.1.0"
}
}
}
},
"@angular-devkit/schematics": {
@ -15818,17 +15715,6 @@
"magic-string": "0.30.1",
"ora": "5.4.1",
"rxjs": "7.8.1"
},
"dependencies": {
"rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
"dev": true,
"requires": {
"tslib": "^2.1.0"
}
}
}
},
"@angular/animations": {
@ -20871,15 +20757,6 @@
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
"rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
"dev": true,
"requires": {
"tslib": "^2.1.0"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@ -25090,18 +24967,11 @@
}
},
"rxjs": {
"version": "6.6.7",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
"integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
"requires": {
"tslib": "^1.9.0"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
"tslib": "^2.1.0"
}
},
"safe-buffer": {

View file

@ -26,7 +26,7 @@
"i": "^0.3.7",
"jquery": "^3.6.0",
"npm": "^10.2.3",
"rxjs": "~6.6.0",
"rxjs": "^7.8.1",
"socket.io-client": "^4.2.0",
"tslib": "^2.3.0",
"zone.js": "~0.13.3"

View file

@ -4,11 +4,12 @@
<button class="btn btn-danger" (click)="resetAllPlayersScore()">Reset score to 0</button>
<h4>Game</h4>
<button class="btn btn-danger" (click)="clearGameQueue()">Clear queue</button>
<button class="btn btn-danger" (click)="simulateEndGamePoints()">Simulate endgame points</button>
<h4>Versus</h4>
<button class="btn btn-danger" (click)="simulateVersus()">Begin versus</button>
<button class="btn btn-danger" (click)="resetAllVersusTasksAsIncompleted()">Mark all as uncompleted</button>
<button class="btn btn-danger" disabled>Stop versus</button>
<button class="btn btn-danger" disabled>Simulate endgame points</button>
</div>
<div class="game-testing m-2" *ngIf="prodMode">

View file

@ -49,4 +49,8 @@ export class AdminTestingComponent implements OnInit, OnDestroy {
clearGameQueue() {
this.testingApiService.clearGameQueue().pipe(takeUntil(this.destroyed$)).subscribe((r => console.log(r)));
}
simulateEndGamePoints() {
this.testingApiService.simulateEndGamePoints().pipe(takeUntil(this.destroyed$)).subscribe(r => console.log(r));
}
}

View file

@ -25,6 +25,7 @@ export class MainActionsComponent implements OnInit {
{ title: 'Registration', name: 'register'},
{ title: 'Onboarding', name: 'onboarding' },
{ title: 'Start quiz', name: 'quiz' },
{ title: 'Endgame Points', name: 'endgamepoints' },
{ title: 'End', name: 'finish' },
];

View file

@ -6,6 +6,7 @@ import { RegisterComponent } from "./views/register/register.component";
import { OnboardingComponent } from "./views/onboarding/onboarding.component";
import { InitialComponent } from './views/initial/initial.component';
import { FinishComponent } from './views/finish/finish.component';
import {EndgamepointsComponent} from "./views/endgamepoints/endgamepoints.component";
const routes: Routes = [
{ path: 'quiz', component: QuizComponent },
@ -14,6 +15,7 @@ const routes: Routes = [
{ path: 'onboarding', component: OnboardingComponent },
{ path: 'initial', component: InitialComponent },
{ path: 'finish', component: FinishComponent },
{ path: 'endgamepoints', component: EndgamepointsComponent },
{ path: 'admin', loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule)},
];

View file

@ -28,6 +28,7 @@ import { FinishComponent } from './views/finish/finish.component';
import { InitialComponent } from './views/initial/initial.component';
import { SkrepaComponent } from './components/skrepa/skrepa.component';
import { VersusComponent } from './components/versus/versus.component';
import { EndgamepointsComponent } from './views/endgamepoints/endgamepoints.component';
@NgModule({
declarations: [
@ -52,6 +53,7 @@ import { VersusComponent } from './components/versus/versus.component';
InitialComponent,
SkrepaComponent,
VersusComponent,
EndgamepointsComponent,
],
imports: [
BrowserModule,

View file

@ -31,18 +31,24 @@
<audio [src]="getAudio(screpaText,2)" autoplay></audio>
<app-skrepa [text]="screpaText"></app-skrepa>
</div>
</div>
<div class="col" *ngIf="action.type === gameQueueTypes.showresults">
<div *ngIf="results && (results.valid.length > 0 || results.invalid.length > 0)">
<h1>Результаты</h1>
<h2>Ответили правильно</h2>
<div *ngFor="let item of results.valid" class="justify-content-center">
<app-participant-item [participant]="participants[item.user]" [small]="true"></app-participant-item>
<div class="d-flex flex-row flex-wrap w-100 justify-content-center">
<h2 *ngIf="results.valid.length > 0">Ответили правильно</h2>
</div>
<h2>Не смогли</h2>
<div *ngFor="let item of results.invalid" class="justify-content-center">
<app-participant-item [participant]="participants[item.user]" [small]="true"></app-participant-item>
<div class="d-flex flex-row w-100 justify-content-center">
<div *ngFor="let item of results.valid">
<app-participant-item [participant]="participants[item.user]" [small]="true"></app-participant-item>
</div>
</div>
<div class="d-flex flex-row flex-wrap w-100 justify-content-center">
<h2 *ngIf="results.invalid.length > 0">Не смогли</h2>
</div>
<div class="d-flex flex-row w-100 justify-content-center">
<div *ngFor="let item of results.invalid">
<app-participant-item [participant]="participants[item.user]" [small]="true"></app-participant-item>
</div>
</div>
</div>
<div *ngIf="!results || (results.valid.length === 0 && results.invalid.length === 0)">

View file

@ -1,9 +1,9 @@
<div class="card rounded m-3 animate__animated" [ngClass]="{ 'small': small, 'shadow': shadow, 'transparent': transparent, 'banned': banned, 'animate__flipInY': small }">
<figure class="p-1">
<figure class="p-1" *ngIf="participant">
<img [src]="getImageUrl()" class="participant-photo img-fluid">
</figure>
<div class="card-title">
{{ participant?.name }}
{{ participant.name }}
</div>
<div class="content" *ngIf="!small">
<div class="card-subtitle">

View file

@ -17,7 +17,7 @@ export class QuestionComponent implements OnInit, OnDestroy {
private questionSubscription: Subscription;
countdownInterval:ReturnType<typeof setInterval>|null= null;
countdown = 0;
readonly countDownTimer =20;
readonly countDownTimer = 20;
constructor(private apiService:ApiService, private eventService: EventService, private voiceService: VoiceService) { }

View file

@ -21,4 +21,5 @@
</div>
</div>
</div>
</div>
<audio src="../../../assets/versus/cinematical-epic-loop-190906.mp3" autoplay loop [volume]="0.2"></audio>

View file

@ -13,7 +13,7 @@ import {QuestionresultsDto} from "../../types/questionresults.dto";
import {map} from "rxjs/operators";
import {VersusItem} from "../../types/versus-item";
export interface FeatureFlagStateDto {
export class FeatureFlagStateDto {
name: string;
state: boolean;
}
@ -34,6 +34,24 @@ export interface ConfigRecordDto {
value: string;
}
export interface EndgameResultsDto {
maxInvalidAnswers: {
id: number;
count: number;
name: string;
},
maxRewards: {
id: number;
count: number;
name: string;
},
maxPenalties: {
id: number;
count: number;
name: string;
}
}
@Injectable({
providedIn: 'root'
})
@ -115,7 +133,14 @@ export class ApiService {
}
getQuestionResults() {
return this.httpClient.get<QuestionresultsDto[]>(`${API_URL}/quiz/question-results`)
return this.httpClient.get<QuestionresultsDto[]>(`${API_URL}/quiz/question-results`).pipe(map((data) =>
data.map((item) => {
return {
...item,
time: new Date(item.time)
}
})
));
}
getFeatureFlagState(feature: string) {
@ -140,4 +165,8 @@ export class ApiService {
loser: loser
});
}
getEndgameResults() {
return this.httpClient.get<EndgameResultsDto>(`${API_URL}/quiz/endgame-results`)
}
}

View file

@ -24,4 +24,8 @@ export class TestingApiService {
clearGameQueue() {
return this.httpClient.post(`${API_URL}/game/clear-queue`, {});
}
simulateEndGamePoints() {
return this.httpClient.post(`${API_URL}/quiz/calculate-endgame-extrapoints`, {})
}
}

View file

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from "@angular/common/http";
import { API_URL } from "../../app.constants";
import { Subject } from "rxjs";
import {Observable, of, Subject} from "rxjs";
@Injectable({
providedIn: 'root'
@ -17,6 +17,19 @@ export class VoiceService {
this.voiceSubject.next(url);
}
playAudio$(url: string) {
this.voiceSubject.next(url);
return new Observable((observer) => {
const subscription = this.audioEndedSubject.subscribe({
next: () => {
observer.next(null);
observer.complete();
}
});
return () => subscription.unsubscribe();
})
}
getAudioUrl(text: string,voice: number = 1) {
return `${API_URL}/voice/tts?voice=${voice}&text=${text}`
}

View file

@ -4,5 +4,6 @@ export class FeatureFlagList {
"DontMarkQuestionsAsCompleted",
"DisableVoice",
"ProdMode",
"EndgamePointsUseCssAnimation",
];
}

View file

@ -0,0 +1,5 @@
export class SharedMethods {
static sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
}

View file

@ -0,0 +1,83 @@
<ng-container *ngIf="loaded">
<ng-container *ngIf="useCssAnimation">
<div class="dark-container">
<div class="night">
<div *ngFor="let i of [].constructor(25);" class="shooting_star">
</div>
</div>
<div class="content">
<div *ngIf="showInitialText" class="text-announce" @slideOut>
Время наградить особо отличившихся!
</div>
<div *ngIf="showMaxAmountOfInvalidAnswers" class="text-announce" @slideOut>
За тупость +2
<ng-container *ngIf="endgameResults && getParticipant(endgameResults?.maxInvalidAnswers?.id); let participant;">
<div class="d-flex justify-content-center">
<app-participant-item [participant]="participant" [small]="true" [transparent]="true" [shadow]="false"></app-participant-item>
</div>
</ng-container>
</div>
<div *ngIf="maxAmountOfPenalties" class="text-announce" @slideOut>
За тяжелую судьбу +2
<ng-container *ngIf="endgameResults && getParticipant(endgameResults?.maxPenalties?.id); let participant;">
<div class="d-flex justify-content-center">
<app-participant-item [participant]="participant" [small]="true" [transparent]="true" [shadow]="false"></app-participant-item>
</div>
</ng-container>
</div>
<div *ngIf="maxAmountOfRewards" class="text-announce" @slideOut>
За полный успех -2
<ng-container *ngIf="endgameResults && getParticipant(endgameResults?.maxRewards?.id); let participant;">
<div class="d-flex justify-content-center">
<app-participant-item [participant]="participant" [small]="true" [transparent]="true" [shadow]="false"></app-participant-item>
</div>
</ng-container>
</div>
</div>
</div>
</ng-container>
<ng-container *ngIf="!useCssAnimation">
<div class="background-video">
<video autoplay muted loop>
<source src="../../../assets/endgame/48569-454825064.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
<div class="content">
<div *ngIf="showInitialText" class="text-announce" @slideOut>
Время наградить особо отличившихся!
</div>
<div *ngIf="showMaxAmountOfInvalidAnswers" class="text-announce" @slideOut>
За тупость <span class="score">+2</span>
<ng-container *ngIf="endgameResults && getParticipant(endgameResults?.maxInvalidAnswers?.id); let participant;">
<div class="d-flex justify-content-center">
<app-participant-item [participant]="participant" [small]="true" [transparent]="true" [shadow]="false"></app-participant-item>
</div>
</ng-container>
</div>
<div *ngIf="maxAmountOfPenalties" class="text-announce" @slideOut>
За тяжелую судьбу <span class="score">+2</span>
<ng-container *ngIf="endgameResults && getParticipant(endgameResults?.maxPenalties?.id); let participant;">
<div class="d-flex justify-content-center">
<app-participant-item [participant]="participant" [small]="true" [transparent]="true" [shadow]="false"></app-participant-item>
</div>
</ng-container>
</div>
<div *ngIf="maxAmountOfRewards" class="text-announce" @slideOut>
За полный успех <span class="score">&minus;2</span>
<ng-container *ngIf="endgameResults && getParticipant(endgameResults?.maxRewards?.id); let participant;">
<div class="d-flex justify-content-center">
<app-participant-item [participant]="participant" [small]="true" [transparent]="true" [shadow]="false"></app-participant-item>
</div>
</ng-container>
</div>
</div>
</div>
</ng-container>
</ng-container>
<audio src="../../../assets/endgame/energetic-bgm-242515.mp3" autoplay [volume]="0.20">
</audio>

View file

@ -0,0 +1,202 @@
$shooting-time: 3000ms;
@keyframes slideInUp {
0% {
transform: translateY(3000%);
}
100% {
transform: translateY(0);
}
}
@keyframes opacityIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes fontSizeTitle {
0% {
font-size: 1em;
}
100% {
font-size: 2em;
}
}
.background-video {
position: relative;
height: 100vh; /* Full viewport height */
width: 100%; /* Full width */
overflow: hidden;
}
.dark-container {
position:relative;
height: 100vh;
width: 100%;
overflow: hidden;
background-color: #000220;
}
.background-video video {
position: absolute;
width: 100%;
height: 100vh;
object-fit: cover; /* Ensures the video covers the container */
z-index: -1; /* Places the video behind the content */
}
.content {
color: white;
position: absolute;
top: 50px;
left: 20px;
right: 20px;
z-index: 100;
text-align: center;
font-family: Arial, sans-serif;
padding: 20px;
}
.text-announce {
margin-top: 15%;
font-size: 2em;
font-weight: bold;
animation: slideInUp 1s forwards, fontSizeTitle 1s forwards, opacityIn 1s forwards;
}
.shooting_star {
position: absolute;
left: 10%;
top: 10%;
// width: 100px;
height: 2px;
background: linear-gradient(-45deg, rgb(243, 227, 6), rgba(0, 0, 255, 0));
border-radius: 999px;
filter: drop-shadow(0 0 6px rgb(250, 253, 148));
animation:
tail $shooting-time ease-in-out infinite,
shooting $shooting-time ease-in-out infinite;
&::before {
content: '';
position: absolute;
top: calc(50% - 1px);
right: 0;
// width: 30px;
height: 2px;
background: linear-gradient(-45deg, rgba(0, 0, 255, 0), rgba(95, 145, 255, 1), rgba(0, 0, 255, 0));
transform: translateX(50%) rotateZ(45deg);
border-radius: 100%;
animation: shining $shooting-time ease-in-out infinite;
}
&::after {
// CodePen Error
// @extend .shooting_star::before;
content: '';
position: absolute;
top: calc(50% - 1px);
right: 0;
// width: 30px;
height: 2px;
background: linear-gradient(-45deg, rgba(0, 0, 255, 0), rgba(95, 145, 255, 1), rgba(0, 0, 255, 0));
transform: translateX(50%) rotateZ(45deg);
border-radius: 100%;
animation: shining $shooting-time ease-in-out infinite;
transform: translateX(50%) rotateZ(-45deg);
}
@for $i from 1 through 20 {
&:nth-child(#{$i}) {
$delay: random(9999) + 0ms;
top: calc(50% - #{random(2000) - 200px});
left: calc(50% - #{random(300) + 0px});
animation-delay: $delay;
opacity: random(50) / 100 + 0.5;
&::before,
&::after {
animation-delay: $delay;
}
}
}
}
@keyframes tail {
0% {
width: 0;
}
30% {
width: 100px;
}
100% {
width: 0;
}
}
@keyframes shining {
0% {
width: 0;
}
50% {
width: 30px;
}
100% {
width: 0;
}
}
@keyframes shooting {
0% {
transform: translateX(0);
}
100% {
transform: translateX(300px);
}
}
@keyframes sky {
0% {
transform: rotate(45deg);
}
100% {
transform: rotate(45 + 360deg);
}
}
@keyframes glow {
0% {
text-shadow: 0 0 2px #f5f5f5, 0 0 10px #f5f5f5, 0 0 20px #ffc14d, 0 0 30px #fff1e0, 0 0 40px #ff4da6, 0 0 50px #ff4da6, 0 0 75px #ff4da6;
}
100% {
text-shadow: 0 0 20px #ffffff, 0 0 20px #ff0080, 0 0 30px #ff0080, 0 0 40px #ff0080, 0 0 50px #ff0080, 0 0 75px #ff0080, 0 0 100px #ff0080;
}
}
.night {
position: relative;
width: 100%;
height: 100%;
transform: rotateZ(45deg);
animation: sky 100000ms linear infinite;
}
.score {
background-color: #f8b12c;
border-radius: 50%;
padding: 10px;
text-shadow: 0 0 5px #f5f5f5, 0 0 10px #f5f5f5, 0 0 20px #ff4da6, 0 0 30px #ff4da6, 0 0 40px #ff4da6, 0 0 50px #ff4da6, 0 0 75px #ff4da6;
animation: glow 2s infinite alternate;
}

View file

@ -0,0 +1,21 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EndgamepointsComponent } from './endgamepoints.component';
describe('EndgamepointsComponent', () => {
let component: EndgamepointsComponent;
let fixture: ComponentFixture<EndgamepointsComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [EndgamepointsComponent]
});
fixture = TestBed.createComponent(EndgamepointsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,99 @@
import {Component, OnInit} from '@angular/core';
import {ApiService, EndgameResultsDto, FeatureFlagStateDto} from "../../services/api.service";
import {
of,
Subject,
lastValueFrom,
firstValueFrom,
mergeWith,
combineLatestAll,
combineLatest,
mergeMap,
forkJoin, merge
} from "rxjs";
import { takeUntil} from "rxjs/operators";
import {VoiceService} from "../../services/voice.service";
import {animate, keyframes, style, transition, trigger} from "@angular/animations";
import {Participant} from "../../../types/participant";
import {SharedMethods} from "../../shared/sharedmethods";
@Component({
selector: 'app-endgamepoints',
templateUrl: './endgamepoints.component.html',
styleUrls: ['./endgamepoints.component.scss'],
animations: [
trigger('slideOut', [
transition(':leave', [
animate(
'1300ms ease-in',
style({ transform: 'translateY(-100%)', opacity: 0 })
),
]),
])]
})
export class EndgamepointsComponent implements OnInit{
loaded = false;
useCssAnimation = false;
destroyed$ = new Subject();
showInitialText = true;
showMaxAmountOfInvalidAnswers = false;
endgameResults: EndgameResultsDto | null;
participants: Participant[] = [];
maxAmountOfPenalties = false;
maxAmountOfRewards = false;
constructor(private apiService: ApiService, private voiceService: VoiceService) {
}
ngOnInit(): void {
const ff$ = this.apiService.getFeatureFlagState("EndgamePointsUseCssAnimation");
// @ts-ignore
const results$ = this.apiService.getEndgameResults();
combineLatest([results$, ff$])
.pipe(takeUntil(this.destroyed$))
.subscribe(([results,ff]) => {
const userData$ = [];
userData$.push(
this.apiService.getParticipant(results.maxInvalidAnswers.id),
this.apiService.getParticipant(results.maxPenalties.id),
this.apiService.getParticipant(results.maxRewards.id),
);
merge(...userData$).pipe(takeUntil(this.destroyed$)).subscribe((r) => this.participants.push(r));
this.useCssAnimation = ff.state;
this.endgameResults = results;
this.loaded = true;
this.playScene().then(() => {});
console.log(results);
});
}
async playScene() {
await firstValueFrom(this.voiceService.playAudio$(this.voiceService.getAudioUrl("Время наградить особо отличившихся!")));
this.showInitialText = false;
await SharedMethods.sleep(1000);
this.showMaxAmountOfInvalidAnswers = true;
await SharedMethods.sleep(500);
await firstValueFrom(this.voiceService.playAudio$(this.voiceService.getAudioUrl(` За максимальное количество неверных ответов, плюс два очка получает ${this.endgameResults?.maxInvalidAnswers.name}`)));
this.showMaxAmountOfInvalidAnswers = false;
await SharedMethods.sleep(3000);
this.maxAmountOfPenalties = true;
await firstValueFrom(this.voiceService.playAudio$(this.voiceService.getAudioUrl(`За самое большое количество полученных наказаний плюс два очка получил ${this.endgameResults?.maxPenalties.name}`)));
await SharedMethods.sleep(3000)
this.maxAmountOfPenalties = false;
await firstValueFrom(this.voiceService.playAudio$(this.voiceService.getAudioUrl(`И чтобы сделать игру более справедливой, есть последняя номинация`)));
this.maxAmountOfRewards = true;
await firstValueFrom(this.voiceService.playAudio$(this.voiceService.getAudioUrl(`${this.endgameResults?.maxRewards.name} лишается двух очков за свой невероятный ум`)));
await SharedMethods.sleep(15000);
}
getParticipant(id: number | undefined): Participant|null {
if(id) {
const p = this.participants.find(x => x.telegramId === id);
return p !== undefined ? p : null;
}
return null;
}
}

View file

@ -118,7 +118,6 @@ export class OnboardingComponent implements OnInit, OnDestroy {
}
shakeCard(card: ElementRef) {
console.log(`shake card`);
this.renderer.addClass(card.nativeElement, 'shake');
this.renderer.addClass(card.nativeElement, 'zoom-in');
if(!this.allRulesAnnounced) {
@ -142,7 +141,6 @@ export class OnboardingComponent implements OnInit, OnDestroy {
}
stopShaking(card: ElementRef) {
console.log(`stop shacking`);
this.renderer.removeClass(card.nativeElement, 'shake');
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.