MOBILE-2632 message: Display offline messages in conversations list
parent
06f7a427c6
commit
ffc98f2c71
|
@ -54,7 +54,7 @@
|
|||
<core-empty-box *ngIf="!messages || messages.length <= 0" icon="chatbubbles" [message]="'addon.messages.nomessages' | translate"></core-empty-box>
|
||||
</core-loading>
|
||||
</ion-content>
|
||||
<ion-footer color="light" class="footer-adjustable">
|
||||
<ion-footer color="light" class="footer-adjustable" *ngIf="!conversationId || conversation">
|
||||
<ion-toolbar color="light" position="bottom">
|
||||
<core-send-message-form (onSubmit)="sendMessage($event)" [showKeyboard]="showKeyboard" [placeholder]="'addon.messages.newmessage' | translate" (onResize)="resizeContent()"></core-send-message-form>
|
||||
</ion-toolbar>
|
||||
|
|
|
@ -781,6 +781,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
|
||||
/**
|
||||
* Sends a message to the server.
|
||||
*
|
||||
* @param {string} text Message text.
|
||||
*/
|
||||
sendMessage(text: string): void {
|
||||
|
@ -810,7 +811,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
let promise;
|
||||
|
||||
if (this.conversationId) {
|
||||
promise = this.messagesProvider.sendMessageToConversation(this.conversationId, text);
|
||||
promise = this.messagesProvider.sendMessageToConversation(this.conversation, text);
|
||||
} else {
|
||||
promise = this.messagesProvider.sendMessage(this.userId, text);
|
||||
}
|
||||
|
|
|
@ -18,11 +18,13 @@ import { TranslateService } from '@ngx-translate/core';
|
|||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { AddonMessagesProvider } from '../../providers/messages';
|
||||
import { AddonMessagesOfflineProvider } from '../../providers/messages-offline';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreAppProvider } from '@providers/app';
|
||||
import { AddonPushNotificationsDelegate } from '@addon/pushnotifications/providers/delegate';
|
||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||
import { CoreUserProvider } from '@core/user/providers/user';
|
||||
|
||||
/**
|
||||
* Page that displays the list of conversations, including group conversations.
|
||||
|
@ -71,7 +73,8 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
|||
constructor(private eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, translate: TranslateService,
|
||||
private messagesProvider: AddonMessagesProvider, private domUtils: CoreDomUtilsProvider, navParams: NavParams,
|
||||
private appProvider: CoreAppProvider, platform: Platform, utils: CoreUtilsProvider,
|
||||
pushNotificationsDelegate: AddonPushNotificationsDelegate) {
|
||||
pushNotificationsDelegate: AddonPushNotificationsDelegate, private messagesOffline: AddonMessagesOfflineProvider,
|
||||
private userProvider: CoreUserProvider) {
|
||||
|
||||
this.search.loading = translate.instant('core.searching');
|
||||
this.loadingString = translate.instant('core.loading');
|
||||
|
@ -179,12 +182,25 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
|||
|
||||
// Load the first conversations of each type.
|
||||
const promises = [];
|
||||
let offlineMessages;
|
||||
|
||||
promises.push(this.fetchDataForOption(this.favourites, false));
|
||||
promises.push(this.fetchDataForOption(this.group, false));
|
||||
promises.push(this.fetchDataForOption(this.individual, false));
|
||||
promises.push(this.messagesOffline.getAllMessages().then((messages) => {
|
||||
offlineMessages = messages;
|
||||
}));
|
||||
|
||||
return Promise.all(promises).then(() => {
|
||||
return this.loadOfflineMessages(offlineMessages);
|
||||
}).then(() => {
|
||||
if (offlineMessages && offlineMessages.length) {
|
||||
// Sort the conversations, the offline messages could affect the order.
|
||||
this.favourites.conversations = this.messagesProvider.sortConversations(this.favourites.conversations);
|
||||
this.group.conversations = this.messagesProvider.sortConversations(this.group.conversations);
|
||||
this.individual.conversations = this.messagesProvider.sortConversations(this.individual.conversations);
|
||||
}
|
||||
|
||||
if (typeof this.favourites.expanded == 'undefined') {
|
||||
// The expanded status hasn't been initialized. Do it now.
|
||||
this.favourites.expanded = this.favourites.count != 0;
|
||||
|
@ -286,6 +302,98 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load offline messages into the conversations.
|
||||
*
|
||||
* @param {any[]} messages Offline messages.
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
protected loadOfflineMessages(messages: any[]): Promise<any> {
|
||||
const promises = [];
|
||||
|
||||
messages.forEach((message) => {
|
||||
if (message.conversationid) {
|
||||
// It's an existing conversation. Search it.
|
||||
let conversation = this.findConversation(message.conversationid);
|
||||
|
||||
if (conversation) {
|
||||
// Check if it's the last message. Offline messages are considered more recent than sent messages.
|
||||
if (typeof conversation.lastmessage === 'undefined' || conversation.lastmessage === null ||
|
||||
!conversation.lastmessagepending || conversation.lastmessagedate <= message.timecreated / 1000) {
|
||||
|
||||
this.addLastOfflineMessage(conversation, message);
|
||||
}
|
||||
} else {
|
||||
// Conversation not found, it's probably an old one. Add it.
|
||||
conversation = message.conversation || {};
|
||||
conversation.id = message.conversationid;
|
||||
|
||||
this.addLastOfflineMessage(conversation, message);
|
||||
this.addOfflineConversation(conversation);
|
||||
}
|
||||
} else {
|
||||
// Its a new conversation. Check if we already created it (there is more than one message for the same user).
|
||||
const conversation = this.individual.conversations.find((conv) => {
|
||||
return conv.userid == message.touserid;
|
||||
});
|
||||
|
||||
message.text = message.smallmessage;
|
||||
|
||||
if (conversation) {
|
||||
// Check if it's the last message. Offline messages are considered more recent than sent messages.
|
||||
if (conversation.lastmessagedate <= message.timecreated / 1000) {
|
||||
this.addLastOfflineMessage(conversation, message);
|
||||
}
|
||||
} else {
|
||||
// Get the user data and create a new conversation.
|
||||
promises.push(this.userProvider.getProfile(message.touserid, undefined, true).catch(() => {
|
||||
// User not found.
|
||||
}).then((user) => {
|
||||
const conversation = {
|
||||
userid: message.touserid,
|
||||
name: user ? user.fullname : String(message.touserid),
|
||||
imageurl: user ? user.profileimageurl : '',
|
||||
type: AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL
|
||||
};
|
||||
|
||||
this.addLastOfflineMessage(conversation, message);
|
||||
this.addOfflineConversation(conversation);
|
||||
}));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an offline conversation into the right list of conversations.
|
||||
*
|
||||
* @param {any} conversation Offline conversation to add.
|
||||
*/
|
||||
protected addOfflineConversation(conversation: any): void {
|
||||
if (conversation.isfavourite) {
|
||||
this.favourites.conversations.unshift(conversation);
|
||||
} else if (conversation.type == AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP) {
|
||||
this.group.conversations.unshift(conversation);
|
||||
} else {
|
||||
this.individual.conversations.unshift(conversation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a last offline message into a conversation.
|
||||
*
|
||||
* @param {any} conversation Conversation where to put the last message.
|
||||
* @param {any} message Offline message to add.
|
||||
*/
|
||||
protected addLastOfflineMessage(conversation: any, message: any): void {
|
||||
conversation.lastmessage = message.text;
|
||||
conversation.lastmessagedate = message.timecreated / 1000;
|
||||
conversation.lastmessagepending = true;
|
||||
conversation.sentfromcurrentuser = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the data.
|
||||
*
|
||||
|
|
|
@ -16,6 +16,7 @@ import { Injectable } from '@angular/core';
|
|||
import { CoreLoggerProvider } from '@providers/logger';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreAppProvider } from '@providers/app';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
|
||||
/**
|
||||
* Service to handle Offline messages.
|
||||
|
@ -73,13 +74,18 @@ export class AddonMessagesOfflineProvider {
|
|||
{
|
||||
name: 'deviceoffline', // If message was stored because device was offline.
|
||||
type: 'INTEGER'
|
||||
},
|
||||
{
|
||||
name: 'conversation', // Data about the conversation.
|
||||
type: 'TEXT'
|
||||
}
|
||||
],
|
||||
primaryKeys: ['conversationid', 'text', 'timecreated']
|
||||
}
|
||||
];
|
||||
|
||||
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private appProvider: CoreAppProvider) {
|
||||
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private appProvider: CoreAppProvider,
|
||||
private textUtils: CoreTextUtilsProvider) {
|
||||
this.logger = logger.getInstance('AddonMessagesOfflineProvider');
|
||||
this.sitesProvider.createTablesFromSchema(this.tablesSchema);
|
||||
}
|
||||
|
@ -136,6 +142,8 @@ export class AddonMessagesOfflineProvider {
|
|||
promises.push(site.getDb().getRecords(AddonMessagesOfflineProvider.CONVERSATION_MESSAGES_TABLE, {deviceoffline: 1}));
|
||||
|
||||
return Promise.all(promises).then((results) => {
|
||||
results[1] = this.parseConversationMessages(results[1]);
|
||||
|
||||
return results[0].concat(results[1]);
|
||||
});
|
||||
});
|
||||
|
@ -155,6 +163,8 @@ export class AddonMessagesOfflineProvider {
|
|||
promises.push(site.getDb().getAllRecords(AddonMessagesOfflineProvider.CONVERSATION_MESSAGES_TABLE));
|
||||
|
||||
return Promise.all(promises).then((results) => {
|
||||
results[1] = this.parseConversationMessages(results[1]);
|
||||
|
||||
return results[0].concat(results[1]);
|
||||
});
|
||||
});
|
||||
|
@ -170,7 +180,10 @@ export class AddonMessagesOfflineProvider {
|
|||
getConversationMessages(conversationId: number, siteId?: string): Promise<any[]> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.getDb().getRecords(AddonMessagesOfflineProvider.CONVERSATION_MESSAGES_TABLE,
|
||||
{conversationid: conversationId});
|
||||
{conversationid: conversationId}).then((messages) => {
|
||||
|
||||
return this.parseConversationMessages(messages);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -213,21 +226,48 @@ export class AddonMessagesOfflineProvider {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse some fields of each offline conversation messages.
|
||||
*
|
||||
* @param {any[]} messages List of messages to parse.
|
||||
* @return {any[]} Parsed messages.
|
||||
*/
|
||||
protected parseConversationMessages(messages: any[]): any[] {
|
||||
if (!messages) {
|
||||
return [];
|
||||
}
|
||||
|
||||
messages.forEach((message) => {
|
||||
if (message.conversation) {
|
||||
message.conversation = this.textUtils.parseJSON(message.conversation, {});
|
||||
}
|
||||
});
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a conversation message to be sent later.
|
||||
*
|
||||
* @param {number} conversationId Conversation ID.
|
||||
* @param {any} conversation Conversation.
|
||||
* @param {string} message The message to send.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved if stored, rejected if failure.
|
||||
*/
|
||||
saveConversationMessage(conversationId: number, message: string, siteId?: string): Promise<any> {
|
||||
saveConversationMessage(conversation: any, message: string, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const entry = {
|
||||
conversationid: conversationId,
|
||||
conversationid: conversation.id,
|
||||
text: message,
|
||||
timecreated: Date.now(),
|
||||
deviceoffline: this.appProvider.isOnline() ? 0 : 1
|
||||
deviceoffline: this.appProvider.isOnline() ? 0 : 1,
|
||||
conversation: JSON.stringify({
|
||||
name: conversation.name || '',
|
||||
subname: conversation.subname || '',
|
||||
imageurl: conversation.imageurl || '',
|
||||
isfavourite: conversation.isfavourite ? 1 : 0,
|
||||
type: conversation.type
|
||||
})
|
||||
};
|
||||
|
||||
return site.getDb().insertRecord(AddonMessagesOfflineProvider.CONVERSATION_MESSAGES_TABLE, entry).then(() => {
|
||||
|
@ -276,9 +316,11 @@ export class AddonMessagesOfflineProvider {
|
|||
|
||||
messages.forEach((message) => {
|
||||
if (message.conversationid) {
|
||||
promises.push(db.insertRecord(AddonMessagesOfflineProvider.CONVERSATION_MESSAGES_TABLE, data));
|
||||
promises.push(db.updateRecords(AddonMessagesOfflineProvider.CONVERSATION_MESSAGES_TABLE, data,
|
||||
{conversationid: message.conversationid, text: message.text, timecreated: message.timecreated}));
|
||||
} else {
|
||||
promises.push(db.insertRecord(AddonMessagesOfflineProvider.MESSAGES_TABLE, data));
|
||||
promises.push(db.updateRecords(AddonMessagesOfflineProvider.MESSAGES_TABLE, data,
|
||||
{touserid: message.touserid, smallmessage: message.smallmessage, timecreated: message.timecreated}));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1506,7 +1506,7 @@ export class AddonMessagesProvider {
|
|||
/**
|
||||
* Send a message to a conversation.
|
||||
*
|
||||
* @param {number} conversationId Conversation ID.
|
||||
* @param {any} conversation Conversation.
|
||||
* @param {string} message The message to send.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved with:
|
||||
|
@ -1514,10 +1514,10 @@ export class AddonMessagesProvider {
|
|||
* - message (any) If sent=false, contains the stored message.
|
||||
* @since 3.6
|
||||
*/
|
||||
sendMessageToConversation(conversationId: number, message: string, siteId?: string): Promise<any> {
|
||||
sendMessageToConversation(conversation: any, message: string, siteId?: string): Promise<any> {
|
||||
// Convenience function to store a message to be synchronized later.
|
||||
const storeOffline = (): Promise<any> => {
|
||||
return this.messagesOffline.saveConversationMessage(conversationId, message, siteId).then((entry) => {
|
||||
return this.messagesOffline.saveConversationMessage(conversation, message, siteId).then((entry) => {
|
||||
return {
|
||||
sent: false,
|
||||
message: entry
|
||||
|
@ -1534,7 +1534,7 @@ export class AddonMessagesProvider {
|
|||
|
||||
// Check if this conversation already has offline messages.
|
||||
// If so, store this message since they need to be sent in order.
|
||||
return this.messagesOffline.hasConversationMessages(conversationId, siteId).catch(() => {
|
||||
return this.messagesOffline.hasConversationMessages(conversation.id, siteId).catch(() => {
|
||||
// Error, it's safer to assume it has messages.
|
||||
return true;
|
||||
}).then((hasStoredMessages) => {
|
||||
|
@ -1543,7 +1543,7 @@ export class AddonMessagesProvider {
|
|||
}
|
||||
|
||||
// Online and no messages stored. Send it to server.
|
||||
return this.sendMessageToConversationOnline(conversationId, message).then(() => {
|
||||
return this.sendMessageToConversationOnline(conversation.id, message).then(() => {
|
||||
return { sent: true };
|
||||
}).catch((error) => {
|
||||
if (this.utils.isWebServiceError(error)) {
|
||||
|
@ -1610,13 +1610,33 @@ export class AddonMessagesProvider {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to sort conversations by last message time.
|
||||
*
|
||||
* @param {any[]} conversations Array of conversations.
|
||||
* @return {any[]} Conversations sorted with most recent last.
|
||||
*/
|
||||
sortConversations(conversations: any[]): any[] {
|
||||
return conversations.sort((a, b) => {
|
||||
const timeA = parseInt(a.lastmessagedate, 10),
|
||||
timeB = parseInt(b.lastmessagedate, 10);
|
||||
|
||||
if (timeA == timeB && a.id) {
|
||||
// Same time, sort by ID.
|
||||
return a.id <= b.id ? 1 : -1;
|
||||
}
|
||||
|
||||
return timeA <= timeB ? 1 : -1;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to sort messages by time.
|
||||
*
|
||||
* @param {any} messages Array of messages containing the key 'timecreated'.
|
||||
* @return {any} Messages sorted with most recent last.
|
||||
* @param {any[]} messages Array of messages containing the key 'timecreated'.
|
||||
* @return {any[]} Messages sorted with most recent last.
|
||||
*/
|
||||
sortMessages(messages: any): any {
|
||||
sortMessages(messages: any[]): any[] {
|
||||
return messages.sort((a, b) => {
|
||||
// Pending messages last.
|
||||
if (a.pending && !b.pending) {
|
||||
|
|
Loading…
Reference in New Issue