commit
b2c5bbc0c7
|
@ -147,8 +147,11 @@
|
||||||
"addon.files.privatefiles": "moodle",
|
"addon.files.privatefiles": "moodle",
|
||||||
"addon.files.sitefiles": "moodle",
|
"addon.files.sitefiles": "moodle",
|
||||||
"addon.messageoutput_airnotifier.processorsettingsdesc": "local_moodlemobileapp",
|
"addon.messageoutput_airnotifier.processorsettingsdesc": "local_moodlemobileapp",
|
||||||
|
"addon.messages.acceptandaddcontact": "message",
|
||||||
"addon.messages.addcontact": "message",
|
"addon.messages.addcontact": "message",
|
||||||
|
"addon.messages.addcontactconfirm": "message",
|
||||||
"addon.messages.addtofavourites": "message",
|
"addon.messages.addtofavourites": "message",
|
||||||
|
"addon.messages.addtoyourcontacts": "message",
|
||||||
"addon.messages.blocknoncontacts": "message",
|
"addon.messages.blocknoncontacts": "message",
|
||||||
"addon.messages.blockuser": "message",
|
"addon.messages.blockuser": "message",
|
||||||
"addon.messages.blockuserconfirm": "message",
|
"addon.messages.blockuserconfirm": "message",
|
||||||
|
@ -159,7 +162,9 @@
|
||||||
"addon.messages.contactblocked": "message",
|
"addon.messages.contactblocked": "message",
|
||||||
"addon.messages.contactlistempty": "local_moodlemobileapp",
|
"addon.messages.contactlistempty": "local_moodlemobileapp",
|
||||||
"addon.messages.contactname": "local_moodlemobileapp",
|
"addon.messages.contactname": "local_moodlemobileapp",
|
||||||
|
"addon.messages.contactrequestsent": "message",
|
||||||
"addon.messages.contacts": "message",
|
"addon.messages.contacts": "message",
|
||||||
|
"addon.messages.decline": "message",
|
||||||
"addon.messages.deleteallconfirm": "message",
|
"addon.messages.deleteallconfirm": "message",
|
||||||
"addon.messages.deleteconversationq": "message",
|
"addon.messages.deleteconversationq": "message",
|
||||||
"addon.messages.deletemessage": "local_moodlemobileapp",
|
"addon.messages.deletemessage": "local_moodlemobileapp",
|
||||||
|
@ -168,34 +173,51 @@
|
||||||
"addon.messages.errorwhileretrievingcontacts": "local_moodlemobileapp",
|
"addon.messages.errorwhileretrievingcontacts": "local_moodlemobileapp",
|
||||||
"addon.messages.errorwhileretrievingdiscussions": "local_moodlemobileapp",
|
"addon.messages.errorwhileretrievingdiscussions": "local_moodlemobileapp",
|
||||||
"addon.messages.errorwhileretrievingmessages": "local_moodlemobileapp",
|
"addon.messages.errorwhileretrievingmessages": "local_moodlemobileapp",
|
||||||
|
"addon.messages.errorwhileretrievingusers": "local_moodlemobileapp",
|
||||||
"addon.messages.groupinfo": "message",
|
"addon.messages.groupinfo": "message",
|
||||||
"addon.messages.groupmessages": "message",
|
"addon.messages.groupmessages": "message",
|
||||||
"addon.messages.info": "message",
|
"addon.messages.info": "message",
|
||||||
|
"addon.messages.isnotinyourcontacts": "message",
|
||||||
"addon.messages.message": "message",
|
"addon.messages.message": "message",
|
||||||
"addon.messages.messagenotsent": "local_moodlemobileapp",
|
"addon.messages.messagenotsent": "local_moodlemobileapp",
|
||||||
"addon.messages.messagepreferences": "message",
|
"addon.messages.messagepreferences": "message",
|
||||||
"addon.messages.messages": "message",
|
"addon.messages.messages": "message",
|
||||||
"addon.messages.newmessage": "message",
|
"addon.messages.newmessage": "message",
|
||||||
"addon.messages.newmessages": "local_moodlemobileapp",
|
"addon.messages.newmessages": "local_moodlemobileapp",
|
||||||
|
"addon.messages.nocontactrequests": "message",
|
||||||
|
"addon.messages.noncontacts": "message",
|
||||||
|
"addon.messages.nocontactsgetstarted": "message",
|
||||||
"addon.messages.nofavourites": "message",
|
"addon.messages.nofavourites": "message",
|
||||||
"addon.messages.nogroupmessages": "message",
|
"addon.messages.nogroupmessages": "message",
|
||||||
"addon.messages.nomessages": "message",
|
"addon.messages.nomessages": "message",
|
||||||
"addon.messages.nousersfound": "local_moodlemobileapp",
|
"addon.messages.nousersfound": "local_moodlemobileapp",
|
||||||
"addon.messages.numparticipants": "message",
|
"addon.messages.numparticipants": "message",
|
||||||
"addon.messages.removecontact": "message",
|
"addon.messages.removecontact": "message",
|
||||||
"addon.messages.removecontactconfirm": "local_moodlemobileapp",
|
"addon.messages.removecontactconfirm": "message",
|
||||||
|
"addon.messages.removefromyourcontacts": "message",
|
||||||
"addon.messages.removefromfavourites": "message",
|
"addon.messages.removefromfavourites": "message",
|
||||||
|
"addon.messages.requests": "moodle",
|
||||||
|
"addon.messages.requirecontacttomessage": "message",
|
||||||
|
"addon.messages.searchcombined": "message",
|
||||||
|
"addon.messages.searchnocontactsfound": "message",
|
||||||
|
"addon.messages.searchnomessagesfound": "message",
|
||||||
|
"addon.messages.searchnononcontactsfound": "message",
|
||||||
"addon.messages.showdeletemessages": "local_moodlemobileapp",
|
"addon.messages.showdeletemessages": "local_moodlemobileapp",
|
||||||
|
"addon.messages.sendcontactrequest": "message",
|
||||||
"addon.messages.type_blocked": "local_moodlemobileapp",
|
"addon.messages.type_blocked": "local_moodlemobileapp",
|
||||||
"addon.messages.type_offline": "local_moodlemobileapp",
|
"addon.messages.type_offline": "local_moodlemobileapp",
|
||||||
"addon.messages.type_online": "local_moodlemobileapp",
|
"addon.messages.type_online": "local_moodlemobileapp",
|
||||||
"addon.messages.type_search": "local_moodlemobileapp",
|
"addon.messages.type_search": "local_moodlemobileapp",
|
||||||
"addon.messages.type_strangers": "local_moodlemobileapp",
|
"addon.messages.type_strangers": "local_moodlemobileapp",
|
||||||
|
"addon.messages.unabletomessage": "message",
|
||||||
"addon.messages.unblockuser": "message",
|
"addon.messages.unblockuser": "message",
|
||||||
"addon.messages.unblockuserconfirm": "message",
|
"addon.messages.unblockuserconfirm": "message",
|
||||||
|
"addon.messages.userwouldliketocontactyou": "message",
|
||||||
"addon.messages.warningconversationmessagenotsent": "local_moodlemobileapp",
|
"addon.messages.warningconversationmessagenotsent": "local_moodlemobileapp",
|
||||||
"addon.messages.warningmessagenotsent": "local_moodlemobileapp",
|
"addon.messages.warningmessagenotsent": "local_moodlemobileapp",
|
||||||
|
"addon.messages.wouldliketocontactyou": "message",
|
||||||
"addon.messages.you": "message",
|
"addon.messages.you": "message",
|
||||||
|
"addon.messages.youhaveblockeduser": "message",
|
||||||
"addon.mod_assign.acceptsubmissionstatement": "local_moodlemobileapp",
|
"addon.mod_assign.acceptsubmissionstatement": "local_moodlemobileapp",
|
||||||
"addon.mod_assign.addattempt": "assign",
|
"addon.mod_assign.addattempt": "assign",
|
||||||
"addon.mod_assign.addnewattempt": "assign",
|
"addon.mod_assign.addnewattempt": "assign",
|
||||||
|
@ -1540,6 +1562,7 @@
|
||||||
"core.quotausage": "moodle",
|
"core.quotausage": "moodle",
|
||||||
"core.redirectingtosite": "local_moodlemobileapp",
|
"core.redirectingtosite": "local_moodlemobileapp",
|
||||||
"core.refresh": "moodle",
|
"core.refresh": "moodle",
|
||||||
|
"core.remove": "moodle",
|
||||||
"core.required": "moodle",
|
"core.required": "moodle",
|
||||||
"core.requireduserdatamissing": "local_moodlemobileapp",
|
"core.requireduserdatamissing": "local_moodlemobileapp",
|
||||||
"core.resources": "moodle",
|
"core.resources": "moodle",
|
||||||
|
|
|
@ -20,11 +20,15 @@ import { CoreComponentsModule } from '@components/components.module';
|
||||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
import { CorePipesModule } from '@pipes/pipes.module';
|
import { CorePipesModule } from '@pipes/pipes.module';
|
||||||
import { AddonMessagesDiscussionsComponent } from '../components/discussions/discussions';
|
import { AddonMessagesDiscussionsComponent } from '../components/discussions/discussions';
|
||||||
|
import { AddonMessagesConfirmedContactsComponent } from '../components/confirmed-contacts/confirmed-contacts';
|
||||||
|
import { AddonMessagesContactRequestsComponent } from '../components/contact-requests/contact-requests';
|
||||||
import { AddonMessagesContactsComponent } from '../components/contacts/contacts';
|
import { AddonMessagesContactsComponent } from '../components/contacts/contacts';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AddonMessagesDiscussionsComponent,
|
AddonMessagesDiscussionsComponent,
|
||||||
|
AddonMessagesConfirmedContactsComponent,
|
||||||
|
AddonMessagesContactRequestsComponent,
|
||||||
AddonMessagesContactsComponent
|
AddonMessagesContactsComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -39,6 +43,8 @@ import { AddonMessagesContactsComponent } from '../components/contacts/contacts'
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
AddonMessagesDiscussionsComponent,
|
AddonMessagesDiscussionsComponent,
|
||||||
|
AddonMessagesConfirmedContactsComponent,
|
||||||
|
AddonMessagesContactRequestsComponent,
|
||||||
AddonMessagesContactsComponent
|
AddonMessagesContactsComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
<ion-content>
|
||||||
|
<ion-refresher [enabled]="loaded" (ionRefresh)="refreshData($event)">
|
||||||
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
|
</ion-refresher>
|
||||||
|
<core-loading [hideUntil]="loaded" class="core-loading-center">
|
||||||
|
<ion-list no-margin>
|
||||||
|
<a ion-item text-wrap *ngFor="let contact of contacts" [title]="contact.fullname" (click)="selectUser(contact.id)" [class.core-split-item-selected]="contact.id == selectedUserId" detail-none>
|
||||||
|
<ion-avatar item-start core-user-avatar [user]="contact" [checkOnline]="true" [linkProfile]="false"></ion-avatar>
|
||||||
|
<h2><core-format-text [text]="contact.fullname"></core-format-text></h2>
|
||||||
|
<core-icon *ngIf="contact.isblocked" name="fa-ban" item-end></core-icon>
|
||||||
|
</a>
|
||||||
|
</ion-list>
|
||||||
|
<core-empty-box *ngIf="!contacts.length" icon="person" [message]="'addon.messages.nocontactsgetstarted' | translate"></core-empty-box>
|
||||||
|
<core-infinite-loading [enabled]="canLoadMore" (action)="loadMore($event)" [error]="loadMoreError" position="bottom"></core-infinite-loading>
|
||||||
|
</core-loading>
|
||||||
|
</ion-content>
|
|
@ -0,0 +1,141 @@
|
||||||
|
// (C) Copyright 2015 Martin Dougiamas
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core';
|
||||||
|
import { Content } from 'ionic-angular';
|
||||||
|
import { CoreEventsProvider } from '@providers/events';
|
||||||
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
|
import { AddonMessagesProvider } from '../../providers/messages';
|
||||||
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that displays the list of confirmed contacts.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'addon-messages-confirmed-contacts',
|
||||||
|
templateUrl: 'addon-messages-confirmed-contacts.html',
|
||||||
|
})
|
||||||
|
export class AddonMessagesConfirmedContactsComponent implements OnInit, OnDestroy {
|
||||||
|
@Output() onUserSelected = new EventEmitter<{userId: number, onInit?: boolean}>();
|
||||||
|
@ViewChild(Content) content: Content;
|
||||||
|
|
||||||
|
loaded = false;
|
||||||
|
canLoadMore = false;
|
||||||
|
loadMoreError = false;
|
||||||
|
contacts = [];
|
||||||
|
selectedUserId: number;
|
||||||
|
|
||||||
|
protected memberInfoObserver;
|
||||||
|
|
||||||
|
constructor(private domUtils: CoreDomUtilsProvider, eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider,
|
||||||
|
private messagesProvider: AddonMessagesProvider) {
|
||||||
|
|
||||||
|
this.onUserSelected = new EventEmitter();
|
||||||
|
|
||||||
|
// Update block status of a user.
|
||||||
|
this.memberInfoObserver = eventsProvider.on(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, (data) => {
|
||||||
|
if (data.userBlocked || data.userUnblocked) {
|
||||||
|
const user = this.contacts.find((user) => user.id == data.userId);
|
||||||
|
if (user) {
|
||||||
|
user.isblocked = data.userBlocked;
|
||||||
|
}
|
||||||
|
} else if (data.contactRemoved) {
|
||||||
|
const index = this.contacts.findIndex((contact) => contact.id == data.userId);
|
||||||
|
if (index >= 0) {
|
||||||
|
this.contacts.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, sitesProvider.getCurrentSiteId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component loaded.
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.fetchData().then(() => {
|
||||||
|
if (this.contacts.length) {
|
||||||
|
this.selectUser(this.contacts[0].id, true);
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
this.loaded = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Workaround for infinite scrolling.
|
||||||
|
this.content.resize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch contacts.
|
||||||
|
*
|
||||||
|
* @param {boolean} [refresh=false] True if we are refreshing contacts, false if we are loading more.
|
||||||
|
* @return {Promise<any>} Promise resolved when done.
|
||||||
|
*/
|
||||||
|
fetchData(refresh: boolean = false): Promise<any> {
|
||||||
|
this.loadMoreError = false;
|
||||||
|
|
||||||
|
const limitFrom = refresh ? 0 : this.contacts.length;
|
||||||
|
|
||||||
|
return this.messagesProvider.getUserContacts(limitFrom).then((result) => {
|
||||||
|
this.contacts = refresh ? result.contacts : this.contacts.concat(result.contacts);
|
||||||
|
this.canLoadMore = result.canLoadMore;
|
||||||
|
}).catch((error) => {
|
||||||
|
this.loadMoreError = true;
|
||||||
|
this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingcontacts', true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh contacts.
|
||||||
|
*
|
||||||
|
* @param {any} [refresher] Refresher.
|
||||||
|
* @return {Promise<any>} Promise resolved when done.
|
||||||
|
*/
|
||||||
|
refreshData(refresher?: any): Promise<any> {
|
||||||
|
return this.messagesProvider.invalidateUserContacts().then(() => {
|
||||||
|
return this.fetchData(true);
|
||||||
|
}).finally(() => {
|
||||||
|
refresher && refresher.complete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load more contacts.
|
||||||
|
*
|
||||||
|
* @param {any} [infiniteComplete] Infinite scroll complete function. Only used from core-infinite-loading.
|
||||||
|
* @return {Promise<any>} Resolved when done.
|
||||||
|
*/
|
||||||
|
loadMore(infiniteComplete?: any): Promise<any> {
|
||||||
|
return this.fetchData().finally(() => {
|
||||||
|
infiniteComplete && infiniteComplete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify that a contact has been selected.
|
||||||
|
*
|
||||||
|
* @param {number} userId User id.
|
||||||
|
* @param {boolean} [onInit=false] Whether the contact is selected on initial load.
|
||||||
|
*/
|
||||||
|
selectUser(userId: number, onInit: boolean = false): void {
|
||||||
|
this.selectedUserId = userId;
|
||||||
|
this.onUserSelected.emit({userId, onInit});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component destroyed.
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.memberInfoObserver && this.memberInfoObserver.off();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
<ion-content>
|
||||||
|
<ion-refresher [enabled]="loaded" (ionRefresh)="refreshData($event)">
|
||||||
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
|
</ion-refresher>
|
||||||
|
<core-loading [hideUntil]="loaded" class="core-loading-center">
|
||||||
|
<ion-list no-margin>
|
||||||
|
<a ion-item text-wrap *ngFor="let request of requests" [title]="request.fullname" (click)="selectUser(request.id)" [class.core-split-item-selected]="request.id == selectedUserId" detail-none>
|
||||||
|
<ion-avatar item-start core-user-avatar [user]="request" [checkOnline]="true" [linkProfile]="false"></ion-avatar>
|
||||||
|
<h2><core-format-text [text]="request.fullname"></core-format-text></h2>
|
||||||
|
<p *ngIf="!request.iscontact && !request.confirmedOrDeclined">{{ 'addon.messages.wouldliketocontactyou' | translate }}</p>
|
||||||
|
</a>
|
||||||
|
</ion-list>
|
||||||
|
<core-empty-box *ngIf="!requests.length" icon="person" [message]="'addon.messages.nocontactrequests' | translate"></core-empty-box>
|
||||||
|
<core-infinite-loading [enabled]="canLoadMore" (action)="loadMore($event)" [error]="loadMoreError" position="bottom"></core-infinite-loading>
|
||||||
|
</core-loading>
|
||||||
|
</ion-content>
|
|
@ -0,0 +1,137 @@
|
||||||
|
// (C) Copyright 2015 Martin Dougiamas
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core';
|
||||||
|
import { Content } from 'ionic-angular';
|
||||||
|
import { CoreEventsProvider } from '@providers/events';
|
||||||
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
|
import { AddonMessagesProvider } from '../../providers/messages';
|
||||||
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that displays the list of contact requests.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'addon-messages-contact-requests',
|
||||||
|
templateUrl: 'addon-messages-contact-requests.html',
|
||||||
|
})
|
||||||
|
export class AddonMessagesContactRequestsComponent implements OnInit, OnDestroy {
|
||||||
|
@Output() onUserSelected = new EventEmitter<{userId: number, onInit?: boolean}>();
|
||||||
|
@ViewChild(Content) content: Content;
|
||||||
|
|
||||||
|
loaded = false;
|
||||||
|
canLoadMore = false;
|
||||||
|
loadMoreError = false;
|
||||||
|
requests = [];
|
||||||
|
selectedUserId: number;
|
||||||
|
|
||||||
|
protected memberInfoObserver;
|
||||||
|
|
||||||
|
constructor(private domUtils: CoreDomUtilsProvider, eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider,
|
||||||
|
private messagesProvider: AddonMessagesProvider) {
|
||||||
|
|
||||||
|
// Hide the "Would like to contact you" message when a contact request is confirmed.
|
||||||
|
this.memberInfoObserver = eventsProvider.on(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, (data) => {
|
||||||
|
if (data.contactRequestConfirmed || data.contactRequestDeclined) {
|
||||||
|
const index = this.requests.findIndex((request) => request.id == data.userId);
|
||||||
|
if (index >= 0) {
|
||||||
|
this.requests.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, sitesProvider.getCurrentSiteId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component loaded.
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.fetchData().then(() => {
|
||||||
|
if (this.requests.length) {
|
||||||
|
this.selectUser(this.requests[0].id, true);
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
this.loaded = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Workaround for infinite scrolling.
|
||||||
|
this.content.resize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch contact requests.
|
||||||
|
*
|
||||||
|
* @param {boolean} [refresh=false] True if we are refreshing contact requests, false if we are loading more.
|
||||||
|
* @return {Promise<any>} Promise resolved when done.
|
||||||
|
*/
|
||||||
|
fetchData(refresh: boolean = false): Promise<any> {
|
||||||
|
this.loadMoreError = false;
|
||||||
|
|
||||||
|
const limitFrom = refresh ? 0 : this.requests.length;
|
||||||
|
|
||||||
|
return this.messagesProvider.getContactRequests(limitFrom).then((result) => {
|
||||||
|
this.requests = refresh ? result.requests : this.requests.concat(result.requests);
|
||||||
|
this.canLoadMore = result.canLoadMore;
|
||||||
|
}).catch((error) => {
|
||||||
|
this.loadMoreError = true;
|
||||||
|
this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingcontacts', true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh contact requests.
|
||||||
|
*
|
||||||
|
* @param {any} [refresher] Refresher.
|
||||||
|
* @return {Promise<any>} Promise resolved when done.
|
||||||
|
*/
|
||||||
|
refreshData(refresher?: any): Promise<any> {
|
||||||
|
// Refresh the number of contacts requests to update badges.
|
||||||
|
this.messagesProvider.refreshContactRequestsCount();
|
||||||
|
|
||||||
|
return this.messagesProvider.invalidateContactRequestsCache().then(() => {
|
||||||
|
return this.fetchData(true);
|
||||||
|
}).finally(() => {
|
||||||
|
refresher && refresher.complete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load more contact requests.
|
||||||
|
*
|
||||||
|
* @param {any} [infiniteComplete] Infinite scroll complete function. Only used from core-infinite-loading.
|
||||||
|
* @return {Promise<any>} Resolved when done.
|
||||||
|
*/
|
||||||
|
loadMore(infiniteComplete?: any): Promise<any> {
|
||||||
|
return this.fetchData().finally(() => {
|
||||||
|
infiniteComplete && infiniteComplete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify that a contact has been selected.
|
||||||
|
*
|
||||||
|
* @param {number} userId User id.
|
||||||
|
* @param {boolean} [onInit=false] Whether the contact is selected on initial load.
|
||||||
|
*/
|
||||||
|
selectUser(userId: number, onInit: boolean = false): void {
|
||||||
|
this.selectedUserId = userId;
|
||||||
|
this.onUserSelected.emit({userId, onInit});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component destroyed.
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.memberInfoObserver && this.memberInfoObserver.off();
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,7 @@
|
||||||
<h2>{{ 'core.searchresults' | translate }}</h2>
|
<h2>{{ 'core.searchresults' | translate }}</h2>
|
||||||
<ion-note item-end>{{ search.results.length }}</ion-note>
|
<ion-note item-end>{{ search.results.length }}</ion-note>
|
||||||
</ion-item-divider>
|
</ion-item-divider>
|
||||||
<a ion-item text-wrap *ngFor="let result of search.results" [title]="result.fullname" (click)="gotoDiscussion(result.userid, result.messageid)" [class.core-split-item-selected]="result.userid == discussionUserId" detail-none>
|
<a ion-item text-wrap *ngFor="let result of search.results" [title]="result.fullname" (click)="gotoDiscussion(result.userid, result.messageid)" [class.core-split-item-selected]="result.userid == discussionUserId" class="addon-message-discussion">
|
||||||
<ion-avatar core-user-avatar [user]="result" item-start></ion-avatar>
|
<ion-avatar core-user-avatar [user]="result" item-start></ion-avatar>
|
||||||
<h2><core-format-text [text]="result.fullname"></core-format-text></h2>
|
<h2><core-format-text [text]="result.fullname"></core-format-text></h2>
|
||||||
<p><core-format-text clean="true" singleLine="true" [text]="result.lastmessage"></core-format-text></p>
|
<p><core-format-text clean="true" singleLine="true" [text]="result.lastmessage"></core-format-text></p>
|
||||||
|
@ -20,15 +20,15 @@
|
||||||
</ion-list>
|
</ion-list>
|
||||||
|
|
||||||
<ion-list *ngIf="!search.showResults" no-margin>
|
<ion-list *ngIf="!search.showResults" no-margin>
|
||||||
<a ion-item text-wrap *ngFor="let discussion of discussions" [title]="discussion.fullname" (click)="gotoDiscussion(discussion.message.user)" [class.core-split-item-selected]="discussion.message.user == discussionUserId" detail-none>
|
<a ion-item text-wrap *ngFor="let discussion of discussions" [title]="discussion.fullname" (click)="gotoDiscussion(discussion.message.user)" [class.core-split-item-selected]="discussion.message.user == discussionUserId" class="addon-message-discussion">
|
||||||
<ion-avatar core-user-avatar [user]="discussion" item-start></ion-avatar>
|
<ion-avatar core-user-avatar [user]="discussion" item-start></ion-avatar>
|
||||||
<h2>
|
<h2>
|
||||||
<core-format-text [text]="discussion.fullname"></core-format-text>
|
<core-format-text [text]="discussion.fullname"></core-format-text>
|
||||||
|
</h2>
|
||||||
<ion-note *ngIf="discussion.message.timecreated > 0 || discussion.unread">
|
<ion-note *ngIf="discussion.message.timecreated > 0 || discussion.unread">
|
||||||
<span *ngIf="discussion.unread" class="core-primary-circle"></span>
|
<span *ngIf="discussion.unread" class="core-primary-circle"></span>
|
||||||
<span *ngIf="discussion.message.timecreated > 0">{{discussion.message.timecreated / 1000 | coreDateDayOrTime}}</span>
|
<span *ngIf="discussion.message.timecreated > 0">{{discussion.message.timecreated / 1000 | coreDateDayOrTime}}</span>
|
||||||
</ion-note>
|
</ion-note>
|
||||||
</h2>
|
|
||||||
<p><core-format-text clean="true" singleLine="true" [text]="discussion.message.message"></core-format-text></p>
|
<p><core-format-text clean="true" singleLine="true" [text]="discussion.message.message"></core-format-text></p>
|
||||||
</a>
|
</a>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
ion-app.app-root addon-messages-discussions {
|
ion-app.app-root .addon-message-discussion {
|
||||||
h2 {
|
h2 {
|
||||||
display: flex;
|
margin-top: 6px;
|
||||||
justify-content: space-between;
|
core-format-text {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.note {
|
.note {
|
||||||
margin: 0;
|
position: absolute;
|
||||||
align-self: flex-end;
|
@include position(0, 0, null, null);
|
||||||
display: inline-flex;
|
margin: 4px 8px;
|
||||||
font-size: initial;
|
font-size: 1.3rem;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -209,7 +209,7 @@ export class AddonMessagesDiscussionsComponent implements OnDestroy {
|
||||||
|
|
||||||
return this.messagesProvider.searchMessages(query).then((searchResults) => {
|
return this.messagesProvider.searchMessages(query).then((searchResults) => {
|
||||||
this.search.showResults = true;
|
this.search.showResults = true;
|
||||||
this.search.results = searchResults;
|
this.search.results = searchResults.messages;
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingmessages', true);
|
this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingmessages', true);
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
|
"acceptandaddcontact": "Accept and add to contacts",
|
||||||
"addcontact": "Add contact",
|
"addcontact": "Add contact",
|
||||||
|
"addcontactconfirm": "Are you sure you want to add {{$a}} to your contacts?",
|
||||||
"addtofavourites": "Star",
|
"addtofavourites": "Star",
|
||||||
|
"addtoyourcontacts": "Add to contacts",
|
||||||
"blocknoncontacts": "Prevent non-contacts from messaging me",
|
"blocknoncontacts": "Prevent non-contacts from messaging me",
|
||||||
"blockuser": "Block user",
|
"blockuser": "Block user",
|
||||||
"blockuserconfirm": "Are you sure you want to block {{$a}}?",
|
"blockuserconfirm": "Are you sure you want to block {{$a}}?",
|
||||||
|
@ -11,7 +14,9 @@
|
||||||
"contactblocked": "Contact blocked",
|
"contactblocked": "Contact blocked",
|
||||||
"contactlistempty": "The contact list is empty",
|
"contactlistempty": "The contact list is empty",
|
||||||
"contactname": "Contact name",
|
"contactname": "Contact name",
|
||||||
|
"contactrequestsent": "Contact request sent",
|
||||||
"contacts": "Contacts",
|
"contacts": "Contacts",
|
||||||
|
"decline": "Decline",
|
||||||
"deleteallconfirm": "Are you sure you would like to delete this entire conversation? This will not delete it for other conversation participants.",
|
"deleteallconfirm": "Are you sure you would like to delete this entire conversation? This will not delete it for other conversation participants.",
|
||||||
"deleteconversation": "Delete conversation",
|
"deleteconversation": "Delete conversation",
|
||||||
"deletemessage": "Delete message",
|
"deletemessage": "Delete message",
|
||||||
|
@ -20,32 +25,50 @@
|
||||||
"errorwhileretrievingcontacts": "Error while retrieving contacts from the server.",
|
"errorwhileretrievingcontacts": "Error while retrieving contacts from the server.",
|
||||||
"errorwhileretrievingdiscussions": "Error while retrieving discussions from the server.",
|
"errorwhileretrievingdiscussions": "Error while retrieving discussions from the server.",
|
||||||
"errorwhileretrievingmessages": "Error while retrieving messages from the server.",
|
"errorwhileretrievingmessages": "Error while retrieving messages from the server.",
|
||||||
|
"errorwhileretrievingusers": "Error while retrieving users from the server.",
|
||||||
"groupinfo": "Group info",
|
"groupinfo": "Group info",
|
||||||
"groupmessages": "Group messages",
|
"groupmessages": "Group messages",
|
||||||
"info": "Info",
|
"info": "Info",
|
||||||
|
"isnotinyourcontacts": "{{$a}} is not in your contacts",
|
||||||
"messagenotsent": "The message was not sent. Please try again later.",
|
"messagenotsent": "The message was not sent. Please try again later.",
|
||||||
"message": "Message",
|
"message": "Message",
|
||||||
"messagepreferences": "Message preferences",
|
"messagepreferences": "Message preferences",
|
||||||
"messages": "Messages",
|
"messages": "Messages",
|
||||||
"newmessage": "New message",
|
"newmessage": "New message",
|
||||||
"newmessages": "New messages",
|
"newmessages": "New messages",
|
||||||
|
"nocontactrequests": "No contact requests",
|
||||||
|
"noncontacts": "Non-contacts",
|
||||||
|
"nocontactsgetstarted": "Try searching for someone to add them as a contact",
|
||||||
"nofavourites": "No favourites",
|
"nofavourites": "No favourites",
|
||||||
"nogroupmessages": "No group messages",
|
"nogroupmessages": "No group messages",
|
||||||
"nomessages": "No messages",
|
"nomessages": "No messages",
|
||||||
"nousersfound": "No users found",
|
"nousersfound": "No users found",
|
||||||
"numparticipants": "{{$a}} participants",
|
"numparticipants": "{{$a}} participants",
|
||||||
"removecontact": "Remove contact",
|
"removecontact": "Remove contact",
|
||||||
"removecontactconfirm": "Contact will be removed from your contacts list.",
|
"removecontactconfirm": "Are you sure you want to remove {{$a}} from your contacts?",
|
||||||
|
"removefromyourcontacts": "Remove from contacts",
|
||||||
"removefromfavourites": "Unstar",
|
"removefromfavourites": "Unstar",
|
||||||
|
"requests": "Requests",
|
||||||
|
"requirecontacttomessage": "You need to request {{$a}} to add you as a contact to be able to message them.",
|
||||||
|
"searchcombined": "Search people and messages",
|
||||||
|
"searchnocontactsfound": "No contacts found",
|
||||||
|
"searchnomessagesfound": "No messages found",
|
||||||
|
"searchnononcontactsfound": "No non contacts found",
|
||||||
|
"sendcontactrequest": "Send contact request",
|
||||||
"showdeletemessages": "Show delete messages",
|
"showdeletemessages": "Show delete messages",
|
||||||
"type_blocked": "Blocked",
|
"type_blocked": "Blocked",
|
||||||
"type_offline": "Offline",
|
"type_offline": "Offline",
|
||||||
"type_online": "Online",
|
"type_online": "Online",
|
||||||
"type_search": "Search results",
|
"type_search": "Search results",
|
||||||
"type_strangers": "Others",
|
"type_strangers": "Others",
|
||||||
|
"unabletomessage": "You are unable to message this user",
|
||||||
"unblockuser": "Unblock user",
|
"unblockuser": "Unblock user",
|
||||||
"unblockuserconfirm": "Are you sure you want to unblock {{$a}}?",
|
"unblockuserconfirm": "Are you sure you want to unblock {{$a}}?",
|
||||||
|
"userwouldliketocontactyou": "{{$a}} would like to contact you",
|
||||||
"warningconversationmessagenotsent": "Couldn't send message(s) to conversation {{conversation}}. {{error}}",
|
"warningconversationmessagenotsent": "Couldn't send message(s) to conversation {{conversation}}. {{error}}",
|
||||||
"warningmessagenotsent": "Couldn't send message(s) to user {{user}}. {{error}}",
|
"warningmessagenotsent": "Couldn't send message(s) to user {{user}}. {{error}}",
|
||||||
"you": "You:"
|
"wouldliketocontactyou": "Would like to contact you",
|
||||||
|
"you": "You:",
|
||||||
|
"youhaveblockeduser": "You have blocked this user in the past",
|
||||||
|
"yourcontactrequestpending": "Your contact request is pending with {{$a}}"
|
||||||
}
|
}
|
|
@ -25,6 +25,7 @@ import { CoreCronDelegate } from '@providers/cron';
|
||||||
import { AddonMessagesSendMessageUserHandler } from './providers/user-send-message-handler';
|
import { AddonMessagesSendMessageUserHandler } from './providers/user-send-message-handler';
|
||||||
import { AddonMessagesAddContactUserHandler } from './providers/user-add-contact-handler';
|
import { AddonMessagesAddContactUserHandler } from './providers/user-add-contact-handler';
|
||||||
import { AddonMessagesBlockContactUserHandler } from './providers/user-block-contact-handler';
|
import { AddonMessagesBlockContactUserHandler } from './providers/user-block-contact-handler';
|
||||||
|
import { AddonMessagesContactRequestLinkHandler } from './providers/contact-request-link-handler';
|
||||||
import { AddonMessagesDiscussionLinkHandler } from './providers/discussion-link-handler';
|
import { AddonMessagesDiscussionLinkHandler } from './providers/discussion-link-handler';
|
||||||
import { AddonMessagesIndexLinkHandler } from './providers/index-link-handler';
|
import { AddonMessagesIndexLinkHandler } from './providers/index-link-handler';
|
||||||
import { AddonMessagesSyncCronHandler } from './providers/sync-cron-handler';
|
import { AddonMessagesSyncCronHandler } from './providers/sync-cron-handler';
|
||||||
|
@ -58,6 +59,7 @@ export const ADDON_MESSAGES_PROVIDERS: any[] = [
|
||||||
AddonMessagesSendMessageUserHandler,
|
AddonMessagesSendMessageUserHandler,
|
||||||
AddonMessagesAddContactUserHandler,
|
AddonMessagesAddContactUserHandler,
|
||||||
AddonMessagesBlockContactUserHandler,
|
AddonMessagesBlockContactUserHandler,
|
||||||
|
AddonMessagesContactRequestLinkHandler,
|
||||||
AddonMessagesDiscussionLinkHandler,
|
AddonMessagesDiscussionLinkHandler,
|
||||||
AddonMessagesIndexLinkHandler,
|
AddonMessagesIndexLinkHandler,
|
||||||
AddonMessagesSyncCronHandler,
|
AddonMessagesSyncCronHandler,
|
||||||
|
@ -74,11 +76,13 @@ export class AddonMessagesModule {
|
||||||
sitesProvider: CoreSitesProvider, linkHelper: CoreContentLinksHelperProvider, updateManager: CoreUpdateManagerProvider,
|
sitesProvider: CoreSitesProvider, linkHelper: CoreContentLinksHelperProvider, updateManager: CoreUpdateManagerProvider,
|
||||||
settingsHandler: AddonMessagesSettingsHandler, settingsDelegate: CoreSettingsDelegate,
|
settingsHandler: AddonMessagesSettingsHandler, settingsDelegate: CoreSettingsDelegate,
|
||||||
pushNotificationsDelegate: AddonPushNotificationsDelegate, utils: CoreUtilsProvider,
|
pushNotificationsDelegate: AddonPushNotificationsDelegate, utils: CoreUtilsProvider,
|
||||||
addContactHandler: AddonMessagesAddContactUserHandler, blockContactHandler: AddonMessagesBlockContactUserHandler) {
|
addContactHandler: AddonMessagesAddContactUserHandler, blockContactHandler: AddonMessagesBlockContactUserHandler,
|
||||||
|
contactRequestLinkHandler: AddonMessagesContactRequestLinkHandler) {
|
||||||
// Register handlers.
|
// Register handlers.
|
||||||
mainMenuDelegate.registerHandler(mainmenuHandler);
|
mainMenuDelegate.registerHandler(mainmenuHandler);
|
||||||
contentLinksDelegate.registerHandler(indexLinkHandler);
|
contentLinksDelegate.registerHandler(indexLinkHandler);
|
||||||
contentLinksDelegate.registerHandler(discussionLinkHandler);
|
contentLinksDelegate.registerHandler(discussionLinkHandler);
|
||||||
|
contentLinksDelegate.registerHandler(contactRequestLinkHandler);
|
||||||
userDelegate.registerHandler(sendMessageHandler);
|
userDelegate.registerHandler(sendMessageHandler);
|
||||||
userDelegate.registerHandler(addContactHandler);
|
userDelegate.registerHandler(addContactHandler);
|
||||||
userDelegate.registerHandler(blockContactHandler);
|
userDelegate.registerHandler(blockContactHandler);
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-navbar core-back-button>
|
||||||
|
<ion-title>{{ 'addon.messages.contacts' | translate }}</ion-title>
|
||||||
|
<ion-buttons end>
|
||||||
|
<button ion-button icon-only (click)="gotoSearch()" [attr.aria-label]="'addon.messages.search' | translate">
|
||||||
|
<ion-icon name="search"></ion-icon>
|
||||||
|
</button>
|
||||||
|
<!-- Add an empty context menu so discussion page can add items in split view, otherwise the menu disappears in some cases. -->
|
||||||
|
<core-context-menu></core-context-menu>
|
||||||
|
</ion-buttons>
|
||||||
|
</ion-navbar>
|
||||||
|
</ion-header>
|
||||||
|
<core-split-view>
|
||||||
|
<ion-content>
|
||||||
|
<core-tabs>
|
||||||
|
<core-tab [title]="'addon.messages.contacts' | translate" (ionSelect)="selectUser('contacts')">
|
||||||
|
<ng-template>
|
||||||
|
<addon-messages-confirmed-contacts (onUserSelected)="selectUser('contacts', $event.userId, $event.onInit)"></addon-messages-confirmed-contacts>
|
||||||
|
</ng-template>
|
||||||
|
</core-tab>
|
||||||
|
<core-tab [title]="'addon.messages.requests' | translate" [badge]="contactRequestsCount" (ionSelect)="selectUser('requests')">
|
||||||
|
<ng-template>
|
||||||
|
<addon-messages-contact-requests (onUserSelected)="selectUser('requests', $event.userId, $event.onInit)"></addon-messages-contact-requests>
|
||||||
|
</ng-template>
|
||||||
|
</core-tab>
|
||||||
|
</core-tabs>
|
||||||
|
</ion-content>
|
||||||
|
</core-split-view>
|
|
@ -0,0 +1,37 @@
|
||||||
|
// (C) Copyright 2015 Martin Dougiamas
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { IonicPageModule } from 'ionic-angular';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { AddonMessagesContactsPage } from './contacts';
|
||||||
|
import { CoreComponentsModule } from '@components/components.module';
|
||||||
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
|
import { CorePipesModule } from '@pipes/pipes.module';
|
||||||
|
import { AddonMessagesComponentsModule } from '../../components/components.module';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AddonMessagesContactsPage,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CoreComponentsModule,
|
||||||
|
CoreDirectivesModule,
|
||||||
|
CorePipesModule,
|
||||||
|
AddonMessagesComponentsModule,
|
||||||
|
IonicPageModule.forChild(AddonMessagesContactsPage),
|
||||||
|
TranslateModule.forChild()
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonMessagesContactsPageModule {}
|
|
@ -0,0 +1,117 @@
|
||||||
|
// (C) Copyright 2015 Martin Dougiamas
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Component, OnDestroy, ViewChild } from '@angular/core';
|
||||||
|
import { IonicPage, NavController } from 'ionic-angular';
|
||||||
|
import { CoreEventsProvider } from '@providers/events';
|
||||||
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
|
import { AddonMessagesProvider } from '../../providers/messages';
|
||||||
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
|
import { CoreTabsComponent } from '@components/tabs/tabs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page that displays contacts and contact requests.
|
||||||
|
*/
|
||||||
|
@IonicPage({ segment: 'addon-messages-contacts' })
|
||||||
|
@Component({
|
||||||
|
selector: 'page-addon-messages-contacts',
|
||||||
|
templateUrl: 'contacts.html',
|
||||||
|
})
|
||||||
|
export class AddonMessagesContactsPage implements OnDestroy {
|
||||||
|
|
||||||
|
@ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent;
|
||||||
|
@ViewChild(CoreTabsComponent) tabsComponent: CoreTabsComponent;
|
||||||
|
|
||||||
|
contactRequestsCount = 0;
|
||||||
|
|
||||||
|
protected loadSplitViewObserver: any;
|
||||||
|
protected siteId: string;
|
||||||
|
protected contactRequestsCountObserver: any;
|
||||||
|
protected conversationUserId: number; // User id of the conversation opened in the split view.
|
||||||
|
protected selectedUserId = {
|
||||||
|
contacts: null, // User id of the selected user in the confirmed contacts tab.
|
||||||
|
requests: null, // User id of the selected user in the contact requests tab.
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider,
|
||||||
|
private navCtrl: NavController, private messagesProvider: AddonMessagesProvider) {
|
||||||
|
|
||||||
|
this.siteId = sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
// Update the contact requests badge.
|
||||||
|
this.contactRequestsCountObserver = eventsProvider.on(AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT, (data) => {
|
||||||
|
this.contactRequestsCount = data.count;
|
||||||
|
}, this.siteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page being initialized.
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.messagesProvider.getContactRequestsCount(this.siteId); // Badge already updated by the observer.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to the search page.
|
||||||
|
*/
|
||||||
|
gotoSearch(): void {
|
||||||
|
this.navCtrl.push('AddonMessagesSearchPage');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User entered the page.
|
||||||
|
*/
|
||||||
|
ionViewDidEnter(): void {
|
||||||
|
this.tabsComponent && this.tabsComponent.ionViewDidEnter();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User left the page.
|
||||||
|
*/
|
||||||
|
ionViewDidLeave(): void {
|
||||||
|
this.tabsComponent && this.tabsComponent.ionViewDidLeave();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the selected user and open the conversation in the split view if needed.
|
||||||
|
*
|
||||||
|
* @param {string} tab Active tab: "contacts" or "requests".
|
||||||
|
* @param {number} [userId] Id of the selected user, undefined to use the last selected user in the tab.
|
||||||
|
* @param {boolean} [onInit=false] Whether the contact was selected on initial load.
|
||||||
|
*/
|
||||||
|
selectUser(tab: string, userId?: number, onInit: boolean = false): void {
|
||||||
|
userId = userId || this.selectedUserId[tab];
|
||||||
|
|
||||||
|
if (!userId || userId == this.conversationUserId) {
|
||||||
|
// No user conversation to open or it is already opened.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onInit && !this.splitviewCtrl.isOn()) {
|
||||||
|
// Do not open a conversation by default when split view is not visible.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.conversationUserId = userId;
|
||||||
|
this.selectedUserId[tab] = userId;
|
||||||
|
this.splitviewCtrl.push('AddonMessagesDiscussionPage', { userId });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page destroyed.
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.contactRequestsCountObserver && this.contactRequestsCountObserver.off();
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,11 +9,15 @@
|
||||||
</ion-navbar>
|
</ion-navbar>
|
||||||
<core-navbar-buttons end>
|
<core-navbar-buttons end>
|
||||||
<core-context-menu>
|
<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.info' | translate" (action)="viewInfo()"></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]="!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)" [iconAction]="favouriteIcon" [closeOnClick]="false"></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]="!canDelete" [priority]="400" [content]="'addon.messages.showdeletemessages' | translate" (action)="toggleDelete()" [iconAction]="'trash'"></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]="!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]="!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-context-menu>
|
||||||
</core-navbar-buttons>
|
</core-navbar-buttons>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
|
@ -21,32 +25,32 @@
|
||||||
<core-loading [hideUntil]="loaded">
|
<core-loading [hideUntil]="loaded">
|
||||||
<!-- Load previous messages. -->
|
<!-- Load previous messages. -->
|
||||||
<core-infinite-loading [enabled]="canLoadMore" (action)="loadPrevious($event)" position="top" [error]="loadMoreError"></core-infinite-loading>
|
<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" [class.addon-messages-discussion-group]="isGroup" [attr.aria-live]="'polite'">
|
||||||
<ng-container *ngFor="let message of messages; index as index; last as last">
|
<ng-container *ngFor="let message of messages; index as index; last as last">
|
||||||
<ion-chip *ngIf="message.showDate" class="addon-messages-date" color="light">
|
<h6 text-center *ngIf="message.showDate" class="addon-messages-date">
|
||||||
<ion-label>{{ message.timecreated | coreFormatDate: "LL" }}</ion-label>
|
{{ message.timecreated | coreFormatDate: "LL" }}
|
||||||
</ion-chip>
|
</h6>
|
||||||
|
|
||||||
<ion-chip class="addon-messages-unreadfrom" *ngIf="unreadMessageFrom && message.id == unreadMessageFrom" color="light">
|
<ion-chip class="addon-messages-unreadfrom" *ngIf="unreadMessageFrom && message.id == unreadMessageFrom" color="light">
|
||||||
<ion-label>{{ 'addon.messages.newmessages' | translate:{$a: title} }}</ion-label>
|
<ion-label>{{ 'addon.messages.newmessages' | translate:{$a: title} }}</ion-label>
|
||||||
<ion-icon name="arrow-round-down"></ion-icon>
|
<ion-icon name="arrow-round-down"></ion-icon>
|
||||||
</ion-chip>
|
</ion-chip>
|
||||||
|
|
||||||
<ion-item text-wrap (longPress)="copyMessage(message)" class="addon-message" [class.addon-message-mine]="message.useridfrom == currentUserId" [@coreSlideInOut]="message.useridfrom == currentUserId ? '' : 'fromLeft'">
|
<ion-item text-wrap (longPress)="copyMessage(message)" class="addon-message" [class.addon-message-mine]="message.useridfrom == currentUserId" [class.addon-message-not-mine]="message.useridfrom != currentUserId" [class.addon-message-no-user]="!message.showUserData" [@coreSlideInOut]="message.useridfrom == currentUserId ? '' : 'fromLeft'">
|
||||||
<!-- User data. -->
|
<!-- User data. -->
|
||||||
<ion-avatar item-start *ngIf="message.showUserData">
|
<h2 class="addon-message-user" >
|
||||||
<img [src]="members[message.useridfrom].profileimageurl" [alt]="'core.pictureof' | translate:{$a: members[message.useridfrom].fullname}" core-external-content onError="this.src='assets/img/user-avatar.png'">
|
<ion-avatar item-start core-user-avatar [user]="members[message.useridfrom]" [linkProfile]="false" *ngIf="message.showUserData"></ion-avatar>
|
||||||
</ion-avatar>
|
|
||||||
<h2 *ngIf="message.showUserData">{{ members[message.useridfrom].fullname }}</h2>
|
<div *ngIf="message.showUserData">{{ members[message.useridfrom].fullname }}</div>
|
||||||
|
|
||||||
|
<ion-note *ngIf="!message.pending">{{ message.timecreated | coreFormatDate: "dftimedate" }}</ion-note>
|
||||||
|
<ion-note *ngIf="message.pending"><ion-icon name="time"></ion-icon></ion-note>
|
||||||
|
</h2>
|
||||||
|
|
||||||
<!-- Some messages have <p> and some others don't. Add a <p> so they all have same styles. -->
|
<!-- Some messages have <p> and some others don't. Add a <p> so they all have same styles. -->
|
||||||
<p class="addon-message-text">
|
<p class="addon-message-text">
|
||||||
<core-format-text (afterRender)="last && scrollToBottom()" [text]="message.text"></core-format-text>
|
<core-format-text (afterRender)="last && scrollToBottom()" [text]="message.text"></core-format-text>
|
||||||
</p>
|
</p>
|
||||||
<ion-note *ngIf="!message.pending">
|
|
||||||
{{ message.timecreated | coreFormatDate: "dftimedate" }}
|
|
||||||
</ion-note>
|
|
||||||
<ion-note *ngIf="message.pending"><ion-icon name="time"></ion-icon></ion-note>
|
|
||||||
|
|
||||||
<button ion-button icon-only clear="true" *ngIf="!message.sending && showDelete" (click)="deleteMessage(message, index)" class="addon-messages-delete-button" [@coreSlideInOut]="'fromRight'" [attr.aria-label]=" 'addon.messages.deletemessage' | translate">
|
<button ion-button icon-only clear="true" *ngIf="!message.sending && showDelete" (click)="deleteMessage(message, index)" class="addon-messages-delete-button" [@coreSlideInOut]="'fromRight'" [attr.aria-label]=" 'addon.messages.deletemessage' | translate">
|
||||||
<ion-icon name="trash" color="danger"></ion-icon>
|
<ion-icon name="trash" color="danger"></ion-icon>
|
||||||
|
@ -59,7 +63,25 @@
|
||||||
</ion-content>
|
</ion-content>
|
||||||
<ion-footer color="light" class="footer-adjustable" *ngIf="loaded && (!conversationId || conversation)">
|
<ion-footer color="light" class="footer-adjustable" *ngIf="loaded && (!conversationId || conversation)">
|
||||||
<ion-toolbar color="light" position="bottom">
|
<ion-toolbar color="light" position="bottom">
|
||||||
<!-- @todo: Check if the user can send messages. -->
|
<p *ngIf="footerType == 'unable'" text-center margin-horizontal>{{ 'addon.messages.unabletomessage' | translate }}</p>
|
||||||
<core-send-message-form (onSubmit)="sendMessage($event)" [showKeyboard]="showKeyboard" [placeholder]="'addon.messages.newmessage' | translate" (onResize)="resizeContent()"></core-send-message-form>
|
<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-toolbar>
|
||||||
</ion-footer>
|
</ion-footer>
|
||||||
|
|
|
@ -1,39 +1,93 @@
|
||||||
// Messages.
|
// Messages.
|
||||||
$item-message-bg: $gray-lighter !default;
|
$item-message-bg: $white !default;
|
||||||
$item-message-note-text: $gray-dark !default;
|
$item-message-note-text: $gray-dark !default;
|
||||||
$item-message-note-font-size: 75% !default;
|
$item-message-note-font-size: 75% !default;
|
||||||
$item-message-mine-bg: $blue-light !default;
|
$item-message-mine-bg: $gray-light !default;
|
||||||
|
|
||||||
ion-app.app-root page-addon-messages-discussion {
|
ion-app.app-root page-addon-messages-discussion {
|
||||||
|
ion-content {
|
||||||
|
background-color: $gray-lighter !important;
|
||||||
|
}
|
||||||
|
|
||||||
.addon-messages-discussion-container {
|
.addon-messages-discussion-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding-bottom: 15px;
|
padding-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.addon-messages-date,
|
.addon-messages-date {
|
||||||
.addon-messages-unreadfrom {
|
font-weight: normal;
|
||||||
margin-top: 10px;
|
font-size: 1.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.addon-messages-unreadfrom {
|
.addon-messages-unreadfrom {
|
||||||
color: $blue;
|
color: $core-color;
|
||||||
|
background-color: transparent;
|
||||||
|
margin-top: 6px;
|
||||||
|
ion-icon {
|
||||||
|
color: $core-color;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message item.
|
// Message item.
|
||||||
.addon-message {
|
.addon-message {
|
||||||
max-width: 80%;
|
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 16px;
|
border-radius: 4px;
|
||||||
padding: 10px;
|
padding: 8px;
|
||||||
margin: 4px;
|
@include margin(8px, 8px, 0, 8px);
|
||||||
background-color: $item-message-bg;
|
background-color: $item-message-bg;
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
width: auto;
|
width: 90%;
|
||||||
|
max-width: 90%;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
@include core-transition(width);
|
@include core-transition(width);
|
||||||
|
|
||||||
|
core-format-text > p:only-child {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.addon-message-user {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: .5rem!important;
|
||||||
|
margin-top: 0;
|
||||||
|
|
||||||
|
ion-avatar {
|
||||||
|
display: block;
|
||||||
|
min-width: 30px;
|
||||||
|
min-height: 30px;
|
||||||
|
margin: 0;
|
||||||
|
img {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
font-weight: 500;
|
||||||
|
flex-grow: 1;
|
||||||
|
@include padding-horizontal(.5rem);
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note {
|
||||||
|
@include text-align('end');
|
||||||
|
color: $item-message-note-text;
|
||||||
|
font-size: $item-message-note-font-size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.addon-message-no-user .addon-message-user .note {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
&.activated {
|
&.activated {
|
||||||
background-color: darken($item-message-bg, 10%);
|
background-color: darken($item-message-bg, 10%);
|
||||||
}
|
}
|
||||||
|
@ -53,12 +107,6 @@ ion-app.app-root page-addon-messages-discussion {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.note {
|
|
||||||
align-self: flex-end;
|
|
||||||
color: $item-message-note-text;
|
|
||||||
font-size: $item-message-note-font-size;
|
|
||||||
@include margin(null, null, null, 10px);
|
|
||||||
}
|
|
||||||
.addon-messages-delete-button {
|
.addon-messages-delete-button {
|
||||||
min-height: initial;
|
min-height: initial;
|
||||||
line-height: initial;
|
line-height: initial;
|
||||||
|
@ -76,11 +124,22 @@ ion-app.app-root page-addon-messages-discussion {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.addon-messages-discussion-group .addon-message + .addon-message-no-user,
|
||||||
|
.addon-message.addon-message-mine + .addon-message-no-user.addon-message-mine,
|
||||||
|
.addon-message.addon-message-not-mine + .addon-message-no-user.addon-message-not-mine {
|
||||||
|
h2 {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
margin-top: -4px;
|
||||||
|
padding-top: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Defines when an item-message is the user's.
|
// Defines when an item-message is the user's.
|
||||||
.addon-message-mine {
|
.addon-message-mine {
|
||||||
background-color: $item-message-mine-bg;
|
background-color: $item-message-mine-bg;
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
max-width: 80%;
|
|
||||||
|
|
||||||
&.activated {
|
&.activated {
|
||||||
background-color: darken($item-message-mine-bg, 10%);
|
background-color: darken($item-message-mine-bg, 10%);
|
||||||
|
@ -97,12 +156,6 @@ ion-app.app-root page-addon-messages-discussion {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.addon-message .item-content,
|
|
||||||
.addon-message-mine .item-content {
|
|
||||||
background-color: transparent;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toolbar-title {
|
.toolbar-title {
|
||||||
img {
|
img {
|
||||||
@include margin-horizontal(null, 6px);
|
@include margin-horizontal(null, 6px);
|
||||||
|
|
|
@ -57,6 +57,8 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
protected keyboardObserver: any;
|
protected keyboardObserver: any;
|
||||||
protected scrollBottom = true;
|
protected scrollBottom = true;
|
||||||
protected viewDestroyed = false;
|
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.
|
conversationId: number; // Conversation ID. Undefined if it's a new individual conversation.
|
||||||
conversation: any; // The conversation object (if it exists).
|
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.
|
members: any = {}; // Members that wrote a message, indexed by ID.
|
||||||
favouriteIcon = 'fa-star';
|
favouriteIcon = 'fa-star';
|
||||||
deleteIcon = 'trash';
|
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,
|
constructor(private eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, navParams: NavParams,
|
||||||
private userProvider: CoreUserProvider, private navCtrl: NavController, private messagesSync: AddonMessagesSyncProvider,
|
private userProvider: CoreUserProvider, private navCtrl: NavController, private messagesSync: AddonMessagesSyncProvider,
|
||||||
|
@ -108,6 +114,13 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, this.siteId);
|
}, 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;
|
const backViewPage = this.navCtrl.getPrevious() && this.navCtrl.getPrevious().component.name;
|
||||||
this.showInfo = !backViewPage || backViewPage !== 'CoreUserProfilePage';
|
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) {
|
if (!this.groupMessagingEnabled && this.userId) {
|
||||||
// Get the user profile to retrieve the user fullname and image.
|
// Get the user profile to retrieve the user fullname and image.
|
||||||
this.userProvider.getProfile(this.userId, undefined, true).then((user) => {
|
this.userProvider.getProfile(this.userId, undefined, true).then((user) => {
|
||||||
|
@ -171,7 +203,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Synchronize messages if needed.
|
// Synchronize messages if needed.
|
||||||
this.messagesSync.syncDiscussion(this.conversationId, this.userId).catch(() => {
|
return this.messagesSync.syncDiscussion(this.conversationId, this.userId).catch(() => {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
}).then((warnings) => {
|
}).then((warnings) => {
|
||||||
if (warnings && warnings[0]) {
|
if (warnings && warnings[0]) {
|
||||||
|
@ -185,8 +217,22 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
// Fetch the messages for the first time.
|
// Fetch the messages for the first time.
|
||||||
return this.fetchMessages();
|
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 {
|
} else {
|
||||||
|
this.otherMember = null;
|
||||||
|
|
||||||
// Fetch the messages for the first time.
|
// Fetch the messages for the first time.
|
||||||
return this.fetchMessages().then(() => {
|
return this.fetchMessages().then(() => {
|
||||||
if (!this.title && this.messages.length) {
|
if (!this.title && this.messages.length) {
|
||||||
|
@ -207,11 +253,9 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
this.resizeContent();
|
this.resizeContent();
|
||||||
this.loaded = true;
|
this.loaded = true;
|
||||||
this.setPolling(); // Make sure we're polling messages.
|
this.setPolling(); // Make sure we're polling messages.
|
||||||
});
|
this.setContactRequestInfo();
|
||||||
|
this.setFooterType();
|
||||||
// Recalculate footer position when keyboard is shown or hidden.
|
loader && loader.dismiss();
|
||||||
this.keyboardObserver = this.eventsProvider.on(CoreEventsProvider.KEYBOARD_CHANGE, (kbHeight) => {
|
|
||||||
this.content.resize();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
* 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.
|
* Page destroyed.
|
||||||
*/
|
*/
|
||||||
|
@ -1020,6 +1254,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
this.unsetPolling();
|
this.unsetPolling();
|
||||||
this.syncObserver && this.syncObserver.off();
|
this.syncObserver && this.syncObserver.off();
|
||||||
this.keyboardObserver && this.keyboardObserver.off();
|
this.keyboardObserver && this.keyboardObserver.off();
|
||||||
|
this.memberInfoObserver && this.memberInfoObserver.off();
|
||||||
this.viewDestroyed = true;
|
this.viewDestroyed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
<ion-navbar core-back-button>
|
<ion-navbar core-back-button>
|
||||||
<ion-title>{{ 'addon.messages.messages' | translate }}</ion-title>
|
<ion-title>{{ 'addon.messages.messages' | translate }}</ion-title>
|
||||||
<ion-buttons end>
|
<ion-buttons end>
|
||||||
<button ion-button icon-only (click)="gotoContacts($event)" [attr.aria-label]="'addon.messages.contacts' | translate">
|
<button ion-button icon-only (click)="gotoSearch()" [attr.aria-label]="'addon.messages.search' | translate">
|
||||||
<ion-icon name="person"></ion-icon> <!-- @todo: Display number of pending requests. -->
|
<ion-icon name="search"></ion-icon>
|
||||||
</button>
|
</button>
|
||||||
<button ion-button icon-only (click)="gotoSettings($event)" [attr.aria-label]="'addon.messages.messagepreferences' | translate">
|
<button ion-button icon-only (click)="gotoSettings($event)" [attr.aria-label]="'addon.messages.messagepreferences' | translate">
|
||||||
<ion-icon name="cog"></ion-icon>
|
<ion-icon name="cog"></ion-icon>
|
||||||
|
@ -19,34 +19,13 @@
|
||||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
</ion-refresher>
|
</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" [disabled]="!loaded"></core-search-box>
|
|
||||||
|
|
||||||
<core-loading [hideUntil]="loaded" [message]="loadingMessage">
|
<core-loading [hideUntil]="loaded" [message]="loadingMessage">
|
||||||
|
<a ion-item text-wrap (click)="gotoContacts($event)" [attr.aria-label]="'addon.messages.contacts' | translate" class="addon-message-discussion">
|
||||||
<!-- Search results. -->
|
<ion-icon name="person" item-start></ion-icon>
|
||||||
<ion-list *ngIf="search.showResults" no-margin>
|
<h2>{{ 'addon.messages.contacts' | translate }}</h2>
|
||||||
<ion-item-divider color="light">
|
<ion-badge *ngIf="contactRequestsCount > 0" item-end>{{contactRequestsCount}}</ion-badge>
|
||||||
<h2>{{ 'core.searchresults' | translate }}</h2>
|
|
||||||
<ion-note item-end>{{ search.results.length }}</ion-note>
|
|
||||||
</ion-item-divider>
|
|
||||||
|
|
||||||
<a ion-item text-wrap *ngFor="let result of search.results" [title]="result.fullname" (click)="gotoConversation(result.conversationid, result.userid, result.messageid)" [class.core-split-item-selected]="(result.conversationid && result.conversationid == selectedConversationId) || (result.userid && result.userid == selectedUserId)" detail-none>
|
|
||||||
<ion-avatar core-user-avatar [user]="result" [linkProfile]="false" item-start></ion-avatar>
|
|
||||||
<h2>
|
|
||||||
<p>
|
|
||||||
<core-format-text [text]="result.fullname"></core-format-text>
|
|
||||||
<core-icon name="fa-ban" *ngIf="result.isblocked" [attr.aria-label]="'addon.messages.contactblocked' | translate"></core-icon>
|
|
||||||
</p>
|
|
||||||
<ion-note *ngIf="result.lastmessagedate > 0">
|
|
||||||
{{result.lastmessagedate | coreDateDayOrTime}}
|
|
||||||
</ion-note>
|
|
||||||
</h2>
|
|
||||||
<p><core-format-text clean="true" singleLine="true" [text]="result.lastmessage" class="addon-message-last-message"></core-format-text></p>
|
|
||||||
</a>
|
</a>
|
||||||
</ion-list>
|
<ion-list>
|
||||||
|
|
||||||
<!-- Conversations. -->
|
|
||||||
<ion-list *ngIf="!search.showResults">
|
|
||||||
<!-- Favourite conversations. -->
|
<!-- Favourite conversations. -->
|
||||||
<ion-item-divider color="light" text-wrap *ngIf="favourites.conversations" (click)="toggle(favourites)" class="core-expandable">
|
<ion-item-divider color="light" text-wrap *ngIf="favourites.conversations" (click)="toggle(favourites)" class="core-expandable">
|
||||||
<core-icon *ngIf="!favourites.expanded" name="fa-caret-right" item-start></core-icon>
|
<core-icon *ngIf="!favourites.expanded" name="fa-caret-right" item-start></core-icon>
|
||||||
|
@ -94,16 +73,13 @@
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</div>
|
</div>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
|
|
||||||
<!-- Search didn't get any result. -->
|
|
||||||
<core-empty-box *ngIf="(!search.results || search.results.length <= 0) && search.showResults" icon="search" [message]="'core.noresults' | translate"></core-empty-box>
|
|
||||||
</core-loading>
|
</core-loading>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
</core-split-view>
|
</core-split-view>
|
||||||
|
|
||||||
<!-- Template to render a list of conversations. -->
|
<!-- Template to render a list of conversations. -->
|
||||||
<ng-template #conversationsTemplate let-conversations="conversations">
|
<ng-template #conversationsTemplate let-conversations="conversations">
|
||||||
<a ion-item text-wrap *ngFor="let conversation of conversations" [title]="conversation.name" detail-none (click)="gotoConversation(conversation.id, conversation.userid)" [class.core-split-item-selected]="(conversation.id && conversation.id == selectedConversationId) || (conversation.userid && conversation.userid == selectedUserId)" id="addon-message-conversation-{{ conversation.id ? conversation.id : 'user-' + conversation.userid }}">
|
<a ion-item text-wrap *ngFor="let conversation of conversations" [title]="conversation.name" (click)="gotoConversation(conversation.id, conversation.userid)" [class.core-split-item-selected]="(conversation.id && conversation.id == selectedConversationId) || (conversation.userid && conversation.userid == selectedUserId)" class="addon-message-discussion" id="addon-message-conversation-{{ conversation.id ? conversation.id : 'user-' + conversation.userid }}">
|
||||||
<!-- Group conversation image. -->
|
<!-- Group conversation image. -->
|
||||||
<ion-avatar item-start *ngIf="conversation.type != typeIndividual && conversation.imageurl">
|
<ion-avatar item-start *ngIf="conversation.type != typeIndividual && conversation.imageurl">
|
||||||
<img [src]="conversation.imageurl" [alt]="conversation.name" core-external-content>
|
<img [src]="conversation.imageurl" [alt]="conversation.name" core-external-content>
|
||||||
|
@ -113,18 +89,18 @@
|
||||||
<ion-avatar *ngIf="conversation.type == typeIndividual" core-user-avatar [user]="conversation.otherUser" [linkProfile]="false" [checkOnline]="conversation.showonlinestatus" item-start></ion-avatar>
|
<ion-avatar *ngIf="conversation.type == typeIndividual" core-user-avatar [user]="conversation.otherUser" [linkProfile]="false" [checkOnline]="conversation.showonlinestatus" item-start></ion-avatar>
|
||||||
|
|
||||||
<h2>
|
<h2>
|
||||||
<p>
|
|
||||||
<core-format-text [text]="conversation.name"></core-format-text>
|
<core-format-text [text]="conversation.name"></core-format-text>
|
||||||
<core-icon name="fa-ban" *ngIf="conversation.isblocked" [attr.aria-label]="'addon.messages.contactblocked' | translate"></core-icon>
|
<core-icon name="fa-ban" *ngIf="conversation.isblocked" [attr.aria-label]="'addon.messages.contactblocked' | translate"></core-icon>
|
||||||
</p>
|
</h2>
|
||||||
<ion-note *ngIf="conversation.lastmessagedate > 0 || conversation.unreadcount">
|
<ion-note *ngIf="conversation.lastmessagedate > 0 || conversation.unreadcount">
|
||||||
<ion-badge *ngIf="conversation.unreadcount > 0">{{ conversation.unreadcount }}</ion-badge>
|
<ion-badge *ngIf="conversation.unreadcount > 0">{{ conversation.unreadcount }}</ion-badge>
|
||||||
<span *ngIf="conversation.lastmessagedate > 0">{{conversation.lastmessagedate | coreDateDayOrTime}}</span>
|
<span *ngIf="conversation.lastmessagedate > 0">{{conversation.lastmessagedate | coreDateDayOrTime}}</span>
|
||||||
</ion-note>
|
</ion-note>
|
||||||
</h2>
|
<p *ngIf="conversation.subname"><core-format-text [text]="conversation.subname"></core-format-text></p>
|
||||||
<p><core-format-text *ngIf="conversation.subname" [text]="conversation.subname"></core-format-text></p>
|
<p class="addon-message-last-message">
|
||||||
<p>
|
<span *ngIf="conversation.sentfromcurrentuser" class="addon-message-last-message-user">{{ 'addon.messages.you' | translate }}</span>
|
||||||
<span *ngIf="conversation.sentfromcurrentuser">{{ 'addon.messages.you' | translate }}</span> <core-format-text clean="true" singleLine="true" [text]="conversation.lastmessage" class="addon-message-last-message"></core-format-text>
|
<core-format-text *ngIf="conversation.type != typeIndividual && conversation.members[0]" [text]="conversation.members[0].fullname + ':'" class="addon-message-last-message-user"></core-format-text>
|
||||||
|
<core-format-text clean="true" singleLine="true" [text]="conversation.lastmessage" class="addon-message-last-message-text"></core-format-text>
|
||||||
</p>
|
</p>
|
||||||
</a>
|
</a>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
|
@ -1,17 +1,21 @@
|
||||||
ion-app.app-root page-addon-messages-group-conversations {
|
ion-app.app-root page-addon-messages-group-conversations {
|
||||||
h2 {
|
.addon-message-last-message {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
.note {
|
|
||||||
margin: 0;
|
|
||||||
align-self: flex-end;
|
|
||||||
display: inline-flex;
|
|
||||||
font-size: initial;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
core-format-text.addon-message-last-message {
|
.addon-message-last-message-user {
|
||||||
display: inline;
|
white-space: nowrap;
|
||||||
|
color: $black;
|
||||||
|
@include margin(null, 2px, null, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
.addon-message-last-message-text {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -13,7 +13,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
|
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
|
||||||
import { IonicPage, Platform, NavParams, Content } from 'ionic-angular';
|
import { IonicPage, Platform, NavController, NavParams, Content } from 'ionic-angular';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { CoreEventsProvider } from '@providers/events';
|
import { CoreEventsProvider } from '@providers/events';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
|
@ -21,7 +21,6 @@ import { AddonMessagesProvider } from '../../providers/messages';
|
||||||
import { AddonMessagesOfflineProvider } from '../../providers/messages-offline';
|
import { AddonMessagesOfflineProvider } from '../../providers/messages-offline';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreAppProvider } from '@providers/app';
|
|
||||||
import { AddonPushNotificationsDelegate } from '@addon/pushnotifications/providers/delegate';
|
import { AddonPushNotificationsDelegate } from '@addon/pushnotifications/providers/delegate';
|
||||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
import { CoreUserProvider } from '@core/user/providers/user';
|
import { CoreUserProvider } from '@core/user/providers/user';
|
||||||
|
@ -42,13 +41,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
||||||
loadingMessage: string;
|
loadingMessage: string;
|
||||||
selectedConversationId: number;
|
selectedConversationId: number;
|
||||||
selectedUserId: number;
|
selectedUserId: number;
|
||||||
search = {
|
contactRequestsCount = 0;
|
||||||
enabled: false,
|
|
||||||
showResults: false,
|
|
||||||
results: [],
|
|
||||||
loading: '',
|
|
||||||
text: ''
|
|
||||||
};
|
|
||||||
favourites: any = {
|
favourites: any = {
|
||||||
type: null,
|
type: null,
|
||||||
favourites: true
|
favourites: true
|
||||||
|
@ -74,14 +67,15 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
||||||
protected cronObserver: any;
|
protected cronObserver: any;
|
||||||
protected openConversationObserver: any;
|
protected openConversationObserver: any;
|
||||||
protected updateConversationListObserver: any;
|
protected updateConversationListObserver: any;
|
||||||
|
protected contactRequestsCountObserver: any;
|
||||||
|
protected memberInfoObserver: any;
|
||||||
|
|
||||||
constructor(private eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, translate: TranslateService,
|
constructor(private eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, translate: TranslateService,
|
||||||
private messagesProvider: AddonMessagesProvider, private domUtils: CoreDomUtilsProvider, navParams: NavParams,
|
private messagesProvider: AddonMessagesProvider, private domUtils: CoreDomUtilsProvider, navParams: NavParams,
|
||||||
private appProvider: CoreAppProvider, platform: Platform, utils: CoreUtilsProvider,
|
private navCtrl: NavController, platform: Platform, utils: CoreUtilsProvider,
|
||||||
pushNotificationsDelegate: AddonPushNotificationsDelegate, private messagesOffline: AddonMessagesOfflineProvider,
|
pushNotificationsDelegate: AddonPushNotificationsDelegate, private messagesOffline: AddonMessagesOfflineProvider,
|
||||||
private userProvider: CoreUserProvider) {
|
private userProvider: CoreUserProvider) {
|
||||||
|
|
||||||
this.search.loading = translate.instant('core.searching');
|
|
||||||
this.loadingString = translate.instant('core.loading');
|
this.loadingString = translate.instant('core.loading');
|
||||||
this.siteId = sitesProvider.getCurrentSiteId();
|
this.siteId = sitesProvider.getCurrentSiteId();
|
||||||
this.currentUserId = sitesProvider.getCurrentSiteUserId();
|
this.currentUserId = sitesProvider.getCurrentSiteUserId();
|
||||||
|
@ -166,6 +160,32 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
||||||
this.refreshData();
|
this.refreshData();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Update the contact requests badge.
|
||||||
|
this.contactRequestsCountObserver = eventsProvider.on(AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT, (data) => {
|
||||||
|
this.contactRequestsCount = data.count;
|
||||||
|
}, this.siteId);
|
||||||
|
|
||||||
|
// Update block status of a user.
|
||||||
|
this.memberInfoObserver = eventsProvider.on(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, (data) => {
|
||||||
|
if (!data.userBlocked && !data.userUnblocked) {
|
||||||
|
// The block status has not changed, ignore.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateConversations = (conversations: any[]): void => {
|
||||||
|
if (!conversations || conversations.length <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const conversation = conversations.find((conv) => conv.userid == data.userId);
|
||||||
|
if (conversation) {
|
||||||
|
conversation.isblocked = data.userBlocked;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
updateConversations(this.individual.conversations);
|
||||||
|
updateConversations(this.favourites.conversations);
|
||||||
|
}, this.siteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -195,6 +215,8 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.messagesProvider.getContactRequestsCount(this.siteId); // Badge is updated by the observer.
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -204,7 +226,6 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
||||||
*/
|
*/
|
||||||
protected fetchData(): Promise<any> {
|
protected fetchData(): Promise<any> {
|
||||||
this.loadingMessage = this.loadingString;
|
this.loadingMessage = this.loadingString;
|
||||||
this.search.enabled = this.messagesProvider.isSearchMessagesEnabled();
|
|
||||||
|
|
||||||
// Load the first conversations of each type.
|
// Load the first conversations of each type.
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
@ -481,6 +502,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
||||||
if (refresher) {
|
if (refresher) {
|
||||||
// Actions to take if refresh comes from the user.
|
// Actions to take if refresh comes from the user.
|
||||||
this.eventsProvider.trigger(AddonMessagesProvider.READ_CHANGED_EVENT, undefined, this.siteId);
|
this.eventsProvider.trigger(AddonMessagesProvider.READ_CHANGED_EVENT, undefined, this.siteId);
|
||||||
|
this.messagesProvider.refreshContactRequestsCount(this.siteId);
|
||||||
refresher.complete();
|
refresher.complete();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -515,36 +537,10 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear search and show conversations again.
|
* Navigate to the search page.
|
||||||
*/
|
*/
|
||||||
clearSearch(): void {
|
gotoSearch(): void {
|
||||||
this.loaded = false;
|
this.navCtrl.push('AddonMessagesSearchPage');
|
||||||
this.search.showResults = false;
|
|
||||||
this.search.text = ''; // Reset searched string.
|
|
||||||
this.fetchData().finally(() => {
|
|
||||||
this.loaded = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search messages cotaining text.
|
|
||||||
*
|
|
||||||
* @param {string} query Text to search for.
|
|
||||||
* @return {Promise<any>} Resolved when done.
|
|
||||||
*/
|
|
||||||
searchMessage(query: string): Promise<any> {
|
|
||||||
this.appProvider.closeKeyboard();
|
|
||||||
this.loaded = false;
|
|
||||||
this.loadingMessage = this.search.loading;
|
|
||||||
|
|
||||||
return this.messagesProvider.searchMessages(query).then((searchResults) => {
|
|
||||||
this.search.showResults = true;
|
|
||||||
this.search.results = searchResults;
|
|
||||||
}).catch((error) => {
|
|
||||||
this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingmessages', true);
|
|
||||||
}).finally(() => {
|
|
||||||
this.loaded = true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -558,5 +554,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
||||||
this.cronObserver && this.cronObserver.off();
|
this.cronObserver && this.cronObserver.off();
|
||||||
this.openConversationObserver && this.openConversationObserver.off();
|
this.openConversationObserver && this.openConversationObserver.off();
|
||||||
this.updateConversationListObserver && this.updateConversationListObserver.off();
|
this.updateConversationListObserver && this.updateConversationListObserver.off();
|
||||||
|
this.contactRequestsCountObserver && this.contactRequestsCountObserver.off();
|
||||||
|
this.memberInfoObserver && this.memberInfoObserver.off();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-navbar core-back-button>
|
||||||
|
<ion-title>{{ 'addon.messages.searchcombined' | translate }}</ion-title>
|
||||||
|
<ion-buttons end>
|
||||||
|
<!-- Add an empty context menu so discussion page can add items in split view, otherwise the menu disappears in some cases. -->
|
||||||
|
<core-context-menu></core-context-menu>
|
||||||
|
</ion-buttons>
|
||||||
|
</ion-navbar>
|
||||||
|
</ion-header>
|
||||||
|
<core-split-view>
|
||||||
|
<ion-content>
|
||||||
|
<core-search-box (onSubmit)="search($event)" (onClear)="clearSearch($event)" [disabled]="disableSearch" autocorrect="off" [spellcheck]="false" [autoFocus]="true" [lengthCheck]="1"></core-search-box>
|
||||||
|
<core-loading [hideUntil]="!displaySearching" [message]="'core.searching' | translate">
|
||||||
|
<ion-list *ngIf="displayResults">
|
||||||
|
<ng-container *ngTemplateOutlet="resultsTemplate; context: {item: contacts}"></ng-container>
|
||||||
|
<ng-container *ngTemplateOutlet="resultsTemplate; context: {item: nonContacts}"></ng-container>
|
||||||
|
<ng-container *ngTemplateOutlet="resultsTemplate; context: {item: messages}"></ng-container>
|
||||||
|
<!-- The infinite loading cannot be inside the ng-template, it fails because it doesn't find ion-content. -->
|
||||||
|
<core-infinite-loading [enabled]="messages.canLoadMore" (action)="search(query, 'messages', $event)" [error]="messages.loadMoreError"></core-infinite-loading>
|
||||||
|
</ion-list>
|
||||||
|
</core-loading>
|
||||||
|
</ion-content>
|
||||||
|
</core-split-view>
|
||||||
|
|
||||||
|
<!-- Template to render a list of results -->
|
||||||
|
<ng-template #resultsTemplate let-item="item">
|
||||||
|
<ion-item-divider color="light" text-wrap>{{ item.titleString | translate }}</ion-item-divider>
|
||||||
|
<ion-item text-wrap *ngIf="item.results.length == 0">
|
||||||
|
{{ item.emptyString | translate }}
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
|
<!-- List of results -->
|
||||||
|
<a ion-item text-wrap *ngFor="let result of item.results" [title]="result.fullname" (click)="openDiscussion(result.id)" [class.core-split-item-selected]="result.id == selectedUserId" class="addon-message-discussion">
|
||||||
|
<ion-avatar item-start core-user-avatar [user]="result" [checkOnline]="true" [linkProfile]="false"></ion-avatar>
|
||||||
|
<h2>
|
||||||
|
<core-format-text [text]="result.fullname"></core-format-text>
|
||||||
|
<core-icon name="fa-ban" *ngIf="result.isblocked" [attr.aria-label]="'addon.messages.contactblocked' | translate"></core-icon>
|
||||||
|
</h2>
|
||||||
|
<ion-note *ngIf="result.lastmessagedate > 0">
|
||||||
|
{{result.lastmessagedate | coreDateDayOrTime}}
|
||||||
|
</ion-note>
|
||||||
|
<core-format-text *ngIf="result.lastmessage" clean="true" singleLine="true" [text]="result.lastmessage"></core-format-text>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- Load more button for contacts and non-contacts -->
|
||||||
|
<ng-container *ngIf="item.type != 'messages'">
|
||||||
|
<div padding-horizontal *ngIf="item.canLoadMore && !item.loadingMore">
|
||||||
|
<button ion-button block color="light" (click)="search(query, item.type)">
|
||||||
|
{{ 'core.loadmore' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="item.loadingMore" padding text-center>
|
||||||
|
<ion-spinner></ion-spinner>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</ng-template>
|
|
@ -0,0 +1,37 @@
|
||||||
|
// (C) Copyright 2015 Martin Dougiamas
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { IonicPageModule } from 'ionic-angular';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { AddonMessagesSearchPage } from './search';
|
||||||
|
import { CoreComponentsModule } from '@components/components.module';
|
||||||
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
|
import { CorePipesModule } from '@pipes/pipes.module';
|
||||||
|
import { AddonMessagesComponentsModule } from '../../components/components.module';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AddonMessagesSearchPage,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CoreComponentsModule,
|
||||||
|
CoreDirectivesModule,
|
||||||
|
CorePipesModule,
|
||||||
|
AddonMessagesComponentsModule,
|
||||||
|
IonicPageModule.forChild(AddonMessagesSearchPage),
|
||||||
|
TranslateModule.forChild()
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonMessagesSearchPageModule {}
|
|
@ -0,0 +1,244 @@
|
||||||
|
// (C) Copyright 2015 Martin Dougiamas
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Component, OnDestroy, ViewChild } from '@angular/core';
|
||||||
|
import { IonicPage } from 'ionic-angular';
|
||||||
|
import { CoreEventsProvider } from '@providers/events';
|
||||||
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
|
import { AddonMessagesProvider } from '../../providers/messages';
|
||||||
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
|
import { CoreAppProvider } from '@providers/app';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page for searching users.
|
||||||
|
*/
|
||||||
|
@IonicPage({ segment: 'addon-messages-search' })
|
||||||
|
@Component({
|
||||||
|
selector: 'page-addon-messages-search',
|
||||||
|
templateUrl: 'search.html',
|
||||||
|
})
|
||||||
|
export class AddonMessagesSearchPage implements OnDestroy {
|
||||||
|
|
||||||
|
disableSearch = false;
|
||||||
|
displaySearching = false;
|
||||||
|
displayResults = false;
|
||||||
|
query = '';
|
||||||
|
contacts = {
|
||||||
|
type: 'contacts',
|
||||||
|
titleString: 'addon.messages.contacts',
|
||||||
|
emptyString: 'addon.messages.searchnocontactsfound',
|
||||||
|
results: [],
|
||||||
|
canLoadMore: false,
|
||||||
|
loadingMore: false
|
||||||
|
};
|
||||||
|
nonContacts = {
|
||||||
|
type: 'noncontacts',
|
||||||
|
titleString: 'addon.messages.noncontacts',
|
||||||
|
emptyString: 'addon.messages.searchnononcontactsfound',
|
||||||
|
results: [],
|
||||||
|
canLoadMore: false,
|
||||||
|
loadingMore: false
|
||||||
|
};
|
||||||
|
messages = {
|
||||||
|
type: 'messages',
|
||||||
|
titleString: 'addon.messages.messages',
|
||||||
|
emptyString: 'addon.messages.searchnomessagesfound',
|
||||||
|
results: [],
|
||||||
|
canLoadMore: false,
|
||||||
|
loadingMore: false,
|
||||||
|
loadMoreError: false
|
||||||
|
};
|
||||||
|
selectedUserId = null;
|
||||||
|
|
||||||
|
protected memberInfoObserver;
|
||||||
|
|
||||||
|
@ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent;
|
||||||
|
|
||||||
|
constructor(private appProvider: CoreAppProvider, private domUtils: CoreDomUtilsProvider, eventsProvider: CoreEventsProvider,
|
||||||
|
sitesProvider: CoreSitesProvider, private messagesProvider: AddonMessagesProvider) {
|
||||||
|
|
||||||
|
// Update block status of a user.
|
||||||
|
this.memberInfoObserver = eventsProvider.on(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, (data) => {
|
||||||
|
if (!data.userBlocked && !data.userUnblocked) {
|
||||||
|
// The block status has not changed, ignore.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const contact = this.contacts.results.find((user) => user.id == data.userId);
|
||||||
|
if (contact) {
|
||||||
|
contact.isblocked = data.userBlocked;
|
||||||
|
} else {
|
||||||
|
const nonContact = this.nonContacts.results.find((user) => user.id == data.userId);
|
||||||
|
if (nonContact) {
|
||||||
|
nonContact.isblocked = data.userBlocked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.messages.results.forEach((message: any): void => {
|
||||||
|
if (message.userid == data.userId) {
|
||||||
|
message.isblocked = data.userBlocked;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, sitesProvider.getCurrentSiteId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear search.
|
||||||
|
*/
|
||||||
|
clearSearch(): void {
|
||||||
|
this.query = '';
|
||||||
|
this.displayResults = false;
|
||||||
|
this.splitviewCtrl.emptyDetails();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a new search or load more results.
|
||||||
|
*
|
||||||
|
* @param {string} query Text to search for.
|
||||||
|
* @param {strings} loadMore Load more contacts, noncontacts or messages. If undefined, start a new search.
|
||||||
|
* @param {any} [infiniteComplete] Infinite scroll complete function. Only used from core-infinite-loading.
|
||||||
|
* @return {Promise<any>} Resolved when done.
|
||||||
|
*/
|
||||||
|
search(query: string, loadMore?: 'contacts' | 'noncontacts' | 'messages', infiniteComplete?: any): Promise<any> {
|
||||||
|
this.appProvider.closeKeyboard();
|
||||||
|
|
||||||
|
this.query = query;
|
||||||
|
this.disableSearch = true;
|
||||||
|
this.displaySearching = !loadMore;
|
||||||
|
|
||||||
|
const promises = [];
|
||||||
|
let newContacts = [];
|
||||||
|
let newNonContacts = [];
|
||||||
|
let newMessages = [];
|
||||||
|
let canLoadMoreContacts = false;
|
||||||
|
let canLoadMoreNonContacts = false;
|
||||||
|
let canLoadMoreMessages = false;
|
||||||
|
|
||||||
|
if (!loadMore || loadMore == 'contacts' || loadMore == 'noncontacts') {
|
||||||
|
const limitNum = loadMore ? AddonMessagesProvider.LIMIT_SEARCH : AddonMessagesProvider.LIMIT_INITIAL_USER_SEARCH;
|
||||||
|
let limitFrom = 0;
|
||||||
|
if (loadMore == 'contacts') {
|
||||||
|
limitFrom = this.contacts.results.length;
|
||||||
|
this.contacts.loadingMore = true;
|
||||||
|
} else if (loadMore == 'noncontacts') {
|
||||||
|
limitFrom = this.nonContacts.results.length;
|
||||||
|
this.nonContacts.loadingMore = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
promises.push(
|
||||||
|
this.messagesProvider.searchUsers(query, limitFrom, limitNum).then((result) => {
|
||||||
|
if (!loadMore || loadMore == 'contacts') {
|
||||||
|
newContacts = result.contacts;
|
||||||
|
canLoadMoreContacts = result.canLoadMoreContacts;
|
||||||
|
}
|
||||||
|
if (!loadMore || loadMore == 'noncontacts') {
|
||||||
|
newNonContacts = result.nonContacts;
|
||||||
|
canLoadMoreNonContacts = result.canLoadMoreNonContacts;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loadMore || loadMore == 'messages') {
|
||||||
|
let limitFrom = 0;
|
||||||
|
if (loadMore == 'messages') {
|
||||||
|
limitFrom = this.messages.results.length;
|
||||||
|
this.messages.loadingMore = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
promises.push(
|
||||||
|
this.messagesProvider.searchMessages(query, undefined, limitFrom).then((result) => {
|
||||||
|
newMessages = result.messages;
|
||||||
|
canLoadMoreMessages = result.canLoadMore;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(promises).then(() => {
|
||||||
|
if (!loadMore) {
|
||||||
|
this.contacts.results = [];
|
||||||
|
this.nonContacts.results = [];
|
||||||
|
this.messages.results = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.displayResults = true;
|
||||||
|
|
||||||
|
if (!loadMore || loadMore == 'contacts') {
|
||||||
|
this.contacts.results.push(...newContacts);
|
||||||
|
this.contacts.canLoadMore = canLoadMoreContacts;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loadMore || loadMore == 'noncontacts') {
|
||||||
|
this.nonContacts.results.push(...newNonContacts);
|
||||||
|
this.nonContacts.canLoadMore = canLoadMoreNonContacts;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loadMore || loadMore == 'messages') {
|
||||||
|
this.messages.results.push(...newMessages);
|
||||||
|
this.messages.canLoadMore = canLoadMoreMessages;
|
||||||
|
this.messages.loadMoreError = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loadMore) {
|
||||||
|
if (this.contacts.results.length > 0) {
|
||||||
|
this.openDiscussion(this.contacts.results[0].id, true);
|
||||||
|
} else if (this.nonContacts.results.length > 0) {
|
||||||
|
this.openDiscussion(this.nonContacts.results[0].id, true);
|
||||||
|
} else if (this.messages.results.length > 0) {
|
||||||
|
this.openDiscussion(this.messages.results[0].userid, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingusers', true);
|
||||||
|
|
||||||
|
if (loadMore == 'messages') {
|
||||||
|
this.messages.loadMoreError = true;
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
this.disableSearch = false;
|
||||||
|
this.displaySearching = false;
|
||||||
|
|
||||||
|
if (loadMore == 'contacts') {
|
||||||
|
this.contacts.loadingMore = false;
|
||||||
|
} else if (loadMore == 'noncontacts') {
|
||||||
|
this.nonContacts.loadingMore = false;
|
||||||
|
} else if (loadMore == 'messages') {
|
||||||
|
this.messages.loadingMore = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
infiniteComplete && infiniteComplete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a discussion in the split view.
|
||||||
|
*
|
||||||
|
* @param {number} userId User id.
|
||||||
|
* @param {boolean} [onInit=false] Whether the tser was selected on initial load.
|
||||||
|
*/
|
||||||
|
openDiscussion(userId: number, onInit: boolean = false): void {
|
||||||
|
if (!onInit || this.splitviewCtrl.isOn()) {
|
||||||
|
this.selectedUserId = userId;
|
||||||
|
this.splitviewCtrl.push('AddonMessagesDiscussionPage', { userId });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component destroyed.
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.memberInfoObserver && this.memberInfoObserver.off();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
// (C) Copyright 2015 Martin Dougiamas
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
|
||||||
|
import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
|
||||||
|
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
|
||||||
|
import { AddonMessagesProvider } from './messages';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content links handler for a contact requests.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class AddonMessagesContactRequestLinkHandler extends CoreContentLinksHandlerBase {
|
||||||
|
name = 'AddonMessagesContactRequestLinkHandler';
|
||||||
|
pattern = /\/message\/pendingcontactrequests\.php/;
|
||||||
|
|
||||||
|
constructor(private linkHelper: CoreContentLinksHelperProvider, private messagesProvider: AddonMessagesProvider) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of actions for a link (url).
|
||||||
|
*
|
||||||
|
* @param {string[]} siteIds List of sites the URL belongs to.
|
||||||
|
* @param {string} url The URL to treat.
|
||||||
|
* @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
|
||||||
|
* @param {number} [courseId] Course ID related to the URL. Optional but recommended.
|
||||||
|
* @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions.
|
||||||
|
*/
|
||||||
|
getActions(siteIds: string[], url: string, params: any, courseId?: number):
|
||||||
|
CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
|
||||||
|
return [{
|
||||||
|
action: (siteId, navCtrl?): void => {
|
||||||
|
// Always use redirect to make it the new history root (to avoid "loops" in history).
|
||||||
|
this.linkHelper.goInSite(navCtrl, 'AddonMessagesContactsPage', {}, siteId);
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the handler is enabled for a certain site (site + user) and a URL.
|
||||||
|
* If not defined, defaults to true.
|
||||||
|
*
|
||||||
|
* @param {string} siteId The site ID.
|
||||||
|
* @param {string} url The URL to treat.
|
||||||
|
* @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
|
||||||
|
* @param {number} [courseId] Course ID related to the URL. Optional but recommended.
|
||||||
|
* @return {boolean|Promise<boolean>} Whether the handler is enabled for the URL and site.
|
||||||
|
*/
|
||||||
|
isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise<boolean> {
|
||||||
|
return this.messagesProvider.isPluginEnabled(siteId).then((enabled) => {
|
||||||
|
if (!enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.messagesProvider.isGroupMessagingEnabled();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,6 +43,8 @@ export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCr
|
||||||
loading: true
|
loading: true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
protected updating = false;
|
||||||
|
|
||||||
constructor(private messagesProvider: AddonMessagesProvider, private sitesProvider: CoreSitesProvider,
|
constructor(private messagesProvider: AddonMessagesProvider, private sitesProvider: CoreSitesProvider,
|
||||||
private eventsProvider: CoreEventsProvider, private appProvider: CoreAppProvider,
|
private eventsProvider: CoreEventsProvider, private appProvider: CoreAppProvider,
|
||||||
private localNotificationsProvider: CoreLocalNotificationsProvider, private textUtils: CoreTextUtilsProvider,
|
private localNotificationsProvider: CoreLocalNotificationsProvider, private textUtils: CoreTextUtilsProvider,
|
||||||
|
@ -57,10 +59,15 @@ export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCr
|
||||||
this.updateBadge(data.siteId);
|
this.updateBadge(data.siteId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
eventsProvider.on(AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT, (data) => {
|
||||||
|
this.updateBadge(data.siteId, data.count);
|
||||||
|
});
|
||||||
|
|
||||||
// Reset info on logout.
|
// Reset info on logout.
|
||||||
eventsProvider.on(CoreEventsProvider.LOGOUT, (data) => {
|
eventsProvider.on(CoreEventsProvider.LOGOUT, (data) => {
|
||||||
this.handler.badge = '';
|
this.handler.badge = '';
|
||||||
this.handler.loading = true;
|
this.handler.loading = true;
|
||||||
|
this.updating = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// If a message push notification is received, refresh the count.
|
// If a message push notification is received, refresh the count.
|
||||||
|
@ -103,23 +110,55 @@ export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCr
|
||||||
/**
|
/**
|
||||||
* Triggers an update for the badge number and loading status. Mandatory if showBadge is enabled.
|
* Triggers an update for the badge number and loading status. Mandatory if showBadge is enabled.
|
||||||
*
|
*
|
||||||
* @param {string} siteId Site ID or current Site if undefined.
|
* @param {string} [siteId] Site ID or current Site if undefined.
|
||||||
|
* @param {number} [contactRequestsCount] Number of contact requests, if known.
|
||||||
*/
|
*/
|
||||||
updateBadge(siteId?: string): void {
|
updateBadge(siteId?: string, contactRequestsCount?: number): void {
|
||||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
if (!siteId) {
|
if (!siteId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.messagesProvider.getUnreadConversationsCount(undefined, siteId).then((unread) => {
|
if (this.updating) {
|
||||||
// Leave badge enter if there is a 0+ or a 0.
|
// An update is already in prgoress.
|
||||||
this.handler.badge = parseInt(unread, 10) > 0 ? unread : '';
|
return;
|
||||||
// Update badge.
|
}
|
||||||
this.pushNotificationsProvider.updateAddonCounter('AddonMessages', unread, siteId);
|
|
||||||
|
this.updating = true;
|
||||||
|
|
||||||
|
const promises = [];
|
||||||
|
let unreadCount = 0;
|
||||||
|
let unreadPlus = false;
|
||||||
|
|
||||||
|
promises.push(this.messagesProvider.getUnreadConversationsCount(undefined, siteId).then((unread) => {
|
||||||
|
unreadCount = parseInt(unread, 10);
|
||||||
|
unreadPlus = (typeof unread === 'string' && unread.slice(-1) === '+');
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
|
// Ignore error.
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Get the number of contact requests in 3.6+ sites if needed.
|
||||||
|
if (contactRequestsCount == null && this.messagesProvider.isGroupMessagingEnabled()) {
|
||||||
|
promises.push(this.messagesProvider.getContactRequestsCount(siteId).then((count) => {
|
||||||
|
contactRequestsCount = count;
|
||||||
|
}).catch(() => {
|
||||||
|
// Ignore errors
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
Promise.all(promises).then(() => {
|
||||||
|
const totalCount = unreadCount + (contactRequestsCount || 0);
|
||||||
|
if (totalCount > 0) {
|
||||||
|
this.handler.badge = totalCount + (unreadPlus ? '+' : '');
|
||||||
|
} else {
|
||||||
this.handler.badge = '';
|
this.handler.badge = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update badge.
|
||||||
|
this.pushNotificationsProvider.updateAddonCounter('AddonMessages', totalCount, siteId);
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.handler.loading = false;
|
this.handler.loading = false;
|
||||||
|
this.updating = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,8 @@ import { AddonMessagesOfflineProvider } from './messages-offline';
|
||||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||||
import { CoreEmulatorHelperProvider } from '@core/emulator/providers/helper';
|
import { CoreEmulatorHelperProvider } from '@core/emulator/providers/helper';
|
||||||
|
import { CoreEventsProvider } from '@providers/events';
|
||||||
|
import { CoreSite } from '@classes/site';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service to handle messages.
|
* Service to handle messages.
|
||||||
|
@ -28,7 +30,6 @@ import { CoreEmulatorHelperProvider } from '@core/emulator/providers/helper';
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AddonMessagesProvider {
|
export class AddonMessagesProvider {
|
||||||
protected ROOT_CACHE_KEY = 'mmaMessages:';
|
protected ROOT_CACHE_KEY = 'mmaMessages:';
|
||||||
protected LIMIT_SEARCH_MESSAGES = 50;
|
|
||||||
protected LIMIT_MESSAGES = AddonMessagesProvider.LIMIT_MESSAGES;
|
protected LIMIT_MESSAGES = AddonMessagesProvider.LIMIT_MESSAGES;
|
||||||
static NEW_MESSAGE_EVENT = 'addon_messages_new_message_event';
|
static NEW_MESSAGE_EVENT = 'addon_messages_new_message_event';
|
||||||
static READ_CHANGED_EVENT = 'addon_messages_read_changed_event';
|
static READ_CHANGED_EVENT = 'addon_messages_read_changed_event';
|
||||||
|
@ -36,6 +37,8 @@ export class AddonMessagesProvider {
|
||||||
static OPEN_CONVERSATION_EVENT = 'addon_messages_open_conversation_event'; // Notify that a conversation should be opened.
|
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 SPLIT_VIEW_LOAD_EVENT = 'addon_messages_split_view_load_event';
|
||||||
static UPDATE_CONVERSATION_LIST_EVENT = 'addon_messages_update_conversation_list_event';
|
static UPDATE_CONVERSATION_LIST_EVENT = 'addon_messages_update_conversation_list_event';
|
||||||
|
static MEMBER_INFO_CHANGED_EVENT = 'addon_messages_member_changed_event';
|
||||||
|
static CONTACT_REQUESTS_COUNT_EVENT = 'addon_messages_contact_requests_count_event';
|
||||||
static POLL_INTERVAL = 10000;
|
static POLL_INTERVAL = 10000;
|
||||||
static PUSH_SIMULATION_COMPONENT = 'AddonMessagesPushSimulation';
|
static PUSH_SIMULATION_COMPONENT = 'AddonMessagesPushSimulation';
|
||||||
|
|
||||||
|
@ -44,7 +47,10 @@ export class AddonMessagesProvider {
|
||||||
static MESSAGE_PRIVACY_SITE = 2; // Privacy setting for being messaged by anyone on the site.
|
static MESSAGE_PRIVACY_SITE = 2; // Privacy setting for being messaged by anyone on the site.
|
||||||
static MESSAGE_CONVERSATION_TYPE_INDIVIDUAL = 1; // An individual conversation.
|
static MESSAGE_CONVERSATION_TYPE_INDIVIDUAL = 1; // An individual conversation.
|
||||||
static MESSAGE_CONVERSATION_TYPE_GROUP = 2; // A group conversation.
|
static MESSAGE_CONVERSATION_TYPE_GROUP = 2; // A group conversation.
|
||||||
|
static LIMIT_CONTACTS = 50;
|
||||||
static LIMIT_MESSAGES = 50;
|
static LIMIT_MESSAGES = 50;
|
||||||
|
static LIMIT_INITIAL_USER_SEARCH = 3;
|
||||||
|
static LIMIT_SEARCH = 50;
|
||||||
|
|
||||||
static NOTIFICATION_PREFERENCES_KEY = 'message_provider_moodle_instantmessage';
|
static NOTIFICATION_PREFERENCES_KEY = 'message_provider_moodle_instantmessage';
|
||||||
|
|
||||||
|
@ -53,7 +59,7 @@ export class AddonMessagesProvider {
|
||||||
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private appProvider: CoreAppProvider,
|
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private appProvider: CoreAppProvider,
|
||||||
private userProvider: CoreUserProvider, private messagesOffline: AddonMessagesOfflineProvider,
|
private userProvider: CoreUserProvider, private messagesOffline: AddonMessagesOfflineProvider,
|
||||||
private utils: CoreUtilsProvider, private timeUtils: CoreTimeUtilsProvider,
|
private utils: CoreUtilsProvider, private timeUtils: CoreTimeUtilsProvider,
|
||||||
private emulatorHelper: CoreEmulatorHelperProvider) {
|
private emulatorHelper: CoreEmulatorHelperProvider, private eventsProvider: CoreEventsProvider) {
|
||||||
this.logger = logger.getInstance('AddonMessagesProvider');
|
this.logger = logger.getInstance('AddonMessagesProvider');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,6 +69,7 @@ export class AddonMessagesProvider {
|
||||||
* @param {number} userId User ID of the person to add.
|
* @param {number} userId User ID of the person to add.
|
||||||
* @param {string} [siteId] Site ID. If not defined, use current site.
|
* @param {string} [siteId] Site ID. If not defined, use current site.
|
||||||
* @return {Promise<any>} Resolved when done.
|
* @return {Promise<any>} Resolved when done.
|
||||||
|
* @deprecated since Moodle 3.6
|
||||||
*/
|
*/
|
||||||
addContact(userId: number, siteId?: string): Promise<any> {
|
addContact(userId: number, siteId?: string): Promise<any> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
@ -101,7 +108,90 @@ export class AddonMessagesProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
return promise.then(() => {
|
return promise.then(() => {
|
||||||
return this.invalidateAllContactsCache(site.getUserId(), site.getId());
|
return this.invalidateAllMemberInfo(userId, site).finally(() => {
|
||||||
|
const data = { userId, userBlocked: true };
|
||||||
|
this.eventsProvider.trigger(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, data, site.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Confirm a contact request from another user.
|
||||||
|
*
|
||||||
|
* @param {number} userId ID of the user who made the contact request.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, use current site.
|
||||||
|
* @return {Promise<any>} Resolved when done.
|
||||||
|
* @since 3.6
|
||||||
|
*/
|
||||||
|
confirmContactRequest(userId: number, siteId?: string): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
const params = {
|
||||||
|
userid: userId,
|
||||||
|
requesteduserid: site.getUserId(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.write('core_message_confirm_contact_request', params).then(() => {
|
||||||
|
return this.utils.allPromises([
|
||||||
|
this.invalidateAllMemberInfo(userId, site),
|
||||||
|
this.invalidateContactsCache(site.id),
|
||||||
|
this.invalidateUserContacts(site.id),
|
||||||
|
this.refreshContactRequestsCount(site.id),
|
||||||
|
]).finally(() => {
|
||||||
|
const data = { userId, contactRequestConfirmed: true };
|
||||||
|
this.eventsProvider.trigger(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, data, site.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a contact request to another user.
|
||||||
|
*
|
||||||
|
* @param {number} userId ID of the receiver of the contact request.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, use current site.
|
||||||
|
* @return {Promise<any>} Resolved when done.
|
||||||
|
* @since 3.6
|
||||||
|
*/
|
||||||
|
createContactRequest(userId: number, siteId?: string): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
const params = {
|
||||||
|
userid: site.getUserId(),
|
||||||
|
requesteduserid: userId,
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.write('core_message_create_contact_request', params).then(() => {
|
||||||
|
return this.invalidateAllMemberInfo(userId, site).finally(() => {
|
||||||
|
const data = { userId, contactRequestCreated: true };
|
||||||
|
this.eventsProvider.trigger(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, data, site.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decline a contact request from another user.
|
||||||
|
*
|
||||||
|
* @param {number} userId ID of the user who made the contact request.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, use current site.
|
||||||
|
* @return {Promise<any>} Resolved when done.
|
||||||
|
* @since 3.6
|
||||||
|
*/
|
||||||
|
declineContactRequest(userId: number, siteId?: string): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
const params = {
|
||||||
|
userid: userId,
|
||||||
|
requesteduserid: site.getUserId(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.write('core_message_decline_contact_request', params).then(() => {
|
||||||
|
return this.utils.allPromises([
|
||||||
|
this.invalidateAllMemberInfo(userId, site),
|
||||||
|
this.refreshContactRequestsCount(site.id),
|
||||||
|
]).finally(() => {
|
||||||
|
const data = { userId, contactRequestDeclined: true };
|
||||||
|
this.eventsProvider.trigger(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, data, site.id);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -247,6 +337,33 @@ export class AddonMessagesProvider {
|
||||||
return this.ROOT_CACHE_KEY + 'contacts';
|
return this.ROOT_CACHE_KEY + 'contacts';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the cache key for comfirmed contacts.
|
||||||
|
*
|
||||||
|
* @return {string} Cache key.
|
||||||
|
*/
|
||||||
|
protected getCacheKeyForUserContacts(): string {
|
||||||
|
return this.ROOT_CACHE_KEY + 'userContacts';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the cache key for contact requests.
|
||||||
|
*
|
||||||
|
* @return {string} Cache key.
|
||||||
|
*/
|
||||||
|
protected getCacheKeyForContactRequests(): string {
|
||||||
|
return this.ROOT_CACHE_KEY + 'contactRequests';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the cache key for contact requests count.
|
||||||
|
*
|
||||||
|
* @return {string} Cache key.
|
||||||
|
*/
|
||||||
|
protected getCacheKeyForContactRequestsCount(): string {
|
||||||
|
return this.ROOT_CACHE_KEY + 'contactRequestsCount';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the cache key for a discussion.
|
* Get the cache key for a discussion.
|
||||||
*
|
*
|
||||||
|
@ -332,6 +449,17 @@ export class AddonMessagesProvider {
|
||||||
return this.getCommonCacheKeyForUserConversations(userId) + ':' + type + ':' + favourites;
|
return this.getCommonCacheKeyForUserConversations(userId) + ':' + type + ':' + favourites;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cache key for member info.
|
||||||
|
*
|
||||||
|
* @param {number} userId User ID.
|
||||||
|
* @param {number} otherUserId The other user ID.
|
||||||
|
* @return {string} Cache key.
|
||||||
|
*/
|
||||||
|
protected getCacheKeyForMemberInfo(userId: number, otherUserId: number): string {
|
||||||
|
return this.ROOT_CACHE_KEY + 'memberInfo:' + userId + ':' + otherUserId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get common cache key for get user conversations.
|
* Get common cache key for get user conversations.
|
||||||
*
|
*
|
||||||
|
@ -356,6 +484,7 @@ export class AddonMessagesProvider {
|
||||||
*
|
*
|
||||||
* @param {string} [siteId] Site ID. If not defined, use current site.
|
* @param {string} [siteId] Site ID. If not defined, use current site.
|
||||||
* @return {Promise<any>} Resolved with the WS data.
|
* @return {Promise<any>} Resolved with the WS data.
|
||||||
|
* @deprecated since Moodle 3.6
|
||||||
*/
|
*/
|
||||||
getAllContacts(siteId?: string): Promise<any> {
|
getAllContacts(siteId?: string): Promise<any> {
|
||||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
@ -377,7 +506,7 @@ export class AddonMessagesProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all the blocked contacts of the current user.
|
* Get all the users blocked by the current user.
|
||||||
*
|
*
|
||||||
* @param {string} [siteId] Site ID. If not defined, use current site.
|
* @param {string} [siteId] Site ID. If not defined, use current site.
|
||||||
* @return {Promise<any>} Resolved with the WS data.
|
* @return {Promise<any>} Resolved with the WS data.
|
||||||
|
@ -403,6 +532,7 @@ export class AddonMessagesProvider {
|
||||||
*
|
*
|
||||||
* @param {string} [siteId] Site ID. If not defined, use current site.
|
* @param {string} [siteId] Site ID. If not defined, use current site.
|
||||||
* @return {Promise<any>} Resolved with the WS data.
|
* @return {Promise<any>} Resolved with the WS data.
|
||||||
|
* @deprecated since Moodle 3.6
|
||||||
*/
|
*/
|
||||||
getContacts(siteId?: string): Promise<any> {
|
getContacts(siteId?: string): Promise<any> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
@ -430,6 +560,114 @@ export class AddonMessagesProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of user contacts.
|
||||||
|
*
|
||||||
|
* @param {number} [limitFrom=0] Position of the first contact to fetch.
|
||||||
|
* @param {number} [limitNum] Number of contacts to fetch. Default is AddonMessagesProvider.LIMIT_CONTACTS.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, use current site.
|
||||||
|
* @return {Promise<{contacts: any[], canLoadMore: boolean}>} Resolved with the list of user contacts.
|
||||||
|
* @since 3.6
|
||||||
|
*/
|
||||||
|
getUserContacts(limitFrom: number = 0, limitNum: number = AddonMessagesProvider.LIMIT_CONTACTS , siteId?: string):
|
||||||
|
Promise<{contacts: any[], canLoadMore: boolean}> {
|
||||||
|
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
const params = {
|
||||||
|
userid: site.getUserId(),
|
||||||
|
limitfrom: limitFrom,
|
||||||
|
limitnum: limitNum <= 0 ? 0 : limitNum + 1
|
||||||
|
};
|
||||||
|
const preSets = {
|
||||||
|
cacheKey: this.getCacheKeyForUserContacts()
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.read('core_message_get_user_contacts', params, preSets).then((contacts) => {
|
||||||
|
if (!contacts || !contacts.length) {
|
||||||
|
return { contacts: [], canLoadMore: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
this.userProvider.storeUsers(contacts, site.id);
|
||||||
|
|
||||||
|
if (limitNum <= 0) {
|
||||||
|
return { contacts, canLoadMore: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
contacts: contacts.slice(0, limitNum),
|
||||||
|
canLoadMore: contacts.length > limitNum
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the contact request sent to the current user.
|
||||||
|
*
|
||||||
|
* @param {number} [limitFrom=0] Position of the first contact request to fetch.
|
||||||
|
* @param {number} [limitNum] Number of contact requests to fetch. Default is AddonMessagesProvider.LIMIT_CONTACTS.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, use current site.
|
||||||
|
* @return {Promise<{requests: any[], canLoadMore: boolean}>} Resolved with the list of contact requests.
|
||||||
|
* @since 3.6
|
||||||
|
*/
|
||||||
|
getContactRequests(limitFrom: number = 0, limitNum: number = AddonMessagesProvider.LIMIT_CONTACTS, siteId?: string):
|
||||||
|
Promise<{requests: any[], canLoadMore: boolean}> {
|
||||||
|
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
const data = {
|
||||||
|
userid: site.getUserId(),
|
||||||
|
limitfrom: limitFrom,
|
||||||
|
limitnum: limitNum <= 0 ? 0 : limitNum + 1
|
||||||
|
};
|
||||||
|
const preSets = {
|
||||||
|
cacheKey: this.getCacheKeyForContactRequests()
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.read('core_message_get_contact_requests', data, preSets).then((requests) => {
|
||||||
|
if (!requests || !requests.length) {
|
||||||
|
return { requests: [], canLoadMore: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
this.userProvider.storeUsers(requests, site.id);
|
||||||
|
|
||||||
|
if (limitNum <= 0) {
|
||||||
|
return { requests, canLoadMore: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
requests: requests.slice(0, limitNum),
|
||||||
|
canLoadMore: requests.length > limitNum
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of contact requests sent to the current user.
|
||||||
|
*
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, use current site.
|
||||||
|
* @return {Promise<number>} Resolved with the number of contact requests.
|
||||||
|
* @since 3.6
|
||||||
|
*/
|
||||||
|
getContactRequestsCount(siteId?: string): Promise<number> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
const data = {
|
||||||
|
userid: site.getUserId(),
|
||||||
|
};
|
||||||
|
const preSets = {
|
||||||
|
cacheKey: this.getCacheKeyForContactRequestsCount(),
|
||||||
|
typeExpected: 'number'
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.read('core_message_get_received_contact_requests_count', data, preSets).then((count) => {
|
||||||
|
// Notify the new count so all badges are updated.
|
||||||
|
this.eventsProvider.trigger(AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT, { count }, site.id);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a conversation by the conversation ID.
|
* Get a conversation by the conversation ID.
|
||||||
*
|
*
|
||||||
|
@ -491,18 +729,20 @@ export class AddonMessagesProvider {
|
||||||
* @param {boolean} [newestFirst=true] Whether to order messages by newest first.
|
* @param {boolean} [newestFirst=true] Whether to order messages by newest first.
|
||||||
* @param {string} [siteId] Site ID. If not defined, use current site.
|
* @param {string} [siteId] Site ID. If not defined, use current site.
|
||||||
* @param {number} [userId] User ID. If not defined, current user in the site.
|
* @param {number} [userId] User ID. If not defined, current user in the site.
|
||||||
|
* @param {boolean} [preferCache] True if shouldn't call WS if data is cached, false otherwise.
|
||||||
* @return {Promise<any>} Promise resolved with the response.
|
* @return {Promise<any>} Promise resolved with the response.
|
||||||
* @since 3.6
|
* @since 3.6
|
||||||
*/
|
*/
|
||||||
getConversationBetweenUsers(otherUserId: number, includeContactRequests?: boolean, includePrivacyInfo?: boolean,
|
getConversationBetweenUsers(otherUserId: number, includeContactRequests?: boolean, includePrivacyInfo?: boolean,
|
||||||
messageOffset: number = 0, messageLimit: number = 1, memberOffset: number = 0, memberLimit: number = 2,
|
messageOffset: number = 0, messageLimit: number = 1, memberOffset: number = 0, memberLimit: number = 2,
|
||||||
newestFirst: boolean = true, siteId?: string, userId?: number): Promise<any> {
|
newestFirst: boolean = true, siteId?: string, userId?: number, preferCache?: boolean): Promise<any> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
userId = userId || site.getUserId();
|
userId = userId || site.getUserId();
|
||||||
|
|
||||||
const preSets = {
|
const preSets = {
|
||||||
cacheKey: this.getCacheKeyForConversationBetweenUsers(userId, otherUserId)
|
cacheKey: this.getCacheKeyForConversationBetweenUsers(userId, otherUserId),
|
||||||
|
omitExpires: !!preferCache,
|
||||||
},
|
},
|
||||||
params: any = {
|
params: any = {
|
||||||
userid: userId,
|
userid: userId,
|
||||||
|
@ -900,6 +1140,40 @@ export class AddonMessagesProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get conversation member info by user id, works even if no conversation betwen the users exists.
|
||||||
|
*
|
||||||
|
* @param {number} otherUserId The other user ID.
|
||||||
|
* @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 with the member info.
|
||||||
|
* @since 3.6
|
||||||
|
*/
|
||||||
|
getMemberInfo(otherUserId: number, siteId?: string, userId?: number): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
userId = userId || site.getUserId();
|
||||||
|
|
||||||
|
const preSets = {
|
||||||
|
cacheKey: this.getCacheKeyForMemberInfo(userId, otherUserId)
|
||||||
|
},
|
||||||
|
params: any = {
|
||||||
|
referenceuserid: userId,
|
||||||
|
userids: [otherUserId],
|
||||||
|
includecontactrequests: 1,
|
||||||
|
includeprivacyinfo: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.read('core_message_get_member_info', params, preSets).then((members) => {
|
||||||
|
if (!members || members.length < 1) {
|
||||||
|
// Should never happen.
|
||||||
|
return Promise.reject(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return members[0];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the cache key for the get message preferences call.
|
* Get the cache key for the get message preferences call.
|
||||||
*
|
*
|
||||||
|
@ -1146,6 +1420,42 @@ export class AddonMessagesProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate user contacts cache.
|
||||||
|
*
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Resolved when done.
|
||||||
|
*/
|
||||||
|
invalidateUserContacts(siteId?: string): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
return site.invalidateWsCacheForKey(this.getCacheKeyForUserContacts());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate contact requests cache.
|
||||||
|
*
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Resolved when done.
|
||||||
|
*/
|
||||||
|
invalidateContactRequestsCache(siteId?: string): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
return site.invalidateWsCacheForKey(this.getCacheKeyForContactRequests());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate contact requests count cache.
|
||||||
|
*
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Resolved when done.
|
||||||
|
*/
|
||||||
|
invalidateContactRequestsCountCache(siteId?: string): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
return site.invalidateWsCacheForKey(this.getCacheKeyForContactRequestsCount());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invalidate conversation.
|
* Invalidate conversation.
|
||||||
*
|
*
|
||||||
|
@ -1256,6 +1566,22 @@ export class AddonMessagesProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate member info cache.
|
||||||
|
*
|
||||||
|
* @param {number} otherUserId The other user ID.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @param {number} [userId] User ID. If not defined, current user in the site.
|
||||||
|
* @return {Promise<any>} Resolved when done.
|
||||||
|
*/
|
||||||
|
invalidateMemberInfo(otherUserId: number, siteId?: string, userId?: number): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
userId = userId || site.getUserId();
|
||||||
|
|
||||||
|
return site.invalidateWsCacheForKey(this.getCacheKeyForMemberInfo(userId, otherUserId));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invalidate get message preferences.
|
* Invalidate get message preferences.
|
||||||
*
|
*
|
||||||
|
@ -1268,6 +1594,31 @@ export class AddonMessagesProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate all cache entries with member info.
|
||||||
|
*
|
||||||
|
* @param {number} userId Id of the user to invalidate.
|
||||||
|
* @param {CoreSite} site Site object.
|
||||||
|
* @return {Promie<any>} Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected invalidateAllMemberInfo(userId: number, site: CoreSite): Promise<any> {
|
||||||
|
return this.utils.allPromises([
|
||||||
|
this.invalidateMemberInfo(userId, site.id),
|
||||||
|
this.invalidateUserContacts(site.id),
|
||||||
|
this.invalidateContactRequestsCache(site.id),
|
||||||
|
this.invalidateConversations(site.id),
|
||||||
|
this.getConversationBetweenUsers(userId, undefined, undefined, undefined, undefined, undefined, undefined, undefined,
|
||||||
|
site.id, undefined, true).then((conversation) => {
|
||||||
|
return this.utils.allPromises([
|
||||||
|
this.invalidateConversation(conversation.id),
|
||||||
|
this.invalidateConversationMembers(conversation.id, site.id),
|
||||||
|
]);
|
||||||
|
}).catch(() => {
|
||||||
|
// The conversation does not exist or we can't fetch it now, ignore it.
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the a user is blocked by the current user.
|
* Checks if the a user is blocked by the current user.
|
||||||
*
|
*
|
||||||
|
@ -1276,6 +1627,12 @@ export class AddonMessagesProvider {
|
||||||
* @return {Promise<boolean>} Resolved with boolean, rejected when we do not know.
|
* @return {Promise<boolean>} Resolved with boolean, rejected when we do not know.
|
||||||
*/
|
*/
|
||||||
isBlocked(userId: number, siteId?: string): Promise<boolean> {
|
isBlocked(userId: number, siteId?: string): Promise<boolean> {
|
||||||
|
if (this.isGroupMessagingEnabled()) {
|
||||||
|
return this.getMemberInfo(userId, siteId).then((member) => {
|
||||||
|
return member.isblocked;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return this.getBlockedContacts(siteId).then((blockedContacts) => {
|
return this.getBlockedContacts(siteId).then((blockedContacts) => {
|
||||||
if (!blockedContacts.users || blockedContacts.users.length < 1) {
|
if (!blockedContacts.users || blockedContacts.users.length < 1) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1295,6 +1652,12 @@ export class AddonMessagesProvider {
|
||||||
* @return {Promise<boolean>} Resolved with boolean, rejected when we do not know.
|
* @return {Promise<boolean>} Resolved with boolean, rejected when we do not know.
|
||||||
*/
|
*/
|
||||||
isContact(userId: number, siteId?: string): Promise<boolean> {
|
isContact(userId: number, siteId?: string): Promise<boolean> {
|
||||||
|
if (this.isGroupMessagingEnabled()) {
|
||||||
|
return this.getMemberInfo(userId, siteId).then((member) => {
|
||||||
|
return member.iscontact;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return this.getContacts(siteId).then((contacts) => {
|
return this.getContacts(siteId).then((contacts) => {
|
||||||
return ['online', 'offline'].some((type) => {
|
return ['online', 'offline'].some((type) => {
|
||||||
if (contacts[type] && contacts[type].length > 0) {
|
if (contacts[type] && contacts[type].length > 0) {
|
||||||
|
@ -1438,6 +1801,21 @@ export class AddonMessagesProvider {
|
||||||
return this.sitesProvider.getCurrentSite().write('core_message_mark_all_messages_as_read', params, preSets);
|
return this.sitesProvider.getCurrentSite().write('core_message_mark_all_messages_as_read', params, preSets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh the number of contact requests sent to the current user.
|
||||||
|
*
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, use current site.
|
||||||
|
* @return {Promise<number>} Resolved with the number of contact requests.
|
||||||
|
* @since 3.6
|
||||||
|
*/
|
||||||
|
refreshContactRequestsCount(siteId?: string): Promise<number> {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
return this.invalidateContactRequestsCountCache(siteId).then(() => {
|
||||||
|
return this.getContactRequestsCount(siteId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a contact.
|
* Remove a contact.
|
||||||
*
|
*
|
||||||
|
@ -1455,7 +1833,17 @@ export class AddonMessagesProvider {
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.write('core_message_delete_contacts', params, preSets).then(() => {
|
return site.write('core_message_delete_contacts', params, preSets).then(() => {
|
||||||
return this.invalidateContactsCache(site.getId());
|
if (this.isGroupMessagingEnabled()) {
|
||||||
|
return this.utils.allPromises([
|
||||||
|
this.invalidateUserContacts(site.id),
|
||||||
|
this.invalidateAllMemberInfo(userId, site),
|
||||||
|
]).then(() => {
|
||||||
|
const data = { userId, contactRemoved: true };
|
||||||
|
this.eventsProvider.trigger(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, data, site.id);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return this.invalidateContactsCache(site.id);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1496,28 +1884,91 @@ export class AddonMessagesProvider {
|
||||||
/**
|
/**
|
||||||
* Search for all the messges with a specific text.
|
* Search for all the messges with a specific text.
|
||||||
*
|
*
|
||||||
* @param {string} query The query string
|
* @param {string} query The query string.
|
||||||
* @param {number} [userId] The user ID. If not defined, current user.
|
* @param {number} [userId] The user ID. If not defined, current user.
|
||||||
* @param {number} [from=0] Position of the first result to get. Defaults to 0.
|
* @param {number} [limitFrom=0] Position of the first result to get. Defaults to 0.
|
||||||
* @param {number} [limit] Number of results to get. Defaults to LIMIT_SEARCH_MESSAGES.
|
* @param {number} [limitNum] Number of results to get. Defaults to AddonMessagesProvider.LIMIT_SEARCH.
|
||||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
* @return {Promise<any>} Promise resolved with the results.
|
* @return {Promise<any>} Promise resolved with the results.
|
||||||
*/
|
*/
|
||||||
searchMessages(query: string, userId?: number, from: number = 0, limit: number = this.LIMIT_SEARCH_MESSAGES, siteId?: string):
|
searchMessages(query: string, userId?: number, limitFrom: number = 0, limitNum: number = AddonMessagesProvider.LIMIT_SEARCH,
|
||||||
Promise<any> {
|
siteId?: string): Promise<{messages: any[], canLoadMore: boolean}> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const param = {
|
const params = {
|
||||||
userid: userId || site.getUserId(),
|
userid: userId || site.getUserId(),
|
||||||
search: query,
|
search: query,
|
||||||
limitfrom: from,
|
limitfrom: limitFrom,
|
||||||
limitnum: limit
|
limitnum: limitNum <= 0 ? 0 : limitNum + 1
|
||||||
},
|
},
|
||||||
preSets = {
|
preSets = {
|
||||||
getFromCache: false // Always try to get updated data. If it fails, it will get it from cache.
|
getFromCache: false // Always try to get updated data. If it fails, it will get it from cache.
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('core_message_data_for_messagearea_search_messages', param, preSets).then((searchResults) => {
|
return site.read('core_message_data_for_messagearea_search_messages', params, preSets).then((result) => {
|
||||||
return searchResults.contacts;
|
if (!result.contacts || !result.contacts.length) {
|
||||||
|
return { messages: [], canLoadMore: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
result.contacts.forEach((result) => {
|
||||||
|
result.id = result.userid;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.userProvider.storeUsers(result.contacts, site.id);
|
||||||
|
|
||||||
|
if (limitNum <= 0) {
|
||||||
|
return { messages: result.contacts, canLoadMore: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
messages: result.contacts.slice(0, limitNum),
|
||||||
|
canLoadMore: result.contacts.length > limitNum
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for users.
|
||||||
|
*
|
||||||
|
* @param {string} query Text to search for.
|
||||||
|
* @param {number} [limitFrom=0] Position of the first found user to fetch.
|
||||||
|
* @param {number} [limitNum] Number of found users to fetch. Defaults to AddonMessagesProvider.LIMIT_SEARCH.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, use current site.
|
||||||
|
* @return {Promise<any>} Resolved with two lists of found users: contacts and non-contacts.
|
||||||
|
* @since 3.6
|
||||||
|
*/
|
||||||
|
searchUsers(query: string, limitFrom: number = 0, limitNum: number = AddonMessagesProvider.LIMIT_SEARCH, siteId?: string):
|
||||||
|
Promise<{contacts: any[], nonContacts: any[], canLoadMoreContacts: boolean, canLoadMoreNonContacts: boolean}> {
|
||||||
|
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
const data = {
|
||||||
|
userid: site.getUserId(),
|
||||||
|
search: query,
|
||||||
|
limitfrom: limitFrom,
|
||||||
|
limitnum: limitNum <= 0 ? 0 : limitNum + 1
|
||||||
|
},
|
||||||
|
preSets = {
|
||||||
|
getFromCache: false // Always try to get updated data. If it fails, it will get it from cache.
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.read('core_message_message_search_users', data, preSets).then((result) => {
|
||||||
|
const contacts = result.contacts || [];
|
||||||
|
const nonContacts = result.noncontacts || [];
|
||||||
|
|
||||||
|
this.userProvider.storeUsers(contacts, site.id);
|
||||||
|
this.userProvider.storeUsers(nonContacts, site.id);
|
||||||
|
|
||||||
|
if (limitNum <= 0) {
|
||||||
|
return { contacts, nonContacts, canLoadMoreContacts: false, canLoadMoreNonContacts: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
contacts: contacts.slice(0, limitNum),
|
||||||
|
nonContacts: nonContacts.slice(0, limitNum),
|
||||||
|
canLoadMoreContacts: contacts.length > limitNum,
|
||||||
|
canLoadMoreNonContacts: nonContacts.length > limitNum
|
||||||
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1918,7 +2369,10 @@ export class AddonMessagesProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
return promise.then(() => {
|
return promise.then(() => {
|
||||||
return this.invalidateAllContactsCache(site.getUserId(), site.getId());
|
return this.invalidateAllMemberInfo(userId, site).finally(() => {
|
||||||
|
const data = { userId, userUnblocked: true };
|
||||||
|
this.eventsProvider.trigger(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, data, site.id);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,14 +95,14 @@ export class AddonMessagesAddContactUserHandler implements CoreUserProfileHandle
|
||||||
|
|
||||||
this.messagesProvider.isContact(user.id).then((isContact) => {
|
this.messagesProvider.isContact(user.id).then((isContact) => {
|
||||||
if (isContact) {
|
if (isContact) {
|
||||||
const template = this.translate.instant('addon.messages.removecontactconfirm'),
|
const message = this.translate.instant('addon.messages.removecontactconfirm', {$a: user.fullname});
|
||||||
title = this.translate.instant('addon.messages.removecontact');
|
const okText = this.translate.instant('core.remove');
|
||||||
|
|
||||||
return this.domUtils.showConfirm(template, title, title).then(() => {
|
return this.domUtils.showConfirm(message, undefined, okText).then(() => {
|
||||||
return this.messagesProvider.removeContact(user.id);
|
return this.messagesProvider.removeContact(user.id);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return this.messagesProvider.addContact(user.id);
|
return this.addContact(user);
|
||||||
}
|
}
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
this.domUtils.showErrorModalDefault(error, 'core.error', true);
|
this.domUtils.showErrorModalDefault(error, 'core.error', true);
|
||||||
|
@ -125,10 +125,12 @@ export class AddonMessagesAddContactUserHandler implements CoreUserProfileHandle
|
||||||
protected checkButton(userId: number): Promise<void> {
|
protected checkButton(userId: number): Promise<void> {
|
||||||
this.updateButton(userId, {spinner: true});
|
this.updateButton(userId, {spinner: true});
|
||||||
|
|
||||||
|
const groupMessagingEnabled = this.messagesProvider.isGroupMessagingEnabled();
|
||||||
|
|
||||||
return this.messagesProvider.isContact(userId).then((isContact) => {
|
return this.messagesProvider.isContact(userId).then((isContact) => {
|
||||||
if (isContact) {
|
if (isContact) {
|
||||||
this.updateButton(userId, {
|
this.updateButton(userId, {
|
||||||
title: 'addon.messages.removecontact',
|
title: groupMessagingEnabled ? 'addon.messages.removefromyourcontacts' : 'addon.messages.removecontact',
|
||||||
class: 'addon-messages-removecontact-handler',
|
class: 'addon-messages-removecontact-handler',
|
||||||
icon: 'remove',
|
icon: 'remove',
|
||||||
hidden: false,
|
hidden: false,
|
||||||
|
@ -136,7 +138,7 @@ export class AddonMessagesAddContactUserHandler implements CoreUserProfileHandle
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.updateButton(userId, {
|
this.updateButton(userId, {
|
||||||
title: 'addon.messages.addcontact',
|
title: groupMessagingEnabled ? 'addon.messages.addtoyourcontacts' : 'addon.messages.addcontact',
|
||||||
class: 'addon-messages-addcontact-handler',
|
class: 'addon-messages-addcontact-handler',
|
||||||
icon: 'add',
|
icon: 'add',
|
||||||
hidden: false,
|
hidden: false,
|
||||||
|
@ -160,6 +162,42 @@ export class AddonMessagesAddContactUserHandler implements CoreUserProfileHandle
|
||||||
this.eventsProvider.trigger(CoreUserDelegate.UPDATE_HANDLER_EVENT, { handler: this.name, data: data, userId: userId });
|
this.eventsProvider.trigger(CoreUserDelegate.UPDATE_HANDLER_EVENT, { handler: this.name, data: data, userId: userId });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a contact or send a contact request if group messaging is enabled.
|
||||||
|
*
|
||||||
|
* @param {any} user User to add as contact.
|
||||||
|
* @return {Promise<any>} Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected addContact(user: any): Promise<any> {
|
||||||
|
if (!this.messagesProvider.isGroupMessagingEnabled()) {
|
||||||
|
return this.messagesProvider.addContact(user.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.messagesProvider.getMemberInfo(user.id).then((member) => {
|
||||||
|
const currentUserId = this.sitesProvider.getCurrentSiteUserId();
|
||||||
|
const requestSent = member.contactrequests.some((request) => {
|
||||||
|
return request.userid == currentUserId && request.requesteduserid == user.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (requestSent) {
|
||||||
|
const message = this.translate.instant('addon.messages.yourcontactrequestpending', {$a: user.fullname});
|
||||||
|
|
||||||
|
return this.domUtils.showAlert(null, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = this.translate.instant('addon.messages.addcontactconfirm', {$a: user.fullname});
|
||||||
|
const okText = this.translate.instant('core.add');
|
||||||
|
|
||||||
|
return this.domUtils.showConfirm(message, undefined, okText).then(() => {
|
||||||
|
return this.messagesProvider.createContactRequest(user.id);
|
||||||
|
}).then(() => {
|
||||||
|
const message = this.translate.instant('addon.messages.contactrequestsent');
|
||||||
|
|
||||||
|
return this.domUtils.showAlert(null, message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroyed method.
|
* Destroyed method.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -48,7 +48,7 @@ export class AddonNotificationsProvider {
|
||||||
protected formatNotificationsData(notifications: any[]): void {
|
protected formatNotificationsData(notifications: any[]): void {
|
||||||
notifications.forEach((notification) => {
|
notifications.forEach((notification) => {
|
||||||
// Set message to show.
|
// Set message to show.
|
||||||
if (notification.contexturl && notification.contexturl.indexOf('/mod/forum/')) {
|
if (notification.contexturl && notification.contexturl.indexOf('/mod/forum/') >= 0) {
|
||||||
notification.mobiletext = notification.smallmessage;
|
notification.mobiletext = notification.smallmessage;
|
||||||
} else {
|
} else {
|
||||||
notification.mobiletext = notification.fullmessage;
|
notification.mobiletext = notification.fullmessage;
|
||||||
|
|
|
@ -147,8 +147,11 @@
|
||||||
"addon.files.privatefiles": "Private files",
|
"addon.files.privatefiles": "Private files",
|
||||||
"addon.files.sitefiles": "Site files",
|
"addon.files.sitefiles": "Site files",
|
||||||
"addon.messageoutput_airnotifier.processorsettingsdesc": "Configure devices",
|
"addon.messageoutput_airnotifier.processorsettingsdesc": "Configure devices",
|
||||||
|
"addon.messages.acceptandaddcontact": "Accept and add to contacts",
|
||||||
"addon.messages.addcontact": "Add contact",
|
"addon.messages.addcontact": "Add contact",
|
||||||
|
"addon.messages.addcontactconfirm": "Are you sure you want to add {{$a}} to your contacts?",
|
||||||
"addon.messages.addtofavourites": "Star",
|
"addon.messages.addtofavourites": "Star",
|
||||||
|
"addon.messages.addtoyourcontacts": "Add to contacts",
|
||||||
"addon.messages.blocknoncontacts": "Prevent non-contacts from messaging me",
|
"addon.messages.blocknoncontacts": "Prevent non-contacts from messaging me",
|
||||||
"addon.messages.blockuser": "Block user",
|
"addon.messages.blockuser": "Block user",
|
||||||
"addon.messages.blockuserconfirm": "Are you sure you want to block {{$a}}?",
|
"addon.messages.blockuserconfirm": "Are you sure you want to block {{$a}}?",
|
||||||
|
@ -159,7 +162,9 @@
|
||||||
"addon.messages.contactblocked": "Contact blocked",
|
"addon.messages.contactblocked": "Contact blocked",
|
||||||
"addon.messages.contactlistempty": "The contact list is empty",
|
"addon.messages.contactlistempty": "The contact list is empty",
|
||||||
"addon.messages.contactname": "Contact name",
|
"addon.messages.contactname": "Contact name",
|
||||||
|
"addon.messages.contactrequestsent": "Contact request sent",
|
||||||
"addon.messages.contacts": "Contacts",
|
"addon.messages.contacts": "Contacts",
|
||||||
|
"addon.messages.decline": "Decline",
|
||||||
"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.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.deleteconversation": "Delete conversation",
|
||||||
"addon.messages.deletemessage": "Delete message",
|
"addon.messages.deletemessage": "Delete message",
|
||||||
|
@ -168,34 +173,52 @@
|
||||||
"addon.messages.errorwhileretrievingcontacts": "Error while retrieving contacts from the server.",
|
"addon.messages.errorwhileretrievingcontacts": "Error while retrieving contacts from the server.",
|
||||||
"addon.messages.errorwhileretrievingdiscussions": "Error while retrieving discussions from the server.",
|
"addon.messages.errorwhileretrievingdiscussions": "Error while retrieving discussions from the server.",
|
||||||
"addon.messages.errorwhileretrievingmessages": "Error while retrieving messages from the server.",
|
"addon.messages.errorwhileretrievingmessages": "Error while retrieving messages from the server.",
|
||||||
|
"addon.messages.errorwhileretrievingusers": "Error while retrieving users from the server.",
|
||||||
"addon.messages.groupinfo": "Group info",
|
"addon.messages.groupinfo": "Group info",
|
||||||
"addon.messages.groupmessages": "Group messages",
|
"addon.messages.groupmessages": "Group messages",
|
||||||
"addon.messages.info": "Info",
|
"addon.messages.info": "Info",
|
||||||
|
"addon.messages.isnotinyourcontacts": "{{$a}} is not in your contacts",
|
||||||
"addon.messages.message": "Message",
|
"addon.messages.message": "Message",
|
||||||
"addon.messages.messagenotsent": "The message was not sent. Please try again later.",
|
"addon.messages.messagenotsent": "The message was not sent. Please try again later.",
|
||||||
"addon.messages.messagepreferences": "Message preferences",
|
"addon.messages.messagepreferences": "Message preferences",
|
||||||
"addon.messages.messages": "Messages",
|
"addon.messages.messages": "Messages",
|
||||||
"addon.messages.newmessage": "New message",
|
"addon.messages.newmessage": "New message",
|
||||||
"addon.messages.newmessages": "New messages",
|
"addon.messages.newmessages": "New messages",
|
||||||
|
"addon.messages.nocontactrequests": "No contact requests",
|
||||||
|
"addon.messages.nocontactsgetstarted": "Try searching for someone to add them as a contact",
|
||||||
"addon.messages.nofavourites": "No favourites",
|
"addon.messages.nofavourites": "No favourites",
|
||||||
"addon.messages.nogroupmessages": "No group messages",
|
"addon.messages.nogroupmessages": "No group messages",
|
||||||
"addon.messages.nomessages": "No messages",
|
"addon.messages.nomessages": "No messages",
|
||||||
|
"addon.messages.noncontacts": "Non-contacts",
|
||||||
"addon.messages.nousersfound": "No users found",
|
"addon.messages.nousersfound": "No users found",
|
||||||
"addon.messages.numparticipants": "{{$a}} participants",
|
"addon.messages.numparticipants": "{{$a}} participants",
|
||||||
"addon.messages.removecontact": "Remove contact",
|
"addon.messages.removecontact": "Remove contact",
|
||||||
"addon.messages.removecontactconfirm": "Contact will be removed from your contacts list.",
|
"addon.messages.removecontactconfirm": "Are you sure you want to remove {{$a}} from your contacts?",
|
||||||
"addon.messages.removefromfavourites": "Unstar",
|
"addon.messages.removefromfavourites": "Unstar",
|
||||||
|
"addon.messages.removefromyourcontacts": "Remove from contacts",
|
||||||
|
"addon.messages.requests": "Requests",
|
||||||
|
"addon.messages.requirecontacttomessage": "You need to request {{$a}} to add you as a contact to be able to message them.",
|
||||||
|
"addon.messages.searchcombined": "Search people and messages",
|
||||||
|
"addon.messages.searchnocontactsfound": "No contacts found",
|
||||||
|
"addon.messages.searchnomessagesfound": "No messages found",
|
||||||
|
"addon.messages.searchnononcontactsfound": "No non contacts found",
|
||||||
|
"addon.messages.sendcontactrequest": "Send contact request",
|
||||||
"addon.messages.showdeletemessages": "Show delete messages",
|
"addon.messages.showdeletemessages": "Show delete messages",
|
||||||
"addon.messages.type_blocked": "Blocked",
|
"addon.messages.type_blocked": "Blocked",
|
||||||
"addon.messages.type_offline": "Offline",
|
"addon.messages.type_offline": "Offline",
|
||||||
"addon.messages.type_online": "Online",
|
"addon.messages.type_online": "Online",
|
||||||
"addon.messages.type_search": "Search results",
|
"addon.messages.type_search": "Search results",
|
||||||
"addon.messages.type_strangers": "Others",
|
"addon.messages.type_strangers": "Others",
|
||||||
|
"addon.messages.unabletomessage": "You are unable to message this user",
|
||||||
"addon.messages.unblockuser": "Unblock user",
|
"addon.messages.unblockuser": "Unblock user",
|
||||||
"addon.messages.unblockuserconfirm": "Are you sure you want to unblock {{$a}}?",
|
"addon.messages.unblockuserconfirm": "Are you sure you want to unblock {{$a}}?",
|
||||||
|
"addon.messages.userwouldliketocontactyou": "{{$a}} would like to contact you",
|
||||||
"addon.messages.warningconversationmessagenotsent": "Couldn't send message(s) to conversation {{conversation}}. {{error}}",
|
"addon.messages.warningconversationmessagenotsent": "Couldn't send message(s) to conversation {{conversation}}. {{error}}",
|
||||||
"addon.messages.warningmessagenotsent": "Couldn't send message(s) to user {{user}}. {{error}}",
|
"addon.messages.warningmessagenotsent": "Couldn't send message(s) to user {{user}}. {{error}}",
|
||||||
|
"addon.messages.wouldliketocontactyou": "Would like to contact you",
|
||||||
"addon.messages.you": "You:",
|
"addon.messages.you": "You:",
|
||||||
|
"addon.messages.youhaveblockeduser": "You have blocked this user in the past",
|
||||||
|
"addon.messages.yourcontactrequestpending": "Your contact request is pending with {{$a}}",
|
||||||
"addon.mod_assign.acceptsubmissionstatement": "Please accept the submission statement.",
|
"addon.mod_assign.acceptsubmissionstatement": "Please accept the submission statement.",
|
||||||
"addon.mod_assign.addattempt": "Allow another attempt",
|
"addon.mod_assign.addattempt": "Allow another attempt",
|
||||||
"addon.mod_assign.addnewattempt": "Add a new attempt",
|
"addon.mod_assign.addnewattempt": "Add a new attempt",
|
||||||
|
@ -1540,6 +1563,7 @@
|
||||||
"core.quotausage": "You have currently used {{$a.used}} of your {{$a.total}} limit.",
|
"core.quotausage": "You have currently used {{$a.used}} of your {{$a.total}} limit.",
|
||||||
"core.redirectingtosite": "You will be redirected to the site.",
|
"core.redirectingtosite": "You will be redirected to the site.",
|
||||||
"core.refresh": "Refresh",
|
"core.refresh": "Refresh",
|
||||||
|
"core.remove": "Remove",
|
||||||
"core.required": "Required",
|
"core.required": "Required",
|
||||||
"core.requireduserdatamissing": "This user lacks some required profile data. Please enter the data in your site and try again.<br>{{$a}}",
|
"core.requireduserdatamissing": "This user lacks some required profile data. Please enter the data in your site and try again.<br>{{$a}}",
|
||||||
"core.resources": "Resources",
|
"core.resources": "Resources",
|
||||||
|
|
|
@ -54,11 +54,7 @@ export class CoreContextMenuPopoverComponent {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
if (!item.iconAction) {
|
if (item.iconAction == 'spinner') {
|
||||||
this.logger.warn('Items with action must have an icon action to work', item);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
} else if (item.iconAction == 'spinner') {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ export class CoreContextMenuComponent implements OnInit, OnDestroy {
|
||||||
@Input() icon?: string; // Icon to be shown on the navigation bar. Default: Kebab menu icon.
|
@Input() icon?: string; // Icon to be shown on the navigation bar. Default: Kebab menu icon.
|
||||||
@Input() title?: string; // Aria label and text to be shown on the top of the popover.
|
@Input() title?: string; // Aria label and text to be shown on the top of the popover.
|
||||||
|
|
||||||
hideMenu: boolean;
|
hideMenu = true; // It will be unhidden when items are added.
|
||||||
ariaLabel: string;
|
ariaLabel: string;
|
||||||
protected items: CoreContextMenuItemComponent[] = [];
|
protected items: CoreContextMenuItemComponent[] = [];
|
||||||
protected itemsMovedToParent: CoreContextMenuItemComponent[] = [];
|
protected itemsMovedToParent: CoreContextMenuItemComponent[] = [];
|
||||||
|
|
|
@ -188,6 +188,7 @@
|
||||||
"quotausage": "You have currently used {{$a.used}} of your {{$a.total}} limit.",
|
"quotausage": "You have currently used {{$a.used}} of your {{$a.total}} limit.",
|
||||||
"redirectingtosite": "You will be redirected to the site.",
|
"redirectingtosite": "You will be redirected to the site.",
|
||||||
"refresh": "Refresh",
|
"refresh": "Refresh",
|
||||||
|
"remove": "Remove",
|
||||||
"required": "Required",
|
"required": "Required",
|
||||||
"requireduserdatamissing": "This user lacks some required profile data. Please enter the data in your site and try again.<br>{{$a}}",
|
"requireduserdatamissing": "This user lacks some required profile data. Please enter the data in your site and try again.<br>{{$a}}",
|
||||||
"resources": "Resources",
|
"resources": "Resources",
|
||||||
|
|
|
@ -184,9 +184,4 @@ ion-app.app-root {
|
||||||
width: min-content;
|
width: min-content;
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message item.
|
|
||||||
.item-message core-format-text > p:only-child {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue