Fixes a bunch of bugs and upgrades to angular 7

parent 9366678e
Pipeline #21120 failed with stages
in 7 minutes and 9 seconds
......@@ -10,8 +10,8 @@
"scripts": {
"ng": "ng",
"start:SSR": "cd dist && node server",
"start:DEV": "ng serve --host 0.0.0.0 --port 4200 --disable-host-check --aot",
"build:DEV": "ng serve --host 0.0.0.0 --port 4200 --disable-host-check --aot --prod",
"start:DEV": "ng serve --host 0.0.0.0 --port 4210 --disable-host-check --aot",
"build:DEV": "ng serve --host 0.0.0.0 --port 4210 --disable-host-check --aot --prod",
"build:SSR": "npm run build:PROD && npm run purify && npm run build:SERVER && npm run webpack:SERVER",
"build:SERVER": "ng run frontend:server",
"build:PROD": "ng build --prod",
......@@ -28,82 +28,81 @@
"http-startup": "http-server dist/browser/ -p 4711",
"prod-test": "npm run build:PROD && npm run purify && npm run compress && npm run http-startup",
"prod-test:SSR": "npm run build:SSR && npm run start:SSR",
"job:images:logo": "cd dist/jobs/; node --experimental-modules GenerateImages.mjs --command=generateLogoImages",
"job:images:frontend": "cd dist/jobs/; node --experimental-modules GenerateImages.mjs --command=generateFrontendPreview",
"job:images": "cd dist/jobs/; node --experimental-modules GenerateImages.mjs --command=all"
"job:images:logo": "cd dist/browser/assets/jobs/; node --experimental-modules GenerateImages.mjs --command=generateLogoImages",
"job:images:frontend": "cd dist/browser/assets/jobs/; node --experimental-modules GenerateImages.mjs --command=generateFrontendPreview --host=http://localhost:4210",
"job:images": "cd dist/browser/assets/jobs/; node --experimental-modules GenerateImages.mjs --command=all"
},
"private": true,
"dependencies": {
"@angular/animations": "^6.1.1",
"@angular/common": "^6.1.1",
"@angular/compiler": "^6.1.1",
"@angular/core": "^6.1.1",
"@angular/forms": "^6.1.1",
"@angular/http": "^6.1.1",
"@angular/platform-browser": "^6.1.1",
"@angular/platform-browser-dynamic": "^6.1.1",
"@angular/platform-server": "^6.1.1",
"@angular/router": "^6.1.1",
"@angular/service-worker": "^6.1.1",
"@ng-bootstrap/ng-bootstrap": "^2.2.1",
"@angular/animations": "^7.1.1",
"@angular/common": "^7.1.1",
"@angular/compiler": "^7.1.1",
"@angular/core": "^7.1.1",
"@angular/forms": "^7.1.1",
"@angular/http": "^7.1.1",
"@angular/platform-browser": "^7.1.1",
"@angular/platform-browser-dynamic": "^7.1.1",
"@angular/platform-server": "^7.1.1",
"@angular/router": "^7.1.1",
"@angular/service-worker": "^7.1.1",
"@ng-bootstrap/ng-bootstrap": "^4.0.0",
"@ng-bootstrap/schematics": "^2.0.0-alpha.1",
"@nguniversal/express-engine": "^6.0.0",
"@nguniversal/module-map-ngfactory-loader": "^6.0.0",
"@ngx-translate/core": "^10.0.2",
"@ngx-translate/http-loader": "~3.0.1",
"@nguniversal/express-engine": "^7.0.2",
"@nguniversal/module-map-ngfactory-loader": "^7.0.2",
"@ngx-translate/core": "^11.0.1",
"@ngx-translate/http-loader": "~4.0.0",
"@techiediaries/ngx-qrcode": "0.0.5",
"angulartics2": "^6.2.0",
"angulartics2": "^7.2.1",
"bootstrap": "^4.1.3",
"chrome-launcher": "^0.10.2",
"chrome-launcher": "^0.10.5",
"classlist.js": "^1.1.20150312",
"cors": "^2.8.4",
"highlight.js": "^9.12.0",
"cors": "^2.8.5",
"highlight.js": "^9.13.1",
"intro.js": "^2.9.3",
"marked": "git+https://github.com/trayhem/marked.git",
"messageformat": "^2.0.4",
"ngx-qrcode2": "^0.1.0",
"ngx-translate-messageformat-compiler": "^4.1.3",
"rxjs": "^6.2.2",
"ts-loader": "^4.3.0",
"ngx-translate-messageformat-compiler": "^4.4.0",
"rxjs": "^6.3.3",
"ts-loader": "^5.3.1",
"zone.js": "^0.8.26",
"@auth0/angular-jwt": "2.0.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.7.2",
"@angular/cli": "^6.1.2",
"@angular/compiler-cli": "^6.1.1",
"@angular/language-service": "^6.1.1",
"@angular-devkit/build-angular": "^0.11.0",
"@angular/cli": "^7.1.0",
"@angular/compiler-cli": "^7.1.1",
"@angular/language-service": "^7.1.1",
"@types/intro.js": "^2.4.3",
"@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.3",
"@types/node": "^10.5.5",
"@types/webpack": "~4.4.9",
"@types/jasmine": "~3.3.0",
"@types/jasminewd2": "~2.0.6",
"@types/node": "^10.12.10",
"@types/webpack": "~4.4.20",
"arsnova-click-v2-types": "git+https://git.thm.de/arsnova/arsnova-click-v2-types.git",
"chrome-remote-interface": "^0.26.1",
"codelyzer": "^4.4.2",
"gm": "^1.23.1",
"chrome-remote-interface": "^0.27.0",
"codelyzer": "^4.5.0",
"http-server": "^0.11.1",
"imagemin": "^5.3.1",
"imagemin-pngquant": "^5.1.0",
"jasmine-core": "~3.1.0",
"imagemin": "^6.0.0",
"imagemin-pngquant": "^6.0.0",
"jasmine-core": "~3.3.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~2.0.5",
"karma": "~3.1.1",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~1.1.2",
"karma-jasmine-html-reporter": "^1.2.0",
"karma-coverage-istanbul-reporter": "~2.0.4",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.0",
"karma-mocha-reporter": "^2.2.5",
"karma-phantomjs-launcher": "^1.0.4",
"minimist": "^1.2.0",
"mkdirp": "^0.5.1",
"protractor": "^5.4.0",
"protractor": "^5.4.1",
"purify-css": "~1.2.5",
"ts-node": "~6.0.3",
"tsickle": ">=0.25.5",
"tslib": "^1.7.1",
"tslint": "~5.10.0",
"typescript": "^2.7",
"webpack-cli": "^2.1.4",
"sharp": "^0.21.0",
"ts-node": "~7.0.1",
"tslib": "^1.9.3",
"tslint": "~5.11.0",
"typescript": "^3.1.6",
"webpack-cli": "^3.1.2",
"webpack-node-externals": "^1.7.2"
},
"keywords": [
......
......@@ -19,10 +19,10 @@
<ng-container *ngFor="let group of memberGroupResults">
<div class="col-12 col-sm-6">
<div class="leaderboard-item rounded p-2 my-2 ">
<h6 class="text-center mb-0">Team "{{group.name}}"</h6>
<h6 class="text-center mb-0">{{'component.leaderboard.team-result.name' | translate:{TEAM_NAME: group.name} }}</h6>
<hr/>
<p>Overall Response Time: {{formatResponseTime(group.responseTime)}}</p>
<p>Score: {{group.score}}</p>
<p>{{'component.leaderboard.team-result.overall-response-time' | translate:{RESPONSE_TIME: formatResponseTime(group.responseTime)} }}</p>
<p>{{'component.leaderboard.team-result.score' | translate:{SCORE: group.score} }}</p>
</div>
</div>
</ng-container>
......
......@@ -25,12 +25,10 @@ export class QuizRenameComponent implements OnInit {
public sendRecommendation(duplicateQuiz: IDuplicateQuiz, renameRecommendation: string): void {
const reader = new FileReader();
const file: File = <File>this.fileUploadService.renameFilesQueue.getAll('uploadFiles[]').find((uploadedFile) => {
return (
<File>uploadedFile
).name === duplicateQuiz.fileName;
return (<File>uploadedFile).name === duplicateQuiz.fileName;
});
reader.addEventListener<'load'>('load', () => {
const jsonData = JSON.parse(reader.result);
const jsonData = JSON.parse(reader.result.toString());
const quizData: IQuestionGroup = questionGroupReflection[jsonData.TYPE](jsonData);
quizData.hashtag = renameRecommendation;
const blob = new Blob([JSON.stringify(quizData.serialize())], { type: 'application/json' });
......
......@@ -106,9 +106,7 @@ export const appRoutes: Routes = [
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: (
createTranslateLoader
),
useFactory: (createTranslateLoader),
deps: [HttpClient],
},
compiler: {
......@@ -125,8 +123,8 @@ export const appRoutes: Routes = [
HeaderModule,
FooterModule,
ModalsModule,
NgbModule.forRoot(),
Angulartics2Module.forRoot([ArsnovaClickAngulartics2Piwik]),
NgbModule,
Angulartics2Module.forRoot(),
I18nManagerModule,
JwtModule.forRoot({
jwtOptionsProvider: {
......@@ -159,6 +157,7 @@ export const appRoutes: Routes = [
HeaderLabelService,
QuestionTextService,
ThemesService,
ArsnovaClickAngulartics2Piwik,
TrackingService,
UserService,
WebsocketService,
......
<ng-container *ngIf="!isLoading">
<h4 class="text-light text-center mb-5 mt-sm-5">Please enter your login credentials to proceed to the requested resource</h4>
<h4 class="text-light text-center mb-5 mt-sm-5">{{'component.login.title'}}</h4>
<div class="input-group input-group-sm">
......@@ -20,7 +20,7 @@
<div class="input-group-append">
<button class="btn btn-info"
(click)="login()">Login
(click)="login()">{{'component.login.login'}}
</button>
</div>
......@@ -28,6 +28,6 @@
<h4 *ngIf="authorizationFailed"
class="text-danger text-center mt-5">Authorization failed
class="text-danger text-center mt-5">{{'component.login.authorization_failed'}}
</h4>
</ng-container>
\ No newline at end of file
<h4 class="text-light mb-4">Select a Team</h4>
<h4 class="text-light mb-4">{{'component.member-group-select.title'}}</h4>
<div *ngFor="let group of memberGroups"
class="rounded px-2 py-2 my-2 pointer"
......
import { isPlatformServer } from '@angular/common';
import { AfterViewInit, Component, EventEmitter, Inject, OnInit, PLATFORM_ID } from '@angular/core';
import { NavigationEnd, RouteConfigLoadEnd, RouteConfigLoadStart, Router } from '@angular/router';
import { ActivatedRoute, NavigationEnd, RouteConfigLoadEnd, RouteConfigLoadStart, Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import * as introJs from 'intro.js';
import { IFooterBarElement } from '../../../lib/footerbar-element/interfaces';
import { INamedType } from '../../../lib/interfaces';
import { QuizManagerDetailsOverviewComponent } from '../../quiz/quiz-manager/quiz-manager-details/quiz-manager-details-overview/quiz-manager-details-overview.component';
import { QuizManagerComponent } from '../../quiz/quiz-manager/quiz-manager/quiz-manager.component';
import { ConnectionService } from '../../service/connection/connection.service';
import { FooterBarService } from '../../service/footer-bar/footer-bar.service';
import { HeaderLabelService } from '../../service/header-label/header-label.service';
......@@ -55,6 +58,7 @@ export class RootComponent implements OnInit, AfterViewInit {
private themesService: ThemesService,
private translateService: TranslateService,
private router: Router,
private activatedRoute: ActivatedRoute,
private storageService: StorageService,
private userService: UserService,
) {
......@@ -72,8 +76,6 @@ export class RootComponent implements OnInit, AfterViewInit {
} else if (event instanceof RouteConfigLoadEnd) {
this._isLoading = false;
}
this.isInQuizManager = this.router.isActive('/quiz/manager/overview', true);
});
}
......@@ -85,11 +87,20 @@ export class RootComponent implements OnInit, AfterViewInit {
return;
}
this.isInQuizManager = [QuizManagerComponent.TYPE, QuizManagerDetailsOverviewComponent.TYPE].includes(
this.fetchChildComponent(this.activatedRoute).TYPE);
this.initializeCookieConsent(nav.url);
}
});
}
private fetchChildComponent(route: ActivatedRoute): INamedType {
return <INamedType>(
route.firstChild ? this.fetchChildComponent(route.firstChild) : route.component
);
}
private initializeCookieConsent(currentUrl): void {
window.addEventListener('load', () => {
if (!(
......
......@@ -44,9 +44,7 @@ export class FileUploadService {
this._duplicateQuizzes = data.payload.duplicateQuizzes;
data.payload.duplicateQuizzes.forEach((duplicateQuiz: IDuplicateQuiz) => {
this._renameFilesQueue.append('uploadFiles[]', <File>formData.getAll('uploadFiles[]').filter((file) => {
return (
<File>file
).name === duplicateQuiz.fileName;
return (<File>file).name === duplicateQuiz.fileName;
})[0], duplicateQuiz.fileName);
});
this.router.navigate(['/quiz', 'rename']);
......@@ -60,7 +58,7 @@ export class FileUploadService {
const reader = new FileReader();
reader.onload = async () => {
const parsedFile = JSON.parse(reader.result);
const parsedFile = JSON.parse(reader.result.toString());
this.activeQuestionGroupService.activeQuestionGroup = questionGroupReflection[parsedFile.TYPE](parsedFile);
this.activeQuestionGroupService.persist();
......
......@@ -5,6 +5,56 @@ import { IFooterBarElement } from '../../../lib/footerbar-element/interfaces';
import { DB_TABLE, STORAGE_KEY } from '../../shared/enums';
import { StorageService } from '../storage/storage.service';
interface IFsDocument extends HTMLDocument {
mozFullScreenElement?: Element;
msFullscreenElement?: Element;
msExitFullscreen?: () => void;
mozCancelFullScreen?: () => void;
}
export function isFullScreen(): boolean {
const fsDoc = <IFsDocument> document;
return !!((fsDoc as any).fullscreenElement || fsDoc.mozFullScreenElement || (fsDoc as any).webkitFullscreenElement || fsDoc.msFullscreenElement);
}
interface IFsDocumentElement extends HTMLElement {
msRequestFullscreen?: () => void;
mozRequestFullScreen?: () => void;
}
export function toggleFullScreen(): void {
const fsDoc = <IFsDocument> document;
if (!isFullScreen()) {
const fsDocElem = <IFsDocumentElement> document.documentElement;
if (fsDocElem.requestFullscreen) {
(fsDocElem as any).requestFullscreen();
} else if (fsDocElem.msRequestFullscreen) {
fsDocElem.msRequestFullscreen();
} else if (fsDocElem.mozRequestFullScreen) {
fsDocElem.mozRequestFullScreen();
} else if ((fsDocElem as any).webkitRequestFullscreen) {
(fsDocElem as any).webkitRequestFullscreen();
}
} else if (fsDoc.exitFullscreen) {
(fsDoc as any).exitFullscreen();
} else if (fsDoc.msExitFullscreen) {
fsDoc.msExitFullscreen();
} else if (fsDoc.mozCancelFullScreen) {
fsDoc.mozCancelFullScreen();
} else if ((fsDoc as any).webkitExitFullscreen) {
(fsDoc as any).webkitExitFullscreen();
}
}
export function setFullScreen(full: boolean): void {
if (full !== isFullScreen()) {
toggleFullScreen();
}
}
@Injectable()
export class FooterBarService {
......@@ -88,22 +138,7 @@ export class FooterBarService {
isActive: isPlatformBrowser(this.platformId) ? window.innerWidth === screen.width && window.innerHeight === screen.height : false,
}, function (): void {
this.isActive = !this.isActive;
if (document) {
const elem = document.documentElement;
if (!document.fullscreenElement && !document.webkitFullscreenElement) {
if (elem.requestFullscreen) {
elem.requestFullscreen();
} else if (elem.webkitRequestFullscreen) {
elem.webkitRequestFullscreen();
}
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
}
}
}
setFullScreen(this.isActive);
});
public footerElemHome: IFooterBarElement = new FooterbarElement({
id: 'home',
......
......@@ -38,7 +38,7 @@ export class AbstractIndexedDbAdapter extends AbstractStorageAdapter<IDBDatabase
}
public async set(table: DB_TABLE, key: STORAGE_KEY | string, item: object | Array<any> | string | number): Promise<any> {
return new Promise<void>(resolve => {
return new Promise<IDBValidKey>(resolve => {
const request = this.getStore(table).put(Object.assign({}, key, item));
request.onsuccess = () => {
resolve(request.result);
......
import { isPlatformBrowser } from '@angular/common';
import { Inject, Injectable, PLATFORM_ID } from '@angular/core';
import { Observable } from 'rxjs';
import { Observable, throwError as observableThrowError } from 'rxjs';
import { DB_TABLE, STORAGE_KEY } from '../../shared/enums';
interface ISchema {
......@@ -244,7 +244,7 @@ export class IndexedDbService {
private handleError(msg: string): Observable<any> {
console.error(msg);
return Observable.throw(msg);
return observableThrowError(msg);
}
private open(): Observable<any> {
......
import { Injectable, Type } from '@angular/core';
import { Injectable } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Angulartics2 } from 'angulartics2';
import { Angulartics2Piwik } from 'angulartics2/piwik';
import { INamedType } from '../../../lib/interfaces';
declare var _paq: any;
interface INamedType extends Type<Function> {
TYPE: string;
}
@Injectable()
export class ArsnovaClickAngulartics2Piwik extends Angulartics2Piwik {
constructor(private _angulartics2: Angulartics2, private route: ActivatedRoute) {
super(_angulartics2);
this.startTracking();
}
public pageTrack(path: string, location?: any): void {
try {
_paq.push([
'setDocumentTitle',
(
<INamedType>this.getFirstRoutingChild(this.route).component
).TYPE,
'setDocumentTitle', (<INamedType>this.getFirstRoutingChild(this.route).component).TYPE,
]);
_paq.push(['setCustomUrl', path]);
_paq.push(['trackPageView']);
} catch (e) {
if (!(
e instanceof ReferenceError
)) {
if (!(e instanceof ReferenceError)) {
throw e;
}
}
......
......@@ -370,7 +370,20 @@
"show_more": "Show 1 more",
"show_more_plural": "Show %s more",
"show_less": "Show less",
"export": "Export"
"export": "Export",
"team-result": {
"name": "Team \"{TEAM_NAME}\"",
"overall-response-time": "Overall Response Time: {RESPONSE_TIME}",
"score": "Score: {SCORE}"
}
},
"login": {
"title": "Please enter your login credentials",
"login": "Login",
"authorization_failed": "Authorization failed"
},
"member-group-select": {
"title": "Select a Team"
},
"liveResults": {
"title": "Results",
......
import path from 'path';
import fs from 'fs';
import gm from 'gm';
import sharp from 'sharp';
import derivates from '../imageDerivates';
import themeData from '../themeData';
import process from 'process';
import child_process from 'child_process';
import minimist from 'minimist';
import imagemin from 'imagemin';
import imageminPngquant from 'imagemin-pngquant';
import {default as chromeLauncher} from 'chrome-launcher';
const gmIM = gm.subClass({imageMagick: true});
const argv = minimist(process.argv.slice(2));
const themes = [
......@@ -32,28 +27,6 @@ const languages = ['en', 'de', 'fr', 'it', 'es'];
const __dirname = path.resolve();
const gmToBuffer = (data) => {
return new Promise((resolve, reject) => {
data.stream((err, stdout, stderr) => {
if (err) {
return reject(err);
}
const chunks = [];
stdout.on('data', (chunk) => {
chunks.push(chunk);
});
// these are 'once' because they can and do fire multiple times for multiple errors,
// but this is a promise so you'll have to deal with them one at a time
stdout.once('end', () => {
resolve(Buffer.concat(chunks));
});
stderr.once('data', (stderrmsg) => {
reject(String(stderrmsg));
});
});
});
};
class GenerateImages {
constructor() {
......@@ -137,53 +110,37 @@ class GenerateImages {
}
async asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
generateLogoImages() {
const source = path.join(this.pathToAssets, 'images', 'logo_transparent.png');
Object.keys(themeData).forEach(async (themeName) => {
await new Promise(resolveLogoImageGeneration => {
const theme = themeData[themeName].quizNameRowStyle.bg;
derivates.forEach(async (derivate) => {
const splittedDerivate = derivate.split('x');
const targetLogo = path.join(this.pathToDestination, `${themeName}`, `logo_s${derivate}.png`);
const size = {
width: splittedDerivate[0],
height: splittedDerivate[1],
roundX: Math.round((splittedDerivate[0] / Math.PI)),
roundY: Math.round((splittedDerivate[1] / Math.PI))
};
const bgData = gm(1, 1, 'none')
.setFormat('png')
.fill(theme)
.resize(size.width, size.height)
.drawRectangle(0, 0, size.width, size.height, size.roundX, size.roundY)
.compose('copyopacity');
const bgBuffer = await gmToBuffer(bgData);
if (!bgBuffer) {
console.log('gm error', bgBuffer);
return;
}
const fgData = gmIM(bgBuffer)
.setFormat('png')
.composite(source)
.resize(size.width, size.height);
const fgBuffer = await gmToBuffer(fgData);
if (!fgBuffer) {
console.log('gmIM error', fgBuffer);
return;
}
const minifiedBuffer = await imagemin.buffer(fgBuffer, {
plugins: [imageminPngquant({quality: '65-80'})]
});
fs.writeFileSync(targetLogo, minifiedBuffer, 'binary');
console.log(`Writing file '${targetLogo}'`);
console.log(`Icon with deriavate ${derivate} generated for theme ${themeName}`);
resolveLogoImageGeneration();
});
this.asyncForEach(Object.keys(themeData), async (themeName) => {
const theme = themeData[themeName].quizNameRowStyle.bg;
await this.asyncForEach(derivates, async (derivate) => {
const splittedDerivate = derivate.split('x');
const targetLogo = path.join(this.pathToDestination, `${themeName}`, `logo_s${derivate}.png`);
const size = {
width: parseInt(splittedDerivate[0], 10),
height: parseInt(splittedDerivate[1], 10),
roundX: Math.round((splittedDerivate[0] / Math.PI)),
roundY: Math.round((splittedDerivate[1] / Math.PI))
};
const minifiedBuffer = await sharp(source)
.resize(size.width, size.height)
.flatten({background: theme})
.sharpen()
.png()
.toBuffer();
fs.writeFileSync(targetLogo, minifiedBuffer, 'binary');
console.log(`Writing file '${targetLogo}'`);
console.log(`Icon with size of ${derivate}px generated for theme ${themeName}`);
});
});
}
......
......@@ -26,7 +26,7 @@ class GenerateMetaNodes {
constructor() {
this.pathToAssets = path.join(__dirname, '..');
this.baseUrl = 'http://localhost:4200';
this.baseUrl = 'http://localhost:4210';
}
generateDirectories() {
......
import { Type } from '@angular/core';
export interface INamedType extends Type<Function> {
TYPE: string;
}
......@@ -35,7 +35,7 @@
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/* Evergreen browsers require these. **/
import 'core-js/es6/reflect';
import 'core-js/es7/reflect';
/**
* Required to support Web Animations `@angular/animation`.
* Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
......
......@@ -2,6 +2,7 @@
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"importHelpers": true,
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment