commit
fbe9efd728
|
@ -433,14 +433,17 @@
|
||||||
"addon.mod_chat.errorwhilegettingchatusers": "local_moodlemobileapp",
|
"addon.mod_chat.errorwhilegettingchatusers": "local_moodlemobileapp",
|
||||||
"addon.mod_chat.errorwhileretrievingmessages": "local_moodlemobileapp",
|
"addon.mod_chat.errorwhileretrievingmessages": "local_moodlemobileapp",
|
||||||
"addon.mod_chat.errorwhilesendingmessage": "local_moodlemobileapp",
|
"addon.mod_chat.errorwhilesendingmessage": "local_moodlemobileapp",
|
||||||
|
"addon.mod_chat.messagebeepseveryone": "chat",
|
||||||
"addon.mod_chat.messagebeepsyou": "chat",
|
"addon.mod_chat.messagebeepsyou": "chat",
|
||||||
"addon.mod_chat.messageenter": "chat",
|
"addon.mod_chat.messageenter": "chat",
|
||||||
"addon.mod_chat.messageexit": "chat",
|
"addon.mod_chat.messageexit": "chat",
|
||||||
"addon.mod_chat.messages": "chat",
|
"addon.mod_chat.messages": "chat",
|
||||||
|
"addon.mod_chat.messageyoubeep": "chat",
|
||||||
"addon.mod_chat.modulenameplural": "chat",
|
"addon.mod_chat.modulenameplural": "chat",
|
||||||
"addon.mod_chat.mustbeonlinetosendmessages": "local_moodlemobileapp",
|
"addon.mod_chat.mustbeonlinetosendmessages": "local_moodlemobileapp",
|
||||||
"addon.mod_chat.nomessages": "chat",
|
"addon.mod_chat.nomessages": "chat",
|
||||||
"addon.mod_chat.nosessionsfound": "local_moodlemobileapp",
|
"addon.mod_chat.nosessionsfound": "local_moodlemobileapp",
|
||||||
|
"addon.mod_chat.saidto": "chat",
|
||||||
"addon.mod_chat.send": "chat",
|
"addon.mod_chat.send": "chat",
|
||||||
"addon.mod_chat.sessionstart": "chat",
|
"addon.mod_chat.sessionstart": "chat",
|
||||||
"addon.mod_chat.showincompletesessions": "local_moodlemobileapp",
|
"addon.mod_chat.showincompletesessions": "local_moodlemobileapp",
|
||||||
|
|
|
@ -4,8 +4,7 @@ $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: $gray-light !default;
|
$item-message-mine-bg: $gray-light !default;
|
||||||
|
|
||||||
ion-app.app-root page-addon-messages-discussion.ion-page {
|
@mixin message-page {
|
||||||
|
|
||||||
ion-content {
|
ion-content {
|
||||||
background-color: $gray-lighter !important;
|
background-color: $gray-lighter !important;
|
||||||
@include darkmode() {
|
@include darkmode() {
|
||||||
|
@ -35,7 +34,7 @@ ion-app.app-root page-addon-messages-discussion.ion-page {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message item.
|
// Message item.
|
||||||
.addon-message {
|
.item.item-block.addon-message {
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
@ -60,7 +59,7 @@ ion-app.app-root page-addon-messages-discussion.ion-page {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: .5rem!important;
|
margin-bottom: .5rem;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
color: $text-color;
|
color: $text-color;
|
||||||
|
|
||||||
|
@ -99,7 +98,7 @@ ion-app.app-root page-addon-messages-discussion.ion-page {
|
||||||
background-color: darken($item-message-bg, 10%);
|
background-color: darken($item-message-bg, 10%);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.item-block .item-inner {
|
.item-inner {
|
||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -131,6 +130,7 @@ ion-app.app-root page-addon-messages-discussion.ion-page {
|
||||||
.icon {
|
.icon {
|
||||||
font-size: 1.4em;
|
font-size: 1.4em;
|
||||||
line-height: initial;
|
line-height: initial;
|
||||||
|
color: $danger;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,10 +142,50 @@ ion-app.app-root page-addon-messages-discussion.ion-page {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
touch-action: none;
|
touch-action: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Defines when an item-message is the user's.
|
||||||
|
&.addon-message-mine {
|
||||||
|
background-color: $item-message-mine-bg;
|
||||||
|
align-self: flex-end;
|
||||||
|
|
||||||
|
&.activated {
|
||||||
|
background-color: darken($item-message-mine-bg, 10%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
@include float(end);
|
||||||
|
@include margin(2px, -3px, -2px, 5px);
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tail {
|
||||||
|
@include position(null, 0, 0, null);
|
||||||
|
@include margin-horizontal(null, -0.5rem);
|
||||||
|
border-bottom-color: $item-message-mine-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.activated .tail {
|
||||||
|
border-bottom-color: darken($item-message-mine-bg, 10%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.addon-message-not-mine .tail {
|
||||||
|
@include position(null, null, 0, 0);
|
||||||
|
@include margin-horizontal(-0.5rem, null);
|
||||||
|
border-bottom-color: $item-message-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.addon-message-not-mine.activated .tail {
|
||||||
|
border-bottom-color: darken($item-message-bg, 10%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.addon-message.addon-message-mine + .addon-message-no-user.addon-message-mine,
|
.item.addon-message.addon-message-mine + .item.addon-message.addon-message-no-user.addon-message-mine,
|
||||||
.addon-message.addon-message-not-mine + .addon-message-no-user.addon-message-not-mine {
|
.item.addon-message.addon-message-not-mine + .item.addon-message.addon-message-no-user.addon-message-not-mine {
|
||||||
h2 {
|
h2 {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
@ -154,46 +194,12 @@ ion-app.app-root page-addon-messages-discussion.ion-page {
|
||||||
border-top-right-radius: 0;
|
border-top-right-radius: 0;
|
||||||
border-top-left-radius: 0;
|
border-top-left-radius: 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Defines when an item-message is the user's.
|
|
||||||
.addon-message-mine {
|
|
||||||
background-color: $item-message-mine-bg;
|
|
||||||
align-self: flex-end;
|
|
||||||
|
|
||||||
&.activated {
|
ion-app.app-root page-addon-messages-discussion.ion-page {
|
||||||
background-color: darken($item-message-mine-bg, 10%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinner {
|
@include message-page();
|
||||||
@include float(end);
|
|
||||||
@include margin(2px, -3px, -2px, 5px);
|
|
||||||
|
|
||||||
svg {
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tail {
|
|
||||||
@include position(null, 0, 0, null);
|
|
||||||
@include margin-horizontal(null, -0.5rem);
|
|
||||||
border-bottom-color: $item-message-mine-bg;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.activated .tail {
|
|
||||||
border-bottom-color: darken($item-message-mine-bg, 10%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.addon-message-not-mine .tail {
|
|
||||||
@include position(null, null, 0, 0);
|
|
||||||
@include margin-horizontal(-0.5rem, null);
|
|
||||||
border-bottom-color: $item-message-bg;
|
|
||||||
}
|
|
||||||
|
|
||||||
.addon-message-not-mine.activated .tail {
|
|
||||||
border-bottom-color: darken($item-message-bg, 10%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toolbar-title {
|
.toolbar-title {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -224,4 +230,4 @@ ion-app.app-root page-addon-messages-discussion.ion-page {
|
||||||
ion-app.app-root.ios page-addon-messages-discussion ion-footer .toolbar:last-child {
|
ion-app.app-root.ios page-addon-messages-discussion ion-footer .toolbar:last-child {
|
||||||
padding-bottom: 4px;
|
padding-bottom: 4px;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate
|
||||||
import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate';
|
import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate';
|
||||||
import { AddonModChatComponentsModule } from './components/components.module';
|
import { AddonModChatComponentsModule } from './components/components.module';
|
||||||
import { AddonModChatProvider } from './providers/chat';
|
import { AddonModChatProvider } from './providers/chat';
|
||||||
|
import { AddonModChatHelperProvider } from './providers/helper';
|
||||||
import { AddonModChatLinkHandler } from './providers/link-handler';
|
import { AddonModChatLinkHandler } from './providers/link-handler';
|
||||||
import { AddonModChatListLinkHandler } from './providers/list-link-handler';
|
import { AddonModChatListLinkHandler } from './providers/list-link-handler';
|
||||||
import { AddonModChatModuleHandler } from './providers/module-handler';
|
import { AddonModChatModuleHandler } from './providers/module-handler';
|
||||||
|
@ -36,6 +37,7 @@ export const ADDON_MOD_CHAT_PROVIDERS: any[] = [
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
AddonModChatProvider,
|
AddonModChatProvider,
|
||||||
|
AddonModChatHelperProvider,
|
||||||
AddonModChatLinkHandler,
|
AddonModChatLinkHandler,
|
||||||
AddonModChatListLinkHandler,
|
AddonModChatListLinkHandler,
|
||||||
AddonModChatModuleHandler,
|
AddonModChatModuleHandler,
|
||||||
|
|
|
@ -9,14 +9,17 @@
|
||||||
"errorwhilegettingchatusers": "Error while getting chat users.",
|
"errorwhilegettingchatusers": "Error while getting chat users.",
|
||||||
"errorwhileretrievingmessages": "Error while retrieving messages from the server.",
|
"errorwhileretrievingmessages": "Error while retrieving messages from the server.",
|
||||||
"errorwhilesendingmessage": "Error while sending the message.",
|
"errorwhilesendingmessage": "Error while sending the message.",
|
||||||
|
"messagebeepseveryone": "{{$a}} beeps everyone!",
|
||||||
"messagebeepsyou": "{{$a}} has just beeped you!",
|
"messagebeepsyou": "{{$a}} has just beeped you!",
|
||||||
"messageenter": "{{$a}} has just entered this chat",
|
"messageenter": "{{$a}} has just entered this chat",
|
||||||
"messageexit": "{{$a}} has left this chat",
|
"messageexit": "{{$a}} has left this chat",
|
||||||
"messages": "Messages",
|
"messages": "Messages",
|
||||||
|
"messageyoubeep": "You beeped {{$a}}",
|
||||||
"modulenameplural": "Chats",
|
"modulenameplural": "Chats",
|
||||||
"mustbeonlinetosendmessages": "You must be online to send messages.",
|
"mustbeonlinetosendmessages": "You must be online to send messages.",
|
||||||
"nomessages": "No messages yet",
|
"nomessages": "No messages yet",
|
||||||
"nosessionsfound": "No sessions found",
|
"nosessionsfound": "No sessions found",
|
||||||
|
"saidto": "said to",
|
||||||
"send": "Send",
|
"send": "Send",
|
||||||
"sessionstart": "The next chat session will start on {{$a.date}}, ({{$a.fromnow}} from now)",
|
"sessionstart": "The next chat session will start on {{$a.date}}, ({{$a.fromnow}} from now)",
|
||||||
"showincompletesessions": "Show incomplete sessions",
|
"showincompletesessions": "Show incomplete sessions",
|
||||||
|
|
|
@ -8,55 +8,68 @@
|
||||||
</ion-buttons>
|
</ion-buttons>
|
||||||
</ion-navbar>
|
</ion-navbar>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
<ion-content class="has-footer">
|
<ion-content class="has-footer">
|
||||||
<core-loading [hideUntil]="loaded">
|
<core-loading [hideUntil]="loaded">
|
||||||
<div aria-live="polite">
|
<ion-list class="addon-messages-discussion-container safe-area-page" aria-live="polite">
|
||||||
<div *ngFor="let message of messages; index as index; last as last">
|
<ng-container *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">
|
<h6 text-center *ngIf="message.showDate" class="addon-messages-date">
|
||||||
<ion-badge text-wrap color="light">
|
{{ message.timestamp * 1000 | coreFormatDate:"strftimedayshort" }}
|
||||||
<span>{{ message.timestamp * 1000 | coreFormatDate:"strftimedayshort" }}</span>
|
</h6>
|
||||||
|
|
||||||
|
<div text-center *ngIf="message.special" class="addon-mod-chat-notice">
|
||||||
|
<ion-badge text-wrap color="success" *ngIf="message.system && 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>
|
||||||
|
|
||||||
|
<ion-badge text-wrap color="danger" *ngIf="message.system && 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-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.system && !message.beep">
|
||||||
|
<span><core-icon name="fa-asterisk"></core-icon> {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
|
||||||
|
<strong>{{ message.userfullname }} <core-format-text [text]="message.message" (afterRender)="last && scrollToBottom()"></core-format-text></strong></span>
|
||||||
</ion-badge>
|
</ion-badge>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div text-center *ngIf="message.system && message.message == 'enter'" class="addon-mod-chat-notice">
|
<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" [@coreSlideInOut]="message.userid == currentUserId ? '' : 'fromLeft'">
|
||||||
<ion-badge text-wrap color="light">
|
<!-- User data. -->
|
||||||
<span>{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ 'addon.mod_chat.messageenter' | translate:{$a: message.userfullname} }}</span>
|
<h2 class="addon-message-user">
|
||||||
</ion-badge>
|
<ion-avatar item-start core-user-avatar [user]="message" [linkProfile]="false" *ngIf="message.showUserData"></ion-avatar>
|
||||||
</div>
|
<div *ngIf="message.showUserData">{{ message.userfullname }}</div>
|
||||||
|
<ion-note>{{ message.timestamp * 1000 | coreFormatDate: "strftimetime" }}</ion-note>
|
||||||
<div text-center *ngIf="message.system && 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>
|
|
||||||
|
|
||||||
<div text-center *ngIf="message.message == currentUserBeep" class="addon-mod-chat-notice">
|
|
||||||
<ion-badge text-wrap color="light">
|
|
||||||
<span>{{ 'addon.mod_chat.messagebeepsyou' | translate:{$a: message.userfullname} }}</span>
|
|
||||||
</ion-badge>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ion-item text-wrap *ngIf="!message.system && 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>
|
</h2>
|
||||||
<core-format-text [text]="message.message" (afterRender)="last && scrollToBottom()"></core-format-text>
|
|
||||||
</ion-item>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div text-center margin *ngIf="!messages || messages.length <= 0">
|
<p class="addon-message-text">
|
||||||
<p>{{ 'addon.mod_chat.nomessages' | translate}}</p>
|
<core-format-text [text]="message.message" (afterRender)="last && scrollToBottom()"></core-format-text>
|
||||||
</div>
|
</p>
|
||||||
</div>
|
<div class="tail" *ngIf="message.showTail"></div>
|
||||||
|
</ion-item>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
</ion-list>
|
||||||
|
<core-empty-box *ngIf="!messages || messages.length <= 0" icon="chatbubbles" [message]="'addon.mod_chat.nomessages' | translate"></core-empty-box>
|
||||||
</core-loading>
|
</core-loading>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
<ion-footer color="light" class="footer-adjustable">
|
<ion-footer color="light" class="footer-adjustable">
|
||||||
<ion-toolbar color="light" position="bottom">
|
<ion-toolbar color="light" position="bottom">
|
||||||
<p text-center *ngIf="!isOnline">{{ 'addon.mod_chat.mustbeonlinetosendmessages' | translate }}</p>
|
<p text-center *ngIf="!isOnline">{{ 'addon.mod_chat.mustbeonlinetosendmessages' | translate }}</p>
|
||||||
<core-send-message-form *ngIf="isOnline && polling && loaded" [message]="newMessage" (onSubmit)="sendMessage($event)" [showKeyboard]="showKeyboard" [placeholder]="'addon.messages.newmessage' | translate" (onResize)="resizeContent()"></core-send-message-form>
|
<core-send-message-form [sendDisabled]="sending" *ngIf="isOnline && polling && loaded" [message]="newMessage" (onSubmit)="sendMessage($event)" [placeholder]="'addon.messages.newmessage' | translate" (onResize)="resizeContent()"></core-send-message-form>
|
||||||
<button *ngIf="isOnline && !polling && loaded" (click)="reconnect()" ion-button block color="light">{{ 'core.login.reconnect' | translate }}</button>
|
<button *ngIf="isOnline && !polling && loaded" (click)="reconnect()" ion-button block color="light">{{ 'core.login.reconnect' | translate }}</button>
|
||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
</ion-footer>
|
</ion-footer>
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
ion-app.app-root page-addon-mod-chat-chat {
|
ion-app.app-root page-addon-mod-chat-chat.ion-page {
|
||||||
|
@include message-page();
|
||||||
|
|
||||||
.addon-mod-chat-notice {
|
.addon-mod-chat-notice {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
.addon-mod-chat-message {
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,11 @@ import { CoreEventsProvider } from '@providers/events';
|
||||||
import { CoreLoggerProvider } from '@providers/logger';
|
import { CoreLoggerProvider } from '@providers/logger';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
import { AddonModChatProvider } from '../../providers/chat';
|
||||||
import { AddonModChatProvider, AddonModChatMessageWithUserData } from '../../providers/chat';
|
import { AddonModChatHelperProvider, AddonModChatMessageForView } from '../../providers/helper';
|
||||||
import { Network } from '@ionic-native/network';
|
import { Network } from '@ionic-native/network';
|
||||||
import * as moment from 'moment';
|
import { coreSlideInOut } from '@classes/animations';
|
||||||
|
import { CoreSendMessageFormComponent } from '@components/send-message-form/send-message-form';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page that displays a chat session.
|
* Page that displays a chat session.
|
||||||
|
@ -31,17 +32,20 @@ import * as moment from 'moment';
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'page-addon-mod-chat-chat',
|
selector: 'page-addon-mod-chat-chat',
|
||||||
templateUrl: 'chat.html',
|
templateUrl: 'chat.html',
|
||||||
|
animations: [coreSlideInOut]
|
||||||
})
|
})
|
||||||
export class AddonModChatChatPage {
|
export class AddonModChatChatPage {
|
||||||
@ViewChild(Content) content: Content;
|
@ViewChild(Content) content: Content;
|
||||||
|
@ViewChild(CoreSendMessageFormComponent) sendMessageForm: CoreSendMessageFormComponent;
|
||||||
|
|
||||||
loaded = false;
|
loaded = false;
|
||||||
title: string;
|
title: string;
|
||||||
messages: AddonModChatMessageWithUserData[] = [];
|
messages: AddonModChatMessageForView[] = [];
|
||||||
newMessage: string;
|
newMessage: string;
|
||||||
polling: any;
|
polling: any;
|
||||||
isOnline: boolean;
|
isOnline: boolean;
|
||||||
currentUserBeep: string;
|
currentUserId: number;
|
||||||
|
sending: boolean;
|
||||||
|
|
||||||
protected logger;
|
protected logger;
|
||||||
protected courseId: number;
|
protected courseId: number;
|
||||||
|
@ -53,17 +57,18 @@ export class AddonModChatChatPage {
|
||||||
protected keyboardObserver: any;
|
protected keyboardObserver: any;
|
||||||
protected viewDestroyed = false;
|
protected viewDestroyed = false;
|
||||||
protected pollingRunning = false;
|
protected pollingRunning = false;
|
||||||
|
protected users = [];
|
||||||
|
|
||||||
constructor(navParams: NavParams, logger: CoreLoggerProvider, network: Network, zone: NgZone, private navCtrl: NavController,
|
constructor(navParams: NavParams, logger: CoreLoggerProvider, network: Network, zone: NgZone, private navCtrl: NavController,
|
||||||
private chatProvider: AddonModChatProvider, private appProvider: CoreAppProvider, sitesProvider: CoreSitesProvider,
|
private chatProvider: AddonModChatProvider, private appProvider: CoreAppProvider, sitesProvider: CoreSitesProvider,
|
||||||
private modalCtrl: ModalController, private domUtils: CoreDomUtilsProvider, private textUtils: CoreTextUtilsProvider,
|
private modalCtrl: ModalController, private domUtils: CoreDomUtilsProvider,
|
||||||
private eventsProvider: CoreEventsProvider) {
|
private eventsProvider: CoreEventsProvider, private chatHelper: AddonModChatHelperProvider) {
|
||||||
|
|
||||||
this.chatId = navParams.get('chatId');
|
this.chatId = navParams.get('chatId');
|
||||||
this.courseId = navParams.get('courseId');
|
this.courseId = navParams.get('courseId');
|
||||||
this.title = navParams.get('title');
|
this.title = navParams.get('title');
|
||||||
this.logger = logger.getInstance('AddonModChoiceChoicePage');
|
this.logger = logger.getInstance('AddonModChoiceChoicePage');
|
||||||
this.currentUserBeep = 'beep ' + sitesProvider.getCurrentSiteUserId();
|
this.currentUserId = sitesProvider.getCurrentSiteUserId();
|
||||||
this.isOnline = this.appProvider.isOnline();
|
this.isOnline = this.appProvider.isOnline();
|
||||||
this.onlineObserver = network.onchange().subscribe(() => {
|
this.onlineObserver = network.onchange().subscribe(() => {
|
||||||
// Execute the callback in the Angular zone, so change detection doesn't stop working.
|
// Execute the callback in the Angular zone, so change detection doesn't stop working.
|
||||||
|
@ -116,16 +121,62 @@ export class AddonModChatChatPage {
|
||||||
* Display the chat users modal.
|
* Display the chat users modal.
|
||||||
*/
|
*/
|
||||||
showChatUsers(): void {
|
showChatUsers(): void {
|
||||||
const modal = this.modalCtrl.create('AddonModChatUsersPage', {sessionId: this.sessionId});
|
// Create the toc modal.
|
||||||
|
const modal = this.modalCtrl.create('AddonModChatUsersPage', {
|
||||||
|
sessionId: this.sessionId
|
||||||
|
}, { cssClass: 'core-modal-lateral',
|
||||||
|
showBackdrop: true,
|
||||||
|
enableBackdropDismiss: true,
|
||||||
|
enterAnimation: 'core-modal-lateral-transition',
|
||||||
|
leaveAnimation: 'core-modal-lateral-transition' });
|
||||||
|
|
||||||
modal.onDidDismiss((data) => {
|
modal.onDidDismiss((data) => {
|
||||||
if (data && data.talkTo) {
|
if (data && data.talkTo) {
|
||||||
this.newMessage = `To ${data.talkTo}: `;
|
this.newMessage = `To ${data.talkTo}: ` + (this.sendMessageForm.message || '');
|
||||||
}
|
}
|
||||||
if (data && data.beepTo) {
|
if (data && data.beepTo) {
|
||||||
this.sendMessage('', data.beepTo);
|
this.sendMessage('', data.beepTo);
|
||||||
}
|
}
|
||||||
|
if (data && data.users) {
|
||||||
|
this.users = data.users;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
modal.present({
|
||||||
|
ev: event
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = this.users.find((user) => user.id == id);
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
return Promise.resolve(user.fullname);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.chatProvider.getChatUsers(this.sessionId).then((data) => {
|
||||||
|
this.users = data.users;
|
||||||
|
const user = this.users.find((user) => user.id == id);
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
return user.fullname;
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}).catch((error) => {
|
||||||
|
// Ignore errors.
|
||||||
|
return id;
|
||||||
});
|
});
|
||||||
modal.present();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -149,8 +200,26 @@ export class AddonModChatChatPage {
|
||||||
this.lastTime = messagesInfo.chatnewlasttime || 0;
|
this.lastTime = messagesInfo.chatnewlasttime || 0;
|
||||||
|
|
||||||
return this.chatProvider.getMessagesUserData(messagesInfo.messages, this.courseId).then((messages) => {
|
return this.chatProvider.getMessagesUserData(messagesInfo.messages, this.courseId).then((messages) => {
|
||||||
this.messages = this.messages.concat(<AddonModChatMessageWithUserData[]> messages);
|
|
||||||
if (messages.length) {
|
if (messages.length) {
|
||||||
|
const previousLength = this.messages.length;
|
||||||
|
this.messages = this.messages.concat(<AddonModChatMessageForView[]> messages);
|
||||||
|
|
||||||
|
// Calculate which messages need to display the date or user data.
|
||||||
|
for (let index = previousLength ; index < this.messages.length; index++) {
|
||||||
|
const message = this.messages[index];
|
||||||
|
const prevMessage = index > 0 ? this.messages[index - 1] : null;
|
||||||
|
|
||||||
|
this.chatHelper.formatMessage(this.currentUserId, message, prevMessage);
|
||||||
|
|
||||||
|
if (message.beep && message.beep != this.currentUserId + '') {
|
||||||
|
this.getUserFullname(message.beep).then((fullname) => {
|
||||||
|
message.beepWho = fullname;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.messages[this.messages.length - 1].showTail = true;
|
||||||
|
|
||||||
// New messages or beeps, scroll to bottom.
|
// New messages or beeps, scroll to bottom.
|
||||||
setTimeout(() => this.scrollToBottom());
|
setTimeout(() => this.scrollToBottom());
|
||||||
}
|
}
|
||||||
|
@ -188,7 +257,7 @@ export class AddonModChatChatPage {
|
||||||
/**
|
/**
|
||||||
* Convenience function to be called every certain time to fetch chat messages.
|
* 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> {
|
protected fetchMessagesInterval(): Promise<void> {
|
||||||
this.logger.debug('Polling for messages');
|
this.logger.debug('Polling for messages');
|
||||||
|
@ -218,22 +287,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.
|
* Send a message to the chat.
|
||||||
*
|
*
|
||||||
|
@ -248,9 +301,8 @@ export class AddonModChatChatPage {
|
||||||
// Silent error.
|
// Silent error.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
text = this.textUtils.replaceNewLines(text, '<br>');
|
|
||||||
|
|
||||||
const modal = this.domUtils.showModalLoading('core.sending', true);
|
this.sending = true;
|
||||||
this.chatProvider.sendMessage(this.sessionId, text, beep).then(() => {
|
this.chatProvider.sendMessage(this.sessionId, text, beep).then(() => {
|
||||||
// Update messages to show the sent message.
|
// Update messages to show the sent message.
|
||||||
this.fetchMessagesInterval().catch(() => {
|
this.fetchMessagesInterval().catch(() => {
|
||||||
|
@ -261,9 +313,11 @@ export class AddonModChatChatPage {
|
||||||
messages without the keyboard being closed. */
|
messages without the keyboard being closed. */
|
||||||
this.appProvider.closeKeyboard();
|
this.appProvider.closeKeyboard();
|
||||||
|
|
||||||
|
this.newMessage = text;
|
||||||
|
|
||||||
this.domUtils.showErrorModalDefault(error, 'addon.mod_chat.errorwhilesendingmessage', true);
|
this.domUtils.showErrorModalDefault(error, 'addon.mod_chat.errorwhilesendingmessage', true);
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
modal.dismiss();
|
this.sending = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,33 +8,56 @@
|
||||||
<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-loading [hideUntil]="loaded">
|
<core-loading [hideUntil]="loaded">
|
||||||
<div *ngFor="let message of messages; index as index; last as last">
|
<ion-list class="addon-messages-discussion-container safe-area-page" aria-live="polite">
|
||||||
<div text-center *ngIf="showDate(messages[index], messages[index - 1])" class="addon-mod-chat-notice">
|
<ng-container *ngFor="let message of messages; index as index;">
|
||||||
<ion-badge text-wrap color="light">
|
<h6 text-center *ngIf="message.showDate" class="addon-messages-date">
|
||||||
<span>{{ message.timestamp * 1000 | coreFormatDate:"strftimedayshort" }}</span>
|
{{ message.timestamp * 1000 | coreFormatDate:"strftimedayshort" }}
|
||||||
</ion-badge>
|
</h6>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div text-center *ngIf="message.issystem && message.message == 'enter'" class="addon-mod-chat-notice">
|
<div text-center *ngIf="message.special" class="addon-mod-chat-notice">
|
||||||
<ion-badge text-wrap color="light">
|
<ion-badge text-wrap color="success" *ngIf="message.issystem && message.message == 'enter'">
|
||||||
<span>{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ 'addon.mod_chat.messageenter' | translate:{$a: message.userfullname} }}</span>
|
<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>
|
</ion-badge>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div text-center *ngIf="message.issystem && message.message == 'exit'" class="addon-mod-chat-notice">
|
<ion-badge text-wrap color="danger" *ngIf="message.issystem && message.message == 'exit'">
|
||||||
<ion-badge text-wrap color="light">
|
<span><core-icon name="fa-sign-out"></core-icon> {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ 'addon.mod_chat.messageexit' | translate:{$a: message.userfullname} }}</span>
|
||||||
<span>{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ 'addon.mod_chat.messageexit' | translate:{$a: message.userfullname} }}</span>
|
</ion-badge>
|
||||||
</ion-badge>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ion-item text-wrap *ngIf="!message.issystem && message.message.substr(0, 4) != 'beep'" class="addon-mod-chat-message">
|
<ion-badge text-wrap color="primary" *ngIf="message.beep == 'all'">
|
||||||
<ion-avatar core-user-avatar [user]="message" item-start></ion-avatar>
|
<span><ion-icon name="notifications"></ion-icon> {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
|
||||||
<h2>
|
{{ 'addon.mod_chat.messagebeepseveryone' | translate:{$a: message.userfullname} }} </span>
|
||||||
<p float-end>{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}</p>
|
</ion-badge>
|
||||||
<core-format-text [text]="message.userfullname"></core-format-text>
|
|
||||||
</h2>
|
<ion-badge text-wrap color="primary" *ngIf="message.userid != currentUserId && message.beep == currentUserId">
|
||||||
<core-format-text [text]="message.message"></core-format-text>
|
<span><ion-icon name="notifications"></ion-icon> {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
|
||||||
</ion-item>
|
{{ 'addon.mod_chat.messagebeepsyou' | translate:{$a: message.userfullname} }} </span>
|
||||||
</div>
|
</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>
|
</core-loading>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
ion-app.app-root page-addon-mod-chat-session-messages {
|
ion-app.app-root page-addon-mod-chat-session-messages.ion-page {
|
||||||
|
@include message-page();
|
||||||
|
|
||||||
.addon-mod-chat-notice {
|
.addon-mod-chat-notice {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
.addon-mod-chat-message {
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,10 @@
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { IonicPage, NavParams } from 'ionic-angular';
|
import { IonicPage, NavParams } from 'ionic-angular';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { AddonModChatProvider, AddonModChatSessionMessageWithUserData } from '../../providers/chat';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import * as moment from 'moment';
|
import { CoreUserProvider } from '@core/user/providers/user';
|
||||||
|
import { AddonModChatProvider } from '../../providers/chat';
|
||||||
|
import { AddonModChatHelperProvider, AddonModChatSessionMessageForView } from '../../providers/helper';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page that displays list of chat session messages.
|
* Page that displays list of chat session messages.
|
||||||
|
@ -28,20 +30,24 @@ import * as moment from 'moment';
|
||||||
})
|
})
|
||||||
export class AddonModChatSessionMessagesPage {
|
export class AddonModChatSessionMessagesPage {
|
||||||
|
|
||||||
|
currentUserId: number;
|
||||||
|
|
||||||
protected courseId: number;
|
protected courseId: number;
|
||||||
protected chatId: number;
|
protected chatId: number;
|
||||||
protected sessionStart: number;
|
protected sessionStart: number;
|
||||||
protected sessionEnd: number;
|
protected sessionEnd: number;
|
||||||
protected groupId: number;
|
protected groupId: number;
|
||||||
protected loaded = false;
|
protected loaded = false;
|
||||||
protected messages: AddonModChatSessionMessageWithUserData[] = [];
|
protected messages: AddonModChatSessionMessageForView[] = [];
|
||||||
|
|
||||||
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.courseId = navParams.get('courseId');
|
||||||
this.chatId = navParams.get('chatId');
|
this.chatId = navParams.get('chatId');
|
||||||
this.groupId = navParams.get('groupId');
|
this.groupId = navParams.get('groupId');
|
||||||
this.sessionStart = navParams.get('sessionStart');
|
this.sessionStart = navParams.get('sessionStart');
|
||||||
this.sessionEnd = navParams.get('sessionEnd');
|
this.sessionEnd = navParams.get('sessionEnd');
|
||||||
|
this.currentUserId = sitesProvider.getCurrentSiteUserId();
|
||||||
|
|
||||||
this.fetchMessages();
|
this.fetchMessages();
|
||||||
}
|
}
|
||||||
|
@ -55,7 +61,25 @@ export class AddonModChatSessionMessagesPage {
|
||||||
return this.chatProvider.getSessionMessages(this.chatId, this.sessionStart, this.sessionEnd, this.groupId)
|
return this.chatProvider.getSessionMessages(this.chatId, this.sessionStart, this.sessionEnd, this.groupId)
|
||||||
.then((messages) => {
|
.then((messages) => {
|
||||||
return this.chatProvider.getMessagesUserData(messages, this.courseId).then((messages) => {
|
return this.chatProvider.getMessagesUserData(messages, this.courseId).then((messages) => {
|
||||||
this.messages = <AddonModChatSessionMessageWithUserData[]> messages;
|
this.messages = <AddonModChatSessionMessageForView[]> messages;
|
||||||
|
|
||||||
|
if (messages.length) {
|
||||||
|
// Calculate which messages need to display the date or user data.
|
||||||
|
for (let index = 0 ; index < this.messages.length; index++) {
|
||||||
|
const message = this.messages[index];
|
||||||
|
const prevMessage = index > 0 ? this.messages[index - 1] : null;
|
||||||
|
|
||||||
|
this.chatHelper.formatMessage(this.currentUserId, message, prevMessage);
|
||||||
|
|
||||||
|
if (message.beep && message.beep != this.currentUserId + '') {
|
||||||
|
this.getUserFullname(message.beep).then((fullname) => {
|
||||||
|
message.beepWho = fullname;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.messages[this.messages.length - 1].showTail = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
this.domUtils.showErrorModalDefault(error, 'core.errorloadingcontent', true);
|
this.domUtils.showErrorModalDefault(error, 'core.errorloadingcontent', true);
|
||||||
|
@ -64,6 +88,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.
|
* Refresh session messages.
|
||||||
*
|
*
|
||||||
|
@ -77,19 +120,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');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ export class AddonModChatUsersPage {
|
||||||
* Close the chat users modal.
|
* Close the chat users modal.
|
||||||
*/
|
*/
|
||||||
closeModal(): void {
|
closeModal(): void {
|
||||||
this.viewCtrl.dismiss();
|
this.viewCtrl.dismiss({users: this.users});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,8 +77,8 @@ export class AddonModChatUsersPage {
|
||||||
*
|
*
|
||||||
* @param user User object.
|
* @param user User object.
|
||||||
*/
|
*/
|
||||||
talkTo(user: AddonModChatUser): void {
|
talkTo(user: AddonModChatUser): void {
|
||||||
this.viewCtrl.dismiss({talkTo: user.fullname});
|
this.viewCtrl.dismiss({talkTo: user.fullname, users: this.users});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -87,7 +87,7 @@ export class AddonModChatUsersPage {
|
||||||
* @param user User object.
|
* @param user User object.
|
||||||
*/
|
*/
|
||||||
beepTo(user: AddonModChatUser): void {
|
beepTo(user: AddonModChatUser): void {
|
||||||
this.viewCtrl.dismiss({beepTo: user.id});
|
this.viewCtrl.dismiss({beepTo: user.id, users: this.users});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
||||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||||
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
|
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
|
||||||
|
import { AddonModChatMessageForView, AddonModChatSessionMessageForView } from './helper';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that provides some features for chats.
|
* Service that provides some features for chats.
|
||||||
|
@ -157,9 +158,9 @@ export class AddonModChatProvider {
|
||||||
* @return Promise always resolved with the formatted messages.
|
* @return Promise always resolved with the formatted messages.
|
||||||
*/
|
*/
|
||||||
getMessagesUserData(messages: (AddonModChatMessage | AddonModChatSessionMessage)[], courseId: number)
|
getMessagesUserData(messages: (AddonModChatMessage | AddonModChatSessionMessage)[], courseId: number)
|
||||||
: Promise<(AddonModChatMessageWithUserData | AddonModChatSessionMessageWithUserData)[]> {
|
: Promise<(AddonModChatMessageForView | AddonModChatSessionMessageForView)[]> {
|
||||||
|
|
||||||
const promises = messages.map((message: AddonModChatMessageWithUserData | AddonModChatSessionMessageWithUserData) => {
|
const promises = messages.map((message: AddonModChatMessageForView | AddonModChatSessionMessageForView) => {
|
||||||
return this.userProvider.getProfile(message.userid, courseId, true).then((user) => {
|
return this.userProvider.getProfile(message.userid, courseId, true).then((user) => {
|
||||||
message.userfullname = user.fullname;
|
message.userfullname = user.fullname;
|
||||||
message.userprofileimageurl = user.profileimageurl;
|
message.userprofileimageurl = user.profileimageurl;
|
||||||
|
@ -448,7 +449,7 @@ export type AddonModChatMessage = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message with user data
|
* Message with user data.
|
||||||
*/
|
*/
|
||||||
export type AddonModChatMessageWithUserData = AddonModChatMessage & AddonModChatMessageUserData;
|
export type AddonModChatMessageWithUserData = AddonModChatMessage & AddonModChatMessageUserData;
|
||||||
|
|
||||||
|
@ -484,7 +485,7 @@ export type AddonModChatSessionMessage = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message with user data
|
* Session message with user data.
|
||||||
*/
|
*/
|
||||||
export type AddonModChatSessionMessageWithUserData = AddonModChatSessionMessage & AddonModChatMessageUserData;
|
export type AddonModChatSessionMessageWithUserData = AddonModChatSessionMessage & AddonModChatMessageUserData;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
// (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 { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||||
|
import * as moment from 'moment';
|
||||||
|
import { AddonModChatMessageWithUserData, AddonModChatSessionMessageWithUserData } from './chat';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper service that provides some features for chat.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class AddonModChatHelperProvider {
|
||||||
|
|
||||||
|
static patternto = new RegExp(/^To\s([^:]+):(.*)/);
|
||||||
|
|
||||||
|
constructor(protected translate: TranslateService,
|
||||||
|
protected textUtils: CoreTextUtilsProvider) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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: AddonModChatMessageForView | AddonModChatSessionMessageForView,
|
||||||
|
prevMessage?: AddonModChatMessageForView | AddonModChatSessionMessageForView): any {
|
||||||
|
message.message = message.message.trim();
|
||||||
|
|
||||||
|
message.showDate = this.showDate(message, prevMessage);
|
||||||
|
message.beep = message.message.substr(0, 5) == 'beep ' && message.message.substr(5).trim();
|
||||||
|
|
||||||
|
message.special = (<AddonModChatSessionMessageForView> message).issystem || (<AddonModChatMessageForView> message).system ||
|
||||||
|
!!message.beep;
|
||||||
|
|
||||||
|
if (message.message.substr(0, 4) == '/me ') {
|
||||||
|
message.special = true;
|
||||||
|
message.message = message.message.substr(4).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!message.special && message.message.match(AddonModChatHelperProvider.patternto)) {
|
||||||
|
const matches = message.message.match(AddonModChatHelperProvider.patternto);
|
||||||
|
message.message = '<b>' + this.translate.instant('addon.mod_chat.saidto') +
|
||||||
|
'</b> <i>' + matches[1] + '</i>: ' + matches[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
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: AddonModChatMessageForView | AddonModChatSessionMessageForView,
|
||||||
|
prevMessage?: AddonModChatMessageForView | AddonModChatSessionMessageForView): 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: AddonModChatMessageForView | AddonModChatSessionMessageForView,
|
||||||
|
nextMessage?: AddonModChatMessageForView | AddonModChatSessionMessageForView): 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: AddonModChatMessageForView | AddonModChatSessionMessageForView,
|
||||||
|
prevMessage: AddonModChatMessageForView | AddonModChatSessionMessageForView): boolean {
|
||||||
|
if (!prevMessage) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if day has changed.
|
||||||
|
return !moment(message.timestamp * 1000).isSame(prevMessage.timestamp * 1000, 'day');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special info for view usage.
|
||||||
|
*/
|
||||||
|
type AddonModChatInfoForView = {
|
||||||
|
showDate?: boolean; // If date should be displayed before the message.
|
||||||
|
beep?: string; // User id of the beeped user or 'all'.
|
||||||
|
special?: boolean; // True if is an special message (system, beep or command).
|
||||||
|
showUserData?: boolean; // If user data should be displayed.
|
||||||
|
showTail?: boolean; // If tail should be displayed (decoration).
|
||||||
|
beepWho?: string; // Fullname of the beeped user.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message with data for view usage.
|
||||||
|
*/
|
||||||
|
export type AddonModChatMessageForView = AddonModChatMessageWithUserData & AddonModChatInfoForView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session message with data for view usage.
|
||||||
|
*/
|
||||||
|
export type AddonModChatSessionMessageForView = AddonModChatSessionMessageWithUserData & AddonModChatInfoForView;
|
|
@ -432,14 +432,17 @@
|
||||||
"addon.mod_chat.errorwhilegettingchatusers": "Error while getting chat users.",
|
"addon.mod_chat.errorwhilegettingchatusers": "Error while getting chat users.",
|
||||||
"addon.mod_chat.errorwhileretrievingmessages": "Error while retrieving messages from the server.",
|
"addon.mod_chat.errorwhileretrievingmessages": "Error while retrieving messages from the server.",
|
||||||
"addon.mod_chat.errorwhilesendingmessage": "Error while sending the message.",
|
"addon.mod_chat.errorwhilesendingmessage": "Error while sending the message.",
|
||||||
|
"addon.mod_chat.messagebeepseveryone": "{{$a}} beeps everyone!",
|
||||||
"addon.mod_chat.messagebeepsyou": "{{$a}} has just beeped you!",
|
"addon.mod_chat.messagebeepsyou": "{{$a}} has just beeped you!",
|
||||||
"addon.mod_chat.messageenter": "{{$a}} has just entered this chat",
|
"addon.mod_chat.messageenter": "{{$a}} has just entered this chat",
|
||||||
"addon.mod_chat.messageexit": "{{$a}} has left this chat",
|
"addon.mod_chat.messageexit": "{{$a}} has left this chat",
|
||||||
"addon.mod_chat.messages": "Messages",
|
"addon.mod_chat.messages": "Messages",
|
||||||
|
"addon.mod_chat.messageyoubeep": "You beeped {{$a}}",
|
||||||
"addon.mod_chat.modulenameplural": "Chats",
|
"addon.mod_chat.modulenameplural": "Chats",
|
||||||
"addon.mod_chat.mustbeonlinetosendmessages": "You must be online to send messages.",
|
"addon.mod_chat.mustbeonlinetosendmessages": "You must be online to send messages.",
|
||||||
"addon.mod_chat.nomessages": "No messages yet",
|
"addon.mod_chat.nomessages": "No messages yet",
|
||||||
"addon.mod_chat.nosessionsfound": "No sessions found",
|
"addon.mod_chat.nosessionsfound": "No sessions found",
|
||||||
|
"addon.mod_chat.saidto": "said to",
|
||||||
"addon.mod_chat.send": "Send",
|
"addon.mod_chat.send": "Send",
|
||||||
"addon.mod_chat.sessionstart": "The next chat session will start on {{$a.date}}, ({{$a.fromnow}} from now)",
|
"addon.mod_chat.sessionstart": "The next chat session will start on {{$a.date}}, ({{$a.fromnow}} from now)",
|
||||||
"addon.mod_chat.showincompletesessions": "Show incomplete sessions",
|
"addon.mod_chat.showincompletesessions": "Show incomplete sessions",
|
||||||
|
|
|
@ -60,7 +60,8 @@ ion-app.app-root core-ion-tabs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-content, .fixed-content {
|
ion-content:not(.has-footer) > .scroll-content,
|
||||||
|
ion-content:not(.has-footer) > .fixed-content {
|
||||||
margin-bottom: 0 !important;
|
margin-bottom: 0 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<form>
|
<form>
|
||||||
<textarea class="core-send-message-input" [core-auto-focus]="showKeyboard" [placeholder]="placeholder" rows="1" core-auto-rows [(ngModel)]="message" name="message" (onResize)="textareaResized()" (keydown.enter)="enterClicked($event)" (keydown.control.enter)="enterClicked($event, 'control')" (keydown.meta.enter)="enterClicked($event, 'meta')" aria-multiline="true"></textarea>
|
<textarea class="core-send-message-input" [core-auto-focus]="showKeyboard" [placeholder]="placeholder" rows="1" core-auto-rows [(ngModel)]="message" name="message" (onResize)="textareaResized()" (keydown.enter)="enterClicked($event)" (keydown.control.enter)="enterClicked($event, 'control')" (keydown.meta.enter)="enterClicked($event, 'meta')" aria-multiline="true"></textarea>
|
||||||
<ion-buttons end>
|
<ion-buttons end>
|
||||||
<button ion-button icon-only clear="true" type="submit" [disabled]="!message" [attr.aria-label]="'core.send' | translate" [core-suppress-events] (onClick)="submitForm($event)">
|
<button ion-button icon-only clear="true" type="submit" [disabled]="!message || sendDisabled" [attr.aria-label]="'core.send' | translate" [core-suppress-events] (onClick)="submitForm($event)">
|
||||||
<ion-icon name="send" color="dark"></ion-icon>
|
<ion-icon name="send" color="dark"></ion-icon>
|
||||||
</button>
|
</button>
|
||||||
</ion-buttons>
|
</ion-buttons>
|
||||||
|
|
|
@ -39,6 +39,7 @@ export class CoreSendMessageFormComponent implements OnInit {
|
||||||
@Input() message: string; // Input text.
|
@Input() message: string; // Input text.
|
||||||
@Input() placeholder = ''; // Placeholder for the input area.
|
@Input() placeholder = ''; // Placeholder for the input area.
|
||||||
@Input() showKeyboard = false; // If keyboard is shown or not.
|
@Input() showKeyboard = false; // If keyboard is shown or not.
|
||||||
|
@Input() sendDisabled = false; // If send is disabled.
|
||||||
@Output() onSubmit: EventEmitter<string>; // Send data when submitting the message form.
|
@Output() onSubmit: EventEmitter<string>; // Send data when submitting the message form.
|
||||||
@Output() onResize: EventEmitter<void>; // Emit when resizing the textarea.
|
@Output() onResize: EventEmitter<void>; // Emit when resizing the textarea.
|
||||||
|
|
||||||
|
@ -99,6 +100,10 @@ export class CoreSendMessageFormComponent implements OnInit {
|
||||||
* @param other The name of the other key that was clicked, undefined if no other key.
|
* @param other The name of the other key that was clicked, undefined if no other key.
|
||||||
*/
|
*/
|
||||||
enterClicked(e: Event, other: string): void {
|
enterClicked(e: Event, other: string): void {
|
||||||
|
if (this.sendDisabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.sendOnEnter && !other) {
|
if (this.sendOnEnter && !other) {
|
||||||
// Enter clicked, send the message.
|
// Enter clicked, send the message.
|
||||||
this.submitForm(e);
|
this.submitForm(e);
|
||||||
|
|
|
@ -21,7 +21,7 @@ ion-app.app-root .ion-page {
|
||||||
color: $core-dark-text-color;
|
color: $core-dark-text-color;
|
||||||
background-color: $core-dark-item-bg-color;
|
background-color: $core-dark-item-bg-color;
|
||||||
|
|
||||||
a {
|
a:not(.button) {
|
||||||
color: $core-dark-link-color;
|
color: $core-dark-link-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue