diff --git a/client/package.json b/client/package.json
index 43176c6..f07cf12 100644
--- a/client/package.json
+++ b/client/package.json
@@ -31,14 +31,14 @@
"@qontu/ngx-inline-editor": "^0.2.0-alpha.12",
"angular2-jwt": "^0.2.3",
"angular2-moment": "^1.8.0",
- "angularfire2": "^5.0.0-rc.6",
+ "angularfire2": "^5.0.0-rc.7",
"angularx-social-login": "^1.1.8",
"applicationinsights-js": "^1.0.15",
"bootstrap": "4.1.0",
"cookieconsent": "^3.0.6",
"core-js": "^2.5.3",
"dropzone": "^5.3.0",
- "firebase": "4.12.1",
+ "firebase": "^4.12.1",
"font-awesome": "^4.7.0",
"howler": "^2.0.9",
"jquery": "^3.3.1",
diff --git a/client/src/app/app.component.html b/client/src/app/app.component.html
index 3ffcc29..2f846d4 100644
--- a/client/src/app/app.component.html
+++ b/client/src/app/app.component.html
@@ -15,3 +15,5 @@
+
+
diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts
index fed53b8..7c1fe54 100644
--- a/client/src/app/app.module.ts
+++ b/client/src/app/app.module.ts
@@ -16,6 +16,7 @@ import { ProgressbarModule } from 'ngx-bootstrap/progressbar';
import { AngularFireDatabaseModule } from 'angularfire2/database';
import { AngularFireAuthModule } from 'angularfire2/auth';
import { AngularFireModule } from 'angularfire2';
+import { AngularFirestoreModule } from 'angularfire2/firestore';
import { QuillModule } from 'ngx-quill';
import { SocialLoginModule, AuthServiceConfig } from 'angularx-social-login';
@@ -77,6 +78,8 @@ import { SideOverlayComponent } from './components/side-overlay/side-overlay.com
import { UiStateService } from './services/ui-state.service';
import { BoilerplateComponent } from './components/boilerplate/boilerplate.component';
import { BasePageComponent } from './components/base-page/base-page.component';
+import { ChatWidgetComponent } from './components/chat-widget/chat-widget.component';
+import { FirebaseAuthService } from './services/firebase-auth.service';
const cookieConfig: NgcCookieConsentConfig = {
cookie: {
@@ -143,20 +146,22 @@ export function provideConfig() {
HumaniseTimePipe,
SideOverlayComponent,
BoilerplateComponent,
- BasePageComponent
+ BasePageComponent,
+ ChatWidgetComponent
],
imports: [
BrowserModule,
AngularFireModule.initializeApp({
- apiKey: 'AIzaSyAaIm8LTB0ZgJ-g7RXEjtVa1EOQB381QLI',
- authDomain: 'podnoms-797e3.firebaseapp.com',
- databaseURL: 'https://podnoms-797e3.firebaseio.com',
- projectId: 'podnoms-797e3',
- storageBucket: 'podnoms-797e3.appspot.com',
- messagingSenderId: '777042345082'
+ apiKey: 'AIzaSyA5pGl4o1oGJi1Ke-842Lq0VvL2YZU2rfc',
+ authDomain: 'podnoms-api.firebaseapp.com',
+ databaseURL: 'https://podnoms-api.firebaseio.com',
+ projectId: 'podnoms-api',
+ storageBucket: '',
+ messagingSenderId: '357461672895'
}),
AngularFireDatabaseModule,
AngularFireAuthModule,
+ AngularFirestoreModule,
HttpClientModule,
AppRoutingModule,
HttpModule,
@@ -207,6 +212,7 @@ export function provideConfig() {
AppInsightsService,
JobsService,
AudioService,
+ FirebaseAuthService,
GlobalsService
],
bootstrap: [AppComponent]
diff --git a/client/src/app/components/chat-widget/chat-widget.component.css b/client/src/app/components/chat-widget/chat-widget.component.css
new file mode 100644
index 0000000..de7d1a5
--- /dev/null
+++ b/client/src/app/components/chat-widget/chat-widget.component.css
@@ -0,0 +1,230 @@
+.floated-chat-btn {
+ z-index: 9999;
+ position: fixed;
+ bottom: 10px;
+ right: 10px;
+ background: #097cff;
+ -webkit-box-shadow: 0 2px 20px 0 rgba(46, 130, 255, 0.75);
+ box-shadow: 0 2px 20px 0 rgba(46, 130, 255, 0.75);
+ border-radius: 75px;
+ color: #fff;
+ padding: 12px 20px;
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ vertical-align: middle;
+ font-size: 1.08rem;
+ cursor: pointer;
+ -webkit-transition: all 0.2s ease;
+ transition: all 0.2s ease;
+}
+.floated-chat-btn i {
+ vertical-align: middle;
+ display: inline-block;
+ font-size: 24px;
+}
+.floated-chat-btn span {
+ vertical-align: middle;
+ display: inline-block;
+ font-weight: 500;
+}
+.floated-chat-btn i + span {
+ margin-left: 15px;
+}
+.floated-chat-btn:hover {
+ -webkit-transform: scale(1.05);
+ transform: scale(1.05);
+ background-color: #0064d5;
+ -webkit-box-shadow: 0 2px 30px 0 rgba(46, 130, 255, 0.8);
+ box-shadow: 0 2px 30px 0 rgba(46, 130, 255, 0.8);
+}
+.floated-chat-w {
+ z-index: 9999;
+ position: fixed;
+ bottom: 70px;
+ right: 10px;
+ visibility: hidden;
+ opacity: 0;
+ -webkit-transform: translateY(-20px);
+ transform: translateY(-20px);
+ -webkit-transition: all 0.3s ease;
+ transition: all 0.3s ease;
+}
+.floated-chat-w.active {
+ visibility: visible;
+ opacity: 1;
+ -webkit-transform: translateY(0px);
+ transform: translateY(0px);
+}
+.floated-chat-w .floated-chat-i {
+ background-color: #fff;
+ -webkit-box-shadow: 0 2px 40px 0 rgba(43, 132, 210, 0.41);
+ box-shadow: 0 2px 40px 0 rgba(43, 132, 210, 0.41);
+ border-radius: 10px;
+ width: 320px;
+ position: relative;
+}
+.floated-chat-w .floated-chat-i .chat-close {
+ position: absolute;
+ top: 10px;
+ right: 10px;
+ color: rgba(0, 0, 0, 0.8);
+ font-size: 10px;
+ cursor: pointer;
+}
+.floated-chat-w .chat-head {
+ padding: 20px;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.05);
+}
+.floated-chat-w .chat-head .user-w {
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: justify;
+ -ms-flex-pack: justify;
+ justify-content: space-between;
+}
+.floated-chat-w .chat-head .user-w .user-avatar-w {
+ width: 50px;
+}
+.floated-chat-w .chat-head .user-w .user-avatar-w .user-avatar {
+ border-radius: 40px;
+ overflow: hidden;
+}
+.floated-chat-w .chat-head .user-w .user-avatar-w .user-avatar img {
+ max-width: 100%;
+ height: auto;
+}
+.floated-chat-w .chat-head .user-w.with-status .user-avatar-w {
+ position: relative;
+}
+.floated-chat-w .chat-head .user-w.with-status .user-avatar-w:before {
+ content: '';
+ width: 10px;
+ height: 10px;
+ position: absolute;
+ top: 2px;
+ right: 2px;
+ border-radius: 10px;
+ -webkit-box-shadow: 0px 0px 0px 3px #fff;
+ box-shadow: 0px 0px 0px 3px #fff;
+}
+.floated-chat-w
+ .chat-head
+ .user-w.with-status.status-green
+ .user-avatar-w:before {
+ background-color: #24b314;
+}
+.floated-chat-w
+ .chat-head
+ .user-w.with-status.status-red
+ .user-avatar-w:before {
+ background-color: #e65252;
+}
+.floated-chat-w .chat-head .user-w .user-name {
+ padding-left: 20px;
+ -webkit-box-flex: 1;
+ -ms-flex: 1;
+ flex: 1;
+}
+.floated-chat-w .chat-head .user-w .user-title {
+ margin-bottom: 2px;
+ color: #047bf8;
+}
+.floated-chat-w .chat-head .user-w .user-role {
+ font-weight: 500;
+ font-size: 0.81rem;
+}
+.floated-chat-w .chat-head .user-w .user-action {
+ width: 50px;
+ color: #047bf8;
+ font-size: 18px;
+}
+.floated-chat-w .chat-messages {
+ padding: 20px;
+ height: 300px;
+ position: relative;
+ overflow: hidden;
+}
+.floated-chat-w .chat-messages .message {
+ margin-bottom: 12px;
+}
+.floated-chat-w .chat-messages .message .message-content {
+ color: #594939;
+ padding: 10px 20px;
+ background-color: #fcf6ee;
+ border-radius: 20px 20px 20px 0px;
+ max-width: 80%;
+ display: inline-block;
+ text-align: left;
+}
+.floated-chat-w .chat-messages .message.self {
+ text-align: right;
+}
+.floated-chat-w .chat-messages .message.self .message-content {
+ border-radius: 20px 20px 0px 20px;
+ background-color: #e2efff;
+ color: #2a4e7f;
+}
+.floated-chat-w .chat-messages .date-break {
+ text-align: center;
+ margin-bottom: 10px;
+ color: rgba(0, 0, 0, 0.4);
+}
+.floated-chat-w .chat-controls {
+ padding: 10px;
+ padding-top: 0px;
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
+}
+.floated-chat-w .chat-controls .message-input {
+ border: 1px solid transparent;
+ background-color: #fff;
+ padding: 10px;
+ width: 100%;
+ display: block;
+ border-radius: 0px;
+}
+.floated-chat-w .chat-controls .message-input:focus {
+ outline: none;
+ border-bottom: 1px solid #047bf8;
+}
+.floated-chat-w .chat-controls .chat-extra {
+ text-align: left;
+ padding-left: 0px;
+ padding-top: 10px;
+}
+.floated-chat-w .chat-controls .chat-extra a {
+ display: inline-block;
+ margin-left: 10px;
+ font-size: 16px;
+ position: relative;
+}
+.floated-chat-w .chat-controls .chat-extra a .extra-tooltip {
+ background-color: rgba(0, 0, 0, 0.9);
+ color: #fff;
+ font-weight: 500;
+ font-size: 0.63rem;
+ text-transform: uppercase;
+ display: inline-block;
+ padding: 2px 7px;
+ border-radius: 4px;
+ position: absolute;
+ top: -20px;
+ left: 50%;
+ -webkit-transform: translateX(-50%);
+ transform: translateX(-50%);
+ white-space: nowrap;
+ display: none;
+}
+.floated-chat-w .chat-controls .chat-extra a:hover {
+ text-decoration: none;
+}
+.floated-chat-w .chat-controls .chat-extra a:hover .extra-tooltip {
+ display: block;
+}
diff --git a/client/src/app/components/chat-widget/chat-widget.component.html b/client/src/app/components/chat-widget/chat-widget.component.html
new file mode 100644
index 0000000..95087ae
--- /dev/null
+++ b/client/src/app/components/chat-widget/chat-widget.component.html
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+

+
+
+
+
Welly Welly Well
+
The old days are dead and gone days.
+
+
+
+
+
+
Tell me where it hurts?
+
+
+
{{message.messageText}}
+
+
+
+
+
+
+
+
+
+
+
+ Talk to us!
+
diff --git a/client/src/app/components/chat-widget/chat-widget.component.ts b/client/src/app/components/chat-widget/chat-widget.component.ts
new file mode 100644
index 0000000..5527938
--- /dev/null
+++ b/client/src/app/components/chat-widget/chat-widget.component.ts
@@ -0,0 +1,76 @@
+import { Component, OnInit } from '@angular/core';
+import { AngularFireAuth } from 'angularfire2/auth';
+import { FirebaseAuthService } from '../../services/firebase-auth.service';
+import {
+ AngularFirestore,
+ AngularFirestoreCollection,
+ AngularFirestoreDocument
+} from 'angularfire2/firestore';
+import { Observable } from 'rxjs/Observable';
+import { ProfileService } from '../../services/profile.service';
+import { FirebaseUser } from '../../models/firebaseuser.model';
+
+interface ChatMessage {
+ messageText: string;
+ name: string;
+ fromUser: string;
+}
+@Component({
+ selector: 'app-chat-widget',
+ templateUrl: './chat-widget.component.html',
+ styleUrls: ['./chat-widget.component.css']
+})
+export class ChatWidgetComponent implements OnInit {
+ chatActive: boolean = false;
+ loading: boolean = false;
+ currentUser: FirebaseUser;
+
+ messageCollection: AngularFirestoreCollection;
+ messages: Observable;
+ userName: string = 'Anonymous User';
+ constructor(
+ private _firebaseAuthService: FirebaseAuthService,
+ private _profileService: ProfileService,
+ private afs: AngularFirestore
+ ) {
+ this._profileService
+ .getProfile()
+ .subscribe((p) => (this.userName = p.firstName + ' ' + p.lastName));
+ }
+
+ ngOnInit() {}
+
+ togglePopup() {
+ this.chatActive = !this.chatActive;
+ if (this.chatActive) {
+ this.loading = true;
+ this._firebaseAuthService.user.subscribe((u) => {
+ if (u === null) {
+ this._firebaseAuthService
+ .anonymousLogin()
+ .then((user) => this._openChat(user));
+ } else {
+ this._openChat(u);
+ }
+ });
+ }
+ }
+ _openChat(user) {
+ this.currentUser = user;
+ this.loading = false;
+ this.messageCollection = this.afs.collection('supportchat');
+ this.messages = this.messageCollection.valueChanges();
+ }
+
+ sendMessage(el: HTMLInputElement) {
+ if (el.value) {
+ this.afs
+ .collection('supportchat')
+ .add({
+ fromUid: this.currentUser.uid,
+ messageText: el.value,
+ name: this.currentUser.displayName
+ });
+ }
+ }
+}
diff --git a/client/src/app/models/firebaseuser.model.ts b/client/src/app/models/firebaseuser.model.ts
new file mode 100644
index 0000000..3c17450
--- /dev/null
+++ b/client/src/app/models/firebaseuser.model.ts
@@ -0,0 +1,6 @@
+export interface FirebaseUser {
+ uid: string;
+ email?: string | null;
+ photoURL?: string;
+ displayName?: string;
+}
diff --git a/client/src/app/services/firebase-auth.service.ts b/client/src/app/services/firebase-auth.service.ts
new file mode 100644
index 0000000..c33fb81
--- /dev/null
+++ b/client/src/app/services/firebase-auth.service.ts
@@ -0,0 +1,58 @@
+import { Injectable } from '@angular/core';
+import { AngularFireAuth } from 'angularfire2/auth';
+import { AngularFireDatabase } from 'angularfire2/database';
+import {
+ AngularFirestore,
+ AngularFirestoreDocument
+} from 'angularfire2/firestore';
+import { Observable } from 'rxjs/Observable';
+import { FirebaseUser } from 'app/models/firebaseuser.model';
+
+@Injectable()
+export class FirebaseAuthService {
+ user: Observable;
+
+ constructor(
+ private afAuth: AngularFireAuth,
+ private afs: AngularFirestore
+ ) {
+ this.user = this.afAuth.authState.switchMap((user) => {
+ if (user) {
+ return this.afs
+ .doc(`users/${user.uid}`)
+ .valueChanges();
+ } else {
+ return Observable.of(null);
+ }
+ });
+ }
+
+ private handleError(error: Error) {
+ console.error(error);
+ }
+
+ private updateUserData(user: FirebaseUser): Promise {
+ const userRef: AngularFirestoreDocument = this.afs.doc(
+ `users/${user.uid}`
+ );
+ const data: FirebaseUser = {
+ uid: user.uid,
+ email: user.email || null,
+ displayName: user.displayName || 'nameless user',
+ photoURL: user.photoURL || 'https://goo.gl/Fz9nrQ'
+ };
+ return userRef.set(data).then(() => data);
+ }
+ anonymousLogin() {
+ return this.afAuth.auth
+ .signInAnonymously()
+ .then((user) => {
+ return this.updateUserData(user); // if using firestore
+ })
+ .catch((error) => {
+ console.error(error.code);
+ console.error(error.message);
+ this.handleError(error);
+ });
+ }
+}
diff --git a/client/src/assets/img/alex-1.png b/client/src/assets/img/alex-1.png
new file mode 100644
index 0000000..b1e9568
Binary files /dev/null and b/client/src/assets/img/alex-1.png differ