quiz-results.component.ts 14.8 KB
Newer Older
1 2
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router';
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
3 4 5 6 7 8
import { IMessage, INickname } from 'arsnova-click-v2-types/dist/common';
import { COMMUNICATION_PROTOCOL } from 'arsnova-click-v2-types/dist/communication_protocol';
import { IQuestion } from 'arsnova-click-v2-types/dist/questions/interfaces';
import { FreeTextQuestion } from 'arsnova-click-v2-types/dist/questions/question_freetext';
import { RangedQuestion } from 'arsnova-click-v2-types/dist/questions/question_ranged';
import { SurveyQuestion } from 'arsnova-click-v2-types/dist/questions/question_survey';
9
import { Countdown } from '../../../../lib/countdown/countdown';
10
import { QuizApiService } from '../../../service/api/quiz/quiz-api.service';
11 12 13 14 15
import { AttendeeService } from '../../../service/attendee/attendee.service';
import { ConnectionService } from '../../../service/connection/connection.service';
import { CurrentQuizService } from '../../../service/current-quiz/current-quiz.service';
import { FooterBarService } from '../../../service/footer-bar/footer-bar.service';
import { HeaderLabelService } from '../../../service/header-label/header-label.service';
16
import { I18nService } from '../../../service/i18n/i18n.service';
17
import { QuestionTextService } from '../../../service/question-text/question-text.service';
18
import { NUMBER_TYPE } from '../../../shared/enums';
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
19 20 21 22

@Component({
  selector: 'app-quiz-results',
  templateUrl: './quiz-results.component.html',
23
  styleUrls: ['./quiz-results.component.scss'],
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
24 25
})
export class QuizResultsComponent implements OnInit, OnDestroy {
26
  public static TYPE = 'QuizResultsComponent';
27 28 29 30
  public countdown: Countdown;
  public answers: Array<string> = [];

  private _selectedQuestionIndex: number;
31

Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
32 33 34 35
  get selectedQuestionIndex(): number {
    return this._selectedQuestionIndex;
  }

36 37 38 39 40 41
  private _ownsQuiz: boolean;

  get ownsQuiz(): boolean {
    return this._ownsQuiz;
  }

Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
42 43
  constructor(
    public currentQuizService: CurrentQuizService,
44
    public attendeeService: AttendeeService,
45
    private i18nService: I18nService,
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
46 47 48 49
    private router: Router,
    private headerLabelService: HeaderLabelService,
    private connectionService: ConnectionService,
    private footerBarService: FooterBarService,
50
    private questionTextService: QuestionTextService,
51
    private quizApiService: QuizApiService,
52
  ) {
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
53

54 55
    this.footerBarService.TYPE_REFERENCE = QuizResultsComponent.TYPE;

Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
56 57 58
    headerLabelService.headerLabel = 'component.liveResults.title';

    this._selectedQuestionIndex = currentQuizService.questionIndex;
59

60 61 62 63
    this.currentQuizService.isOwner.subscribe(val => {
      this._ownsQuiz = !!val;
      this.addFooterElements();
    });
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
64 65
  }

66
  public showLeaderBoardButton(index: number): boolean {
67 68 69
    return !(
      this.currentQuizService.quiz.questionList[index] instanceof SurveyQuestion
    );
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
70 71
  }

72
  public showStopQuizButton(): boolean {
73 74 75 76 77
    return this.ownsQuiz && !this.currentQuizService.currentQuestion().timer && (
      this.attendeeService.attendees.length > this.attendeeService.attendees.filter(nick => {
        return nick.responses[this.currentQuizService.questionIndex];
      }).length
    );
78 79
  }

80
  public showStopCountdownButton(): boolean {
81 82 83 84 85 86 87
    return this.ownsQuiz && (
      this.attendeeService.attendees.length > this.attendeeService.attendees.filter(nick => {
        return nick.responses[this.currentQuizService.questionIndex];
      }).length
    ) && (
           this.countdown && this.countdown.isRunning && this.countdown.remainingTime > 0
           );
88 89
  }

90
  public showStartQuizButton(): boolean {
91 92 93 94 95
    return this.ownsQuiz && !this.showStopCountdownButton() && !this.showStopQuizButton() && this.currentQuizService.questionIndex
           === this._selectedQuestionIndex && (
             this.currentQuizService.questionIndex < this.currentQuizService.quiz.questionList.length - 1
             || this.currentQuizService.quiz.sessionConfig.readingConfirmationEnabled && this.currentQuizService.readingConfirmationRequested
           );
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
96 97
  }

98
  public hideProgressbarCssStyle(): boolean {
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
99 100 101 102 103 104 105 106
    const resultLength = this.attendeeService.attendees.filter(nick => {
      const responses = nick.responses[this._selectedQuestionIndex];
      if (responses) {
        if (typeof responses.value === 'number') {
          return responses.value > 0;
        }
        return responses.value.length > 0;
      }
107
      return false;
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
108 109
    }).length;

110
    return (
111
      this._selectedQuestionIndex <= this.currentQuizService.questionIndex && !resultLength && !this.currentQuizService.readingConfirmationRequested
112
    );
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
113 114
  }

115
  public showConfidenceRate(questionIndex: number): boolean {
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
116 117 118 119
    const matches = this.attendeeService.attendees.filter(value => {
      return value.responses[questionIndex] ? value.responses[questionIndex].confidence : false;
    });
    const hasConfidenceSet = typeof this.currentQuizService.quiz.sessionConfig.confidenceSliderEnabled !== 'undefined';
120
    const isConfidenceEnabled = typeof hasConfidenceSet ? this.currentQuizService.quiz.sessionConfig.confidenceSliderEnabled : false;
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
121 122 123
    return hasConfidenceSet ? matches.length > 0 || isConfidenceEnabled : matches.length > 0;
  }

124
  public async modifyVisibleQuestion(index: number): Promise<void> {
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
125
    this._selectedQuestionIndex = index;
126
    await this.generateAnswers(this.currentQuizService.quiz.questionList[index]);
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
127 128
  }

129
  public getConfidenceData(questionIndex: number): { base: number, absolute: number, percent: string } {
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
130 131 132
    const result = {
      base: this.attendeeService.attendees.length,
      absolute: 0,
133
      percent: '0',
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
134 135 136 137 138 139 140 141 142 143 144
    };
    if (questionIndex >= 0) {
      const matches = this.attendeeService.attendees.filter(value => {
        return value.responses[questionIndex] ? value.responses[questionIndex].confidence : false;
      });
      const absoluteValues = matches.length ? this.attendeeService.attendees.map(value => {
        return value.responses[questionIndex] ? value.responses[questionIndex].confidence : 0;
      }).reduce((currentValue, nextValue) => {
        return currentValue + nextValue;
      }) : 0;
      result.absolute = matches.length;
145
      result.percent = this.i18nService.formatNumber(absoluteValues / (
146
        matches.length || 1
147
      ) / 100, NUMBER_TYPE.PERCENT);
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
148 149 150 151
    }
    return result;
  }

152
  public showReadingConfirmation(questionIndex: number): boolean {
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
153 154 155
    const matchCount = this.attendeeService.attendees.filter(value => {
      return value.responses[questionIndex] ? value.responses[questionIndex].readingConfirmation : false;
    }).length;
156 157
    const readingConfirmationStatus = this.currentQuizService.quiz.sessionConfig.readingConfirmationEnabled;
    const isReadingConfirmationEnabled = typeof readingConfirmationStatus === 'undefined' ? false : readingConfirmationStatus;
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
158 159 160
    return matchCount > 0 || isReadingConfirmationEnabled;
  }

161
  public showResponseProgress(): boolean {
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
162 163 164
    return this.currentQuizService.quiz.sessionConfig.showResponseProgress;
  }

165
  public getReadingConfirmationData(questionIndex: number): { base: number, absolute: number, percent: string } {
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
166 167 168
    const result = {
      base: this.attendeeService.attendees.length,
      absolute: 0,
169
      percent: '0',
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
170 171 172 173 174 175
    };
    if (questionIndex >= 0) {
      const matchCount = this.attendeeService.attendees.filter(value => {
        return value.responses[questionIndex] ? value.responses[questionIndex].readingConfirmation : false;
      }).length;
      result.absolute = matchCount;
176
      result.percent = this.i18nService.formatNumber(matchCount / (
177
        this.attendeeService.attendees.length || 1
178
      ), NUMBER_TYPE.PERCENT);
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
179 180 181 182
    }
    return result;
  }

183
  public ngOnInit(): void {
184 185 186 187
    this.questionTextService.eventEmitter.subscribe((data: Array<string>) => {
      this.answers = data;
    });

188
    this.connectionService.initConnection().then(() => {
189

190 191 192 193
      if (this.ownsQuiz) {
        this.connectionService.authorizeWebSocketAsOwner(this.currentQuizService.quiz.hashtag);
      } else {
        this.connectionService.authorizeWebSocket(this.currentQuizService.quiz.hashtag);
194
      }
195
      this.handleMessages();
196

197
      this.quizApiService.getCurrentQuizState(this.currentQuizService.quiz.hashtag).toPromise().then(currentStateData => {
198
        if (currentStateData.status === COMMUNICATION_PROTOCOL.STATUS.SUCCESSFUL) {
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
          const question = this.currentQuizService.currentQuestion();

          if (question.timer && new Date().getTime() - currentStateData.payload.startTimestamp < 0) {
            this.countdown = new Countdown(question, currentStateData.payload.startTimestamp);
          }

          this.generateAnswers(question);
        }
        if (this.attendeeService.attendees.filter(attendee => {
          return attendee.responses[this.currentQuizService.questionIndex];
        }).length === this.attendeeService.attendees.length && this.countdown) {
          this.countdown.stop();
        }
      });
    });
214 215 216 217 218 219
  }

  public ngOnDestroy(): void {
    this.footerBarService.footerElemBack.restoreClickCallback();
  }

220 221 222 223
  public stopQuiz(): void {
    this.quizApiService.postQuizStop({
      quizName: this.currentQuizService.quiz.hashtag,
    }).subscribe(data => {
224
      if (data.status !== COMMUNICATION_PROTOCOL.STATUS.SUCCESSFUL) {
225 226 227 228 229 230 231
        console.log(data);
      }
    });
    this.countdown.stop();
  }

  private addFooterElements(): void {
232 233 234

    let footerElems;

235
    if (this.ownsQuiz) {
236 237 238
      this.connectionService.authorizeWebSocketAsOwner(this.currentQuizService.quiz.hashtag);
      if (this.currentQuizService.questionIndex === this.currentQuizService.quiz.questionList.length - 1) {
        footerElems = [
239
          this.footerBarService.footerElemBack, this.footerBarService.footerElemLeaderboard, this.footerBarService.footerElemFullscreen,
240 241 242 243 244 245 246 247 248 249 250
        ];
      } else {
        footerElems = [
          this.footerBarService.footerElemBack,
          this.footerBarService.footerElemReadingConfirmation,
          this.footerBarService.footerElemConfidenceSlider,
          this.footerBarService.footerElemResponseProgress,
          this.footerBarService.footerElemFullscreen,
        ];
      }
      this.footerBarService.footerElemBack.onClickCallback = async () => {
251
        await this.quizApiService.patchQuizReset(this.currentQuizService.quiz.hashtag).toPromise();
252 253 254 255 256 257
        this.currentQuizService.questionIndex = 0;
        this.router.navigate(['/quiz', 'flow', 'lobby']);
      };
    } else {
      if (this.currentQuizService.questionIndex === this.currentQuizService.quiz.questionList.length - 1) {
        footerElems = [
258
          this.footerBarService.footerElemLeaderboard, this.footerBarService.footerElemFullscreen,
259 260 261 262 263 264 265 266 267 268 269
        ];
      } else {
        footerElems = [
          this.footerBarService.footerElemFullscreen,
        ];
      }
    }
    this.footerBarService.replaceFooterElements(footerElems);
  }

  private handleMessages(): void {
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
270 271
    if (!this.attendeeService.attendees.length) {
      this.connectionService.sendMessage({
272 273
        status: COMMUNICATION_PROTOCOL.STATUS.SUCCESSFUL,
        step: COMMUNICATION_PROTOCOL.LOBBY.GET_PLAYERS,
274
        payload: { quizName: this.currentQuizService.quiz.hashtag },
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
275 276
      });
    }
277
    this.connectionService.socket.subscribe(async (data: IMessage) => {
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
278
      switch (data.step) {
279
        case COMMUNICATION_PROTOCOL.LOBBY.ALL_PLAYERS:
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
280 281 282 283
          data.payload.members.forEach((elem: INickname) => {
            this.attendeeService.addMember(elem);
          });
          break;
284
        case COMMUNICATION_PROTOCOL.MEMBER.UPDATED_RESPONSE:
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
285 286
          this.attendeeService.modifyResponse(data.payload.nickname);
          if (this.attendeeService.attendees.filter(attendee => {
287 288
            return attendee.responses[this.currentQuizService.questionIndex] ? attendee.responses[this.currentQuizService.questionIndex].value
                                                                             : false;
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
289 290 291 292
          }).length === this.attendeeService.attendees.length && this.countdown) {
            this.countdown.stop();
          }
          break;
293
        case COMMUNICATION_PROTOCOL.QUIZ.NEXT_QUESTION:
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
294 295 296
          this.currentQuizService.questionIndex = data.payload.questionIndex;
          this._selectedQuestionIndex = data.payload.questionIndex;
          break;
297
        case COMMUNICATION_PROTOCOL.QUIZ.RESET:
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
298 299 300 301 302
          this.attendeeService.clearResponses();
          this.currentQuizService.questionIndex = 0;
          this.router.navigate(['/quiz', 'flow', 'lobby']);
          break;
      }
303
      this.ownsQuiz ? this.handleMessagesForOwner(data) : this.handleMessagesForAttendee(data);
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
304 305 306
    });
  }

307
  private handleMessagesForOwner(data: IMessage): void {
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
308 309 310 311 312 313
    switch (data.step) {
      default:
        return;
    }
  }

314
  private handleMessagesForAttendee(data: IMessage): void {
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
315
    switch (data.step) {
316
      case COMMUNICATION_PROTOCOL.QUIZ.START:
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
317 318
        this.router.navigate(['/quiz', 'flow', 'voting']);
        break;
319
      case COMMUNICATION_PROTOCOL.QUIZ.READING_CONFIRMATION_REQUESTED:
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
320 321
        this.router.navigate(['/quiz', 'flow', 'reading-confirmation']);
        break;
322
      case COMMUNICATION_PROTOCOL.LOBBY.CLOSED:
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
323 324
        this.router.navigate(['/']);
        break;
325
      case COMMUNICATION_PROTOCOL.QUIZ.STOP:
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
326 327 328 329 330
        this.countdown.stop();
        break;
    }
  }

331
  private async startQuiz(): Promise<void> {
332 333
    const target = this.currentQuizService.quiz.sessionConfig.readingConfirmationEnabled && !this.currentQuizService.readingConfirmationRequested
                   ? 'reading-confirmation' : 'start';
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
334

335
    const startQuizData = await this.quizApiService.postQuizData(target, {
336 337
      quizName: this.currentQuizService.quiz.hashtag,
    }).toPromise();
338
    if (startQuizData.status !== COMMUNICATION_PROTOCOL.STATUS.SUCCESSFUL) {
339
      console.log(startQuizData);
340 341
      return;
    }
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
342

343 344
    const question = this.currentQuizService.currentQuestion();
    await this.generateAnswers(question);
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
345

346
    if (startQuizData.step === COMMUNICATION_PROTOCOL.QUIZ.READING_CONFIRMATION_REQUESTED) {
347 348 349
      this.currentQuizService.readingConfirmationRequested = true;
      return;
    }
350

351
    this.currentQuizService.readingConfirmationRequested = false;
352

353 354 355
    if (question.timer) {
      this.countdown = new Countdown(question, startQuizData.payload.startTimestamp);
    }
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
356

357 358
    if (this.currentQuizService.questionIndex === this.currentQuizService.quiz.questionList.length - 1) {
      this.footerBarService.replaceFooterElements([
359
        this.footerBarService.footerElemBack, this.footerBarService.footerElemLeaderboard, this.footerBarService.footerElemFullscreen,
360 361
      ]);
    }
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
362 363
  }

364
  private async generateAnswers(question: IQuestion): Promise<void> {
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
365 366 367 368 369 370 371
    if (question instanceof RangedQuestion) {
      this.answers = ['guessed_correct', 'guessed_in_range', 'guessed_wrong'];

    } else if (question instanceof FreeTextQuestion) {
      this.answers = ['correct_answer', 'wrong_answer'];

    } else {
372
      await this.questionTextService.changeMultiple(question.answerOptionList.map(answer => {
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
373 374 375 376 377 378
        return answer.answerText;
      }));
    }
  }

}