diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..4da0039
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,32 @@
+# Stage 1: Build the Angular application
+FROM node:20 as build
+
+# Set the working directory
+WORKDIR /app
+
+# Copy package.json and package-lock.json to install dependencies
+COPY package*.json ./
+
+# Install Node.js dependencies
+RUN npm install
+
+# Copy the rest of the application code
+COPY . .
+
+# Build the Angular application in production mode
+RUN npm run build --configuration=prod
+
+# Stage 2: Serve the app with Nginx
+FROM nginx:1.21-alpine
+
+# Copy the built app from the previous stage
+COPY --from=build /app/dist/thanksgiving /usr/share/nginx/html
+
+# Copy the custom Nginx configuration
+COPY nginx.conf /etc/nginx/conf.d/default.conf
+
+# Expose port 80
+EXPOSE 80
+
+# Start Nginx server
+CMD ["nginx", "-g", "daemon off;"]
\ No newline at end of file
diff --git a/gift.json b/gift.json
deleted file mode 100644
index b259c43..0000000
--- a/gift.json
+++ /dev/null
@@ -1,201 +0,0 @@
-[{
-"prizeID": 1,
-"name": "Черные носки похуиста",
-"isGifted": false
-},
-{
-"prizeID": 2,
-"name": "Красные носки с алфавитом (выучи эти буквы, наконец!)",
-"isGifted": false
-},
-{
-"prizeID": 3,
-"name": "Червячков, кислых как твои щи",
-"isGifted": false
-},
-{
-"prizeID": 4,
-"name": "Массажер для жопы",
-"isGifted": false
-},
-{
-"prizeID": 5,
-"name": "Мыльные пузыри от Жозе",
-"isGifted": false
-},
-{
-"prizeID": 6,
-"name": "Татуировки переводные \"Вечер в хату\"",
-"isGifted": false
-},
-{
-"prizeID": 7,
-"name": "Шары воздушные женские",
-"isGifted": false
-},
-{
-"prizeID": 8,
-"name": "Неоновые палочки моргалочки",
-"isGifted": false
-},
-{
-"prizeID": 9,
-"name": "Свечу в гранулах с ароматом собачьих жоп",
-"isGifted": false
-},
-{
-"prizeID": 10,
-"name": "Чучело белки омерзительное",
-"isGifted": false
-},
-{
-"prizeID": 11,
-"name": "Почтовый ящик с пингвином",
-"isGifted": false
-},
-{
-"prizeID": 12,
-"name": "Ленту новогоднюю нарядную",
-"isGifted": false
-},
-{
-"prizeID": 13,
-"name": "Резинку для волос от Тимошенко",
-"isGifted": false
-},
-{
-"prizeID": 14,
-"name": "Воздушный шар в виде пингвина",
-"isGifted": false
-},
-{
-"prizeID": 15,
-"name": "Мармеладки из медвежатины",
-"isGifted": false
-},
-{
-"prizeID": 16,
-"name": "Мыльные пузыри космические",
-"isGifted": false
-},
-{
-"prizeID": 17,
-"name": "Ободок деда Мороза красный прекрасный",
-"isGifted": false
-},
-{
-"prizeID": 18,
-"name": "Палочки с ароматом тайского блядюшника",
-"isGifted": false
-},
-{
-"prizeID": 19,
-"name": "Набор для выживания",
-"isGifted": false
-},
-{
-"prizeID": 20,
-"name": "Ободок зеленый, как ты после грузинских маршруток",
-"isGifted": false
-},
-{
-"prizeID": 21,
-"name": "Наклейки новогодние",
-"isGifted": false
-},
-{
-"prizeID": 22,
-"name": "Книгу джунглей",
-"isGifted": false
-},
-{
-"prizeID": 23,
-"name": "Брелок патриотический",
-"isGifted": false
-},
-{
-"prizeID": 24,
-"name": "Солевого Георгия",
-"isGifted": false
-},
-{
-"prizeID": 25,
-"name": "Глину полимерную синюю, как ты после чачи",
-"isGifted": false
-},
-{
-"prizeID": 26,
-"name": "Листы для заметок весёленькие",
-"isGifted": false
-},
-{
-"prizeID": 27,
-"name": "Дракончика серого, как жизнь без вина",
-"isGifted": false
-},
-{
-"prizeID": 28,
-"name": "Паззл-предсказание твоего светлого будущего",
-"isGifted": false
-},
-{
-"prizeID": 29,
-"name": "Глину с ароматом глины (набор)",
-"isGifted": false
-},
-{
-"prizeID": 30,
-"name": "Носки от Лукашенко",
-"isGifted": false
-},
-{
-"prizeID": 31,
-"name": "Лося-летчика",
-"isGifted": false
-},
-{
-"prizeID": 32,
-"name": "Щетку для массажа простаты",
-"isGifted": false
-},
-{
-"prizeID": 33,
-"name": "Тесто для лепки хачапури (ведро)",
-"isGifted": false
-},
-{
-"prizeID": 34,
-"name": "Карусель на выборах",
-"isGifted": false
-},
-{
-"prizeID": 35,
-"name": "Зеркало компактное \"Котик\" (зеркало котик, а не ты)",
-"isGifted": false
-},
-{
-"prizeID": 36,
-"name": "Дракончика нетрадиционного розового",
-"isGifted": false
-},
-{
-"prizeID": 37,
-"name": "Глину полимерную желтую несмешную",
-"isGifted": false
-},
-{
-"prizeID": 38,
-"name": "Ручку с алмазом, как у Путина",
-"isGifted": false
-},
-{
-"prizeID": 39,
-"name": "Ручку зеленую с цыпленком",
-"isGifted": false
-},
-{
-"prizeID": 40,
-"name": "Светильник романтишный",
-"isGifted": false
-}
-]
\ No newline at end of file
diff --git a/nginx.conf b/nginx.conf
new file mode 100644
index 0000000..fcd2c37
--- /dev/null
+++ b/nginx.conf
@@ -0,0 +1,11 @@
+server {
+ listen 80;
+ server_name localhost;
+
+ root /usr/share/nginx/html;
+ index index.html;
+
+ location / {
+ try_files $uri $uri/ /index.html;
+ }
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index e858c06..2653efe 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -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"
@@ -34,7 +34,7 @@
"@angular/cli": "^16.2.9",
"@angular/compiler-cli": "~16.2.12",
"@types/jasmine": "~3.8.0",
- "@types/node": "^12.11.1",
+ "@types/node": "^12.20.55",
"jasmine-core": "~3.8.0",
"karma": "~6.3.0",
"karma-chrome-launcher": "~3.1.0",
@@ -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",
@@ -3691,7 +3646,8 @@
"version": "12.20.55",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz",
"integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/@types/node-forge": {
"version": "1.3.9",
@@ -7331,15 +7287,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 +13371,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 +15567,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 +15667,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 +15689,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 +15703,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 +15716,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 +20758,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 +24968,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": {
diff --git a/package.json b/package.json
index afafcf0..d06d966 100644
--- a/package.json
+++ b/package.json
@@ -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"
@@ -36,7 +36,7 @@
"@angular/cli": "^16.2.9",
"@angular/compiler-cli": "~16.2.12",
"@types/jasmine": "~3.8.0",
- "@types/node": "^12.11.1",
+ "@types/node": "^12.20.55",
"jasmine-core": "~3.8.0",
"karma": "~6.3.0",
"karma-chrome-launcher": "~3.1.0",
diff --git a/punishments.json b/punishments.json
index ee322cd..2193aba 100644
--- a/punishments.json
+++ b/punishments.json
@@ -1,170 +1,152 @@
[
{
- "text": "Расскажите про свою самую любимую игрушку"
+ "text": "Сделать смешной комплимент каждому присутствующему"
},
{
- "text": "Назовите 20 слов на букву Ч"
+ "text": "Назвать 20 слов на букву Ч"
},
{
- "text": "Без слов изобразите то, чем приходится заниматься на работе, чтобы присутствующие угадали."
+ "text": "Рассказать две истории - правду и выдумку. Остальные должны угадать, где какая"
},
{
- "text": "Изобразите 5 видов спорта так, чтобы присутствующие смогли их назвать."
+ "text": "Сосед ручкой рисует тебе татуировку на руке"
},
{
- "text": "Выполните приседания (10 раз), положив на голову книгу."
+ "text": "Пусть кто-то из игроков сделает тебе новую прическу"
},
{
- "text": "Посчитайте любую считалку, на ком она остановится, должен выпить с тобой"
+ "text": "Сделать себе патчи под глаза из огурца и сидеть так 5 минут"
},
{
- "text": "Попросите каждого игрока по очереди назвать слово и придумать к нему рифму"
+ "text": "Стоя дотянуться коленкой до носа"
},
{
- "text": "Назовите 5 грузинских вин"
+ "text": "Нарисовать себе усы"
},
{
- "text": "Изобразите кота, которому страшно, но любопытно "
+ "text": "В течение минуты гладить себя одновременно по голове и по животу, но в разных направлениях"
},
{
- "text": "Сделайте необычный подарок игроку с максимальным количеством очков, не выходя из комнаты"
+ "text": "Прочитать любое детское стихотворение с кавказским акцентом"
},
{
- "text": "Нарисуйте или приклейте милые усики "
+ "text": "Сказать быстро 3 скороговорки"
},
{
- "text": "Изобразите иностранца. Говорите на любом языке, можно даже на собственном."
+ "text": "Играть пантомиму \"Гопник\": сесть на корточки и щелкать семечки, можно потребовать мелочь у ближайших игроков"
},
{
- "text": "Расскажите плохой анекдот"
+ "text": "Набрать в рот орехов и произнести 5 раз фразу \"толстощекий вкусноежка\""
},
{
- "text": "Опишите свою работу тремя словами"
+ "text": "Съесть четверть лимона"
},
{
- "text": "Распознайте на ощупь 5 разных предметов с завязанными глазами, конечно же."
+ "text": "Подпрыгивая на одной ноге, читать стихотворение"
},
{
- "text": "Подпрыгните 10 раз, каждый раз произнося \"индейка\"."
+ "text": "Попрыгать по комнате как лягушка"
},
{
- "text": "Расскажите стих, в котором будет ваше имя."
+ "text": "Выпить вино из блюдца без помощи рук"
},
{
- "text": "Изобразите свой любимый фрукт без слов."
+ "text": "Написать свое имя на бумаге, держа карандаш в зубах"
},
{
- "text": "Расскажите мини-историю о забавных приключениях вашей левой руки."
+ "text": "Открыть книгу на любой странице и прочитать отрывок похотливым голосом"
},
{
- "text": "Придумайте себе псевдоним и откликайтесь только на него следующие 5 минут."
+ "text": "Попытаться засунуть в рот кулак"
},
{
- "text": "Изобразите муравья, который нашел огромную еду."
+ "text": "Выполнить приседания (10 раз), положив на голову книгу"
},
{
- "text": "Нарисуйте свой знак зодиака, чтобы остальные игроки отгадали."
+ "text": "Посчитать любую считалку, на ком она остановится, должен выпить с тобой"
},
{
- "text": "Подпевайте любимой песне, заменяя слова на \"ля-ля-ля\"."
+ "text": "Рассказать плохой анекдот"
},
{
- "text": "Играйте в невидимую гитару и исполняйте короткую мелодию."
+ "text": "Распознать на ощупь 5 разных предметов (с завязанными глазами, конечно же)"
},
{
- "text": "Представьте, что вы робот, и произнесите что-то с использованием роботизированного голоса."
+ "text": "Подпрыгнуть 10 раз, каждый раз произнося \"индейка\""
},
{
- "text": "Изобразите свой страх перед любым предметом в комнате."
+ "text": "Изобразить муравья, который нашел огромную еду"
},
{
- "text": "Постарайтесь нарисовать свою любимую песню."
+ "text": "Представить, что вы все находитесь на красной дорожке, и ты - главная звезда. Пройтись по комнате с гордой осанкой"
},
{
- "text": "Изобразите смешное животное, которого нет в реальном мире."
+ "text": "Говорить как пират в течение пяти минут"
},
{
- "text": "Перевоплотитесь в своего любимого персонажа книги или фильма и представьтесь."
+ "text": "Дотянуться языком до носа"
},
{
- "text": "Назовите алфавит задом наперед."
+ "text": "Изображать известного мультяшного персонажа в течение 1 минуты"
},
{
- "text": "Спойте отрывок из любимой детской песни."
+ "text": "Съесть кусочек хлеба, на который несколько игроков добавят один ингредиент по выбору"
},
{
- "text": "Изобразите, что вы танцуете на льду, не поднимаясь с места."
+ "text": "Сфотографироваться в самой нелепой позе"
},
{
- "text": "Постарайтесь сказать \"индейка\" наоборот."
+ "text": "Попытаться постоять на одной ноге 30 секунд с завязанными глазами"
},
{
- "text": "Расскажите короткую историю, используя только по три слова в каждом предложении."
+ "text": "Исполнить короткий танец на месте"
},
{
- "text": "Играйте в 'испорченный телефон': прошепчите любую фразу первому человеку, а затем посмотрите, как она изменится по цепочке."
+ "text": "Произнести алфавит, чередуя громкий голос и шёпот"
},
{
- "text": "Назовите пять стран, начинающихся на букву \"И\"."
+ "text": "Сказать фразу из трёх слов, которая заставит всех засмеяться"
},
{
- "text": "Переведите любое слово на вымышленный язык и объясните его значение."
+ "text": "Танцевать под песню, которую выберут другие"
},
{
"text": "Представьте, что вы новый супергерой с уникальной способностью, и расскажите о ней."
},
{
- "text": "Издайте звук, который в вашем представлении соответствует слову 'веселье'."
+ "text": "Нарисовать кошку, не отрывая ручку от бумаги"
},
{
- "text": "Расскажите короткую историю о приключениях своей тапочки."
+ "text": "Изобразить известного супергероя, но на пенсии"
},
{
- "text": "Изобразите смешное лицо и попросите остальных угадать эмоцию."
+ "text": "Придумать и показать три способа необычного использования стула"
},
{
- "text": "Придумайте по одному положительному качества для каждого игрока."
+ "text": "Изобразить, как вы идёте по раскалённым углям"
},
{
- "text": "Представьте, что вы ведущий радиошоу и сделайте короткую передачу на любую тему."
+ "text": "Сказать комплимент каждому игроку, используя слова, начинающиеся на одну букву"
},
{
- "text": "Постарайтесь сделать звуковое подражание своего любимого животного."
+ "text": "Придумать стихотворение из 4 строк про самого себя. Рассказать с выражением"
},
{
- "text": "Расскажите короткую историю о приключениях своего домашнего растения."
+ "text": "Провести импровизированный урок танцев, обучая всех \"новому движению\""
},
{
"text": "Представьтесь как профессиональный критик и дайте короткий обзор своего дня."
},
{
- "text": "Представьте, что вы находитесь на красной дорожке, и вы - главная звезда. Пройдитесь по комнате с гордой осанкой."
+ "text": "Сказать скороговорку, зажав язык зубами"
},
{
- "text": "Играйте в \"замедленное движение\": выполните простую задачу (например, открытие двери) медленно и торжественно."
+ "text": "Сделать из салфеток или бумаги шляпу и носить её до следующего наказания"
},
{
- "text": "Говорите как пират в течение пяти минут."
+ "text": "\"атака роботов\": исполнить наказание, придуманное искусственным интеллектом в реальном времени"
},
{
"text": "Возьмите на себя роль человеческой статуи и замрите в забавной позе на пять минут."
-},
-{
- "text": "Рассказать скороговорку без запинок, если запнулся, то начать заново."
-},
-{
- "text": "Нарисовать монобровь."
-},
-{
- "text": "Выпить или съесть что-то, не используя руки."
-},
-{
- "text": "Дотянуться языком до носа."
-},
-{
- "text": "Вылакать стаканчик сока или молока из блюдца."
-},
-{
- "text": "Набить рот чем-то вкусненьким и произнести 5 раз фразу \"толстощекий вкуснооежка\"."
- }
+}
]
\ No newline at end of file
diff --git a/src/app.constants.ts b/src/app.constants.ts
deleted file mode 100644
index 22ea441..0000000
--- a/src/app.constants.ts
+++ /dev/null
@@ -1,4 +0,0 @@
- export const API_URL = 'http://127.0.0.1:3000';
-//export const WEBSOCK_URL = 'http://127.0.0.1:3000';
-// export const API_URL = 'https://thanksgiving2023.ngweb.io/api';
- export const WEBSOCK_URL = "https://thanksgiving2023.ngweb.io/"
diff --git a/src/app/admin/admin-main/admin-main.component.html b/src/app/admin/admin-main/admin-main.component.html
new file mode 100644
index 0000000..8ef7813
--- /dev/null
+++ b/src/app/admin/admin-main/admin-main.component.html
@@ -0,0 +1,6 @@
+
+
diff --git a/src/app/admin/admin-main/admin-main.component.scss b/src/app/admin/admin-main/admin-main.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/admin/admin-main/admin-main.component.spec.ts b/src/app/admin/admin-main/admin-main.component.spec.ts
new file mode 100644
index 0000000..04075ad
--- /dev/null
+++ b/src/app/admin/admin-main/admin-main.component.spec.ts
@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { AdminMainComponent } from './admin-main.component';
+
+describe('AdminMainComponent', () => {
+ let component: AdminMainComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [AdminMainComponent]
+ });
+ fixture = TestBed.createComponent(AdminMainComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/admin/admin-main/admin-main.component.ts b/src/app/admin/admin-main/admin-main.component.ts
new file mode 100644
index 0000000..719d3cc
--- /dev/null
+++ b/src/app/admin/admin-main/admin-main.component.ts
@@ -0,0 +1,10 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-admin-main',
+ templateUrl: './admin-main.component.html',
+ styleUrls: ['./admin-main.component.scss']
+})
+export class AdminMainComponent {
+
+}
diff --git a/src/app/admin/admin-routing.module.ts b/src/app/admin/admin-routing.module.ts
index 261ed5a..598a087 100644
--- a/src/app/admin/admin-routing.module.ts
+++ b/src/app/admin/admin-routing.module.ts
@@ -2,6 +2,9 @@ import { NgModule } from "@angular/core";
import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes, UrlTree } from "@angular/router";
import { HomeComponent } from "./home/home.component";
import { Observable, of } from "rxjs";
+import {ConfigurationComponent} from "./configuration/configuration.component";
+import {AdminMainComponent} from "./admin-main/admin-main.component";
+import {AdminTestingComponent} from "./admin-testing/admin-testing.component";
export class AdminGuard {
@@ -10,18 +13,38 @@ export class AdminGuard {
}
canDeactivate(component: HomeComponent, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, nextState?: RouterStateSnapshot): Observable | Promise | boolean | UrlTree {
+ if(nextState?.url.indexOf('admin') !== -1){
+ return of(true);
+ }
return of(false);
}
}
-
const routes: Routes = [
{
path: '',
component: HomeComponent,
canDeactivate: [AdminGuard],
- }
+ children: [
+ {
+ path:'',
+ component: AdminMainComponent,
+ canDeactivate: [AdminGuard],
+ },
+ {
+ path: 'configuration',
+ component: ConfigurationComponent,
+ canDeactivate: [AdminGuard],
+ },
+ {
+ path:'testing',
+ component: AdminTestingComponent,
+ canDeactivate: [AdminGuard],
+ }
+ ]
+ },
+
]
@NgModule({
diff --git a/src/app/admin/admin-testing/admin-testing.component.html b/src/app/admin/admin-testing/admin-testing.component.html
new file mode 100644
index 0000000..0075a22
--- /dev/null
+++ b/src/app/admin/admin-testing/admin-testing.component.html
@@ -0,0 +1,20 @@
+
+
Game testing menu
+ Players
+
+ Game
+
+
+
+ Versus
+
+
+
+
+
+
+
+
+ You are in prod mode, testing disabled
+
+
\ No newline at end of file
diff --git a/src/app/admin/admin-testing/admin-testing.component.scss b/src/app/admin/admin-testing/admin-testing.component.scss
new file mode 100644
index 0000000..57fb940
--- /dev/null
+++ b/src/app/admin/admin-testing/admin-testing.component.scss
@@ -0,0 +1,6 @@
+div {
+
+ button {
+ margin: 5px;
+ }
+}
\ No newline at end of file
diff --git a/src/app/admin/admin-testing/admin-testing.component.spec.ts b/src/app/admin/admin-testing/admin-testing.component.spec.ts
new file mode 100644
index 0000000..d3e3612
--- /dev/null
+++ b/src/app/admin/admin-testing/admin-testing.component.spec.ts
@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { AdminTestingComponent } from './admin-testing.component';
+
+describe('AdminTestingComponent', () => {
+ let component: AdminTestingComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [AdminTestingComponent]
+ });
+ fixture = TestBed.createComponent(AdminTestingComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/admin/admin-testing/admin-testing.component.ts b/src/app/admin/admin-testing/admin-testing.component.ts
new file mode 100644
index 0000000..dcec8de
--- /dev/null
+++ b/src/app/admin/admin-testing/admin-testing.component.ts
@@ -0,0 +1,60 @@
+import {Component, OnDestroy, OnInit} from '@angular/core';
+import {ApiService} from "../../services/api.service";
+import {Subject} from "rxjs";
+import {takeUntil} from "rxjs/operators";
+import {EventService} from "../../services/event.service";
+import {TestingApiService} from "../../services/testing-api.service";
+
+@Component({
+ selector: 'app-admin-testing',
+ templateUrl: './admin-testing.component.html',
+ styleUrls: ['./admin-testing.component.scss']
+})
+export class AdminTestingComponent implements OnInit, OnDestroy {
+ prodMode = false;
+ destroyed$ = new Subject();
+ constructor(
+ private apiService: ApiService,
+ private eventService: EventService,
+ private testingApiService: TestingApiService) {
+ }
+
+ ngOnInit(): void {
+ this.getFFState();
+ this.eventService.featureFlagChanged.pipe(takeUntil(this.destroyed$)).subscribe((r) => this.getFFState());
+ }
+
+ private getFFState() {
+ this.apiService.getFeatureFlagState("ProdMode").pipe(takeUntil(this.destroyed$)).subscribe((res) =>
+ {
+ this.prodMode = res.state;
+ });
+ }
+ ngOnDestroy() {
+ this.destroyed$.complete();
+ }
+
+ simulateVersus() {
+ this.testingApiService.simulateVersus().pipe(takeUntil(this.destroyed$)).subscribe((r) => console.log(r));
+ }
+
+ resetAllVersusTasksAsIncompleted() {
+ this.testingApiService.resetAllVersusTasksAsIncompleted().pipe(takeUntil(this.destroyed$)).subscribe((r) => console.log(r));
+ }
+
+ resetAllPlayersScore() {
+ this.testingApiService.resetAllPlayersScore().pipe(takeUntil(this.destroyed$)).subscribe(r => console.log(r));
+ }
+
+ 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));
+ }
+
+ simulateValidAnswer() {
+ this.testingApiService.simulateValidAnswer().pipe(takeUntil(this.destroyed$)).subscribe(r => console.log(r));
+ }
+}
diff --git a/src/app/admin/admin.module.ts b/src/app/admin/admin.module.ts
index b3b35a4..8798690 100644
--- a/src/app/admin/admin.module.ts
+++ b/src/app/admin/admin.module.ts
@@ -3,9 +3,13 @@ import { CommonModule } from '@angular/common';
import { HomeComponent } from './home/home.component';
import { AdminRoutingModule } from "./admin-routing.module";
import { MainActionsComponent } from './components/main-actions/main-actions.component';
-import { AppModule } from "../app.module";
import { SharedModule } from "../shared/shared.module";
import { QueueActionsComponent } from './components/queue-actions/queue-actions.component';
+import { ConfigurationComponent } from './configuration/configuration.component';
+import { AdminNavComponent } from './components/admin-nav/admin-nav.component';
+import { AdminMainComponent } from './admin-main/admin-main.component';
+import { FeatureflagsComponent } from './components/featureflags/featureflags.component';
+import { AdminTestingComponent } from './admin-testing/admin-testing.component';
@@ -13,7 +17,12 @@ import { QueueActionsComponent } from './components/queue-actions/queue-actions.
declarations: [
HomeComponent,
MainActionsComponent,
- QueueActionsComponent
+ QueueActionsComponent,
+ ConfigurationComponent,
+ AdminNavComponent,
+ AdminMainComponent,
+ FeatureflagsComponent,
+ AdminTestingComponent,
],
imports: [
CommonModule, AdminRoutingModule, SharedModule,
diff --git a/src/app/admin/components/admin-nav/admin-nav.component.html b/src/app/admin/components/admin-nav/admin-nav.component.html
new file mode 100644
index 0000000..1b34e8a
--- /dev/null
+++ b/src/app/admin/components/admin-nav/admin-nav.component.html
@@ -0,0 +1,4 @@
+
+Main
+Testing
+Config
diff --git a/src/app/admin/components/admin-nav/admin-nav.component.scss b/src/app/admin/components/admin-nav/admin-nav.component.scss
new file mode 100644
index 0000000..cf1a774
--- /dev/null
+++ b/src/app/admin/components/admin-nav/admin-nav.component.scss
@@ -0,0 +1,4 @@
+a:link, a:active, a:visited {
+ color: white;
+ padding: 3px;
+}
diff --git a/src/app/admin/components/admin-nav/admin-nav.component.spec.ts b/src/app/admin/components/admin-nav/admin-nav.component.spec.ts
new file mode 100644
index 0000000..9c03deb
--- /dev/null
+++ b/src/app/admin/components/admin-nav/admin-nav.component.spec.ts
@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { AdminNavComponent } from './admin-nav.component';
+
+describe('AdminNavComponent', () => {
+ let component: AdminNavComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [AdminNavComponent]
+ });
+ fixture = TestBed.createComponent(AdminNavComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/admin/components/admin-nav/admin-nav.component.ts b/src/app/admin/components/admin-nav/admin-nav.component.ts
new file mode 100644
index 0000000..3b237d2
--- /dev/null
+++ b/src/app/admin/components/admin-nav/admin-nav.component.ts
@@ -0,0 +1,10 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-admin-nav',
+ templateUrl: './admin-nav.component.html',
+ styleUrls: ['./admin-nav.component.scss']
+})
+export class AdminNavComponent {
+
+}
diff --git a/src/app/admin/components/featureflags/featureflags.component.html b/src/app/admin/components/featureflags/featureflags.component.html
new file mode 100644
index 0000000..59b6e70
--- /dev/null
+++ b/src/app/admin/components/featureflags/featureflags.component.html
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app/admin/components/featureflags/featureflags.component.scss b/src/app/admin/components/featureflags/featureflags.component.scss
new file mode 100644
index 0000000..1aa47e8
--- /dev/null
+++ b/src/app/admin/components/featureflags/featureflags.component.scss
@@ -0,0 +1,8 @@
+.featureflags {
+ .form-group {
+ margin-left: 5px;
+ }
+ label {
+ margin-left: 3px;
+ }
+}
\ No newline at end of file
diff --git a/src/app/admin/components/featureflags/featureflags.component.spec.ts b/src/app/admin/components/featureflags/featureflags.component.spec.ts
new file mode 100644
index 0000000..2d31e29
--- /dev/null
+++ b/src/app/admin/components/featureflags/featureflags.component.spec.ts
@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { FeatureflagsComponent } from './featureflags.component';
+
+describe('FeatureflagsComponent', () => {
+ let component: FeatureflagsComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [FeatureflagsComponent]
+ });
+ fixture = TestBed.createComponent(FeatureflagsComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/admin/components/featureflags/featureflags.component.ts b/src/app/admin/components/featureflags/featureflags.component.ts
new file mode 100644
index 0000000..d72e250
--- /dev/null
+++ b/src/app/admin/components/featureflags/featureflags.component.ts
@@ -0,0 +1,59 @@
+import {Component, OnDestroy, OnInit} from '@angular/core';
+import {ApiService, FeatureFlagStateDto} from "../../../services/api.service";
+import {FeatureFlagList} from "../../../shared/featureflags";
+import {takeUntil} from "rxjs/operators";
+import {Subject} from "rxjs";
+import {EventService} from "../../../services/event.service";
+
+@Component({
+ selector: 'app-featureflags',
+ templateUrl: './featureflags.component.html',
+ styleUrls: ['./featureflags.component.scss']
+})
+export class FeatureflagsComponent implements OnInit, OnDestroy {
+ destroyed$ = new Subject();
+ public features: FeatureFlagStateDto[] = [];
+ constructor(private apiService: ApiService, private eventService: EventService) {
+ }
+
+ ngOnDestroy(): void {
+ this.destroyed$.complete();
+ }
+ ngOnInit(): void {
+ this.eventService.featureFlagChanged.pipe(takeUntil(this.destroyed$)).subscribe(result => this.loadFeatureFlags());
+ this.loadFeatureFlags();
+ }
+ private loadFeatureFlags() {
+
+ FeatureFlagList.FeatureFlags.map((featureFlag) => {
+ this.apiService.getFeatureFlagState(featureFlag).pipe(takeUntil(this.destroyed$)).subscribe((result) => {
+ if(!this.features.find((x) => x.name === result.name)) {
+ this.features.push(result);
+ this.features.sort((a, b) => {
+ if (a.name < b.name) {
+ return -1;
+ }
+ if (a.name > b.name) {
+ return 1;
+ }
+ return 0;
+ });
+ } else {
+ const index = this.features.findIndex((x) => x.name === result.name);
+ this.features[index] = result;
+ }
+ });
+ })
+ }
+
+ setFeatureFlag(name: string) {
+ const ff = this.features.find((featureFlag) => featureFlag.name === name);
+ let newState = false;
+ if(ff) {
+ newState = !ff.state;
+ }
+ this.apiService.setFeatureFlagState(name, newState).pipe(takeUntil(this.destroyed$)).subscribe((result) => {
+ this.loadFeatureFlags();
+ });
+ }
+}
diff --git a/src/app/admin/components/main-actions/main-actions.component.ts b/src/app/admin/components/main-actions/main-actions.component.ts
index f5c5c3c..6925519 100644
--- a/src/app/admin/components/main-actions/main-actions.component.ts
+++ b/src/app/admin/components/main-actions/main-actions.component.ts
@@ -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' },
];
diff --git a/src/app/admin/components/queue-actions/queue-actions.component.html b/src/app/admin/components/queue-actions/queue-actions.component.html
index cc698f5..1421ccf 100644
--- a/src/app/admin/components/queue-actions/queue-actions.component.html
+++ b/src/app/admin/components/queue-actions/queue-actions.component.html
@@ -3,3 +3,8 @@
tg: {{ gameQueue.type }}
+
+
Who won
+
+
+
\ No newline at end of file
diff --git a/src/app/admin/components/queue-actions/queue-actions.component.ts b/src/app/admin/components/queue-actions/queue-actions.component.ts
index fb00be4..c69a02f 100644
--- a/src/app/admin/components/queue-actions/queue-actions.component.ts
+++ b/src/app/admin/components/queue-actions/queue-actions.component.ts
@@ -2,7 +2,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
import { EventService } from "../../../services/event.service";
import { Subject } from "rxjs";
import { map, takeUntil } from "rxjs/operators";
-import { EventGameQueue, QueueTypes } from "../../../../types/server-event";
+import {EventGameQueue, QueueTypes, VersusBeginEvent} from "../../../../types/server-event";
import { ApiService } from "../../../services/api.service";
@Component({
@@ -14,7 +14,7 @@ export class QueueActionsComponent implements OnInit, OnDestroy {
destroyed$ = new Subject()
constructor(private eventService: EventService, private apiService: ApiService) { }
gameQueue: EventGameQueue | null;
-
+ versusData: VersusBeginEvent| null = null;
ngOnInit(): void {
this.eventService.gameQueueEvent.pipe(
takeUntil(this.destroyed$),
@@ -22,7 +22,17 @@ export class QueueActionsComponent implements OnInit, OnDestroy {
).subscribe(e => {
this.gameQueue = e;
});
+ this.setVersusHandler();
}
+
+ setVersusHandler() {
+ this.eventService.versusBegin.pipe(takeUntil(this.destroyed$)).subscribe((r) => {
+ this.versusData = r.data;
+
+ });
+
+ }
+
ngOnDestroy() {
this.destroyed$.complete();
}
@@ -32,4 +42,10 @@ export class QueueActionsComponent implements OnInit, OnDestroy {
// this.gameQueue = null;
})
}
+
+ versusWon(playerId: number, loser: number) {
+ this.apiService.completeVersus(playerId, loser).subscribe(r => {
+ this.versusData = null;
+ })
+ }
}
diff --git a/src/app/admin/configuration/configuration.component.html b/src/app/admin/configuration/configuration.component.html
new file mode 100644
index 0000000..efab6d3
--- /dev/null
+++ b/src/app/admin/configuration/configuration.component.html
@@ -0,0 +1,4 @@
+
diff --git a/src/app/admin/configuration/configuration.component.scss b/src/app/admin/configuration/configuration.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/admin/configuration/configuration.component.spec.ts b/src/app/admin/configuration/configuration.component.spec.ts
new file mode 100644
index 0000000..f9a950d
--- /dev/null
+++ b/src/app/admin/configuration/configuration.component.spec.ts
@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ConfigurationComponent } from './configuration.component';
+
+describe('ConfigurationComponent', () => {
+ let component: ConfigurationComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [ConfigurationComponent]
+ });
+ fixture = TestBed.createComponent(ConfigurationComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/admin/configuration/configuration.component.ts b/src/app/admin/configuration/configuration.component.ts
new file mode 100644
index 0000000..56d6668
--- /dev/null
+++ b/src/app/admin/configuration/configuration.component.ts
@@ -0,0 +1,10 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-configuration',
+ templateUrl: './configuration.component.html',
+ styleUrls: ['./configuration.component.scss']
+})
+export class ConfigurationComponent {
+
+}
diff --git a/src/app/admin/home/home.component.html b/src/app/admin/home/home.component.html
index 53771d1..2dd4ab4 100644
--- a/src/app/admin/home/home.component.html
+++ b/src/app/admin/home/home.component.html
@@ -1,8 +1,9 @@
-
+
+
+
diff --git a/src/app/admin/home/home.component.scss b/src/app/admin/home/home.component.scss
index e69de29..5e69929 100644
--- a/src/app/admin/home/home.component.scss
+++ b/src/app/admin/home/home.component.scss
@@ -0,0 +1,5 @@
+@import "../../../styles";
+.nav {
+ background-color: $thg_orange;
+
+}
\ No newline at end of file
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index 02f5a9f..e6ac10a 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -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)},
];
diff --git a/src/app/app.component.html b/src/app/app.component.html
index bbd7a4b..518d5f2 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -1,5 +1,7 @@
-
+
+
+
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 7f8d511..a0aa6c9 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,25 +1,40 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { io, Socket } from "socket.io-client";
-import { API_URL, WEBSOCK_URL } from '../app.constants';
import { EventService } from "./services/event.service";
-import { EventStateChanged, ServerEvent } from "../types/server-event";
+import {EventStateChanged, ServerEvent, VersusBeginEvent} from "../types/server-event";
import { ApiService } from "./services/api.service";
import { ActivatedRoute, Router } from "@angular/router";
import { filter, map, takeUntil } from "rxjs/operators";
import { ToastService } from "./toast.service";
import { VoiceService } from "./services/voice.service";
-import { Subject } from "rxjs";
+import {delay, delayWhen, Subject} from "rxjs";
import { getAudioPath } from "./helper/tts.helper";
+import {animate, keyframes, style, transition, trigger} from "@angular/animations";
+import {environment} from "../environments/environment";
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
- styleUrls: ['./app.component.scss']
+ styleUrls: ['./app.component.scss'],
+ animations: [
+ trigger(
+ 'enterAnimation', [
+ transition(':enter', [
+ style({transform: 'translateX(100%)', opacity: 0}),
+ animate('500ms', style({transform: 'translateX(0)', opacity: 1}))
+ ]),
+ transition(':leave', [
+ style({transform: 'translateX(0)', opacity: 1}),
+ animate('2000ms', style({transform: 'translateX(100%)', opacity: 0}))
+ ])
+ ]
+ )]
})
export class AppComponent implements OnInit, OnDestroy {
title = 'thanksgiving';
- connection = io(WEBSOCK_URL, { transports: ['websocket']});
+ connection = io(environment.WEBSOCK_URL, { transports: ['websocket']});
destroyed = new Subject();
+ versusData: VersusBeginEvent|null = null;
audioSrc: string;
constructor(
@@ -38,10 +53,12 @@ export class AppComponent implements OnInit, OnDestroy {
console.log(data);
this.eventService.emit(data);
});
- this.apiService.getAppState('main').subscribe((result) => {
- this.router.navigate([`/${result.value}`]).then(() => {
- console.log(`navigated to ${result.value}`);
- })
+ this.apiService.getAppState('main').pipe(takeUntil(this.destroyed),delay(300)).subscribe((result) => {
+ if(this.router.url.indexOf('admin') === -1 || window.location.href.indexOf('admin') === -1) {
+ this.router.navigate([`/${result.value}`]).then(() => {
+ console.log(`navigated to ${result.value}`);
+ })
+ }
});
this.eventService.stateChangedEvent.pipe(
map(e => e.data),
@@ -55,6 +72,7 @@ export class AppComponent implements OnInit, OnDestroy {
console.log(text);
this.audioSrc = text;
})
+ this.setupVersusHandler();
}
ngOnDestroy() {
this.destroyed.complete();
@@ -63,4 +81,16 @@ export class AppComponent implements OnInit, OnDestroy {
onAudioEnded() {
this.voiceService.audioEnded();
}
+
+ private setupVersusHandler() {
+ this.eventService.versusBegin.pipe(takeUntil(this.destroyed)).subscribe(r => {
+ if (this.router.url.indexOf('admin') === -1) {
+ this.versusData = r.data;
+ }
+ })
+ this.eventService.versusEnd.pipe(takeUntil(this.destroyed)).subscribe((r) => {
+ console.log(r);
+ this.versusData = null;
+ })
+ }
}
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index f2a7954..abee8ec 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -27,6 +27,8 @@ import { AvatarComponent } from './components/avatar/avatar.component';
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: [
@@ -50,6 +52,8 @@ import { SkrepaComponent } from './components/skrepa/skrepa.component';
FinishComponent,
InitialComponent,
SkrepaComponent,
+ VersusComponent,
+ EndgamepointsComponent,
],
imports: [
BrowserModule,
diff --git a/src/app/components/answer-notification/answer-notification.component.html b/src/app/components/answer-notification/answer-notification.component.html
index 2c8d376..e9ceced 100644
--- a/src/app/components/answer-notification/answer-notification.component.html
+++ b/src/app/components/answer-notification/answer-notification.component.html
@@ -3,8 +3,10 @@
🎉 Ура, правильный ответ!
❌ А вот и нет! ❌
-
-
+
выйграл наказание
diff --git a/src/app/components/answer-notification/answer-notification.component.ts b/src/app/components/answer-notification/answer-notification.component.ts
index dc32863..c23b264 100644
--- a/src/app/components/answer-notification/answer-notification.component.ts
+++ b/src/app/components/answer-notification/answer-notification.component.ts
@@ -1,6 +1,6 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { ApiService } from "../../services/api.service";
-import { interval, Observable, Subject } from "rxjs";
+import {concatMap, interval, Observable, Subject} from "rxjs";
import { EventService } from "../../services/event.service";
import { filter, map, take, takeUntil, tap } from "rxjs/operators";
import { Participant } from "../../../types/participant";
@@ -51,14 +51,12 @@ import { VoiceService } from "../../services/voice.service";
export class AnswerNotificationComponent implements OnInit, OnDestroy {
isShown = false;
answerIsValid = false;
- participant: Participant;
- timer: Observable
;
countdown = 10;
showCountdown = false;
announceAudio = true;
+ participants: Participant[] = [];
audioSrc: string;
private destroyed$ = new Subject();
-
constructor(private apiService: ApiService, private eventService: EventService, private voiceService: VoiceService) {
this.eventService.answerReceivedEvent.pipe(
takeUntil(this.destroyed$),
@@ -68,20 +66,23 @@ export class AnswerNotificationComponent implements OnInit, OnDestroy {
takeUntil(this.destroyed$),
map(e => e.data)
).subscribe(d => this.showNotification(d.telegramId, false, d.validAnswer, null));
- this.eventService.scoreChangedEvent.pipe(
- takeUntil(this.destroyed$),
- map(e => e.data),
- ).subscribe(e => {
- if(e.telegramId === this.participant.telegramId) {
- this.participant.score = e.newScore
- }
- });
+ // this.eventService.scoreChangedEvent.pipe(
+ // takeUntil(this.destroyed$),
+ // map(e => e.data),
+ // ).subscribe(e => {
+ // if(e.telegramId === this.participant.telegramId) {
+ // this.participant.score = e.newScore
+ // }
+ // });
+
+
}
showNotification(telegramId: number, validAnswer: boolean, validAnswerValue: string, note: string|null) {
- this.countdown = validAnswer ? 10 : 5;
+ console.log(`showNotification`);
this.apiService.getParticipant(telegramId).subscribe(p => {
- this.participant = p;
+ this.countdown = validAnswer ? 10 : 5;
+ this.participants.push(p);
this.isShown = true;
this.answerIsValid = validAnswer;
const template = validAnswer ? 'announce-valid' : 'announce-invalid';
@@ -89,7 +90,10 @@ export class AnswerNotificationComponent implements OnInit, OnDestroy {
templateData['user'] = p.name;
templateData['answer'] = validAnswerValue;
templateData['user-genitive'] = p.properties.genitive;
- this.voiceService.playAudio(getAudioPathWithTemplate(template, '', templateData));
+ if(this.participants.length === 1) {
+ this.voiceService.playAudio(getAudioPathWithTemplate(template, '', templateData));
+ }
+ //this.voiceService.playAudio(getAudioPathWithTemplate(template, '', templateData));
this.voiceService.audioEndedSubject.pipe(takeUntil(this.destroyed$), take(1)).subscribe(r => {
if (note && validAnswer) {
this.voiceService.playAudio(getAudioPath(note))
@@ -101,12 +105,14 @@ export class AnswerNotificationComponent implements OnInit, OnDestroy {
}
countdownCompleted() {
+ this.participants = [];
console.log(`countdown-completed`);
this.showCountdown = false;
this.isShown = false;
this.announceAudio = false;
this.countdown = 10;
- this.apiService.continueGame().subscribe(r => console.log(r));
+ // this.apiService.continueGame().subscribe(r => console.log(r));
+
}
ngOnInit(): void {
diff --git a/src/app/components/card-played/card-played.component.ts b/src/app/components/card-played/card-played.component.ts
index c3de195..2291bb2 100644
--- a/src/app/components/card-played/card-played.component.ts
+++ b/src/app/components/card-played/card-played.component.ts
@@ -1,12 +1,11 @@
import { Component, OnInit } from '@angular/core';
import { EventService } from "../../services/event.service";
import { filter, map } from "rxjs/operators";
-import { EventCardPlayed } from "../../../types/server-event";
import { ApiService } from "../../services/api.service";
import { animate, style, transition, trigger } from "@angular/animations";
-import { API_URL } from "../../../app.constants";
import { getAudioPath } from "../../helper/tts.helper";
import { VoiceService } from "../../services/voice.service";
+import {environment} from "../../../environments/environment";
@Component({
selector: 'app-card-played',
@@ -59,7 +58,7 @@ export class CardPlayedComponent implements OnInit {
})
}
getImageUrl() {
- return `${API_URL}/guests/photo/${this.participantId}?$t=${this.imgTimestamp}`;
+ return `${environment.API_URL}/guests/photo/${this.participantId}?$t=${this.imgTimestamp}`;
}
getAudioSrc(text: string) {
diff --git a/src/app/components/cards-history/cards-history.component.scss b/src/app/components/cards-history/cards-history.component.scss
index ae0a97f..707c824 100644
--- a/src/app/components/cards-history/cards-history.component.scss
+++ b/src/app/components/cards-history/cards-history.component.scss
@@ -2,7 +2,7 @@
.cards-history {
position: fixed;
- z-index: 20000;
+ z-index: 1000;
width: 100%;
bottom: 0;
max-height: 70px;
diff --git a/src/app/components/game-pause/game-pause.component.ts b/src/app/components/game-pause/game-pause.component.ts
index f772170..663f371 100644
--- a/src/app/components/game-pause/game-pause.component.ts
+++ b/src/app/components/game-pause/game-pause.component.ts
@@ -9,7 +9,7 @@ import { getAudioPath } from "../../helper/tts.helper";
})
export class GamePauseComponent implements OnInit, OnDestroy {
tstamp = new Date().getTime();
- private interval: number;
+ private interval: NodeJS.Timeout;
constructor(private voiceService: VoiceService) { }
diff --git a/src/app/components/game-queue/game-queue.component.html b/src/app/components/game-queue/game-queue.component.html
index b0a1549..0851407 100644
--- a/src/app/components/game-queue/game-queue.component.html
+++ b/src/app/components/game-queue/game-queue.component.html
@@ -1,10 +1,14 @@
-
+
-
-
-
+
+
-
+
Ура, приз!
@@ -27,12 +31,34 @@
-
+
+
+
0 || results.invalid.length > 0)">
+
+
0">Ответили правильно
+
+
+
+
0">Не смогли
+
+
+
+
+
Результаты (не утешительные)
+ Так вышло, что никто не ответил на вопросы вообще
+
-
-
-
-
+
+
+
\ No newline at end of file
diff --git a/src/app/components/game-queue/game-queue.component.scss b/src/app/components/game-queue/game-queue.component.scss
index 7aca17c..e693b83 100644
--- a/src/app/components/game-queue/game-queue.component.scss
+++ b/src/app/components/game-queue/game-queue.component.scss
@@ -11,6 +11,10 @@
to { background-color: $thg_orange }
}
+@keyframes results {
+ from { background-color: inherit }
+ to { background-color: $thg_yellow }
+}
.queue-container {
width: 100%;
@@ -18,6 +22,7 @@
display: flex;
align-items: center;
justify-content: center;
+ z-index: 100;
}
.queue-info {
@@ -40,3 +45,16 @@ h1,h3 {
color: white;
background-color: $thg_orange;
}
+
+.results {
+ animation: results 3s 1;
+ background-color: $thg_yellow;
+ color: black;
+}
+
+.versus-container {
+ position: absolute;
+ width: 100%;
+ height: 100vh;
+ background-color: $thg_red;
+}
\ No newline at end of file
diff --git a/src/app/components/game-queue/game-queue.component.ts b/src/app/components/game-queue/game-queue.component.ts
index 868d12e..596fd11 100644
--- a/src/app/components/game-queue/game-queue.component.ts
+++ b/src/app/components/game-queue/game-queue.component.ts
@@ -3,21 +3,37 @@ import { EventGameQueue, QueueTypes } from "../../../types/server-event";
import { Participant } from "../../../types/participant";
import { ApiService } from "../../services/api.service";
import { Subject } from "rxjs";
-import { takeUntil } from "rxjs/operators";
+import {map, take, takeUntil} from "rxjs/operators";
import { Question } from "../../../types/question";
import { getAudioPath } from "../../helper/tts.helper";
import { PrizeDto } from "../../../types/prize.dto";
+
+class ResultEntity {
+ valid: {
+ user: number;
+ time: Date;
+ valid: boolean;
+ }[];
+ invalid: {
+ user: number;
+ time: Date;
+ valid: boolean;
+ }[];
+}
@Component({
selector: 'app-game-queue',
templateUrl: './game-queue.component.html',
styleUrls: ['./game-queue.component.scss']
})
+
export class GameQueueComponent implements OnInit {
@Input() action: EventGameQueue;
readonly gameQueueTypes = QueueTypes
- participant: Participant;
+ participant: Participant | null;
+ participants: Participant[] = [];
destroyed$ = new Subject
();
+ results: ResultEntity;
penalty = '';
countdown: number;
showCountdown: boolean;
@@ -31,11 +47,14 @@ export class GameQueueComponent implements OnInit {
constructor(private apiService: ApiService) { }
ngOnInit(): void {
- this.apiService.getParticipant(this.action.target).pipe(
+ if(this.action.target) {
+ this.apiService.getParticipant(this.action.target).pipe(
takeUntil(this.destroyed$)
- ).subscribe(e => {
- this.participant = e;
- });
+ ).subscribe(e => {
+ this.participant = e;
+ });
+ }
+
if(this.action.type === this.gameQueueTypes.penalty) {
this.getPenalty();
}
@@ -59,7 +78,12 @@ export class GameQueueComponent implements OnInit {
this.screpaText = this.action.text ?? '';
}
+
+ if(this.action.type == this.gameQueueTypes.showresults) {
+ this.getResults();
+ }
console.log(this.action);
+
}
getPenalty() {
@@ -98,10 +122,36 @@ export class GameQueueComponent implements OnInit {
}
private getPrize() {
+ if(!this.participant === null) {
+ return;
+ }
this.apiService.getPrize().pipe(takeUntil(this.destroyed$)).subscribe((r) => {
this.prize = r;
this.showPrize = true;
- this.prizeAudioSrc = getAudioPath(`Поздравляю, ${this.participant.name} получает ${this.prize.name}`);
+ this.prizeAudioSrc = getAudioPath(`Поздравляю, ${this.participant?.name} получает ${this.prize.name}`);
});
}
+
+ private getResults() {
+ this.apiService.getQuestionResults().pipe(takeUntil(this.destroyed$), map(result => {
+ result.map(r => {
+ this.apiService.getParticipant(r.user).pipe(takeUntil(this.destroyed$)).subscribe((particip) => {
+ if(!this.participants[r.user]) {
+ this.participants[r.user] = particip;
+ }
+ })
+ })
+ return result;
+ })
+ ).subscribe((results) => {
+ this.results = {
+ valid: [],
+ invalid: [],
+ }
+ let sortedByTime = results.sort((a,b) => a.time.getTime() - b.time.getTime());
+ this.results.valid = sortedByTime.filter((r) => r.valid);
+ this.results.invalid = sortedByTime.filter((r) => !r.valid);
+ console.log(this.results)
+ })
+ }
}
diff --git a/src/app/components/participant-item/participant-item.component.html b/src/app/components/participant-item/participant-item.component.html
index 85bf2df..b2804d4 100644
--- a/src/app/components/participant-item/participant-item.component.html
+++ b/src/app/components/participant-item/participant-item.component.html
@@ -1,4 +1,4 @@
-
+
diff --git a/src/app/components/participant-item/participant-item.component.scss b/src/app/components/participant-item/participant-item.component.scss
index dba52f1..98d6079 100644
--- a/src/app/components/participant-item/participant-item.component.scss
+++ b/src/app/components/participant-item/participant-item.component.scss
@@ -1,8 +1,8 @@
@import "../../../styles.scss";
@import url('https://fonts.googleapis.com/css2?family=Pacifico&display=swap');
.card {
- min-width: 150px;
- max-width: 150px;
+ min-width: 140px;
+ max-width: 140px;
min-height: 230px;
border: 0px solid #c2c2c2;
background: rgb(255,166,1);
@@ -11,10 +11,15 @@
padding: 0px;
}
+.transparent {
+ background: inherit;
+ max-width: 200px;
+}
+
figure {
border-radius:100%;
display:inline-block;
- margin-bottom: 15px;
+ margin-bottom: 5px;
margin-left: auto;
margin-right: auto;
}
@@ -62,7 +67,7 @@ figure {
}
.big {
- font-size: 7em;
+ font-size: 3em;
color: $thg_green;
transition-delay: 2s;
diff --git a/src/app/components/participant-item/participant-item.component.ts b/src/app/components/participant-item/participant-item.component.ts
index 10c3b8a..235716f 100644
--- a/src/app/components/participant-item/participant-item.component.ts
+++ b/src/app/components/participant-item/participant-item.component.ts
@@ -4,9 +4,9 @@ import { EventService } from "../../services/event.service";
import { Observable, Subject, Subscription } from "rxjs";
import { filter, map, takeUntil } from "rxjs/operators";
import { EventCardPlayed, EventCardsChanged, EventPhotosUpdated, ServerEvent } from "../../../types/server-event";
-import { API_URL } from "../../../app.constants";
import { ApiService } from "../../services/api.service";
import { CardItem } from "../../../types/card-item";
+import {environment} from "../../../environments/environment";
@Component({
selector: 'app-participant-item',
@@ -23,6 +23,8 @@ export class ParticipantItemComponent implements OnInit, OnDestroy, OnChanges {
imgTimestamp = (new Date()).getTime();
addAnimatedClass = false;
@Input() bannedRemaining: number|undefined = 0;
+ @Input() transparent = false;
+ @Input() shadow = true;
constructor(private eventService: EventService, private apiService: ApiService) {
}
@@ -62,13 +64,18 @@ export class ParticipantItemComponent implements OnInit, OnDestroy, OnChanges {
}
getCards() {
- this.apiService.getCards(this.participant.telegramId).subscribe((r) => {
- this.cards = r;
- })
+ if(this.participant) {
+ this.apiService.getCards(this.participant.telegramId).subscribe((r) => {
+ this.cards = r;
+ })
+ }
}
getImageUrl() {
- return `${API_URL}/guests/photo/${this.participant.telegramId}?$t=${this.imgTimestamp}`;
+ if(this.participant) {
+ return `${environment.API_URL}/guests/photo/${this.participant.telegramId}?$t=${this.imgTimestamp}`;
+ }
+ return null;
}
}
diff --git a/src/app/components/participants/participants.component.html b/src/app/components/participants/participants.component.html
index 24fe064..f59a88c 100644
--- a/src/app/components/participants/participants.component.html
+++ b/src/app/components/participants/participants.component.html
@@ -1,10 +1,9 @@
-
+
diff --git a/src/app/components/question/question.component.html b/src/app/components/question/question.component.html
index 3ad7618..a2cc5e6 100644
--- a/src/app/components/question/question.component.html
+++ b/src/app/components/question/question.component.html
@@ -1,10 +1,10 @@
-
- Вопрос
+
+ Вопрос
-
+
{{ question.text }}
@@ -15,7 +15,14 @@
+
+
+ = 0">{{ countdown }}
+
\ No newline at end of file
diff --git a/src/app/components/question/question.component.scss b/src/app/components/question/question.component.scss
index 9524995..b0bc1a7 100644
--- a/src/app/components/question/question.component.scss
+++ b/src/app/components/question/question.component.scss
@@ -25,8 +25,7 @@ section {
margin: 15px;
background: $yellow_gradient;
font-size: 1.5em;
- padding: 10px;
- padding-top: 20px;
+ padding: 20px 10px 10px;
border-radius: 23px;
p {
text-align: center;
@@ -34,3 +33,21 @@ section {
}
}
}
+
+.countdown {
+ &.warn {
+ color: $thg_red;
+ transition: color 2000ms linear, font-size 5000ms ease;
+ border-radius: 10px;
+ font-size: 3em;
+ }
+ min-width: 40px;
+ position: absolute;
+ bottom: 60px;
+ right: 20px;
+ span {
+ font-size: 3em;
+ font-weight: bold;
+ }
+ color: $thg_brown;
+}
\ No newline at end of file
diff --git a/src/app/components/question/question.component.ts b/src/app/components/question/question.component.ts
index 5fcfa98..a6e885c 100644
--- a/src/app/components/question/question.component.ts
+++ b/src/app/components/question/question.component.ts
@@ -15,6 +15,9 @@ export class QuestionComponent implements OnInit, OnDestroy {
@Input() question: Question;
destroyed$ = new Subject
();
private questionSubscription: Subscription;
+ countdownInterval:ReturnType|null= null;
+ countdown = 0;
+ readonly countDownTimer = 20;
constructor(private apiService:ApiService, private eventService: EventService, private voiceService: VoiceService) { }
@@ -24,10 +27,44 @@ export class QuestionComponent implements OnInit, OnDestroy {
return;
}
setTimeout(() => this.getQuestion(), 3000);
- this.questionSubscription = this.eventService.questionChangedEvent.subscribe(() =>{
- this.getQuestion();
+
+ this.eventService.gameQueueEvent.pipe(takeUntil(this.destroyed$)).subscribe(() => {
+ this.countdown = -1;
});
+ this.startCountdown();
+
+ this.questionSubscription = this.eventService.questionChangedEvent.subscribe(() =>{
+ this.getQuestion();
+ this.countdown = 20;
+ });
+ this.setUpVersusHandler();
+ }
+ startCountdown() {
+ this.countdown = this.countDownTimer;
+ this.countdownInterval = setInterval(() => {
+ if(this.countdown === 0) {
+ this.continueGame();
+ }
+ this.countdown--;
+ }, 1000)
+ }
+
+ setUpVersusHandler() {
+ this.eventService.versusBegin.pipe(takeUntil(this.destroyed$)).subscribe((r) => {
+ if(this.countdownInterval) {
+ clearInterval(this.countdownInterval);
+ }
+ });
+ this.eventService.versusBegin.pipe(takeUntil(this.destroyed$)).subscribe((r) => {
+ this.startCountdown();
+ })
+ }
+
+ continueGame() {
+ this.apiService.continueGame().subscribe((r) => {
+ console.log(r);
+ });
}
getQuestion() {
diff --git a/src/app/components/versus/versus.component.html b/src/app/components/versus/versus.component.html
new file mode 100644
index 0000000..9755b3d
--- /dev/null
+++ b/src/app/components/versus/versus.component.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+
{{ versusData.text}}
+
+
+
{{ versusData.description }}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app/components/versus/versus.component.scss b/src/app/components/versus/versus.component.scss
new file mode 100644
index 0000000..bbd3b21
--- /dev/null
+++ b/src/app/components/versus/versus.component.scss
@@ -0,0 +1,115 @@
+@import '../../../styles';
+@keyframes slideDown {
+ 0% {
+ top: -100vh;
+ }
+ 100% {
+ top: 0;
+ }
+}
+@keyframes slideRight {
+ 0% {
+ left: -100vh;
+ }
+ 100% {
+ left: 0;
+ }
+}
+
+@keyframes slideLeft {
+ 0% {
+ right: -100vh;
+ }
+ 100% {
+ right: 0;
+ }
+}
+
+@keyframes slideInUp {
+ 0% {
+ bottom: -100px;
+ }
+ 100% {
+ bottom: 25%;
+ }
+}
+
+@keyframes opacityIn {
+ 0% {
+ opacity: 0;
+ }
+ 100% {
+ opacity: 0.84;
+ }
+}
+
+
+
+
+.versus {
+ background-color: $thg_brown;
+ z-index: 20000;
+ position: fixed;
+ top: -100vh;
+ left: 0;
+ width: 100%;
+ height: 100vh;
+ animation: slideDown 1s ease forwards;
+}
+.versus:before {
+ content: "VS";
+ position: absolute;
+ font-size: 20vw; /* Large size for the background */
+ color: rgba(255, 255, 255, 0.2); /* Light opacity */
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ font-weight: bold;
+ z-index: 22000; /* Puts it behind other content */
+ pointer-events: none;
+}
+
+.players {
+ height: 100vh;
+}
+
+/* Left player area */
+.player-one {
+ position: relative;
+ width: 50%;
+ height: 100%;
+ background-color: #4a90e2;
+ clip-path: polygon(0 0, 100% 0, 50% 100%, 0 100%);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ color: white;
+ font-size: 2rem;
+ font-weight: bold;
+ animation: slideRight 2s ease forwards;
+}
+
+/* Right player area */
+.player-two {
+ position: relative;
+ width: 50%;
+ height: 100%;
+ background-color: #d9534f;
+ clip-path: polygon(50% 0, 100% 0, 100% 100%, 0 100%);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ color: white;
+ font-size: 2rem;
+ font-weight: bold;
+ animation: slideLeft 2s ease forwards;
+}
+
+.task {
+ position: absolute;
+ bottom: 0;
+ width: 50%;
+ background-color: white;
+ opacity: 0.84;
+ animation: slideInUp 3s ease forwards, opacityIn 1500ms linear;
+}
\ No newline at end of file
diff --git a/src/app/components/versus/versus.component.spec.ts b/src/app/components/versus/versus.component.spec.ts
new file mode 100644
index 0000000..2cad49d
--- /dev/null
+++ b/src/app/components/versus/versus.component.spec.ts
@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { VersusComponent } from './versus.component';
+
+describe('VersusComponent', () => {
+ let component: VersusComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [VersusComponent]
+ });
+ fixture = TestBed.createComponent(VersusComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/components/versus/versus.component.ts b/src/app/components/versus/versus.component.ts
new file mode 100644
index 0000000..f938d6a
--- /dev/null
+++ b/src/app/components/versus/versus.component.ts
@@ -0,0 +1,57 @@
+import {Component, Input, OnDestroy, OnInit} from '@angular/core';
+import {ApiService} from "../../services/api.service";
+import {combineLatest, Subject} from "rxjs";
+import {Participant} from "../../../types/participant";
+import {VersusItem} from "../../../types/versus-item";
+import {VoiceService} from "../../services/voice.service";
+import {getAudioPath} from "../../helper/tts.helper";
+import {takeUntil} from "rxjs/operators";
+
+@Component({
+ selector: 'app-versus',
+ templateUrl: './versus.component.html',
+ styleUrls: ['./versus.component.scss']
+})
+export class VersusComponent implements OnInit, OnDestroy{
+ @Input() player1: number;
+ @Input() player2: number;
+ player1data: Participant;
+ player2data: Participant;
+ destroyed$ = new Subject();
+ playersLoaded = false;
+ versusData: VersusItem | null = null;
+
+ constructor(private apiService: ApiService, private voiceService: VoiceService) {
+ }
+ ngOnInit() {
+ this.loadPlayersData();
+ this.loadTask();
+ this.playAudio();
+ }
+ ngOnDestroy() {
+ this.destroyed$.complete();
+ }
+
+ playAudio() {
+ this.voiceService.playAudio(getAudioPath('Схватка!'));
+ }
+
+ loadPlayersData() {
+ const player1Data$ = this.apiService.getParticipant(this.player1);
+ const player2Data$ = this.apiService.getParticipant(this.player2);
+ combineLatest([player1Data$,player2Data$]).pipe(takeUntil(this.destroyed$)).subscribe(([d1,d2]) => {
+ this.player1data = d1;
+ this.player2data = d2;
+ this.playersLoaded = true;
+ })
+ }
+
+ private loadTask() {
+ setTimeout(() => {
+ this.apiService.getVersus().pipe(takeUntil(this.destroyed$)).subscribe((r) => {
+ this.versusData = r;
+ })
+ }, 1500);
+
+ }
+}
diff --git a/src/app/helper/tts.helper.ts b/src/app/helper/tts.helper.ts
index f6f0090..0cd8eee 100644
--- a/src/app/helper/tts.helper.ts
+++ b/src/app/helper/tts.helper.ts
@@ -1,9 +1,10 @@
-import { API_URL } from "../../app.constants";
+import {environment} from "../../environments/environment";
+
export function getAudioPath(text: string, voice: number = 1) {
- return `${API_URL}/voice/tts?text=${text}&voice=${voice}`;
+ return `${environment.API_URL}/voice/tts?text=${text}&voice=${voice}`;
}
export function getAudioPathWithTemplate(path: string, text: string, vars: { [index: string]: string }) {
const t = new Date().getTime();
- return `${API_URL}/voice/${path}?text=${text}&vars=${JSON.stringify(vars)}&t=${t}`;
+ return `${environment.API_URL}/voice/${path}?text=${text}&vars=${JSON.stringify(vars)}&t=${t}`;
}
diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts
index cb0332d..ee603ca 100644
--- a/src/app/services/api.service.ts
+++ b/src/app/services/api.service.ts
@@ -1,7 +1,6 @@
import { Injectable } from '@angular/core';
import { HttpClient } from "@angular/common/http";
import { Observable } from "rxjs";
-import { API_URL } from "../../app.constants";
import { AppState } from "../../types/app-state";
import { Participant } from "../../types/participant";
import { Question } from "../../types/question";
@@ -9,84 +8,172 @@ import { CardItem } from "../../types/card-item";
import { GameState } from "./gameState";
import { PenaltyDto } from "../../types/penalty.dto";
import { PrizeDto } from "../../types/prize.dto";
+import {QuestionresultsDto} from "../../types/questionresults.dto";
+import {map} from "rxjs/operators";
+import {VersusItem} from "../../types/versus-item";
+import {environment} from "../../environments/environment";
+
+export class FeatureFlagStateDto {
+ name: string;
+ state: boolean;
+}
+
+export interface StateInformationDto {
+ key: string;
+ value: { key: string; value: T };
+}
+
+export interface StateInformationVersusDto {
+ action: any;
+ player1: number;
+ player2: number;
+}
+
+export interface ConfigRecordDto {
+ key: string;
+ 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'
})
export class ApiService {
- constructor(private httpClient: HttpClient) { }
+ constructor(private httpClient: HttpClient) {
+ console.log(environment.API_URL);
+ }
public getAppState(state: string): Observable {
- return this.httpClient.get(`${API_URL}/state/${state}`);
+ return this.httpClient.get(`${environment.API_URL}/state/${state}`);
}
public getParticipants(): Observable {
- return this.httpClient.get(`${API_URL}/guests`);
+ return this.httpClient.get(`${environment.API_URL}/guests`);
}
public getParticipant(id: number): Observable {
- return this.httpClient.get(`${API_URL}/guests/${id}`);
+ return this.httpClient.get(`${environment.API_URL}/guests/${id}`);
}
public getQuestion(): Observable {
- return this.httpClient.get(`${API_URL}/quiz`);
+ return this.httpClient.get(`${environment.API_URL}/quiz`);
}
public setAppState(state: string, value: string) {
- return this.httpClient.post(`${API_URL}/state`, {
+ return this.httpClient.post(`${environment.API_URL}/state`, {
state,
value
});
}
getCards(telegramId: number): Observable {
- return this.httpClient.get(`${API_URL}/cards/${telegramId}`);
+ return this.httpClient.get(`${environment.API_URL}/cards/${telegramId}`);
}
continueGame() {
console.log(`continue game`);
- return this.httpClient.post(`${API_URL}/quiz/proceed`, {});
+ return this.httpClient.post(`${environment.API_URL}/quiz/proceed`, {});
+ }
+
+ questionTimeout() {
+ console.log(`continue game`);
+ return this.httpClient.post(`${environment.API_URL}/quiz/timeout`, {});
}
markQueueAsCompleted(_id: string) {
- return this.httpClient.post(`${API_URL}/game/${_id}/complete`, {});
+ return this.httpClient.post(`${environment.API_URL}/game/${_id}/complete`, {});
}
pauseGame() {
- return this.httpClient.post(`${API_URL}/game/pause`, {});
+ return this.httpClient.post(`${environment.API_URL}/game/pause`, {});
}
resumeGame() {
- return this.httpClient.post(`${API_URL}/game/resume`, {});
+ return this.httpClient.post(`${environment.API_URL}/game/resume`, {});
}
getGameState() {
- return this.httpClient.get(`${API_URL}/game/state`);
+ return this.httpClient.get(`${environment.API_URL}/game/state`);
}
getPenalty() {
console.log(`get penalty`);
- return this.httpClient.get(`${API_URL}/penalty`);
+ return this.httpClient.get(`${environment.API_URL}/penalty`);
}
playExtraCards() {
console.log(`play extra cards`);
- return this.httpClient.get(`${API_URL}/game/playextracards`);
+ return this.httpClient.get(`${environment.API_URL}/game/playextracards`);
}
getAdditionalQuestion(target: number) {
- return this.httpClient.post(`${API_URL}/quiz/extraquestion`, {
+ return this.httpClient.post(`${environment.API_URL}/quiz/extraquestion`, {
telegramId: target,
});
}
getImageUrl(id: number) {
const timestamp = new Date().getTime();
- return `${API_URL}/guests/photo/${id}?$t=${timestamp}}`;
+ return `${environment.API_URL}/guests/photo/${id}?$t=${timestamp}}`;
}
getPrize(): Observable {
- return this.httpClient.get(`${API_URL}/gifts`);
+ return this.httpClient.get(`${environment.API_URL}/gifts`);
+ }
+
+ getQuestionResults() {
+ return this.httpClient.get(`${environment.API_URL}/quiz/question-results`).pipe(map((data) =>
+ data.map((item) => {
+ return {
+ ...item,
+ time: new Date(item.time)
+ }
+ })
+ ));
+ }
+
+ getFeatureFlagState(feature: string) {
+ return this.httpClient.get(`${environment.API_URL}/featureflag/${feature}`);
+ }
+
+ setFeatureFlagState(feature: string, state: boolean) {
+ return this.httpClient.post(`${environment.API_URL}/featureflag`, { name: feature, state: state });
+ }
+
+ getStateDetails() {
+ return this.httpClient.get(`${environment.API_URL}/game/state-details`);
+ }
+
+ getVersus() {
+ return this.httpClient.get(`${environment.API_URL}/versus`);
+ }
+
+ completeVersus(winner: number, loser: number) {
+ return this.httpClient.post(`${environment.API_URL}/versus/complete`, {
+ winner: winner,
+ loser: loser
+ });
+ }
+
+ getEndgameResults() {
+ return this.httpClient.get(`${environment.API_URL}/quiz/endgame-results`)
}
}
diff --git a/src/app/services/event.service.ts b/src/app/services/event.service.ts
index 261b0e5..c963857 100644
--- a/src/app/services/event.service.ts
+++ b/src/app/services/event.service.ts
@@ -9,7 +9,7 @@ import {
EventUserAdded,
EventWrongAnswerReceived,
QuestionChangedEvent,
- ServerEvent, UserPropertyChanged
+ ServerEvent, UserPropertyChanged, VersusBeginEvent
} from "../../types/server-event";
@Injectable({
@@ -31,6 +31,9 @@ export class EventService {
public gameResumed = new EventEmitter>();
public notificationEvent = new EventEmitter>();
public userPropertyChanged = new EventEmitter>();
+ public featureFlagChanged = new EventEmitter>()
+ public versusBegin = new EventEmitter>();
+ public versusEnd = new EventEmitter>
constructor() { }
public emit(event: ServerEvent) {
@@ -81,6 +84,15 @@ export class EventService {
case "user_property_changed":
this.userPropertyChanged.emit(event as ServerEvent);
break;
+ case "feature_flag_changed":
+ this.featureFlagChanged.emit(event);
+ break;
+ case "begin_versus":
+ this.versusBegin.emit(event as ServerEvent);
+ break;
+ case "end_versus":
+ this.versusEnd.emit(event as ServerEvent<{winner: number}>);
+ break;
}
}
}
diff --git a/src/app/services/testing-api.service.ts b/src/app/services/testing-api.service.ts
new file mode 100644
index 0000000..a5a16c0
--- /dev/null
+++ b/src/app/services/testing-api.service.ts
@@ -0,0 +1,35 @@
+import { Injectable } from '@angular/core';
+import {HttpClient} from "@angular/common/http";
+import {environment} from "../../environments/environment";
+
+@Injectable({
+ providedIn: 'root'
+})
+export class TestingApiService {
+
+ constructor(private httpClient: HttpClient) { }
+
+ public simulateVersus() {
+ return this.httpClient.post(`${environment.API_URL}/versus/simulate-versus`, {});
+ }
+
+ resetAllVersusTasksAsIncompleted() {
+ return this.httpClient.post(`${environment.API_URL}/versus/reset-all`, {});
+ }
+
+ resetAllPlayersScore() {
+ return this.httpClient.post(`${environment.API_URL}/guests/reset-score`, {});
+ }
+
+ clearGameQueue() {
+ return this.httpClient.post(`${environment.API_URL}/game/clear-queue`, {});
+ }
+
+ simulateEndGamePoints() {
+ return this.httpClient.post(`${environment.API_URL}/quiz/calculate-endgame-extrapoints`, {})
+ }
+
+ simulateValidAnswer() {
+ return this.httpClient.post(`${environment.API_URL}/game/simulate-valid-answer`, {});
+ }
+}
diff --git a/src/app/services/testingapi.service.spec.ts b/src/app/services/testingapi.service.spec.ts
new file mode 100644
index 0000000..65c79e7
--- /dev/null
+++ b/src/app/services/testingapi.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { TestingApiService } from './testing-api.service';
+
+describe('TestingapiService', () => {
+ let service: TestingApiService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(TestingApiService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/src/app/services/voice.service.ts b/src/app/services/voice.service.ts
index 3341ca6..253061d 100644
--- a/src/app/services/voice.service.ts
+++ b/src/app/services/voice.service.ts
@@ -1,14 +1,24 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from "@angular/common/http";
-import { API_URL } from "../../app.constants";
-import { Subject } from "rxjs";
+import {delay, delayWhen, interval, Observable, of, Subject} from "rxjs";
+import {ApiService} from "./api.service";
+import {takeUntil, tap} from "rxjs/operators";
+import {SharedMethods} from "../shared/sharedmethods";
+import {environment} from "../../environments/environment";
@Injectable({
providedIn: 'root'
})
export class VoiceService {
+ destroyed$ = new Subject();
+ voiceDisabled = false;
- constructor(private httpClient: HttpClient) { }
+ constructor(private httpClient: HttpClient, private apiService: ApiService) {
+ this.apiService.getFeatureFlagState("DisableVoice").pipe(takeUntil(this.destroyed$))
+ .subscribe((result) => {
+ this.voiceDisabled = result.state;
+ })
+ }
public voiceSubject = new Subject();
public audioEndedSubject = new Subject();
@@ -17,12 +27,29 @@ export class VoiceService {
this.voiceSubject.next(url);
}
+ playAudio$(url: string) {
+ this.voiceSubject.next(url);
+ return new Observable((observer) => {
+ if(this.voiceDisabled) {
+ observer.next(null);
+ observer.complete();
+ }
+ const subscription = this.audioEndedSubject.subscribe({
+ next: () => {
+ observer.next(null);
+ observer.complete();
+ }
+ });
+ return () => subscription.unsubscribe();
+ }).pipe(delayWhen(val => this.voiceDisabled ? interval(5000) : interval(0)));
+ }
+
getAudioUrl(text: string,voice: number = 1) {
- return `${API_URL}/voice/tts?voice=${voice}&text=${text}`
+ return `${environment.API_URL}/voice/tts?voice=${voice}&text=${text}`
}
getAudioUrlSSML(text: string) {
- return `${API_URL}/voice/ssml?text=${encodeURI(text)}`
+ return `${environment.API_URL}/voice/ssml?text=${encodeURI(text)}`
}
audioEnded() {
diff --git a/src/app/shared/featureflags.ts b/src/app/shared/featureflags.ts
new file mode 100644
index 0000000..f77ba4d
--- /dev/null
+++ b/src/app/shared/featureflags.ts
@@ -0,0 +1,10 @@
+export class FeatureFlagList {
+ static readonly FeatureFlags: string[] = [
+ "EnableEndgamePoints",
+ "DontMarkQuestionsAsCompleted",
+ "DisableVoice",
+ "ProdMode",
+ "EndgamePointsUseCssAnimation",
+ "StartVersusIfPlayersAnsweredInSameTime",
+ ];
+}
\ No newline at end of file
diff --git a/src/app/shared/sharedmethods.ts b/src/app/shared/sharedmethods.ts
new file mode 100644
index 0000000..37be26b
--- /dev/null
+++ b/src/app/shared/sharedmethods.ts
@@ -0,0 +1,5 @@
+export class SharedMethods {
+ static sleep(ms: number) {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+ }
+}
\ No newline at end of file
diff --git a/src/app/views/endgamepoints/endgamepoints.component.html b/src/app/views/endgamepoints/endgamepoints.component.html
new file mode 100644
index 0000000..edae027
--- /dev/null
+++ b/src/app/views/endgamepoints/endgamepoints.component.html
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+ Время наградить особо отличившихся!
+
+
+
+ За тяжелую судьбу +2
+
+
+
+
+
+ За полный успех -2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Время наградить особо отличившихся!
+
+
+
+ За тяжелую судьбу
+2
+
+
+
+
+
+ За полный успех
−2
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app/views/endgamepoints/endgamepoints.component.scss b/src/app/views/endgamepoints/endgamepoints.component.scss
new file mode 100644
index 0000000..102b8a9
--- /dev/null
+++ b/src/app/views/endgamepoints/endgamepoints.component.scss
@@ -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;
+}
\ No newline at end of file
diff --git a/src/app/views/endgamepoints/endgamepoints.component.spec.ts b/src/app/views/endgamepoints/endgamepoints.component.spec.ts
new file mode 100644
index 0000000..f4f8e6e
--- /dev/null
+++ b/src/app/views/endgamepoints/endgamepoints.component.spec.ts
@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { EndgamepointsComponent } from './endgamepoints.component';
+
+describe('EndgamepointsComponent', () => {
+ let component: EndgamepointsComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [EndgamepointsComponent]
+ });
+ fixture = TestBed.createComponent(EndgamepointsComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/views/endgamepoints/endgamepoints.component.ts b/src/app/views/endgamepoints/endgamepoints.component.ts
new file mode 100644
index 0000000..a7e7e05
--- /dev/null
+++ b/src/app/views/endgamepoints/endgamepoints.component.ts
@@ -0,0 +1,94 @@
+import {Component, OnInit} from '@angular/core';
+import {ApiService, EndgameResultsDto, FeatureFlagStateDto} from "../../services/api.service";
+import {
+ Subject,
+ firstValueFrom,
+ combineLatest,
+ merge
+} from "rxjs";
+import { takeUntil} from "rxjs/operators";
+import {VoiceService} from "../../services/voice.service";
+import {animate, 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;
+ }
+}
diff --git a/src/app/views/finish/finish.component.html b/src/app/views/finish/finish.component.html
index 6c9eb45..064cbb8 100644
--- a/src/app/views/finish/finish.component.html
+++ b/src/app/views/finish/finish.component.html
@@ -15,6 +15,6 @@
diff --git a/src/app/views/onboarding/onboarding.component.html b/src/app/views/onboarding/onboarding.component.html
index 11b6408..071a83c 100644
--- a/src/app/views/onboarding/onboarding.component.html
+++ b/src/app/views/onboarding/onboarding.component.html
@@ -22,11 +22,11 @@
Если тебе требуется пояснять эту карточку - то игра не для тебя, налей себе алкоголь и побольше.
-
-

+
+
-
Говнокарта
-
Можно подкинуть еще один вопрос игроку, который правильно ответил.
+
Поединок
+
Можно вызвать другого игрока на схватку 1 на 1 в мини-игре
@@ -54,7 +54,7 @@
Заблокировать игрока
-
Запрещает игроку давать ответы в следующих двух раундах
+
Запрещает игроку давать ответы в случайном количестве раундов
diff --git a/src/app/views/onboarding/onboarding.component.ts b/src/app/views/onboarding/onboarding.component.ts
index beee989..ced4203 100644
--- a/src/app/views/onboarding/onboarding.component.ts
+++ b/src/app/views/onboarding/onboarding.component.ts
@@ -33,7 +33,7 @@ interface RuleItem {
export class OnboardingComponent implements OnInit, OnDestroy {
@ViewChild('avoidPenaltyCard') private avoidPenaltyCardEl: ElementRef;
@ViewChild('stolePrizeCard') private stolePrizeCardEl: ElementRef;
- @ViewChild('shitCard') private shitCardEl: ElementRef;
+ @ViewChild('versusCard') private versusCardEl: ElementRef;
@ViewChild('luckyCard') private luckyCardEl: ElementRef;
@ViewChild('banPlayerCard') private banPlayerEl: ElementRef;
@ViewChild('doubleTreasureCard') private doubleTreasureCardEl: ElementRef;
@@ -42,30 +42,25 @@ export class OnboardingComponent implements OnInit, OnDestroy {
{ text: 'Вопросы и ответы будут отображаться на экране и в Боте Благодарения.' },
{ text: 'Каждый игрок в начале игры имеет на руках 4 карты, набор карт определяется случайно. Описание карт ты найдешь ниже. После использования карты ты получаешь новую случайную карту.' },
{ text: 'На разыгрывание карты время ограничено, примерно 10 секунд.' },
- { text: 'Задача игрока - ответить правильно и быстрее других.' },
- { text: 'Первый игрок, ответивший правильно, получает одно очко и шанс выиграть приз.' },
- { text: 'Приз??? Какой приз?', screpa: true, voice: 2 },
- { text: 'Я не думаю, что их мозгов хватит для получения призов', screpa: true, voice: 2 },
- { text: 'А ты вообще кто такая?', hideWithoutVoice: true },
- { text: 'Ах, да.. простите, забыла представиться, я - Скрепа по фамилии Духовная', screpa: true, voice: 2},
- { text: 'И на кой ты нам нужна?', hideWithoutVoice: true },
- { text: 'Я тут, чтобы нарушать ход игры, и вообще тебя не спрашивали. ', screpa: true, voice: 2},
- { text: '[Ладно, ]В общем - чем больше правильных ответов - тем больше призов '},
- { text: 'Первый игрок, ответивший неправильно, получает наказание, и мы переходим к следующему вопросу' },
+ { text: 'Вы долго просили оптимизировать геймплей для медленных и глупых, и мы это сделали!'},
+ { text: 'Задача игрока - ответить правильно и быстрее других, ну или хотя бы просто правильно в течение 20 секунд' },
+ { text: 'Первый игрок, ответивший правильно, получает два очка' },
+ { text: 'Все остальные, ответившие правильно, получают одно очко'},
+ { text: 'Иногда за неправильные ответы игроки будут получать наказания' },
{ text: 'Избежать наказания можно только с помощью соотвествуещей карты, данную карту ты можешь сыграть перед озвучиванием наказания', action: () => {
this.shakeCard(this.avoidPenaltyCardEl);
}},
{ text: 'Карту "украсть приз" ты можешь сыграть в момент, когда кто-то собирается получить награду, но до момента того, как ты узнаешь, что это именно за приз', action: () => {
this.shakeCard(this.stolePrizeCardEl);
}},
- { text: '"Говно-карту" ты можешь разыграть в момент, когда кто-то ответил правильно, тем самым ты заставишь именно этого игрока ответить на один дополнительный вопрос. На одного игрока можно сыграть неограниченное количество этих карт', action: () => {
- this.shakeCard(this.shitCardEl);
+ { text: 'Карту "Поединок" ты можешь разыграть в любой момент, чтобы вызвать игрока на дуэль', action: () => {
+ this.shakeCard(this.versusCardEl);
}},
{ text: '"Лаки карту" ты сможешь сыграть после своего правильного ответа, она увеличит твои шансы на получение приза', action: () => {
this.shakeCard(this.luckyCardEl);
}},
{
- text: 'Карту бана можно сыграть на любого игрока (даже себя), чтобы отправить его на пару ходов во Владикавказ. Игрок не сможет участвовать в игре в течение двух раундов',
+ text: 'Карту бана можно сыграть на любого игрока (даже себя), чтобы отправить его на пару ходов во Владикавказ. Игрок не сможет участвовать в случайном количестве раундов',
action: () => {
this.shakeCard(this.banPlayerEl);
}
@@ -78,22 +73,10 @@ export class OnboardingComponent implements OnInit, OnDestroy {
},
{ text: 'Не торопись с ответами, игра идет до той поры, пока мы не разыграем все призы' },
{
- text: 'Ах да, так как создатель игры, работает на Microsoft, мы добавили привычные вам баги, сейчас расскажу',
- screpa: true,
- voice: 2
+ text: 'Ах да, так как создатель игры, работает на Microsoft, мы добавили привычные вам баги, но все их оставим в секрете',
},
{
- text: 'Если у вас нет вариантов ответа - перезайдите в бот или перезапустите телеграмм',
- screpa: true,
- voice: 2,
- },
- {
- text: 'Остальные баги будут сюрпризом! Но не забывайте громко кричать, когда что-то работает не так',
- screpa: true,
- voice: 2,
- },
- {
- text: 'Кажется правила закончились'
+ text: 'Кажется, правила закончились'
}
];
@@ -114,11 +97,10 @@ export class OnboardingComponent implements OnInit, OnDestroy {
}
ngOnDestroy(): void {
this.destroyed$.complete();
- this.voiceSubscription.unsubscribe();
+ this.voiceSubscription?.unsubscribe();
}
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 +124,6 @@ export class OnboardingComponent implements OnInit, OnDestroy {
}
stopShaking(card: ElementRef) {
- console.log(`stop shacking`);
this.renderer.removeClass(card.nativeElement, 'shake');
}
@@ -174,7 +155,7 @@ export class OnboardingComponent implements OnInit, OnDestroy {
this.allRulesAnnounced = true;
this.voiceService.playAudio(getAudioPath(`Это все правила, надеюсь, все понятно. А если нет - сейчас Кирилл и Оксана вам все пояснят,
ну и совсем для тупых - пустила по кругу правила на экране,
- а если ты их не поймешь - то за Путина голосовать пойдешь (или за Грузинскую мечту) . Каждый правильный ответ отнимает у Путина год жизни, постарайтесь!`));
+ а если ты их не поймешь - то очень жаль тебя глупенького`));
this.voiceService.audioEndedSubject.pipe(takeUntil(this.destroyed$),take(1)).subscribe(() => {
setInterval(() => { this.playNextRule() }, 6000);
this.currentRulePosition = 0
diff --git a/src/assets/cards/VersusCard.png b/src/assets/cards/VersusCard.png
new file mode 100644
index 0000000..365aa36
Binary files /dev/null and b/src/assets/cards/VersusCard.png differ
diff --git a/src/assets/endgame/1469-147538044.mp4 b/src/assets/endgame/1469-147538044.mp4
new file mode 100644
index 0000000..771f3d3
Binary files /dev/null and b/src/assets/endgame/1469-147538044.mp4 differ
diff --git a/src/assets/endgame/48569-454825064.mp4 b/src/assets/endgame/48569-454825064.mp4
new file mode 100644
index 0000000..2199982
Binary files /dev/null and b/src/assets/endgame/48569-454825064.mp4 differ
diff --git a/src/assets/endgame/energetic-bgm-242515.mp3 b/src/assets/endgame/energetic-bgm-242515.mp3
new file mode 100644
index 0000000..c7308e9
Binary files /dev/null and b/src/assets/endgame/energetic-bgm-242515.mp3 differ
diff --git a/src/assets/finalcaption.mov b/src/assets/finalcaption.mov
new file mode 100644
index 0000000..123a32c
Binary files /dev/null and b/src/assets/finalcaption.mov differ
diff --git a/src/assets/qr-code.svg b/src/assets/qr-code.svg
index 3376fef..57d0e90 100644
--- a/src/assets/qr-code.svg
+++ b/src/assets/qr-code.svg
@@ -1,148 +1,1156 @@
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/assets/versus/cinematical-epic-loop-190906.mp3 b/src/assets/versus/cinematical-epic-loop-190906.mp3
new file mode 100644
index 0000000..0ae2c1b
Binary files /dev/null and b/src/assets/versus/cinematical-epic-loop-190906.mp3 differ
diff --git a/src/dicts/voice.dicts.ts b/src/dicts/voice.dicts.ts
new file mode 100644
index 0000000..5a745ba
--- /dev/null
+++ b/src/dicts/voice.dicts.ts
@@ -0,0 +1,4 @@
+export const VoiceDict = {
+
+
+}
\ No newline at end of file
diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts
index 3612073..b8d4a3e 100644
--- a/src/environments/environment.prod.ts
+++ b/src/environments/environment.prod.ts
@@ -1,3 +1,5 @@
export const environment = {
- production: true
+ production: true,
+ API_URL: "https://thanksgiving2024.ngweb.io/api",
+ WEBSOCK_URL: "https://thanksgiving2024.ngweb.io"
};
diff --git a/src/environments/environment.ts b/src/environments/environment.ts
index f56ff47..3062ec4 100644
--- a/src/environments/environment.ts
+++ b/src/environments/environment.ts
@@ -3,7 +3,9 @@
// The list of file replacements can be found in `angular.json`.
export const environment = {
- production: false
+ production: false,
+ API_URL: "http://localhost:3000",
+ WEBSOCK_URL: "http://localhost:3000"
};
/*
diff --git a/src/types/questionresults.dto.ts b/src/types/questionresults.dto.ts
new file mode 100644
index 0000000..a2969df
--- /dev/null
+++ b/src/types/questionresults.dto.ts
@@ -0,0 +1,5 @@
+export class QuestionresultsDto {
+ user: number;
+ time: Date;
+ valid: boolean;
+}
\ No newline at end of file
diff --git a/src/types/server-event.ts b/src/types/server-event.ts
index ba41064..ac762bb 100644
--- a/src/types/server-event.ts
+++ b/src/types/server-event.ts
@@ -6,6 +6,8 @@ export enum QueueTypes {
penalty = 'penalty',
playExtraCard = 'play_extra_card',
screpa = 'screpa',
+ showresults = 'show_results',
+ versus = 'versus',
}
export interface EventPhotosUpdated {
@@ -50,6 +52,13 @@ export interface EventScoreChanged {
newScore: number;
}
+export interface VersusBeginEvent {
+ player1: number;
+ player2: number;
+ player1name: string;
+ player2name: string;
+}
+
export interface EventGameQueue {
text?: string;
target: number;
@@ -86,5 +95,8 @@ export interface ServerEvent {
| 'game_resumed'
| 'notification'
| 'user_property_changed'
+ | 'feature_flag_changed'
+ | 'begin_versus'
+ | 'end_versus'
data: T
}
diff --git a/src/types/versus-item.ts b/src/types/versus-item.ts
new file mode 100644
index 0000000..a5e3dfd
--- /dev/null
+++ b/src/types/versus-item.ts
@@ -0,0 +1,6 @@
+export interface VersusItem {
+ text: string;
+ name: string;
+ completed: boolean;
+ description: string;
+}
\ No newline at end of file
diff --git a/tsconfig.app.json b/tsconfig.app.json
index 82d91dc..b120c1d 100644
--- a/tsconfig.app.json
+++ b/tsconfig.app.json
@@ -3,7 +3,7 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
- "types": []
+ "types": ["node"]
},
"files": [
"src/main.ts",