| @@ -5410,6 +5410,11 @@ | |||
| "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", | |||
| "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": { | |||
| "version": "2.0.1", | |||
| "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", | |||
| @@ -21,6 +21,7 @@ | |||
| "@angular/router": "~9.1.1", | |||
| "@angular/service-worker": "~9.1.1", | |||
| "angular-svg-icon": "^9.2.0", | |||
| "hammerjs": "^2.0.8", | |||
| "moment": "^2.27.0", | |||
| "ngx-scroll-event": "^1.0.8", | |||
| "ngx-siema": "^2.0.1", | |||
| @@ -1,11 +1,14 @@ | |||
| // Module imports | |||
| import { BrowserModule } from '@angular/platform-browser'; | |||
| import { NgModule } from '@angular/core'; | |||
| import { AngularSvgIconModule } from 'angular-svg-icon'; | |||
| import { HttpClientModule } from '@angular/common/http'; | |||
| import { ScrollEventModule } from 'ngx-scroll-event'; | |||
| import { NgxSiemaModule } from 'ngx-siema'; | |||
| import { AppRoutingModule } from './app-routing.module'; | |||
| import { FormsModule } from '@angular/forms'; | |||
| // Component imports | |||
| import { AppComponent } from './app.component'; | |||
| import { WelcomeComponent } from './welcome/welcome.component'; | |||
| import { TabsComponent } from './tabs/tabs.component'; | |||
| @@ -57,9 +60,10 @@ import { SettingsComponent } from './settings/settings.component'; | |||
| imports: [ | |||
| BrowserModule, | |||
| AppRoutingModule, | |||
| HttpClientModule, | |||
| ScrollEventModule, | |||
| NgxSiemaModule.forRoot(), | |||
| HttpClientModule, | |||
| ScrollEventModule, | |||
| FormsModule, | |||
| NgxSiemaModule.forRoot(), | |||
| AngularSvgIconModule.forRoot(), | |||
| ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }) | |||
| ], | |||
| @@ -9,5 +9,38 @@ | |||
| </h5> | |||
| </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> | |||
| @@ -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 { Location } from '@angular/common'; | |||
| import * as moment from 'moment'; | |||
| @Component({ | |||
| selector: 'app-chat-page', | |||
| @@ -7,6 +8,90 @@ import { Location } from '@angular/common'; | |||
| styleUrls: ['./chat-page.component.scss'] | |||
| }) | |||
| 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( | |||
| private location: Location | |||
| @@ -19,4 +104,18 @@ export class ChatPageComponent implements OnInit { | |||
| 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({ | |||
| 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 { | |||
| @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); | |||
| height: 70px; | |||
| padding: 0 15px; | |||
| color: var(--light-grey); | |||
| color: rgba(white, 0.7); | |||
| font-size: 14px; | |||
| 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> | |||