MOBILE-3631 messages: Add contacts page
parent
99448a3054
commit
04c6f4480d
|
@ -16,6 +16,7 @@ import { Injector, NgModule } from '@angular/core';
|
|||
import { RouterModule, ROUTES, Routes } from '@angular/router';
|
||||
|
||||
import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.module';
|
||||
import { AddonMessagesContactsRoutingModule } from './pages/contacts/messages-contacts-routing.module';
|
||||
import { AddonMessagesIndexRoutingModule } from './pages/index-35/messages-index-routing.module';
|
||||
|
||||
function buildRoutes(injector: Injector): Routes {
|
||||
|
@ -34,6 +35,11 @@ function buildRoutes(injector: Injector): Routes {
|
|||
loadChildren: () => import('./pages/search/search.module')
|
||||
.then(m => m.AddonMessagesSearchPageModule),
|
||||
},
|
||||
{
|
||||
path: 'contacts', // 3.6 or greater.
|
||||
loadChildren: () => import('./pages/contacts/contacts.module')
|
||||
.then(m => m.AddonMessagesContactsPageModule),
|
||||
},
|
||||
...buildTabMainRoutes(injector, {
|
||||
redirectTo: 'index',
|
||||
pathMatch: 'full',
|
||||
|
@ -53,8 +59,25 @@ const indexTabRoutes: Routes = [
|
|||
},
|
||||
];
|
||||
|
||||
// 3.6 or greater.
|
||||
const contactsTabRoutes: Routes = [
|
||||
{
|
||||
path: 'confirmed',
|
||||
loadChildren: () => import('./pages/contacts-confirmed/contacts-confirmed.module')
|
||||
.then(m => m.AddonMessagesContactsConfirmedPageModule),
|
||||
},
|
||||
{
|
||||
path: 'requests',
|
||||
loadChildren: () => import('./pages/contacts-requests/contacts-requests.module')
|
||||
.then(m => m.AddonMessagesContactsRequestsPageModule),
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [AddonMessagesIndexRoutingModule.forChild({ children: indexTabRoutes })],
|
||||
imports: [
|
||||
AddonMessagesIndexRoutingModule.forChild({ children: indexTabRoutes }),
|
||||
AddonMessagesContactsRoutingModule.forChild({ children: contactsTabRoutes }),
|
||||
],
|
||||
exports: [RouterModule],
|
||||
providers: [
|
||||
{
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!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 class="ion-no-margin">
|
||||
<ion-item class="ion-text-wrap" *ngFor="let contact of contacts" [title]="contact.fullname"
|
||||
(click)="selectUser(contact.id)"
|
||||
[class.core-split-item-selected]="contact.id == selectedUserId" class="addon-messages-conversation-item">
|
||||
<core-user-avatar slot="start" core-user-avatar [user]="contact" [checkOnline]="contact.showonlinestatus"
|
||||
[linkProfile]="false"></core-user-avatar>
|
||||
<ion-label>
|
||||
<h2>
|
||||
{{ contact.fullname }}
|
||||
<ion-icon *ngIf="contact.isblocked" name="fas-user-slash" slot="end">
|
||||
</ion-icon>
|
||||
</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
|
||||
<core-empty-box *ngIf="!contacts.length" icon="far-address-book"
|
||||
[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,45 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// 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 { IonicModule } from '@ionic/angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { CoreSharedModule } from '@/core/shared.module';
|
||||
|
||||
import { AddonMessagesContactsConfirmedPage } from './contacts-confirmed.page';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: AddonMessagesContactsConfirmedPage,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild(routes),
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
CoreSharedModule,
|
||||
],
|
||||
declarations: [
|
||||
AddonMessagesContactsConfirmedPage,
|
||||
],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class AddonMessagesContactsConfirmedPageModule {}
|
|
@ -0,0 +1,165 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// 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, OnInit } from '@angular/core';
|
||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import {
|
||||
AddonMessagesProvider,
|
||||
AddonMessagesConversationMember,
|
||||
AddonMessages,
|
||||
AddonMessagesMemberInfoChangedEventData,
|
||||
AddonMessagesSplitViewLoadContactsEventData,
|
||||
} from '../../services/messages';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { IonRefresher } from '@ionic/angular';
|
||||
import { CoreNavigator } from '@services/navigator';
|
||||
|
||||
/**
|
||||
* Component that displays the list of confirmed contacts.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'addon-messages-confirmed-contacts',
|
||||
templateUrl: 'contacts-confirmed.html',
|
||||
})
|
||||
export class AddonMessagesContactsConfirmedPage implements OnInit, OnDestroy {
|
||||
|
||||
loaded = false;
|
||||
canLoadMore = false;
|
||||
loadMoreError = false;
|
||||
contacts: AddonMessagesConversationMember[] = [];
|
||||
selectedUserId?: number;
|
||||
|
||||
protected memberInfoObserver: CoreEventObserver;
|
||||
|
||||
constructor() {
|
||||
// this.onUserSelected = new EventEmitter();
|
||||
|
||||
// Update block status of a user.
|
||||
this.memberInfoObserver = CoreEvents.on<AddonMessagesMemberInfoChangedEventData>(
|
||||
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);
|
||||
}
|
||||
} else if (data.contactRequestConfirmed) {
|
||||
this.refreshData();
|
||||
}
|
||||
},
|
||||
CoreSites.instance.getCurrentSiteId(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component loaded.
|
||||
*/
|
||||
async ngOnInit(): Promise<void> {
|
||||
try {
|
||||
await this.fetchData();
|
||||
if (this.contacts.length) {
|
||||
this.selectUser(this.contacts[0].id, true);
|
||||
}
|
||||
} finally {
|
||||
this.loaded = true;
|
||||
}
|
||||
// Workaround for infinite scrolling.
|
||||
// @todo this.content.resize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch contacts.
|
||||
*
|
||||
* @param refresh True if we are refreshing contacts, false if we are loading more.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async fetchData(refresh: boolean = false): Promise<void> {
|
||||
this.loadMoreError = false;
|
||||
|
||||
const limitFrom = refresh ? 0 : this.contacts.length;
|
||||
|
||||
if (limitFrom === 0) {
|
||||
// Always try to get latest data from server.
|
||||
await AddonMessages.instance.invalidateUserContacts();
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await AddonMessages.instance.getUserContacts(limitFrom);
|
||||
this.contacts = refresh ? result.contacts : this.contacts.concat(result.contacts);
|
||||
this.canLoadMore = result.canLoadMore;
|
||||
} catch (error) {
|
||||
this.loadMoreError = true;
|
||||
CoreDomUtils.instance.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingcontacts', true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh contacts.
|
||||
*
|
||||
* @param refresher Refresher.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async refreshData(refresher?: CustomEvent<IonRefresher>): Promise<void> {
|
||||
// No need to invalidate contacts, we always try to get the latest.
|
||||
await this.fetchData(true).finally(() => {
|
||||
refresher?.detail.complete();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load more contacts.
|
||||
*
|
||||
* @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading.
|
||||
* @return Resolved when done.
|
||||
*/
|
||||
async loadMore(infiniteComplete?: () => void): Promise<void> {
|
||||
await this.fetchData().finally(() => {
|
||||
infiniteComplete && infiniteComplete();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify that a contact has been selected.
|
||||
*
|
||||
* @param userId User id.
|
||||
* @param onInit Whether the contact is selected on initial load.
|
||||
*/
|
||||
selectUser(userId: number, onInit: boolean = false): void {
|
||||
this.selectedUserId = userId;
|
||||
|
||||
const data: AddonMessagesSplitViewLoadContactsEventData = {
|
||||
userId,
|
||||
onInit,
|
||||
};
|
||||
|
||||
CoreEvents.trigger(AddonMessagesProvider.SPLIT_VIEW_LOAD_CONTACTS_EVENT, data);
|
||||
|
||||
// @todo: Check if split view is visible before
|
||||
CoreNavigator.instance.navigateToSitePath('discussion', { params : { userId } });
|
||||
}
|
||||
|
||||
/**
|
||||
* Component destroyed.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.memberInfoObserver?.off();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!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 class="ion-no-margin">
|
||||
<ion-item class="ion-text-wrap" *ngFor="let request of requests" [title]="request.fullname"
|
||||
(click)="selectUser(request.id)" [class.core-split-item-selected]="request.id == selectedUserId"
|
||||
class="addon-messages-conversation-item">
|
||||
<core-user-avatar slot="start" [user]="request" [linkProfile]="false"></core-user-avatar>
|
||||
<ion-label>
|
||||
<h2>{{ request.fullname }}</h2>
|
||||
<p *ngIf="!request.iscontact">
|
||||
{{ 'addon.messages.wouldliketocontactyou' | translate }}
|
||||
</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
<core-empty-box *ngIf="!requests.length" icon="far-address-book" [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,45 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// 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 { IonicModule } from '@ionic/angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { CoreSharedModule } from '@/core/shared.module';
|
||||
|
||||
import { AddonMessagesContactsRequestsPage } from './contacts-requests.page';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: AddonMessagesContactsRequestsPage,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild(routes),
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
CoreSharedModule,
|
||||
],
|
||||
declarations: [
|
||||
AddonMessagesContactsRequestsPage,
|
||||
],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class AddonMessagesContactsRequestsPageModule {}
|
|
@ -0,0 +1,161 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// 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, OnInit } from '@angular/core';
|
||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import {
|
||||
AddonMessagesProvider,
|
||||
AddonMessagesConversationMember,
|
||||
AddonMessagesSplitViewLoadContactsEventData,
|
||||
AddonMessages,
|
||||
AddonMessagesMemberInfoChangedEventData,
|
||||
} from '../../services/messages';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreNavigator } from '@services/navigator';
|
||||
import { IonRefresher } from '@ionic/angular';
|
||||
|
||||
/**
|
||||
* Component that displays the list of contact requests.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'addon-messages-contact-requests',
|
||||
templateUrl: 'contacts-requests.html',
|
||||
})
|
||||
export class AddonMessagesContactsRequestsPage implements OnInit, OnDestroy {
|
||||
|
||||
loaded = false;
|
||||
canLoadMore = false;
|
||||
loadMoreError = false;
|
||||
requests: AddonMessagesConversationMember[] = [];
|
||||
selectedUserId?: number;
|
||||
|
||||
protected memberInfoObserver: CoreEventObserver;
|
||||
|
||||
constructor() {
|
||||
|
||||
// Hide the "Would like to contact you" message when a contact request is confirmed.
|
||||
this.memberInfoObserver = CoreEvents.on<AddonMessagesMemberInfoChangedEventData>(
|
||||
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);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
CoreSites.instance.getCurrentSiteId(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component loaded.
|
||||
*/
|
||||
async ngOnInit(): Promise<void> {
|
||||
try {
|
||||
await this.fetchData();
|
||||
if (this.requests.length) {
|
||||
this.selectUser(this.requests[0].id, true);
|
||||
}
|
||||
} finally {
|
||||
this.loaded = true;
|
||||
}
|
||||
// Workaround for infinite scrolling.
|
||||
// @todo this.content.resize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch contact requests.
|
||||
*
|
||||
* @param refresh True if we are refreshing contact requests, false if we are loading more.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async fetchData(refresh: boolean = false): Promise<void> {
|
||||
this.loadMoreError = false;
|
||||
|
||||
const limitFrom = refresh ? 0 : this.requests.length;
|
||||
|
||||
if (limitFrom === 0) {
|
||||
// Always try to get latest data from server.
|
||||
await AddonMessages.instance.invalidateContactRequestsCache();
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await AddonMessages.instance.getContactRequests(limitFrom);
|
||||
this.requests = refresh ? result.requests : this.requests.concat(result.requests);
|
||||
this.canLoadMore = result.canLoadMore;
|
||||
} catch (error) {
|
||||
this.loadMoreError = true;
|
||||
CoreDomUtils.instance.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingcontacts', true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh contact requests.
|
||||
*
|
||||
* @param refresher Refresher.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async refreshData(refresher?: CustomEvent<IonRefresher>): Promise<void> {
|
||||
// Refresh the number of contacts requests to update badges.
|
||||
AddonMessages.instance.refreshContactRequestsCount();
|
||||
|
||||
// No need to invalidate contact requests, we always try to get the latest.
|
||||
await this.fetchData(true).finally(() => {
|
||||
refresher?.detail.complete();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load more contact requests.
|
||||
*
|
||||
* @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading.
|
||||
* @return Resolved when done.
|
||||
*/
|
||||
async loadMore(infiniteComplete?: () => void): Promise<void> {
|
||||
await this.fetchData().finally(() => {
|
||||
infiniteComplete && infiniteComplete();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify that a contact has been selected.
|
||||
*
|
||||
* @param userId User id.
|
||||
* @param onInit Whether the contact is selected on initial load.
|
||||
*/
|
||||
selectUser(userId: number, onInit: boolean = false): void {
|
||||
this.selectedUserId = userId;
|
||||
|
||||
const data: AddonMessagesSplitViewLoadContactsEventData = {
|
||||
userId,
|
||||
onInit,
|
||||
};
|
||||
|
||||
CoreEvents.trigger(AddonMessagesProvider.SPLIT_VIEW_LOAD_CONTACTS_EVENT, data);
|
||||
|
||||
// @todo: Check if split view is visible before
|
||||
CoreNavigator.instance.navigateToSitePath('discussion', { params : { userId } });
|
||||
}
|
||||
|
||||
/**
|
||||
* Component destroyed.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.memberInfoObserver?.off();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>{{ 'addon.messages.contacts' | translate }}</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button (click)="gotoSearch()" [attr.aria-label]="'addon.messages.search' | translate">
|
||||
<ion-icon name="fas-search" slot="icon-only"></ion-icon>
|
||||
</ion-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-toolbar>
|
||||
</ion-header>
|
||||
<!-- @todo <core-split-view>-->
|
||||
<ion-content>
|
||||
<core-tabs [tabs]="tabs" hideUntil="true"></core-tabs>
|
||||
</ion-content>
|
|
@ -0,0 +1,64 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// 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 { Injector, NgModule } from '@angular/core';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { RouterModule, ROUTES, Routes } from '@angular/router';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { CoreSharedModule } from '@/core/shared.module';
|
||||
|
||||
import { ADDON_MESSAGES_CONTACTS_ROUTES } from './messages-contacts-routing.module';
|
||||
import { AddonMessagesContactsPage } from './contacts.page';
|
||||
import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.module';
|
||||
import { resolveModuleRoutes } from '@/app/app-routing.module';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: AddonMessagesContactsPage,
|
||||
},
|
||||
];
|
||||
|
||||
function buildRoutes(injector: Injector): Routes {
|
||||
const routes = resolveModuleRoutes(injector, ADDON_MESSAGES_CONTACTS_ROUTES);
|
||||
|
||||
return [
|
||||
...buildTabMainRoutes(injector, {
|
||||
path: '',
|
||||
component: AddonMessagesContactsPage,
|
||||
children: routes.children,
|
||||
}),
|
||||
...routes.siblings,
|
||||
];
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild(routes),
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
CoreSharedModule,
|
||||
],
|
||||
providers: [
|
||||
{ provide: ROUTES, multi: true, useFactory: buildRoutes, deps: [Injector] },
|
||||
],
|
||||
declarations: [
|
||||
AddonMessagesContactsPage,
|
||||
],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class AddonMessagesContactsPageModule {}
|
|
@ -0,0 +1,134 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// 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, OnInit } from '@angular/core';
|
||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import {
|
||||
AddonMessages,
|
||||
AddonMessagesContactRequestCountEventData,
|
||||
AddonMessagesProvider,
|
||||
AddonMessagesSplitViewLoadContactsEventData,
|
||||
} from '../../services/messages';
|
||||
import { CoreTab } from '@components/tabs/tabs';
|
||||
import { CoreNavigator } from '@services/navigator';
|
||||
// @todo import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||
|
||||
/**
|
||||
* Page that displays contacts and contact requests.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'page-addon-messages-contacts',
|
||||
templateUrl: 'contacts.html',
|
||||
})
|
||||
export class AddonMessagesContactsPage implements OnInit, OnDestroy {
|
||||
|
||||
// @todo @ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent;
|
||||
|
||||
protected contactsTab: CoreTab = {
|
||||
id: 'contacts-confirmed',
|
||||
class: '',
|
||||
title: 'addon.messages.contacts',
|
||||
icon: 'fas-address-book',
|
||||
enabled: true,
|
||||
page: 'main/messages/contacts/confirmed',
|
||||
};
|
||||
|
||||
protected requestsTab: CoreTab = {
|
||||
id: 'contact-requests',
|
||||
class: '',
|
||||
title: 'addon.messages.requests',
|
||||
icon: 'fas-user-plus',
|
||||
enabled: true,
|
||||
page: 'main/messages/contacts/requests',
|
||||
badge: '',
|
||||
};
|
||||
|
||||
tabs: CoreTab[] = [];
|
||||
|
||||
protected siteId: string;
|
||||
protected contactRequestsCountObserver: CoreEventObserver;
|
||||
protected splitViewObserver: CoreEventObserver;
|
||||
protected selectedUserId?: number; // User id of the conversation opened in the split view.
|
||||
|
||||
constructor() {
|
||||
|
||||
this.tabs = [this.contactsTab, this.requestsTab];
|
||||
|
||||
this.siteId = CoreSites.instance.getCurrentSiteId();
|
||||
|
||||
// Update the contact requests badge.
|
||||
this.contactRequestsCountObserver = CoreEvents.on<AddonMessagesContactRequestCountEventData>(
|
||||
AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT,
|
||||
(data) => {
|
||||
this.requestsTab.badge = data.count > 0 ? String(data.count) : '';
|
||||
},
|
||||
this.siteId,
|
||||
);
|
||||
|
||||
// Update the contact requests badge.
|
||||
this.splitViewObserver = CoreEvents.on<AddonMessagesSplitViewLoadContactsEventData>(
|
||||
AddonMessagesProvider.SPLIT_VIEW_LOAD_CONTACTS_EVENT,
|
||||
(data) => {
|
||||
this.selectUser(data.userId, data.onInit);
|
||||
},
|
||||
this.siteId,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Page being initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
AddonMessages.instance.getContactRequestsCount(this.siteId); // Badge already updated by the observer.
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the search page.
|
||||
*/
|
||||
gotoSearch(): void {
|
||||
CoreNavigator.instance.navigateToSitePath('search');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the selected user and open the conversation in the split view if needed.
|
||||
*
|
||||
* @param userId Id of the selected user, undefined to use the last selected user in the tab.
|
||||
* @param onInit Whether the contact was selected on initial load.
|
||||
*/
|
||||
selectUser(userId: number, onInit = false): void {
|
||||
/* @todo if (userId == this.selectedUserId && this.splitviewCtrl.isOn()) {
|
||||
// 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.selectedUserId = userId;
|
||||
// @todo this.splitviewCtrl.push('AddonMessagesDiscussionPage', { userId });
|
||||
CoreNavigator.instance.navigateToSitePath('discussion', { params : { userId } });
|
||||
}
|
||||
|
||||
/**
|
||||
* Page destroyed.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.contactRequestsCountObserver?.off();
|
||||
this.splitViewObserver?.off();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// 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 { InjectionToken, ModuleWithProviders, NgModule } from '@angular/core';
|
||||
|
||||
import { ModuleRoutesConfig } from '@/app/app-routing.module';
|
||||
|
||||
export const ADDON_MESSAGES_CONTACTS_ROUTES = new InjectionToken('ADDON_MESSAGES_CONTACTS_ROUTES');
|
||||
|
||||
@NgModule()
|
||||
export class AddonMessagesContactsRoutingModule {
|
||||
|
||||
static forChild(routes: ModuleRoutesConfig): ModuleWithProviders<AddonMessagesContactsRoutingModule> {
|
||||
return {
|
||||
ngModule: AddonMessagesContactsRoutingModule,
|
||||
providers: [
|
||||
{ provide: ADDON_MESSAGES_CONTACTS_ROUTES, multi: true, useValue: routes },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -43,6 +43,7 @@ export class AddonMessagesProvider {
|
|||
static readonly READ_CHANGED_EVENT = 'addon_messages_read_changed_event';
|
||||
static readonly OPEN_CONVERSATION_EVENT = 'addon_messages_open_conversation_event'; // Notify a conversation should be opened.
|
||||
static readonly SPLIT_VIEW_LOAD_INDEX_EVENT = 'addon_messages_split_view_load_index_event'; // Used on 3.5 or lower.
|
||||
static readonly SPLIT_VIEW_LOAD_CONTACTS_EVENT = 'addon_messages_split_view_load_contacts_event'; // Used on 3.6 or greater.
|
||||
static readonly UPDATE_CONVERSATION_LIST_EVENT = 'addon_messages_update_conversation_list_event';
|
||||
static readonly MEMBER_INFO_CHANGED_EVENT = 'addon_messages_member_changed_event';
|
||||
static readonly UNREAD_CONVERSATION_COUNTS_EVENT = 'addon_messages_unread_conversation_counts_event';
|
||||
|
@ -3639,3 +3640,11 @@ export type AddonMessagesSplitViewLoadIndexEventData = {
|
|||
onlyWithSplitView?: boolean;
|
||||
message?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Data sent by SPLIT_VIEW_LOAD_CONTACTS_EVENT event. Used on 3.6 or greater.
|
||||
*/
|
||||
export type AddonMessagesSplitViewLoadContactsEventData = {
|
||||
userId: number;
|
||||
onInit: boolean;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue