From 54abd2e0bc2e42d6a87c68fa697e111e975874d3 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Thu, 5 Aug 2021 09:38:41 +0200 Subject: [PATCH 1/4] MOBILE-3793 forum: Fix sync not unblocked in offline It happened when the user clicked to Reply another post while editing an offline reply --- src/addons/mod/forum/components/post/post.ts | 45 ++++++++++--------- .../forum/pages/discussion/discussion.page.ts | 9 +++- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/addons/mod/forum/components/post/post.ts b/src/addons/mod/forum/components/post/post.ts index a7021eee4..a59a9c1da 100644 --- a/src/addons/mod/forum/components/post/post.ts +++ b/src/addons/mod/forum/components/post/post.ts @@ -54,6 +54,7 @@ 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'; /** * Components that shows a discussion post, its attachments and the action buttons allowed (reply, etc.). @@ -71,7 +72,7 @@ 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!: AddonModForumReply; // Object with the new post data. Usually shared between posts. + @Input() replyData!: AddonModForumSharedReplyData; // Object with the new post data. Usually shared between posts. @Input() originalData!: Omit; // Object with the 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. @@ -93,8 +94,6 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges displaySubject = true; optionsMenuEnabled = false; - protected syncId!: string; - constructor( protected elementRef: ElementRef, @Optional() protected content?: IonContent, @@ -372,8 +371,8 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges try { await this.confirmDiscard(); - this.syncId = AddonModForumSync.getDiscussionSyncId(this.discussionId); - CoreSync.blockOperation(AddonModForumProvider.COMPONENT, this.syncId); + this.replyData.syncId = AddonModForumSync.getDiscussionSyncId(this.discussionId); + CoreSync.blockOperation(AddonModForumProvider.COMPONENT, this.replyData.syncId); this.setReplyFormData( this.post.parentid, @@ -498,9 +497,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges CoreForms.triggerFormSubmittedEvent(this.formElement, sent, CoreSites.getCurrentSiteId()); - if (this.syncId) { - CoreSync.unblockOperation(AddonModForumProvider.COMPONENT, this.syncId); - } + this.unblockOperation(); } catch (error) { CoreDomUtils.showErrorModalDefault(error, 'addon.mod_forum.couldnotadd', true); } finally { @@ -520,9 +517,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges CoreForms.triggerFormCancelledEvent(this.formElement, CoreSites.getCurrentSiteId()); - if (this.syncId) { - CoreSync.unblockOperation(AddonModForumProvider.COMPONENT, this.syncId); - } + this.unblockOperation(); } catch (error) { // Cancelled. } @@ -552,9 +547,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges this.onPostChange.emit(); - if (this.syncId) { - CoreSync.unblockOperation(AddonModForumProvider.COMPONENT, this.syncId); - } + this.unblockOperation(); } catch (error) { // Cancelled. } @@ -578,9 +571,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges * Component being destroyed. */ ngOnDestroy(): void { - if (this.syncId) { - CoreSync.unblockOperation(AddonModForumProvider.COMPONENT, this.syncId); - } + this.unblockOperation(); } /** @@ -588,13 +579,25 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges * * @return Promise resolved if the user confirms or data was not changed and rejected otherwise. */ - protected confirmDiscard(): Promise { + protected async confirmDiscard(): Promise { if (AddonModForumHelper.hasPostDataChanged(this.replyData, this.originalData)) { // Show confirmation if some data has been modified. - return CoreDomUtils.showConfirm(Translate.instant('core.confirmloss')); - } else { - return Promise.resolve(); + await CoreDomUtils.showConfirm(Translate.instant('core.confirmloss')); } + + this.unblockOperation(); + } + + /** + * Unblock operation if there's any blocked operation. + */ + protected unblockOperation(): void { + if (!this.replyData.syncId) { + return; + } + + CoreSync.unblockOperation(AddonModForumProvider.COMPONENT, this.replyData.syncId); + delete this.replyData.syncId; } } diff --git a/src/addons/mod/forum/pages/discussion/discussion.page.ts b/src/addons/mod/forum/pages/discussion/discussion.page.ts index c9b89e9c6..ae7f6d128 100644 --- a/src/addons/mod/forum/pages/discussion/discussion.page.ts +++ b/src/addons/mod/forum/pages/discussion/discussion.page.ts @@ -74,7 +74,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes postHasOffline!: boolean; sort: SortType = 'nested'; trackPosts!: boolean; - replyData: Omit = { + replyData: AddonModForumSharedReplyData = { replyingTo: 0, isEditing: false, subject: '', @@ -795,3 +795,10 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes } } + +/** + * Reply data shared by post. + */ +export type AddonModForumSharedReplyData = Omit & { + syncId?: string; // Sync ID if some post has blocked synchronization. +}; From a0683848677c809c977e1ac76692671bc8be3a25 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Thu, 5 Aug 2021 11:25:13 +0200 Subject: [PATCH 2/4] MOBILE-3793 forum: Scroll to form when edit offline reply --- src/addons/mod/forum/components/post/post.ts | 44 +++++++++++--------- src/core/services/utils/dom.ts | 19 ++++++--- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/addons/mod/forum/components/post/post.ts b/src/addons/mod/forum/components/post/post.ts index a59a9c1da..0f1107cb6 100644 --- a/src/addons/mod/forum/components/post/post.ts +++ b/src/addons/mod/forum/components/post/post.ts @@ -225,6 +225,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges cmId: this.forum.cmid, }, event, + waitForDismiss: true, }); if (popoverData && popoverData.action) { @@ -319,15 +320,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges await this.confirmDiscard(); this.setReplyFormData(this.post.id); - if (this.content) { - setTimeout(() => { - CoreDomUtils.scrollToElementBySelector( - this.elementRef.nativeElement, - this.content, - '#addon-forum-reply-edit-form-' + this.uniqueId, - ); - }); - } + this.scrollToForm(); } catch { // Cancelled. } @@ -351,16 +344,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges this.messageControl.setValue(this.replyData.message); } - if (this.content) { - setTimeout(() => { - CoreDomUtils.scrollToElementBySelector( - this.elementRef.nativeElement, - this.content, - '#addon-forum-reply-edit-form-' + this.uniqueId, - ); - }); - } - + this.scrollToForm(); } /** @@ -382,6 +366,8 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges this.post.attachments, this.post.isprivatereply, ); + + this.scrollToForm(5); } catch (error) { // Cancelled. } @@ -600,4 +586,24 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges delete this.replyData.syncId; } + /** + * Scroll to reply/edit form. + * + * @param ticksToWait Number of ticks to wait before scrolling. + * @return Promise resolved when done. + */ + protected async scrollToForm(ticksToWait = 1): Promise { + if (!this.content) { + return; + } + + await CoreUtils.nextTicks(ticksToWait); + + CoreDomUtils.scrollToElementBySelector( + this.elementRef.nativeElement, + this.content, + '#addon-forum-reply-edit-form-' + this.uniqueId, + ); + } + } diff --git a/src/core/services/utils/dom.ts b/src/core/services/utils/dom.ts index 7a3d37e90..966324808 100644 --- a/src/core/services/utils/dom.ts +++ b/src/core/services/utils/dom.ts @@ -1728,13 +1728,13 @@ export class CoreDomUtilsProvider { /** * Opens a popover. * - * @param popoverOptions Modal Options. + * @param options Options. */ async openPopover( - popoverOptions: PopoverOptions, + options: OpenPopoverOptions, ): Promise { - const popover = await PopoverController.create(popoverOptions); + const popover = await PopoverController.create(options); const zoomLevel = await CoreConfig.get(CoreConstants.SETTINGS_ZOOM_LEVEL, CoreZoomLevel.NORMAL); await popover.present(); @@ -1743,16 +1743,16 @@ export class CoreDomUtilsProvider { if (zoomLevel !== CoreZoomLevel.NORMAL) { switch (getMode()) { case 'ios': - fixIOSPopoverPosition(popover, popoverOptions.event); + fixIOSPopoverPosition(popover, options.event); break; case 'md': - fixMDPopoverPosition(popover, popoverOptions.event); + fixMDPopoverPosition(popover, options.event); break; } } // If onDidDismiss is nedded we can add a new param to the function to wait one function or the other. - const result = await popover.onWillDismiss(); + const result = options.waitForDismiss ? await popover.onDidDismiss() : await popover.onWillDismiss(); if (result?.data) { return result?.data; } @@ -2046,3 +2046,10 @@ export const CoreDomUtils = makeSingleton(CoreDomUtilsProvider); type AnchorOrMediaElement = HTMLAnchorElement | HTMLImageElement | HTMLAudioElement | HTMLVideoElement | HTMLSourceElement | HTMLTrackElement; + +/** + * Options for the openPopover function. + */ +export type OpenPopoverOptions = PopoverOptions & { + waitForDismiss?: boolean; +}; From 5fa280fe32e19e5a605925f4e337ba00696b811e Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Thu, 5 Aug 2021 13:28:18 +0200 Subject: [PATCH 3/4] MOBILE-3793 forum: Hide post being edited --- .../mod/forum/components/post/post.html | 194 +++++++++--------- 1 file changed, 99 insertions(+), 95 deletions(-) diff --git a/src/addons/mod/forum/components/post/post.html b/src/addons/mod/forum/components/post/post.html index 89dbb0ed0..f14b4febb 100644 --- a/src/addons/mod/forum/components/post/post.html +++ b/src/addons/mod/forum/components/post/post.html @@ -1,111 +1,115 @@
- - - -
-

- - - - - - -

- - - - - - -
- +
{{ 'addon.mod_forum.subject' | translate }} - + From 44bc62e0a2b28348bc4289d575d36ea38810ba76 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 6 Aug 2021 07:57:47 +0200 Subject: [PATCH 4/4] 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 --- .../mod/forum/components/components.module.ts | 2 - .../forum/components/edit-post/edit-post.html | 60 ----- .../forum/components/edit-post/edit-post.ts | 150 ----------- .../post-options-menu/post-options-menu.ts | 6 +- .../mod/forum/components/post/post.html | 12 +- src/addons/mod/forum/components/post/post.ts | 241 +++++++----------- .../forum/pages/discussion/discussion.html | 6 +- .../forum/pages/discussion/discussion.page.ts | 13 +- src/addons/mod/forum/services/forum.ts | 4 +- src/core/services/utils/dom.ts | 13 +- 10 files changed, 121 insertions(+), 386 deletions(-) delete mode 100644 src/addons/mod/forum/components/edit-post/edit-post.html delete mode 100644 src/addons/mod/forum/components/edit-post/edit-post.ts diff --git a/src/addons/mod/forum/components/components.module.ts b/src/addons/mod/forum/components/components.module.ts index 55da612ac..d57add762 100644 --- a/src/addons/mod/forum/components/components.module.ts +++ b/src/addons/mod/forum/components/components.module.ts @@ -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, diff --git a/src/addons/mod/forum/components/edit-post/edit-post.html b/src/addons/mod/forum/components/edit-post/edit-post.html deleted file mode 100644 index 19f1c65ba..000000000 --- a/src/addons/mod/forum/components/edit-post/edit-post.html +++ /dev/null @@ -1,60 +0,0 @@ - - -

{{ 'addon.mod_forum.yourreply' | translate }}

- - - - - -
-
- - - - {{ 'addon.mod_forum.subject' | translate }} - - - - - {{ 'addon.mod_forum.message' | translate }} - - - - - - -

{{ 'addon.mod_forum.advanced' | translate }}

-
-
- - -
- - - - - {{ 'core.savechanges' | translate }} - - - - {{ 'core.cancel' | translate }} - - - - -
diff --git a/src/addons/mod/forum/components/edit-post/edit-post.ts b/src/addons/mod/forum/components/edit-post/edit-post.ts deleted file mode 100644 index e3a81636c..000000000 --- a/src/addons/mod/forum/components/edit-post/edit-post.ts +++ /dev/null @@ -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; // 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 { - 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 { - 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; - } - } - -} diff --git a/src/addons/mod/forum/components/post-options-menu/post-options-menu.ts b/src/addons/mod/forum/components/post-options-menu/post-options-menu.ts index cf2d1acc8..8c7a51b87 100644 --- a/src/addons/mod/forum/components/post-options-menu/post-options-menu.ts +++ b/src/addons/mod/forum/components/post-options-menu/post-options-menu.ts @@ -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' }); } } diff --git a/src/addons/mod/forum/components/post/post.html b/src/addons/mod/forum/components/post/post.html index f14b4febb..4dcded210 100644 --- a/src/addons/mod/forum/components/post/post.html +++ b/src/addons/mod/forum/components/post/post.html @@ -1,5 +1,5 @@
- + @@ -94,7 +94,7 @@ {{ 'addon.mod_forum.reply' | translate }} @@ -108,7 +108,7 @@ [id]="'addon-forum-reply-edit-form-' + uniqueId" #replyFormEl> {{ 'addon.mod_forum.subject' | translate }} - @@ -123,7 +123,7 @@ {{ 'addon.mod_forum.privatereply' | translate }} - +
@@ -150,7 +150,7 @@ - + {{ 'addon.mod_forum.posttoforum' | translate }} diff --git a/src/addons/mod/forum/components/post/post.ts b/src/addons/mod/forum/components/post/post.ts index 0f1107cb6..2a2cf19ed 100644 --- a/src/addons/mod/forum/components/post/post.ts +++ b/src/addons/mod/forum/components/post/post.ts @@ -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; // 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; // 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 { - const modalData = await CoreDomUtils.openModal({ - 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 { + async editPost(): Promise { // 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 { - if (!this.replyData.subject) { + async send(): Promise { + 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 { - 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; } /** diff --git a/src/addons/mod/forum/pages/discussion/discussion.html b/src/addons/mod/forum/pages/discussion/discussion.html index 8900625fe..6cc5bb804 100644 --- a/src/addons/mod/forum/pages/discussion/discussion.html +++ b/src/addons/mod/forum/pages/discussion/discussion.html @@ -93,7 +93,7 @@ @@ -104,7 +104,7 @@ diff --git a/src/addons/mod/forum/pages/discussion/discussion.page.ts b/src/addons/mod/forum/pages/discussion/discussion.page.ts index ae7f6d128..44452fe37 100644 --- a/src/addons/mod/forum/pages/discussion/discussion.page.ts +++ b/src/addons/mod/forum/pages/discussion/discussion.page.ts @@ -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 = { + originalData: Omit = { 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 { - 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 & { +export type AddonModForumSharedPostFormData = Omit & { + id?: number; // ID when editing an online reply. syncId?: string; // Sync ID if some post has blocked synchronization. }; diff --git a/src/addons/mod/forum/services/forum.ts b/src/addons/mod/forum/services/forum.ts index bf53d6c30..aae5118d1 100644 --- a/src/addons/mod/forum/services/forum.ts +++ b/src/addons/mod/forum/services/forum.ts @@ -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. diff --git a/src/core/services/utils/dom.ts b/src/core/services/utils/dom.ts index 966324808..b94b8c5e0 100644 --- a/src/core/services/utils/dom.ts +++ b/src/core/services/utils/dom.ts @@ -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( - options: OpenPopoverOptions, - ): Promise { + async openPopover(options: OpenPopoverOptions): Promise { - 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() : await popover.onWillDismiss(); + const result = waitForDismissCompleted ? await popover.onDidDismiss() : await popover.onWillDismiss(); 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; };