| @@ -5410,6 +5410,11 @@ | |||||
| "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", | "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", | ||||
| "dev": true | "dev": true | ||||
| }, | }, | ||||
| "hammerjs": { | |||||
| "version": "2.0.8", | |||||
| "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", | |||||
| "integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=" | |||||
| }, | |||||
| "handle-thing": { | "handle-thing": { | ||||
| "version": "2.0.1", | "version": "2.0.1", | ||||
| "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", | "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", | ||||
| @@ -21,6 +21,7 @@ | |||||
| "@angular/router": "~9.1.1", | "@angular/router": "~9.1.1", | ||||
| "@angular/service-worker": "~9.1.1", | "@angular/service-worker": "~9.1.1", | ||||
| "angular-svg-icon": "^9.2.0", | "angular-svg-icon": "^9.2.0", | ||||
| "hammerjs": "^2.0.8", | |||||
| "moment": "^2.27.0", | "moment": "^2.27.0", | ||||
| "ngx-scroll-event": "^1.0.8", | "ngx-scroll-event": "^1.0.8", | ||||
| "ngx-siema": "^2.0.1", | "ngx-siema": "^2.0.1", | ||||
| @@ -1,11 +1,14 @@ | |||||
| // Module imports | |||||
| import { BrowserModule } from '@angular/platform-browser'; | import { BrowserModule } from '@angular/platform-browser'; | ||||
| import { NgModule } from '@angular/core'; | import { NgModule } from '@angular/core'; | ||||
| import { AngularSvgIconModule } from 'angular-svg-icon'; | import { AngularSvgIconModule } from 'angular-svg-icon'; | ||||
| import { HttpClientModule } from '@angular/common/http'; | import { HttpClientModule } from '@angular/common/http'; | ||||
| import { ScrollEventModule } from 'ngx-scroll-event'; | import { ScrollEventModule } from 'ngx-scroll-event'; | ||||
| import { NgxSiemaModule } from 'ngx-siema'; | import { NgxSiemaModule } from 'ngx-siema'; | ||||
| import { AppRoutingModule } from './app-routing.module'; | import { AppRoutingModule } from './app-routing.module'; | ||||
| import { FormsModule } from '@angular/forms'; | |||||
| // Component imports | |||||
| import { AppComponent } from './app.component'; | import { AppComponent } from './app.component'; | ||||
| import { WelcomeComponent } from './welcome/welcome.component'; | import { WelcomeComponent } from './welcome/welcome.component'; | ||||
| import { TabsComponent } from './tabs/tabs.component'; | import { TabsComponent } from './tabs/tabs.component'; | ||||
| @@ -57,9 +60,10 @@ import { SettingsComponent } from './settings/settings.component'; | |||||
| imports: [ | imports: [ | ||||
| BrowserModule, | BrowserModule, | ||||
| AppRoutingModule, | AppRoutingModule, | ||||
| HttpClientModule, | |||||
| ScrollEventModule, | |||||
| NgxSiemaModule.forRoot(), | |||||
| HttpClientModule, | |||||
| ScrollEventModule, | |||||
| FormsModule, | |||||
| NgxSiemaModule.forRoot(), | |||||
| AngularSvgIconModule.forRoot(), | AngularSvgIconModule.forRoot(), | ||||
| ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }) | ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }) | ||||
| ], | ], | ||||
| @@ -9,5 +9,38 @@ | |||||
| </h5> | </h5> | ||||
| </header> | </header> | ||||
| <section class="upfold"></section> | |||||
| <section class="segment-buttons"> | |||||
| <button [ngClass]="{'active' : selectedSegment === 'messages'}" | |||||
| (click)="selectedSegment = 'messages'"> Messages </button> | |||||
| <button [ngClass]="{'active' : selectedSegment === 'broadcasts'}" | |||||
| (click)="selectedSegment = 'broadcasts'"> Broadcasts </button> | |||||
| </section> | |||||
| <section class="segment-details"> | |||||
| <div *ngIf="selectedSegment === 'messages'"> | |||||
| <ul class="chat-list"> | |||||
| <li *ngFor="let chat of chatList; let i = index" (click)="openChat(i)"> | |||||
| <img src="{{ chat.user.imgUrl }}"> | |||||
| <div class="content"> | |||||
| <label> {{ chat.user.name }} </label> | |||||
| <p> | |||||
| {{ chat.conversation[chat.conversation.length - 1].message }} | |||||
| </p> | |||||
| </div> | |||||
| <div class="time-stamp"> | |||||
| {{ getFormattedDate() }} | |||||
| </div> | |||||
| </li> | |||||
| </ul> | |||||
| </div> | |||||
| </section> | |||||
| <section class="chat-window-slideup" [ngClass]="{'active' : selectedChat}"> | |||||
| <app-chat-window [conversation]="selectedChat" *ngIf="selectedChat" | |||||
| (hideChat)="getChatEvent($event)"></app-chat-window> | |||||
| </section> | |||||
| </div> | </div> | ||||
| @@ -45,6 +45,117 @@ | |||||
| } | } | ||||
| } | } | ||||
| .upfold { | |||||
| height: 30vh; | |||||
| .segment-buttons { | |||||
| display: flex; | |||||
| align-items: stretch; | |||||
| height: 40px; | |||||
| border-radius: 7px; | |||||
| overflow: hidden; | |||||
| width: 90%; | |||||
| margin: 20px auto; | |||||
| background-color: var(--dark-grey); | |||||
| padding: 1px; | |||||
| button { | |||||
| flex-grow: 1; | |||||
| border-radius: 7px; | |||||
| background-color: transparent; | |||||
| color: white; | |||||
| font-size: 13px; | |||||
| border: 0px; | |||||
| &.active { | |||||
| background-color: var(--teal-green); | |||||
| color: white; | |||||
| font-weight: 500; | |||||
| } | |||||
| } | |||||
| } | |||||
| .segment-details { | |||||
| background-color: white; | |||||
| overflow: auto; | |||||
| height: calc(100vh - 140px); | |||||
| padding: 20px 5%; | |||||
| border-top-left-radius: 30px; | |||||
| border-top-right-radius: 30px; | |||||
| } | |||||
| .chat-list { | |||||
| list-style: none; | |||||
| li { | |||||
| display: flex; | |||||
| width: 100%; | |||||
| align-items: center; | |||||
| justify-content: space-between; | |||||
| padding: 15px 0; | |||||
| margin: 0px auto; | |||||
| border-bottom: 1px solid #cecece; | |||||
| img { | |||||
| width: 40px; | |||||
| height: 40px; | |||||
| border-radius: 50%; | |||||
| margin-right: 15px; | |||||
| } | |||||
| .content { | |||||
| max-width: calc(100% - 100px); | |||||
| flex-grow: 1; | |||||
| } | |||||
| .time-stamp { | |||||
| font-size: 10px; | |||||
| color: var(--light-grey); | |||||
| margin-left: 15px; | |||||
| } | |||||
| label { | |||||
| display: block; | |||||
| font-size: 15px; | |||||
| font-weight: 500; | |||||
| color: var(--dark-grey); | |||||
| } | |||||
| p { | |||||
| font-size: 13px; | |||||
| color: var(--light-grey); | |||||
| white-space: nowrap; | |||||
| overflow: hidden; | |||||
| text-overflow: ellipsis; | |||||
| } | |||||
| } | |||||
| } | |||||
| .chat-window-slideup { | |||||
| position: fixed; | |||||
| width: 100%; | |||||
| bottom: 0px; | |||||
| left: 0px; | |||||
| height: calc(100vh - 70px); | |||||
| background-color: white; | |||||
| z-index: 1; | |||||
| border-top-left-radius: 30px; | |||||
| border-top-right-radius: 30px; | |||||
| transform: translateY(100vh); | |||||
| opacity: 0; | |||||
| transition: opacity 0.3s, transform 0.3s; | |||||
| padding: 40px 0 20px; | |||||
| &::before { | |||||
| content: ''; | |||||
| position: absolute; | |||||
| width: 60px; | |||||
| height: 4px; | |||||
| border-radius: 30px; | |||||
| background-color: var(--light-grey); | |||||
| left: calc(50% - 30px); | |||||
| top: 20px; | |||||
| } | |||||
| &.active { | |||||
| opacity: 1; | |||||
| transform: translateY(0); | |||||
| } | |||||
| } | } | ||||
| @@ -1,5 +1,6 @@ | |||||
| import { Component, OnInit } from '@angular/core'; | import { Component, OnInit } from '@angular/core'; | ||||
| import { Location } from '@angular/common'; | import { Location } from '@angular/common'; | ||||
| import * as moment from 'moment'; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-chat-page', | selector: 'app-chat-page', | ||||
| @@ -7,6 +8,90 @@ import { Location } from '@angular/common'; | |||||
| styleUrls: ['./chat-page.component.scss'] | styleUrls: ['./chat-page.component.scss'] | ||||
| }) | }) | ||||
| export class ChatPageComponent implements OnInit { | export class ChatPageComponent implements OnInit { | ||||
| selectedSegment: string = 'messages'; | |||||
| selectedChat: any = null; | |||||
| chatList = [{ | |||||
| user: { | |||||
| id: 1, | |||||
| name: 'Jordan Janardhan', | |||||
| imgUrl: 'https://c-sf.smule.com/rs-s79/arr/c1/0b/83a9328a-1469-4bfd-9e94-622f7ca7b1b5.jpg' | |||||
| }, | |||||
| conversation: [{ | |||||
| message: 'Hi, What\'s up!?', | |||||
| user: 1 | |||||
| }, { | |||||
| message: 'Nothing, Was checking upon you!', | |||||
| user: 0 | |||||
| }, { | |||||
| message: 'How you?', | |||||
| user: 1 | |||||
| }, { | |||||
| message: 'All Good! you?', | |||||
| user: 0 | |||||
| }, { | |||||
| message: 'Dont you feel sad?', | |||||
| user: 1 | |||||
| }, { | |||||
| message: 'Why?', | |||||
| user: 0 | |||||
| }, { | |||||
| message: 'Cause you flunked the quiz and probably will end up writing a lot of assignments', | |||||
| user: 1 | |||||
| }, { | |||||
| message: 'Meh, I will game!', | |||||
| user: 0 | |||||
| }, { | |||||
| message: 'Nice! Which game?', | |||||
| user: 1 | |||||
| }, { | |||||
| message: 'WOW', | |||||
| user: 0 | |||||
| }, { | |||||
| message: 'WOW?', | |||||
| user: 1 | |||||
| }, { | |||||
| message: 'World of warcraft you newb!', | |||||
| user: 0 | |||||
| }] | |||||
| }, { | |||||
| user: { | |||||
| id: 2, | |||||
| name: 'Sannidhi Mahajan', | |||||
| imgUrl: 'https://pbs.twimg.com/profile_images/416884752377843712/MW2qg7-f.jpeg' | |||||
| }, | |||||
| conversation: [{ | |||||
| message: 'Did you get to answer Today\'s Quiz?', | |||||
| user: 2 | |||||
| }, { | |||||
| message: 'Yes!, wbu?', | |||||
| user: 0 | |||||
| }, { | |||||
| message: 'I Flunked as usual', | |||||
| user: 0 | |||||
| }, { | |||||
| message: 'Oh, tats sad! Learn to study daa', | |||||
| user: 2 | |||||
| }, { | |||||
| message: 'I topped the class, hehe', | |||||
| user: 2 | |||||
| }, { | |||||
| message: 'Did you get to answer Today\'s Quiz?', | |||||
| user: 2 | |||||
| }, { | |||||
| message: 'Yes!, wbu?', | |||||
| user: 0 | |||||
| }, { | |||||
| message: 'I Flunked as usual', | |||||
| user: 0 | |||||
| }, { | |||||
| message: 'Oh, tats sad! Learn to study daa', | |||||
| user: 2 | |||||
| }, { | |||||
| message: 'I topped the class, hehe', | |||||
| user: 2 | |||||
| }] | |||||
| }]; | |||||
| constructor( | constructor( | ||||
| private location: Location | private location: Location | ||||
| @@ -19,4 +104,18 @@ export class ChatPageComponent implements OnInit { | |||||
| this.location.back(); | this.location.back(); | ||||
| } | } | ||||
| openChat(index: number) { | |||||
| this.selectedChat = this.chatList[index]; | |||||
| } | |||||
| getChatEvent(e: boolean) { | |||||
| if (e) { | |||||
| this.selectedChat = null; | |||||
| } | |||||
| } | |||||
| getFormattedDate() { | |||||
| return moment().format('ddd, hh:MM a') | |||||
| } | |||||
| } | } | ||||
| @@ -1 +1,21 @@ | |||||
| <p>chat-window works!</p> | |||||
| <header> | |||||
| <h5> | |||||
| {{ conversation.user.name }} | |||||
| </h5> | |||||
| <button (click)="closeChat()"> <svg-icon [applyClass]="true" class="icon" src="assets/custom-icons/close.svg"></svg-icon> </button> | |||||
| </header> | |||||
| <ul> | |||||
| <li *ngFor="let message of conversation.conversation;let i = index" | |||||
| [ngClass]="{'sent' : message.user === 0, 'received' : message.user !== 0 }" | |||||
| class="message-{{ i }}"> | |||||
| <div class="message"> | |||||
| {{ message.message }} | |||||
| </div> | |||||
| <div class="time-stamp"> {{ getFormattedTime() }} </div> | |||||
| </li> | |||||
| </ul> | |||||
| <div class="input-holder"> | |||||
| <input type="text" placeholder="Type your message" [(ngModel)]="messageText"> | |||||
| <button (click)="sendMessage()"> <svg-icon [applyClass]="true" class="icon" src="assets/custom-icons/send.svg"></svg-icon> </button> | |||||
| </div> | |||||
| @@ -0,0 +1,125 @@ | |||||
| header { | |||||
| display: flex; | |||||
| align-items: center; | |||||
| justify-content: space-between; | |||||
| width: 100%; | |||||
| padding: 0 5%; | |||||
| margin: 0 auto 20px; | |||||
| background-color: white; | |||||
| h5 { | |||||
| font-weight: 500; | |||||
| letter-spacing: 1px; | |||||
| font-size: 18px; | |||||
| color: var(--ash-black); | |||||
| max-width: 50%; | |||||
| white-space: nowrap; | |||||
| overflow: hidden; | |||||
| text-overflow: ellipsis; | |||||
| } | |||||
| button { | |||||
| width: 30px; | |||||
| height: 30px; | |||||
| border-radius: 50%; | |||||
| background-color: var(--light-grey); | |||||
| border: 0px; | |||||
| .icon { | |||||
| width: 12px; | |||||
| height: 12px; | |||||
| fill: white; | |||||
| } | |||||
| } | |||||
| } | |||||
| ul { | |||||
| width: 100%; | |||||
| margin: 0 auto; | |||||
| list-style: none; | |||||
| max-height: calc(100% - 175px); | |||||
| overflow: auto; | |||||
| position: absolute; | |||||
| left: 0px; | |||||
| bottom: 95px; | |||||
| padding: 0 5%; | |||||
| li { | |||||
| width: 45%; | |||||
| } | |||||
| .message { | |||||
| border-radius: 10px; | |||||
| padding: 10px; | |||||
| font-size: 14px; | |||||
| } | |||||
| .sent { | |||||
| margin-left: auto; | |||||
| .time-stamp { | |||||
| text-align: right; | |||||
| } | |||||
| .message { | |||||
| color: white; | |||||
| background-color: var(--teal); | |||||
| border-bottom-right-radius: 0px; | |||||
| } | |||||
| } | |||||
| .received { | |||||
| margin-right: auto; | |||||
| .message { | |||||
| color: var(--dark-grey); | |||||
| background-color: rgba(#cecece, 0.4); | |||||
| border-bottom-left-radius: 0px; | |||||
| } | |||||
| } | |||||
| .time-stamp { | |||||
| font-size: 12px; | |||||
| color: var(--dark-grey); | |||||
| margin: 5px; | |||||
| } | |||||
| } | |||||
| .input-holder { | |||||
| display: flex; | |||||
| align-items: stretch; | |||||
| width: 90%; | |||||
| margin: 0 auto; | |||||
| position: absolute; | |||||
| bottom: 20px; | |||||
| left: 5%; | |||||
| border-radius: 30px; | |||||
| background-color: rgba(#cecece, 0.4); | |||||
| overflow: hidden; | |||||
| padding: 5px; | |||||
| .icon { | |||||
| fill: white; | |||||
| width: 15px; | |||||
| height: 15px; | |||||
| } | |||||
| input { | |||||
| border: 0px; | |||||
| background-color: transparent; | |||||
| flex-grow: 1; | |||||
| padding-left: 10px; | |||||
| } | |||||
| button { | |||||
| width: 40px; | |||||
| border-radius: 50%; | |||||
| border: 0px; | |||||
| height: 40px; | |||||
| background-color: var(--teal-green); | |||||
| display: flex; | |||||
| align-items: center; | |||||
| justify-content: center; | |||||
| box-shadow: 0px 0px 5px var(--light-grey); | |||||
| } | |||||
| } | |||||
| @@ -1,15 +1,51 @@ | |||||
| import { Component, OnInit } from '@angular/core'; | |||||
| import { Component, OnInit, Input, EventEmitter, Output } from '@angular/core'; | |||||
| import * as moment from 'moment'; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-chat-window', | |||||
| templateUrl: './chat-window.component.html', | |||||
| styleUrls: ['./chat-window.component.scss'] | |||||
| selector: 'app-chat-window', | |||||
| templateUrl: './chat-window.component.html', | |||||
| styleUrls: ['./chat-window.component.scss'] | |||||
| }) | }) | ||||
| export class ChatWindowComponent implements OnInit { | export class ChatWindowComponent implements OnInit { | ||||
| @Input() conversation: any; | |||||
| messageText: string = ''; | |||||
| @Output() hideChat = new EventEmitter(); | |||||
| constructor() { } | |||||
| constructor() { } | |||||
| ngOnInit(): void { | |||||
| } | |||||
| ngOnInit(): void { | |||||
| setTimeout(() => { | |||||
| this.scrollToBottom(); | |||||
| }, 500); | |||||
| } | |||||
| getFormattedTime() { | |||||
| return moment().format('ddd, hh:MM a'); | |||||
| } | |||||
| sendMessage() { | |||||
| if (this.messageText.trim()) { | |||||
| this.conversation.conversation.push({ | |||||
| message: this.messageText, | |||||
| user: 0, | |||||
| }); | |||||
| setTimeout(() => { | |||||
| this.scrollToBottom(); | |||||
| }, 200); | |||||
| this.messageText = ''; | |||||
| } | |||||
| } | |||||
| scrollToBottom() { | |||||
| document.querySelector('.message-' + (this.conversation.conversation.length - 1)).scrollIntoView({ | |||||
| behavior: 'smooth', | |||||
| block: 'end' | |||||
| }); | |||||
| } | |||||
| closeChat() { | |||||
| this.hideChat.emit(true); | |||||
| } | |||||
| } | } | ||||
| @@ -56,7 +56,7 @@ li { | |||||
| border-bottom: 1px solid var(--dark-grey); | border-bottom: 1px solid var(--dark-grey); | ||||
| height: 70px; | height: 70px; | ||||
| padding: 0 15px; | padding: 0 15px; | ||||
| color: var(--light-grey); | |||||
| color: rgba(white, 0.7); | |||||
| font-size: 14px; | font-size: 14px; | ||||
| letter-spacing: 1px; | letter-spacing: 1px; | ||||
| @@ -0,0 +1 @@ | |||||
| <svg enable-background="new 0 0 24 24" height="512" viewBox="0 0 24 24" width="512" xmlns="http://www.w3.org/2000/svg"><path d="m8.75 17.612v4.638c0 .324.208.611.516.713.077.025.156.037.234.037.234 0 .46-.11.604-.306l2.713-3.692z"/><path d="m23.685.139c-.23-.163-.532-.185-.782-.054l-22.5 11.75c-.266.139-.423.423-.401.722.023.3.222.556.505.653l6.255 2.138 13.321-11.39-10.308 12.419 10.483 3.583c.078.026.16.04.242.04.136 0 .271-.037.39-.109.19-.116.319-.311.352-.53l2.75-18.5c.041-.28-.077-.558-.307-.722z"/></svg> | |||||