commit
ecf835e1eb
41
src/addon/mod/chat/chat.module.ts
Normal file
41
src/addon/mod/chat/chat.module.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// (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 { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate';
|
||||||
|
import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate';
|
||||||
|
import { AddonModChatComponentsModule } from './components/components.module';
|
||||||
|
import { AddonModChatProvider } from './providers/chat';
|
||||||
|
import { AddonModChatLinkHandler } from './providers/link-handler';
|
||||||
|
import { AddonModChatModuleHandler } from './providers/module-handler';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
AddonModChatComponentsModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
AddonModChatProvider,
|
||||||
|
AddonModChatLinkHandler,
|
||||||
|
AddonModChatModuleHandler,
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class AddonModChatModule {
|
||||||
|
constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModChatModuleHandler,
|
||||||
|
contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModChatLinkHandler) {
|
||||||
|
moduleDelegate.registerHandler(moduleHandler);
|
||||||
|
contentLinksDelegate.registerHandler(linkHandler);
|
||||||
|
}
|
||||||
|
}
|
45
src/addon/mod/chat/components/components.module.ts
Normal file
45
src/addon/mod/chat/components/components.module.ts
Normal file
@ -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 {}
|
22
src/addon/mod/chat/components/index/index.html
Normal file
22
src/addon/mod/chat/components/index/index.html
Normal file
@ -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 icon-start class="core-info-card" *ngIf="chatInfo">
|
||||||
|
<ion-icon 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>
|
95
src/addon/mod/chat/components/index/index.ts
Normal file
95
src/addon/mod/chat/components/index/index.ts
Normal file
@ -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 });
|
||||||
|
}
|
||||||
|
}
|
19
src/addon/mod/chat/lang/en.json
Normal file
19
src/addon/mod/chat/lang/en.json
Normal file
@ -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"
|
||||||
|
}
|
64
src/addon/mod/chat/pages/chat/chat.html
Normal file
64
src/addon/mod/chat/pages/chat/chat.html
Normal file
@ -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>
|
35
src/addon/mod/chat/pages/chat/chat.module.ts
Normal file
35
src/addon/mod/chat/pages/chat/chat.module.ts
Normal file
@ -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 {}
|
9
src/addon/mod/chat/pages/chat/chat.scss
Normal file
9
src/addon/mod/chat/pages/chat/chat.scss
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
312
src/addon/mod/chat/pages/chat/chat.ts
Normal file
312
src/addon/mod/chat/pages/chat/chat.ts
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
// (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.
|
||||||
|
*
|
||||||
|
* @param {string} text Text of the nessage.
|
||||||
|
* @param {number} [beep=0] ID of the user to beep.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
16
src/addon/mod/chat/pages/index/index.html
Normal file
16
src/addon/mod/chat/pages/index/index.html
Normal file
@ -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>
|
33
src/addon/mod/chat/pages/index/index.module.ts
Normal file
33
src/addon/mod/chat/pages/index/index.module.ts
Normal file
@ -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 {}
|
48
src/addon/mod/chat/pages/index/index.ts
Normal file
48
src/addon/mod/chat/pages/index/index.ts
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
30
src/addon/mod/chat/pages/users/users.html
Normal file
30
src/addon/mod/chat/pages/users/users.html
Normal file
@ -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>
|
35
src/addon/mod/chat/pages/users/users.module.ts
Normal file
35
src/addon/mod/chat/pages/users/users.module.ts
Normal file
@ -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 {}
|
5
src/addon/mod/chat/pages/users/users.scss
Normal file
5
src/addon/mod/chat/pages/users/users.scss
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
page-addon-mod-chat-users {
|
||||||
|
.addon-mod-chat-user ion-label {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
96
src/addon/mod/chat/pages/users/users.ts
Normal file
96
src/addon/mod/chat/pages/users/users.ts
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
172
src/addon/mod/chat/providers/chat.ts
Normal file
172
src/addon/mod/chat/providers/chat.ts
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
29
src/addon/mod/chat/providers/link-handler.ts
Normal file
29
src/addon/mod/chat/providers/link-handler.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// (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 { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler';
|
||||||
|
import { CoreCourseHelperProvider } from '@core/course/providers/helper';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to treat links to chat.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class AddonModChatLinkHandler extends CoreContentLinksModuleIndexHandler {
|
||||||
|
name = 'AddonModChatLinkHandler';
|
||||||
|
|
||||||
|
constructor(courseHelper: CoreCourseHelperProvider) {
|
||||||
|
super(courseHelper, 'AddonModChat', 'chat');
|
||||||
|
}
|
||||||
|
}
|
70
src/addon/mod/chat/providers/module-handler.ts
Normal file
70
src/addon/mod/chat/providers/module-handler.ts
Normal file
@ -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 { AddonUserProfileFieldModule } from '@addon/userprofilefield/userprofilefield.module';
|
||||||
import { AddonFilesModule } from '@addon/files/files.module';
|
import { AddonFilesModule } from '@addon/files/files.module';
|
||||||
import { AddonModBookModule } from '@addon/mod/book/book.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 { AddonModLabelModule } from '@addon/mod/label/label.module';
|
||||||
import { AddonModResourceModule } from '@addon/mod/resource/resource.module';
|
import { AddonModResourceModule } from '@addon/mod/resource/resource.module';
|
||||||
import { AddonModFeedbackModule } from '@addon/mod/feedback/feedback.module';
|
import { AddonModFeedbackModule } from '@addon/mod/feedback/feedback.module';
|
||||||
@ -172,6 +173,7 @@ export const CORE_PROVIDERS: any[] = [
|
|||||||
AddonUserProfileFieldModule,
|
AddonUserProfileFieldModule,
|
||||||
AddonFilesModule,
|
AddonFilesModule,
|
||||||
AddonModBookModule,
|
AddonModBookModule,
|
||||||
|
AddonModChatModule,
|
||||||
AddonModLabelModule,
|
AddonModLabelModule,
|
||||||
AddonModResourceModule,
|
AddonModResourceModule,
|
||||||
AddonModFeedbackModule,
|
AddonModFeedbackModule,
|
||||||
|
@ -31,13 +31,12 @@ import { CoreTextUtilsProvider } from '@providers/utils/text';
|
|||||||
templateUrl: 'send-message-form.html'
|
templateUrl: 'send-message-form.html'
|
||||||
})
|
})
|
||||||
export class CoreSendMessageFormComponent implements OnInit {
|
export class CoreSendMessageFormComponent implements OnInit {
|
||||||
|
@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.
|
||||||
@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.
|
||||||
|
|
||||||
message: string;
|
|
||||||
|
|
||||||
constructor(private utils: CoreUtilsProvider, private textUtils: CoreTextUtilsProvider) {
|
constructor(private utils: CoreUtilsProvider, private textUtils: CoreTextUtilsProvider) {
|
||||||
this.onSubmit = new EventEmitter();
|
this.onSubmit = new EventEmitter();
|
||||||
this.onResize = new EventEmitter();
|
this.onResize = new EventEmitter();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user