From 1119f1d4374ff5691dcd4c2038afceaea29d9bf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Fri, 22 Jan 2021 13:40:50 +0100 Subject: [PATCH] MOBILE-3631 messages: Implement handlers --- src/addons/messages/messages.module.ts | 101 +++----- .../services/handlers/contact-request-link.ts | 62 +++++ .../services/handlers/discussion-link.ts | 85 +++++++ .../messages/services/handlers/index-link.ts | 61 +++++ .../messages/services/handlers/push-click.ts | 84 +++++++ .../services/handlers/user-add-contact.ts | 222 ++++++++++++++++++ .../services/handlers/user-block-contact.ts | 197 ++++++++++++++++ .../services/handlers/user-send-message.ts | 85 +++++++ src/addons/messages/services/messages.ts | 16 +- 9 files changed, 839 insertions(+), 74 deletions(-) create mode 100644 src/addons/messages/services/handlers/contact-request-link.ts create mode 100644 src/addons/messages/services/handlers/discussion-link.ts create mode 100644 src/addons/messages/services/handlers/index-link.ts create mode 100644 src/addons/messages/services/handlers/push-click.ts create mode 100644 src/addons/messages/services/handlers/user-add-contact.ts create mode 100644 src/addons/messages/services/handlers/user-block-contact.ts create mode 100644 src/addons/messages/services/handlers/user-send-message.ts diff --git a/src/addons/messages/messages.module.ts b/src/addons/messages/messages.module.ts index 4cb26505b..30adcf591 100644 --- a/src/addons/messages/messages.module.ts +++ b/src/addons/messages/messages.module.ts @@ -24,6 +24,16 @@ import { CoreCronDelegate } from '@services/cron'; import { CoreSettingsDelegate } from '@features/settings/services/settings-delegate'; import { AddonMessagesSettingsHandler } from './services/handlers/settings'; import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module'; +import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate'; +import { AddonMessagesIndexLinkHandler } from './services/handlers/index-link'; +import { AddonMessagesDiscussionLinkHandler } from './services/handlers/discussion-link'; +import { AddonMessagesContactRequestLinkHandler } from './services/handlers/contact-request-link'; +import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate'; +import { AddonMessagesPushClickHandler } from './services/handlers/push-click'; +import { CoreUserDelegate } from '@features/user/services/user-delegate'; +import { AddonMessagesSendMessageUserHandler } from './services/handlers/user-send-message'; +import { AddonMessagesAddContactUserHandler } from './services/handlers/user-add-contact'; +import { AddonMessagesBlockContactUserHandler } from './services/handlers/user-block-contact'; const mainMenuChildrenRoutes: Routes = [ { @@ -48,84 +58,29 @@ const mainMenuChildrenRoutes: Routes = [ multi: true, deps: [], useFactory: () => () => { + // Register handlers. CoreMainMenuDelegate.instance.registerHandler(AddonMessagesMainMenuHandler.instance); CoreCronDelegate.instance.register(AddonMessagesMainMenuHandler.instance); + // @todo CoreCronDelegate.instance.register(AddonMessagesSyncCronHandler.instance); CoreSettingsDelegate.instance.registerHandler(AddonMessagesSettingsHandler.instance); + CoreContentLinksDelegate.instance.registerHandler(AddonMessagesIndexLinkHandler.instance); + CoreContentLinksDelegate.instance.registerHandler(AddonMessagesDiscussionLinkHandler.instance); + CoreContentLinksDelegate.instance.registerHandler(AddonMessagesContactRequestLinkHandler.instance); + CorePushNotificationsDelegate.instance.registerClickHandler(AddonMessagesPushClickHandler.instance); + CoreUserDelegate.instance.registerHandler(AddonMessagesSendMessageUserHandler.instance); + CoreUserDelegate.instance.registerHandler(AddonMessagesAddContactUserHandler.instance); + CoreUserDelegate.instance.registerHandler(AddonMessagesBlockContactUserHandler.instance); + + // Sync some discussions when device goes online. + /* @todo Network.instance.onConnect().subscribe(() => { + // Execute the callback in the Angular zone, so change detection doesn't stop working. + NgZone.instance.run(() => { + AddonMessagesSync.instance.syncAllDiscussions(undefined, true); + }); + });*/ }, }, ], }) -export class AddonMessagesModule { - - /* constructor( - contentLinksDelegate: CoreContentLinksDelegate, - indexLinkHandler: AddonMessagesIndexLinkHandler, - discussionLinkHandler: AddonMessagesDiscussionLinkHandler, - sendMessageHandler: AddonMessagesSendMessageUserHandler, - userDelegate: CoreUserDelegate, - cronDelegate: CoreCronDelegate, - syncHandler: AddonMessagesSyncCronHandler, - network: Network, - zone: NgZone, - messagesSync: AddonMessagesSyncProvider, - messagesProvider: AddonMessagesProvider, - sitesProvider: CoreSitesProvider, - linkHelper: CoreContentLinksHelperProvider, - pushNotificationsDelegate: CorePushNotificationsDelegate, - addContactHandler: AddonMessagesAddContactUserHandler, - blockContactHandler: AddonMessagesBlockContactUserHandler, - contactRequestLinkHandler: AddonMessagesContactRequestLinkHandler, - pushClickHandler: AddonMessagesPushClickHandler, - ) { - // Register handlers. - contentLinksDelegate.registerHandler(indexLinkHandler); - contentLinksDelegate.registerHandler(discussionLinkHandler); - contentLinksDelegate.registerHandler(contactRequestLinkHandler); - userDelegate.registerHandler(sendMessageHandler); - userDelegate.registerHandler(addContactHandler); - userDelegate.registerHandler(blockContactHandler); - cronDelegate.register(syncHandler); - pushNotificationsDelegate.registerClickHandler(pushClickHandler); - - // Sync some discussions when device goes online. - network.onConnect().subscribe(() => { - // Execute the callback in the Angular zone, so change detection doesn't stop working. - zone.run(() => { - messagesSync.syncAllDiscussions(undefined, true); - }); - }); - - const notificationClicked = (notification: any): void => { - messagesProvider.isMessagingEnabledForSite(notification.site).then(() => { - sitesProvider.isFeatureDisabled('CoreMainMenuDelegate_AddonMessages', notification.site).then((disabled) => { - if (disabled) { - // Messages are disabled, stop. - return; - } - - messagesProvider.invalidateDiscussionsCache(notification.site).finally(() => { - // Check if group messaging is enabled, to determine which page should be loaded. - messagesProvider.isGroupMessagingEnabledInSite(notification.site).then((enabled) => { - const pageParams: any = {}; - let pageName = 'AddonMessagesIndexPage'; - if (enabled) { - pageName = 'AddonMessagesGroupConversationsPage'; - } - - // Check if we have enough information to open the conversation. - if (notification.convid && enabled) { - pageParams.conversationId = Number(notification.convid); - } else if (notification.userfromid || notification.useridfrom) { - pageParams.discussionUserId = Number(notification.userfromid || notification.useridfrom); - } - - linkHelper.goInSite(undefined, pageName, pageParams, notification.site); - }); - }); - }); - }); - }; - }*/ - -} +export class AddonMessagesModule {} diff --git a/src/addons/messages/services/handlers/contact-request-link.ts b/src/addons/messages/services/handlers/contact-request-link.ts new file mode 100644 index 000000000..7c3622647 --- /dev/null +++ b/src/addons/messages/services/handlers/contact-request-link.ts @@ -0,0 +1,62 @@ +// (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 { Injectable } from '@angular/core'; +import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler'; +import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate'; +import { CoreNavigator } from '@services/navigator'; +import { makeSingleton } from '@singletons'; +import { AddonMessages } from '../messages'; + +/** + * Content links handler for a contact requests. + */ +@Injectable({ providedIn: 'root' }) +export class AddonMessagesContactRequestLinkHandlerService extends CoreContentLinksHandlerBase { + + name = 'AddonMessagesContactRequestLinkHandler'; + pattern = /\/message\/pendingcontactrequests\.php/; + + /** + * Get the list of actions for a link (url). + * + * @return List of (or promise resolved with list of) actions. + */ + getActions(): CoreContentLinksAction[] | Promise { + return [{ + action: (siteId): void => { + CoreNavigator.instance.navigateToSitePath('/messages/contacts', { siteId }); + }, + }]; + } + + /** + * Check if the handler is enabled for a certain site (site + user) and a URL. + * If not defined, defaults to true. + * + * @param siteId The site ID. + * @return Whether the handler is enabled for the URL and site. + */ + async isEnabled(siteId: string): Promise { + const enabled = await AddonMessages.instance.isPluginEnabled(siteId); + if (!enabled) { + return false; + } + + return AddonMessages.instance.isGroupMessagingEnabledInSite(siteId); + } + +} + +export class AddonMessagesContactRequestLinkHandler extends makeSingleton(AddonMessagesContactRequestLinkHandlerService) {} diff --git a/src/addons/messages/services/handlers/discussion-link.ts b/src/addons/messages/services/handlers/discussion-link.ts new file mode 100644 index 000000000..48d3853d8 --- /dev/null +++ b/src/addons/messages/services/handlers/discussion-link.ts @@ -0,0 +1,85 @@ +// (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 { Injectable } from '@angular/core'; +import { Params } from '@angular/router'; +import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler'; +import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate'; +import { CoreNavigator } from '@services/navigator'; +import { CoreSites } from '@services/sites'; +import { makeSingleton } from '@singletons'; +import { AddonMessages } from '../messages'; + +/** + * Content links handler for a discussion. + * Match message index URL with params id, user1 or user2. + */ +@Injectable({ providedIn: 'root' }) +export class AddonMessagesDiscussionLinkHandlerService extends CoreContentLinksHandlerBase { + + name = 'AddonMessagesDiscussionLinkHandler'; + pattern = /\/message\/index\.php.*([?&](id|user1|user2)=\d+)/; + + /** + * Get the list of actions for a link (url). + * + * @param siteIds List of sites the URL belongs to. + * @param url The URL to treat. + * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} + * @return List of (or promise resolved with list of) actions. + */ + getActions(siteIds: string[], url: string, params: Params): CoreContentLinksAction[] | Promise { + return [{ + action: (siteId): void => { + const stateParams = { + userId: parseInt(params.id || params.user2, 10), + }; + CoreNavigator.instance.navigateToSitePath('/messages/discussion', { params: stateParams, siteId }); + }, + }]; + } + + /** + * Check if the handler is enabled for a certain site (site + user) and a URL. + * If not defined, defaults to true. + * + * @param siteId The site ID. + * @param url The URL to treat. + * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} + * @return Whether the handler is enabled for the URL and site. + */ + async isEnabled(siteId: string, url: string, params: Params): Promise { + const enabled = await AddonMessages.instance.isPluginEnabled(siteId); + if (!enabled) { + return false; + } + + if (typeof params.id == 'undefined' && typeof params.user2 == 'undefined') { + // Other user not defined, cannot treat the URL. + return false; + } + + if (typeof params.user1 != 'undefined') { + // Check if user1 is the current user, since the app only supports current user. + const site = await CoreSites.instance.getSite(siteId); + + return parseInt(params.user1, 10) == site.getUserId(); + } + + return true; + } + +} + +export class AddonMessagesDiscussionLinkHandler extends makeSingleton(AddonMessagesDiscussionLinkHandlerService) {} diff --git a/src/addons/messages/services/handlers/index-link.ts b/src/addons/messages/services/handlers/index-link.ts new file mode 100644 index 000000000..33be2367b --- /dev/null +++ b/src/addons/messages/services/handlers/index-link.ts @@ -0,0 +1,61 @@ +// (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 { Injectable } from '@angular/core'; +import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler'; +import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate'; +import { CoreNavigator } from '@services/navigator'; +import { makeSingleton } from '@singletons'; +import { AddonMessages } from '../messages'; + +/** + * Content links handler for messaging index. + * Match message index URL without params id, user1 or user2. + */ +@Injectable({ providedIn: 'root' }) +export class AddonMessagesIndexLinkHandlerService extends CoreContentLinksHandlerBase { + + name = 'AddonMessagesIndexLinkHandler'; + pattern = /\/message\/index\.php((?![?&](id|user1|user2)=\d+).)*$/; + + + /** + * Get the list of actions for a link (url). + * + * @return List of (or promise resolved with list of) actions. + */ + getActions(): CoreContentLinksAction[] | Promise { + return [{ + action: async (siteId): Promise => { + const pageName = await AddonMessages.instance.getMainMessagesPagePathInSite(siteId); + + CoreNavigator.instance.navigateToSitePath(pageName, { siteId }); + }, + }]; + } + + /** + * Check if the handler is enabled for a certain site (site + user) and a URL. + * If not defined, defaults to true. + * + * @param siteId The site ID. + * @return Whether the handler is enabled for the URL and site. + */ + isEnabled(siteId: string): Promise { + return AddonMessages.instance.isPluginEnabled(siteId); + } + +} + +export class AddonMessagesIndexLinkHandler extends makeSingleton(AddonMessagesIndexLinkHandlerService) {} diff --git a/src/addons/messages/services/handlers/push-click.ts b/src/addons/messages/services/handlers/push-click.ts new file mode 100644 index 000000000..05b215885 --- /dev/null +++ b/src/addons/messages/services/handlers/push-click.ts @@ -0,0 +1,84 @@ +// (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 { Injectable } from '@angular/core'; +import { Params } from '@angular/router'; +import { CorePushNotificationsClickHandler } from '@features/pushnotifications/services/push-delegate'; +import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifications/services/pushnotifications'; +import { CoreNavigator } from '@services/navigator'; +import { CoreUtils } from '@services/utils/utils'; +import { makeSingleton } from '@singletons'; +import { AddonMessages } from '../messages'; + +/** + * Handler for messaging push notifications clicks. + */ +@Injectable({ providedIn: 'root' }) +export class AddonMessagesPushClickHandlerService implements CorePushNotificationsClickHandler { + + name = 'AddonMessagesPushClickHandler'; + priority = 200; + featureName = 'CoreMainMenuDelegate_AddonMessages'; + + /** + * Check if a notification click is handled by this handler. + * + * @param notification The notification to check. + * @return Whether the notification click is handled by this handler + */ + async handles(notification: AddonMessagesPushNotificationData): Promise { + if (CoreUtils.instance.isTrueOrOne(notification.notif) && notification.name != 'messagecontactrequests') { + return false; + } + + // Check that messaging is enabled. + return AddonMessages.instance.isPluginEnabled(notification.site); + } + + /** + * Handle the notification click. + * + * @param notification The notification to check. + * @return Promise resolved when done. + */ + async handleClick(notification: AddonMessagesPushNotificationData): Promise { + try { + await AddonMessages.instance.invalidateDiscussionsCache(notification.site); + } catch { + // Ignore errors. + } + + // Check if group messaging is enabled, to determine which page should be loaded. + const enabled = await AddonMessages.instance.isGroupMessagingEnabledInSite(notification.site); + const pageName = await AddonMessages.instance.getMainMessagesPagePathInSite(notification.site); + + const pageParams: Params = {}; + + // Check if we have enough information to open the conversation. + if (notification.convid && enabled) { + pageParams.conversationId = Number(notification.convid); + } else if (notification.userfromid) { + pageParams.discussionUserId = Number(notification.userfromid); + } + + await CoreNavigator.instance.navigateToSitePath(pageName, { params: pageParams, siteId: notification.site }); + } + +} + +export class AddonMessagesPushClickHandler extends makeSingleton(AddonMessagesPushClickHandlerService) {} + +type AddonMessagesPushNotificationData = CorePushNotificationsNotificationBasicData & { + convid?: number; // Conversation Id. +}; diff --git a/src/addons/messages/services/handlers/user-add-contact.ts b/src/addons/messages/services/handlers/user-add-contact.ts new file mode 100644 index 000000000..72dfadcdd --- /dev/null +++ b/src/addons/messages/services/handlers/user-add-contact.ts @@ -0,0 +1,222 @@ +// (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 { Injectable, OnDestroy } from '@angular/core'; +import { CoreEventObserver, CoreEvents } from '@singletons/events'; +import { makeSingleton, Translate } from '@singletons'; +import { + CoreUserDelegateService, + CoreUserProfileHandler, + CoreUserProfileHandlerData, +} from '@features/user/services/user-delegate'; +import { AddonMessages } from '../messages'; +import { CoreSites } from '@services/sites'; +import { CoreUserProfile } from '@features/user/services/user'; +import { CoreDomUtils } from '@services/utils/dom'; + +/** + * Profile add/remove contact handler. + */ +@Injectable({ providedIn: 'root' }) +export class AddonMessagesAddContactUserHandlerService implements CoreUserProfileHandler, OnDestroy { + + /** + * Update handler information event. + */ + static readonly UPDATED_EVENT = 'AddonMessagesAddContactUserHandler_updated_event'; + + name = 'AddonMessages:addContact'; + priority = 800; + type = CoreUserDelegateService.TYPE_ACTION; + + protected disabled = false; + protected updateObserver: CoreEventObserver; + + constructor() { + this.updateObserver = CoreEvents.on<{ userId: number }>( + AddonMessagesAddContactUserHandlerService.UPDATED_EVENT, + (data) => { + this.checkButton(data.userId); + }, + ); + } + + /** + * Check if handler is enabled. + * + * @return Promise resolved with true if enabled, rejected or resolved with false otherwise. + */ + async isEnabled(): Promise { + return AddonMessages.instance.isPluginEnabled(); + } + + /** + * Check if handler is enabled for this user in this context. + * + * @param user User to check. + * @return Promise resolved with true if enabled, resolved with false otherwise. + */ + async isEnabledForUser(user: CoreUserProfile): Promise { + return user.id != CoreSites.instance.getCurrentSiteUserId(); + } + + /** + * Returns the data needed to render the handler. + * + * @param user User object. + * @return Data needed to render the handler. + */ + getDisplayData(user: CoreUserProfile): CoreUserProfileHandlerData { + this.checkButton(user.id); + + return { + icon: '', + title: '', + spinner: false, + class: '', + action: async (event: Event, user: CoreUserProfile): Promise => { + event.preventDefault(); + event.stopPropagation(); + + if (this.disabled) { + return; + } + this.disabled = true; + this.updateButton(user.id, { spinner: true }); + + try { + const isContact = await AddonMessages.instance.isContact(user.id); + if (isContact) { + const message = Translate.instance.instant('addon.messages.removecontactconfirm', { $a: user.fullname }); + const okText = Translate.instance.instant('core.remove'); + let confirm = false; + + try { + await CoreDomUtils.instance.showConfirm(message, undefined, okText); + confirm = true; + } catch { + // Do nothing. + confirm = false; + } + + if (confirm) { + await AddonMessages.instance.removeContact(user.id); + } + } else { + await this.addContact(user); + } + } catch (error) { + CoreDomUtils.instance.showErrorModalDefault(error, 'core.error', true); + } finally { + CoreEvents.trigger(AddonMessagesAddContactUserHandlerService.UPDATED_EVENT, { userId: user.id }); + + this.checkButton(user.id).finally(() => { + this.disabled = false; + }); + } + + }, + }; + } + + /** + * Update Button with avalaible data. + * + * @param userId User Id to update. + * @return Promise resolved when done. + */ + protected async checkButton(userId: number): Promise { + this.updateButton(userId, { spinner: true }); + + const groupMessagingEnabled = AddonMessages.instance.isGroupMessagingEnabled(); + + try { + const isContact = await AddonMessages.instance.isContact(userId); + + if (isContact) { + this.updateButton(userId, { + title: groupMessagingEnabled ? 'addon.messages.removefromyourcontacts' : 'addon.messages.removecontact', + class: 'addon-messages-removecontact-handler', + icon: 'fas-user-times', + hidden: false, + spinner: false, + }); + } else { + this.updateButton(userId, { + title: groupMessagingEnabled ? 'addon.messages.addtoyourcontacts' : 'addon.messages.addcontact', + class: 'addon-messages-addcontact-handler', + icon: 'fas-user-plus', + hidden: false, + spinner: false, + }); + } + } catch { + // This fails for some reason, let's just hide the button. + this.updateButton(userId, { hidden: true }); + } + } + + /** + * Triggers the event to update the handler information. + * + * @param userId The user ID the handler belongs to. + * @param data Data that should be updated. + */ + protected updateButton(userId: number, data: Record): void { + // This fails for some reason, let's just hide the button. + CoreEvents.trigger(CoreUserDelegateService.UPDATE_HANDLER_EVENT, { handler: this.name, data: data, userId: userId }); + } + + /** + * Add a contact or send a contact request if group messaging is enabled. + * + * @param user User to add as contact. + * @return Promise resolved when done. + */ + protected async addContact(user: CoreUserProfile): Promise { + if (!AddonMessages.instance.isGroupMessagingEnabled()) { + return AddonMessages.instance.addContact(user.id); + } + + const member = await AddonMessages.instance.getMemberInfo(user.id); + const currentUserId = CoreSites.instance.getCurrentSiteUserId(); + const requestSent = member.contactrequests?.some((request) => + request.userid == currentUserId && request.requesteduserid == user.id); + if (requestSent) { + const message = Translate.instance.instant('addon.messages.yourcontactrequestpending', { $a: user.fullname }); + + await CoreDomUtils.instance.showAlert(undefined, message); + + return; + } + + const message = Translate.instance.instant('addon.messages.addcontactconfirm', { $a: user.fullname }); + const okText = Translate.instance.instant('core.add'); + await CoreDomUtils.instance.showConfirm(message, undefined, okText); + + await AddonMessages.instance.createContactRequest(user.id); + + await CoreDomUtils.instance.showAlert(undefined, Translate.instance.instant('addon.messages.contactrequestsent')); + } + + /** + * Destroyed method. + */ + ngOnDestroy(): void { + this.updateObserver?.off(); + } + +} + +export class AddonMessagesAddContactUserHandler extends makeSingleton(AddonMessagesAddContactUserHandlerService) {} diff --git a/src/addons/messages/services/handlers/user-block-contact.ts b/src/addons/messages/services/handlers/user-block-contact.ts new file mode 100644 index 000000000..9529244ad --- /dev/null +++ b/src/addons/messages/services/handlers/user-block-contact.ts @@ -0,0 +1,197 @@ +// (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 { Injectable, OnDestroy } from '@angular/core'; +import { CoreUserProfile } from '@features/user/services/user'; +import { CoreUserDelegateService, CoreUserProfileHandler, CoreUserProfileHandlerData } from '@features/user/services/user-delegate'; +import { CoreSites } from '@services/sites'; +import { CoreDomUtils } from '@services/utils/dom'; +import { makeSingleton, Translate } from '@singletons'; +import { CoreEventObserver, CoreEvents } from '@singletons/events'; +import { AddonMessages } from '../messages'; + +/** + * Profile block/unblock contact handler. + */ +@Injectable({ providedIn: 'root' }) +export class AddonMessagesBlockContactUserHandlerService implements CoreUserProfileHandler, OnDestroy { + + /** + * Update handler information event. + */ + static readonly UPDATED_EVENT = 'AddonMessagesBlockContactUserHandler_updated_event'; + + name = 'AddonMessages:blockContact'; + priority = 600; + type = CoreUserDelegateService.TYPE_ACTION; + + protected disabled = false; + protected updateObserver: CoreEventObserver; + + constructor() { + + this.updateObserver = CoreEvents.on<{ userId: number }>( + AddonMessagesBlockContactUserHandlerService.UPDATED_EVENT, + (data) => { + this.checkButton(data.userId); + }, + ); + } + + /** + * Check if handler is enabled. + * + * @return Promise resolved with true if enabled, rejected or resolved with false otherwise. + */ + async isEnabled(): Promise { + return AddonMessages.instance.isPluginEnabled(); + } + + /** + * Check if handler is enabled for this user in this context. + * + * @param user User to check. + * @return Promise resolved with true if enabled, resolved with false otherwise. + */ + async isEnabledForUser(user: CoreUserProfile): Promise { + return user.id != CoreSites.instance.getCurrentSiteUserId(); + } + + /** + * Returns the data needed to render the handler. + * + * @param user User object. + * @return Data needed to render the handler. + */ + getDisplayData(user: CoreUserProfile): CoreUserProfileHandlerData { + this.checkButton(user.id); + + return { + icon: '', + title: '', + spinner: false, + class: '', + action: async (event: Event, user: CoreUserProfile): Promise => { + event.preventDefault(); + event.stopPropagation(); + + if (this.disabled) { + return; + } + this.disabled = true; + this.updateButton(user.id, { spinner: true }); + + try { + const isBlocked = await AddonMessages.instance.isBlocked(user.id); + if (isBlocked) { + const template = Translate.instance.instant('addon.messages.unblockuserconfirm', { $a: user.fullname }); + const okText = Translate.instance.instant('addon.messages.unblockuser'); + let confirm = false; + + try { + await CoreDomUtils.instance.showConfirm(template, undefined, okText); + confirm = true; + } catch { + // Do nothing. + confirm = false; + } + + if (confirm) { + await AddonMessages.instance.unblockContact(user.id); + } + } else { + const template = Translate.instance.instant('addon.messages.blockuserconfirm', { $a: user.fullname }); + const okText = Translate.instance.instant('addon.messages.blockuser'); + let confirm = false; + + try { + await CoreDomUtils.instance.showConfirm(template, undefined, okText); + confirm = true; + } catch { + // Do nothing. + confirm = false; + } + + if (confirm) { + await AddonMessages.instance.blockContact(user.id); + } + } + } catch (error) { + CoreDomUtils.instance.showErrorModalDefault(error, 'core.error', true); + } finally { + CoreEvents.trigger(AddonMessagesBlockContactUserHandlerService.UPDATED_EVENT, { userId: user.id }); + + this.checkButton(user.id).finally(() => { + this.disabled = false; + }); + } + }, + }; + } + + /** + * Update Button with avalaible data. + * + * @param userId User Id to update. + * @return Promise resolved when done. + */ + protected async checkButton(userId: number): Promise { + this.updateButton(userId, { spinner: true }); + + try { + const isBlocked = await AddonMessages.instance.isBlocked(userId); + if (isBlocked) { + this.updateButton(userId, { + title: 'addon.messages.unblockuser', + class: 'addon-messages-unblockcontact-handler', + icon: 'fas-user-check', + hidden: false, + spinner: false, + }); + } else { + this.updateButton(userId, { + title: 'addon.messages.blockuser', + class: 'addon-messages-blockcontact-handler', + icon: 'fas-user-lock', + hidden: false, + spinner: false, + }); + } + } catch { + // This fails for some reason, let's just hide the button. + this.updateButton(userId, { hidden: true }); + } + } + + /** + * Triggers the event to update the handler information. + * + * @param userId The user ID the handler belongs to. + * @param data Data that should be updated. + */ + protected updateButton(userId: number, data: Record): void { + // This fails for some reason, let's just hide the button. + CoreEvents.trigger(CoreUserDelegateService.UPDATE_HANDLER_EVENT, { handler: this.name, data: data, userId: userId }); + } + + /** + * Destroyed method. + */ + ngOnDestroy(): void { + this.updateObserver?.off(); + } + +} + +export class AddonMessagesBlockContactUserHandler extends makeSingleton(AddonMessagesBlockContactUserHandlerService) {} diff --git a/src/addons/messages/services/handlers/user-send-message.ts b/src/addons/messages/services/handlers/user-send-message.ts new file mode 100644 index 000000000..b3c99a5c6 --- /dev/null +++ b/src/addons/messages/services/handlers/user-send-message.ts @@ -0,0 +1,85 @@ +// (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 { Injectable } from '@angular/core'; +import { Params } from '@angular/router'; +import { CoreUserProfile } from '@features/user/services/user'; +import { CoreUserDelegateService, CoreUserProfileHandler, CoreUserProfileHandlerData } from '@features/user/services/user-delegate'; +import { CoreNavigator } from '@services/navigator'; +import { CoreSites } from '@services/sites'; +import { makeSingleton } from '@singletons'; +import { AddonMessages } from '../messages'; + +/** + * Profile send message handler. + */ +@Injectable({ providedIn: 'root' }) +export class AddonMessagesSendMessageUserHandlerService implements CoreUserProfileHandler { + + name = 'AddonMessages:sendMessage'; + priority = 1000; + type = CoreUserDelegateService.TYPE_COMMUNICATION; + + /** + * Check if handler is enabled. + * + * @return Promise resolved with true if enabled, rejected or resolved with false otherwise. + */ + isEnabled(): Promise { + return AddonMessages.instance.isPluginEnabled(); + } + + /** + * Check if handler is enabled for this user in this context. + * + * @param user User to check. + * @return Promise resolved with true if enabled, resolved with false otherwise. + */ + async isEnabledForUser(user: CoreUserProfile): Promise { + const currentSite = CoreSites.instance.getCurrentSite(); + + if (!currentSite) { + return false; + } + + // From 3.7 you can send messages to yourself. + return user.id != currentSite.getUserId() || currentSite.isVersionGreaterEqualThan('3.7'); + } + + /** + * Returns the data needed to render the handler. + * + * @return Data needed to render the handler. + */ + getDisplayData(): CoreUserProfileHandlerData { + return { + icon: 'fas-paper-plane', + title: 'addon.messages.message', + class: 'addon-messages-send-message-handler', + action: (event: Event, user: CoreUserProfile): void => { + event.preventDefault(); + event.stopPropagation(); + + const pageParams: Params = { + showKeyboard: true, + userId: user.id, + }; + CoreNavigator.instance.navigateToSitePath('/messages/discussion', { params: pageParams }); + }, + }; + } + +} + +export class AddonMessagesSendMessageUserHandler extends makeSingleton(AddonMessagesSendMessageUserHandlerService) {} diff --git a/src/addons/messages/services/messages.ts b/src/addons/messages/services/messages.ts index fcc510658..b820c8fe6 100644 --- a/src/addons/messages/services/messages.ts +++ b/src/addons/messages/services/messages.ts @@ -1388,13 +1388,27 @@ export class AddonMessagesProvider { throw new CoreError('Error getting message preferences'); } + /** + * Gets the site main messages page path for a site. + * + * @param siteId Site ID. If not defined, use current site. + * @return Main messages page path of the site. + */ + async getMainMessagesPagePathInSite(siteId?: string): Promise { + const enabled = await this.isGroupMessagingEnabledInSite(siteId); + + return AddonMessagesMainMenuHandlerService.PAGE_NAME + ( enabled ? '/group-conversations' : ''); + } + /** * Gets the site main messages page path. * * @return Main messages page path of the site. */ getMainMessagesPagePath(): string { - return AddonMessagesMainMenuHandlerService.PAGE_NAME + (this.isGroupMessagingEnabled() ? '/group-conversations' : ''); + const enabled = this.isGroupMessagingEnabled(); + + return AddonMessagesMainMenuHandlerService.PAGE_NAME + ( enabled ? '/group-conversations' : ''); }