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