diff --git a/src/addon/messages/lang/en.json b/src/addon/messages/lang/en.json index e76d05634..1972d107d 100644 --- a/src/addon/messages/lang/en.json +++ b/src/addon/messages/lang/en.json @@ -1,4 +1,5 @@ { + "blocknoncontacts": "Prevent non-contacts from messaging me", "contacts": "Contacts", "deletemessage": "Delete message", "deletemessageconfirmation": "Are you sure you want to delete this message? It will only be deleted from your messaging history and will still be viewable by the user who sent or received the message.", diff --git a/src/addon/messages/pages/settings/settings.html b/src/addon/messages/pages/settings/settings.html new file mode 100644 index 000000000..e9b27fbc1 --- /dev/null +++ b/src/addon/messages/pages/settings/settings.html @@ -0,0 +1,57 @@ + + + {{ 'addon.messages.messagepreferences' | translate }} + + + + + + + + + + + {{ 'addon.messages.blocknoncontacts' | translate }} + + + + + +
+ + + + {{ notification.displayname }} + {{ 'core.settings.loggedin' | translate }} + {{ 'core.settings.loggedoff' | translate }} + + + + + + {{ processor.displayname }} + + + + + + + {{ 'core.settings.disabled' | translate }} + + + + {{ processor.displayname }} + + + {{ 'core.settings.' + state | translate }} + + + + {{ 'core.settings.disabled' | translate }} + + + +
+
+
+
diff --git a/src/addon/messages/pages/settings/settings.module.ts b/src/addon/messages/pages/settings/settings.module.ts new file mode 100644 index 000000000..ce9ec9475 --- /dev/null +++ b/src/addon/messages/pages/settings/settings.module.ts @@ -0,0 +1,35 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { NgModule } from '@angular/core'; +import { IonicPageModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { AddonMessagesSettingsPage } from './settings'; +import { CoreComponentsModule } from '../../../../components/components.module'; +import { CoreDirectivesModule } from '../../../../directives/directives.module'; +import { AddonMessagesComponentsModule } from '../../components/components.module'; + +@NgModule({ + declarations: [ + AddonMessagesSettingsPage, + ], + imports: [ + CoreComponentsModule, + CoreDirectivesModule, + AddonMessagesComponentsModule, + IonicPageModule.forChild(AddonMessagesSettingsPage), + TranslateModule.forChild() + ], +}) +export class AddonMessagesSettingsPageModule {} diff --git a/src/addon/messages/pages/settings/settings.scss b/src/addon/messages/pages/settings/settings.scss new file mode 100644 index 000000000..4b0e04a62 --- /dev/null +++ b/src/addon/messages/pages/settings/settings.scss @@ -0,0 +1,10 @@ +page-addon-messages-settings { + .list-header { + margin-bottom: 0; + border-top: 0; + } + + .toggle { + display: inline-block; + } +} \ No newline at end of file diff --git a/src/addon/messages/pages/settings/settings.ts b/src/addon/messages/pages/settings/settings.ts new file mode 100644 index 000000000..ea4ec02c9 --- /dev/null +++ b/src/addon/messages/pages/settings/settings.ts @@ -0,0 +1,166 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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, OnDestroy } from '@angular/core'; +import { IonicPage } from 'ionic-angular'; +import { AddonMessagesProvider } from '../../providers/messages'; +import { CoreUserProvider } from '@core/user/providers/user'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; + +/** + * Page that displays the messages settings page. + */ +@IonicPage({ segment: 'addon-messages-settings' }) +@Component({ + selector: 'page-addon-messages-settings', + templateUrl: 'settings.html', +}) +export class AddonMessagesSettingsPage implements OnDestroy { + protected updateTimeout: any; + + preferences: any; + preferencesLoaded: boolean; + blockNonContactsState = false; + + constructor(private messagesProvider: AddonMessagesProvider, private domUtils: CoreDomUtilsProvider, + private userProvider: CoreUserProvider) { + } + + /** + * Runs when the page has loaded. This event only happens once per page being created. + * If a page leaves but is cached, then this event will not fire again on a subsequent viewing. + * Setup code for the page. + */ + ionViewDidLoad(): void { + this.fetchPreferences(); + } + + /** + * Fetches preference data. + * @return {Promise} Resolved when done. + */ + protected fetchPreferences(): Promise { + return this.messagesProvider.getMessagePreferences().then((preferences) => { + this.preferences = preferences; + this.blockNonContactsState = preferences.blocknoncontacts; + }).catch((message) => { + this.domUtils.showErrorModal(message); + }).finally(() => { + this.preferencesLoaded = true; + }); + } + + /** + * Update preferences. The purpose is to store the updated data, it won't be reflected in the view. + */ + protected updatePreferences(): void { + this.messagesProvider.invalidateMessagePreferences().finally(() => { + this.fetchPreferences(); + }); + } + + /** + * Update preferences after a certain time. The purpose is to store the updated data, it won't be reflected in the view. + */ + protected updatePreferencesAfterDelay(): void { + // Cancel pending updates. + clearTimeout(this.updateTimeout); + + this.updateTimeout = setTimeout(() => { + this.updateTimeout = null; + this.updatePreferences(); + }, 5000); + } + + /** + * Block non contacts. + * @param {boolean} block If it should be blocked or not. + */ + blockNonContacts(block: boolean): void { + const modal = this.domUtils.showModalLoading('core.sending', true); + this.userProvider.updateUserPreference('message_blocknoncontacts', block ? 1 : 0).then(() => { + // Update the preferences since they were modified. + this.updatePreferencesAfterDelay(); + }).catch((message) => { + // Show error and revert change. + this.domUtils.showErrorModal(message); + this.blockNonContactsState = !this.blockNonContactsState; + }).finally(() => { + modal.dismiss(); + }); + } + + /** + * Change the value of a certain preference. + * @param {any} notification Notification object. + * @param {string} state State name, ['loggedin', 'loggedoff']. + * @param {any} processor Notification processor. + */ + changePreference(notification: any, state: string, processor: any): void { + const processorState = processor[state], + preferenceName = notification.preferencekey + '_' + processorState.name, + valueArray = []; + let value = 'none'; + + notification.processors.forEach((processor) => { + if (processor[state].checked) { + valueArray.push(processor.name); + } + }); + + if (value.length > 0) { + value = valueArray.join(','); + } + + if (!notification.updating) { + notification.updating = {}; + } + + notification.updating[state] = true; + this.userProvider.updateUserPreference(preferenceName, value).then(() => { + // Update the preferences since they were modified. + this.updatePreferencesAfterDelay(); + }).catch((message) => { + // Show error and revert change. + this.domUtils.showErrorModal(message); + processorState.checked = !processorState.checked; + }).finally(() => { + notification.updating[state] = false; + }); + } + + /** + * Refresh the list of preferences. + * + * @param {any} refresher Refresher. + */ + refreshEvent(refresher: any): void { + this.messagesProvider.invalidateMessagePreferences().finally(() => { + this.fetchPreferences().finally(() => { + refresher.complete(); + }); + }); + } + + /** + * Page destroyed. + */ + ngOnDestroy(): void { + // If there is a pending action to update preferences, execute it right now. + if (this.updateTimeout) { + clearTimeout(this.updateTimeout); + this.updatePreferences(); + } + } +} diff --git a/src/addon/messages/providers/messages.ts b/src/addon/messages/providers/messages.ts index 6e7362f81..60f8bea99 100644 --- a/src/addon/messages/providers/messages.ts +++ b/src/addon/messages/providers/messages.ts @@ -304,6 +304,41 @@ export class AddonMessagesProvider { }); } + /** + * Get the cache key for the get message preferences call. + * + * @return {string} Cache key. + */ + protected getMessagePreferencesCacheKey(): string { + return this.ROOT_CACHE_KEY + 'messagePreferences'; + } + + /** + * Get message preferences. + * + * @param {string} [siteId] Site ID. If not defined, use current site. + * @return {Promise} Promise resolved with the message preferences. + */ + getMessagePreferences(siteId?: string): Promise { + this.logger.debug('Get message preferences'); + + return this.sitesProvider.getSite(siteId).then((site) => { + const preSets = { + cacheKey: this.getMessagePreferencesCacheKey() + }; + + return site.read('core_message_get_user_message_preferences', {}, preSets).then((data) => { + if (data.preferences) { + data.preferences.blocknoncontacts = !!data.blocknoncontacts; + + return data.preferences; + } + + return Promise.reject(null); + }); + }); + } + /** * Get messages according to the params. * @@ -515,6 +550,18 @@ export class AddonMessagesProvider { }); } + /** + * Invalidate get message preferences. + * + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when data is invalidated. + */ + invalidateMessagePreferences(siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return site.invalidateWsCacheForKey(this.getMessagePreferencesCacheKey()); + }); + } + /** * Returns whether or not we can mark all messages as read. * diff --git a/src/core/user/providers/user.ts b/src/core/user/providers/user.ts index b6af8f055..866f8bc42 100644 --- a/src/core/user/providers/user.ts +++ b/src/core/user/providers/user.ts @@ -405,4 +405,52 @@ export class CoreUserProvider { return Promise.all(promises); } + + /** + * Update a preference for a user. + * + * @param {string} name Preference name. + * @param {any} value Preference new value. + * @param {number} [userId] User ID. If not defined, site's current user. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved if success. + */ + updateUserPreference(name:string, value: any, userId?: number, siteId?: string): Promise { + const preferences = [ + { + type: name, + value: value + } + ]; + return this.updateUserPreferences(preferences, undefined, userId, siteId); + } + + /** + * Update some preferences for a user. + * + * @param {any} preferences List of preferences. + * @param {boolean} [disableNotifications] Whether to disable all notifications. Undefined to not update this value. + * @param {number} [userId] User ID. If not defined, site's current user. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved if success. + */ + updateUserPreferences(preferences: any, disableNotifications: boolean, userId?: number, siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + userId = userId || site.getUserId(); + + const data = { + userid: userId, + preferences: preferences + }, + preSets = { + responseExpected: false + }; + + if (typeof disableNotifications != 'undefined') { + data['emailstop'] = disableNotifications ? 1 : 0; + } + + return site.write('core_user_update_user_preferences', data, preSets); + }); + } }