import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Component, OnInit, OnDestroy, AfterViewInit, ViewChild } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import * as _ from 'lodash-es';
import {
ILoaderMessage, ConfigService, ICollectionTreeOptions, ResourceService,
NavigationHelperService
} from '@sunbird/shared';
import { CourseConsumptionService } from '@sunbird/learn';
import { IImpressionEventInput, TelemetryService } from '@sunbird/telemetry';
import TreeModel from 'tree-model';
import { UserService, GeneraliseLabelService } from '@sunbird/core';
import { TocCardType } from '@project-sunbird/common-consumption';
import { ITelemetryShare, ContentUtilsServiceService } from '@sunbird/shared';
@Component({
selector: 'app-public-course-player',
templateUrl: './public-course-player.component.html',
styleUrls: ['./public-course-player.component.scss']
})
export class PublicCoursePlayerComponent implements OnInit, OnDestroy, AfterViewInit {
private courseId: string;
collectionTreeNodes: any;
loader = true;
showError = false;
courseHierarchy: any;
readMore = false;
curriculum = [];
telemetryCourseImpression: IImpressionEventInput;
treeModel: any;
showContentCreditsModal: boolean;
cardType: TocCardType = TocCardType.COURSE;
loaderMessage: ILoaderMessage = {
headerMessage: 'Please wait...',
loaderMessage: 'Fetching content details!'
};
collectionTreeOptions: ICollectionTreeOptions;
unsubscribe = new Subject<void>();
showJoinTrainingModal = false;
shareLinkModal = false;
telemetryShareData: Array<ITelemetryShare>;
shareLink: string;
@ViewChild('joinTrainingModal') joinTrainingModal;
isExpandedAll: boolean;
constructor(
public activatedRoute: ActivatedRoute,
private configService: ConfigService,
private courseConsumptionService: CourseConsumptionService,
public router: Router,
public resourceService: ResourceService,
public navigationhelperService: NavigationHelperService,
private userService: UserService,
public telemetryService: TelemetryService,
private contentUtilsServiceService: ContentUtilsServiceService,
public generaliseLabelService: GeneraliseLabelService
) {
this.collectionTreeOptions = this.configService.appConfig.collectionTreeOptions;
}
ngOnInit() {
const routeParams: any = { ...this.activatedRoute.snapshot.params };
this.courseId = routeParams.courseId;
const inputParams = { params: this.configService.appConfig.CourseConsumption.contentApiQueryParams };
this.courseConsumptionService.getCourseHierarchy(routeParams.courseId, inputParams).pipe(
takeUntil(this.unsubscribe))
.subscribe(courseHierarchy => {
this.loader = false;
this.courseHierarchy = courseHierarchy;
this.isExpandedAll = this.courseHierarchy.children && this.courseHierarchy.children.length === 1 ? true : undefined;
this.parseChildContent();
this.collectionTreeNodes = { data: this.courseHierarchy };
this.getGeneraliseResourceBundle();
});
}
getGeneraliseResourceBundle() {
this.resourceService.languageSelected$.pipe(takeUntil(this.unsubscribe)).subscribe(item => {
this.generaliseLabelService.initialize(this.courseHierarchy, item.value);
});
}
private parseChildContent() {
const model = new TreeModel();
const mimeTypeCount = {};
this.treeModel = model.parse(this.courseHierarchy);
this.treeModel.walk((node) => {
if (node.model.mimeType !== 'application/vnd.ekstep.content-collection') {
if (mimeTypeCount[node.model.mimeType]) {
mimeTypeCount[node.model.mimeType] += 1;
} else {
mimeTypeCount[node.model.mimeType] = 1;
}
}
});
let videoContentCount = 0;
_.forEach(mimeTypeCount, (value, key) => {
if (key.includes('video')) {
videoContentCount = videoContentCount + value;
} else {
this.curriculum.push({ mimeType: key, count: value });
}
});
if (videoContentCount > 0) {
this.curriculum.push({ mimeType: 'video', count: videoContentCount });
}
}
ngAfterViewInit() {
setTimeout(() => {
this.setTelemetryCourseImpression();
});
}
isExpanded(index: number) {
if (_.isUndefined(this.isExpandedAll)) {
return Boolean(index === 0);
}
return this.isExpandedAll;
}
ngOnDestroy() {
if (this.joinTrainingModal && this.joinTrainingModal.deny) {
this.joinTrainingModal.deny();
}
this.unsubscribe.next();
this.unsubscribe.complete();
}
private setTelemetryCourseImpression() {
this.telemetryCourseImpression = {
context: {
env: this.activatedRoute.snapshot.data.telemetry.env
},
edata: {
type: this.activatedRoute.snapshot.data.telemetry.type,
pageid: this.activatedRoute.snapshot.data.telemetry.pageid,
uri: this.router.url,
duration: this.navigationhelperService.getPageLoadTime()
},
object: {
id: this.activatedRoute.snapshot.params.courseId,
type: 'Course',
ver: '1.0',
rollup: {
l1: this.activatedRoute.snapshot.params.courseId
}
}
};
}
public navigateToContent(event: any, id) {
this.logTelemetry(id, event.data);
if (!_.get(this.userService, 'userid') && !_.isEmpty(event.event)) {
this.showJoinTrainingModal = true;
}
}
logTelemetry(id, content?: {}) {
const objectRollUp = this.courseConsumptionService.getContentRollUp(this.courseHierarchy, _.get(content, 'identifier'));
const interactData = {
context: {
env: _.get(this.activatedRoute.snapshot.data.telemetry, 'env') || 'content',
cdata: []
},
edata: {
id: id,
type: 'click',
pageid: _.get(this.activatedRoute.snapshot.data.telemetry, 'pageid') || 'explore-course-toc',
},
object: {
id: content ? _.get(content, 'identifier') : this.activatedRoute.snapshot.params.courseId,
type: content ? _.get(content, 'contentType') : 'Course',
ver: content ? `${_.get(content, 'pkgVersion')}` : `1.0`,
rollup: this.courseConsumptionService.getRollUp(objectRollUp) || {}
}
};
this.telemetryService.interact(interactData);
}
getAllBatchDetails(event) {
this.courseConsumptionService.getAllOpenBatches(event);
}
shareUnitLink(unit: any) {
this.shareLink = `${this.contentUtilsServiceService.getCoursePublicShareUrl(this.courseId)}?moduleId=${unit.identifier}`;
this.shareLinkModal = true;
this.setTelemetryShareData(this.courseHierarchy);
}
setTelemetryShareData(param) {
this.telemetryShareData = [{
id: param.identifier,
type: param.contentType,
ver: param.pkgVersion ? param.pkgVersion.toString() : '1.0'
}];
}
closeSharePopup(id) {
this.shareLinkModal = false;
const interactData = {
context: {
env: _.get(this.activatedRoute.snapshot.data.telemetry, 'env') || 'explore',
cdata: [{ id: this.courseId, type: 'Course' }]
},
edata: {
id: id,
type: 'click',
pageid: _.get(this.activatedRoute.snapshot.data.telemetry, 'pageid') || 'explore-course-toc',
},
object: {
id: _.get(this.courseHierarchy, 'identifier'),
type: _.get(this.courseHierarchy, 'contentType') || 'Course',
ver: `${_.get(this.courseHierarchy, 'pkgVersion')}` || `1.0`,
rollup: { l1: this.courseId }
}
};
this.telemetryService.interact(interactData);
}
}
<div *ngIf="!loader">
<div class="ui container" [appTelemetryImpression]="telemetryCourseImpression">
<div class="sb-course-details sb-g sb-g--gap24 text-left course-consumption-container">
<div class="sb-course-details__training sb-g-col-xs-12 sb-g-col-md-9 sb-g-col-lg-9 sb-g-col-xxxl-12">
<!-- Course details -->
<div class="training-info" *ngIf="courseHierarchy">
<h4 class="font-weight-bold">{{generaliseLabelService?.frmelmnts?.lbl?.courseDetails}}</h4>
<div class="training-relevant mb-16">
<h6 class="mt-0 mb-8 fsmall training-relevant__heading">
{{generaliseLabelService?.frmelmnts?.lbl?.courseRelevantFor}}</h6>
<div class="training-relevant__class mb-4 sb-color-gray-400 fnormal" *ngIf="courseHierarchy?.se_boards">
<span>{{resourceService?.frmelmnts?.lbl?.board | transposeTerms: 'frmelmnts.lbl.board' : resourceService?.selectedLang}}: </span><span>{{ courseHierarchy?.se_boards?.join(', ')}}</span>
</div>
<div class="training-relevant__medium mb-4 sb-color-gray-400 fnormal" *ngIf="courseHierarchy?.se_mediums">
<span>{{resourceService?.frmelmnts?.lbl?.medium | transposeTerms: 'frmelmnts.lbl.medium' : resourceService?.selectedLang}}: </span><span>{{ courseHierarchy?.se_mediums?.join(', ')}}</span>
</div>
<div class="training-relevant__subject mb-4 sb-color-gray-400 fnormal" *ngIf="courseHierarchy?.se_gradeLevels">
<span>{{resourceService?.frmelmnts?.lbl?.class | transposeTerms: 'frmelmnts.lbl.class' : resourceService?.selectedLang}}: </span><span>{{ courseHierarchy?.se_gradeLevels?.join(', ')}}</span>
</div>
<!-- <div class="training-relevant__medium mb-4 sb-color-gray-400 fnormal" *ngIf="courseHierarchy?.se_subjects">
<span>{{resourceService?.frmelmnts?.lbl?.subject}}: </span><span>{{ courseHierarchy?.se_subjects?.join(', ')}}</span>
</div> -->
<div class="training-relevant__medium mb-4 sb-color-gray-400 fnormal" *ngIf="courseHierarchy?.audience">
<span>{{resourceService?.frmelmnts?.lbl?.userType}}: </span><span>{{ courseHierarchy?.audience?.join(', ')}}</span>
</div>
<div *ngIf="courseHierarchy?.description">
<label class="fsmall mt-16 mb-8">{{resourceService?.frmelmnts?.lbl?.description}}</label>
<p *ngIf="!readMore" class="traning-relevant__description sb-color-gray-400 fnormal">
{{ courseHierarchy?.description | slice:0:200 }}
<span class="ui cardsKnowMore mouse-pointer"
*ngIf="courseHierarchy?.description?.length > 200 && readMore === false"
tabindex="0" (click)="readMore = !readMore;">{{resourceService?.frmelmnts?.lbl?.readmore}}</span>
</p>
<p *ngIf="readMore" class="traning-relevant__description sb-color-gray-400">
{{ courseHierarchy?.description}}
<span class="ui cardsKnowMore mouse-pointer"
tabindex="0" (click)="readMore = false;">{{resourceService?.frmelmnts?.lbl?.readless}}</span>
</p>
</div>
</div>
</div>
<div class="training-modules my-16">
<div class="training-modules__title d-flex flex-ai-center mb-16">
<h4 class="sb-pageSection-title sb-pageSection-title-light">
{{generaliseLabelService?.frmelmnts?.lbl?.coursestructure}}</h4>
<button *ngIf="!isExpandedAll"
class="sb-btn sb-btn-normal sb-btn-outline-primary ml-auto d-flex flex-ai-center" tabindex="0"
tabindex="0" (click)="isExpandedAll = true; logTelemetry('expand-all', courseHierarchy);"><img
src="assets/images/plus.png" class="mr-8" alt=""
width="12px">{{resourceService?.frmelmnts?.lbl?.expandAll}}</button>
<button *ngIf="isExpandedAll"
class="sb-btn sb-btn-normal sb-btn-outline-primary ml-auto d-flex flex-ai-center" tabindex="0"
tabindex="0" (click)="isExpandedAll = false; logTelemetry('collapse-all', courseHierarchy);"><img
src="assets/images/collapse.png" class="mr-8" alt=""
width="12px">{{resourceService?.frmelmnts?.lbl?.collapseAll}}</button>
</div>
<div class="course-consumption-modules-container" *ngFor="let item of courseHierarchy?.children; let index = index">
<sb-accordion [multi]="false">
<sb-accordion-item [expanded]="isExpanded(index)">
<sb-accordion-header class="sb-bg-color-gray-0">
<i class="icon-svg icon-svg--sm icon-tick mr-8"
*ngIf="enrolledCourse && item?.consumedContent === item?.contentCount">
<svg class="icon">
<use xlink:href="./assets/images/sprite.svg#circle-with-check-symbol"></use>
</svg>
</i>
{{item?.name}}
</sb-accordion-header>
<sb-accordion-body>
<div class="chapter-box sb-g">
<section class="sb-g-col-xs-12 sb-g-col-md-12 sb-g-col-lg-12 sb-g-col-xxxl-16">
<div *ngIf="item;else noContent">
<div
*ngIf="item?.mimeType !== 'application/vnd.ekstep.content-collection' && !item?.children?.length">
<div class="child-content-padding">
<sb-toc-card [content]="item" (tocCardClick)="navigateToContent($event, 'toc-card')"
[type]="cardType">
</sb-toc-card>
</div>
</div>
<div *ngIf="item?.children?.length">
<div *ngFor="let child of item?.children">
<div>
<sb-toc-child-item [childData]="child"
(tocCardClick)="navigateToContent($event, 'child-item')" [type]="cardType" class="sb-toc-child-item">
</sb-toc-child-item>
</div>
</div>
</div>
</div>
<ng-template #noContent>
<div class="heading">{{noContentMessage}}</div>
</ng-template>
</section>
</div>
</sb-accordion-body>
</sb-accordion-item>
</sb-accordion>
</div>
</div>
</div>
<div class="sb-course-details__info sb-g-col-xs-12 sb-g-col-md-3 sb-g-col-lg-3 sb-g-col-xxxl-4">
<app-public-batch-details [courseId]="courseId" [courseHierarchy]="courseHierarchy"
(allBatchDetails)="getAllBatchDetails($event)">
</app-public-batch-details>
<!-- Credits & License information -->
<div class="course-player--metadata text-left">
<app-course-info [courseHierarchy]="courseHierarchy"></app-course-info>
</div>
</div>
</div>
</div>
</div>
<div *ngIf="loader">
<app-loader [data]="loaderMessage"></app-loader>
</div>
<app-modal-wrapper *ngIf="showJoinTrainingModal" [config]="{disableClose: true, size: 'small', panelClass: 'material-modal'}"
(dismiss)="showJoinTrainingModal = false; logTelemetry('join-training-popup-close')" #joinTrainingModal>
<ng-template sbModalContent>
<div class="sb-mat__modal sb-mat__modal--small">
<div mat-dialog-title class="mb-0">
<div class="title">{{generaliseLabelService?.frmelmnts?.btn?.enroll}}</div>
<button aria-label="close dialog" mat-dialog-close class="close-btn"></button>
</div>
<div class="sb-mat__modal__content d-flex flex-ai-center flex-jc-center">
<div>
<p>{{generaliseLabelService?.frmelmnts?.lbl?.accessToLogin}}</p>
</div>
</div>
<div class="sb-mat__modal__actions"></div>
</div>
</ng-template>
</app-modal-wrapper>
<app-modal-wrapper *ngIf="shareLinkModal" [config]="{disableClose: false, panelClass: 'material-modal'}"
(dismiss)="closeSharePopup('close-share-link-popup')">
<ng-template sbModalContent>
<app-share-link [shareLink]="shareLink" [telemetryShareData]="telemetryShareData">
</app-share-link>
</ng-template>
</app-modal-wrapper>