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