MOBILE-2620 messages: Contact requests in discussion page
parent
d9a22a43b5
commit
43cbd655d8
|
@ -9,11 +9,15 @@
|
|||
</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]="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-item [hidden]="!showInfo || isGroup" [priority]="1000" [content]="'addon.messages.info' | translate" (action)="viewInfo()"></core-context-menu-item>
|
||||
<core-context-menu-item [hidden]="!showInfo || !isGroup" [priority]="1000" [content]="'addon.messages.groupinfo' | translate" (action)="viewInfo()"></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)" [closeOnClick]="false"></core-context-menu-item>
|
||||
<core-context-menu-item [hidden]="!otherMember || otherMember.isblocked" [priority]="700" [content]="'addon.messages.blockuser' | translate" (action)="blockUser()"></core-context-menu-item>
|
||||
<core-context-menu-item [hidden]="!otherMember || !otherMember.isblocked" [priority]="700" [content]="'addon.messages.unblockuser' | translate" (action)="unblockUser()"></core-context-menu-item>
|
||||
<core-context-menu-item [hidden]="!canDelete" [priority]="400" [content]="'addon.messages.showdeletemessages' | translate" (action)="toggleDelete()"></core-context-menu-item>
|
||||
<core-context-menu-item [hidden]="!groupMessagingEnabled || !conversationId || isGroup" [priority]="200" [content]="'addon.messages.deleteconversation' | translate" (action)="deleteConversation($event)" [closeOnClick]="false"></core-context-menu-item>
|
||||
<core-context-menu-item [hidden]="!otherMember || otherMember.iscontact || requestContactSent || requestContactReceived" [priority]="100" [content]="'addon.messages.addtoyourcontacts' | translate" (action)="createContactRequest()"></core-context-menu-item>
|
||||
<core-context-menu-item [hidden]="!otherMember || !otherMember.iscontact" [priority]="100" [content]="'addon.messages.removefromyourcontacts' | translate" (action)="removeContact()"></core-context-menu-item>
|
||||
</core-context-menu>
|
||||
</core-navbar-buttons>
|
||||
</ion-header>
|
||||
|
@ -21,7 +25,7 @@
|
|||
<core-loading [hideUntil]="loaded">
|
||||
<!-- Load previous messages. -->
|
||||
<core-infinite-loading [enabled]="canLoadMore" (action)="loadPrevious($event)" position="top" [error]="loadMoreError"></core-infinite-loading>
|
||||
<ion-list class="addon-messages-discussion-container safe-area-page" [attr.aria-live]="polite">
|
||||
<ion-list class="addon-messages-discussion-container safe-area-page" [attr.aria-live]="'polite'">
|
||||
<ng-container *ngFor="let message of messages; index as index; last as last">
|
||||
<ion-chip *ngIf="message.showDate" class="addon-messages-date" color="light">
|
||||
<ion-label>{{ message.timecreated | coreFormatDate: "LL" }}</ion-label>
|
||||
|
@ -59,7 +63,25 @@
|
|||
</ion-content>
|
||||
<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>
|
||||
<p *ngIf="footerType == 'unable'" text-center margin-horizontal>{{ 'addon.messages.unabletomessage' | translate }}</p>
|
||||
<div *ngIf="footerType == 'blocked'" padding-horizontal>
|
||||
<p text-center>{{ 'addon.messages.youhaveblockeduser' | translate }}</p>
|
||||
<button ion-button block text-wrap margin-bottom (click)="unblockUser()">{{ 'addon.messages.unblockuser' | translate }}</button>
|
||||
</div>
|
||||
<div *ngIf="footerType == 'requiresContact'" padding-horizontal>
|
||||
<p text-center><strong>{{ 'addon.messages.isnotinyourcontacts' | translate: {$a: otherMember.fullname} }}</strong></p>
|
||||
<p text-center>{{ 'addon.messages.requirecontacttomessage' | translate: {$a: otherMember.fullname} }}</p>
|
||||
<button ion-button block text-wrap margin-bottom (click)="createContactRequest()">{{ 'addon.messages.sendcontactrequest' | translate }}</button>
|
||||
</div>
|
||||
<div *ngIf="footerType == 'requestReceived'" padding-horizontal>
|
||||
<p text-center>{{ 'addon.messages.userwouldliketocontactyou' | translate: {$a: otherMember.fullname} }}</p>
|
||||
<button ion-button block text-wrap margin-bottom (click)="confirmContactRequest()">{{ 'addon.messages.acceptandaddcontact' | translate }}</button>
|
||||
<button ion-button block text-wrap margin-bottom color="light" (click)="declineContactRequest()">{{ 'addon.messages.decline' | translate }}</button>
|
||||
</div>
|
||||
<div *ngIf="footerType == 'requestSent' || (footerType == 'message' && requestContactSent)" padding-horizontal>
|
||||
<p text-center><strong>{{ 'addon.messages.contactrequestsent' | translate }}</strong></p>
|
||||
<p text-center>{{ 'addon.messages.yourcontactrequestpending' | translate: {$a: otherMember.fullname} }}</p>
|
||||
</div>
|
||||
<core-send-message-form *ngIf="footerType == 'message'" (onSubmit)="sendMessage($event)" [showKeyboard]="showKeyboard" [placeholder]="'addon.messages.newmessage' | translate" (onResize)="resizeContent()"></core-send-message-form>
|
||||
</ion-toolbar>
|
||||
</ion-footer>
|
||||
|
|
|
@ -57,6 +57,8 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
protected keyboardObserver: any;
|
||||
protected scrollBottom = true;
|
||||
protected viewDestroyed = false;
|
||||
protected memberInfoObserver: any;
|
||||
protected showLoadingModal = false; // Whether to show a loading modal while fetching data.
|
||||
|
||||
conversationId: number; // Conversation ID. Undefined if it's a new individual conversation.
|
||||
conversation: any; // The conversation object (if it exists).
|
||||
|
@ -77,6 +79,10 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
members: any = {}; // Members that wrote a message, indexed by ID.
|
||||
favouriteIcon = 'fa-star';
|
||||
deleteIcon = 'trash';
|
||||
otherMember: any; // Other member information (individual conversations only).
|
||||
footerType: 'message' | 'blocked' | 'requiresContact' | 'requestSent' | 'requestReceived' | 'unable';
|
||||
requestContactSent = false;
|
||||
requestContactReceived = false;
|
||||
|
||||
constructor(private eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, navParams: NavParams,
|
||||
private userProvider: CoreUserProvider, private navCtrl: NavController, private messagesSync: AddonMessagesSyncProvider,
|
||||
|
@ -108,6 +114,13 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
}
|
||||
}
|
||||
}, this.siteId);
|
||||
|
||||
// Refresh data if info of a mamber of the conversation have changed.
|
||||
this.memberInfoObserver = eventsProvider.on(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, (data) => {
|
||||
if (data.userId && (this.members[data.userId] || this.otherMember && data.userId == this.otherMember.id)) {
|
||||
this.fetchData();
|
||||
}
|
||||
}, this.siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -160,6 +173,25 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
const backViewPage = this.navCtrl.getPrevious() && this.navCtrl.getPrevious().component.name;
|
||||
this.showInfo = !backViewPage || backViewPage !== 'CoreUserProfilePage';
|
||||
|
||||
// Recalculate footer position when keyboard is shown or hidden.
|
||||
this.keyboardObserver = this.eventsProvider.on(CoreEventsProvider.KEYBOARD_CHANGE, (kbHeight) => {
|
||||
this.content.resize();
|
||||
});
|
||||
|
||||
this.fetchData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to fetch the conversation data.
|
||||
*
|
||||
* @return {Promise<any>} Resolved when done.
|
||||
*/
|
||||
protected fetchData(): Promise<any> {
|
||||
let loader;
|
||||
if (this.showLoadingModal) {
|
||||
loader = this.domUtils.showModalLoading();
|
||||
}
|
||||
|
||||
if (!this.groupMessagingEnabled && this.userId) {
|
||||
// Get the user profile to retrieve the user fullname and image.
|
||||
this.userProvider.getProfile(this.userId, undefined, true).then((user) => {
|
||||
|
@ -171,7 +203,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
}
|
||||
|
||||
// Synchronize messages if needed.
|
||||
this.messagesSync.syncDiscussion(this.conversationId, this.userId).catch(() => {
|
||||
return this.messagesSync.syncDiscussion(this.conversationId, this.userId).catch(() => {
|
||||
// Ignore errors.
|
||||
}).then((warnings) => {
|
||||
if (warnings && warnings[0]) {
|
||||
|
@ -185,8 +217,22 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
// Fetch the messages for the first time.
|
||||
return this.fetchMessages();
|
||||
}
|
||||
}).then(() => {
|
||||
let promise;
|
||||
if (this.userId) {
|
||||
promise = this.messagesProvider.getMemberInfo(this.userId);
|
||||
} else {
|
||||
// Group conversation.
|
||||
promise = Promise.resolve(null);
|
||||
}
|
||||
|
||||
return promise.then((member) => {
|
||||
this.otherMember = member;
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.otherMember = null;
|
||||
|
||||
// Fetch the messages for the first time.
|
||||
return this.fetchMessages().then(() => {
|
||||
if (!this.title && this.messages.length) {
|
||||
|
@ -207,11 +253,9 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
this.resizeContent();
|
||||
this.loaded = true;
|
||||
this.setPolling(); // Make sure we're polling messages.
|
||||
});
|
||||
|
||||
// Recalculate footer position when keyboard is shown or hidden.
|
||||
this.keyboardObserver = this.eventsProvider.on(CoreEventsProvider.KEYBOARD_CHANGE, (kbHeight) => {
|
||||
this.content.resize();
|
||||
this.setContactRequestInfo();
|
||||
this.setFooterType();
|
||||
loader && loader.dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -985,6 +1029,71 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate whether there are pending contact requests.
|
||||
*/
|
||||
protected setContactRequestInfo(): void {
|
||||
this.requestContactSent = false;
|
||||
this.requestContactReceived = false;
|
||||
if (this.otherMember && !this.otherMember.iscontact) {
|
||||
this.requestContactSent = this.otherMember.contactrequests.some((request) => {
|
||||
return request.userid == this.currentUserId && request.requesteduserid == this.otherMember.id;
|
||||
});
|
||||
this.requestContactReceived = this.otherMember.contactrequests.some((request) => {
|
||||
return request.userid == this.otherMember.id && request.requesteduserid == this.currentUserId;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate what to display in the footer.
|
||||
*/
|
||||
protected setFooterType(): void {
|
||||
if (!this.otherMember) {
|
||||
// Group conversation or group messaging not available.
|
||||
this.footerType = 'message';
|
||||
} else if (this.otherMember.isblocked) {
|
||||
this.footerType = 'blocked';
|
||||
} else if (this.requestContactReceived) {
|
||||
this.footerType = 'requestReceived';
|
||||
} else if (this.otherMember.canmessage) {
|
||||
this.footerType = 'message';
|
||||
} else if (this.requestContactSent) {
|
||||
this.footerType = 'requestSent';
|
||||
} else if (this.otherMember.requirescontact) {
|
||||
this.footerType = 'requiresContact';
|
||||
} else {
|
||||
this.footerType = 'unable';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation modal to block the user of the individual conversation.
|
||||
*
|
||||
* @return {Promise<any>} Promise resolved when user is blocked or dialog is cancelled.
|
||||
*/
|
||||
blockUser(): Promise<any> {
|
||||
if (!this.otherMember) {
|
||||
// Should never happen.
|
||||
return Promise.reject(null);
|
||||
}
|
||||
|
||||
const template = this.translate.instant('addon.messages.blockuserconfirm', {$a: this.otherMember.fullname});
|
||||
const okText = this.translate.instant('addon.messages.blockuser');
|
||||
|
||||
return this.domUtils.showConfirm(template, undefined, okText).then(() => {
|
||||
const modal = this.domUtils.showModalLoading('core.sending', true);
|
||||
this.showLoadingModal = true;
|
||||
|
||||
return this.messagesProvider.blockContact(this.otherMember.id).finally(() => {
|
||||
modal.dismiss();
|
||||
this.showLoadingModal = false;
|
||||
});
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'core.error', true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the conversation.
|
||||
*
|
||||
|
@ -1012,6 +1121,131 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation modal to unblock the user of the individual conversation.
|
||||
*
|
||||
* @return {Promise<any>} Promise resolved when user is unblocked or dialog is cancelled.
|
||||
*/
|
||||
unblockUser(): Promise<any> {
|
||||
if (!this.otherMember) {
|
||||
// Should never happen.
|
||||
return Promise.reject(null);
|
||||
}
|
||||
|
||||
const template = this.translate.instant('addon.messages.unblockuserconfirm', {$a: this.otherMember.fullname});
|
||||
const okText = this.translate.instant('addon.messages.unblockuser');
|
||||
|
||||
return this.domUtils.showConfirm(template, undefined, okText).then(() => {
|
||||
const modal = this.domUtils.showModalLoading('core.sending', true);
|
||||
this.showLoadingModal = true;
|
||||
|
||||
return this.messagesProvider.unblockContact(this.otherMember.id).finally(() => {
|
||||
modal.dismiss();
|
||||
this.showLoadingModal = false;
|
||||
});
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'core.error', true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation modal to send a contact request to the other user of the individual conversation.
|
||||
*
|
||||
* @return {Promise<any>} Promise resolved when the request is sent or the dialog is cancelled.
|
||||
*/
|
||||
createContactRequest(): Promise<any> {
|
||||
if (!this.otherMember) {
|
||||
// Should never happen.
|
||||
return Promise.reject(null);
|
||||
}
|
||||
|
||||
const template = this.translate.instant('addon.messages.addcontactconfirm', { $a: this.otherMember.fullname });
|
||||
const okText = this.translate.instant('core.add');
|
||||
|
||||
return this.domUtils.showConfirm(template, undefined, okText).then(() => {
|
||||
const modal = this.domUtils.showModalLoading('core.sending', true);
|
||||
this.showLoadingModal = true;
|
||||
|
||||
return this.messagesProvider.createContactRequest(this.otherMember.id).finally(() => {
|
||||
modal.dismiss();
|
||||
this.showLoadingModal = false;
|
||||
});
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'core.error', true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms the contact request of the other user of the individual conversation.
|
||||
*
|
||||
* @return {Promise<any>} Promise resolved when the request is confirmed.
|
||||
*/
|
||||
confirmContactRequest(): Promise<any> {
|
||||
if (!this.otherMember) {
|
||||
// Should never happen.
|
||||
return Promise.reject(null);
|
||||
}
|
||||
|
||||
const modal = this.domUtils.showModalLoading('core.sending', true);
|
||||
this.showLoadingModal = true;
|
||||
|
||||
return this.messagesProvider.confirmContactRequest(this.otherMember.id).finally(() => {
|
||||
modal.dismiss();
|
||||
this.showLoadingModal = false;
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'core.error', true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Declines the contact request of the other user of the individual conversation.
|
||||
*
|
||||
* @return {Promise<any>} Promise resolved when the request is confirmed.
|
||||
*/
|
||||
declineContactRequest(): Promise<any> {
|
||||
if (!this.otherMember) {
|
||||
// Should never happen.
|
||||
return Promise.reject(null);
|
||||
}
|
||||
|
||||
const modal = this.domUtils.showModalLoading('core.sending', true);
|
||||
this.showLoadingModal = true;
|
||||
|
||||
return this.messagesProvider.declineContactRequest(this.otherMember.id).finally(() => {
|
||||
modal.dismiss();
|
||||
this.showLoadingModal = false;
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'core.error', true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation modal to remove the other user of the conversation from contacts.
|
||||
*
|
||||
* @return {Promise<any>} Promise resolved when the request is sent or the dialog is cancelled.
|
||||
*/
|
||||
removeContact(): Promise<any> {
|
||||
if (!this.otherMember) {
|
||||
// Should never happen.
|
||||
return Promise.reject(null);
|
||||
}
|
||||
|
||||
const template = this.translate.instant('addon.messages.removecontactconfirm', { $a: this.otherMember.fullname });
|
||||
const okText = this.translate.instant('core.remove');
|
||||
|
||||
return this.domUtils.showConfirm(template, undefined, okText).then(() => {
|
||||
const modal = this.domUtils.showModalLoading('core.sending', true);
|
||||
this.showLoadingModal = true;
|
||||
|
||||
return this.messagesProvider.removeContact(this.otherMember.id).finally(() => {
|
||||
modal.dismiss();
|
||||
this.showLoadingModal = false;
|
||||
});
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'core.error', true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Page destroyed.
|
||||
*/
|
||||
|
@ -1020,6 +1254,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
this.unsetPolling();
|
||||
this.syncObserver && this.syncObserver.off();
|
||||
this.keyboardObserver && this.keyboardObserver.off();
|
||||
this.memberInfoObserver && this.memberInfoObserver.off();
|
||||
this.viewDestroyed = true;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue