MOBILE-2327 messages: Implement settings page
parent
29809e8dbc
commit
86edc5f2b0
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"blocknoncontacts": "Prevent non-contacts from messaging me",
|
||||||
"contacts": "Contacts",
|
"contacts": "Contacts",
|
||||||
"deletemessage": "Delete message",
|
"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.",
|
"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.",
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-navbar>
|
||||||
|
<ion-title>{{ 'addon.messages.messagepreferences' | translate }}</ion-title>
|
||||||
|
</ion-navbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content>
|
||||||
|
<ion-refresher [enabled]="preferencesLoaded" (ionRefresh)="refreshPreferences($event)">
|
||||||
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
|
</ion-refresher>
|
||||||
|
<core-loading [hideUntil]="preferencesLoaded">
|
||||||
|
<!-- Block non contacts. -->
|
||||||
|
<ion-card>
|
||||||
|
<ion-item>
|
||||||
|
<ion-label>{{ 'addon.messages.blocknoncontacts' | translate }}</ion-label>
|
||||||
|
<ion-toggle [(ngModel)]="blockNonContactsState" (ionChange)="blockNonContacts(blockNonContactsState)"></ion-toggle>
|
||||||
|
</ion-item>
|
||||||
|
</ion-card>
|
||||||
|
|
||||||
|
<ng-container *ngIf="preferences">
|
||||||
|
<div *ngFor="let component of preferences.components">
|
||||||
|
<ion-card list *ngFor="let notification of component.notifications">
|
||||||
|
<ion-item-divider color="light" text-wrap>
|
||||||
|
<ion-row no-padding>
|
||||||
|
<ion-col no-padding>{{ notification.displayname }}</ion-col>
|
||||||
|
<ion-col col-2 text-center no-padding class="hidden-phone">{{ 'core.settings.loggedin' | translate }}</ion-col>
|
||||||
|
<ion-col col-2 text-center no-padding class="hidden-phone">{{ 'core.settings.loggedoff' | translate }}</ion-col>
|
||||||
|
</ion-row>
|
||||||
|
</ion-item-divider>
|
||||||
|
<ng-container *ngFor="let processor of notification.processors">
|
||||||
|
<!-- Tablet view -->
|
||||||
|
<ion-row text-wrap class="hidden-phone" align-items-center>
|
||||||
|
<ion-col margin-horizontal>{{ processor.displayname }}</ion-col>
|
||||||
|
<ion-col col-2 text-center *ngFor="let state of ['loggedin', 'loggedoff']">
|
||||||
|
<!-- If notifications not disabled, show toggle. -->
|
||||||
|
<ion-spinner [hidden]="preferences.disableall || !(notification.updating && notification.updating[state])"></ion-spinner>
|
||||||
|
<ion-toggle *ngIf="!preferences.disableall" [(ngModel)]="processor[state].checked" (ionChange)="changePreference(notification, state, processor)" [disabled]="processor.locked || (notification.updating && notification.updating[state])">
|
||||||
|
</ion-toggle>
|
||||||
|
<!-- If notifications are disabled, show "Disabled" instead of toggle. -->
|
||||||
|
<span *ngIf="preferences.disableall">{{ 'core.settings.disabled' | translate }}</span>
|
||||||
|
</ion-col>
|
||||||
|
</ion-row>
|
||||||
|
<!-- Phone view -->
|
||||||
|
<ion-list-header text-wrap class="hidden-tablet">{{ processor.displayname }}</ion-list-header>
|
||||||
|
<!-- If notifications not disabled, show toggles. If notifications are disabled, show "Disabled" instead of toggle. -->
|
||||||
|
<ion-item *ngFor="let state of ['loggedin', 'loggedoff']" text-wrap class="hidden-tablet">
|
||||||
|
<ion-label>{{ 'core.settings.' + state | translate }}</ion-label>
|
||||||
|
<ion-spinner item-end *ngIf="!preferences.disableall && (notification.updating && notification.updating[state])"></ion-spinner>
|
||||||
|
<ion-toggle item-end *ngIf="!preferences.disableall" [(ngModel)]="processor[state].checked" (ionChange)="changePreference(notification, state, processor)" [disabled]="processor.locked || (notification.updating && notification.updating[state])">
|
||||||
|
</ion-toggle>
|
||||||
|
<ion-note item-end *ngIf="preferences.disableall">{{ 'core.settings.disabled' | translate }}</ion-note>
|
||||||
|
</ion-item>
|
||||||
|
</ng-container>
|
||||||
|
</ion-card>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</core-loading>
|
||||||
|
</ion-content>
|
|
@ -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 {}
|
|
@ -0,0 +1,10 @@
|
||||||
|
page-addon-messages-settings {
|
||||||
|
.list-header {
|
||||||
|
margin-bottom: 0;
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<any>} Resolved when done.
|
||||||
|
*/
|
||||||
|
protected fetchPreferences(): Promise<any> {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<any>} Promise resolved with the message preferences.
|
||||||
|
*/
|
||||||
|
getMessagePreferences(siteId?: string): Promise<any> {
|
||||||
|
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.
|
* 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<any>} Promise resolved when data is invalidated.
|
||||||
|
*/
|
||||||
|
invalidateMessagePreferences(siteId?: string): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
return site.invalidateWsCacheForKey(this.getMessagePreferencesCacheKey());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether or not we can mark all messages as read.
|
* Returns whether or not we can mark all messages as read.
|
||||||
*
|
*
|
||||||
|
|
|
@ -405,4 +405,52 @@ export class CoreUserProvider {
|
||||||
|
|
||||||
return Promise.all(promises);
|
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<any>} Promise resolved if success.
|
||||||
|
*/
|
||||||
|
updateUserPreference(name:string, value: any, userId?: number, siteId?: string): Promise<any> {
|
||||||
|
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<any>} Promise resolved if success.
|
||||||
|
*/
|
||||||
|
updateUserPreferences(preferences: any, disableNotifications: boolean, userId?: number, siteId?: string): Promise<any> {
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue