MOBILE-2336 chat: Migrate Chat
parent
11e3cca5c1
commit
c0b43405b6
|
@ -0,0 +1,36 @@
|
|||
// (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 { NgModule } from '@angular/core';
|
||||
import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate';
|
||||
import { AddonModChatComponentsModule } from './components/components.module';
|
||||
import { AddonModChatModuleHandler } from './providers/module-handler';
|
||||
import { AddonModChatProvider } from './providers/chat';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
AddonModChatComponentsModule
|
||||
],
|
||||
providers: [
|
||||
AddonModChatProvider,
|
||||
AddonModChatModuleHandler,
|
||||
]
|
||||
})
|
||||
export class AddonModChatModule {
|
||||
constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModChatModuleHandler) {
|
||||
moduleDelegate.registerHandler(moduleHandler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
// (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 { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { IonicModule } from 'ionic-angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CoreComponentsModule } from '@components/components.module';
|
||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||
import { CoreCourseComponentsModule } from '@core/course/components/components.module';
|
||||
import { AddonModChatIndexComponent } from './index/index';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AddonModChatIndexComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
CoreCourseComponentsModule
|
||||
],
|
||||
providers: [
|
||||
],
|
||||
exports: [
|
||||
AddonModChatIndexComponent
|
||||
],
|
||||
entryComponents: [
|
||||
AddonModChatIndexComponent
|
||||
]
|
||||
})
|
||||
export class AddonModChatComponentsModule {}
|
|
@ -0,0 +1,22 @@
|
|||
<!-- Buttons to add to the header. -->
|
||||
<core-navbar-buttons end>
|
||||
<core-context-menu>
|
||||
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" [href]="externalUrl" [iconAction]="'open'"></core-context-menu-item>
|
||||
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" (action)="expandDescription()" [iconAction]="'arrow-forward'"></core-context-menu-item>
|
||||
<core-context-menu-item *ngIf="loaded && !hasOffline && isOnline" [priority]="700" [content]="'core.refresh' | translate" (action)="doRefresh(null, $event)" [iconAction]="refreshIcon" [closeOnClick]="false"></core-context-menu-item>
|
||||
</core-context-menu>
|
||||
</core-navbar-buttons>
|
||||
|
||||
<!-- Content. -->
|
||||
<core-loading [hideUntil]="loaded" class="core-loading-center">
|
||||
|
||||
<core-course-module-description [description]="description" [component]="component" [componentId]="componentId"></core-course-module-description>
|
||||
|
||||
<ion-card class="core-info-card" *ngIf="chatInfo">
|
||||
<ion-icon item-start name="time"></ion-icon> {{ 'addon.mod_chat.sessionstart' | translate:{$a: chatInfo} }}
|
||||
</ion-card>
|
||||
|
||||
<div padding-horizontal>
|
||||
<a ion-button block color="primary" (click)="enterChat()">{{ 'addon.mod_chat.enterchat' | translate }}</a>
|
||||
</div>
|
||||
</core-loading>
|
|
@ -0,0 +1,95 @@
|
|||
// (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 { Component, Injector } from '@angular/core';
|
||||
import { NavController } from 'ionic-angular';
|
||||
import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component';
|
||||
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||
import { AddonModChatProvider } from '../../providers/chat';
|
||||
import * as moment from 'moment';
|
||||
|
||||
/**
|
||||
* Component that displays a chat.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'addon-mod-chat-index',
|
||||
templateUrl: 'index.html',
|
||||
})
|
||||
export class AddonModChatIndexComponent extends CoreCourseModuleMainActivityComponent {
|
||||
component = AddonModChatProvider.COMPONENT;
|
||||
moduleName = 'chat';
|
||||
|
||||
chat: any;
|
||||
chatInfo: any;
|
||||
|
||||
protected title: string;
|
||||
|
||||
constructor(injector: Injector, private chatProvider: AddonModChatProvider, private timeUtils: CoreTimeUtilsProvider,
|
||||
private navCtrl: NavController) {
|
||||
super(injector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
|
||||
this.loadContent().then(() => {
|
||||
this.chatProvider.logView(this.chat.id).then(() => {
|
||||
this.courseProvider.checkModuleCompletion(this.courseId, this.module.completionstatus);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Download chat.
|
||||
*
|
||||
* @param {boolean} [refresh=false] If it's refreshing content.
|
||||
* @param {boolean} [sync=false] If the refresh is needs syncing.
|
||||
* @param {boolean} [showErrors=false] If show errors to the user of hide them.
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise<any> {
|
||||
return this.chatProvider.getChat(this.courseId, this.module.id).then((chat) => {
|
||||
this.chat = chat;
|
||||
this.description = chat.intro || chat.description;
|
||||
|
||||
const now = this.timeUtils.timestamp();
|
||||
const span = chat.chattime - now;
|
||||
|
||||
if (chat.chattime && chat.schedule > 0 && span > 0) {
|
||||
this.chatInfo = {
|
||||
date: moment(chat.chattime * 1000).format('LLL'),
|
||||
fromnow: this.timeUtils.formatTime(span)
|
||||
};
|
||||
} else {
|
||||
this.chatInfo = false;
|
||||
}
|
||||
|
||||
this.dataRetrieved.emit(chat);
|
||||
|
||||
// All data obtained, now fill the context menu.
|
||||
this.fillContextMenu(refresh);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Enter the chat.
|
||||
*/
|
||||
enterChat(): void {
|
||||
const title = this.chat.name || this.moduleName;
|
||||
this.navCtrl.push('AddonModChatChatPage', {chatId: this.chat.id, courseId: this.courseId, title: title });
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"beep": "Beep",
|
||||
"currentusers": "Current users",
|
||||
"enterchat": "Click here to enter the chat now",
|
||||
"entermessage": "Enter your message",
|
||||
"errorwhileconnecting": "Error while connecting to the chat.",
|
||||
"errorwhilegettingchatdata": "Error while getting chat data.",
|
||||
"errorwhilegettingchatusers": "Error while getting chat users.",
|
||||
"errorwhileretrievingmessages": "Error while retrieving messages from the server.",
|
||||
"errorwhilesendingmessage": "Error while sending the message.",
|
||||
"messagebeepsyou": "{{$a}} has just beeped you!",
|
||||
"messageenter": "{{$a}} has just entered this chat",
|
||||
"messageexit": "{{$a}} has left this chat",
|
||||
"mustbeonlinetosendmessages": "You must be online to send messages.",
|
||||
"nomessages": "No messages yet",
|
||||
"send": "Send",
|
||||
"sessionstart": "The next chat session will start on {{$a.date}}, ({{$a.fromnow}} from now)",
|
||||
"talk": "Talk"
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title><core-format-text [text]="title"></core-format-text></ion-title>
|
||||
<ion-buttons end>
|
||||
<button *ngIf="loaded" ion-button icon-only (click)="showChatUsers()">
|
||||
<ion-icon name="people"></ion-icon>
|
||||
</button>
|
||||
</ion-buttons>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<core-loading [hideUntil]="loaded">
|
||||
<div aria-live="polite">
|
||||
<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:"dfdayweekmonth" }}</span>
|
||||
</ion-badge>
|
||||
</div>
|
||||
|
||||
<div text-center *ngIf="message.system && message.message == 'enter'" class="addon-mod-chat-notice">
|
||||
<ion-badge text-wrap color="light">
|
||||
<span>{{ message.timestamp * 1000 | coreFormatDate:"dftimedate" }} {{ 'addon.mod_chat.messageenter' | translate:{$a: message.userfullname} }}</span>
|
||||
</ion-badge>
|
||||
</div>
|
||||
|
||||
<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:"dftimedate" }} {{ '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 item-start>
|
||||
<img [src]="message.userprofileimageurl" onError="this.src='assets/img/user-avatar.png'" core-external-content [alt]="'core.pictureof' | translate:{$a: message.userfullname}" role="presentation">
|
||||
</ion-avatar>
|
||||
<h2>
|
||||
<p float-right>{{ message.timestamp * 1000 | coreFormatDate:"dftimedate" }}</p>
|
||||
<core-format-text [text]="message.userfullname"></core-format-text>
|
||||
</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>{{ 'addon.mod_chat.nomessages' | translate}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</core-loading>
|
||||
</ion-content>
|
||||
<ion-footer color="light" class="footer-adjustable">
|
||||
<ion-toolbar color="light" position="bottom">
|
||||
<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>
|
||||
<button *ngIf="isOnline && !polling && loaded" (click)="reconnect()" ion-button block color="light">{{ 'core.login.reconnect' | translate }}</button>
|
||||
</ion-toolbar>
|
||||
</ion-footer>
|
|
@ -0,0 +1,35 @@
|
|||
// (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 { NgModule } from '@angular/core';
|
||||
import { IonicPageModule } from 'ionic-angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CoreComponentsModule } from '@components/components.module';
|
||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||
import { CorePipesModule } from '@pipes/pipes.module';
|
||||
import { AddonModChatChatPage } from './chat';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AddonModChatChatPage,
|
||||
],
|
||||
imports: [
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
CorePipesModule,
|
||||
IonicPageModule.forChild(AddonModChatChatPage),
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
})
|
||||
export class AddonModChatChatPageModule {}
|
|
@ -0,0 +1,9 @@
|
|||
page-addon-mod-chat-chat {
|
||||
.addon-mod-chat-notice {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.addon-mod-chat-message {
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,309 @@
|
|||
// (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 { Component, ViewChild } from '@angular/core';
|
||||
import { Content, IonicPage, ModalController, NavController, NavParams } from 'ionic-angular';
|
||||
import { CoreAppProvider } from '@providers/app';
|
||||
import { CoreLoggerProvider } from '@providers/logger';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
import { AddonModChatProvider } from '../../providers/chat';
|
||||
import { Network } from '@ionic-native/network';
|
||||
import * as moment from 'moment';
|
||||
|
||||
/**
|
||||
* Page that displays a chat session.
|
||||
*/
|
||||
@IonicPage({ segment: 'addon-mod-chat-chat' })
|
||||
@Component({
|
||||
selector: 'page-addon-mod-chat-chat',
|
||||
templateUrl: 'chat.html',
|
||||
})
|
||||
export class AddonModChatChatPage {
|
||||
@ViewChild(Content) content: Content;
|
||||
|
||||
loaded = false;
|
||||
title: string;
|
||||
messages = [];
|
||||
newMessage: string;
|
||||
polling: any;
|
||||
isOnline: boolean;
|
||||
currentUserBeep: string;
|
||||
|
||||
protected logger;
|
||||
protected courseId: number;
|
||||
protected chatId: number;
|
||||
protected sessionId: number;
|
||||
protected lastTime = 0;
|
||||
protected oldContentHeight = 0;
|
||||
protected onlineObserver: any;
|
||||
protected viewDestroyed = false;
|
||||
protected pollingRunning = false;
|
||||
|
||||
constructor(navParams: NavParams, logger: CoreLoggerProvider, network: Network, private navCtrl: NavController,
|
||||
private chatProvider: AddonModChatProvider, private appProvider: CoreAppProvider, sitesProvider: CoreSitesProvider,
|
||||
private modalCtrl: ModalController, private domUtils: CoreDomUtilsProvider, private textUtils: CoreTextUtilsProvider) {
|
||||
|
||||
this.chatId = navParams.get('chatId');
|
||||
this.courseId = navParams.get('courseId');
|
||||
this.title = navParams.get('title');
|
||||
this.logger = logger.getInstance('AddonModChoiceChoicePage');
|
||||
this.currentUserBeep = 'beep ' + sitesProvider.getCurrentSiteUserId();
|
||||
this.isOnline = this.appProvider.isOnline();
|
||||
this.onlineObserver = network.onchange().subscribe((online) => {
|
||||
this.isOnline = this.appProvider.isOnline();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* View loaded.
|
||||
*/
|
||||
ionViewDidLoad(): void {
|
||||
this.loginUser().then(() => {
|
||||
return this.fetchMessages().then(() => {
|
||||
this.startPolling();
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'addon.mod_chat.errorwhileretrievingmessages', true);
|
||||
this.navCtrl.pop();
|
||||
});
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'addon.mod_chat.errorwhileconnecting', true);
|
||||
this.navCtrl.pop();
|
||||
}).finally(() => {
|
||||
this.loaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs when the page has fully entered and is now the active page.
|
||||
* This event will fire, whether it was the first load or a cached page.
|
||||
*/
|
||||
ionViewDidEnter(): void {
|
||||
this.startPolling();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs when the page is about to leave and no longer be the active page.
|
||||
*/
|
||||
ionViewWillLeave(): void {
|
||||
this.stopPolling();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the chat users modal.
|
||||
*/
|
||||
showChatUsers(): void {
|
||||
const modal = this.modalCtrl.create('AddonModChatUsersPage', {sessionId: this.sessionId});
|
||||
modal.onDidDismiss((data) => {
|
||||
if (data && data.talkTo) {
|
||||
this.newMessage = `To ${data.talkTo}: `;
|
||||
}
|
||||
if (data && data.beepTo) {
|
||||
this.sendMessage('', data.beepTo);
|
||||
}
|
||||
});
|
||||
modal.present();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to login the user.
|
||||
*
|
||||
* @return {Promise<any>} Resolved when done.
|
||||
*/
|
||||
protected loginUser(): Promise<any> {
|
||||
return this.chatProvider.loginUser(this.chatId).then((sessionId) => {
|
||||
this.sessionId = sessionId;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to fetch chat messages.
|
||||
*
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
protected fetchMessages(): Promise<any> {
|
||||
return this.chatProvider.getLatestMessages(this.sessionId, this.lastTime).then((messagesInfo) => {
|
||||
this.lastTime = messagesInfo.chatnewlasttime || 0;
|
||||
|
||||
return this.chatProvider.getMessagesUserData(messagesInfo.messages, this.courseId).then((messages) => {
|
||||
this.messages = this.messages.concat(messages);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the polling to get chat messages periodically.
|
||||
*/
|
||||
protected startPolling(): void {
|
||||
// We already have the polling in place.
|
||||
if (this.polling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Start polling.
|
||||
this.polling = setInterval(() => {
|
||||
this.fetchMessagesInterval().catch(() => {
|
||||
// Ignore errors.
|
||||
});
|
||||
}, AddonModChatProvider.POLL_INTERVAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop polling for messages.
|
||||
*/
|
||||
protected stopPolling(): void {
|
||||
if (this.polling) {
|
||||
this.logger.debug('Cancelling polling for messages');
|
||||
clearInterval(this.polling);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to be called every certain time to fetch chat messages.
|
||||
*
|
||||
* @return {Promise<any>} Promised resolved when done.
|
||||
*/
|
||||
protected fetchMessagesInterval(): Promise<any> {
|
||||
this.logger.debug('Polling for messages');
|
||||
if (!this.isOnline || this.pollingRunning) {
|
||||
// Obviously we cannot check for new messages when the app is offline.
|
||||
return Promise.reject(null);
|
||||
}
|
||||
|
||||
this.pollingRunning = true;
|
||||
|
||||
return this.fetchMessages().catch(() => {
|
||||
// Try to login, it might have failed because the session expired.
|
||||
return this.loginUser().then(() => {
|
||||
return this.fetchMessages();
|
||||
}).catch((error) => {
|
||||
// Fail again. Stop polling if needed.
|
||||
if (this.polling) {
|
||||
clearInterval(this.polling);
|
||||
this.polling = undefined;
|
||||
}
|
||||
this.domUtils.showErrorModalDefault(error, 'addon.mod_chat.errorwhileretrievingmessages', true);
|
||||
|
||||
return Promise.reject(null);
|
||||
});
|
||||
}).finally(() => {
|
||||
this.pollingRunning = false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the date should be displayed between messages (when the day changes at midnight for example).
|
||||
*
|
||||
* @param {any} message New message object.
|
||||
* @param {any} prevMessage Previous message object.
|
||||
* @return {boolean} True if messages are from diferent days, false othetwise.
|
||||
*/
|
||||
showDate(message: any, prevMessage: any): 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.
|
||||
*/
|
||||
sendMessage(text: string, beep: number = 0): void {
|
||||
if (!this.isOnline) {
|
||||
// Silent error, the view should prevent this.
|
||||
return;
|
||||
} else if (beep === 0 && !text.trim()) {
|
||||
// Silent error.
|
||||
return;
|
||||
}
|
||||
text = this.textUtils.replaceNewLines(text, '<br>');
|
||||
|
||||
const modal = this.domUtils.showModalLoading('core.sending', true);
|
||||
this.chatProvider.sendMessage(this.sessionId, text, beep).then(() => {
|
||||
// Update messages to show the sent message.
|
||||
this.fetchMessagesInterval().catch(() => {
|
||||
// Ignore errors.
|
||||
});
|
||||
}).catch((error) => {
|
||||
/* Only close the keyboard if an error happens, we want the user to be able to send multiple
|
||||
messages without the keyboard being closed. */
|
||||
this.appProvider.closeKeyboard();
|
||||
|
||||
this.domUtils.showErrorModalDefault(error, 'addon.mod_chat.errorwhilesendingmessage', true);
|
||||
}).finally(() => {
|
||||
modal.dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
reconnect(): Promise<any> {
|
||||
const modal = this.domUtils.showModalLoading();
|
||||
|
||||
// Call startPolling would take a while for the first execution, so we'll execute it manually to check if it works now.
|
||||
return this.fetchMessagesInterval().then(() => {
|
||||
// It works, start the polling again.
|
||||
this.startPolling();
|
||||
}).catch(() => {
|
||||
// Ignore errors.
|
||||
}).finally(() => {
|
||||
modal.dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll bottom when render has finished.
|
||||
*/
|
||||
scrollToBottom(): void {
|
||||
// Need a timeout to leave time to the view to be rendered.
|
||||
setTimeout(() => {
|
||||
if (!this.viewDestroyed) {
|
||||
this.content.scrollToBottom(0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Content or scroll has been resized. For content, only call it if it's been added on top.
|
||||
*/
|
||||
resizeContent(): void {
|
||||
let top = this.content.getContentDimensions().scrollTop;
|
||||
this.content.resize();
|
||||
|
||||
// Wait for new content height to be calculated.
|
||||
setTimeout(() => {
|
||||
// Visible content size changed, maintain the bottom position.
|
||||
if (!this.viewDestroyed && this.content && this.content.contentHeight != this.oldContentHeight) {
|
||||
if (!top) {
|
||||
top = this.content.getContentDimensions().scrollTop;
|
||||
}
|
||||
|
||||
top += this.oldContentHeight - this.content.contentHeight;
|
||||
this.oldContentHeight = this.content.contentHeight;
|
||||
|
||||
this.content.scrollTo(0, top, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Page destroyed.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.onlineObserver && this.onlineObserver.unsubscribe();
|
||||
this.stopPolling();
|
||||
this.viewDestroyed = true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title><core-format-text [text]="title"></core-format-text></ion-title>
|
||||
|
||||
<ion-buttons end>
|
||||
<!-- The buttons defined by the component will be added in here. -->
|
||||
</ion-buttons>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher [enabled]="chatComponent.loaded" (ionRefresh)="chatComponent.doRefresh($event)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
<addon-mod-chat-index [module]="module" [courseId]="courseId" (dataRetrieved)="updateData($event)"></addon-mod-chat-index>
|
||||
</ion-content>
|
|
@ -0,0 +1,33 @@
|
|||
// (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 { NgModule } from '@angular/core';
|
||||
import { IonicPageModule } from 'ionic-angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||
import { AddonModChatComponentsModule } from '../../components/components.module';
|
||||
import { AddonModChatIndexPage } from './index';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AddonModChatIndexPage,
|
||||
],
|
||||
imports: [
|
||||
CoreDirectivesModule,
|
||||
AddonModChatComponentsModule,
|
||||
IonicPageModule.forChild(AddonModChatIndexPage),
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
})
|
||||
export class AddonModChatIndexPageModule {}
|
|
@ -0,0 +1,48 @@
|
|||
// (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 { Component, ViewChild } from '@angular/core';
|
||||
import { IonicPage, NavParams } from 'ionic-angular';
|
||||
import { AddonModChatIndexComponent } from '../../components/index/index';
|
||||
|
||||
/**
|
||||
* Page that displays a chat.
|
||||
*/
|
||||
@IonicPage({ segment: 'addon-mod-chat-index' })
|
||||
@Component({
|
||||
selector: 'page-addon-mod-chat-index',
|
||||
templateUrl: 'index.html',
|
||||
})
|
||||
export class AddonModChatIndexPage {
|
||||
@ViewChild(AddonModChatIndexComponent) chatComponent: AddonModChatIndexComponent;
|
||||
|
||||
title: string;
|
||||
module: any;
|
||||
courseId: number;
|
||||
|
||||
constructor(navParams: NavParams) {
|
||||
this.module = navParams.get('module') || {};
|
||||
this.courseId = navParams.get('courseId');
|
||||
this.title = this.module.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update some data based on the chat instance.
|
||||
*
|
||||
* @param {any} chat Chat instance.
|
||||
*/
|
||||
updateData(chat: any): void {
|
||||
this.title = chat.name || this.title;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title>{{ 'addon.mod_chat.currentusers' | translate }}</ion-title>
|
||||
<ion-buttons end>
|
||||
<button ion-button icon-only (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
|
||||
<ion-icon name="close"></ion-icon>
|
||||
</button>
|
||||
</ion-buttons>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<core-loading [hideUntil]="usersLoaded">
|
||||
<ion-item text-wrap *ngFor="let user of users" [class.addon-mod-chat-user]="currentUserId != user.id && isOnline">
|
||||
<ion-avatar item-start>
|
||||
<img [src]="user.profileimageurl" onError="this.src='assets/img/user-avatar.png'" core-external-content [alt]="'core.pictureof' | translate:{$a: user.fullname}" role="presentation">
|
||||
</ion-avatar>
|
||||
<h2><core-format-text [text]="user.fullname"></core-format-text></h2>
|
||||
<ng-container *ngIf="currentUserId != user.id && isOnline">
|
||||
<button ion-button clear icon-left (click)="talkTo(user)">
|
||||
<ion-icon name="chatboxes"></ion-icon>
|
||||
{{ 'addon.mod_chat.talk' | translate }}
|
||||
</button>
|
||||
<button ion-button clear icon-left (click)="beepTo(user)">
|
||||
<ion-icon name="notifications"></ion-icon>
|
||||
{{ 'addon.mod_chat.beep' | translate }}
|
||||
</button>
|
||||
</ng-container>
|
||||
</ion-item>
|
||||
</core-loading>
|
||||
</ion-content>
|
|
@ -0,0 +1,35 @@
|
|||
// (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 { NgModule } from '@angular/core';
|
||||
import { IonicPageModule } from 'ionic-angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CoreComponentsModule } from '@components/components.module';
|
||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||
import { CorePipesModule } from '@pipes/pipes.module';
|
||||
import { AddonModChatUsersPage } from './users';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AddonModChatUsersPage,
|
||||
],
|
||||
imports: [
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
CorePipesModule,
|
||||
IonicPageModule.forChild(AddonModChatUsersPage),
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
})
|
||||
export class AddonModChatUsersPageModule {}
|
|
@ -0,0 +1,5 @@
|
|||
page-addon-mod-chat-users {
|
||||
.addon-mod-chat-user ion-label {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
// (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 { Component } from '@angular/core';
|
||||
import { IonicPage, NavParams, ViewController } from 'ionic-angular';
|
||||
import { CoreAppProvider } from '@providers/app';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { AddonModChatProvider } from '../../providers/chat';
|
||||
import { Network } from '@ionic-native/network';
|
||||
|
||||
/**
|
||||
* Page that displays the chat session users.
|
||||
*/
|
||||
@IonicPage({ segment: 'addon-mod-chat-users' })
|
||||
@Component({
|
||||
selector: 'page-addon-mod-chat-users',
|
||||
templateUrl: 'users.html',
|
||||
})
|
||||
export class AddonModChatUsersPage {
|
||||
|
||||
users = [];
|
||||
usersLoaded = false;
|
||||
currentUserId: number;
|
||||
isOnline: boolean;
|
||||
|
||||
protected sessionId: number;
|
||||
protected onlineObserver: any;
|
||||
|
||||
constructor(navParams: NavParams, network: Network, private appProvider: CoreAppProvider,
|
||||
private sitesProvider: CoreSitesProvider, private viewCtrl: ViewController,
|
||||
private domUtils: CoreDomUtilsProvider, private chatProvider: AddonModChatProvider) {
|
||||
this.sessionId = navParams.get('sessionId');
|
||||
this.isOnline = this.appProvider.isOnline();
|
||||
this.currentUserId = this.sitesProvider.getCurrentSiteUserId();
|
||||
this.onlineObserver = network.onchange().subscribe((online) => {
|
||||
this.isOnline = this.appProvider.isOnline();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* View loaded.
|
||||
*/
|
||||
ionViewDidLoad(): void {
|
||||
this.chatProvider.getChatUsers(this.sessionId).then((data) => {
|
||||
this.users = data.users;
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'addon.mod_chat.errorwhilegettingchatusers', true);
|
||||
}).finally(() => {
|
||||
this.usersLoaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the chat users modal.
|
||||
*/
|
||||
closeModal(): void {
|
||||
this.viewCtrl.dismiss();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add "To user:".
|
||||
*
|
||||
* @param {any} user User object.
|
||||
*/
|
||||
talkTo(user: any): void {
|
||||
this.viewCtrl.dismiss({talkTo: user.fullname});
|
||||
}
|
||||
|
||||
/**
|
||||
* Beep a user.
|
||||
*
|
||||
* @param {any} user User object.
|
||||
*/
|
||||
beepTo(user: any): void {
|
||||
this.viewCtrl.dismiss({beepTo: user.id});
|
||||
}
|
||||
|
||||
/**
|
||||
* Page destroyed.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.onlineObserver && this.onlineObserver.unsubscribe();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
// (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 { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreUserProvider } from '@core/user/providers/user';
|
||||
|
||||
/**
|
||||
* Service that provides some features for chats.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonModChatProvider {
|
||||
static COMPONENT = 'mmaModChat';
|
||||
static POLL_INTERVAL = 4000;
|
||||
|
||||
constructor(private sitesProvider: CoreSitesProvider, private userProvider: CoreUserProvider) {}
|
||||
|
||||
/**
|
||||
* Get a chat.
|
||||
*
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {number} cmId Course module ID.
|
||||
* @param {boolean} [refresh=false] True when we should not get the value from the cache.
|
||||
* @return {Promise<any>} Promise resolved when the chat is retrieved.
|
||||
*/
|
||||
getChat(courseId: number, cmId: number, refresh: boolean = false): Promise<any> {
|
||||
const params = {
|
||||
courseids: [courseId]
|
||||
};
|
||||
const preSets = {
|
||||
getFromCache: refresh ? false : undefined,
|
||||
};
|
||||
|
||||
return this.sitesProvider.getCurrentSite().read('mod_chat_get_chats_by_courses', params, preSets).then((response) => {
|
||||
if (response.chats) {
|
||||
const chat = response.chats.find((chat) => chat.coursemodule == cmId);
|
||||
if (chat) {
|
||||
return chat;
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.reject(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the user into a chat room.
|
||||
*
|
||||
* @param {number} chatId Chat instance ID.
|
||||
* @return {Promise<any>} Promise resolved when the WS is executed.
|
||||
*/
|
||||
loginUser(chatId: number): Promise<any> {
|
||||
const params = {
|
||||
chatid: chatId
|
||||
};
|
||||
|
||||
return this.sitesProvider.getCurrentSite().write('mod_chat_login_user', params).then((response) => {
|
||||
if (response.chatsid) {
|
||||
return response.chatsid;
|
||||
}
|
||||
|
||||
return Promise.reject(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Report a chat as being viewed.
|
||||
*
|
||||
* @param {number} chatId Chat instance ID.
|
||||
* @return {Promise<any>} Promise resolved when the WS call is executed.
|
||||
*/
|
||||
logView(chatId: number): Promise<any> {
|
||||
const params = {
|
||||
chatid: chatId
|
||||
};
|
||||
|
||||
return this.sitesProvider.getCurrentSite().write('mod_chat_view_chat', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to a chat.
|
||||
*
|
||||
* @param {number} sessionId Chat sessiond ID.
|
||||
* @param {string} message Message text.
|
||||
* @param {number} beepUserId Beep user ID.
|
||||
* @return {Promise<any>} Promise resolved when the WS is executed.
|
||||
*/
|
||||
sendMessage(sessionId: number, message: string, beepUserId: number): Promise<any> {
|
||||
const params = {
|
||||
chatsid: sessionId,
|
||||
messagetext: message,
|
||||
beepid: beepUserId
|
||||
};
|
||||
|
||||
return this.sitesProvider.getCurrentSite().write('mod_chat_send_chat_message', params).then((response) => {
|
||||
if (response.messageid) {
|
||||
return response.messageid;
|
||||
}
|
||||
|
||||
return Promise.reject(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest messages from a chat session.
|
||||
*
|
||||
* @param {number} sessionId Chat sessiond ID.
|
||||
* @param {number} lastTime Last time when messages were retrieved.
|
||||
* @return {Promise<any>} Promise resolved when the WS is executed.
|
||||
*/
|
||||
getLatestMessages(sessionId: number, lastTime: number): Promise<any> {
|
||||
const params = {
|
||||
chatsid: sessionId,
|
||||
chatlasttime: lastTime
|
||||
};
|
||||
|
||||
/* We use write to not use cache. It doesn't make sense to store the messages in cache
|
||||
because we won't be able to retireve them if AddonModChatProvider.loginUser fails. */
|
||||
return this.sitesProvider.getCurrentSite().write('mod_chat_get_chat_latest_messages', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user data for messages since they only have userid.
|
||||
*
|
||||
* @param {any[]} messages Messages to get the user data for.
|
||||
* @param {number} courseId ID of the course the messages belong to.
|
||||
* @return {Promise<any>} Promise always resolved with the formatted messages.
|
||||
*/
|
||||
getMessagesUserData(messages: any[], courseId: number): Promise<any> {
|
||||
const promises = messages.map((message) => {
|
||||
return this.userProvider.getProfile(message.userid, courseId, true).then((user) => {
|
||||
message.userfullname = user.fullname;
|
||||
message.userprofileimageurl = user.profileimageurl;
|
||||
}).catch(() => {
|
||||
// Error getting profile. Set default data.
|
||||
message.userfullname = message.userid;
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.all(promises).then(() => {
|
||||
return messages;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actives users of a current chat.
|
||||
*
|
||||
* @param {number} sessionId Chat sessiond ID.
|
||||
* @return {Promise<any>} Promise resolved when the WS is executed.
|
||||
*/
|
||||
getChatUsers(sessionId: number): Promise<any> {
|
||||
const params = {
|
||||
chatsid: sessionId
|
||||
};
|
||||
const preSets = {
|
||||
getFromCache: false
|
||||
};
|
||||
|
||||
return this.sitesProvider.getCurrentSite().read('mod_chat_get_chat_users', params, preSets);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
// (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 { NavController, NavOptions } from 'ionic-angular';
|
||||
import { AddonModChatIndexComponent } from '../components/index/index';
|
||||
import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate';
|
||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||
|
||||
/**
|
||||
* Handler to support chat modules.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonModChatModuleHandler implements CoreCourseModuleHandler {
|
||||
name = 'AddonModChat';
|
||||
modName = 'chat';
|
||||
|
||||
constructor(private courseProvider: CoreCourseProvider) { }
|
||||
|
||||
/**
|
||||
* Check if the handler is enabled on a site level.
|
||||
*
|
||||
* @return {boolean} Whether or not the handler is enabled on a site level.
|
||||
*/
|
||||
isEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data required to display the module in the course contents view.
|
||||
*
|
||||
* @param {any} module The module object.
|
||||
* @param {number} courseId The course ID.
|
||||
* @param {number} sectionId The section ID.
|
||||
* @return {CoreCourseModuleHandlerData} Data to render the module.
|
||||
*/
|
||||
getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData {
|
||||
return {
|
||||
icon: this.courseProvider.getModuleIconSrc('chat'),
|
||||
title: module.name,
|
||||
class: 'addon-mod_chat-handler',
|
||||
action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions): void {
|
||||
navCtrl.push('AddonModChatIndexPage', {module: module, courseId: courseId}, options);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the component to render the module. This is needed to support singleactivity course format.
|
||||
* The component returned must implement CoreCourseModuleMainComponent.
|
||||
*
|
||||
* @param {any} course The course object.
|
||||
* @param {any} module The module object.
|
||||
* @return {any} The component to use, undefined if not found.
|
||||
*/
|
||||
getMainComponent(course: any, module: any): any {
|
||||
return AddonModChatIndexComponent;
|
||||
}
|
||||
}
|
|
@ -77,6 +77,7 @@ import { AddonCompetencyModule } from '@addon/competency/competency.module';
|
|||
import { AddonUserProfileFieldModule } from '@addon/userprofilefield/userprofilefield.module';
|
||||
import { AddonFilesModule } from '@addon/files/files.module';
|
||||
import { AddonModBookModule } from '@addon/mod/book/book.module';
|
||||
import { AddonModChatModule } from '@addon/mod/chat/chat.module';
|
||||
import { AddonModLabelModule } from '@addon/mod/label/label.module';
|
||||
import { AddonModResourceModule } from '@addon/mod/resource/resource.module';
|
||||
import { AddonModFolderModule } from '@addon/mod/folder/folder.module';
|
||||
|
@ -170,6 +171,7 @@ export const CORE_PROVIDERS: any[] = [
|
|||
AddonUserProfileFieldModule,
|
||||
AddonFilesModule,
|
||||
AddonModBookModule,
|
||||
AddonModChatModule,
|
||||
AddonModLabelModule,
|
||||
AddonModResourceModule,
|
||||
AddonModFolderModule,
|
||||
|
|
|
@ -31,13 +31,12 @@ import { CoreTextUtilsProvider } from '@providers/utils/text';
|
|||
templateUrl: 'send-message-form.html'
|
||||
})
|
||||
export class CoreSendMessageFormComponent implements OnInit {
|
||||
@Input() message: string; // Input text.
|
||||
@Input() placeholder = ''; // Placeholder for the input area.
|
||||
@Input() showKeyboard = false; // If keyboard is shown or not.
|
||||
@Output() onSubmit: EventEmitter<string>; // Send data when submitting the message form.
|
||||
@Output() onResize: EventEmitter<void>; // Emit when resizing the textarea.
|
||||
|
||||
message: string;
|
||||
|
||||
constructor(private utils: CoreUtilsProvider, private textUtils: CoreTextUtilsProvider) {
|
||||
this.onSubmit = new EventEmitter();
|
||||
this.onResize = new EventEmitter();
|
||||
|
|
Loading…
Reference in New Issue