From ccf4aeadea6d7f3330e9b795c726fede401f85e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 24 Oct 2019 15:24:21 +0200 Subject: [PATCH] MOBILE-3205 forum: Add status actions onto discussion list --- .../mod/forum/components/components.module.ts | 10 +- .../addon-forum-discussion-options-menu.html | 24 +++ .../discussion-options-menu.ts | 145 ++++++++++++++++++ .../index/addon-mod-forum-index.html | 8 +- src/addon/mod/forum/components/index/index.ts | 105 +++++++++++-- .../components/post/addon-mod-forum-post.html | 9 +- .../mod/forum/pages/discussion/discussion.ts | 16 ++ src/addon/mod/forum/providers/forum.ts | 2 +- 8 files changed, 289 insertions(+), 30 deletions(-) create mode 100644 src/addon/mod/forum/components/discussion-options-menu/addon-forum-discussion-options-menu.html create mode 100644 src/addon/mod/forum/components/discussion-options-menu/discussion-options-menu.ts diff --git a/src/addon/mod/forum/components/components.module.ts b/src/addon/mod/forum/components/components.module.ts index b26b599fe..8b0c288d8 100644 --- a/src/addon/mod/forum/components/components.module.ts +++ b/src/addon/mod/forum/components/components.module.ts @@ -24,11 +24,13 @@ import { CoreRatingComponentsModule } from '@core/rating/components/components.m import { CoreTagComponentsModule } from '@core/tag/components/components.module'; import { AddonModForumIndexComponent } from './index/index'; import { AddonModForumPostComponent } from './post/post'; +import { AddonForumDiscussionOptionsMenuComponent } from './discussion-options-menu/discussion-options-menu'; @NgModule({ declarations: [ AddonModForumIndexComponent, - AddonModForumPostComponent + AddonModForumPostComponent, + AddonForumDiscussionOptionsMenuComponent ], imports: [ CommonModule, @@ -45,10 +47,12 @@ import { AddonModForumPostComponent } from './post/post'; ], exports: [ AddonModForumIndexComponent, - AddonModForumPostComponent + AddonModForumPostComponent, + AddonForumDiscussionOptionsMenuComponent ], entryComponents: [ - AddonModForumIndexComponent + AddonModForumIndexComponent, + AddonForumDiscussionOptionsMenuComponent ] }) export class AddonModForumComponentsModule {} diff --git a/src/addon/mod/forum/components/discussion-options-menu/addon-forum-discussion-options-menu.html b/src/addon/mod/forum/components/discussion-options-menu/addon-forum-discussion-options-menu.html new file mode 100644 index 000000000..ba53fa1cc --- /dev/null +++ b/src/addon/mod/forum/components/discussion-options-menu/addon-forum-discussion-options-menu.html @@ -0,0 +1,24 @@ + + +

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

+
+ + +

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

+
+ + +

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

+
+ + +

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

+
+ + +

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

+
+ + +

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

+
\ No newline at end of file diff --git a/src/addon/mod/forum/components/discussion-options-menu/discussion-options-menu.ts b/src/addon/mod/forum/components/discussion-options-menu/discussion-options-menu.ts new file mode 100644 index 000000000..142c25706 --- /dev/null +++ b/src/addon/mod/forum/components/discussion-options-menu/discussion-options-menu.ts @@ -0,0 +1,145 @@ +// (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, OnInit } from '@angular/core'; +import { NavParams, ViewController } from 'ionic-angular'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreEventsProvider } from '@providers/events'; +import { CoreSitesProvider } from '@providers/sites'; +import { AddonModForumProvider } from '../../providers/forum'; + +/** + * This component is meant to display a popover with the discussion options. + */ +@Component({ + selector: 'addon-forum-discussion-options-menu', + templateUrl: 'addon-forum-discussion-options-menu.html' +}) +export class AddonForumDiscussionOptionsMenuComponent implements OnInit { + discussion: any; // The discussion. + forumId: number; // The forum Id. + cmId: number; // The component module Id. + canPin = false; + + constructor(navParams: NavParams, + protected viewCtrl: ViewController, + protected forumProvider: AddonModForumProvider, + protected domUtils: CoreDomUtilsProvider, + protected eventsProvider: CoreEventsProvider, + protected sitesProvider: CoreSitesProvider) { + this.discussion = navParams.get('discussion'); + this.forumId = navParams.get('forumId'); + this.cmId = navParams.get('cmId'); + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + if (this.forumProvider.isSetPinStateAvailableForSite()) { + // Use the canAddDiscussion WS to check if the user can pin discussions. + this.forumProvider.canAddDiscussionToAll(this.forumId).then((response) => { + this.canPin = !!response.canpindiscussions; + }).catch(() => { + this.canPin = false; + }); + } else { + this.canPin = false; + } + } + + /** + * Lock or unlock the discussion. + * + * @param locked True to lock the discussion, false to unlock. + */ + setLockState(locked: boolean): void { + const modal = this.domUtils.showModalLoading('core.sending', true); + + this.forumProvider.setLockState(this.forumId, this.discussion.discussion, locked).then((response) => { + this.viewCtrl.dismiss({action: 'lock', value: locked}); + + const data = { + forumId: this.forumId, + discussionId: this.discussion.discussion, + cmId: this.cmId, + locked: response.locked + }; + this.eventsProvider.trigger(AddonModForumProvider.CHANGE_DISCUSSION_EVENT, data, this.sitesProvider.getCurrentSiteId()); + + this.domUtils.showToast('addon.mod_forum.lockupdated', true); + }).catch((error) => { + this.domUtils.showErrorModal(error); + this.viewCtrl.dismiss(); + }).finally(() => { + modal.dismiss(); + }); + } + + /** + * Pin or unpin the discussion. + * + * @param pinned True to pin the discussion, false to unpin it. + */ + setPinState(pinned: boolean): void { + const modal = this.domUtils.showModalLoading('core.sending', true); + + this.forumProvider.setPinState(this.discussion.discussion, pinned).then(() => { + this.viewCtrl.dismiss({action: 'pin', value: pinned}); + + const data = { + forumId: this.forumId, + discussionId: this.discussion.discussion, + cmId: this.cmId, + pinned: pinned + }; + this.eventsProvider.trigger(AddonModForumProvider.CHANGE_DISCUSSION_EVENT, data, this.sitesProvider.getCurrentSiteId()); + + this.domUtils.showToast('addon.mod_forum.pinupdated', true); + }).catch((error) => { + this.domUtils.showErrorModal(error); + this.viewCtrl.dismiss(); + }).finally(() => { + modal.dismiss(); + }); + } + + /** + * Star or unstar the discussion. + * + * @param starred True to star the discussion, false to unstar it. + */ + toggleFavouriteState(starred: boolean): void { + const modal = this.domUtils.showModalLoading('core.sending', true); + + this.forumProvider.toggleFavouriteState(this.discussion.discussion, starred).then(() => { + this.viewCtrl.dismiss({action: 'star', value: starred}); + + const data = { + forumId: this.forumId, + discussionId: this.discussion.discussion, + cmId: this.cmId, + starred: starred + }; + this.eventsProvider.trigger(AddonModForumProvider.CHANGE_DISCUSSION_EVENT, data, this.sitesProvider.getCurrentSiteId()); + + this.domUtils.showToast('addon.mod_forum.favouriteupdated', true); + }).catch((error) => { + this.domUtils.showErrorModal(error); + this.viewCtrl.dismiss(); + }).finally(() => { + modal.dismiss(); + }); + } +} diff --git a/src/addon/mod/forum/components/index/addon-mod-forum-index.html b/src/addon/mod/forum/components/index/addon-mod-forum-index.html index 9e9f3badf..eda6f5033 100644 --- a/src/addon/mod/forum/components/index/addon-mod-forum-index.html +++ b/src/addon/mod/forum/components/index/addon-mod-forum-index.html @@ -65,7 +65,7 @@ - @@ -74,18 +74,18 @@

{{discussion.userfullname}}

{{ discussion.groupname }}

-

{{discussion.created | coreFormatDate: "strftimedatetimeshort"}}

+

{{discussion.created * 1000 | coreFormatDate: "strftimerecentfull"}}

- + {{ 'addon.mod_forum.lastpost' | translate }} {{discussion.timemodified | coreTimeAgo}} {{discussion.created | coreTimeAgo}} - + {{ 'addon.mod_forum.numreplies' | translate:{numreplies: discussion.numreplies} }} {{ discussion.numunread }} diff --git a/src/addon/mod/forum/components/index/index.ts b/src/addon/mod/forum/components/index/index.ts index 8edf898d8..ec64d2e1f 100644 --- a/src/addon/mod/forum/components/index/index.ts +++ b/src/addon/mod/forum/components/index/index.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Component, Optional, Injector, ViewChild } from '@angular/core'; -import { Content, ModalController, NavController } from 'ionic-angular'; +import { Content, ModalController, NavController, PopoverController } from 'ionic-angular'; import { CoreSplitViewComponent } from '@components/split-view/split-view'; import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component'; import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; @@ -27,6 +27,7 @@ import { AddonModForumHelperProvider } from '../../providers/helper'; import { AddonModForumOfflineProvider } from '../../providers/offline'; import { AddonModForumSyncProvider } from '../../providers/sync'; import { AddonModForumPrefetchHandler } from '../../providers/prefetch-handler'; +import { AddonForumDiscussionOptionsMenuComponent } from '../discussion-options-menu/discussion-options-menu'; /** * Component that displays a forum entry page. @@ -61,6 +62,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom protected page = 0; protected trackPosts = false; protected usesGroups = false; + protected canPin = false; protected syncManualObserver: any; // It will observe the sync manual event. protected replyObserver: any; protected newDiscObserver: any; @@ -83,7 +85,8 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom protected forumSync: AddonModForumSyncProvider, protected prefetchDelegate: CoreCourseModulePrefetchDelegate, protected prefetchHandler: AddonModForumPrefetchHandler, - protected ratingOffline: CoreRatingOfflineProvider) { + protected ratingOffline: CoreRatingOfflineProvider, + protected popoverCtrl: PopoverController) { super(injector); this.sortingAvailable = this.forumProvider.isDiscussionListSortingAvailable(); @@ -106,8 +109,30 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom this.eventReceived.bind(this, true)); this.replyObserver = this.eventsProvider.on(AddonModForumProvider.REPLY_DISCUSSION_EVENT, this.eventReceived.bind(this, false)); - this.changeDiscObserver = this.eventsProvider.on(AddonModForumProvider.CHANGE_DISCUSSION_EVENT, - this.eventReceived.bind(this, false)); + this.changeDiscObserver = this.eventsProvider.on(AddonModForumProvider.CHANGE_DISCUSSION_EVENT, (data) => { + this.forumProvider.invalidateDiscussionsList(this.forum.id).finally(() => { + // If it's a new discussion in tablet mode, try to open it. + if (data.discussionId) { + // Discussion sent to server, search it in the list of discussions. + const discussion = this.discussions.find((disc) => { + return data.discussionId = disc.discussion; + }); + + if (discussion) { + if (typeof data.locked != 'undefined') { + discussion.locked = data.locked; + } + if (typeof data.pinned != 'undefined') { + discussion.pinned = data.pinned; + } + if (typeof data.starred != 'undefined') { + discussion.starred = data.starred; + } + } + + } + }); + }); // Select the current opened discussion. this.viewDiscObserver = this.eventsProvider.on(AddonModForumProvider.VIEW_DISCUSSION_EVENT, (data) => { @@ -211,18 +236,30 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom }); } }).then(() => { - return Promise.all([ - // Check if the activity uses groups. - this.groupsProvider.getActivityGroupMode(this.forum.cmid).then((mode) => { - this.usesGroups = (mode === CoreGroupsProvider.SEPARATEGROUPS || mode === CoreGroupsProvider.VISIBLEGROUPS); - }), - this.forumProvider.getAccessInformation(this.forum.id).then((accessInfo) => { - // Disallow adding discussions if cut-off date is reached and the user has not the capability to override it. - // Just in case the forum was fetched from WS when the cut-off date was not reached but it is now. - const cutoffDateReached = this.forumHelper.isCutoffDateReached(this.forum) && !accessInfo.cancanoverridecutoff; - this.canAddDiscussion = this.forum.cancreatediscussions && !cutoffDateReached; - }), - ]); + const promises = []; + // Check if the activity uses groups. + promises.push(this.groupsProvider.getActivityGroupMode(this.forum.cmid).then((mode) => { + this.usesGroups = (mode === CoreGroupsProvider.SEPARATEGROUPS || mode === CoreGroupsProvider.VISIBLEGROUPS); + })); + promises.push(this.forumProvider.getAccessInformation(this.forum.id).then((accessInfo) => { + // Disallow adding discussions if cut-off date is reached and the user has not the capability to override it. + // Just in case the forum was fetched from WS when the cut-off date was not reached but it is now. + const cutoffDateReached = this.forumHelper.isCutoffDateReached(this.forum) && !accessInfo.cancanoverridecutoff; + this.canAddDiscussion = this.forum.cancreatediscussions && !cutoffDateReached; + })); + + if (this.forumProvider.isSetPinStateAvailableForSite()) { + // Use the canAddDiscussion WS to check if the user can pin discussions. + promises.push(this.forumProvider.canAddDiscussionToAll(this.forum.id).then((response) => { + this.canPin = !!response.canpindiscussions; + }).catch(() => { + this.canPin = false; + })); + } else { + this.canPin = false; + } + + return Promise.all(promises); })); promises.push(this.fetchSortOrderPreference()); @@ -559,6 +596,42 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom this.sortOrderSelectorExpanded = true; } + /** + * Show the context menu. + * + * @param e Click Event. + */ + showOptionsMenu(e: Event, discussion: any): void { + e.preventDefault(); + e.stopPropagation(); + + const popover = this.popoverCtrl.create(AddonForumDiscussionOptionsMenuComponent, { + discussion: discussion, + forumId: this.forum.id, + cmId: this.module.id + }); + popover.onDidDismiss((data) => { + if (data && data.action) { + switch (data.action) { + case 'lock': + discussion.locked = data.value; + break; + case 'pin': + discussion.pinned = data.value; + break; + case 'star': + discussion.starred = data.value; + break; + default: + break; + } + } + }); + popover.present({ + ev: e + }); + } + /** * Component being destroyed. */ diff --git a/src/addon/mod/forum/components/post/addon-mod-forum-post.html b/src/addon/mod/forum/components/post/addon-mod-forum-post.html index f6cca3c17..d0512dd01 100644 --- a/src/addon/mod/forum/components/post/addon-mod-forum-post.html +++ b/src/addon/mod/forum/components/post/addon-mod-forum-post.html @@ -6,19 +6,16 @@ - + - - + diff --git a/src/addon/mod/forum/pages/discussion/discussion.ts b/src/addon/mod/forum/pages/discussion/discussion.ts index 6cf56461f..dec2adf0a 100644 --- a/src/addon/mod/forum/pages/discussion/discussion.ts +++ b/src/addon/mod/forum/pages/discussion/discussion.ts @@ -89,6 +89,7 @@ export class AddonModForumDiscussionPage implements OnDestroy { hasOfflineRatings: boolean; protected ratingOfflineObserver: any; protected ratingSyncObserver: any; + protected changeDiscObserver: any; constructor(navParams: NavParams, network: Network, @@ -183,6 +184,20 @@ export class AddonModForumDiscussionPage implements OnDestroy { this.hasOfflineRatings = false; } }); + + this.changeDiscObserver = this.eventsProvider.on(AddonModForumProvider.CHANGE_DISCUSSION_EVENT, (data) => { + this.forumProvider.invalidateDiscussionsList(this.forum.id).finally(() => { + if (typeof data.locked != 'undefined') { + this.discussion.locked = data.locked; + } + if (typeof data.pinned != 'undefined') { + this.discussion.pinned = data.pinned; + } + if (typeof data.starred != 'undefined') { + this.discussion.starred = data.starred; + } + }); + }); } /** @@ -610,6 +625,7 @@ export class AddonModForumDiscussionPage implements OnDestroy { this.syncManualObserver && this.syncManualObserver.off(); this.ratingOfflineObserver && this.ratingOfflineObserver.off(); this.ratingSyncObserver && this.ratingSyncObserver.off(); + this.changeDiscObserver && this.changeDiscObserver.off(); } /** diff --git a/src/addon/mod/forum/providers/forum.ts b/src/addon/mod/forum/providers/forum.ts index 55a568574..c59e5cdf7 100644 --- a/src/addon/mod/forum/providers/forum.ts +++ b/src/addon/mod/forum/providers/forum.ts @@ -35,7 +35,7 @@ export class AddonModForumProvider { static NEW_DISCUSSION_EVENT = 'addon_mod_forum_new_discussion'; static REPLY_DISCUSSION_EVENT = 'addon_mod_forum_reply_discussion'; static VIEW_DISCUSSION_EVENT = 'addon_mod_forum_view_discussion'; - static CHANGE_DISCUSSION_EVENT = 'addon_mod_forum_lock_discussion'; + static CHANGE_DISCUSSION_EVENT = 'addon_mod_forum_change_discussion_status'; static MARK_READ_EVENT = 'addon_mod_forum_mark_read'; static PREFERENCE_SORTORDER = 'forum_discussionlistsortorder';