MOBILE-3320 comments: Show comments as messages
This commit is contained in:
		
							parent
							
								
									e7d4588bcf
								
							
						
					
					
						commit
						5a15d1722f
					
				| @ -1,95 +0,0 @@ | ||||
| // (C) Copyright 2015 Moodle Pty Ltd.
 | ||||
| //
 | ||||
| // 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.
 | ||||
| 
 | ||||
| import { Component, ViewChild, ElementRef, Input } from '@angular/core'; | ||||
| import { CoreComments } from '@features/comments/services/comments'; | ||||
| import { CoreApp } from '@services/app'; | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { CoreDomUtils } from '@services/utils/dom'; | ||||
| import { CoreForms } from '@singletons/form'; | ||||
| import { ModalController } from '@singletons'; | ||||
| 
 | ||||
| /** | ||||
|  * Component that displays a text area for composing a comment. | ||||
|  */ | ||||
| @Component({ | ||||
|     selector: 'core-comments-add', | ||||
|     templateUrl: 'add.html', | ||||
| }) | ||||
| export class CoreCommentsAddComponent { | ||||
| 
 | ||||
|     @ViewChild('commentForm') formElement?: ElementRef; | ||||
| 
 | ||||
|     @Input() protected contextLevel!: string; | ||||
|     @Input() protected instanceId!: number; | ||||
|     @Input() protected componentName!: string; | ||||
|     @Input() protected itemId!: number; | ||||
|     @Input() protected area = ''; | ||||
|     @Input() content = ''; | ||||
| 
 | ||||
|     processing = false; | ||||
| 
 | ||||
|     /** | ||||
|      * Send the comment or store it offline. | ||||
|      * | ||||
|      * @param e Event. | ||||
|      */ | ||||
|     async addComment(e: Event): Promise<void> { | ||||
|         e.preventDefault(); | ||||
|         e.stopPropagation(); | ||||
| 
 | ||||
|         CoreApp.closeKeyboard(); | ||||
|         const loadingModal = await CoreDomUtils.showModalLoading('core.sending', true); | ||||
|         // Freeze the add comment button.
 | ||||
|         this.processing = true; | ||||
|         try { | ||||
|             const commentsResponse = await CoreComments.addComment( | ||||
|                 this.content, | ||||
|                 this.contextLevel, | ||||
|                 this.instanceId, | ||||
|                 this.componentName, | ||||
|                 this.itemId, | ||||
|                 this.area, | ||||
|             ); | ||||
| 
 | ||||
|             CoreForms.triggerFormSubmittedEvent( | ||||
|                 this.formElement, | ||||
|                 !!commentsResponse, | ||||
|                 CoreSites.getCurrentSiteId(), | ||||
|             ); | ||||
| 
 | ||||
|             ModalController.dismiss(commentsResponse).finally(() => { | ||||
|                 CoreDomUtils.showToast( | ||||
|                     commentsResponse ? 'core.comments.eventcommentcreated' : 'core.datastoredoffline', | ||||
|                     true, | ||||
|                     3000, | ||||
|                 ); | ||||
|             }); | ||||
|         } catch (error) { | ||||
|             CoreDomUtils.showErrorModal(error); | ||||
|             this.processing = false; | ||||
|         } finally { | ||||
|             loadingModal.dismiss(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Close modal. | ||||
|      */ | ||||
|     closeModal(): void { | ||||
|         CoreForms.triggerFormCancelledEvent(this.formElement, CoreSites.getCurrentSiteId()); | ||||
|         ModalController.dismiss(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,26 +0,0 @@ | ||||
| <ion-header> | ||||
|     <ion-toolbar> | ||||
|         <h2>{{ 'core.comments.addcomment' | translate }}</h2> | ||||
|         <ion-buttons slot="end"> | ||||
|             <ion-button fill="clear" (click)="closeModal()" [attr.aria-label]="'core.close' | translate"> | ||||
|                 <ion-icon name="fas-times" slot="icon-only" aria-hidden=true></ion-icon> | ||||
|             </ion-button> | ||||
|         </ion-buttons> | ||||
|     </ion-toolbar> | ||||
| </ion-header> | ||||
| <ion-content> | ||||
|     <form name="itemEdit" (ngSubmit)="addComment($event)" #commentForm> | ||||
|         <ion-item> | ||||
|             <ion-label> | ||||
|                 <ion-textarea placeholder="{{ 'core.comments.addcomment' | translate }}" rows="5" [(ngModel)]="content" | ||||
|                     name="content" required="required"> | ||||
|                 </ion-textarea> | ||||
|             </ion-label> | ||||
|         </ion-item> | ||||
|         <div class="ion-padding"> | ||||
|             <ion-button expand="block" type="submit" [disabled]="processing || content.length < 1"> | ||||
|                 {{ 'core.comments.savecomment' | translate }} | ||||
|             </ion-button> | ||||
|         </div> | ||||
|     </form> | ||||
| </ion-content> | ||||
| @ -14,20 +14,17 @@ | ||||
| 
 | ||||
| import { CoreSharedModule } from '@/core/shared.module'; | ||||
| import { NgModule } from '@angular/core'; | ||||
| import { CoreCommentsAddComponent } from './add/add-modal'; | ||||
| import { CoreCommentsCommentsComponent } from './comments/comments'; | ||||
| 
 | ||||
| @NgModule({ | ||||
|     declarations: [ | ||||
|         CoreCommentsCommentsComponent, | ||||
|         CoreCommentsAddComponent, | ||||
|     ], | ||||
|     imports: [ | ||||
|         CoreSharedModule, | ||||
|     ], | ||||
|     exports: [ | ||||
|         CoreCommentsCommentsComponent, | ||||
|         CoreCommentsAddComponent, | ||||
|     ], | ||||
| }) | ||||
| export class CoreCommentsComponentsModule {} | ||||
|  | ||||
| @ -34,75 +34,95 @@ | ||||
|             [message]="'core.comments.nocomments' | translate"> | ||||
|         </core-empty-box> | ||||
| 
 | ||||
|         <ion-card class="core-warning-card" *ngIf="hasOffline"> | ||||
|             <ion-item> | ||||
|                 <ion-icon name="fas-exclamation-triangle" slot="start" aria-hidden="true"></ion-icon> | ||||
|                 <ion-label> | ||||
|                     {{ 'core.thereisdatatosync' | translate:{$a: 'core.comments.comments' | translate | lowercase } }} | ||||
|                 </ion-label> | ||||
|             </ion-item> | ||||
|         </ion-card> | ||||
|         <!-- Load previous messages. --> | ||||
|         <core-infinite-loading [enabled]="canLoadMore" position="top" (action)="loadPrevious($event)" [error]="loadMoreError"> | ||||
|         </core-infinite-loading> | ||||
| 
 | ||||
|         <ion-card *ngIf="offlineComment" (click)="addComment($event)"> | ||||
|             <ion-item class="ion-text-wrap"> | ||||
|                 <core-user-avatar [user]="offlineComment" slot="start"></core-user-avatar> | ||||
|         <ion-list class="addon-messages-discussion-container"> | ||||
|             <ng-container *ngFor="let comment of comments; index as index; last as last"> | ||||
| 
 | ||||
|                 <p class="ion-text-center addon-messages-date" *ngIf="comment.showDate"> | ||||
|                     {{ comment.timecreated * 1000 | coreFormatDate: "strftimedayshort" }} | ||||
|                 </p> | ||||
| 
 | ||||
|                 <ion-item class="ion-text-wrap addon-message" | ||||
|                     [class.addon-message-mine]="comment.userid == currentUserId" | ||||
|                     [class.addon-message-not-mine]="comment.userid != currentUserId" | ||||
|                     [class.addon-message-no-user]="!comment.showUserData" | ||||
|                     [@coreSlideInOut]="comment.userid == currentUserId ? '' : 'fromLeft'"> | ||||
|                     <ion-label> | ||||
|                         <!-- User data. --> | ||||
|                         <h2 class="addon-message-user" *ngIf="comment.showUserData"> | ||||
|                             <core-user-avatar slot="start" [user]="comment" [linkProfile]="false"> | ||||
|                             </core-user-avatar> | ||||
|                             <div>{{ comment.fullname }}</div> | ||||
|                         </h2> | ||||
| 
 | ||||
|                         <p class="addon-message-text"> | ||||
|                             <core-format-text [text]="comment.content" [contextLevel]="contextLevel" [contextInstanceId]="instanceId" | ||||
|                                 [courseId]="courseId"> | ||||
|                             </core-format-text> | ||||
|                         </p> | ||||
|                     </ion-label> | ||||
|                     <ion-note> | ||||
|                         <ng-container *ngIf="!comment.deleted"> | ||||
|                             {{ comment.timecreated * 1000 | coreFormatDate: 'strftimetime' }} | ||||
|                         </ng-container> | ||||
|                         <ng-container *ngIf="comment.deleted"> | ||||
|                             <ion-icon name="fas-trash" aria-hidden="true"></ion-icon> <span class="ion-text-wrap"> | ||||
|                                 {{ 'core.deletedoffline' | translate }} | ||||
|                             </span> | ||||
|                         </ng-container> | ||||
|                     </ion-note> | ||||
|                     <div class="tail" *ngIf="comment.showTail"></div> | ||||
|                     <ion-button *ngIf="showDelete && !comment.deleted && comment.delete" slot="end" fill="clear" | ||||
|                         [@coreSlideInOut]="'fromRight'" color="danger" (click)="deleteComment($event, comment)" | ||||
|                         [attr.aria-label]="'core.delete' | translate" class="addon-messages-delete-button"> | ||||
|                         <ion-icon name="fas-trash" slot="icon-only" aria-hidden="true"></ion-icon> | ||||
|                     </ion-button> | ||||
|                     <ion-button *ngIf="showDelete && comment.deleted" slot="end" fill="clear" color="danger" | ||||
|                         (click)="undoDeleteComment($event, comment)" [attr.aria-label]="'core.restore' | translate" | ||||
|                         class="addon-messages-delete-button"> | ||||
|                         <ion-icon name="fas-undo-alt" slot="icon-only" aria-hidden="true"></ion-icon> | ||||
|                     </ion-button> | ||||
|                 </ion-item> | ||||
|             </ng-container> | ||||
| 
 | ||||
|             <ion-item | ||||
|                 *ngIf="offlineComment" | ||||
|                 class="ion-text-wrap addon-message addon-message-mine" | ||||
|             > | ||||
|                 <ion-label> | ||||
|                     <p class="item-heading">{{ offlineComment.fullname }}</p> | ||||
|                     <p> | ||||
|                         <ion-icon name="fas-clock" aria-hidden="true"></ion-icon> {{ 'core.notsent' | translate }} | ||||
|                     <!-- User data. --> | ||||
|                     <p class="ion-text-center"> | ||||
|                         <ion-icon name="fas-exclamation-triangle" aria-hidden="true"></ion-icon> | ||||
|                         {{ 'core.thereisdatatosync' | translate:{$a: 'core.comments.comments' | translate | lowercase } }} | ||||
|                     </p> | ||||
| 
 | ||||
|                     <p class="addon-message-text"> | ||||
|                         <core-format-text [text]="offlineComment.content" [contextLevel]="contextLevel" [contextInstanceId]="instanceId" | ||||
|                             [courseId]="courseId"> | ||||
|                         </core-format-text> | ||||
|                     </p> | ||||
|                 </ion-label> | ||||
|                 <ion-button *ngIf="showDelete" slot="end" fill="clear" [@coreSlideInOut]="'fromRight'" color="danger" | ||||
|                     (click)="deleteComment($event, offlineComment)" [attr.aria-label]="'core.delete' | translate"> | ||||
|                 <ion-note> | ||||
|                     <ion-icon name="fas-clock" aria-hidden="true"></ion-icon> {{ 'core.notsent' | translate }} | ||||
|                 </ion-note> | ||||
|                 <div class="tail"></div> | ||||
|                 <ion-button *ngIf="showDelete" slot="end" fill="clear" | ||||
|                     [@coreSlideInOut]="'fromRight'" color="danger" (click)="deleteComment($event, offlineComment)" | ||||
|                     [attr.aria-label]="'core.delete' | translate" class="addon-messages-delete-button"> | ||||
|                     <ion-icon name="fas-trash" slot="icon-only" aria-hidden="true"></ion-icon> | ||||
|                 </ion-button> | ||||
|             </ion-item> | ||||
|             <ion-item class="ion-text-wrap"> | ||||
|                 <ion-label> | ||||
|                     <core-format-text clean="true" [text]="offlineComment.content" [contextLevel]="contextLevel" | ||||
|                         [contextInstanceId]="instanceId" [courseId]="courseId"> | ||||
|                     </core-format-text> | ||||
|                 </ion-label> | ||||
|             </ion-item> | ||||
|         </ion-card> | ||||
|         </ion-list> | ||||
| 
 | ||||
|         <ion-card *ngFor="let comment of comments"> | ||||
|             <ion-item class="ion-text-wrap"> | ||||
|                 <core-user-avatar [user]="comment" slot="start"></core-user-avatar> | ||||
|                 <ion-label> | ||||
|                     <p class="item-heading">{{ comment.fullname }}</p> | ||||
|                     <p *ngIf="!comment.deleted">{{ comment.timecreated * 1000 | coreFormatDate: 'strftimerecentfull' }}</p> | ||||
|                     <p *ngIf="comment.deleted"> | ||||
|                         <ion-icon name="fas-trash" aria-hidden="true"></ion-icon> <span class="ion-text-wrap"> | ||||
|                             {{ 'core.deletedoffline' | translate }} | ||||
|                         </span> | ||||
|                     </p> | ||||
|                 </ion-label> | ||||
|                 <ion-button *ngIf="showDelete && !comment.deleted && comment.delete" slot="end" fill="clear" | ||||
|                     [@coreSlideInOut]="'fromRight'" color="danger" (click)="deleteComment($event, comment)" | ||||
|                     [attr.aria-label]="'core.delete' | translate"> | ||||
|                     <ion-icon name="fas-trash" slot="icon-only" aria-hidden="true"></ion-icon> | ||||
|                 </ion-button> | ||||
|                 <ion-button *ngIf="showDelete && comment.deleted" slot="end" fill="clear" color="danger" | ||||
|                     (click)="undoDeleteComment($event, comment)" [attr.aria-label]="'core.restore' | translate"> | ||||
|                     <ion-icon name="fas-undo-alt" slot="icon-only" aria-hidden="true"></ion-icon> | ||||
|                 </ion-button> | ||||
|             </ion-item> | ||||
|             <ion-item class="ion-text-wrap"> | ||||
|                 <ion-label> | ||||
|                     <core-format-text [text]="comment.content" [contextLevel]="contextLevel" [contextInstanceId]="instanceId" | ||||
|                         [courseId]="courseId"> | ||||
|                     </core-format-text> | ||||
|                 </ion-label> | ||||
|             </ion-item> | ||||
|         </ion-card> | ||||
| 
 | ||||
|         <core-infinite-loading [enabled]="canLoadMore" (action)="loadMore($event)" [error]="loadMoreError"></core-infinite-loading> | ||||
|     </core-loading> | ||||
| 
 | ||||
|     <ion-fab slot="fixed" core-fab vertical="bottom" horizontal="end" *ngIf="canAddComments"> | ||||
|         <ion-fab-button (click)="addComment($event)" [attr.aria-label]="'core.comments.addcomment' | translate"> | ||||
|             <ion-icon name="fas-plus" aria-hidden="true"></ion-icon> | ||||
|         </ion-fab-button> | ||||
|     </ion-fab> | ||||
| </ion-content> | ||||
| <ion-footer color="light" class="footer-adjustable" *ngIf="commentsLoaded"> | ||||
|     <ion-toolbar color="light"> | ||||
|         <core-send-message-form [sendDisabled]="sending" [message]="newComment" | ||||
|             (onSubmit)="addComment($event)" [placeholder]="'core.comments.addcomment' | translate"> | ||||
|         </core-send-message-form> | ||||
|     </ion-toolbar> | ||||
| </ion-footer> | ||||
|  | ||||
| @ -15,7 +15,7 @@ | ||||
| import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; | ||||
| import { CoreEventObserver, CoreEvents } from '@singletons/events'; | ||||
| import { CoreAnimations } from '@components/animations'; | ||||
| import { ActivatedRoute, Params } from '@angular/router'; | ||||
| import { ActivatedRoute } from '@angular/router'; | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { | ||||
|     CoreComments, | ||||
| @ -33,13 +33,14 @@ import { CoreNavigator } from '@services/navigator'; | ||||
| import { Translate } from '@singletons'; | ||||
| import { CoreUtils } from '@services/utils/utils'; | ||||
| import { CoreDomUtils } from '@services/utils/dom'; | ||||
| import { CoreUser, CoreUserProfile } from '@features/user/services/user'; | ||||
| import { CoreUser } from '@features/user/services/user'; | ||||
| import { CoreTextUtils } from '@services/utils/text'; | ||||
| import { CoreError } from '@classes/errors/error'; | ||||
| import { CoreCommentsOffline } from '@features/comments/services/comments-offline'; | ||||
| import { CoreCommentsDBRecord } from '@features/comments/services/database/comments'; | ||||
| import { CoreTimeUtils } from '@services/utils/time'; | ||||
| import { CoreCommentsAddComponent } from '@features/comments/components/add/add-modal'; | ||||
| import { CoreApp } from '@services/app'; | ||||
| import moment from 'moment'; | ||||
| 
 | ||||
| /** | ||||
|  * Page that displays comments. | ||||
| @ -48,12 +49,13 @@ import { CoreCommentsAddComponent } from '@features/comments/components/add/add- | ||||
|     selector: 'page-core-comments-viewer', | ||||
|     templateUrl: 'viewer.html', | ||||
|     animations: [CoreAnimations.SLIDE_IN_OUT], | ||||
|     styleUrls: ['viewer.scss'], | ||||
| }) | ||||
| export class CoreCommentsViewerPage implements OnInit, OnDestroy { | ||||
| 
 | ||||
|     @ViewChild(IonContent) content?: IonContent; | ||||
| 
 | ||||
|     comments: CoreCommentsDataWithUser[] = []; | ||||
|     comments: CoreCommentsDataToDisplay[] = []; | ||||
|     commentsLoaded = false; | ||||
|     contextLevel!: ContextLevel; | ||||
|     instanceId!: number; | ||||
| @ -73,10 +75,12 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { | ||||
|     syncIcon = CoreConstants.ICON_LOADING; | ||||
|     offlineComment?: CoreCommentsOfflineWithUser; | ||||
|     currentUserId: number; | ||||
|     sending = false; | ||||
|     newComment = ''; | ||||
| 
 | ||||
|     protected addDeleteCommentsAvailable = false; | ||||
|     protected syncObserver?: CoreEventObserver; | ||||
|     protected currentUser?: CoreUserProfile; | ||||
|     protected viewDestroyed = false; | ||||
| 
 | ||||
|     constructor( | ||||
|         protected route: ActivatedRoute, | ||||
| @ -95,8 +99,6 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { | ||||
|                 this.refreshIcon = CoreConstants.ICON_LOADING; | ||||
|                 this.syncIcon = CoreConstants.ICON_LOADING; | ||||
| 
 | ||||
|                 this.content?.scrollToTop(); | ||||
| 
 | ||||
|                 this.page = 0; | ||||
|                 this.comments = []; | ||||
|                 this.fetchComments(false); | ||||
| @ -151,7 +153,7 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { | ||||
|             ); | ||||
|             this.canAddComments = this.addDeleteCommentsAvailable && !!commentsResponse.canpost; | ||||
| 
 | ||||
|             let comments = commentsResponse.comments.sort((a, b) => b.timecreated - a.timecreated); | ||||
|             let comments = commentsResponse.comments.sort((a, b) => a.timecreated - b.timecreated); | ||||
|             if (typeof commentsResponse.count != 'undefined') { | ||||
|                 this.canLoadMore = (this.comments.length + comments.length) < commentsResponse.count; | ||||
|             } else { | ||||
| @ -162,7 +164,13 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { | ||||
| 
 | ||||
|             comments = await Promise.all(comments.map((comment) => this.loadCommentProfile(comment))); | ||||
| 
 | ||||
|             this.comments = this.comments.concat(comments); | ||||
|             this.comments = comments.concat(this.comments); | ||||
| 
 | ||||
|             this.comments.forEach((comment, index) => { | ||||
|                 comment.showDate = this.showDate(comment, this.comments[index - 1]); | ||||
|                 comment.showUserData = this.showUserData(comment, this.comments[index - 1]); | ||||
|                 comment.showTail = this.showTail(comment, this.comments[index + 1]); | ||||
|             }); | ||||
| 
 | ||||
|             this.canDeleteComments = this.addDeleteCommentsAvailable && | ||||
|                 (this.hasOffline || this.comments.some((comment) => !!comment.delete)); | ||||
| @ -179,6 +187,10 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { | ||||
|             this.commentsLoaded = true; | ||||
|             this.refreshIcon = CoreConstants.ICON_REFRESH; | ||||
|             this.syncIcon = CoreConstants.ICON_SYNC; | ||||
| 
 | ||||
|             if (this.page == 0) { | ||||
|                 this.scrollToBottom(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| @ -189,7 +201,7 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { | ||||
|      * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading. | ||||
|      * @return Resolved when done. | ||||
|      */ | ||||
|     loadMore(infiniteComplete?: () => void): Promise<void> { | ||||
|     loadPrevious(infiniteComplete?: () => void): Promise<void> { | ||||
|         this.page++; | ||||
|         this.canLoadMore = false; | ||||
| 
 | ||||
| @ -262,48 +274,64 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Add a new comment to the list. | ||||
|      * Send the comment or store it offline. | ||||
|      * | ||||
|      * @param e Event. | ||||
|      * @param text Comment text to add. | ||||
|      */ | ||||
|     async addComment(e: Event): Promise<void> { | ||||
|         e.preventDefault(); | ||||
|         e.stopPropagation(); | ||||
|     async addComment(text: string): Promise<void> { | ||||
|         CoreApp.closeKeyboard(); | ||||
|         const loadingModal = await CoreDomUtils.showModalLoading('core.sending', true); | ||||
|         // Freeze the add comment button.
 | ||||
|         this.sending = true; | ||||
|         try { | ||||
|             const commentsResponse = await CoreComments.addComment( | ||||
|                 text, | ||||
|                 this.contextLevel, | ||||
|                 this.instanceId, | ||||
|                 this.componentName, | ||||
|                 this.itemId, | ||||
|                 this.area, | ||||
|             ); | ||||
| 
 | ||||
|         const params: Params = { | ||||
|             contextLevel: this.contextLevel, | ||||
|             instanceId: this.instanceId, | ||||
|             componentName: this.componentName, | ||||
|             itemId: this.itemId, | ||||
|             area: this.area, | ||||
|             content: this.offlineComment ? this.offlineComment!.content : '', | ||||
|         }; | ||||
|             CoreDomUtils.showToast( | ||||
|                 commentsResponse ? 'core.comments.eventcommentcreated' : 'core.datastoredoffline', | ||||
|                 true, | ||||
|                 3000, | ||||
|             ); | ||||
| 
 | ||||
|         const comment = await CoreDomUtils.openModal<CoreCommentsDataWithUser>({ | ||||
|             component: CoreCommentsAddComponent, | ||||
|             componentProps: params, | ||||
|         }); | ||||
|             if (commentsResponse) { | ||||
|                 this.invalidateComments(); | ||||
| 
 | ||||
|         if (comment) { | ||||
|             this.invalidateComments(); | ||||
|                 const addedComments = await this.loadCommentProfile(commentsResponse); | ||||
|                 addedComments.showDate = this.showDate(addedComments, this.comments[this.comments.length - 1]); | ||||
|                 addedComments.showUserData = this.showUserData(addedComments, this.comments[this.comments.length - 1]); | ||||
|                 addedComments.showTail = this.showTail(addedComments, this.comments[this.comments.length + 1]); | ||||
| 
 | ||||
|             const addedComments = await this.loadCommentProfile(comment); | ||||
|             // Add the comment to the top.
 | ||||
|             this.comments = [addedComments].concat(this.comments); | ||||
|             this.canDeleteComments = this.addDeleteCommentsAvailable; | ||||
|                 // Add the comment to the top.
 | ||||
|                 this.comments = this.comments.concat([addedComments]); | ||||
|                 this.canDeleteComments = this.addDeleteCommentsAvailable; | ||||
| 
 | ||||
|             CoreEvents.trigger(CoreCommentsProvider.COMMENTS_COUNT_CHANGED_EVENT, { | ||||
|                 contextLevel: this.contextLevel, | ||||
|                 instanceId: this.instanceId, | ||||
|                 component: this.componentName, | ||||
|                 itemId: this.itemId, | ||||
|                 area: this.area, | ||||
|                 countChange: 1, | ||||
|             }, CoreSites.getCurrentSiteId()); | ||||
|                 CoreEvents.trigger(CoreCommentsProvider.COMMENTS_COUNT_CHANGED_EVENT, { | ||||
|                     contextLevel: this.contextLevel, | ||||
|                     instanceId: this.instanceId, | ||||
|                     component: this.componentName, | ||||
|                     itemId: this.itemId, | ||||
|                     area: this.area, | ||||
|                     countChange: 1, | ||||
|                 }, CoreSites.getCurrentSiteId()); | ||||
| 
 | ||||
|         } else if (comment === false) { | ||||
|             // Comments added in offline mode.
 | ||||
|             return this.loadOfflineData(); | ||||
|             } else if (commentsResponse === false) { | ||||
|                 // Comments added in offline mode.
 | ||||
|                 await this.loadOfflineData(); | ||||
|             } | ||||
|         } catch (error) { | ||||
|             CoreDomUtils.showErrorModal(error); | ||||
|         } finally { | ||||
|             loadingModal.dismiss(); | ||||
|             this.sending = false; | ||||
| 
 | ||||
|             // New comments.
 | ||||
|             this.scrollToBottom(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -313,7 +341,7 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { | ||||
|      * @param e Click event. | ||||
|      * @param comment Comment to delete. | ||||
|      */ | ||||
|     async deleteComment(e: Event, comment: CoreCommentsDataWithUser | CoreCommentsOfflineWithUser): Promise<void> { | ||||
|     async deleteComment(e: Event, comment: CoreCommentsDataToDisplay | CoreCommentsOfflineWithUser): Promise<void> { | ||||
|         e.preventDefault(); | ||||
|         e.stopPropagation(); | ||||
| 
 | ||||
| @ -397,7 +425,7 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { | ||||
|      * @param comment Comment object. | ||||
|      * @return Promise resolved with modified comment when done. | ||||
|      */ | ||||
|     protected async loadCommentProfile(comment: CoreCommentsDataWithUser): Promise<CoreCommentsDataWithUser> { | ||||
|     protected async loadCommentProfile(comment: CoreCommentsDataToDisplay): Promise<CoreCommentsDataToDisplay> { | ||||
|         // Get the user profile image.
 | ||||
|         try { | ||||
|             const user = await CoreUser.getProfile(comment.userid!, undefined, true); | ||||
| @ -411,6 +439,54 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check if the user info should be displayed for the current message. | ||||
|      * User data is only displayed if the previous message was from another user. | ||||
|      * | ||||
|      * @param comment Comment object. | ||||
|      * @param prevComment Previous comment object. | ||||
|      * @return Whether user data should be shown. | ||||
|      */ | ||||
|     protected showUserData( | ||||
|         comment: CoreCommentsDataToDisplay, | ||||
|         prevComment?: CoreCommentsDataToDisplay, | ||||
|     ): boolean { | ||||
|         return comment.userid != this.currentUserId && (!prevComment || prevComment.userid != comment.userid || !!comment.showDate); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check if a css tail should be shown. | ||||
|      * | ||||
|      * @param comment Comment object. | ||||
|      * @param nextComment Previous comment object. | ||||
|      * @return Whether user data should be shown. | ||||
|      */ | ||||
|     protected showTail( | ||||
|         comment: CoreCommentsDataToDisplay, | ||||
|         nextComment?: CoreCommentsDataToDisplay, | ||||
|     ): boolean { | ||||
|         return !nextComment || nextComment.userid != comment.userid || !!nextComment.showDate; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check if the date should be displayed between messages (when the day changes at midnight for example). | ||||
|      * | ||||
|      * @param comment Comment object. | ||||
|      * @param prevComment Previous comment object. | ||||
|      * @return True if messages are from diferent days, false othetwise. | ||||
|      */ | ||||
|     protected showDate( | ||||
|         comment: CoreCommentsDataToDisplay, | ||||
|         prevComment?: CoreCommentsDataToDisplay, | ||||
|     ): boolean { | ||||
|         if (!prevComment) { | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         // Check if day has changed.
 | ||||
|         return !moment(comment.timecreated * 1000).isSame(prevComment.timecreated * 1000, 'day'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Load offline comments. | ||||
|      * | ||||
| @ -434,14 +510,10 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             if (!this.currentUser) { | ||||
|                 this.currentUser = await CoreUser.getProfile(this.currentUserId, undefined, true); | ||||
|             if (this.newComment == '') { | ||||
|                 this.newComment = this.offlineComment!.content; | ||||
|             } | ||||
| 
 | ||||
|             if (this.currentUser) { | ||||
|                 this.offlineComment!.profileimageurl = this.currentUser.profileimageurl; | ||||
|                 this.offlineComment!.fullname = this.currentUser.fullname; | ||||
|             } | ||||
|             this.offlineComment!.userid = this.currentUserId; | ||||
| 
 | ||||
|             return; | ||||
| @ -481,7 +553,7 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { | ||||
|      * @param e Click event. | ||||
|      * @param comment Comment to delete. | ||||
|      */ | ||||
|     async undoDeleteComment(e: Event, comment: CoreCommentsDataWithUser): Promise<void> { | ||||
|     async undoDeleteComment(e: Event, comment: CoreCommentsDataToDisplay): Promise<void> { | ||||
|         e.preventDefault(); | ||||
|         e.stopPropagation(); | ||||
| 
 | ||||
| @ -491,6 +563,18 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { | ||||
|         this.showDelete = false; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Scroll bottom when render has finished. | ||||
|      */ | ||||
|     protected scrollToBottom(): void { | ||||
|         // Need a timeout to leave time to the view to be rendered.
 | ||||
|         setTimeout(() => { | ||||
|             if (!this.viewDestroyed) { | ||||
|                 this.content?.scrollToBottom(); | ||||
|             } | ||||
|         }, 100); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Toggle delete. | ||||
|      */ | ||||
| @ -502,15 +586,19 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { | ||||
|      * Page destroyed. | ||||
|      */ | ||||
|     ngOnDestroy(): void { | ||||
|         this.syncObserver && this.syncObserver.off(); | ||||
|         this.syncObserver?.off(); | ||||
|         this.viewDestroyed = true; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| export type CoreCommentsDataWithUser = CoreCommentsData & { | ||||
| export type CoreCommentsDataToDisplay = CoreCommentsData & { | ||||
|     profileimageurl?: string; | ||||
|     fullname?: string; | ||||
|     deleted?: boolean; | ||||
|     showDate?: boolean; | ||||
|     showTail?: boolean; | ||||
|     showUserData?: boolean; | ||||
| }; | ||||
| 
 | ||||
| export type CoreCommentsOfflineWithUser = CoreCommentsDBRecord & { | ||||
|  | ||||
							
								
								
									
										1
									
								
								src/core/features/comments/pages/viewer/viewer.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/core/features/comments/pages/viewer/viewer.scss
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| @import "~theme/components/discussion.scss"; | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user