MOBILE-2327 messages: Fix errors from Peer review
parent
06b8bcc026
commit
f938b37f73
|
@ -7,7 +7,7 @@
|
|||
"email": "mobile@moodle.com"
|
||||
},
|
||||
"config": {
|
||||
"ionic_webpack": "./webpack.config.js"
|
||||
"ionic_webpack": "./config/webpack.config.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
<core-search-box (onSubmit)="search($event)" (onClear])="clearSearch($event)" [placeholder]=" 'addon.messages.contactname' | translate" autocorrect="off" spellcheck="false" lengthCheck="2"></core-search-box>
|
||||
<core-search-box (onSubmit)="search($event)" (onClear])="clearSearch($event)" [placeholder]=" 'addon.messages.contactname' | translate" autocorrect="off" spellcheck="false" lengthCheck="2" [disabled]="!loaded"></core-search-box>
|
||||
|
||||
<core-loading [hideUntil]="loaded" [message]="loadingMessage">
|
||||
<core-empty-box *ngIf="!hasContacts && searchString == ''" icon="person" [message]="'addon.messages.contactlistempty' | translate"></core-empty-box>
|
||||
|
|
|
@ -134,8 +134,6 @@ export class AddonMessagesContactsComponent {
|
|||
this.clearSearch();
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingcontacts', true);
|
||||
|
||||
return Promise.reject(null);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -202,8 +200,6 @@ export class AddonMessagesContactsComponent {
|
|||
this.contacts['search'] = this.sortUsers(result);
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingcontacts', true);
|
||||
|
||||
return Promise.reject(null);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
<core-search-box *ngIf="search.enabled" (onSubmit)="searchMessage($event)" (onClear)="clearSearch($event)" [placeholder]=" 'addon.messages.message' | translate" autocorrect="off" spellcheck="false" lengthCheck="2"></core-search-box>
|
||||
<core-search-box *ngIf="search.enabled" (onSubmit)="searchMessage($event)" (onClear)="clearSearch($event)" [placeholder]=" 'addon.messages.message' | translate" autocorrect="off" spellcheck="false" lengthCheck="2" [disabled]="!loaded"></core-search-box>
|
||||
|
||||
<core-loading [hideUntil]="loaded" [message]="loadingMessage">
|
||||
<!-- Message telling there are no files. -->
|
||||
|
||||
<core-empty-box *ngIf="(!discussions || discussions.length <= 0) && !search.showResults" icon="chatbubbles" [message]="'addon.messages.nomessages' | translate"></core-empty-box>
|
||||
|
||||
<core-empty-box *ngIf="(!search.results || search.results.length <= 0) && search.showResults" icon="search" [message]="'core.noresults' | translate"></core-empty-box>
|
||||
|
@ -33,7 +33,7 @@
|
|||
<h2>
|
||||
<core-format-text [text]="discussion.fullname"></core-format-text>
|
||||
<ion-note *ngIf="discussion.message.timecreated > 0 || discussion.unread">
|
||||
<div *ngIf="discussion.message.timecreated> 0">{{discussion.message.timecreated / 1000 | coreDateDayOrTime}}</div>
|
||||
<div *ngIf="discussion.message.timecreated > 0">{{discussion.message.timecreated / 1000 | coreDateDayOrTime}}</div>
|
||||
<div text-right *ngIf="discussion.unread" class="core-primary-circle"></div>
|
||||
</ion-note>
|
||||
</h2>
|
||||
|
|
|
@ -42,7 +42,7 @@ export class AddonMessagesDiscussionsComponent implements OnDestroy {
|
|||
loadingMessage: string;
|
||||
discussions: any;
|
||||
discussionUserId: number;
|
||||
messageId: number;
|
||||
pushObserver: any;
|
||||
search = {
|
||||
enabled: false,
|
||||
showResults: false,
|
||||
|
@ -54,7 +54,7 @@ export class AddonMessagesDiscussionsComponent implements OnDestroy {
|
|||
constructor(private eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, translate: TranslateService,
|
||||
private messagesProvider: AddonMessagesProvider, private domUtils: CoreDomUtilsProvider, navParams: NavParams,
|
||||
private appProvider: CoreAppProvider, platform: Platform, utils: CoreUtilsProvider,
|
||||
private pushNotificationsDelegate: AddonPushNotificationsDelegate) {
|
||||
pushNotificationsDelegate: AddonPushNotificationsDelegate) {
|
||||
|
||||
this.search.loading = translate.instant('core.searching');
|
||||
this.loadingMessages = translate.instant('core.loading');
|
||||
|
@ -114,7 +114,7 @@ export class AddonMessagesDiscussionsComponent implements OnDestroy {
|
|||
this.discussionUserId = navParams.get('discussionUserId') || false;
|
||||
|
||||
// If a message push notification is received, refresh the view.
|
||||
pushNotificationsDelegate.registerReceiveHandler('AddonMessagesDiscussionsComponent', (notification) => {
|
||||
this.pushObserver = pushNotificationsDelegate.on('receive').subscribe((notification) => {
|
||||
// New message received. If it's from current site, refresh the data.
|
||||
if (utils.isFalseOrZero(notification.notif) && notification.site == this.siteId) {
|
||||
this.refreshData();
|
||||
|
@ -211,7 +211,7 @@ export class AddonMessagesDiscussionsComponent implements OnDestroy {
|
|||
this.search.showResults = true;
|
||||
this.search.results = searchResults;
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'mma.messages.errorwhileretrievingmessages', true);
|
||||
this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingmessages', true);
|
||||
}).finally(() => {
|
||||
this.loaded = true;
|
||||
});
|
||||
|
@ -233,7 +233,6 @@ export class AddonMessagesDiscussionsComponent implements OnDestroy {
|
|||
};
|
||||
if (messageId) {
|
||||
params['message'] = messageId;
|
||||
this.messageId = messageId;
|
||||
}
|
||||
this.eventsProvider.trigger(AddonMessagesProvider.SPLIT_VIEW_LOAD_EVENT, params, this.siteId);
|
||||
}
|
||||
|
@ -246,6 +245,6 @@ export class AddonMessagesDiscussionsComponent implements OnDestroy {
|
|||
this.readChangedObserver && this.readChangedObserver.off();
|
||||
this.cronObserver && this.cronObserver.off();
|
||||
this.appResumeSubscription && this.appResumeSubscription.unsubscribe();
|
||||
this.pushNotificationsDelegate.unregisterReceiveHandler('AddonMessagesDiscussionsComponent');
|
||||
this.pushObserver && this.pushObserver.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ export class AddonMessagesModule {
|
|||
}
|
||||
|
||||
// Register push notification clicks.
|
||||
pushNotificationsDelegate.registerHandler('mmaMessages', (notification) => {
|
||||
pushNotificationsDelegate.on('click').subscribe((notification) => {
|
||||
if (utils.isFalseOrZero(notification.notif)) {
|
||||
notificationClicked(notification);
|
||||
|
||||
|
|
|
@ -172,7 +172,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingmessages', true);
|
||||
}).finally(() => {
|
||||
this.triggerDiscussionLoadedEvent();
|
||||
this.checkCanDelete();
|
||||
this.resizeContent();
|
||||
this.loaded = true;
|
||||
});
|
||||
|
@ -201,9 +201,8 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
protected fetchData(): Promise<any> {
|
||||
this.logger.debug(`Polling new messages for discussion with user '${this.userId}'`);
|
||||
if (this.messagesBeingSent > 0) {
|
||||
// We do not poll while a message is being sent or we could confuse the user
|
||||
// as his message would disappear from the list, and he'd have to wait for the
|
||||
// interval to check for new messages.
|
||||
// We do not poll while a message is being sent or we could confuse the user.
|
||||
// Otherwise, his message would disappear from the list, and he'd have to wait for the interval to check for messages.
|
||||
return Promise.reject(null);
|
||||
} else if (this.fetching) {
|
||||
// Already fetching.
|
||||
|
@ -384,7 +383,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
// Update navBar links and buttons.
|
||||
const newCanDelete = (last && last.id && this.messages.length == 1) || this.messages.length > 1;
|
||||
if (this.canDelete != newCanDelete) {
|
||||
this.triggerDiscussionLoadedEvent();
|
||||
this.checkCanDelete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -425,7 +424,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
/**
|
||||
* Check if there's any message in the list that can be deleted.
|
||||
*/
|
||||
protected triggerDiscussionLoadedEvent(): void {
|
||||
protected checkCanDelete(): void {
|
||||
// All messages being sent should be at the end of the list.
|
||||
const first = this.messages[0];
|
||||
this.canDelete = first && !first.sending;
|
||||
|
@ -503,7 +502,6 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
this.utils.copyToClipboard(text);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function to delete a message.
|
||||
*
|
||||
|
@ -627,8 +625,8 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
}
|
||||
|
||||
promise.catch(() => {
|
||||
// Fetch failed or is offline message, mark the message as sent. If fetch is successful there's no need
|
||||
// to mark it because the fetch will already show the message received from the server.
|
||||
// Fetch failed or is offline message, mark the message as sent.
|
||||
// If fetch is successful there's no need to mark it because the fetch will already show the message received.
|
||||
message.sending = false;
|
||||
if (data.sent) {
|
||||
// Message sent to server, not pending anymore.
|
||||
|
@ -642,8 +640,8 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
}).catch((error) => {
|
||||
this.messagesBeingSent--;
|
||||
|
||||
// Only close the keyboard if an error happens, we want the user to be able to send multiple
|
||||
// messages without the keyboard being closed.
|
||||
// Only close the keyboard if an error happens.
|
||||
// We want the user to be able to send multiple messages without the keyboard being closed.
|
||||
this.appProvider.closeKeyboard();
|
||||
|
||||
this.domUtils.showErrorModalDefault(error, 'addon.messages.messagenotsent', true);
|
||||
|
|
|
@ -17,8 +17,6 @@ import { IonicPageModule } from 'ionic-angular';
|
|||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { AddonMessagesIndexPage } from './index';
|
||||
import { CoreComponentsModule } from '@components/components.module';
|
||||
import { CoreDirectivesModule } from '@directives';
|
||||
import { CorePipesModule } from '@pipes';
|
||||
import { AddonMessagesComponentsModule } from '../../components/components.module';
|
||||
|
||||
@NgModule({
|
||||
|
@ -27,8 +25,6 @@ import { AddonMessagesComponentsModule } from '../../components/components.modul
|
|||
],
|
||||
imports: [
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
CorePipesModule,
|
||||
AddonMessagesComponentsModule,
|
||||
IonicPageModule.forChild(AddonMessagesIndexPage),
|
||||
TranslateModule.forChild()
|
||||
|
|
|
@ -48,6 +48,7 @@ export class AddonMessagesSettingsPage implements OnDestroy {
|
|||
|
||||
/**
|
||||
* Fetches preference data.
|
||||
*
|
||||
* @return {Promise<any>} Resolved when done.
|
||||
*/
|
||||
protected fetchPreferences(): Promise<any> {
|
||||
|
@ -85,6 +86,7 @@ export class AddonMessagesSettingsPage implements OnDestroy {
|
|||
|
||||
/**
|
||||
* Block non contacts.
|
||||
*
|
||||
* @param {boolean} block If it should be blocked or not.
|
||||
*/
|
||||
blockNonContacts(block: boolean): void {
|
||||
|
@ -103,6 +105,7 @@ export class AddonMessagesSettingsPage implements OnDestroy {
|
|||
|
||||
/**
|
||||
* Change the value of a certain preference.
|
||||
*
|
||||
* @param {any} notification Notification object.
|
||||
* @param {string} state State name, ['loggedin', 'loggedoff'].
|
||||
* @param {any} processor Notification processor.
|
||||
|
|
|
@ -56,7 +56,7 @@ export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCr
|
|||
});
|
||||
|
||||
// If a message push notification is received, refresh the count.
|
||||
pushNotificationsDelegate.registerReceiveHandler('AddonMessagesMainMenuHandler', (notification) => {
|
||||
pushNotificationsDelegate.on('receive').subscribe((notification) => {
|
||||
// New message received. If it's from current site, refresh the data.
|
||||
if (utils.isFalseOrZero(notification.notif) && this.sitesProvider.isCurrentSite(notification.site)) {
|
||||
this.updateBadge(notification.site);
|
||||
|
@ -64,7 +64,7 @@ export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCr
|
|||
});
|
||||
|
||||
// Register Badge counter.
|
||||
pushNotificationsDelegate.registerCounterHandler('mmaMessages');
|
||||
pushNotificationsDelegate.registerCounterHandler('AddonMessages');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -91,7 +91,7 @@ export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCr
|
|||
title: 'addon.messages.messages',
|
||||
page: 'AddonMessagesIndexPage',
|
||||
class: 'addon-messages-handler',
|
||||
showBadge: true, // Do not check isMessageCountEnabled because we'll use fallback it not enabled.,
|
||||
showBadge: true, // Do not check isMessageCountEnabled because we'll use fallback it not enabled.
|
||||
badge: this.badge,
|
||||
loading: this.loading
|
||||
};
|
||||
|
@ -112,7 +112,7 @@ export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCr
|
|||
// Leave badge enter if there is a 0+ or a 0.
|
||||
this.badge = parseInt(unread, 10) > 0 ? unread : '';
|
||||
// Update badge.
|
||||
this.pushNotificationsProvider.updateAddonCounter('mmaMessages', unread, siteId);
|
||||
this.pushNotificationsProvider.updateAddonCounter('AddonMessages', unread, siteId);
|
||||
}).catch(() => {
|
||||
this.badge = '';
|
||||
}).finally(() => {
|
||||
|
|
|
@ -26,7 +26,7 @@ export class AddonMessagesOfflineProvider {
|
|||
protected logger;
|
||||
|
||||
// Variables for database.
|
||||
protected MESSAGES_TABLE = 'mma_messages_offline_messages';
|
||||
protected MESSAGES_TABLE = 'addon_messages_offline_messages';
|
||||
protected tablesSchema = [
|
||||
{
|
||||
name: this.MESSAGES_TABLE,
|
||||
|
@ -140,7 +140,7 @@ export class AddonMessagesOfflineProvider {
|
|||
*/
|
||||
saveMessage(toUserId: number, message: string, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const entry = {
|
||||
const entry = {
|
||||
touserid: toUserId,
|
||||
useridfrom: site.getUserId(),
|
||||
smallmessage: message,
|
||||
|
|
|
@ -29,10 +29,10 @@ export class AddonMessagesProvider {
|
|||
protected ROOT_CACHE_KEY = 'mmaMessages:';
|
||||
protected LIMIT_MESSAGES = 50;
|
||||
protected LIMIT_SEARCH_MESSAGES = 50;
|
||||
static NEW_MESSAGE_EVENT = 'new_message_event';
|
||||
static READ_CHANGED_EVENT = 'read_changed_event';
|
||||
static READ_CRON_EVENT = 'read_cron_event';
|
||||
static SPLIT_VIEW_LOAD_EVENT = 'split_view_load_event';
|
||||
static NEW_MESSAGE_EVENT = 'addon_messages_new_message_event';
|
||||
static READ_CHANGED_EVENT = 'addon_messages_read_changed_event';
|
||||
static READ_CRON_EVENT = 'addon_messages_read_cron_event';
|
||||
static SPLIT_VIEW_LOAD_EVENT = 'addon_messages_split_view_load_event';
|
||||
static POLL_INTERVAL = 10000;
|
||||
static PUSH_SIMULATION_COMPONENT = 'AddonMessagesPushSimulation';
|
||||
|
||||
|
@ -148,6 +148,16 @@ export class AddonMessagesProvider {
|
|||
return this.ROOT_CACHE_KEY + 'discussion:' + userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cache key for the message count.
|
||||
*
|
||||
* @param {number} userId User ID.
|
||||
* @return {string} Cache key.
|
||||
*/
|
||||
protected getCacheKeyForMessageCount(userId: number): string {
|
||||
return this.ROOT_CACHE_KEY + 'count:' + userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cache key for the list of discussions.
|
||||
*
|
||||
|
@ -267,8 +277,8 @@ export class AddonMessagesProvider {
|
|||
hasSent;
|
||||
|
||||
if (lfReceivedUnread > 0 || lfReceivedRead > 0 || lfSentUnread > 0 || lfSentRead > 0) {
|
||||
// Do not use cache when retrieving older messages. This is to prevent storing too much data
|
||||
// and to prevent inconsistencies between "pages" loaded.
|
||||
// Do not use cache when retrieving older messages.
|
||||
// This is to prevent storing too much data and to prevent inconsistencies between "pages" loaded.
|
||||
preSets['getFromCache'] = false;
|
||||
preSets['saveToCache'] = false;
|
||||
preSets['emergencyCache'] = false;
|
||||
|
@ -566,9 +576,10 @@ export class AddonMessagesProvider {
|
|||
useridto: userId
|
||||
},
|
||||
preSets = {
|
||||
cacheKey: this.getCacheKeyForMessageCount(userId),
|
||||
getFromCache: false,
|
||||
emergencyCache: false,
|
||||
saveToCache: false,
|
||||
emergencyCache: true,
|
||||
saveToCache: true,
|
||||
typeExpected: 'number'
|
||||
};
|
||||
|
||||
|
@ -590,11 +601,11 @@ export class AddonMessagesProvider {
|
|||
return this.getMessages(params, undefined, false, siteId).then((response) => {
|
||||
// Count the discussions by filtering same senders.
|
||||
const discussions = {};
|
||||
let count;
|
||||
|
||||
response.messages.forEach((message) => {
|
||||
discussions[message.useridto] = 1;
|
||||
});
|
||||
count = Object.keys(discussions).length;
|
||||
const count = Object.keys(discussions).length;
|
||||
|
||||
// Add + sign if there are more than the limit reachable.
|
||||
return (count > this.LIMIT_MESSAGES) ? count + '+' : count;
|
||||
|
@ -700,9 +711,11 @@ export class AddonMessagesProvider {
|
|||
*/
|
||||
invalidateDiscussionsCache(siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.invalidateWsCacheForKey(this.getCacheKeyForDiscussions()).then(() => {
|
||||
return this.invalidateContactsCache(site.getId());
|
||||
});
|
||||
const promises = [];
|
||||
promises.push(site.invalidateWsCacheForKey(this.getCacheKeyForDiscussions()));
|
||||
promises.push(this.invalidateContactsCache(site.getId()));
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -797,12 +810,10 @@ export class AddonMessagesProvider {
|
|||
* @return {Promise<any>} Resolved when enabled, otherwise rejected.
|
||||
*/
|
||||
isMessagingEnabledForSite(siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
if (!site.canUseAdvancedFeature('messaging')) {
|
||||
return this.isPluginEnabled(siteId).then((enabled) => {
|
||||
if (!enabled) {
|
||||
return Promise.reject(null);
|
||||
}
|
||||
|
||||
return Promise.resolve(true);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -986,14 +997,14 @@ export class AddonMessagesProvider {
|
|||
// Online and no messages stored. Send it to server.
|
||||
return this.sendMessageOnline(toUserId, message).then(() => {
|
||||
return { sent: true };
|
||||
}).catch((data) => {
|
||||
if (data.wserror) {
|
||||
}).catch((error) => {
|
||||
if (this.utils.isWebServiceError(error)) {
|
||||
// It's a WebService error, the user cannot send the message so don't store it.
|
||||
return Promise.reject(data.error);
|
||||
} else {
|
||||
// Error sending message, store it to retry later.
|
||||
return storeOffline();
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
// Error sending message, store it to retry later.
|
||||
return storeOffline();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -1004,9 +1015,7 @@ export class AddonMessagesProvider {
|
|||
* @param {number} toUserId User ID to send the message to.
|
||||
* @param {string} message The message to send
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved if success, rejected if failure. Reject param is an object with:
|
||||
* - error: The error message.
|
||||
* - wserror: True if it's an error returned by the WebService, false otherwise.
|
||||
* @return {Promise<any>} Promise resolved if success, rejected if failure.
|
||||
*/
|
||||
sendMessageOnline(toUserId: number, message: string, siteId?: string): Promise<any> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
@ -1019,18 +1028,10 @@ export class AddonMessagesProvider {
|
|||
}
|
||||
];
|
||||
|
||||
return this.sendMessagesOnline(messages, siteId).catch((error) => {
|
||||
return Promise.reject({
|
||||
error: error,
|
||||
wserror: this.utils.isWebServiceError(error)
|
||||
});
|
||||
}).then((response) => {
|
||||
return this.sendMessagesOnline(messages, siteId).then((response) => {
|
||||
if (response && response[0] && response[0].msgid === -1) {
|
||||
// There was an error, and it should be translated already.
|
||||
return Promise.reject({
|
||||
error: response[0].errormessage,
|
||||
wserror: true
|
||||
});
|
||||
return this.utils.createFakeWSError(response[0].errormessage);
|
||||
}
|
||||
|
||||
return this.invalidateDiscussionCache(toUserId, siteId).catch(() => {
|
||||
|
|
|
@ -31,7 +31,7 @@ export class AddonMessagesSettingsHandler implements CoreSettingsHandler {
|
|||
/**
|
||||
* Check if the handler is enabled on a site level.
|
||||
*
|
||||
* @return {boolean} Whether or not the handler is enabled on a site level.
|
||||
* @return {boolean | Promise<boolean>} Whether or not the handler is enabled on a site level.
|
||||
*/
|
||||
isEnabled(): boolean | Promise<boolean> {
|
||||
return this.messagesProvider.isMessagePreferencesEnabled();
|
||||
|
|
|
@ -21,7 +21,9 @@ import { AddonMessagesOfflineProvider } from './messages-offline';
|
|||
import { AddonMessagesProvider } from './messages';
|
||||
import { CoreUserProvider } from '@core/user/providers/user';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreSyncProvider } from '@providers/sync';
|
||||
|
||||
/**
|
||||
* Service to sync messages.
|
||||
|
@ -34,8 +36,9 @@ export class AddonMessagesSyncProvider extends CoreSyncBaseProvider {
|
|||
constructor(protected sitesProvider: CoreSitesProvider, protected loggerProvider: CoreLoggerProvider,
|
||||
protected appProvider: CoreAppProvider, private messagesOffline: AddonMessagesOfflineProvider,
|
||||
private eventsProvider: CoreEventsProvider, private messagesProvider: AddonMessagesProvider,
|
||||
private userProvider: CoreUserProvider, private translate: TranslateService) {
|
||||
super('AddonMessagesSync', sitesProvider, loggerProvider, appProvider);
|
||||
private userProvider: CoreUserProvider, private translate: TranslateService, private utils: CoreUtilsProvider,
|
||||
syncProvider: CoreSyncProvider) {
|
||||
super('AddonMessagesSync', sitesProvider, loggerProvider, appProvider, syncProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,7 +52,7 @@ export class AddonMessagesSyncProvider extends CoreSyncBaseProvider {
|
|||
syncAllDiscussions(siteId?: string, onlyDeviceOffline: boolean = false): Promise<any> {
|
||||
const syncFunctionLog = 'all discussions' + (onlyDeviceOffline ? ' (Only offline)' : '');
|
||||
|
||||
return this.syncOnSites(syncFunctionLog, 'syncAllDiscussionsFunc', {onlyDeviceOffline: onlyDeviceOffline}, siteId);
|
||||
return this.syncOnSites(syncFunctionLog, this.syncAllDiscussionsFunc.bind(this), [onlyDeviceOffline], siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -106,13 +109,12 @@ export class AddonMessagesSyncProvider extends CoreSyncBaseProvider {
|
|||
return this.getOngoingSync(userId, siteId);
|
||||
}
|
||||
|
||||
let syncPromise;
|
||||
const warnings = [];
|
||||
|
||||
this.logger.debug(`Try to sync discussion with user '${userId}'`);
|
||||
|
||||
// Get offline messages to be sent.
|
||||
syncPromise = this.messagesOffline.getMessages(userId, siteId).then((messages) => {
|
||||
const syncPromise = this.messagesOffline.getMessages(userId, siteId).then((messages) => {
|
||||
if (!messages.length) {
|
||||
// Nothing to sync.
|
||||
return [];
|
||||
|
@ -123,33 +125,35 @@ export class AddonMessagesSyncProvider extends CoreSyncBaseProvider {
|
|||
return Promise.reject(null);
|
||||
}
|
||||
|
||||
let promise: Promise<any>;
|
||||
promise = Promise.resolve();
|
||||
let promise: Promise<any> = Promise.resolve();
|
||||
const errors = [];
|
||||
|
||||
// Order message by timecreated.
|
||||
messages = this.messagesProvider.sortMessages(messages);
|
||||
|
||||
// Send the messages. We don't use $mmaMessages#sendMessagesOnline because there's a problem with display order.
|
||||
// @todo Use $mmaMessages#sendMessagesOnline once the display order is fixed.
|
||||
// Send the messages.
|
||||
// We don't use AddonMessagesProvider#sendMessagesOnline because there's a problem with display order.
|
||||
// @todo Use AddonMessagesProvider#sendMessagesOnline once the display order is fixed.
|
||||
messages.forEach((message, index) => {
|
||||
// Chain message sending. If 1 message fails to be sent we'll stop sending.
|
||||
promise = promise.then(() => {
|
||||
return this.messagesProvider.sendMessageOnline(userId, message.smallmessage, siteId).catch((data) => {
|
||||
if (data.wserror) {
|
||||
return this.messagesProvider.sendMessageOnline(userId, message.smallmessage, siteId).catch((error) => {
|
||||
if (this.utils.isWebServiceError(error)) {
|
||||
// Error returned by WS. Store the error to show a warning but keep sending messages.
|
||||
if (errors.indexOf(data.error) == -1) {
|
||||
errors.push(data.error);
|
||||
}
|
||||
} else {
|
||||
// Error sending, stop execution.
|
||||
if (this.appProvider.isOnline()) {
|
||||
// App is online, unmark deviceoffline if marked.
|
||||
this.messagesOffline.setMessagesDeviceOffline(messages, false);
|
||||
if (errors.indexOf(error) == -1) {
|
||||
errors.push(error);
|
||||
}
|
||||
|
||||
return Promise.reject(data.error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Error sending, stop execution.
|
||||
if (this.appProvider.isOnline()) {
|
||||
// App is online, unmark deviceoffline if marked.
|
||||
this.messagesOffline.setMessagesDeviceOffline(messages, false);
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
}).then(() => {
|
||||
// Message was sent, delete it from local DB.
|
||||
return this.messagesOffline.deleteMessage(userId, message.smallmessage, message.timecreated, siteId);
|
||||
|
|
|
@ -33,8 +33,8 @@ export class AddonMessagesAddContactUserHandler implements CoreUserProfileHandle
|
|||
*/
|
||||
static UPDATED_EVENT = 'AddonMessagesAddContactUserHandler_updated_event';
|
||||
|
||||
name = 'mmaMessages:blockContact';
|
||||
priority = 600;
|
||||
name = 'AddonMessages:addContact';
|
||||
priority = 800;
|
||||
type = CoreUserDelegate.TYPE_ACTION;
|
||||
|
||||
protected disabled = false;
|
||||
|
@ -52,9 +52,9 @@ export class AddonMessagesAddContactUserHandler implements CoreUserProfileHandle
|
|||
/**
|
||||
* Check if handler is enabled.
|
||||
*
|
||||
* @return {Promise<any>} Promise resolved with true if enabled, rejected or resolved with false otherwise.
|
||||
* @return {Promise<boolean>} Promise resolved with true if enabled, rejected or resolved with false otherwise.
|
||||
*/
|
||||
isEnabled(): Promise<any> {
|
||||
isEnabled(): Promise<boolean> {
|
||||
return this.messagesProvider.isPluginEnabled();
|
||||
}
|
||||
|
||||
|
@ -101,14 +101,14 @@ export class AddonMessagesAddContactUserHandler implements CoreUserProfileHandle
|
|||
|
||||
return this.domUtils.showConfirm(template, title, title).then(() => {
|
||||
return this.messagesProvider.removeContact(user.id);
|
||||
}).catch(() => {
|
||||
}, () => {
|
||||
// Ignore on cancel.
|
||||
});
|
||||
} else {
|
||||
return this.messagesProvider.addContact(user.id);
|
||||
}
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModal(error);
|
||||
this.domUtils.showErrorModalDefault(error, 'core.error', true);
|
||||
}).finally(() => {
|
||||
this.eventsProvider.trigger(AddonMessagesAddContactUserHandler.UPDATED_EVENT, {userId: user.id});
|
||||
this.checkButton(user.id).finally(() => {
|
||||
|
@ -132,7 +132,7 @@ export class AddonMessagesAddContactUserHandler implements CoreUserProfileHandle
|
|||
if (isContact) {
|
||||
this.updateButton({
|
||||
title: 'addon.messages.removecontact',
|
||||
class: 'mma-messages-removecontact-handler',
|
||||
class: 'addon-messages-removecontact-handler',
|
||||
icon: 'remove',
|
||||
hidden: false,
|
||||
spinner: false
|
||||
|
@ -140,7 +140,7 @@ export class AddonMessagesAddContactUserHandler implements CoreUserProfileHandle
|
|||
} else {
|
||||
this.updateButton({
|
||||
title: 'addon.messages.addcontact',
|
||||
class: 'mma-messages-addcontact-handler',
|
||||
class: 'addon-messages-addcontact-handler',
|
||||
icon: 'add',
|
||||
hidden: false,
|
||||
spinner: false
|
||||
|
|
|
@ -33,8 +33,8 @@ export class AddonMessagesBlockContactUserHandler implements CoreUserProfileHand
|
|||
*/
|
||||
static UPDATED_EVENT = 'AddonMessagesBlockContactUserHandler_updated_event';
|
||||
|
||||
name = 'mmaMessages:addContact';
|
||||
priority = 800;
|
||||
name = 'AddonMessages:blockContact';
|
||||
priority = 600;
|
||||
type = CoreUserDelegate.TYPE_ACTION;
|
||||
|
||||
protected disabled = false;
|
||||
|
@ -52,9 +52,9 @@ export class AddonMessagesBlockContactUserHandler implements CoreUserProfileHand
|
|||
/**
|
||||
* Check if handler is enabled.
|
||||
*
|
||||
* @return {Promise<any>} Promise resolved with true if enabled, rejected or resolved with false otherwise.
|
||||
* @return {Promise<boolean>} Promise resolved with true if enabled, rejected or resolved with false otherwise.
|
||||
*/
|
||||
isEnabled(): Promise<any> {
|
||||
isEnabled(): Promise<boolean> {
|
||||
return this.messagesProvider.isPluginEnabled();
|
||||
}
|
||||
|
||||
|
@ -104,12 +104,12 @@ export class AddonMessagesBlockContactUserHandler implements CoreUserProfileHand
|
|||
|
||||
return this.domUtils.showConfirm(template, title, title).then(() => {
|
||||
return this.messagesProvider.blockContact(user.id);
|
||||
}).catch(() => {
|
||||
}, () => {
|
||||
// Ignore on cancel.
|
||||
});
|
||||
}
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModal(error);
|
||||
this.domUtils.showErrorModalDefault(error, 'core.error', true);
|
||||
}).finally(() => {
|
||||
this.eventsProvider.trigger(AddonMessagesBlockContactUserHandler.UPDATED_EVENT, {userId: user.id});
|
||||
this.checkButton(user.id).finally(() => {
|
||||
|
@ -133,7 +133,7 @@ export class AddonMessagesBlockContactUserHandler implements CoreUserProfileHand
|
|||
if (isBlocked) {
|
||||
this.updateButton({
|
||||
title: 'addon.messages.unblockcontact',
|
||||
class: 'mma-messages-unblockcontact-handler',
|
||||
class: 'addon-messages-unblockcontact-handler',
|
||||
icon: 'checkmark-circle',
|
||||
hidden: false,
|
||||
spinner: false
|
||||
|
@ -141,7 +141,7 @@ export class AddonMessagesBlockContactUserHandler implements CoreUserProfileHand
|
|||
} else {
|
||||
this.updateButton({
|
||||
title: 'addon.messages.blockcontact',
|
||||
class: 'mma-messages-blockcontact-handler',
|
||||
class: 'addon-messages-blockcontact-handler',
|
||||
icon: 'close-circle',
|
||||
hidden: false,
|
||||
spinner: false
|
||||
|
|
|
@ -23,7 +23,7 @@ import { AddonMessagesProvider } from './messages';
|
|||
*/
|
||||
@Injectable()
|
||||
export class AddonMessagesSendMessageUserHandler implements CoreUserProfileHandler {
|
||||
name = 'mmaMessages:sendMessage';
|
||||
name = 'AddonMessages:sendMessage';
|
||||
priority = 1000;
|
||||
type = CoreUserDelegate.TYPE_COMMUNICATION;
|
||||
|
||||
|
|
|
@ -14,20 +14,22 @@
|
|||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CoreLoggerProvider } from '@providers/logger';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
/**
|
||||
* Service to handle push notifications clicks.
|
||||
* Service to handle push notifications actions to perform when clicked and received.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonPushNotificationsDelegate {
|
||||
|
||||
protected logger;
|
||||
protected clickHandlers: { [s: string]: Function } = {};
|
||||
protected receiveHandlers: { [s: string]: Function } = {};
|
||||
protected observables: { [s: string]: Subject<any> } = {};
|
||||
protected counterHandlers: { [s: string]: string } = {};
|
||||
|
||||
constructor(loggerProvider: CoreLoggerProvider) {
|
||||
this.logger = loggerProvider.getInstance('AddonPushNotificationsDelegate');
|
||||
this.observables['click'] = new Subject<any>();
|
||||
this.observables['receive'] = new Subject<any>();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -36,15 +38,7 @@ export class AddonPushNotificationsDelegate {
|
|||
* @param {any} notification Notification clicked.
|
||||
*/
|
||||
clicked(notification: any): void {
|
||||
for (const name in this.clickHandlers) {
|
||||
const callback = this.clickHandlers[name];
|
||||
if (typeof callback == 'function') {
|
||||
const treated = callback(notification);
|
||||
if (treated) {
|
||||
return; // Stop execution when notification is treated.
|
||||
}
|
||||
}
|
||||
}
|
||||
this.observables['click'].next(notification);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,60 +48,42 @@ export class AddonPushNotificationsDelegate {
|
|||
* @param {any} notification Notification received.
|
||||
*/
|
||||
received(notification: any): void {
|
||||
for (const name in this.receiveHandlers) {
|
||||
const callback = this.receiveHandlers[name];
|
||||
if (typeof callback == 'function') {
|
||||
callback(notification);
|
||||
}
|
||||
this.observables['receive'].next(notification);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a push notifications observable for click and receive notification event.
|
||||
* When a notification is clicked or received, the observable will receive a notification to treat.
|
||||
* let observer = pushNotificationsDelegate.on('click').subscribe((notification) => {
|
||||
* ...
|
||||
* observer.unsuscribe();
|
||||
*
|
||||
* @param {string} eventName Only click and receive are permitted.
|
||||
* @return {Subject<any>} Observer to subscribe.
|
||||
*/
|
||||
on(eventName: string): Subject<any> {
|
||||
if (typeof this.observables[eventName] == 'undefined') {
|
||||
const eventNames = Object.keys(this.observables).join(', ');
|
||||
this.logger.warn(`'${eventName}' event name is not allowed. Use one of the following: '${eventNames}'.`);
|
||||
|
||||
return new Subject<any>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a push notifications handler for CLICKS.
|
||||
* When a notification is clicked, the handler will receive a notification to treat.
|
||||
*
|
||||
* @param {string} name Handler's name.
|
||||
* @param {Function} callback The callback function. Will get as parameter the clicked notification.
|
||||
* @description
|
||||
* The handler should return true if the notification is the one expected, false otherwise.
|
||||
* @see {@link AddonPushNotificationsDelegate#clicked}
|
||||
*/
|
||||
registerHandler(name: string, callback: Function): void {
|
||||
this.logger.debug(`Registered handler '${name}' as CLICK push notification handler.`);
|
||||
this.clickHandlers[name] = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a push notifications handler for RECEIVE notifications in foreground (cannot tell when it's received in background).
|
||||
* When a notification is received, the handler will receive a notification to treat.
|
||||
*
|
||||
* @param {string} name Handler's name.
|
||||
* @param {Function} callback The callback function. Will get as parameter the clicked notification.
|
||||
* @see {@link AddonPushNotificationsDelegate#received}
|
||||
*/
|
||||
registerReceiveHandler(name: string, callback: Function): void {
|
||||
this.logger.debug(`Registered handler '${name}' as RECEIVE push notification handler.`);
|
||||
this.receiveHandlers[name] = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a push notifications handler for RECEIVE notifications.
|
||||
*
|
||||
* @param {string} name Handler's name.
|
||||
*/
|
||||
unregisterReceiveHandler(name: string): void {
|
||||
this.logger.debug(`Unregister handler '${name}' from RECEIVE push notification handlers.`);
|
||||
delete this.receiveHandlers[name];
|
||||
return this.observables[eventName];
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a push notifications handler for update badge counter.
|
||||
*
|
||||
* @param {string} name Handler's name.
|
||||
* @param {string} name Handler's name.
|
||||
*/
|
||||
registerCounterHandler(name: string): void {
|
||||
this.logger.debug(`Registered handler '${name}' as badge counter handler.`);
|
||||
this.counterHandlers[name] = name;
|
||||
if (typeof this.counterHandlers[name] == 'undefined') {
|
||||
this.logger.debug(`Registered handler '${name}' as badge counter handler.`);
|
||||
this.counterHandlers[name] = name;
|
||||
} else {
|
||||
this.logger.log(`Handler '${name}' as badge counter handler already registered.`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -36,17 +36,17 @@ export class AddonPushNotificationsProvider {
|
|||
protected logger;
|
||||
protected pushID: string;
|
||||
protected appDB: any;
|
||||
static COMPONENT = 'mmaPushNotifications';
|
||||
static COMPONENT = 'AddonPushNotificationsProvider';
|
||||
|
||||
// Variables for database.
|
||||
protected BADGE_TABLE = 'mma_pushnotifications_badge';
|
||||
protected BADGE_TABLE = 'addon_pushnotifications_badge';
|
||||
protected tablesSchema = [
|
||||
{
|
||||
name: this.BADGE_TABLE,
|
||||
columns: [
|
||||
{
|
||||
name: 'siteid',
|
||||
type: 'INTEGER'
|
||||
type: 'TEXT'
|
||||
},
|
||||
{
|
||||
name: 'addon',
|
||||
|
@ -84,25 +84,26 @@ export class AddonPushNotificationsProvider {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns options for push notifications based on
|
||||
* @return {Promise<PushOptions>} [description]
|
||||
* Returns options for push notifications based on device.
|
||||
*
|
||||
* @return {Promise<PushOptions>} Promise with the push options resolved when done.
|
||||
*/
|
||||
protected getOptions(): Promise<PushOptions> {
|
||||
// @todo: CoreSettingsProvider.NOTIFICATION_SOUND
|
||||
return this.configProvider.get('CoreSettingsProvider.NOTIFICATION_SOUND', true).then((soundEnabled) => {
|
||||
return {
|
||||
android: {
|
||||
senderID: CoreConfigConstants.gcmpn,
|
||||
sound: !!soundEnabled
|
||||
},
|
||||
ios: {
|
||||
alert: 'true',
|
||||
badge: true,
|
||||
sound: !!soundEnabled
|
||||
},
|
||||
windows: {
|
||||
sound: !!soundEnabled
|
||||
}
|
||||
android: {
|
||||
senderID: CoreConfigConstants.gcmpn,
|
||||
sound: !!soundEnabled
|
||||
},
|
||||
ios: {
|
||||
alert: 'true',
|
||||
badge: true,
|
||||
sound: !!soundEnabled
|
||||
},
|
||||
windows: {
|
||||
sound: !!soundEnabled
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@ -259,9 +260,9 @@ export class AddonPushNotificationsProvider {
|
|||
|
||||
return Promise.all(promises).then((counters) => {
|
||||
const total = counters.reduce((previous, counter) => {
|
||||
// The app badge counter does not support strings, so parse to int before.
|
||||
return previous + parseInt(counter, 10);
|
||||
}, 0);
|
||||
// The app badge counter does not support strings, so parse to int before.
|
||||
return previous + parseInt(counter, 10);
|
||||
}, 0);
|
||||
|
||||
// Set the app badge.
|
||||
return this.badge.set(total).then(() => {
|
||||
|
|
|
@ -44,7 +44,7 @@ export class CoreSyncBaseProvider {
|
|||
protected syncPromises: { [siteId: string]: { [uniqueId: string]: Promise<any> } } = {};
|
||||
|
||||
constructor(component: string, protected sitesProvider: CoreSitesProvider, protected loggerProvider: CoreLoggerProvider,
|
||||
protected appProvider: CoreAppProvider) {
|
||||
protected appProvider: CoreAppProvider, protected syncProvider: CoreSyncProvider) {
|
||||
this.logger = this.loggerProvider.getInstance(component);
|
||||
this.component = component;
|
||||
}
|
||||
|
@ -52,12 +52,12 @@ export class CoreSyncBaseProvider {
|
|||
/**
|
||||
* Add an ongoing sync to the syncPromises list. On finish the promise will be removed.
|
||||
*
|
||||
* @param {number} id Unique sync identifier per component.
|
||||
* @param {string | number} id Unique sync identifier per component.
|
||||
* @param {Promise<any>} promise The promise of the sync to add.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} The sync promise.
|
||||
*/
|
||||
addOngoingSync(id: number, promise: Promise<any>, siteId?: string): Promise<any> {
|
||||
addOngoingSync(id: string | number, promise: Promise<any>, siteId?: string): Promise<any> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
const uniqueId = this.getUniqueSyncId(id);
|
||||
|
@ -76,11 +76,11 @@ export class CoreSyncBaseProvider {
|
|||
/**
|
||||
* If there's an ongoing sync for a certain identifier return it.
|
||||
*
|
||||
* @param {number} id Unique sync identifier per component.
|
||||
* @param {string | number} id Unique sync identifier per component.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise of the current sync or undefined if there isn't any.
|
||||
*/
|
||||
getOngoingSync(id: number, siteId?: string): Promise<any> {
|
||||
getOngoingSync(id: string | number, siteId?: string): Promise<any> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
if (this.isSyncing(id, siteId)) {
|
||||
|
@ -94,59 +94,55 @@ export class CoreSyncBaseProvider {
|
|||
/**
|
||||
* Get the synchronization time. Returns 0 if no time stored.
|
||||
*
|
||||
* @param {number} id Unique sync identifier per component.
|
||||
* @param {string | number} id Unique sync identifier per component.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<number>} Promise resolved with the time.
|
||||
*/
|
||||
getSyncTime(id: number, siteId?: string): Promise<number> {
|
||||
return this.sitesProvider.getSiteDb(siteId).then((db) => {
|
||||
return db.getRecord(CoreSyncProvider.SYNC_TABLE, { component: this.component, id: id }).then((entry) => {
|
||||
return entry.time;
|
||||
}).catch(() => {
|
||||
return 0;
|
||||
});
|
||||
getSyncTime(id: string | number, siteId?: string): Promise<number> {
|
||||
return this.syncProvider.getSyncRecord(this.component, id, siteId).then((entry) => {
|
||||
return entry.time;
|
||||
}).catch(() => {
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the synchronization warnings of an instance.
|
||||
*
|
||||
* @param {number} id Unique sync identifier per component.
|
||||
* @param {string | number} id Unique sync identifier per component.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<string[]>} Promise resolved with the warnings.
|
||||
*/
|
||||
getSyncWarnings(id: number, siteId?: string): Promise<string[]> {
|
||||
return this.sitesProvider.getSiteDb(siteId).then((db) => {
|
||||
return db.getRecord(CoreSyncProvider.SYNC_TABLE, { component: this.component, id: id }).then((entry) => {
|
||||
try {
|
||||
return JSON.parse(entry.warnings);
|
||||
} catch (ex) {
|
||||
return [];
|
||||
}
|
||||
}).catch(() => {
|
||||
getSyncWarnings(id: string | number, siteId?: string): Promise<string[]> {
|
||||
return this.syncProvider.getSyncRecord(this.component, id, siteId).then((entry) => {
|
||||
try {
|
||||
return JSON.parse(entry.warnings);
|
||||
} catch (ex) {
|
||||
return [];
|
||||
});
|
||||
}
|
||||
}).catch(() => {
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a unique identifier from component and id.
|
||||
*
|
||||
* @param {number} id Unique sync identifier per component.
|
||||
* @param {string | number} id Unique sync identifier per component.
|
||||
* @return {string} Unique identifier from component and id.
|
||||
*/
|
||||
protected getUniqueSyncId(id: number): string {
|
||||
protected getUniqueSyncId(id: string | number): string {
|
||||
return this.component + '#' + id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a there's an ongoing syncronization for the given id.
|
||||
*
|
||||
* @param {number} id Unique sync identifier per component.
|
||||
* @param {string | number} id Unique sync identifier per component.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {boolean} Whether it's synchronizing.
|
||||
*/
|
||||
isSyncing(id: number, siteId?: string): boolean {
|
||||
isSyncing(id: string | number, siteId?: string): boolean {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
const uniqueId = this.getUniqueSyncId(id);
|
||||
|
@ -157,11 +153,11 @@ export class CoreSyncBaseProvider {
|
|||
/**
|
||||
* Check if a sync is needed: if a certain time has passed since the last time.
|
||||
*
|
||||
* @param {number} id Unique sync identifier per component.
|
||||
* @param {string} id Unique sync identifier per component.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<boolean>} Promise resolved with boolean: whether sync is needed.
|
||||
*/
|
||||
isSyncNeeded(id: number, siteId?: string): Promise<boolean> {
|
||||
isSyncNeeded(id: string, siteId?: string): Promise<boolean> {
|
||||
return this.getSyncTime(id, siteId).then((time) => {
|
||||
return Date.now() - this.syncInterval >= time;
|
||||
});
|
||||
|
@ -170,59 +166,47 @@ export class CoreSyncBaseProvider {
|
|||
/**
|
||||
* Set the synchronization time.
|
||||
*
|
||||
* @param {number} id Unique sync identifier per component.
|
||||
* @param {string | number} id Unique sync identifier per component.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @param {number} [time] Time to set. If not defined, current time.
|
||||
* @return {Promise<any>} Promise resolved when the time is set.
|
||||
*/
|
||||
setSyncTime(id: number, siteId?: string, time?: number): Promise<any> {
|
||||
return this.sitesProvider.getSiteDb(siteId).then((db) => {
|
||||
time = typeof time != 'undefined' ? time : Date.now();
|
||||
setSyncTime(id: string | number, siteId?: string, time?: number): Promise<any> {
|
||||
time = typeof time != 'undefined' ? time : Date.now();
|
||||
|
||||
return db.insertOrUpdateRecord(CoreSyncProvider.SYNC_TABLE, { time: time }, { component: this.component, id: id });
|
||||
});
|
||||
return this.syncProvider.insertOrUpdateSyncRecord(this.component, id, { time: time }, siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the synchronization warnings.
|
||||
*
|
||||
* @param {number} id Unique sync identifier per component.
|
||||
* @param {string} id Unique sync identifier per component.
|
||||
* @param {string[]} warnings Warnings to set.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
setSyncWarnings(id: number, warnings: string[], siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSiteDb(siteId).then((db) => {
|
||||
warnings = warnings || [];
|
||||
setSyncWarnings(id: string, warnings: string[], siteId?: string): Promise<any> {
|
||||
const warningsText = JSON.stringify(warnings || []);
|
||||
|
||||
return db.insertOrUpdateRecord(CoreSyncProvider.SYNC_TABLE, { warnings: JSON.stringify(warnings) },
|
||||
{ component: this.component, id: id });
|
||||
});
|
||||
return this.syncProvider.insertOrUpdateSyncRecord(this.component, id, { warnings: warningsText }, siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a sync function on selected sites.
|
||||
*
|
||||
* @param {string} syncFunctionLog Log message to explain the sync function purpose.
|
||||
* @param {string} syncFunction Sync function to execute.
|
||||
* @param {any} [params] Object that defines the params that admit the funcion.
|
||||
* @param {Function} syncFunction Sync function to execute.
|
||||
* @param {any[]} [params] Array that defines the params that admit the funcion.
|
||||
* @param {string} [siteId] Site ID to sync. If not defined, sync all sites.
|
||||
* @return {Promise<any>} Resolved with siteIds selected. Rejected if offline.
|
||||
*/
|
||||
syncOnSites(syncFunctionLog: string, syncFunction: string, params?: any, siteId?: string): Promise<any> {
|
||||
syncOnSites(syncFunctionLog: string, syncFunction: Function, params?: any[], siteId?: string): Promise<any> {
|
||||
if (!this.appProvider.isOnline()) {
|
||||
this.logger.debug(`Cannot sync '${syncFunctionLog}' because device is offline.`);
|
||||
|
||||
return Promise.reject(null);
|
||||
}
|
||||
|
||||
if (!this[syncFunction]) {
|
||||
this.logger.debug(`Cannot sync '${syncFunctionLog}' function '${syncFunction}' does not exist.`);
|
||||
|
||||
return Promise.reject(null);
|
||||
}
|
||||
params = params || {};
|
||||
|
||||
let promise;
|
||||
if (!siteId) {
|
||||
// No site ID defined, sync all sites.
|
||||
|
@ -233,12 +217,13 @@ export class CoreSyncBaseProvider {
|
|||
promise = Promise.resolve([siteId]);
|
||||
}
|
||||
|
||||
params = params || [];
|
||||
|
||||
return promise.then((siteIds) => {
|
||||
const sitePromises = [];
|
||||
siteIds.forEach((siteId) => {
|
||||
params['siteId'] = siteId;
|
||||
// Execute function for every site selected.
|
||||
sitePromises.push(this[syncFunction].apply(this, params));
|
||||
sitePromises.push(syncFunction.apply(syncFunction, [siteId].concat(params)));
|
||||
});
|
||||
|
||||
return Promise.all(sitePromises);
|
||||
|
@ -249,11 +234,11 @@ export class CoreSyncBaseProvider {
|
|||
* If there's an ongoing sync for a certain identifier, wait for it to end.
|
||||
* If there's no sync ongoing the promise will be resolved right away.
|
||||
*
|
||||
* @param {number} id Unique sync identifier per component.
|
||||
* @param {string | number} id Unique sync identifier per component.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved when there's no sync going on for the identifier.
|
||||
*/
|
||||
waitForSync(id: number, siteId?: string): Promise<any> {
|
||||
waitForSync(id: string | number, siteId?: string): Promise<any> {
|
||||
const promise = this.getOngoingSync(id, siteId);
|
||||
if (promise) {
|
||||
return promise.catch(() => {
|
||||
|
@ -262,5 +247,5 @@ export class CoreSyncBaseProvider {
|
|||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -531,7 +531,7 @@ export class CoreSite {
|
|||
} else {
|
||||
this.logger.error(`WS function '${method}' is not available, even in compatibility mode.`);
|
||||
|
||||
return Promise.reject(this.wsProvider.createFakeWSError('core.wsfunctionnotavailable', true));
|
||||
return Promise.reject(this.utils.createFakeWSError('core.wsfunctionnotavailable', true));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -560,7 +560,7 @@ export class CoreSite {
|
|||
data = this.wsProvider.convertValuesToString(data, wsPreSets.cleanUnicode);
|
||||
} catch (e) {
|
||||
// Empty cleaned text found.
|
||||
return Promise.reject(this.wsProvider.createFakeWSError('core.unicodenotsupportedcleanerror', true));
|
||||
return Promise.reject(this.utils.createFakeWSError('core.unicodenotsupportedcleanerror', true));
|
||||
}
|
||||
|
||||
return this.getFromCache(method, data, preSets).catch(() => {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<ion-card>
|
||||
<form #f="ngForm" (ngSubmit)="submitForm()">
|
||||
<ion-item>
|
||||
<ion-input type="text" name="search" [(ngModel)]="searchText" [placeholder]="placeholder" [autocorrect]="autocorrect" [spellcheck]="spellcheck" [core-auto-focus]="autoFocus"></ion-input>
|
||||
<button item-end ion-button clear icon-only type="submit" class="button-small" [attr.aria-label]="searchLabel" [disabled]="!searchText || (searchText.length < lengthCheck)">
|
||||
<ion-input type="text" name="search" [(ngModel)]="searchText" [placeholder]="placeholder" [autocorrect]="autocorrect" [spellcheck]="spellcheck" [core-auto-focus]="autoFocus" [disabled]="disabled"></ion-input>
|
||||
<button item-end ion-button clear icon-only type="submit" class="button-small" [attr.aria-label]="searchLabel" [disabled]="!searchText || (searchText.length < lengthCheck)" [disabled]="disabled">
|
||||
<ion-icon name="search"></ion-icon>
|
||||
</button>
|
||||
<button *ngIf="showClear" item-end ion-button clear icon-only class="button-small" [attr.aria-label]="'core.clearsearch' | translate" [disabled]="!searched" (click)="clearForm()">
|
||||
<button *ngIf="showClear" item-end ion-button clear icon-only class="button-small" [attr.aria-label]="'core.clearsearch' | translate" [disabled]="!searched" (click)="clearForm()" [disabled]="disabled">
|
||||
<ion-icon name="close"></ion-icon>
|
||||
</button>
|
||||
</ion-item>
|
||||
|
|
|
@ -38,6 +38,7 @@ export class CoreSearchBoxComponent implements OnInit {
|
|||
@Input() autoFocus?: string | boolean; // Enables/disable Autofocus when entering view.
|
||||
@Input() lengthCheck? = 3; // Check value length before submit. If 0, any string will be submitted.
|
||||
@Input() showClear? = true; // Show/hide clear button.
|
||||
@Input() disabled? = false; // Disables the input text.
|
||||
@Output() onSubmit: EventEmitter<string>; // Send data when submitting the search form.
|
||||
@Output() onClear?: EventEmitter<void>; // Send event when clearing the search form.
|
||||
|
||||
|
|
|
@ -83,4 +83,8 @@ core-tabs {
|
|||
top: 0;
|
||||
height: 100%;
|
||||
}
|
||||
ion-content core-tabs core-tab .core-avoid-header ion-content {
|
||||
top: 0;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/* tslint:disable:no-console */
|
||||
|
||||
import { SQLiteDB } from '@classes/sqlitedb';
|
||||
|
||||
/**
|
||||
|
|
|
@ -91,7 +91,6 @@ export class CoreMainMenuPage implements OnDestroy {
|
|||
});
|
||||
if (tab) {
|
||||
tab.badge = data.badge;
|
||||
tab.loading = false;
|
||||
}
|
||||
}, site.getId());
|
||||
|
||||
|
|
|
@ -68,7 +68,6 @@ export class CoreMainMenuMorePage implements OnDestroy {
|
|||
});
|
||||
if (handler) {
|
||||
handler.badge = data.badge;
|
||||
handler.loading = false;
|
||||
}
|
||||
}, this.sitesProvider.getCurrentSiteId());
|
||||
}
|
||||
|
|
|
@ -6,32 +6,32 @@
|
|||
<core-split-view>
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<a ion-item (click)="openHandler('CoreSettingsGeneralPage')" [title]="'core.settings.general' | translate" [class.core-split-item-selected]="'CoreSettingsGeneralPage' == selectedPage">
|
||||
<ion-item (click)="openHandler('CoreSettingsGeneralPage')" [title]="'core.settings.general' | translate" [class.core-split-item-selected]="'CoreSettingsGeneralPage' == selectedPage" detail-push>
|
||||
<ion-icon name="construct" item-start></ion-icon>
|
||||
<p>{{ 'core.settings.general' | translate }}</p>
|
||||
</a>
|
||||
<a ion-item (click)="openHandler('CoreSettingsSpaceUsagePage')" [title]="'core.settings.spaceusage' | translate" [class.core-split-item-selected]="'CoreSettingsSpaceUsagePage' == selectedPage">
|
||||
</ion-item>
|
||||
<ion-item (click)="openHandler('CoreSettingsSpaceUsagePage')" [title]="'core.settings.spaceusage' | translate" [class.core-split-item-selected]="'CoreSettingsSpaceUsagePage' == selectedPage" detail-push>
|
||||
<ion-icon name="stats" item-start></ion-icon>
|
||||
<p>{{ 'core.settings.spaceusage' | translate }}</p>
|
||||
</a>
|
||||
<a ion-item (click)="openHandler('CoreSettingSynchronizationPage')" [title]="'core.settings.synchronization' | translate" [class.core-split-item-selected]="'CoreSettingSynchronizationPage' == selectedPage">
|
||||
</ion-item>
|
||||
<ion-item (click)="openHandler('CoreSettingSynchronizationPage')" [title]="'core.settings.synchronization' | translate" [class.core-split-item-selected]="'CoreSettingSynchronizationPage' == selectedPage" detail-push>
|
||||
<ion-icon name="sync" item-start></ion-icon>
|
||||
<p>{{ 'core.settings.synchronization' | translate }}</p>
|
||||
</a>
|
||||
<a ion-item *ngIf="isIOS" (click)="openHandler('CoreSharedFilesListPage', {manage: true})" [title]="'core.sharedfiles.sharedfiles' | translate" [class.core-split-item-selected]="'CoreSharedFilesListPage' == selectedPage">
|
||||
</ion-item>
|
||||
<ion-item *ngIf="isIOS" (click)="openHandler('CoreSharedFilesListPage', {manage: true})" [title]="'core.sharedfiles.sharedfiles' | translate" [class.core-split-item-selected]="'CoreSharedFilesListPage' == selectedPage" detail-push>
|
||||
<ion-icon name="folder" item-start></ion-icon>
|
||||
<p>{{ 'core.sharedfiles.sharedfiles' | translate }}</p>
|
||||
</a>
|
||||
</ion-item>
|
||||
|
||||
<ion-item *ngFor="let handler of handlers" [ngClass]="['core-settings-handler', handler.class]" (click)="openHandler(handler.page, handler.params)" [title]="handler.title | translate" detail-push [class.core-split-item-selected]="handler.page == selectedPage">
|
||||
<ion-icon [name]="handler.icon" item-start *ngIf="handler.icon"></ion-icon>
|
||||
<p>{{ handler.title | translate}}</p>
|
||||
</ion-item>
|
||||
|
||||
<a ion-item (click)="openHandler('CoreSettingsAboutPage')" [title]="'core.settings.about' | translate" [class.core-split-item-selected]="'CoreSettingsAboutPage' == selectedPage">
|
||||
<ion-item (click)="openHandler('CoreSettingsAboutPage')" [title]="'core.settings.about' | translate" [class.core-split-item-selected]="'CoreSettingsAboutPage' == selectedPage" detail-push>
|
||||
<ion-icon name="contacts" item-start></ion-icon>
|
||||
<p>{{ 'core.settings.about' | translate }}</p>
|
||||
</a>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
</core-split-view>
|
||||
|
|
|
@ -29,7 +29,6 @@ export class CoreSettingsListPage {
|
|||
@ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent;
|
||||
|
||||
handlers: CoreSettingsHandlerData[];
|
||||
areHandlersLoaded: Function;
|
||||
isIOS: boolean;
|
||||
selectedPage: string;
|
||||
|
||||
|
@ -61,7 +60,7 @@ export class CoreSettingsListPage {
|
|||
*/
|
||||
openHandler(page: string, params?: any): void {
|
||||
this.selectedPage = page;
|
||||
this.navCtrl.push(page, params);
|
||||
this.splitviewCtrl.push(page, params);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -52,9 +52,9 @@
|
|||
</a>
|
||||
|
||||
<ion-item *ngIf="actionHandlers && actionHandlers.length">
|
||||
<button *ngFor="let actHandler of actionHandlers" ion-button block outline [ngClass]="['core-user-profile-handler', actHandler.class]" (click)="handlerClicked($event, actHandler)" [hidden]="actHandler.hidden" title="{{ actHandler.title | translate }}" icon-start>
|
||||
<ion-icon *ngIf="!actHandler.spinner && actHandler.icon" [name]="actHandler.icon" start></ion-icon>
|
||||
<span *ngIf="!actHandler.spinner">{{ actHandler.title | translate }}</span>
|
||||
<button *ngFor="let actHandler of actionHandlers" ion-button block outline [ngClass]="['core-user-profile-handler', actHandler.class]" (click)="handlerClicked($event, actHandler)" [hidden]="actHandler.hidden" title="{{ actHandler.title | translate }}" icon-start [disabled]="actHandler.spinner">
|
||||
<ion-icon *ngIf="actHandler.icon" [name]="actHandler.icon" start></ion-icon>
|
||||
<span>{{ actHandler.title | translate }}</span>
|
||||
<ion-spinner *ngIf="actHandler.spinner"></ion-spinner>
|
||||
</button>
|
||||
</ion-item>
|
||||
|
|
|
@ -31,4 +31,10 @@ page-core-user-profile {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.core-user-profile-handler {
|
||||
ion-spinner {
|
||||
margin-left: 0.3em;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,11 +18,11 @@ import { CoreUserProvider } from '../../providers/user';
|
|||
import { CoreUserHelperProvider } from '../../providers/helper';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreCoursesProvider } from '../../../courses/providers/courses';
|
||||
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype';
|
||||
import { CoreFileUploaderHelperProvider } from '../../../fileuploader/providers/helper';
|
||||
import { CoreFileUploaderHelperProvider } from '@core/fileuploader/providers/helper';
|
||||
import { CoreUserDelegate, CoreUserProfileHandlerData } from '../../providers/user-delegate';
|
||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||
|
||||
|
|
|
@ -20,11 +20,11 @@ import { CoreUserHelperProvider } from './providers/helper';
|
|||
import { CoreUserProfileMailHandler } from './providers/user-handler';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreContentLinksDelegate } from '../contentlinks/providers/delegate';
|
||||
import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate';
|
||||
import { CoreUserProfileLinkHandler } from './providers/user-link-handler';
|
||||
import { CoreUserParticipantsCourseOptionHandler } from './providers/course-option-handler';
|
||||
import { CoreUserParticipantsLinkHandler } from './providers/participants-link-handler';
|
||||
import { CoreCourseOptionsDelegate } from '../course/providers/options-delegate';
|
||||
import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate';
|
||||
import { CoreUserComponentsModule } from './components/components.module';
|
||||
|
||||
@NgModule({
|
||||
|
|
|
@ -59,7 +59,7 @@ export class CoreDateDayOrTimePipe implements PipeTransform {
|
|||
}
|
||||
|
||||
return moment(timestamp * 1000).calendar(null, {
|
||||
sameDay: 'LT', //this.translate.instant('core.dftimedate'),
|
||||
sameDay: 'LT',
|
||||
lastDay: this.translate.instant('core.dflastweekdate'),
|
||||
lastWeek: this.translate.instant('core.dflastweekdate'),
|
||||
sameElse: 'L'
|
||||
|
|
|
@ -42,10 +42,13 @@ export interface CoreILocalNotification extends ILocalNotification {
|
|||
}
|
||||
|
||||
/*
|
||||
Generated class for the LocalNotificationsProvider provider.
|
||||
|
||||
See https://angular.io/guide/dependency-injection for more info on providers
|
||||
and Angular DI.
|
||||
* Generated class for the LocalNotificationsProvider provider.
|
||||
*
|
||||
* See https://angular.io/guide/dependency-injection for more info on providers
|
||||
* and Angular DI.
|
||||
*
|
||||
* @todo We might have to translate the old component name to the new one.
|
||||
* Otherwise the unique ID of local notifications could change.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreLocalNotificationsProvider {
|
||||
|
|
|
@ -23,9 +23,9 @@ import { CoreSitesProvider } from './sites';
|
|||
export class CoreSyncProvider {
|
||||
|
||||
// Variables for the database.
|
||||
static SYNC_TABLE = 'sync';
|
||||
protected SYNC_TABLE = 'sync';
|
||||
protected tableSchema = {
|
||||
name: CoreSyncProvider.SYNC_TABLE,
|
||||
name: this.SYNC_TABLE,
|
||||
columns: [
|
||||
{
|
||||
name: 'component',
|
||||
|
@ -34,7 +34,7 @@ export class CoreSyncProvider {
|
|||
},
|
||||
{
|
||||
name: 'id',
|
||||
type: 'INTEGER',
|
||||
type: 'TEXT',
|
||||
notNull: true
|
||||
},
|
||||
{
|
||||
|
@ -116,6 +116,36 @@ export class CoreSyncProvider {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sync record.
|
||||
* @param {string} component Component name.
|
||||
* @param {string | number} id Unique ID per component.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Record if found or reject.
|
||||
*/
|
||||
getSyncRecord(component: string, id: string | number, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSiteDb(siteId).then((db) => {
|
||||
return db.getRecord(this.SYNC_TABLE, { component: component, id: id });
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts or Updates info of a sync record.
|
||||
* @param {string} component Component name.
|
||||
* @param {string | number} id Unique ID per component.
|
||||
* @param {any} data Data that updates the record.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved with done.
|
||||
*/
|
||||
insertOrUpdateSyncRecord(component: string, id: string | number, data: any, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSiteDb(siteId).then((db) => {
|
||||
data.component = component;
|
||||
data.id = id;
|
||||
|
||||
return db.insertOrUpdateRecord(this.SYNC_TABLE, data, { component: component, id: id });
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to create unique identifiers for a component and id.
|
||||
*
|
||||
|
|
|
@ -23,6 +23,7 @@ import { CoreEventsProvider } from '../events';
|
|||
import { CoreLoggerProvider } from '../logger';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreLangProvider } from '../lang';
|
||||
import { CoreWSError } from '../ws';
|
||||
|
||||
/**
|
||||
* Deferred promise. It's similar to the result of $q.defer() in AngularJS.
|
||||
|
@ -601,28 +602,31 @@ export class CoreUtilsProvider {
|
|||
return typeof value != 'undefined' && (value === true || value === 'true' || parseInt(value, 10) === 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a "fake" WS error for local errors.
|
||||
*
|
||||
* @param {string} message The message to include in the error.
|
||||
* @param {boolean} [needsTranslate] If the message needs to be translated.
|
||||
* @return {CoreWSError} Fake WS error.
|
||||
*/
|
||||
createFakeWSError(message: string, needsTranslate?: boolean): CoreWSError {
|
||||
if (needsTranslate) {
|
||||
message = this.translate.instant(message);
|
||||
}
|
||||
|
||||
return {
|
||||
message: message
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an error returned by a WS call, check if the error is generated by the app or it has been returned by the WebSwervice.
|
||||
*
|
||||
* @param {string} error Error to check.
|
||||
* @param {any} error Error to check.
|
||||
* @return {boolean} Whether the error was returned by the WebService.
|
||||
*/
|
||||
isWebServiceError(error: string): boolean {
|
||||
const localErrors = [
|
||||
this.translate.instant('core.wsfunctionnotavailable'),
|
||||
this.translate.instant('core.lostconnection'),
|
||||
this.translate.instant('core.userdeleted'),
|
||||
this.translate.instant('core.unexpectederror'),
|
||||
this.translate.instant('core.networkerrormsg'),
|
||||
this.translate.instant('core.serverconnection'),
|
||||
this.translate.instant('core.errorinvalidresponse'),
|
||||
this.translate.instant('core.sitemaintenance'),
|
||||
this.translate.instant('core.upgraderunning'),
|
||||
this.translate.instant('core.nopasswordchangeforced'),
|
||||
this.translate.instant('core.unicodenotsupported')
|
||||
];
|
||||
|
||||
return error && localErrors.indexOf(error) == -1;
|
||||
isWebServiceError(error: any): boolean {
|
||||
return typeof error.errorcode == 'undefined';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -174,9 +174,9 @@ export class CoreWSProvider {
|
|||
let siteUrl;
|
||||
|
||||
if (!preSets) {
|
||||
return Promise.reject(this.createFakeWSError('core.unexpectederror', true));
|
||||
return Promise.reject(this.utils.createFakeWSError('core.unexpectederror', true));
|
||||
} else if (!this.appProvider.isOnline()) {
|
||||
return Promise.reject(this.createFakeWSError('core.networkerrormsg', true));
|
||||
return Promise.reject(this.utils.createFakeWSError('core.networkerrormsg', true));
|
||||
}
|
||||
|
||||
preSets.typeExpected = preSets.typeExpected || 'object';
|
||||
|
@ -320,23 +320,6 @@ export class CoreWSProvider {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a "fake" WS error for local errors.
|
||||
*
|
||||
* @param {string} message The message to include in the error.
|
||||
* @param {boolean} [needsTranslate] If the message needs to be translated.
|
||||
* @return {CoreWSError} Fake WS error.
|
||||
*/
|
||||
createFakeWSError(message: string, needsTranslate?: boolean): CoreWSError {
|
||||
if (needsTranslate) {
|
||||
message = this.translate.instant(message);
|
||||
}
|
||||
|
||||
return {
|
||||
message: message
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a file from Moodle using Cordova File API.
|
||||
*
|
||||
|
@ -532,11 +515,11 @@ export class CoreWSProvider {
|
|||
}
|
||||
|
||||
if (!data) {
|
||||
return Promise.reject(this.createFakeWSError('core.serverconnection', true));
|
||||
return Promise.reject(this.utils.createFakeWSError('core.serverconnection', true));
|
||||
} else if (typeof data != preSets.typeExpected) {
|
||||
this.logger.warn('Response of type "' + typeof data + `" received, expecting "${preSets.typeExpected}"`);
|
||||
|
||||
return Promise.reject(this.createFakeWSError('core.errorinvalidresponse', true));
|
||||
return Promise.reject(this.utils.createFakeWSError('core.errorinvalidresponse', true));
|
||||
}
|
||||
|
||||
if (typeof data.exception !== 'undefined') {
|
||||
|
@ -544,7 +527,7 @@ export class CoreWSProvider {
|
|||
}
|
||||
|
||||
if (typeof data.debuginfo != 'undefined') {
|
||||
return Promise.reject(this.createFakeWSError('Error. ' + data.message));
|
||||
return Promise.reject(this.utils.createFakeWSError('Error. ' + data.message));
|
||||
}
|
||||
|
||||
return data;
|
||||
|
@ -572,7 +555,7 @@ export class CoreWSProvider {
|
|||
return retryPromise;
|
||||
}
|
||||
|
||||
return Promise.reject(this.createFakeWSError('core.serverconnection', true));
|
||||
return Promise.reject(this.utils.createFakeWSError('core.serverconnection', true));
|
||||
});
|
||||
|
||||
promise = this.setPromiseHttp(promise, 'post', preSets.siteUrl, ajaxData);
|
||||
|
|
Loading…
Reference in New Issue