File

src/app/modules/workspace/components/review-comments/review-comments.component.ts

Implements

OnInit OnChanges OnDestroy

Metadata

Index

Properties
Methods
Inputs
Outputs

Constructor

constructor(resourceService: ResourceService, toasterService: ToasterService, userService: UserService, reviewCommentsService: ReviewCommentsService)
Parameters :
Name Type Optional
resourceService ResourceService No
toasterService ToasterService No
userService UserService No
reviewCommentsService ReviewCommentsService No

Inputs

contentData
Type : ContentData
playerLoaded
Type : boolean
stageId
Type : string

Outputs

reviewCommentEvent
Type : EventEmitter

Methods

addReviewComments
addReviewComments()
Returns : void
focusOnInput
focusOnInput()
Returns : void
getReviewComments
getReviewComments()
Returns : any
ngOnChanges
ngOnChanges()
Returns : void
ngOnDestroy
ngOnDestroy()
Returns : void
ngOnInit
ngOnInit()
Returns : void
setInteractEventData
setInteractEventData()
Returns : void

Properties

commentInput
Type : ElementRef
Decorators :
@ViewChild('commentInput')
comments
Default value : new UntypedFormControl()
contentComments
Type : any
disableSubmitcommentsButton
Default value : true
disableTextArea
Default value : false
Public resourceService
Type : ResourceService
Public reviewCommentsService
Type : ReviewCommentsService
sortedComments
Type : any
Default value : {}
Public submitCommentsInteractEdata
Type : IInteractEventEdata

Creates a object of the form control

Public telemetryInteractObject
Type : IInteractEventObject
Public toasterService
Type : ToasterService
Public unsubscribe
Default value : new Subject<void>()
Public userService
Type : UserService
import { Component, OnInit, Input, OnChanges, OnDestroy, ViewChild, ElementRef, Output, EventEmitter } from '@angular/core';
import { UntypedFormControl } from '@angular/forms';
import { ResourceService, ToasterService, ContentData, ServerResponse } from '@sunbird/shared';
import { UserService } from '@sunbird/core';
import { ReviewCommentsService } from '../../services';
import { Subject } from 'rxjs';
import { takeUntil, map } from 'rxjs/operators';
import * as _ from 'lodash-es';
import { IInteractEventObject, IInteractEventEdata } from '@sunbird/telemetry';
import dayjs from 'dayjs';

@Component({
  selector: 'app-review-comments',
  templateUrl: './review-comments.component.html',
  styleUrls: ['./review-comments.component.scss']
})
export class ReviewCommentsComponent implements OnInit, OnChanges, OnDestroy {

  public unsubscribe = new Subject<void>();

  /**
	 * Creates a object of the form control
	 */
  public submitCommentsInteractEdata: IInteractEventEdata;

  public telemetryInteractObject: IInteractEventObject;

  comments = new UntypedFormControl();

  sortedComments: any = {};

  contentComments: any;

  disableSubmitcommentsButton = true;

  disableTextArea = false;

  @Input() contentData: ContentData;

  @Input() stageId: string;

  @Input() playerLoaded: boolean;

  @Output() reviewCommentEvent = new EventEmitter();

  @ViewChild('commentInput') commentInput: ElementRef;

  constructor(public resourceService: ResourceService, public toasterService: ToasterService,
    public userService: UserService, public reviewCommentsService: ReviewCommentsService,
  ) { }

  ngOnInit() {
    this.setInteractEventData();
    this.getReviewComments().pipe(takeUntil(this.unsubscribe)).subscribe(
        (data) => {
          this.sortedComments = data;
          this.reviewCommentEvent.emit(this.sortedComments ); // emit data to parent
          this.reviewCommentsService.contextDetails = {
            contentId: this.contentData.identifier,
            contentVer: this.contentData.pkgVersion ? this.contentData.pkgVersion.toString() : '0', // this should be version not versionKey
            contentType: this.contentData.mimeType
          };
        },
        (error) => this.toasterService.error(this.resourceService.messages.emsg.m0011));
  }
  ngOnChanges() {
    if (!this.stageId) {
      this.disableTextArea = true;
    } else {
      this.disableTextArea = false;
    }
    this.comments = new UntypedFormControl();
  }
  focusOnInput() {
    this.commentInput.nativeElement.focus();
  }
  getReviewComments() {
    // fetch all comments for content then show content related to stageID
    const requestBody = {
      request: {
        contextDetails: {
          contentId: this.contentData.identifier,
          contentVer: this.contentData.pkgVersion ? this.contentData.pkgVersion.toString() : '0', // this should be version not versionKey
          contentType: this.contentData.mimeType
        }
      }
    };
    return this.reviewCommentsService.getComments(requestBody).pipe(map((data) => {
      const commentList = _.get(data, 'result.comments');
      return commentList.reduce((accumulator, current) => {
        if (accumulator[current.stageId]) {
          accumulator[current.stageId].push(current);
        } else {
          accumulator[current.stageId] = [];
          accumulator[current.stageId].push(current);
        }
        return accumulator;
      }, {});
    }));
  }

  addReviewComments() {
    if (!this.stageId) { // if stageId not fetched, throw error
      this.toasterService.error(this.resourceService.messages.emsg.m0010);
      return;
    }
    if (!this.comments.value || !this.comments.value.trim()) {
      return;
    }
    this.disableTextArea = true;
    const requestBody: any = {
      request: {
        contextDetails: {
          contentId: this.contentData.identifier,
          contentVer: this.contentData.pkgVersion ? this.contentData.pkgVersion.toString() : '0', // this should be version not versionKey
          contentType: this.contentData.mimeType,
          stageId: this.stageId
        },
        body: this.comments.value,
        userId: this.userService.userProfile.userId,
        userInfo: {
          name: this.userService.userProfile.firstName + ' ' + this.userService.userProfile.lastName
        }
      }
    };
    if (this.userService.userProfile.avatar) {
      requestBody.request.userInfo.logo = this.userService.userProfile.avatar;
    }
    this.reviewCommentsService.createComment(requestBody).pipe(
      takeUntil(this.unsubscribe))
      .subscribe(
        (apiResponse: ServerResponse) => {
          this.disableTextArea = false;
          const newComment = {
            userId: this.userService.userProfile.userId,
            userInfo: {
              name: this.userService.userProfile.firstName + ' ' + this.userService.userProfile.lastName,
              logo: this.userService.userProfile.avatar,
            },
            body: this.comments.value,
            createdOn: dayjs().format()
          };
          if (this.sortedComments[this.stageId]) {
            this.sortedComments[this.stageId].push(newComment);
          } else {
            this.sortedComments[this.stageId] = [];
            this.sortedComments[this.stageId].push(newComment);
          }
          this.reviewCommentEvent.emit(this.sortedComments); // emit data to parent
          this.comments = new UntypedFormControl();
        },
        err => {
          this.disableTextArea = false;
          this.toasterService.error(this.resourceService.messages.emsg.m0010);
        }
      );
  }

  ngOnDestroy() {
    this.unsubscribe.next();
    this.unsubscribe.complete();
  }

  setInteractEventData() {
    this.submitCommentsInteractEdata = {
      id: 'submit-review-comments',
      type: 'click',
      pageid: 'upForReview-content-player'
    };
    this.telemetryInteractObject = {
      id: this.contentData.identifier,
      type: this.contentData.contentType,
      ver: this.contentData.pkgVersion ? this.contentData.pkgVersion.toString() : '1.0'
    };
  }
}
<div *ngIf="playerLoaded" class="review-comments-box br-4 bs-2 p-8">
  <div class="ui small comments scrolling p-5" *ngIf="sortedComments[stageId] && sortedComments[stageId].length else emptyComments" >
    <div class="comment" *ngFor="let commentList of sortedComments[stageId]">
      <span class="avatar">
        <img *ngIf="commentList.userInfo.logo" src="{{commentList.userInfo.logo | cdnprefixurl}}">
        <i *ngIf="!commentList.userInfo.logo" class="icon sb-icon-reviewer" aria-hidden="true"></i>
      </span>
      <div class="content pr-8">
          <div class="d-flex flex-jc-space-between mb-5">
            <span class="sb-color-primary">{{commentList.userInfo.name}}</span>
            <span class="date ui metadata">{{commentList.createdOn | date: 'MMM d'}}</span>
          </div>
          <div class="text">
            {{commentList.body}}
          </div>
      </div>
    </div>
  </div>
  <ng-template #emptyComments>
    <div tabindex="0" (click)="focusOnInput()" class="ui comments scrolling">
      <div class="placeholder">
          <i class="sb-icon-comment"></i>
          <span class="">{{resourceService.frmelmnts.lbl.emptycomments}}</span>
      </div>
    </div>
  </ng-template>
  <div class="ui form p-8">
    <div class="content mb-5 d-flex flex-jc-space-between">
      <span class="ui metadata">{{resourceService.frmelmnts.lbl.dropcomment}} </span>
      <span class="ui metadata" *ngIf="comments.value"> {{comments.value.trim().length}} / 250</span>
    </div>
    <div [ngClass]="{ 'disabled' : disableTextArea }" class="ui icon input width-100">
      <textarea [attr.disabled]="disableTextArea ? true : null" #commentInput maxlength="250" autocomplete="off" class="width-100 br-4 reply-box form-control"[formControl]="comments"></textarea>
      <i id="submit-review-comments" (click)="addReviewComments()" class="opacity-1 mr-5 right sb-icon-send icon link" aria-hidden="true" tabindex="0"
      appTelemetryInteract [telemetryInteractObject]="telemetryInteractObject" [telemetryInteractEdata]="submitCommentsInteractEdata"></i>
    </div>
  </div>
</div>
<div *ngIf="!playerLoaded" class="review-comments-box br-4 bs-2 p-8 review-comments-loader">
    <div class="ui active inverted dimmer">
      <div class="ui text loader">{{resourceService.frmelmnts.instn.t0080}}</div>
    </div>
</div>

./review-comments.component.scss

@use "@project-sunbird/sb-styles/assets/mixins/mixins" as *;

.review-comments-box {
    max-height: calc(100% - calculateRem(4px));
    position: absolute;
    top: calculateRem(15px);
    bottom: calculateRem(18px);
    left: calculateRem(15px);
    right: calculateRem(15px);	
}
.ui.dimmer{
    z-index: inherit;
}
.ui.comments.scrolling{
    height:calc(100% - calculateRem(115px));
    max-height: calc(100% - calculateRem(115px));
    overflow-y: auto;
}
.reply-box{
    height: calculateRem(60px) !important;
    max-height: calculateRem(60px) !important;
    min-height: calculateRem(10px) !important;
    resize: none !important;
    padding-right: 2.67142857em !important;
    border: calculateRem(1px) solid var(--gray-300) !important;
    border-radius: calculateRem(4px) !important;
}
.icon.sb-icon-send:before {
    font-size: calculateRem(18px);
    width: 1.671429em;
}
.sb-icon-comment:before {
    font-size: calculateRem(72px);
    width: calculateRem(72px);
    color: var(--secondary-color);
}
.icon.sb-icon-reviewer:before {
    font-size: calculateRem(30px);
    color: var(--primary-color);
}
textarea:focus+i.icon.sb-icon-send:before {
    color: var(--primary-color);
}
.ui.form textarea:focus {
    border-color:var(--primary-color) !important;
}
.comment:after {
    content: ""; 
    visibility: hidden;
    display: block;
    height: 0;
    clear: both;
}
.review-comments-loader {
    position: relative;
}
.placeholder{
    height:100%;
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""