MOBILE-3793 forum: Make edit post more consistent
Now edit online and offline have the same behaviour, and the original post disappears when editing it
This commit is contained in:
		
							parent
							
								
									5fa280fe32
								
							
						
					
					
						commit
						44bc62e0a2
					
				| @ -21,7 +21,6 @@ import { CoreTagComponentsModule } from '@features/tag/components/components.mod | ||||
| import { CoreRatingComponentsModule } from '@features/rating/components/components.module'; | ||||
| 
 | ||||
| import { AddonModForumDiscussionOptionsMenuComponent } from './discussion-options-menu/discussion-options-menu'; | ||||
| import { AddonModForumEditPostComponent } from './edit-post/edit-post'; | ||||
| import { AddonModForumIndexComponent } from './index/index'; | ||||
| import { AddonModForumPostComponent } from './post/post'; | ||||
| import { AddonModForumPostOptionsMenuComponent } from './post-options-menu/post-options-menu'; | ||||
| @ -30,7 +29,6 @@ import { AddonModForumSortOrderSelectorComponent } from './sort-order-selector/s | ||||
| @NgModule({ | ||||
|     declarations: [ | ||||
|         AddonModForumDiscussionOptionsMenuComponent, | ||||
|         AddonModForumEditPostComponent, | ||||
|         AddonModForumIndexComponent, | ||||
|         AddonModForumPostComponent, | ||||
|         AddonModForumPostOptionsMenuComponent, | ||||
|  | ||||
| @ -1,60 +0,0 @@ | ||||
| <ion-header> | ||||
|     <ion-toolbar> | ||||
|         <h2>{{ 'addon.mod_forum.yourreply' | 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 #editFormEl> | ||||
|         <ion-item> | ||||
|             <ion-label position="stacked">{{ 'addon.mod_forum.subject' | translate }}</ion-label> | ||||
|             <ion-input type="text" [placeholder]="'addon.mod_forum.subject' | translate" [(ngModel)]="replyData.subject" name="subject"> | ||||
|             </ion-input> | ||||
|         </ion-item> | ||||
|         <ion-item> | ||||
|             <ion-label position="stacked">{{ 'addon.mod_forum.message' | translate }}</ion-label> | ||||
|             <core-rich-text-editor elementId="message" | ||||
|                 [name]="'mod_forum_reply_' + replyData.id" [control]="messageControl" | ||||
|                 [placeholder]="'addon.mod_forum.replyplaceholder' | translate" [autoSave]="true" | ||||
|                 [component]="component" [componentId]="componentId" [draftExtraParams]="{edit: replyData.id}" | ||||
|                 contextLevel="module" [contextInstanceId]="forum.cmid" | ||||
|                 (contentChanged)="onMessageChange($event)"> | ||||
|             </core-rich-text-editor> | ||||
|         </ion-item> | ||||
|         <ion-item | ||||
|             button class="divider ion-text-wrap" | ||||
|             (click)="toggleAdvanced()" | ||||
|             role="heading" | ||||
|             detail="false" | ||||
|             [attr.aria-expanded]="advanced" | ||||
|             aria-controls="addon-mod-forum-advanced" | ||||
|             [attr.aria-label]="(advanced ? 'core.hideadvanced' : 'core.showadvanced') | translate" | ||||
|         > | ||||
|             <ion-icon *ngIf="!advanced" name="fas-caret-right" flip-rtl slot="start" aria-hidden="true"></ion-icon> | ||||
|             <ion-icon *ngIf="advanced" name="fas-caret-down" slot="start" aria-hidden="true"></ion-icon> | ||||
|             <ion-label><h2>{{ 'addon.mod_forum.advanced' | translate }}</h2></ion-label> | ||||
|         </ion-item> | ||||
|         <div *ngIf="advanced" id="addon-mod-forum-advanced"> | ||||
|             <core-attachments *ngIf="forum.id && forum.maxattachments > 0" | ||||
|                 [maxSize]="forum.maxbytes" [maxSubmissions]="forum.maxattachments" [allowOffline]="true" [files]="replyData.files" | ||||
|                 [component]="component" [componentId]="forum.cmid" [courseId]="forum.course"> | ||||
|             </core-attachments> | ||||
|         </div> | ||||
|         <ion-grid> | ||||
|             <ion-row> | ||||
|                 <ion-col> | ||||
|                     <ion-button expand="block" (click)="reply($event)" [disabled]="replyData.subject == '' || replyData.message == null"> | ||||
|                         {{ 'core.savechanges' | translate }} | ||||
|                     </ion-button> | ||||
|                 </ion-col> | ||||
|                 <ion-col> | ||||
|                     <ion-button expand="block" color="light" (click)="closeModal()">{{ 'core.cancel' | translate }}</ion-button> | ||||
|                 </ion-col> | ||||
|             </ion-row> | ||||
|         </ion-grid> | ||||
|     </form> | ||||
| </ion-content> | ||||
| @ -1,150 +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, OnInit } from '@angular/core'; | ||||
| import { FormControl } from '@angular/forms'; | ||||
| import { CoreFileUploader } from '@features/fileuploader/services/fileuploader'; | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { CoreDomUtils } from '@services/utils/dom'; | ||||
| import { ModalController, Translate } from '@singletons'; | ||||
| import { AddonModForumData, AddonModForumPost, AddonModForumReply } from '@addons/mod/forum/services/forum'; | ||||
| import { AddonModForumHelper } from '@addons/mod/forum/services/forum-helper'; | ||||
| import { CoreForms } from '@singletons/form'; | ||||
| import { CoreFileEntry } from '@services/file-helper'; | ||||
| 
 | ||||
| /** | ||||
|  * Page that displays a form to edit discussion post. | ||||
|  */ | ||||
| @Component({ | ||||
|     selector: 'addon-mod-forum-edit-post', | ||||
|     templateUrl: 'edit-post.html', | ||||
| }) | ||||
| export class AddonModForumEditPostComponent implements OnInit { | ||||
| 
 | ||||
|     @ViewChild('editFormEl') formElement!: ElementRef; | ||||
| 
 | ||||
|     @Input() component!: string; // Component this post belong to.
 | ||||
|     @Input() componentId!: number; // Component ID.
 | ||||
|     @Input() forum!: AddonModForumData; // The forum the post belongs to. Required for attachments and offline posts.
 | ||||
|     @Input() post!: AddonModForumPost; | ||||
| 
 | ||||
|     messageControl = new FormControl(); | ||||
|     advanced = false; // Display all form fields.
 | ||||
|     replyData!: AddonModForumReply; | ||||
|     originalData!: Omit<AddonModForumReply, 'id'>; // Object with the original post data. Usually shared between posts.
 | ||||
| 
 | ||||
|     protected forceLeave = false; // To allow leaving the page without checking for changes.
 | ||||
| 
 | ||||
|     ngOnInit(): void { | ||||
|         // @todo Override android back button to show confirmation before dismiss.
 | ||||
| 
 | ||||
|         this.replyData = { | ||||
|             id: this.post.id, | ||||
|             subject: this.post.subject, | ||||
|             message: this.post.message, | ||||
|             files: this.post.attachments || [], | ||||
|         }; | ||||
| 
 | ||||
|         // Delete the local files from the tmp folder if any.
 | ||||
|         CoreFileUploader.clearTmpFiles(this.replyData.files as CoreFileEntry[]); | ||||
| 
 | ||||
|         // Update rich text editor.
 | ||||
|         this.messageControl.setValue(this.replyData.message); | ||||
| 
 | ||||
|         // Update original data.
 | ||||
|         this.originalData = { | ||||
|             subject: this.replyData.subject, | ||||
|             message: this.replyData.message, | ||||
|             files: this.replyData.files.slice(), | ||||
|         }; | ||||
| 
 | ||||
|         // Show advanced fields if any of them has not the default value.
 | ||||
|         this.advanced = this.replyData.files.length > 0; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Message changed. | ||||
|      * | ||||
|      * @param text The new text. | ||||
|      */ | ||||
|     onMessageChange(text: string): void { | ||||
|         this.replyData.message = text; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Close modal. | ||||
|      * | ||||
|      * @param data Data to return to the page. | ||||
|      */ | ||||
|     async closeModal(data?: AddonModForumReply): Promise<void> { | ||||
|         const confirmDismiss = await this.confirmDismiss(); | ||||
| 
 | ||||
|         if (!confirmDismiss) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (data) { | ||||
|             CoreForms.triggerFormSubmittedEvent(this.formElement, false, CoreSites.getCurrentSiteId()); | ||||
|         } else { | ||||
|             CoreForms.triggerFormCancelledEvent(this.formElement, CoreSites.getCurrentSiteId()); | ||||
|         } | ||||
| 
 | ||||
|         ModalController.dismiss(data); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Reply to this post. | ||||
|      * | ||||
|      * @param e Click event. | ||||
|      */ | ||||
|     reply(e: Event): void { | ||||
|         e.preventDefault(); | ||||
|         e.stopPropagation(); | ||||
| 
 | ||||
|         // Close the modal, sending the input data.
 | ||||
|         this.forceLeave = true; | ||||
|         this.closeModal(this.replyData); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Show or hide advanced form fields. | ||||
|      */ | ||||
|     toggleAdvanced(): void { | ||||
|         this.advanced = !this.advanced; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check if we can leave the page or not. | ||||
|      * | ||||
|      * @return Resolved if we can leave it, rejected if not. | ||||
|      */ | ||||
|     private async confirmDismiss(): Promise<boolean> { | ||||
|         if (this.forceLeave || !AddonModForumHelper.hasPostDataChanged(this.replyData, this.originalData)) { | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             // Show confirmation if some data has been modified.
 | ||||
|             await CoreDomUtils.showConfirm(Translate.instant('core.confirmcanceledit')); | ||||
| 
 | ||||
|             // Delete the local files from the tmp folder.
 | ||||
|             CoreFileUploader.clearTmpFiles(this.replyData.files as CoreFileEntry[]); | ||||
| 
 | ||||
|             return true; | ||||
|         } catch (error) { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -122,11 +122,7 @@ export class AddonModForumPostOptionsMenuComponent implements OnInit, OnDestroy | ||||
|      * Edit a post. | ||||
|      */ | ||||
|     editPost(): void { | ||||
|         if (!this.offlinePost) { | ||||
|             PopoverController.dismiss({ action: 'edit' }); | ||||
|         } else { | ||||
|             PopoverController.dismiss({ action: 'editoffline' }); | ||||
|         } | ||||
|         PopoverController.dismiss({ action: 'edit' }); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <div class="addon-mod_forum-post"> | ||||
|     <ng-container *ngIf="!replyData.isEditing || !showForm"> | ||||
|     <ng-container *ngIf="!formData.isEditing || !showForm"> | ||||
|         <ion-card-header class="ion-text-wrap ion-no-padding" id="addon-mod_forum-post-{{post.id}}"> | ||||
|             <ion-item class="ion-text-wrap" [class.highlight]="highlight" lines="none"> | ||||
|                 <ion-label> | ||||
| @ -94,7 +94,7 @@ | ||||
|                 <ion-label> | ||||
|                     <ion-button fill="clear" size="small" | ||||
|                         [attr.aria-controls]="'addon-forum-reply-edit-form-' + uniqueId" | ||||
|                         [attr.aria-expanded]="replyData.replyingTo === post.id" | ||||
|                         [attr.aria-expanded]="formData.replyingTo === post.id" | ||||
|                         (click)="showReplyForm($event)"> | ||||
|                         <ion-icon name="fas-reply" slot="start" aria-hidden="true"></ion-icon> | ||||
|                         {{ 'addon.mod_forum.reply' | translate }} | ||||
| @ -108,7 +108,7 @@ | ||||
|         [id]="'addon-forum-reply-edit-form-' + uniqueId" #replyFormEl> | ||||
|         <ion-item> | ||||
|             <ion-label position="stacked">{{ 'addon.mod_forum.subject' | translate }}</ion-label> | ||||
|             <ion-input type="text" [placeholder]="'addon.mod_forum.subject' | translate" [(ngModel)]="replyData.subject" | ||||
|             <ion-input type="text" [placeholder]="'addon.mod_forum.subject' | translate" [(ngModel)]="formData.subject" | ||||
|                 name="subject"> | ||||
|             </ion-input> | ||||
|         </ion-item> | ||||
| @ -123,7 +123,7 @@ | ||||
|         </ion-item> | ||||
|         <ion-item class="ion-text-wrap" *ngIf="accessInfo.canpostprivatereply"> | ||||
|             <ion-label>{{ 'addon.mod_forum.privatereply' | translate }}</ion-label> | ||||
|             <ion-checkbox slot="end" [(ngModel)]="replyData.isprivatereply" name="isprivatereply"></ion-checkbox> | ||||
|             <ion-checkbox slot="end" [(ngModel)]="formData.isprivatereply" name="isprivatereply"></ion-checkbox> | ||||
|         </ion-item> | ||||
|         <ng-container *ngIf="forum.id && forum.maxattachments > 0"> | ||||
|             <ion-item | ||||
| @ -142,7 +142,7 @@ | ||||
|             </ion-item> | ||||
|             <div *ngIf="advanced" [id]="'addon-forum-reply-edit-form-advanced-' + uniqueId"> | ||||
|                 <core-attachments | ||||
|                     [files]="replyData.files" [maxSize]="forum.maxbytes" [maxSubmissions]="forum.maxattachments" | ||||
|                     [files]="formData.files" [maxSize]="forum.maxbytes" [maxSubmissions]="forum.maxattachments" | ||||
|                     [component]="component" [componentId]="forum.cmid" [allowOffline]="true" [courseId]="courseId"> | ||||
|                 </core-attachments> | ||||
|             </div> | ||||
| @ -150,7 +150,7 @@ | ||||
|         <ion-grid> | ||||
|             <ion-row> | ||||
|                 <ion-col> | ||||
|                     <ion-button expand="block" (click)="reply()" [disabled]="replyData.subject == '' || replyData.message == null"> | ||||
|                     <ion-button expand="block" (click)="send()" [disabled]="formData.subject == '' || formData.message == null"> | ||||
|                         {{ 'addon.mod_forum.posttoforum' | translate }} | ||||
|                     </ion-button> | ||||
|                 </ion-col> | ||||
|  | ||||
| @ -36,8 +36,7 @@ import { | ||||
|     AddonModForumDiscussion, | ||||
|     AddonModForumPost, | ||||
|     AddonModForumProvider, | ||||
|     AddonModForumReply, | ||||
|     AddonModForumUpdateDiscussionPostWSOptionsObject, | ||||
|     AddonModForumPostFormData, | ||||
| } from '../../services/forum'; | ||||
| import { CoreTag } from '@features/tag/services/tag'; | ||||
| import { Translate } from '@singletons'; | ||||
| @ -47,14 +46,13 @@ import { AddonModForumSync } from '../../services/forum-sync'; | ||||
| import { CoreSync } from '@services/sync'; | ||||
| import { CoreTextUtils } from '@services/utils/text'; | ||||
| import { AddonModForumHelper } from '../../services/forum-helper'; | ||||
| import { AddonModForumOffline, AddonModForumReplyOptions } from '../../services/forum-offline'; | ||||
| import { AddonModForumOffline } from '../../services/forum-offline'; | ||||
| import { CoreUtils } from '@services/utils/utils'; | ||||
| import { AddonModForumPostOptionsMenuComponent } from '../post-options-menu/post-options-menu'; | ||||
| import { AddonModForumEditPostComponent } from '../edit-post/edit-post'; | ||||
| import { CoreRatingInfo } from '@features/rating/services/rating'; | ||||
| import { CoreForms } from '@singletons/form'; | ||||
| import { CoreFileEntry } from '@services/file-helper'; | ||||
| import { AddonModForumSharedReplyData } from '../../pages/discussion/discussion.page'; | ||||
| import { AddonModForumSharedPostFormData } from '../../pages/discussion/discussion.page'; | ||||
| 
 | ||||
| /** | ||||
|  * Components that shows a discussion post, its attachments and the action buttons allowed (reply, etc.). | ||||
| @ -72,8 +70,8 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges | ||||
|     @Input() discussion?: AddonModForumDiscussion; // Post's' discussion, only for starting posts.
 | ||||
|     @Input() component!: string; // Component this post belong to.
 | ||||
|     @Input() componentId!: number; // Component ID.
 | ||||
|     @Input() replyData!: AddonModForumSharedReplyData; // Object with the new post data. Usually shared between posts.
 | ||||
|     @Input() originalData!: Omit<AddonModForumReply, 'id'>; // Object with the original post data. Usually shared between posts.
 | ||||
|     @Input() formData!: AddonModForumSharedPostFormData; // Object with the new post data. Usually shared between posts.
 | ||||
|     @Input() originalData!: Omit<AddonModForumPostFormData, 'id'>; // Original post data. Usually shared between posts.
 | ||||
|     @Input() trackPosts!: boolean; // True if post is being tracked.
 | ||||
|     @Input() forum!: AddonModForumData; // The forum the post belongs to. Required for attachments and offline posts.
 | ||||
|     @Input() accessInfo!: AddonModForumAccessInformation; // Forum access information.
 | ||||
| @ -101,8 +99,9 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges | ||||
| 
 | ||||
|     get showForm(): boolean { | ||||
|         return this.post.id > 0 | ||||
|             ? !this.replyData.isEditing && this.replyData.replyingTo === this.post.id | ||||
|             : !!this.replyData.isEditing && this.replyData.replyingTo === this.post.parentid; | ||||
|             ? (!this.formData.isEditing && this.formData.replyingTo === this.post.id) || | ||||
|                 (!!this.formData.isEditing && this.formData.id === this.post.id) | ||||
|             : !!this.formData.isEditing && this.formData.replyingTo === this.post.parentid; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -171,44 +170,47 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Set data to new reply post, clearing temporary files and updating original data. | ||||
|      * Set data to new/edit post, clearing temporary files and updating original data. | ||||
|      * | ||||
|      * @param replyingTo Id of post beeing replied. | ||||
|      * @param isEditing True it's an offline reply beeing edited, false otherwise. | ||||
|      * @param subject Subject of the reply. | ||||
|      * @param message Message of the reply. | ||||
|      * @param isPrivate True if it's private reply. | ||||
|      * @param files Reply attachments. | ||||
|      * @param isPrivate True if it's private reply. | ||||
|      * @param postId The post ID if user is editing an online post. | ||||
|      */ | ||||
|     protected setReplyFormData( | ||||
|     protected setFormData( | ||||
|         replyingTo?: number, | ||||
|         isEditing?: boolean, | ||||
|         subject?: string, | ||||
|         message?: string, | ||||
|         files?: CoreFileEntry[], | ||||
|         isPrivate?: boolean, | ||||
|         postId?: number, | ||||
|     ): void { | ||||
|         // Delete the local files from the tmp folder if any.
 | ||||
|         CoreFileUploader.clearTmpFiles(this.replyData.files); | ||||
|         CoreFileUploader.clearTmpFiles(this.formData.files); | ||||
| 
 | ||||
|         this.replyData.replyingTo = replyingTo || 0; | ||||
|         this.replyData.isEditing = !!isEditing; | ||||
|         this.replyData.subject = subject || this.defaultReplySubject || ''; | ||||
|         this.replyData.message = message || null; | ||||
|         this.replyData.files = files || []; | ||||
|         this.replyData.isprivatereply = !!isPrivate; | ||||
|         this.formData.replyingTo = replyingTo || 0; | ||||
|         this.formData.isEditing = !!isEditing; | ||||
|         this.formData.subject = subject || this.defaultReplySubject || ''; | ||||
|         this.formData.message = message || null; | ||||
|         this.formData.files = files || []; | ||||
|         this.formData.isprivatereply = !!isPrivate; | ||||
|         this.formData.id = postId; | ||||
| 
 | ||||
|         // Update rich text editor.
 | ||||
|         this.messageControl.setValue(this.replyData.message); | ||||
|         this.messageControl.setValue(this.formData.message); | ||||
| 
 | ||||
|         // Update original data.
 | ||||
|         this.originalData.subject = this.replyData.subject; | ||||
|         this.originalData.message = this.replyData.message; | ||||
|         this.originalData.files = this.replyData.files.slice(); | ||||
|         this.originalData.isprivatereply = this.replyData.isprivatereply; | ||||
|         this.originalData.subject = this.formData.subject; | ||||
|         this.originalData.message = this.formData.message; | ||||
|         this.originalData.files = this.formData.files.slice(); | ||||
|         this.originalData.isprivatereply = this.formData.isprivatereply; | ||||
| 
 | ||||
|         // Show advanced fields if any of them has not the default value.
 | ||||
|         this.advanced = this.replyData.files.length > 0; | ||||
|         this.advanced = this.formData.files.length > 0; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -225,7 +227,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges | ||||
|                 cmId: this.forum.cmid, | ||||
|             }, | ||||
|             event, | ||||
|             waitForDismiss: true, | ||||
|             waitForDismissCompleted: true, | ||||
|         }); | ||||
| 
 | ||||
|         if (popoverData && popoverData.action) { | ||||
| @ -233,9 +235,6 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges | ||||
|                 case 'edit': | ||||
|                     this.editPost(); | ||||
|                     break; | ||||
|                 case 'editoffline': | ||||
|                     this.editOfflineReply(); | ||||
|                     break; | ||||
|                 case 'delete': | ||||
|                     this.deletePost(); | ||||
|                     break; | ||||
| @ -246,65 +245,6 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Shows a form modal to edit an online post. | ||||
|      */ | ||||
|     async editPost(): Promise<void> { | ||||
|         const modalData = await CoreDomUtils.openModal<AddonModForumReply>({ | ||||
|             component: AddonModForumEditPostComponent, | ||||
|             componentProps: { | ||||
|                 post: this.post, | ||||
|                 component: this.component, | ||||
|                 componentId: this.componentId, | ||||
|                 forum: this.forum, | ||||
|             }, | ||||
|             backdropDismiss: false, | ||||
|             cssClass: 'core-modal-fullscreen', | ||||
|         }); | ||||
| 
 | ||||
|         if (!modalData) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // Add some HTML to the message if needed.
 | ||||
|         const message = CoreTextUtils.formatHtmlLines(modalData.message!); | ||||
|         const files = modalData.files; | ||||
|         const options: AddonModForumUpdateDiscussionPostWSOptionsObject = {}; | ||||
| 
 | ||||
|         const sendingModal = await CoreDomUtils.showModalLoading('core.sending', true); | ||||
| 
 | ||||
|         try { | ||||
|             // Upload attachments first if any.
 | ||||
|             if (files.length) { | ||||
|                 const attachment = await AddonModForumHelper.uploadOrStoreReplyFiles( | ||||
|                     this.forum.id, | ||||
|                     this.post.id, | ||||
|                     files as CoreFileEntry[], | ||||
|                     false, | ||||
|                 ); | ||||
| 
 | ||||
|                 options.attachmentsid = attachment; | ||||
|             } | ||||
| 
 | ||||
|             // Try to send it to server.
 | ||||
|             const sent = await AddonModForum.updatePost(this.post.id, modalData.subject!, message, options); | ||||
| 
 | ||||
|             if (sent && this.forum.id) { | ||||
|                 // Data sent to server, delete stored files (if any).
 | ||||
|                 AddonModForumHelper.deleteReplyStoredFiles(this.forum.id, this.post.id); | ||||
| 
 | ||||
|                 this.onPostChange.emit(); | ||||
|                 this.post.subject = modalData.subject!; | ||||
|                 this.post.message = message; | ||||
|                 this.post.attachments = modalData.files; | ||||
|             } | ||||
|         } catch (error) { | ||||
|             CoreDomUtils.showErrorModalDefault(error, 'addon.mod_forum.couldnotupdate', true); | ||||
|         } finally { | ||||
|             sendingModal.dismiss(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Set this post as being replied to. | ||||
|      * | ||||
| @ -314,11 +254,11 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges | ||||
|         event.preventDefault(); | ||||
|         event.stopPropagation(); | ||||
| 
 | ||||
|         if (this.replyData.isEditing) { | ||||
|         if (this.formData.isEditing) { | ||||
|             // User is editing a post, data needs to be resetted. Ask confirm if there is unsaved data.
 | ||||
|             try { | ||||
|                 await this.confirmDiscard(); | ||||
|                 this.setReplyFormData(this.post.id); | ||||
|                 this.setFormData(this.post.id); | ||||
| 
 | ||||
|                 this.scrollToForm(); | ||||
|             } catch { | ||||
| @ -328,20 +268,20 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (!this.replyData.replyingTo) { | ||||
|         if (!this.formData.replyingTo) { | ||||
|             // User isn't replying, it's a brand new reply. Initialize the data.
 | ||||
|             this.setReplyFormData(this.post.id); | ||||
|             this.setFormData(this.post.id); | ||||
|         } else { | ||||
|             // The post being replied has changed but the data will be kept.
 | ||||
|             this.replyData.replyingTo = this.post.id; | ||||
|             this.formData.replyingTo = this.post.id; | ||||
| 
 | ||||
|             if (this.replyData.subject == this.originalData.subject) { | ||||
|             if (this.formData.subject == this.originalData.subject) { | ||||
|                 // Update subject only if it hadn't been modified
 | ||||
|                 this.replyData.subject = this.defaultReplySubject; | ||||
|                 this.formData.subject = this.defaultReplySubject; | ||||
|                 this.originalData.subject = this.defaultReplySubject; | ||||
|             } | ||||
| 
 | ||||
|             this.messageControl.setValue(this.replyData.message); | ||||
|             this.messageControl.setValue(this.formData.message); | ||||
|         } | ||||
| 
 | ||||
|         this.scrollToForm(); | ||||
| @ -350,21 +290,22 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges | ||||
|     /** | ||||
|      * Set this post as being edited to. | ||||
|      */ | ||||
|     async editOfflineReply(): Promise<void> { | ||||
|     async editPost(): Promise<void> { | ||||
|         // Ask confirm if there is unsaved data.
 | ||||
|         try { | ||||
|             await this.confirmDiscard(); | ||||
| 
 | ||||
|             this.replyData.syncId = AddonModForumSync.getDiscussionSyncId(this.discussionId); | ||||
|             CoreSync.blockOperation(AddonModForumProvider.COMPONENT, this.replyData.syncId); | ||||
|             this.formData.syncId = AddonModForumSync.getDiscussionSyncId(this.discussionId); | ||||
|             CoreSync.blockOperation(AddonModForumProvider.COMPONENT, this.formData.syncId); | ||||
| 
 | ||||
|             this.setReplyFormData( | ||||
|             this.setFormData( | ||||
|                 this.post.parentid, | ||||
|                 true, | ||||
|                 this.post.subject, | ||||
|                 this.post.message, | ||||
|                 this.post.attachments, | ||||
|                 this.post.isprivatereply, | ||||
|                 this.post.id > 0 ? this.post.id : undefined, | ||||
|             ); | ||||
| 
 | ||||
|             this.scrollToForm(5); | ||||
| @ -379,67 +320,67 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges | ||||
|      * @param text The new text. | ||||
|      */ | ||||
|     onMessageChange(text: string): void { | ||||
|         this.replyData.message = text; | ||||
|         this.formData.message = text; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Reply to this post. | ||||
|      * Reply to this post or edit post data. | ||||
|      */ | ||||
|     async reply(): Promise<void> { | ||||
|         if (!this.replyData.subject) { | ||||
|     async send(): Promise<void> { | ||||
|         if (!this.formData.subject) { | ||||
|             CoreDomUtils.showErrorModal('addon.mod_forum.erroremptysubject', true); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (!this.replyData.message) { | ||||
|         if (!this.formData.message) { | ||||
|             CoreDomUtils.showErrorModal('addon.mod_forum.erroremptymessage', true); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         let saveOffline = false; | ||||
|         let message = this.replyData.message; | ||||
|         const subject = this.replyData.subject; | ||||
|         const replyingTo = this.replyData.replyingTo!; | ||||
|         const files = this.replyData.files || []; | ||||
|         const options: AddonModForumReplyOptions = {}; | ||||
|         let message = this.formData.message; | ||||
|         const subject = this.formData.subject; | ||||
|         const replyingTo = this.formData.replyingTo!; | ||||
|         const files = this.formData.files || []; | ||||
|         const isEditOnline = this.formData.id && this.formData.id > 0; | ||||
|         const modal = await CoreDomUtils.showModalLoading('core.sending', true); | ||||
| 
 | ||||
|         // Add some HTML to the message if needed.
 | ||||
|         message = CoreTextUtils.formatHtmlLines(message); | ||||
| 
 | ||||
|         // Set private option if checked.
 | ||||
|         if (this.replyData.isprivatereply) { | ||||
|             options.private = true; | ||||
|         } | ||||
| 
 | ||||
|         // Upload attachments first if any.
 | ||||
|         let attachments; | ||||
| 
 | ||||
|         if (files.length) { | ||||
|             try { | ||||
|                 attachments = await AddonModForumHelper.uploadOrStoreReplyFiles(this.forum.id, replyingTo, files, false); | ||||
|             } catch (error) { | ||||
| 
 | ||||
|                 // Cannot upload them in online, save them in offline.
 | ||||
|                 if (!this.forum.id) { | ||||
|                     // Cannot store them in offline without the forum ID. Reject.
 | ||||
|                     return Promise.reject(error); | ||||
|                 } | ||||
| 
 | ||||
|                 saveOffline = true; | ||||
|                 attachments = await AddonModForumHelper.uploadOrStoreReplyFiles(this.forum.id, replyingTo, files, true); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             if (attachments) { | ||||
|                 options.attachmentsid = attachments; | ||||
|             if (files.length) { | ||||
|                 try { | ||||
|                     attachments = await AddonModForumHelper.uploadOrStoreReplyFiles( | ||||
|                         this.forum.id, | ||||
|                         isEditOnline ? this.formData.id! : replyingTo, | ||||
|                         files, | ||||
|                         false, | ||||
|                     ); | ||||
|                 } catch (error) { | ||||
|                     // Cannot upload them in online, save them in offline.
 | ||||
|                     if (!this.forum.id || isEditOnline) { | ||||
|                         // Cannot store them in offline. Reject.
 | ||||
|                         throw error; | ||||
|                     } | ||||
| 
 | ||||
|                     saveOffline = true; | ||||
|                     attachments = await AddonModForumHelper.uploadOrStoreReplyFiles(this.forum.id, replyingTo, files, true); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             let sent; | ||||
|             if (saveOffline) { | ||||
|             let sent = false; | ||||
| 
 | ||||
|             if (isEditOnline) { | ||||
|                 sent = await AddonModForum.updatePost(this.formData.id!, subject, message, { | ||||
|                     attachmentsid: attachments, | ||||
|                 }); | ||||
|             } else if (saveOffline) { | ||||
|                 // Save post in offline.
 | ||||
|                 await AddonModForumOffline.replyPost( | ||||
|                     replyingTo, | ||||
| @ -449,7 +390,10 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges | ||||
|                     this.courseId, | ||||
|                     subject, | ||||
|                     message, | ||||
|                     options, | ||||
|                     { | ||||
|                         attachmentsid: attachments, | ||||
|                         private: !!this.formData.isprivatereply, | ||||
|                     }, | ||||
|                 ); | ||||
| 
 | ||||
|                 // Set sent to false since it wasn't sent to server.
 | ||||
| @ -465,7 +409,10 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges | ||||
|                     this.courseId, | ||||
|                     subject, | ||||
|                     message, | ||||
|                     options, | ||||
|                     { | ||||
|                         attachmentsid: attachments, | ||||
|                         private: !!this.formData.isprivatereply, | ||||
|                     }, | ||||
|                     undefined, | ||||
|                     !files.length, | ||||
|                 ); | ||||
| @ -477,7 +424,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges | ||||
|             } | ||||
| 
 | ||||
|             // Reset data.
 | ||||
|             this.setReplyFormData(); | ||||
|             this.setFormData(); | ||||
| 
 | ||||
|             this.onPostChange.emit(); | ||||
| 
 | ||||
| @ -485,7 +432,11 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges | ||||
| 
 | ||||
|             this.unblockOperation(); | ||||
|         } catch (error) { | ||||
|             CoreDomUtils.showErrorModalDefault(error, 'addon.mod_forum.couldnotadd', true); | ||||
|             CoreDomUtils.showErrorModalDefault( | ||||
|                 error, | ||||
|                 isEditOnline ? 'addon.mod_forum.couldnotupdate' : 'addon.mod_forum.couldnotadd', | ||||
|                 true, | ||||
|             ); | ||||
|         } finally { | ||||
|             modal.dismiss(); | ||||
|         } | ||||
| @ -499,7 +450,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges | ||||
|             await this.confirmDiscard(); | ||||
| 
 | ||||
|             // Reset data.
 | ||||
|             this.setReplyFormData(); | ||||
|             this.setFormData(); | ||||
| 
 | ||||
|             CoreForms.triggerFormCancelledEvent(this.formElement, CoreSites.getCurrentSiteId()); | ||||
| 
 | ||||
| @ -529,7 +480,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges | ||||
|             await CoreUtils.ignoreErrors(Promise.all(promises)); | ||||
| 
 | ||||
|             // Reset data.
 | ||||
|             this.setReplyFormData(); | ||||
|             this.setFormData(); | ||||
| 
 | ||||
|             this.onPostChange.emit(); | ||||
| 
 | ||||
| @ -566,7 +517,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges | ||||
|      * @return Promise resolved if the user confirms or data was not changed and rejected otherwise. | ||||
|      */ | ||||
|     protected async confirmDiscard(): Promise<void> { | ||||
|         if (AddonModForumHelper.hasPostDataChanged(this.replyData, this.originalData)) { | ||||
|         if (AddonModForumHelper.hasPostDataChanged(this.formData, this.originalData)) { | ||||
|             // Show confirmation if some data has been modified.
 | ||||
|             await CoreDomUtils.showConfirm(Translate.instant('core.confirmloss')); | ||||
|         } | ||||
| @ -578,12 +529,12 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges | ||||
|      * Unblock operation if there's any blocked operation. | ||||
|      */ | ||||
|     protected unblockOperation(): void { | ||||
|         if (!this.replyData.syncId) { | ||||
|         if (!this.formData.syncId) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         CoreSync.unblockOperation(AddonModForumProvider.COMPONENT, this.replyData.syncId); | ||||
|         delete this.replyData.syncId; | ||||
|         CoreSync.unblockOperation(AddonModForumProvider.COMPONENT, this.formData.syncId); | ||||
|         delete this.formData.syncId; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -93,7 +93,7 @@ | ||||
|             <addon-mod-forum-post | ||||
|                 [post]="startingPost" [discussion]="discussion" [courseId]="courseId" [highlight]="true" | ||||
|                 [discussionId]="discussionId" [component]="component" [componentId]="cmId" | ||||
|                 [replyData]="replyData" [originalData]="originalData" [forum]="forum" [accessInfo]="accessInfo" | ||||
|                 [formData]="formData" [originalData]="originalData" [forum]="forum" [accessInfo]="accessInfo" | ||||
|                 [trackPosts]="trackPosts" [ratingInfo]="ratingInfo" [leavingPage]="leavingPage" | ||||
|                 (onPostChange)="postListChanged()"> | ||||
|             </addon-mod-forum-post> | ||||
| @ -104,7 +104,7 @@ | ||||
|                 <core-spacer *ngIf="!first"></core-spacer> | ||||
|                 <addon-mod-forum-post | ||||
|                     [post]="post" [courseId]="courseId" [discussionId]="discussionId" | ||||
|                     [component]="component" [componentId]="cmId" [replyData]="replyData" | ||||
|                     [component]="component" [componentId]="cmId" [formData]="formData" | ||||
|                     [originalData]="originalData" [parentSubject]="postSubjects[post.parentid]" | ||||
|                     [forum]="forum" [accessInfo]="accessInfo" [trackPosts]="trackPosts" [ratingInfo]="ratingInfo" | ||||
|                     [leavingPage]="leavingPage" | ||||
| @ -123,7 +123,7 @@ | ||||
|             <ion-card> | ||||
|                 <addon-mod-forum-post | ||||
|                     [post]="post" [courseId]="courseId" [discussionId]="discussionId" [component]="component" | ||||
|                     [componentId]="cmId" [replyData]="replyData" [originalData]="originalData" | ||||
|                     [componentId]="cmId" [formData]="formData" [originalData]="originalData" | ||||
|                     [parentSubject]="postSubjects[post.parentid]" [forum]="forum" [accessInfo]="accessInfo" | ||||
|                     [trackPosts]="trackPosts" [ratingInfo]="ratingInfo" [leavingPage]="leavingPage" | ||||
|                     (onPostChange)="postListChanged()"> | ||||
|  | ||||
| @ -39,7 +39,7 @@ import { | ||||
|     AddonModForumDiscussion, | ||||
|     AddonModForumPost, | ||||
|     AddonModForumProvider, | ||||
|     AddonModForumReply, | ||||
|     AddonModForumPostFormData, | ||||
| } from '../../services/forum'; | ||||
| import { AddonModForumHelper } from '../../services/forum-helper'; | ||||
| import { AddonModForumOffline } from '../../services/forum-offline'; | ||||
| @ -74,7 +74,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes | ||||
|     postHasOffline!: boolean; | ||||
|     sort: SortType = 'nested'; | ||||
|     trackPosts!: boolean; | ||||
|     replyData: AddonModForumSharedReplyData = { | ||||
|     formData: AddonModForumSharedPostFormData = { | ||||
|         replyingTo: 0, | ||||
|         isEditing: false, | ||||
|         subject: '', | ||||
| @ -83,7 +83,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes | ||||
|         isprivatereply: false, | ||||
|     }; | ||||
| 
 | ||||
|     originalData: Omit<AddonModForumReply, 'id'> = { | ||||
|     originalData: Omit<AddonModForumPostFormData, 'id'> = { | ||||
|         subject: null, | ||||
|         message: null, | ||||
|         files: [], | ||||
| @ -259,13 +259,13 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes | ||||
|      * @return Resolved if we can leave it, rejected if not. | ||||
|      */ | ||||
|     async canLeave(): Promise<boolean> { | ||||
|         if (AddonModForumHelper.hasPostDataChanged(this.replyData, this.originalData)) { | ||||
|         if (AddonModForumHelper.hasPostDataChanged(this.formData, this.originalData)) { | ||||
|             // Show confirmation if some data has been modified.
 | ||||
|             await CoreDomUtils.showConfirm(Translate.instant('core.confirmcanceledit')); | ||||
|         } | ||||
| 
 | ||||
|         // Delete the local files from the tmp folder.
 | ||||
|         CoreFileUploader.clearTmpFiles(this.replyData.files); | ||||
|         CoreFileUploader.clearTmpFiles(this.formData.files); | ||||
| 
 | ||||
|         this.leavingPage = true; | ||||
| 
 | ||||
| @ -799,6 +799,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes | ||||
| /** | ||||
|  * Reply data shared by post. | ||||
|  */ | ||||
| export type AddonModForumSharedReplyData = Omit<AddonModForumReply, 'id'> & { | ||||
| export type AddonModForumSharedPostFormData = Omit<AddonModForumPostFormData, 'id'> & { | ||||
|     id?: number; // ID when editing an online reply.
 | ||||
|     syncId?: string; // Sync ID if some post has blocked synchronization.
 | ||||
| }; | ||||
|  | ||||
| @ -1558,9 +1558,9 @@ export type AddonModForumAccessInformation = { | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Reply info. | ||||
|  * Post creation or edition data. | ||||
|  */ | ||||
| export type AddonModForumReply = { | ||||
| export type AddonModForumPostFormData = { | ||||
|     id: number; | ||||
|     subject: string | null; // Null means original data is not set.
 | ||||
|     message: string | null; // Null means empty or just white space.
 | ||||
|  | ||||
| @ -1729,12 +1729,12 @@ export class CoreDomUtilsProvider { | ||||
|      * Opens a popover. | ||||
|      * | ||||
|      * @param options Options. | ||||
|      * @return Promise resolved when the popover is dismissed or will be dismissed. | ||||
|      */ | ||||
|     async openPopover<T = void>( | ||||
|         options: OpenPopoverOptions, | ||||
|     ): Promise<T | undefined> { | ||||
|     async openPopover<T = void>(options: OpenPopoverOptions): Promise<T | undefined> { | ||||
| 
 | ||||
|         const popover = await PopoverController.create(options); | ||||
|         const { waitForDismissCompleted, ...popoverOptions } = options; | ||||
|         const popover = await PopoverController.create(popoverOptions); | ||||
|         const zoomLevel = await CoreConfig.get(CoreConstants.SETTINGS_ZOOM_LEVEL, CoreZoomLevel.NORMAL); | ||||
| 
 | ||||
|         await popover.present(); | ||||
| @ -1751,8 +1751,7 @@ export class CoreDomUtilsProvider { | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // If onDidDismiss is nedded we can add a new param to the function to wait one function or the other.
 | ||||
|         const result = options.waitForDismiss ? await popover.onDidDismiss<T>() : await popover.onWillDismiss<T>(); | ||||
|         const result = waitForDismissCompleted ? await popover.onDidDismiss<T>() : await popover.onWillDismiss<T>(); | ||||
|         if (result?.data) { | ||||
|             return result?.data; | ||||
|         } | ||||
| @ -2051,5 +2050,5 @@ type AnchorOrMediaElement = | ||||
|  * Options for the openPopover function. | ||||
|  */ | ||||
| export type OpenPopoverOptions = PopoverOptions & { | ||||
|     waitForDismiss?: boolean; | ||||
|     waitForDismissCompleted?: boolean; | ||||
| }; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user