MOBILE-3631 messages: Add group info modal

main
Pau Ferrer Ocaña 2021-01-26 16:15:17 +01:00
parent e3b28d87e5
commit 210518d549
5 changed files with 249 additions and 8 deletions

View File

@ -0,0 +1,39 @@
// (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 { CommonModule } from '@angular/common';
import { IonicModule } from '@ionic/angular';
import { TranslateModule } from '@ngx-translate/core';
import { CoreSharedModule } from '@/core/shared.module';
import { AddonMessagesConversationInfoComponent } from './conversation-info/conversation-info';
@NgModule({
declarations: [
AddonMessagesConversationInfoComponent,
],
imports: [
CommonModule,
IonicModule,
TranslateModule.forChild(),
CoreSharedModule,
],
entryComponents: [
AddonMessagesConversationInfoComponent,
],
})
export class AddonMessagesComponentsModule {}

View File

@ -0,0 +1,54 @@
<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.groupinfo' | translate }}</ion-title>
<ion-buttons slot="end">
<ion-button (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
<ion-icon name="close" slot="icon-only"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<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">
<ion-item class="ion-text-center" *ngIf="conversation">
<ion-label>
<div class="large-avatar">
<img class="avatar" [src]="conversation!.imageurl" core-external-content [alt]="conversation!.name"
role="presentation" onError="this.src='assets/img/group-avatar.png'">
</div>
<h2>
<core-format-text [text]="conversation!.name" contextLevel="system" [contextInstanceId]="0"></core-format-text>
</h2>
<p>
<core-format-text *ngIf="conversation!.subname" [text]="conversation!.subname" contextLevel="system"
[contextInstanceId]="0">
</core-format-text>
</p>
<p>{{ 'addon.messages.numparticipants' | translate:{$a: conversation!.membercount} }}</p>
</ion-label>
</ion-item>
<ion-item class="ion-text-wrap addon-messages-conversation-item" *ngFor="let member of members"
(click)="closeModal(member.id)" detail>
<core-user-avatar [user]="member" [linkProfile]="false" [checkOnline]="member.showonlinestatus" slot="start">
</core-user-avatar>
<ion-label>
<h2>
{{ member.fullname }}
<ion-icon name="fas-user-slash" *ngIf="member.isblocked" [title]="'addon.messages.contactblocked' | translate">
</ion-icon>
</h2>
</ion-label>
</ion-item>
<core-infinite-loading [enabled]="canLoadMore" (action)="loadMoreMembers($event)" [error]="loadMoreError">
</core-infinite-loading>
</core-loading>
</ion-content>

View File

@ -0,0 +1,146 @@
// (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, OnInit } from '@angular/core';
import { IonRefresher } from '@ionic/angular';
import {
AddonMessagesConversationFormatted,
AddonMessagesConversationMember,
AddonMessages,
} from '../../services/messages';
import { CoreDomUtils } from '@services/utils/dom';
import { ActivatedRoute } from '@angular/router';
import { ModalController } from '@singletons';
/**
* Component that displays the list of conversations, including group conversations.
*/
@Component({
selector: 'page-addon-messages-conversation-info',
templateUrl: 'conversation-info.html',
})
export class AddonMessagesConversationInfoComponent implements OnInit {
loaded = false;
conversation?: AddonMessagesConversationFormatted;
members: AddonMessagesConversationMember[] = [];
canLoadMore = false;
loadMoreError = false;
protected conversationId!: number;
constructor(
protected route: ActivatedRoute,
) {
}
/**
* Component loaded.
*/
ngOnInit(): void {
this.route.queryParams.subscribe(async params => {
this.conversationId = parseInt(params['conversationId'], 10);
this.loaded = false;
this.fetchData().finally(() => {
this.loaded = true;
});
});
}
/**
* Fetch the required data.
*
* @return Promise resolved when done.
*/
protected async fetchData(): Promise<void> {
// Get the conversation data first.
try {
const conversation = await AddonMessages.instance.getConversation(this.conversationId, false, true, 0, 0);
this.conversation = conversation;
// Now get the members.
await this.fetchMembers();
} catch (error) {
CoreDomUtils.instance.showErrorModalDefault(error, 'Error getting members.');
}
}
/**
* Get conversation members.
*
* @param loadingMore Whether we are loading more data or just the first ones.
* @return Promise resolved when done.
*/
protected async fetchMembers(loadingMore?: boolean): Promise<void> {
this.loadMoreError = false;
const limitFrom = loadingMore ? this.members.length : 0;
const data = await AddonMessages.instance.getConversationMembers(this.conversationId, limitFrom);
if (loadingMore) {
this.members = this.members.concat(data.members);
} else {
this.members = data.members;
}
this.canLoadMore = data.canLoadMore;
}
/**
* Function to load more members.
*
* @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading.
* @return Resolved when done.
*/
async loadMoreMembers(infiniteComplete?: () => void): Promise<void> {
try {
await this.fetchMembers(true);
} catch (error) {
CoreDomUtils.instance.showErrorModalDefault(error, 'Error getting members.');
this.loadMoreError = true;
} finally {
infiniteComplete && infiniteComplete();
}
}
/**
* Refresh the data.
*
* @param refresher Refresher.
* @return Promise resolved when done.
*/
async refreshData(refresher?: CustomEvent<IonRefresher>): Promise<void> {
const promises: Promise<void>[] = [];
promises.push(AddonMessages.instance.invalidateConversation(this.conversationId));
promises.push(AddonMessages.instance.invalidateConversationMembers(this.conversationId));
await Promise.all(promises);
await this.fetchData().finally(() => {
refresher?.detail.complete();
});
}
/**
* Close modal.
*
* @param userId User conversation to load.
*/
closeModal(userId?: number): void {
ModalController.instance.dismiss(userId);
}
}

View File

@ -21,6 +21,7 @@ import { CommonModule } from '@angular/common';
import { CoreSharedModule } from '@/core/shared.module'; import { CoreSharedModule } from '@/core/shared.module';
import { AddonMessagesDiscussionPage } from './discussion.page'; import { AddonMessagesDiscussionPage } from './discussion.page';
import { AddonMessagesComponentsModule } from '@addons/messages/components/components.module';
const routes: Routes = [ const routes: Routes = [
{ {
@ -36,6 +37,7 @@ const routes: Routes = [
IonicModule, IonicModule,
TranslateModule.forChild(), TranslateModule.forChild(),
CoreSharedModule, CoreSharedModule,
AddonMessagesComponentsModule,
], ],
declarations: [ declarations: [
AddonMessagesDiscussionPage, AddonMessagesDiscussionPage,

View File

@ -52,6 +52,7 @@ import { ActivatedRoute } from '@angular/router';
import { import {
AddonMessagesOfflineMessagesDBRecordFormatted, AddonMessagesOfflineMessagesDBRecordFormatted,
} from '@addons/messages/services/database/messages'; } from '@addons/messages/services/database/messages';
import { AddonMessagesConversationInfoComponent } from '../../components/conversation-info/conversation-info';
/** /**
* Page that displays a message discussion page. * Page that displays a message discussion page.
@ -171,8 +172,8 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
this.showInfo = !backViewPage || !CoreTextUtils.instance.matchesGlob(backViewPage, '**/user/profile'); this.showInfo = !backViewPage || !CoreTextUtils.instance.matchesGlob(backViewPage, '**/user/profile');
this.loaded = false; this.loaded = false;
this.conversationId = parseInt(params['conversationId'], 10) || undefined; this.conversationId = params['conversationId'] ? parseInt(params['conversationId'], 10) : undefined;
this.userId = parseInt(params['userId'], 10) || undefined; this.userId = params['userId'] ? parseInt(params['userId'], 10) : undefined;
this.showKeyboard = !!params['showKeyboard']; this.showKeyboard = !!params['showKeyboard'];
await this.fetchData(); await this.fetchData();
@ -289,7 +290,6 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
// Get the member info. Invalidate first to make sure we get the latest status. // Get the member info. Invalidate first to make sure we get the latest status.
promises.push(AddonMessages.instance.invalidateMemberInfo(this.userId).then(() => promises.push(AddonMessages.instance.invalidateMemberInfo(this.userId).then(() =>
AddonMessages.instance.getMemberInfo(this.userId!)).then((member) => { AddonMessages.instance.getMemberInfo(this.userId!)).then((member) => {
this.otherMember = member;
if (!exists && member) { if (!exists && member) {
this.conversationImage = member.profileimageurl; this.conversationImage = member.profileimageurl;
this.title = member.fullname; this.title = member.fullname;
@ -362,10 +362,10 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
if (this.messagesBeingSent > 0) { if (this.messagesBeingSent > 0) {
// We do not poll while a message is being sent or we could confuse the user. // We do not poll while a message is being sent or we could confuse the user.
// Otherwise, his message would disappear from the list, and he'd have to wait for the interval to check for messages. // Otherwise, his message would disappear from the list, and he'd have to wait for the interval to check for messages.
throw null; return;
} else if (this.fetching) { } else if (this.fetching) {
// Already fetching. // Already fetching.
throw null; return;
} else if (this.groupMessagingEnabled && !this.conversationId) { } else if (this.groupMessagingEnabled && !this.conversationId) {
// Don't have enough data to fetch messages. // Don't have enough data to fetch messages.
throw new CoreError('No enough data provided to fetch messages'); throw new CoreError('No enough data provided to fetch messages');
@ -1286,7 +1286,7 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
if (this.isGroup) { if (this.isGroup) {
// Display the group information. // Display the group information.
const modal = await ModalController.instance.create({ const modal = await ModalController.instance.create({
component: 'AddonMessagesConversationInfoPage', // @todo component: AddonMessagesConversationInfoComponent,
componentProps: { componentProps: {
conversationId: this.conversationId, conversationId: this.conversationId,
}, },
@ -1296,7 +1296,7 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
const result = await modal.onDidDismiss(); const result = await modal.onDidDismiss();
if (typeof result.data.userId != 'undefined') { if (typeof result.data != 'undefined') {
const splitViewLoaded = CoreNavigator.instance.isSplitViewOutletLoaded('**/messages/**/discussion'); const splitViewLoaded = CoreNavigator.instance.isSplitViewOutletLoaded('**/messages/**/discussion');
// Open user conversation. // Open user conversation.
@ -1304,7 +1304,7 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
// Notify the left pane to load it, this way the right conversation will be highlighted. // Notify the left pane to load it, this way the right conversation will be highlighted.
CoreEvents.trigger<AddonMessagesOpenConversationEventData>( CoreEvents.trigger<AddonMessagesOpenConversationEventData>(
AddonMessagesProvider.OPEN_CONVERSATION_EVENT, AddonMessagesProvider.OPEN_CONVERSATION_EVENT,
{ userId: result.data.userId }, { userId: result.data },
this.siteId, this.siteId,
); );
} else { } else {