core/templates/pages/exploration-player-page/services/checkpoint-celebration-utility.service.ts

Summary

Maintainability
A
3 hrs
Test Coverage
// Copyright 2022 The Oppia Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview Provides a collection of utility functions for the
 * checkpoint celebration feature.
 */

import {Injectable, EventEmitter} from '@angular/core';
import {TranslateService} from '@ngx-translate/core';

import {ComputeGraphService} from 'services/compute-graph.service';
import {StateObjectsBackendDict} from 'domain/exploration/StatesObjectFactory';
import {StatesObjectFactory} from 'domain/exploration/StatesObjectFactory';

enum CheckpointMessageTypes {
  FIRST = 'FIRST',
  SECOND = 'SECOND',
  MIDWAY = 'MIDWAY',
  TWO_REMAINING = 'TWO_REMAINING',
  ONE_REMAINING = 'ONE_REMAINING',
  GENERIC = 'GENERIC',
}

@Injectable({
  providedIn: 'root',
})
export class CheckpointCelebrationUtilityService {
  private isOnCheckpointedState: boolean = false;
  private _openLessonInformationModalEmitter = new EventEmitter<void>();
  constructor(
    private computeGraphService: ComputeGraphService,
    private statesObjectFactory: StatesObjectFactory,
    private translateService: TranslateService
  ) {}

  getStateListForCheckpointMessages(
    statesbackendDict: StateObjectsBackendDict,
    initStateName: string
  ): string[] {
    const states =
      this.statesObjectFactory.createFromBackendDict(statesbackendDict);
    const bfsStateList = this.computeGraphService.computeBfsTraversalOfStates(
      initStateName,
      states,
      initStateName
    );
    let stateListForCheckpointMessages: string[] = [];
    bfsStateList.forEach(state => {
      if (statesbackendDict[state].card_is_checkpoint) {
        stateListForCheckpointMessages.push(state);
      }
    });
    return stateListForCheckpointMessages;
  }

  getRandomI18nKey(
    i18nKeyPrefix: string,
    availableKeyCount: number,
    messageKind: string | null
  ): string {
    // 'randomValue' is being set to lie between 1 and availableKeyCount, the
    // total number of i18n keys available to choose from.
    const randomValue = Math.floor(Math.random() * availableKeyCount) + 1;
    if (messageKind) {
      return i18nKeyPrefix + '_' + messageKind + '_' + randomValue.toString();
    }
    return i18nKeyPrefix + '_' + randomValue.toString();
  }

  // This function fetches the i18n key for the checkpoint message.
  // There are 6 different kinds of i18n keys available (enumerated in the
  // 'CheckpointMessageTypes' enum above) for the checkpoint message, 3 of
  // each kind for a total of 18.
  // The kind of key is determined using the number of completed checkpoints and
  // the total number of checkpoints.
  // The function returns a random key from the 3 available ones for the
  // correct type, using the 'getRandomI18nKey' method.
  getCheckpointMessageI18nKey(
    completedCheckpointCount: number,
    totalCheckpointCount: number
  ): string {
    const messageI18nKeyPrefix = 'I18N_CONGRATULATORY_CHECKPOINT_MESSAGE';
    if (completedCheckpointCount === 1) {
      return this.getRandomI18nKey(
        messageI18nKeyPrefix,
        3,
        CheckpointMessageTypes.FIRST
      );
    } else if (completedCheckpointCount === 2) {
      return this.getRandomI18nKey(
        messageI18nKeyPrefix,
        3,
        CheckpointMessageTypes.SECOND
      );
      // The condition below evaluates to true when the learner has just
      // completed the middle checkpoint and returns the midway message's
      // i18n key.
    } else if (
      Math.ceil(totalCheckpointCount / 2) === completedCheckpointCount
    ) {
      return this.getRandomI18nKey(
        messageI18nKeyPrefix,
        3,
        CheckpointMessageTypes.MIDWAY
      );
    } else if (totalCheckpointCount - completedCheckpointCount === 2) {
      return this.getRandomI18nKey(
        messageI18nKeyPrefix,
        3,
        CheckpointMessageTypes.TWO_REMAINING
      );
    } else if (totalCheckpointCount - completedCheckpointCount === 1) {
      return this.getRandomI18nKey(
        messageI18nKeyPrefix,
        3,
        CheckpointMessageTypes.ONE_REMAINING
      );
    } else {
      return this.getRandomI18nKey(
        messageI18nKeyPrefix,
        3,
        CheckpointMessageTypes.GENERIC
      );
    }
  }

  getCheckpointMessage(
    completedCheckpointCount: number,
    totalCheckpointCount: number
  ): string {
    const messageI18nKey = this.getCheckpointMessageI18nKey(
      completedCheckpointCount,
      totalCheckpointCount
    );
    return this.translateService.instant(messageI18nKey);
  }

  getCheckpointTitleI18nKey(): string {
    const titleI18nKeyPrefix = 'I18N_CONGRATULATORY_CHECKPOINT_TITLE';
    // A null messageKind is passed to the function below because there
    // is only one kind of title i18n keys.
    return this.getRandomI18nKey(titleI18nKeyPrefix, 6, null);
  }

  getCheckpointTitle(): string {
    const titleI18nKey = this.getCheckpointTitleI18nKey();
    return this.translateService.instant(titleI18nKey);
  }

  setIsOnCheckpointedState(isOnCheckpointedState: boolean): void {
    this.isOnCheckpointedState = isOnCheckpointedState;
  }

  getIsOnCheckpointedState(): boolean {
    return this.isOnCheckpointedState;
  }

  getOpenLessonInformationModalEmitter(): EventEmitter<void> {
    return this._openLessonInformationModalEmitter;
  }

  openLessonInformationModal(): void {
    this._openLessonInformationModalEmitter.emit();
  }
}