Merge pull request #1642 from dpalou/MOBILE-2631

Mobile 2631
main
Juan Leyva 2018-12-03 16:27:10 +01:00 committed by GitHub
commit af916b10ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 244 additions and 44 deletions

View File

@ -148,6 +148,7 @@
"addon.files.sitefiles": "moodle",
"addon.messageoutput_airnotifier.processorsettingsdesc": "local_moodlemobileapp",
"addon.messages.addcontact": "message",
"addon.messages.addtofavourites": "message",
"addon.messages.blocknoncontacts": "message",
"addon.messages.blockuser": "message",
"addon.messages.blockuserconfirm": "message",
@ -159,6 +160,8 @@
"addon.messages.contactlistempty": "local_moodlemobileapp",
"addon.messages.contactname": "local_moodlemobileapp",
"addon.messages.contacts": "message",
"addon.messages.deleteallconfirm": "message",
"addon.messages.deleteconversationq": "message",
"addon.messages.deletemessage": "local_moodlemobileapp",
"addon.messages.deletemessageconfirmation": "local_moodlemobileapp",
"addon.messages.errordeletemessage": "local_moodlemobileapp",
@ -181,6 +184,7 @@
"addon.messages.numparticipants": "message",
"addon.messages.removecontact": "message",
"addon.messages.removecontactconfirm": "local_moodlemobileapp",
"addon.messages.removefromfavourites": "message",
"addon.messages.showdeletemessages": "local_moodlemobileapp",
"addon.messages.type_blocked": "local_moodlemobileapp",
"addon.messages.type_offline": "local_moodlemobileapp",

View File

@ -1,5 +1,6 @@
{
"addcontact": "Add contact",
"addtofavourites": "Star",
"blocknoncontacts": "Prevent non-contacts from messaging me",
"blockuser": "Block user",
"blockuserconfirm": "Are you sure you want to block {{$a}}?",
@ -11,6 +12,8 @@
"contactlistempty": "The contact list is empty",
"contactname": "Contact name",
"contacts": "Contacts",
"deleteallconfirm": "Are you sure you would like to delete this entire conversation? This will not delete it for other conversation participants.",
"deleteconversation": "Delete conversation",
"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.",
"errordeletemessage": "Error while deleting the message.",
@ -33,6 +36,7 @@
"numparticipants": "{{$a}} participants",
"removecontact": "Remove contact",
"removecontactconfirm": "Contact will be removed from your contacts list.",
"removefromfavourites": "Unstar",
"showdeletemessages": "Show delete messages",
"type_blocked": "Blocked",
"type_offline": "Offline",

View File

@ -3,14 +3,17 @@
<ion-title>
<img *ngIf="conversationImage" class="core-bar-button-image" [src]="conversationImage">
<core-format-text [text]="title"></core-format-text>
<core-icon *ngIf="conversation && conversation.isfavourite" name="fa-star"></core-icon>
</ion-title>
<ion-buttons end></ion-buttons>
</ion-navbar>
<core-navbar-buttons end>
<core-context-menu>
<core-context-menu-item [hidden]="!showInfo || isGroup" [priority]="1000" [content]="'addon.messages.info' | translate" (action)="viewInfo()" [iconAction]="'information-circle'"></core-context-menu-item>
<core-context-menu-item [hidden]="!showInfo || !isGroup" [priority]="999" [content]="'addon.messages.groupinfo' | translate" (action)="viewInfo()" [iconAction]="'information-circle'"></core-context-menu-item>
<core-context-menu-item [hidden]="!canDelete" [priority]="800" [content]="'addon.messages.showdeletemessages' | translate" (action)="toggleDelete()" [iconAction]="'trash'"></core-context-menu-item>
<core-context-menu-item [hidden]="!showInfo || !isGroup" [priority]="1000" [content]="'addon.messages.groupinfo' | translate" (action)="viewInfo()" [iconAction]="'information-circle'"></core-context-menu-item>
<core-context-menu-item [hidden]="!groupMessagingEnabled || !conversation" [priority]="800" [content]="(conversation && conversation.isfavourite ? 'addon.messages.removefromfavourites' : 'addon.messages.addtofavourites') | translate" (action)="changeFavourite($event)" [iconAction]="favouriteIcon" [closeOnClick]="false"></core-context-menu-item>
<core-context-menu-item [hidden]="!canDelete" [priority]="400" [content]="'addon.messages.showdeletemessages' | translate" (action)="toggleDelete()" [iconAction]="'trash'"></core-context-menu-item>
<core-context-menu-item [hidden]="!groupMessagingEnabled || !conversationId || isGroup" [priority]="200" [content]="'addon.messages.deleteconversation' | translate" (action)="deleteConversation($event)" [iconAction]="deleteIcon" [closeOnClick]="false"></core-context-menu-item>
</core-context-menu>
</core-navbar-buttons>
</ion-header>
@ -54,7 +57,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" *ngIf="!conversationId || conversation">
<ion-footer color="light" class="footer-adjustable" *ngIf="loaded && (!conversationId || conversation)">
<ion-toolbar color="light" position="bottom">
<!-- @todo: Check if the user can send messages. -->
<core-send-message-form (onSubmit)="sendMessage($event)" [showKeyboard]="showKeyboard" [placeholder]="'addon.messages.newmessage' | translate" (onResize)="resizeContent()"></core-send-message-form>

View File

@ -75,6 +75,8 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
groupMessagingEnabled: boolean;
isGroup = false;
members: any = {}; // Members that wrote a message, indexed by ID.
favouriteIcon = 'fa-star';
deleteIcon = 'trash';
constructor(private eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, navParams: NavParams,
private userProvider: CoreUserProvider, private navCtrl: NavController, private messagesSync: AddonMessagesSyncProvider,
@ -332,51 +334,67 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
* @return {Promise<boolean>} Promise resolved with a boolean: whether the conversation exists or not.
*/
protected getConversation(conversationId: number, userId: number): Promise<boolean> {
let promise;
let promise,
fallbackConversation;
// Try to get the conversationId if we don't have it.
if (conversationId) {
// Retrieve the conversation. Invalidate data first to get the right unreadcount.
promise = this.messagesProvider.invalidateConversation(conversationId).then(() => {
return this.messagesProvider.getConversation(conversationId);
});
promise = Promise.resolve(conversationId);
} else {
// We don't have the conversation ID, check if it exists.
promise = this.messagesProvider.getConversationBetweenUsers(userId).catch((error) => {
promise = this.messagesProvider.getConversationBetweenUsers(userId).then((conversation) => {
fallbackConversation = conversation;
// Probably conversation does not exist or user is offline. Try to load offline messages.
return this.messagesOffline.getMessages(userId).then((messages) => {
if (messages && messages.length) {
// We have offline messages, this probably means that the conversation didn't exist. Don't display error.
messages.forEach((message) => {
message.pending = true;
message.text = message.smallmessage;
});
this.loadMessages(messages);
} else if (error.errorcode != 'errorconversationdoesnotexist') {
// Display the error.
return Promise.reject(error);
}
});
return conversation.id;
});
}
return promise.then((conversation) => {
this.conversation = conversation;
if (conversation) {
this.conversationId = conversation.id;
this.title = conversation.name;
this.conversationImage = conversation.imageurl;
this.isGroup = conversation.type == AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP;
if (!this.isGroup) {
this.userId = conversation.userid;
return promise.then((conversationId) => {
// Retrieve the conversation. Invalidate data first to get the right unreadcount.
return this.messagesProvider.invalidateConversation(conversationId).catch(() => {
// Ignore errors.
}).then(() => {
return this.messagesProvider.getConversation(conversationId);
}).catch((error) => {
// Get conversation failed, use the fallback one if we have it.
if (fallbackConversation) {
return fallbackConversation;
}
return true;
} else {
return false;
}
return Promise.reject(error);
}).then((conversation) => {
this.conversation = conversation;
if (conversation) {
this.conversationId = conversation.id;
this.title = conversation.name;
this.conversationImage = conversation.imageurl;
this.isGroup = conversation.type == AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP;
this.favouriteIcon = conversation.isfavourite ? 'fa-star-o' : 'fa-star';
if (!this.isGroup) {
this.userId = conversation.userid;
}
return true;
} else {
return false;
}
});
}, (error) => {
// Probably conversation does not exist or user is offline. Try to load offline messages.
return this.messagesOffline.getMessages(userId).then((messages) => {
if (messages && messages.length) {
// We have offline messages, this probably means that the conversation didn't exist. Don't display error.
messages.forEach((message) => {
message.pending = true;
message.text = message.smallmessage;
});
this.loadMessages(messages);
} else if (error.errorcode != 'errorconversationdoesnotexist') {
// Display the error.
return Promise.reject(error);
}
});
});
}
@ -940,6 +958,60 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
}
}
/**
* Change the favourite state of the current conversation.
*
* @param {Function} [done] Function to call when done.
*/
changeFavourite(done?: () => void): void {
this.favouriteIcon = 'spinner';
this.messagesProvider.setFavouriteConversation(this.conversation.id, !this.conversation.isfavourite).then(() => {
this.conversation.isfavourite = !this.conversation.isfavourite;
// Get the conversation data so it's cached. Don't block the user for this.
this.messagesProvider.getConversation(this.conversation.id);
this.eventsProvider.trigger(AddonMessagesProvider.UPDATE_CONVERSATION_LIST_EVENT, {
conversationId: this.conversation.id,
action: 'favourite',
value: this.conversation.isfavourite
}, this.siteId);
}).catch((error) => {
this.domUtils.showErrorModalDefault(error, 'Error changing favourite state.');
}).finally(() => {
this.favouriteIcon = this.conversation.isfavourite ? 'fa-star-o' : 'fa-star';
done && done();
});
}
/**
* Delete the conversation.
*
* @param {Function} [done] Function to call when done.
*/
deleteConversation(done?: () => void): void {
this.domUtils.showConfirm(this.translate.instant('addon.messages.deleteallconfirm')).then(() => {
this.deleteIcon = 'spinner';
return this.messagesProvider.deleteConversation(this.conversation.id).then(() => {
this.eventsProvider.trigger(AddonMessagesProvider.UPDATE_CONVERSATION_LIST_EVENT, {
conversationId: this.conversation.id,
action: 'delete'
}, this.siteId);
this.conversationId = undefined;
this.conversation = undefined;
this.messages = [];
}).finally(() => {
this.deleteIcon = 'trash';
done && done();
});
}).catch((error) => {
this.domUtils.showErrorModalDefault(error, 'Error deleting conversation.');
});
}
/**
* Page destroyed.
*/

View File

@ -73,6 +73,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
protected readChangedObserver: any;
protected cronObserver: any;
protected openConversationObserver: any;
protected updateConversationListObserver: any;
constructor(private eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, translate: TranslateService,
private messagesProvider: AddonMessagesProvider, private domUtils: CoreDomUtilsProvider, navParams: NavParams,
@ -115,22 +116,22 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
}
}, this.siteId);
// Update discussions when a message is read.
// Update conversations when a message is read.
this.readChangedObserver = eventsProvider.on(AddonMessagesProvider.READ_CHANGED_EVENT, (data) => {
if (data.conversationId) {
const conversation = this.findConversation(data.conversationId);
if (typeof conversation != 'undefined') {
// A discussion has been read reset counter.
// A conversation has been read reset counter.
conversation.unreadcount = 0;
// Discussions changed, invalidate them.
// Conversations changed, invalidate them.
this.messagesProvider.invalidateConversations();
}
}
}, this.siteId);
// Update discussions when cron read is executed.
// Update conversations when cron read is executed.
this.cronObserver = eventsProvider.on(AddonMessagesProvider.READ_CRON_EVENT, (data) => {
this.refreshData();
}, this.siteId);
@ -153,6 +154,11 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
});
});
// Update conversations if we receive an event to do so.
this.updateConversationListObserver = eventsProvider.on(AddonMessagesProvider.UPDATE_CONVERSATION_LIST_EVENT, () => {
this.refreshData();
}, this.siteId);
// If a message push notification is received, refresh the view.
this.pushObserver = pushNotificationsDelegate.on('receive').subscribe((notification) => {
// New message received. If it's from current site, refresh the data.
@ -551,5 +557,6 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
this.readChangedObserver && this.readChangedObserver.off();
this.cronObserver && this.cronObserver.off();
this.openConversationObserver && this.openConversationObserver.off();
this.updateConversationListObserver && this.updateConversationListObserver.off();
}
}

View File

@ -109,6 +109,21 @@ export class AddonMessagesOfflineProvider {
});
}
/**
* Delete all the messages in a conversation.
*
* @param {number} conversationId Conversation ID.
* @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved if stored, rejected if failure.
*/
deleteConversationMessages(conversationId: number, siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
return site.getDb().deleteRecords(AddonMessagesOfflineProvider.CONVERSATION_MESSAGES_TABLE, {
conversationid: conversationId
});
});
}
/**
* Delete a message.
*

View File

@ -35,6 +35,7 @@ export class AddonMessagesProvider {
static READ_CRON_EVENT = 'addon_messages_read_cron_event';
static OPEN_CONVERSATION_EVENT = 'addon_messages_open_conversation_event'; // Notify that a conversation should be opened.
static SPLIT_VIEW_LOAD_EVENT = 'addon_messages_split_view_load_event';
static UPDATE_CONVERSATION_LIST_EVENT = 'addon_messages_update_conversation_list_event';
static POLL_INTERVAL = 10000;
static PUSH_SIMULATION_COMPONENT = 'AddonMessagesPushSimulation';
@ -105,6 +106,49 @@ export class AddonMessagesProvider {
});
}
/**
* Delete a conversation.
*
* @param {number} conversationId Conversation to delete.
* @param {string} [siteId] Site ID. If not defined, use current site.
* @param {number} [userId] User ID. If not defined, current user in the site.
* @return {Promise<any>} Promise resolved when the conversation has been deleted.
*/
deleteConversation(conversationId: number, siteId?: string, userId?: number): Promise<any> {
return this.deleteConversations([conversationId], siteId, userId);
}
/**
* Delete several conversations.
*
* @param {number[]} conversationIds Conversations to delete.
* @param {string} [siteId] Site ID. If not defined, use current site.
* @param {number} [userId] User ID. If not defined, current user in the site.
* @return {Promise<any>} Promise resolved when the conversations have been deleted.
*/
deleteConversations(conversationIds: number[], siteId?: string, userId?: number): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
userId = userId || site.getUserId();
const params = {
userid: userId,
conversationids: conversationIds
};
return site.write('core_message_delete_conversations_by_id', params).then(() => {
const promises = [];
conversationIds.forEach((conversationId) => {
promises.push(this.messagesOffline.deleteConversationMessages(conversationId, site.getId()).catch(() => {
// Ignore errors (shouldn't happen).
}));
});
return Promise.all(promises);
});
});
}
/**
* Delete a message (online or offline).
*
@ -1114,7 +1158,7 @@ export class AddonMessagesProvider {
return this.sitesProvider.getSite(siteId).then((site) => {
userId = userId || site.getUserId();
return site.invalidateWsCacheForKey(this.getCacheKeyForConversation(conversationId, userId));
return site.invalidateWsCacheForKey(this.getCacheKeyForConversation(userId, conversationId));
});
}
@ -1697,6 +1741,53 @@ export class AddonMessagesProvider {
});
}
/**
* Set or unset a conversation as favourite.
*
* @param {number} conversationId Conversation ID.
* @param {boolean} set Whether to set or unset it as favourite.
* @param {string} [siteId] Site ID. If not defined, use current site.
* @param {number} [userId] User ID. If not defined, current user in the site.
* @return {Promise<any>} Resolved when done.
*/
setFavouriteConversation(conversationId: number, set: boolean, siteId?: string, userId?: number): Promise<any> {
return this.setFavouriteConversations([conversationId], set, siteId, userId);
}
/**
* Set or unset some conversations as favourites.
*
* @param {number[]} conversations Conversation IDs.
* @param {boolean} set Whether to set or unset them as favourites.
* @param {string} [siteId] Site ID. If not defined, use current site.
* @param {number} [userId] User ID. If not defined, current user in the site.
* @return {Promise<any>} Resolved when done.
*/
setFavouriteConversations(conversations: number[], set: boolean, siteId?: string, userId?: number): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
userId = userId || site.getUserId();
const params = {
userid: userId,
conversations: conversations
},
wsName = set ? 'core_message_set_favourite_conversations' : 'core_message_unset_favourite_conversations';
return site.write(wsName, params).then(() => {
// Invalidate the conversations data.
const promises = [];
conversations.forEach((conversationId) => {
promises.push(this.invalidateConversation(conversationId, site.getId(), userId));
});
return Promise.all(promises).catch(() => {
// Ignore errors.
});
});
});
}
/**
* Helper method to sort conversations by last message time.
*

View File

@ -148,6 +148,7 @@
"addon.files.sitefiles": "Site files",
"addon.messageoutput_airnotifier.processorsettingsdesc": "Configure devices",
"addon.messages.addcontact": "Add contact",
"addon.messages.addtofavourites": "Star",
"addon.messages.blocknoncontacts": "Prevent non-contacts from messaging me",
"addon.messages.blockuser": "Block user",
"addon.messages.blockuserconfirm": "Are you sure you want to block {{$a}}?",
@ -159,6 +160,8 @@
"addon.messages.contactlistempty": "The contact list is empty",
"addon.messages.contactname": "Contact name",
"addon.messages.contacts": "Contacts",
"addon.messages.deleteallconfirm": "Are you sure you would like to delete this entire conversation? This will not delete it for other conversation participants.",
"addon.messages.deleteconversation": "Delete conversation",
"addon.messages.deletemessage": "Delete message",
"addon.messages.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.",
"addon.messages.errordeletemessage": "Error while deleting the message.",
@ -181,6 +184,7 @@
"addon.messages.numparticipants": "{{$a}} participants",
"addon.messages.removecontact": "Remove contact",
"addon.messages.removecontactconfirm": "Contact will be removed from your contacts list.",
"addon.messages.removefromfavourites": "Unstar",
"addon.messages.showdeletemessages": "Show delete messages",
"addon.messages.type_blocked": "Blocked",
"addon.messages.type_offline": "Offline",