MOBILE-3142 chat: Past sessions look and feel

main
Pau Ferrer Ocaña 2019-09-10 14:59:08 +02:00
parent e01cce68dc
commit 0dd869d59e
6 changed files with 173 additions and 104 deletions

View File

@ -18,6 +18,7 @@ import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate
import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate';
import { AddonModChatComponentsModule } from './components/components.module';
import { AddonModChatProvider } from './providers/chat';
import { AddonModChatHelperProvider } from './providers/helper';
import { AddonModChatLinkHandler } from './providers/link-handler';
import { AddonModChatListLinkHandler } from './providers/list-link-handler';
import { AddonModChatModuleHandler } from './providers/module-handler';
@ -36,6 +37,7 @@ export const ADDON_MOD_CHAT_PROVIDERS: any[] = [
],
providers: [
AddonModChatProvider,
AddonModChatHelperProvider,
AddonModChatLinkHandler,
AddonModChatListLinkHandler,
AddonModChatModuleHandler,

View File

@ -21,10 +21,10 @@ import { CoreSitesProvider } from '@providers/sites';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreTextUtilsProvider } from '@providers/utils/text';
import { AddonModChatProvider, AddonModChatMessageWithUserData } from '../../providers/chat';
import { AddonModChatHelperProvider } from '../../providers/helper';
import { Network } from '@ionic-native/network';
import { coreSlideInOut } from '@classes/animations';
import { CoreSendMessageFormComponent } from '@components/send-message-form/send-message-form';
import * as moment from 'moment';
/**
* Page that displays a chat session.
@ -62,8 +62,8 @@ export class AddonModChatChatPage {
constructor(navParams: NavParams, logger: CoreLoggerProvider, network: Network, zone: NgZone, private navCtrl: NavController,
private chatProvider: AddonModChatProvider, private appProvider: CoreAppProvider, sitesProvider: CoreSitesProvider,
private modalCtrl: ModalController, private domUtils: CoreDomUtilsProvider, private textUtils: CoreTextUtilsProvider,
private eventsProvider: CoreEventsProvider) {
private modalCtrl: ModalController, private domUtils: CoreDomUtilsProvider,
private eventsProvider: CoreEventsProvider, private chatHelper: AddonModChatHelperProvider) {
this.chatId = navParams.get('chatId');
this.courseId = navParams.get('courseId');
@ -210,25 +210,13 @@ export class AddonModChatChatPage {
const message = this.messages[index];
const prevMessage = index > 0 ? this.messages[index - 1] : null;
message.showDate = this.showDate(message, prevMessage);
message.beep = message.message.substr(0, 5) == 'beep ' && message.message.substr(5).trim();
this.chatHelper.formatMessage(this.currentUserId, message, prevMessage);
if (message.beep && message.beep != this.currentUserId) {
this.getUserFullname(message.beep).then((fullname) => {
message.beepwho = fullname;
});
}
message.special = message.system || !!message.beep;
if (message.message.substr(0, 4) == '/me ') {
message.special = true;
message.message = message.message.substr(4).trim();
}
message.showUserData = this.showUserData(message, prevMessage);
prevMessage ?
prevMessage.showTail = this.showTail(prevMessage, message) : null;
}
this.messages[this.messages.length - 1].showTail = true;
@ -267,34 +255,10 @@ export class AddonModChatChatPage {
}
}
/**
* Check if the user info should be displayed for the current message.
* User data is only displayed if the previous message was from another user.
*
* @param message Current message where to show the user info.
* @param prevMessage Previous message.
* @return Whether user data should be shown.
*/
showUserData(message: any, prevMessage?: any): boolean {
return message.userid != this.currentUserId &&
(!prevMessage || prevMessage.userid != message.userid || message.showDate || prevMessage.special);
}
/**
* Check if a css tail should be shown.
*
* @param message Current message where to show the user info.
* @param nextMessage Next message.
* @return Whether user data should be shown.
*/
showTail(message: any, nextMessage?: any): boolean {
return !nextMessage || nextMessage.userid != message.userid || nextMessage.showDate || nextMessage.special;
}
/**
* Convenience function to be called every certain time to fetch chat messages.
*
* @return Promised resolved when done.
* @return Promise resolved when done.
*/
protected fetchMessagesInterval(): Promise<void> {
this.logger.debug('Polling for messages');
@ -324,22 +288,6 @@ export class AddonModChatChatPage {
});
}
/**
* Check if the date should be displayed between messages (when the day changes at midnight for example).
*
* @param message New message object.
* @param prevMessage Previous message object.
* @return True if messages are from diferent days, false othetwise.
*/
showDate(message: AddonModChatMessageWithUserData, prevMessage: AddonModChatMessageWithUserData): boolean {
if (!prevMessage) {
return true;
}
// Check if day has changed.
return !moment(message.timestamp * 1000).isSame(prevMessage.timestamp * 1000, 'day');
}
/**
* Send a message to the chat.
*
@ -371,7 +319,6 @@ export class AddonModChatChatPage {
this.domUtils.showErrorModalDefault(error, 'addon.mod_chat.errorwhilesendingmessage', true);
}).finally(() => {
this.sending = false;
this.sendMessageForm.focus();
});
}

View File

@ -8,33 +8,56 @@
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
</ion-refresher>
<core-loading [hideUntil]="loaded">
<div *ngFor="let message of messages; index as index; last as last">
<div text-center *ngIf="showDate(messages[index], messages[index - 1])" class="addon-mod-chat-notice">
<ion-badge text-wrap color="light">
<span>{{ message.timestamp * 1000 | coreFormatDate:"strftimedayshort" }}</span>
</ion-badge>
</div>
<ion-list class="addon-messages-discussion-container safe-area-page" aria-live="polite">
<ng-container *ngFor="let message of messages; index as index;">
<h6 text-center *ngIf="message.showDate" class="addon-messages-date">
{{ message.timestamp * 1000 | coreFormatDate:"strftimedayshort" }}
</h6>
<div text-center *ngIf="message.issystem && message.message == 'enter'" class="addon-mod-chat-notice">
<ion-badge text-wrap color="light">
<span>{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ 'addon.mod_chat.messageenter' | translate:{$a: message.userfullname} }}</span>
</ion-badge>
</div>
<div text-center *ngIf="message.special" class="addon-mod-chat-notice">
<ion-badge text-wrap color="success" *ngIf="message.issystem && message.message == 'enter'">
<span><core-icon name="fa-sign-in"></core-icon> {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ 'addon.mod_chat.messageenter' | translate:{$a: message.userfullname} }}</span>
</ion-badge>
<div text-center *ngIf="message.issystem && message.message == 'exit'" class="addon-mod-chat-notice">
<ion-badge text-wrap color="light">
<span>{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ 'addon.mod_chat.messageexit' | translate:{$a: message.userfullname} }}</span>
</ion-badge>
</div>
<ion-badge text-wrap color="danger" *ngIf="message.issystem && message.message == 'exit'">
<span><core-icon name="fa-sign-out"></core-icon> {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ 'addon.mod_chat.messageexit' | translate:{$a: message.userfullname} }}</span>
</ion-badge>
<ion-item text-wrap *ngIf="!message.issystem && message.message.substr(0, 4) != 'beep'" class="addon-mod-chat-message">
<ion-avatar core-user-avatar [user]="message" item-start></ion-avatar>
<h2>
<p float-end>{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}</p>
<core-format-text [text]="message.userfullname"></core-format-text>
</h2>
<core-format-text [text]="message.message"></core-format-text>
</ion-item>
</div>
<ion-badge text-wrap color="primary" *ngIf="message.beep == 'all'">
<span><ion-icon name="notifications"></ion-icon> {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
{{ 'addon.mod_chat.messagebeepseveryone' | translate:{$a: message.userfullname} }} </span>
</ion-badge>
<ion-badge text-wrap color="primary" *ngIf="message.userid != currentUserId && message.beep == currentUserId">
<span><ion-icon name="notifications"></ion-icon> {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
{{ 'addon.mod_chat.messagebeepsyou' | translate:{$a: message.userfullname} }} </span>
</ion-badge>
<ion-badge text-wrap color="light" *ngIf="message.userid == currentUserId && message.beep && message.beep != 'all'">
<span><ion-icon name="notifications"></ion-icon> {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
{{ 'addon.mod_chat.messageyoubeep' | translate:{$a: message.beepwho} }} </span>
</ion-badge>
<ion-badge text-wrap color="info" *ngIf="!message.issystem && !message.beep">
<span><core-icon name="fa-asterisk"></core-icon> {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
<strong>{{ message.userfullname }} <core-format-text [text]="message.message"></core-format-text></strong></span>
</ion-badge>
</div>
<ion-item text-wrap *ngIf="!message.special" class="addon-message" [class.addon-message-mine]="message.userid == currentUserId" [class.addon-message-not-mine]="message.userid != currentUserId" [class.addon-message-no-user]="!message.showUserData">
<!-- User data. -->
<h2 class="addon-message-user">
<ion-avatar item-start core-user-avatar [user]="message" [linkProfile]="false" *ngIf="message.showUserData"></ion-avatar>
<div *ngIf="message.showUserData">{{ message.userfullname }}</div>
<ion-note>{{ message.timestamp * 1000 | coreFormatDate: "strftimetime" }}</ion-note>
</h2>
<p class="addon-message-text">
<core-format-text [text]="message.message"></core-format-text>
</p>
<div class="tail" *ngIf="message.showTail"></div>
</ion-item>
</ng-container>
</ion-list>
</core-loading>
</ion-content>

View File

@ -1,9 +1,8 @@
ion-app.app-root page-addon-mod-chat-session-messages {
@include message-page();
.addon-mod-chat-notice {
margin-top: 10px;
margin-bottom: 10px;
}
.addon-mod-chat-message {
align-items: flex-start;
}
}

View File

@ -15,8 +15,10 @@
import { Component } from '@angular/core';
import { IonicPage, NavParams } from 'ionic-angular';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreSitesProvider } from '@providers/sites';
import { CoreUserProvider } from '@core/user/providers/user';
import { AddonModChatProvider, AddonModChatSessionMessageWithUserData } from '../../providers/chat';
import * as moment from 'moment';
import { AddonModChatHelperProvider } from '../../providers/helper';
/**
* Page that displays list of chat session messages.
@ -28,6 +30,8 @@ import * as moment from 'moment';
})
export class AddonModChatSessionMessagesPage {
currentUserId: number;
protected courseId: number;
protected chatId: number;
protected sessionStart: number;
@ -36,12 +40,14 @@ export class AddonModChatSessionMessagesPage {
protected loaded = false;
protected messages: AddonModChatSessionMessageWithUserData[] = [];
constructor(navParams: NavParams, private domUtils: CoreDomUtilsProvider, private chatProvider: AddonModChatProvider) {
constructor(navParams: NavParams, private domUtils: CoreDomUtilsProvider, private chatProvider: AddonModChatProvider,
sitesProvider: CoreSitesProvider, private chatHelper: AddonModChatHelperProvider, private userProvider: CoreUserProvider) {
this.courseId = navParams.get('courseId');
this.chatId = navParams.get('chatId');
this.groupId = navParams.get('groupId');
this.sessionStart = navParams.get('sessionStart');
this.sessionEnd = navParams.get('sessionEnd');
this.currentUserId = sitesProvider.getCurrentSiteUserId();
this.fetchMessages();
}
@ -55,7 +61,7 @@ export class AddonModChatSessionMessagesPage {
return this.chatProvider.getSessionMessages(this.chatId, this.sessionStart, this.sessionEnd, this.groupId)
.then((messages) => {
return this.chatProvider.getMessagesUserData(messages, this.courseId).then((messages) => {
this.messages = <AddonModChatSessionMessageWithUserData[]> messages;
});
}).catch((error) => {
this.domUtils.showErrorModalDefault(error, 'core.errorloadingcontent', true);
@ -64,6 +70,25 @@ export class AddonModChatSessionMessagesPage {
});
}
/**
* Get the user fullname for a beep.
*
* @param id User Id before parsing.
* @return User fullname.
*/
protected getUserFullname(id: string): Promise<string> {
if (isNaN(parseInt(id, 10))) {
return Promise.resolve(id);
}
return this.userProvider.getProfile(parseInt(id, 10), this.courseId, true).then((user) => {
return user.fullname;
}).catch(() => {
// Error getting profile.
return id;
});
}
/**
* Refresh session messages.
*
@ -77,19 +102,4 @@ export class AddonModChatSessionMessagesPage {
});
}
/**
* Check if the date should be displayed between messages (when the day changes at midnight for example).
*
* @param message New message object.
* @param prevMessage Previous message object.
* @return True if messages are from diferent days, false othetwise.
*/
showDate(message: AddonModChatSessionMessageWithUserData, prevMessage: AddonModChatSessionMessageWithUserData): boolean {
if (!prevMessage) {
return true;
}
// Check if day has changed.
return !moment(message.timestamp * 1000).isSame(prevMessage.timestamp * 1000, 'day');
}
}

View File

@ -0,0 +1,88 @@
// (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 * as moment from 'moment';
import AddonModChatMessageWithUserData from './chat';
/**
* Helper service that provides some features for quiz.
*/
@Injectable()
export class AddonModChatHelperProvider {
/**
* Give some format info about messages.
*
* @param currentUserId User Id.
* @param message Message in a discussion.
* @param prevMessage Previous Message in a discussion (if any).
* @return Message with additional info.
*/
formatMessage(currentUserId: number, message: AddonModChatMessageWithUserData, prevMessage?: any): any {
message.showDate = this.showDate(message, prevMessage);
message.beep = message.message.substr(0, 5) == 'beep ' && message.message.substr(5).trim();
message.special = message.issystem || message.system || !!message.beep;
if (message.message.substr(0, 4) == '/me ') {
message.special = true;
message.message = message.message.substr(4).trim();
}
message.showUserData = this.showUserData(currentUserId, message, prevMessage);
prevMessage ?
prevMessage.showTail = this.showTail(prevMessage, message) : null;
}
/**
* Check if the user info should be displayed for the current message.
* User data is only displayed if the previous message was from another user.
*
* @param message Current message where to show the user info.
* @param prevMessage Previous message.
* @return Whether user data should be shown.
*/
protected showUserData(currentUserId: number, message: any, prevMessage?: any): boolean {
return message.userid != currentUserId &&
(!prevMessage || prevMessage.userid != message.userid || message.showDate || prevMessage.special);
}
/**
* Check if a css tail should be shown.
*
* @param message Current message where to show the user info.
* @param nextMessage Next message.
* @return Whether user data should be shown.
*/
protected showTail(message: AddonModChatMessageWithUserData, nextMessage?: AddonModChatMessageWithUserData): boolean {
return !nextMessage || nextMessage.userid != message.userid || nextMessage.showDate || nextMessage.special;
}
/**
* Check if the date should be displayed between messages (when the day changes at midnight for example).
*
* @param message New message object.
* @param prevMessage Previous message object.
* @return True if messages are from diferent days, false othetwise.
*/
protected showDate(message: AddonModChatMessageWithUserData, prevMessage: AddonModChatMessageWithUserData): boolean {
if (!prevMessage) {
return true;
}
// Check if day has changed.
return !moment(message.timestamp * 1000).isSame(prevMessage.timestamp * 1000, 'day');
}
}