UNPKG

angular-chat-widget-rasa

Version:

A chatbot widget that is able to connect to a rasa chatbot using SocketIO

472 lines (461 loc) 17.8 kB
import { CommonModule } from '@angular/common'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { Subject } from 'rxjs'; import { animate, style, transition, trigger } from '@angular/animations'; import io from 'socket.io-client'; import { Observable } from 'rxjs/Observable'; import { Component, Input, Injectable, EventEmitter, Output, ViewChild, ViewEncapsulation, HostListener, NgModule, defineInjectable } from '@angular/core'; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class ChatAvatarComponent { } ChatAvatarComponent.decorators = [ { type: Component, args: [{ selector: 'chat-avatar', template: ` <img [attr.src]="image" class="avatar" /> `, styles: [` .avatar { height: 30px; width: 30px; border-radius: 50%; float: left; margin-right: 10px; } `] }] } ]; ChatAvatarComponent.propDecorators = { image: [{ type: Input }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ const fadeInOut = trigger('fadeInOut', [ transition(':enter', [ style({ opacity: 0 }), animate(250) ]), transition('* => void', [ animate(250, style({ opacity: 0, })) ]) ]); /** @type {?} */ const fadeIn = trigger('fadeIn', [ transition(':enter', [ style({ opacity: 0 }), animate(500) ]), transition(':leave', [ style({ opacity: 0 }), animate(1) ]), ]); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class ChatService { constructor() { this.getMessages = (/** * @return {?} */ () => { return Observable.create((/** * @param {?} observer * @return {?} */ (observer) => { this.socket.on('bot_uttered', (/** * @param {?} message * @return {?} */ (message) => { observer.next(message); })); })); }); } /** * @param {?} url * @return {?} */ connect(url) { this.socket = io(url); this.socket.on('connect', (/** * @return {?} */ () => { this.socket.emit('session_request', { 'session_id': this.socket.id }); })); this.socket.on('session_confirm', (/** * @param {?} remote_id * @return {?} */ (remote_id) => { })); this.socket.on('connect_error', (/** * @param {?} error * @return {?} */ (error) => { console.log(error); })); this.socket.on('error', (/** * @param {?} error * @return {?} */ (error) => { console.log(error); })); this.socket.on('disconnect', (/** * @param {?} reason * @return {?} */ (reason) => { console.log(reason); })); } /** * @param {?} message * @return {?} */ sendMessage(message) { this.socket.emit('user_uttered', { 'message': message }); } } ChatService.decorators = [ { type: Injectable, args: [{ providedIn: 'root' },] } ]; /** @nocollapse */ ChatService.ctorParameters = () => []; /** @nocollapse */ ChatService.ngInjectableDef = defineInjectable({ factory: function ChatService_Factory() { return new ChatService(); }, token: ChatService, providedIn: "root" }); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class ChatWidgetComponent { /** * @param {?} chatService */ constructor(chatService) { this.theme = 'blue'; this.botName = 'Bot'; this.botAvatar = `https://cdn.dribbble.com/users/275794/screenshots/3128598/gbot_800.png`; this.userAvatar = `https://storage.proboards.com/6172192/images/gKhXFw_5W0SD4nwuMev1.png`; this.url = 'http://localhost:5002'; this.startingMessage = 'Hi, how can we help you?'; this.opened = true; this._visible = false; this.focus = new Subject(); this.messages = []; this.chatService = chatService; this.chatService.connect(this.url); } /** * @return {?} */ get visible() { return this._visible; } /** * @param {?} visible * @return {?} */ set visible(visible) { this._visible = visible; if (this._visible) { setTimeout((/** * @return {?} */ () => { this.scrollToBottom(); this.focusMessage(); }), 0); } } /** * @param {?} from * @param {?} text * @param {?} type * @return {?} */ addMessage(from, text, type) { this.messages.unshift({ from, text, type, date: new Date().getTime(), }); this.scrollToBottom(); } /** * @return {?} */ scrollToBottom() { if (this.bottom !== undefined) { this.bottom.nativeElement.scrollIntoView(); } } /** * @return {?} */ focusMessage() { this.focus.next(true); } /** * @return {?} */ ngOnInit() { this.client = { name: 'Guest User', status: 'online', avatar: this.userAvatar, }; this.operator = { name: this.botName, status: 'online', avatar: this.botAvatar, }; if (this.opened) { setTimeout((/** * @return {?} */ () => this.visible = true), 1000); } setTimeout((/** * @return {?} */ () => { this.addMessage(this.operator, this.startingMessage, 'received'); }), 1500); this.chatService .getMessages() .subscribe((/** * @param {?} message * @return {?} */ (message) => { setTimeout((/** * @return {?} */ () => { this.addMessage(this.operator, message.text, 'received'); }), 1000); })); } /** * @return {?} */ toggleChat() { this.visible = !this.visible; } /** * @param {?} __0 * @return {?} */ sendMessage({ message }) { if (message.trim() === '') { return; } this.addMessage(this.client, message, 'sent'); this.chatService.sendMessage(message); } /** * @param {?} event * @return {?} */ handleKeyboardEvent(event) { if (event.key === '/') { this.focusMessage(); } if (event.key === '?' && !this._visible) { this.toggleChat(); } } } ChatWidgetComponent.decorators = [ { type: Component, args: [{ selector: 'chat-widget', template: "<div class=\"wrapper {{theme}}\">\n <div class=\"chat-box\" *ngIf=\"visible\" [@fadeInOut]=\"visible\">\n <div class=\"chat-box-header\">\n <div class=\"\">\n <div class=\"operator-status\">\n {{operator.status}}\n <span class=\"operator-status-online\">\u25CF</span>\n <button class=\"chat-button-header\" (click)=\"toggleChat()\">\u2715</button>\n </div>\n <chat-avatar [image]=\"operator.avatar\"></chat-avatar>\n <h3 class=\"operator-name\">\n {{operator.name}}\n </h3>\n </div>\n </div>\n <div class=\"chat-box-main\">\n <div class=\"chat-message-bottom\" #bottom></div>\n <ng-container *ngFor=\"let message of messages\">\n <div class=\"chat-message\" [class.chat-message-received]=\"message.type === 'received'\"\n [@fadeIn]\n [class.chat-message-sent]=\"message.type === 'sent'\">\n <div >\n <chat-avatar [image]=\"message.from.avatar\" class=\"chat-message-from-avatar\"></chat-avatar>\n <div class=\"chat-message-text\">\n {{message.text}}\n </div>\n </div>\n <div class=\"chat-message-date\">\n {{message.date | date: 'short'}}\n </div>\n </div>\n </ng-container>\n </div>\n <div class=\"chat-box-footer\">\n <chat-input (send)=\"sendMessage($event)\" (dismiss)=\"toggleChat()\" [focus]=\"focus\"></chat-input>\n </div>\n </div>\n <button class=\"chat-button\" (click)=\"toggleChat()\">\n <span [@fadeIn] *ngIf=\"visible\">\u2715</span>\n <span [@fadeIn] *ngIf=\"!visible\">?</span>\n </button>\n</div>\n", animations: [fadeInOut, fadeIn], styles: ["*{box-sizing:border-box;position:relative;font-family:\"Open Sans\",\"Helvetica Neue\",sans-serif}.wrapper{position:absolute;top:0;bottom:0;height:100vh;width:100vw}.chat-button{z-index:1;width:60px;height:60px;position:absolute;bottom:20px;right:40px;box-shadow:0 5px 40px rgba(0,0,0,.16);background:#1976d2;border-radius:50%;border:none;outline:0;color:#fff;font-size:32px}.chat-button-header{z-index:1;font-weight:700;color:#fff;border:1px solid #fff;background-color:inherit;padding:5px 9px;margin-left:5px}.chat-button:focus{border:2px solid #fff}.chat-box{z-index:2;position:absolute;left:0;height:100vh;width:100vw;margin:0;display:flex;flex-direction:column;box-shadow:0 5px 40px rgba(0,0,0,.16);background:#1976d2}.chat-box-hidden{display:none}.chat-box-header{padding:10px;color:#fff}.chat-box-main{flex:1;width:100%;background:#f5f5f5;display:flex;flex-direction:column-reverse;overflow:auto}.chat-box-footer{color:#fff;height:50px}.operator-name{margin:0;padding:1px}.operator-status{float:right;padding:4px}.operator-status span{margin-left:4px}.operator-status-online{color:#7cfc00}.operator-status-offline{color:red}.operator-avatar{height:30px;width:30px;border-radius:50%;float:left;margin-right:10px}.chat-message{display:block;width:auto;margin:5px;align-self:flex-start;flex-direction:row;max-width:80%;word-wrap:break-word}.chat-message-date{font-size:11px;color:#8d898d;padding:5px}.chat-message-from-avatar{height:35px;width:35px;border-radius:50%}.chat-message-text{margin-left:10px;padding:10px;border-radius:3px}.chat-message-received{margin-right:50px}.chat-message-received .chat-message-text{background:#b9d6f2;margin-left:50px;border-bottom-left-radius:0}.chat-message-received .chat-message-text:before{position:absolute;content:\" \";left:-10px;bottom:0;border-right:10px solid #b9d6f2;border-top:10px solid transparent;z-index:0}.chat-message-received .chat-message-date{margin-left:50px}.chat-message-received .chat-message-from-avatar{position:absolute;left:0;bottom:-15px}.chat-message-sent{align-self:flex-end}.chat-message-sent .chat-message-from{float:right}.chat-message-sent .chat-message-text{background:#84dccf;margin-right:50px;border-bottom-right-radius:0}.chat-message-sent .chat-message-text:after{position:absolute;content:\" \";right:-10px;bottom:0;border-left:10px solid #84dccf;border-top:10px solid transparent;z-index:0}.chat-message-sent .chat-message-date{text-align:right;padding-right:50px}.chat-message-sent .chat-message-from-avatar{position:absolute;right:0;bottom:-15px}.blue .chat-box,.blue .chat-button{background:#1976d2}.grey .chat-box,.grey .chat-button{background:#454549}.red .chat-box,.red .chat-button{background:#dd0031}@media (min-width:576px){.wrapper{top:auto;right:0;width:300px}.chat-button{display:block}.chat-button-header{display:none}.chat-box{top:auto;left:auto;bottom:100px;right:40px;height:60vh;width:300px;border-radius:10px}}@media (min-width:768px){.chat-box{height:70vh}}@media (min-width:992px){.chat-box{height:80vh}}"] }] } ]; /** @nocollapse */ ChatWidgetComponent.ctorParameters = () => [ { type: ChatService } ]; ChatWidgetComponent.propDecorators = { bottom: [{ type: ViewChild, args: ['bottom',] }], theme: [{ type: Input }], botName: [{ type: Input }], botAvatar: [{ type: Input }], userAvatar: [{ type: Input }], url: [{ type: Input }], startingMessage: [{ type: Input }], opened: [{ type: Input }], visible: [{ type: Input }], handleKeyboardEvent: [{ type: HostListener, args: ['document:keypress', ['$event'],] }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class ChatInputComponent { constructor() { this.buttonText = '↩︎'; this.focus = new EventEmitter(); this.send = new EventEmitter(); this.dismiss = new EventEmitter(); } /** * @return {?} */ ngOnInit() { this.focus.subscribe((/** * @return {?} */ () => this.focusMessage())); } /** * @return {?} */ focusMessage() { this.message.nativeElement.focus(); } /** * @return {?} */ getMessage() { return this.message.nativeElement.value; } /** * @return {?} */ clearMessage() { this.message.nativeElement.value = ''; } /** * @return {?} */ onSubmit() { /** @type {?} */ const message = this.getMessage(); if (message.trim() === '') { return; } this.send.emit({ message }); this.clearMessage(); this.focusMessage(); } } ChatInputComponent.decorators = [ { type: Component, args: [{ selector: 'chat-input', template: ` <textarea type="text" class="chat-input-text" placeholder="Type message..." #message (keydown.enter)="onSubmit()" (keyup.enter)="message.value = ''" (keyup.escape)="dismiss.emit()"></textarea> <button type="submit" class="chat-input-submit" (click)="onSubmit()"> {{buttonText}} </button> `, encapsulation: ViewEncapsulation.None, styles: [".chat-input-text{margin:14px 0 0 14px;height:25px;width:70%;border:0;resize:none;border:none;overflow:auto;outline:0;box-shadow:none;font-size:14px;background-color:inherit;color:inherit}.chat-input-text::-webkit-input-placeholder{color:inherit}.chat-input-text::-moz-placeholder{color:inherit}.chat-input-text::-ms-input-placeholder{color:inherit}.chat-input-submit{margin:14px 12px;float:right;background-color:inherit;color:inherit;font-size:24px;border:0;outline:0}"] }] } ]; ChatInputComponent.propDecorators = { buttonText: [{ type: Input }], focus: [{ type: Input }], send: [{ type: Output }], dismiss: [{ type: Output }], message: [{ type: ViewChild, args: ['message',] }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class ChatConfigComponent { constructor() { this.text = 'Select theme'; this.themeChange = new EventEmitter(); this.themes = ['blue', 'grey', 'red']; } /** * @param {?} theme * @return {?} */ setTheme(theme) { this.theme = theme; this.themeChange.emit(this.theme); } } ChatConfigComponent.decorators = [ { type: Component, args: [{ selector: 'chat-config', template: ` <div class="chat-config"> {{text}} <button *ngFor="let item of themes" class="btn" [class.btn-active]="item === theme" (click)="setTheme(item)"> {{item}} </button> </div> `, styles: [` .chat-config { padding: 20px; } .btn { padding: 5px; margin: 0px 2px; border: 1px solid #dedede; outline: none; } .btn-active { border: 1px solid #a0a0a0; } .btn:focus { border: 1px solid #333; } `] }] } ]; ChatConfigComponent.propDecorators = { theme: [{ type: Input }], text: [{ type: Input }], themeChange: [{ type: Output }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class ChatbotRasaModule { } ChatbotRasaModule.decorators = [ { type: NgModule, args: [{ imports: [CommonModule, BrowserAnimationsModule], declarations: [ChatAvatarComponent, ChatWidgetComponent, ChatInputComponent, ChatConfigComponent], exports: [ChatWidgetComponent, ChatConfigComponent], entryComponents: [ChatWidgetComponent, ChatConfigComponent], providers: [ChatService] },] } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ export { ChatbotRasaModule, fadeIn as ɵd, fadeInOut as ɵc, ChatAvatarComponent as ɵa, ChatConfigComponent as ɵg, ChatInputComponent as ɵf, ChatWidgetComponent as ɵb, ChatService as ɵe }; //# sourceMappingURL=angular-chat-widget-rasa.js.map