MOBILE-3631 messages: Add group info modal
parent
e3b28d87e5
commit
210518d549
|
@ -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 {}
|
|
@ -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>
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue