@@ -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> |