mirror of
https://github.com/richard-loafle/fuse-angular.git
synced 2025-01-09 20:15:07 +00:00
Filter pipe added,
spacing class helpers added(margin, padding), Normalize css added, Icon size class helpers added, slideInLeft, slideInRight animation added, Chat app almost completed.
This commit is contained in:
parent
b9569a5ba8
commit
56e6b854c2
|
@ -20,6 +20,7 @@ import { PerfectScrollbarConfigInterface, PerfectScrollbarModule } from 'ngx-per
|
|||
import { HttpModule } from '@angular/http';
|
||||
import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
|
||||
import { FuseFakeDbService } from './fuse-fake-db/fuse-fake-db.service';
|
||||
import { INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS } from '@angular/platform-browser-dynamic/src/platform_providers';
|
||||
|
||||
const PERFECT_SCROLLBAR_CONFIG: PerfectScrollbarConfigInterface = {
|
||||
suppressScrollX: true
|
||||
|
@ -30,6 +31,10 @@ const appRoutes: Routes = [
|
|||
path : 'apps/mail',
|
||||
loadChildren: './main/apps/mail/mail.module#MailModule'
|
||||
},
|
||||
{
|
||||
path : 'apps/chat',
|
||||
loadChildren: './main/apps/chat/chat.module#ChatModule'
|
||||
},
|
||||
{
|
||||
path : '**',
|
||||
redirectTo: 'apps/dashboards/project'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {trigger, state, transition, animate, style} from '@angular/animations';
|
||||
import { trigger, state, transition, animate, style } from '@angular/animations';
|
||||
|
||||
export class Animations
|
||||
{
|
||||
|
@ -8,4 +8,16 @@ export class Animations
|
|||
transition('1 => 0', animate('300ms ease-out')),
|
||||
transition('0 => 1', animate('300ms ease-in'))
|
||||
]);
|
||||
public static slideInLeft = trigger('slideInLeft', [
|
||||
state('void', style({transform: 'translateX(-100%)', display: 'none'})),
|
||||
state('*', style({transform: 'translateX(0)', display: 'flex'})),
|
||||
transition('void => *', animate('300ms')),
|
||||
transition('* => void', animate('300ms'))
|
||||
]);
|
||||
public static slideInRight = trigger('slideInRight', [
|
||||
state('void', style({transform: 'translateX(100%)', display: 'none'})),
|
||||
state('*', style({transform: 'translateX(0)', display: 'flex'})),
|
||||
transition('void => *', animate('300ms')),
|
||||
transition('* => void', animate('300ms'))
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { MaterialModule } from './material.module';
|
||||
|
@ -23,7 +23,8 @@ import { FusePipesModule } from '../pipes/pipes.module';
|
|||
CommonModule,
|
||||
FormsModule,
|
||||
FusePipesModule,
|
||||
PerfectScrollbarModule
|
||||
PerfectScrollbarModule,
|
||||
ReactiveFormsModule
|
||||
],
|
||||
exports : [
|
||||
FlexLayoutModule,
|
||||
|
@ -33,7 +34,8 @@ import { FusePipesModule } from '../pipes/pipes.module';
|
|||
FuseMdSidenavHelperDirective,
|
||||
FuseMdSidenavTogglerDirective,
|
||||
FusePipesModule,
|
||||
PerfectScrollbarModule
|
||||
PerfectScrollbarModule,
|
||||
ReactiveFormsModule
|
||||
]
|
||||
})
|
||||
|
||||
|
|
152
src/app/core/pipes/filter.pipe.ts
Normal file
152
src/app/core/pipes/filter.pipe.ts
Normal file
|
@ -0,0 +1,152 @@
|
|||
/**
|
||||
* Created by vadimdez on 28/06/16.
|
||||
*/
|
||||
import { Pipe, Injectable } from '@angular/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'filterBy',
|
||||
pure: false
|
||||
})
|
||||
|
||||
@Injectable()
|
||||
export class FilterPipe {
|
||||
|
||||
private filterByString(filter) {
|
||||
if (filter) {
|
||||
filter = filter.toLowerCase();
|
||||
}
|
||||
return value => {
|
||||
return !filter || (value ? ('' + value).toLowerCase().indexOf(filter) !== -1 : false);
|
||||
}
|
||||
}
|
||||
|
||||
private filterByBoolean(filter) {
|
||||
return value => {
|
||||
return Boolean(value) === filter;
|
||||
}
|
||||
}
|
||||
|
||||
private filterByObject(filter) {
|
||||
return value => {
|
||||
for (let key in filter) {
|
||||
|
||||
if (key === '$or') {
|
||||
if (!this.filterByOr(filter.$or)(this.getValue(value))) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!value.hasOwnProperty(key) && !Object.getOwnPropertyDescriptor(Object.getPrototypeOf(value), key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let val = this.getValue(value[key]);
|
||||
const filterType = typeof filter[key];
|
||||
let isMatching;
|
||||
|
||||
if (filterType === 'boolean') {
|
||||
isMatching = this.filterByBoolean(filter[key])(val);
|
||||
} else if (filterType === 'string') {
|
||||
isMatching = this.filterByString(filter[key])(val);
|
||||
} else if (filterType === 'object') {
|
||||
isMatching = this.filterByObject(filter[key])(val);
|
||||
} else {
|
||||
isMatching = this.filterDefault(filter[key])(val);
|
||||
}
|
||||
|
||||
if (!isMatching) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter value by $or
|
||||
*
|
||||
* @param filter
|
||||
* @returns {(value:any)=>boolean}
|
||||
*/
|
||||
private filterByOr(filter: any[]) {
|
||||
return (value: any) => {
|
||||
let hasMatch = false;
|
||||
const length = filter.length;
|
||||
const isArray = value instanceof Array;
|
||||
|
||||
const arrayComparison = (i) => {
|
||||
return value.indexOf(filter[i]) !== -1;
|
||||
};
|
||||
const otherComparison = (i) => {
|
||||
return value === filter[i];
|
||||
};
|
||||
const comparison = isArray ? arrayComparison : otherComparison;
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (comparison(i)) {
|
||||
hasMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return hasMatch;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks function's value if type is function otherwise same value
|
||||
* @param value
|
||||
* @returns {any}
|
||||
*/
|
||||
private getValue(value: any) {
|
||||
return typeof value === 'function' ? value() : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defatul filterDefault function
|
||||
*
|
||||
* @param filter
|
||||
* @returns {(value:any)=>boolean}
|
||||
*/
|
||||
private filterDefault(filter) {
|
||||
return value => {
|
||||
return filter === undefined || filter == value;
|
||||
}
|
||||
}
|
||||
|
||||
private isNumber(value) {
|
||||
return !isNaN(parseInt(value, 10)) && isFinite(value);
|
||||
}
|
||||
|
||||
transform(array: any[], filter: any): any {
|
||||
const type = typeof filter;
|
||||
|
||||
if (!array) {
|
||||
return array;
|
||||
}
|
||||
|
||||
if (type === 'boolean') {
|
||||
return array.filter(this.filterByBoolean(filter));
|
||||
}
|
||||
|
||||
if (type === 'string') {
|
||||
if (this.isNumber(filter)) {
|
||||
return array.filter(this.filterDefault(filter));
|
||||
}
|
||||
|
||||
return array.filter(this.filterByString(filter));
|
||||
}
|
||||
|
||||
if (type === 'object') {
|
||||
return array.filter(this.filterByObject(filter));
|
||||
}
|
||||
|
||||
if (type === 'function') {
|
||||
return array.filter(filter);
|
||||
}
|
||||
|
||||
return array.filter(this.filterDefault(filter));
|
||||
}
|
||||
}
|
|
@ -4,20 +4,24 @@ import { KeysPipe } from './keys.pipe';
|
|||
import { GetByIdPipe } from './getById.pipe';
|
||||
import { HtmlToPlaintextPipe } from './htmlToPlaintext.pipe';
|
||||
import { SearchPipe } from './search.pipe';
|
||||
import { FilterPipe } from './filter.pipe';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
KeysPipe,
|
||||
GetByIdPipe,
|
||||
HtmlToPlaintextPipe,
|
||||
SearchPipe
|
||||
SearchPipe,
|
||||
FilterPipe
|
||||
|
||||
],
|
||||
imports : [],
|
||||
exports : [
|
||||
KeysPipe,
|
||||
GetByIdPipe,
|
||||
HtmlToPlaintextPipe,
|
||||
SearchPipe
|
||||
SearchPipe,
|
||||
FilterPipe
|
||||
]
|
||||
})
|
||||
|
||||
|
|
|
@ -20,12 +20,16 @@
|
|||
@include angular-material-typography($custom-typography);
|
||||
|
||||
// Partials
|
||||
@import "partials/normalize";
|
||||
@import "partials/spacing";
|
||||
@import "partials/global";
|
||||
@import "partials/_material";
|
||||
@import "partials/icons";
|
||||
@import "partials/material";
|
||||
@import "partials/angular-material-fix";
|
||||
@import "partials/typography";
|
||||
@import "partials/page-layouts";
|
||||
@import "partials/navigation";
|
||||
@import "partials/forms";
|
||||
|
||||
// Plugins
|
||||
@import "partials/plugins/plugins";
|
||||
|
|
|
@ -3,4 +3,4 @@
|
|||
.mat-button-ripple {
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
15
src/app/core/scss/partials/_forms.scss
Normal file
15
src/app/core/scss/partials/_forms.scss
Normal file
|
@ -0,0 +1,15 @@
|
|||
button,
|
||||
input[type=email],
|
||||
input[type=tel],
|
||||
input[type=text],
|
||||
input[type=password],
|
||||
input[type=image],
|
||||
input[type=submit],
|
||||
input[type=button],
|
||||
input[type=search],
|
||||
textarea {
|
||||
appearance: none;
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
outline: none;
|
||||
}
|
|
@ -2,6 +2,10 @@
|
|||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
|
||||
}
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
|
@ -21,4 +25,4 @@ body {
|
|||
> md-sidenav-container {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
22
src/app/core/scss/partials/_icons.scss
Normal file
22
src/app/core/scss/partials/_icons.scss
Normal file
|
@ -0,0 +1,22 @@
|
|||
i,
|
||||
md-icon {
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
font-size: 24px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
min-width: 24px;
|
||||
min-height: 24px;
|
||||
line-height: 24px;
|
||||
|
||||
@for $size from 2 through 128 {
|
||||
|
||||
&.s-#{$size * 2} {
|
||||
font-size: #{($size * 2) + 'px'} !important;
|
||||
width: #{($size * 2) + 'px'} !important;
|
||||
height: #{($size * 2) + 'px'} !important;
|
||||
min-width: #{($size * 2) + 'px'} !important;
|
||||
min-height: #{($size * 2) + 'px'} !important;
|
||||
line-height: #{($size * 2) + 'px'} !important;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -39,6 +39,54 @@
|
|||
}
|
||||
}
|
||||
|
||||
.avatar-wrapper {
|
||||
position: relative;
|
||||
|
||||
.avatar {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
md-icon.status {
|
||||
position: absolute;
|
||||
top: 28px;
|
||||
left: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
md-icon.status {
|
||||
border-radius: 50%;
|
||||
|
||||
&.online {
|
||||
color: #4CAF50;
|
||||
&:before {
|
||||
content: "check_circle";
|
||||
}
|
||||
}
|
||||
|
||||
&.do-not-disturb {
|
||||
color: #F44336;
|
||||
&:before {
|
||||
content: "do_not_disturb_on";
|
||||
}
|
||||
}
|
||||
|
||||
&.away {
|
||||
background-color: #FFC107;
|
||||
color: #FFFFFF;
|
||||
&:before {
|
||||
content: "access_time";
|
||||
}
|
||||
}
|
||||
|
||||
&.offline {
|
||||
color: #646464;
|
||||
background-color: #FFFFFF;
|
||||
&:before {
|
||||
content: "not_interested";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
/* Forms
|
||||
/*----------------------------------------------------------------*/
|
||||
|
|
447
src/app/core/scss/partials/_normalize.scss
Normal file
447
src/app/core/scss/partials/_normalize.scss
Normal file
|
@ -0,0 +1,447 @@
|
|||
/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */
|
||||
|
||||
/* Document
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Correct the line height in all browsers.
|
||||
* 2. Prevent adjustments of font size after orientation changes in
|
||||
* IE on Windows Phone and in iOS.
|
||||
*/
|
||||
|
||||
html {
|
||||
line-height: 1.15; /* 1 */
|
||||
-ms-text-size-adjust: 100%; /* 2 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
/* Sections
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the margin in all browsers (opinionated).
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
*/
|
||||
|
||||
article,
|
||||
aside,
|
||||
footer,
|
||||
header,
|
||||
nav,
|
||||
section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the font size and margin on `h1` elements within `section` and
|
||||
* `article` contexts in Chrome, Firefox, and Safari.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
/* Grouping content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
* 1. Add the correct display in IE.
|
||||
*/
|
||||
|
||||
figcaption,
|
||||
figure,
|
||||
main { /* 1 */
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct margin in IE 8.
|
||||
*/
|
||||
|
||||
figure {
|
||||
margin: 1em 40px;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in Firefox.
|
||||
* 2. Show the overflow in Edge and IE.
|
||||
*/
|
||||
|
||||
hr {
|
||||
box-sizing: content-box; /* 1 */
|
||||
height: 0; /* 1 */
|
||||
overflow: visible; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/* Text-level semantics
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Remove the gray background on active links in IE 10.
|
||||
* 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
|
||||
*/
|
||||
|
||||
a {
|
||||
background-color: transparent; /* 1 */
|
||||
-webkit-text-decoration-skip: objects; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Remove the bottom border in Chrome 57- and Firefox 39-.
|
||||
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: none; /* 1 */
|
||||
text-decoration: underline; /* 2 */
|
||||
text-decoration: underline dotted; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent the duplicate application of `bolder` by the next rule in Safari 6.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font style in Android 4.3-.
|
||||
*/
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct background and color in IE 9-.
|
||||
*/
|
||||
|
||||
mark {
|
||||
background-color: #ff0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||
* all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/* Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
*/
|
||||
|
||||
audio,
|
||||
video {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in iOS 4-7.
|
||||
*/
|
||||
|
||||
audio:not([controls]) {
|
||||
display: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the border on images inside links in IE 10-.
|
||||
*/
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the overflow in IE.
|
||||
*/
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Change the font styles in all browsers (opinionated).
|
||||
* 2. Remove the margin in Firefox and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: sans-serif; /* 1 */
|
||||
font-size: 100%; /* 1 */
|
||||
line-height: 1.15; /* 1 */
|
||||
margin: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the overflow in IE.
|
||||
* 1. Show the overflow in Edge.
|
||||
*/
|
||||
|
||||
button,
|
||||
input { /* 1 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||
* 1. Remove the inheritance of text transform in Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select { /* 1 */
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
|
||||
* controls in Android 4.
|
||||
* 2. Correct the inability to style clickable types in iOS and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
html [type="button"], /* 1 */
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner border and padding in Firefox.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the focus styles unset by the previous rule.
|
||||
*/
|
||||
|
||||
button:-moz-focusring,
|
||||
[type="button"]:-moz-focusring,
|
||||
[type="reset"]:-moz-focusring,
|
||||
[type="submit"]:-moz-focusring {
|
||||
outline: 1px dotted ButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the padding in Firefox.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
padding: 0.35em 0.75em 0.625em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the text wrapping in Edge and IE.
|
||||
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||
* 3. Remove the padding so developers are not caught out when they zero out
|
||||
* `fieldset` elements in all browsers.
|
||||
*/
|
||||
|
||||
legend {
|
||||
box-sizing: border-box; /* 1 */
|
||||
color: inherit; /* 2 */
|
||||
display: table; /* 1 */
|
||||
max-width: 100%; /* 1 */
|
||||
padding: 0; /* 3 */
|
||||
white-space: normal; /* 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct display in IE 9-.
|
||||
* 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||
*/
|
||||
|
||||
progress {
|
||||
display: inline-block; /* 1 */
|
||||
vertical-align: baseline; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the default vertical scrollbar in IE.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in IE 10-.
|
||||
* 2. Remove the padding in IE 10-.
|
||||
*/
|
||||
|
||||
[type="checkbox"],
|
||||
[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||
*/
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the odd appearance in Chrome and Safari.
|
||||
* 2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
outline-offset: -2px; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
[type="search"]::-webkit-search-cancel-button,
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||
* 2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button; /* 1 */
|
||||
font: inherit; /* 2 */
|
||||
}
|
||||
|
||||
/* Interactive
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Add the correct display in IE 9-.
|
||||
* 1. Add the correct display in Edge, IE, and Firefox.
|
||||
*/
|
||||
|
||||
details, /* 1 */
|
||||
menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the correct display in all browsers.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/* Scripting
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
*/
|
||||
|
||||
canvas {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE.
|
||||
*/
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Hidden
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10-.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
62
src/app/core/scss/partials/_spacing.scss
Normal file
62
src/app/core/scss/partials/_spacing.scss
Normal file
|
@ -0,0 +1,62 @@
|
|||
// Margin and Padding
|
||||
@each $prop, $abbrev in (margin: m, padding: p) {
|
||||
|
||||
@for $index from 0 through 64 {
|
||||
$size: $index *4;
|
||||
$length: #{$size}px;
|
||||
.#{$abbrev}-#{$size} {
|
||||
#{$prop}: $length !important;
|
||||
}
|
||||
.#{$abbrev}t-#{$size} {
|
||||
#{$prop}-top: $length !important;
|
||||
}
|
||||
.#{$abbrev}r-#{$size} {
|
||||
#{$prop}-right: $length !important;
|
||||
}
|
||||
.#{$abbrev}b-#{$size} {
|
||||
#{$prop}-bottom: $length !important;
|
||||
}
|
||||
.#{$abbrev}l-#{$size} {
|
||||
#{$prop}-left: $length !important;
|
||||
}
|
||||
.#{$abbrev}x-#{$size} {
|
||||
#{$prop}-right: $length !important;
|
||||
#{$prop}-left: $length !important;
|
||||
}
|
||||
.#{$abbrev}y-#{$size} {
|
||||
#{$prop}-top: $length !important;
|
||||
#{$prop}-bottom: $length !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Some special margin utils
|
||||
.m-auto {
|
||||
margin: auto !important;
|
||||
}
|
||||
|
||||
.mt-auto {
|
||||
margin-top: auto !important;
|
||||
}
|
||||
|
||||
.mr-auto {
|
||||
margin-right: auto !important;
|
||||
}
|
||||
|
||||
.mb-auto {
|
||||
margin-bottom: auto !important;
|
||||
}
|
||||
|
||||
.ml-auto {
|
||||
margin-left: auto !important;
|
||||
}
|
||||
|
||||
.mx-auto {
|
||||
margin-right: auto !important;
|
||||
margin-left: auto !important;
|
||||
}
|
||||
|
||||
.my-auto {
|
||||
margin-top: auto !important;
|
||||
margin-bottom: auto !important;
|
||||
}
|
325
src/app/fuse-fake-db/chat.ts
Normal file
325
src/app/fuse-fake-db/chat.ts
Normal file
|
@ -0,0 +1,325 @@
|
|||
export class ChatFakeDb
|
||||
{
|
||||
public static contacts = [
|
||||
{
|
||||
'id' : '5725a680b3249760ea21de52',
|
||||
'name' : 'Alice Freeman',
|
||||
'avatar': 'assets/images/avatars/alice.jpg',
|
||||
'status': 'online',
|
||||
'mood' : 'I never sign anything until I pretend to read it first..'
|
||||
},
|
||||
{
|
||||
'id' : '5725a680606588342058356d',
|
||||
'name' : 'Arnold',
|
||||
'avatar': 'assets/images/avatars/Arnold.jpg',
|
||||
'status': 'do-not-disturb',
|
||||
'mood' : 'Looks like Andrew Jackson\'s been tossed to the back of the bus.'
|
||||
},
|
||||
{
|
||||
'id' : '5725a68009e20d0a9e9acf2a',
|
||||
'name' : 'Barrera',
|
||||
'avatar': 'assets/images/avatars/Barrera.jpg',
|
||||
'status': 'do-not-disturb',
|
||||
'mood' : 'Love is going to bed early.Marriage is going to sleep early.',
|
||||
'unread': null
|
||||
},
|
||||
{
|
||||
'id' : '5725a6809fdd915739187ed5',
|
||||
'name' : 'Blair',
|
||||
'avatar': 'assets/images/avatars/Blair.jpg',
|
||||
'status': 'offline',
|
||||
'mood' : 'I would be unstoppable. If i could just get started.',
|
||||
'unread': 3
|
||||
},
|
||||
{
|
||||
'id' : '5725a68007920cf75051da64',
|
||||
'name' : 'Boyle',
|
||||
'avatar': 'assets/images/avatars/Boyle.jpg',
|
||||
'status': 'offline',
|
||||
'mood' : '\'GOOD MORNING COFFEE\'....Meet your maker!!!!'
|
||||
},
|
||||
{
|
||||
'id' : '5725a68031fdbb1db2c1af47',
|
||||
'name' : 'Christy',
|
||||
'avatar': 'assets/images/avatars/Christy.jpg',
|
||||
'status': 'offline',
|
||||
'mood' : 'We always hold hands. If I let go, she shops.',
|
||||
},
|
||||
{
|
||||
'id' : '5725a680bc670af746c435e2',
|
||||
'name' : 'Copeland',
|
||||
'avatar': 'assets/images/avatars/Copeland.jpg',
|
||||
'status': 'online',
|
||||
'mood' : 'I get enough exercise just pushing my luck.',
|
||||
},
|
||||
{
|
||||
'id' : '5725a680e7eb988a58ddf303',
|
||||
'name' : 'Estes',
|
||||
'avatar': 'assets/images/avatars/Estes.jpg',
|
||||
'status': 'away',
|
||||
'mood' : 'What comes after the man bun hairstyle? The he-hive!',
|
||||
},
|
||||
{
|
||||
'id' : '5725a680dcb077889f758961',
|
||||
'name' : 'Harper',
|
||||
'avatar': 'assets/images/avatars/Harper.jpg',
|
||||
'status': 'offline',
|
||||
'mood' : 'Always try to be modest and be proud of it!',
|
||||
},
|
||||
{
|
||||
'id' : '5725a6806acf030f9341e925',
|
||||
'name' : 'Helen',
|
||||
'avatar': 'assets/images/avatars/Helen.jpg',
|
||||
'status': 'away',
|
||||
'mood' : 'Why are there stitch marks on zombies? Who\'s giving them medical attention?',
|
||||
},
|
||||
{
|
||||
'id' : '5725a680ae1ae9a3c960d487',
|
||||
'name' : 'Henderson',
|
||||
'avatar': 'assets/images/avatars/Henderson.jpg',
|
||||
'status': 'offline',
|
||||
'mood' : 'I can\'t decide if people who wear pajamas in public have given up on life or are living it to the fullest.',
|
||||
},
|
||||
{
|
||||
'id' : '5725a680b8d240c011dd224b',
|
||||
'name' : 'Josefina',
|
||||
'avatar': 'assets/images/avatars/Josefina.jpg',
|
||||
'status': 'online',
|
||||
'mood' : 'The fastest way to being happy is to make other people happy. You go first',
|
||||
},
|
||||
{
|
||||
'id' : '5725a68034cb3968e1f79eac',
|
||||
'name' : 'Katina',
|
||||
'avatar': 'assets/images/avatars/Katina.jpg',
|
||||
'status': 'away',
|
||||
'mood' : 'If I was a rat,,, I wouldn\'t give anyone my ass.',
|
||||
},
|
||||
{
|
||||
'id' : '5725a6801146cce777df2a08',
|
||||
'name' : 'Lily',
|
||||
'avatar': 'assets/images/avatars/Lily.jpg',
|
||||
'status': 'do-not-disturb',
|
||||
'mood' : 'A zip line but from the sofa to the fridge',
|
||||
},
|
||||
{
|
||||
'id' : '5725a6808a178bfd034d6ecf',
|
||||
'name' : 'Mai',
|
||||
'avatar': 'assets/images/avatars/Mai.jpg',
|
||||
'status': 'away',
|
||||
'mood' : 'If a girl tells you she has a nipple ring, the only correct response is \'I don\'t believe you.\'',
|
||||
},
|
||||
{
|
||||
'id' : '5725a680653c265f5c79b5a9',
|
||||
'name' : 'Nancy',
|
||||
'avatar': 'assets/images/avatars/Nancy.jpg',
|
||||
'status': 'do-not-disturb',
|
||||
'mood' : 'Prison counts as a gated community, right?',
|
||||
},
|
||||
{
|
||||
'id' : '5725a680bbcec3cc32a8488a',
|
||||
'name' : 'Nora',
|
||||
'avatar': 'assets/images/avatars/Nora.jpg',
|
||||
'status': 'do-not-disturb',
|
||||
'mood' : 'I never date left handed women. Righty tighty, lefty loosey.',
|
||||
},
|
||||
{
|
||||
'id' : '5725a6803d87f1b77e17b62b',
|
||||
'name' : 'Odessa',
|
||||
'avatar': 'assets/images/avatars/Odessa.jpg',
|
||||
'status': 'away',
|
||||
'mood' : 'A day without sunshine is like, night.',
|
||||
},
|
||||
{
|
||||
'id' : '5725a680e87cb319bd9bd673',
|
||||
'name' : 'Reyna',
|
||||
'avatar': 'assets/images/avatars/Reyna.jpg',
|
||||
'status': 'offline',
|
||||
'mood' : 'I can\'t wait for summer in Canada...',
|
||||
},
|
||||
{
|
||||
'id' : '5725a6802d10e277a0f35775',
|
||||
'name' : 'Shauna',
|
||||
'avatar': 'assets/images/avatars/Shauna.jpg',
|
||||
'status': 'online',
|
||||
'mood' : 'My take home pay doesn’t ven take me home.',
|
||||
'unread': null,
|
||||
},
|
||||
{
|
||||
'id' : '5725a680aef1e5cf26dd3d1f',
|
||||
'name' : 'Shepard',
|
||||
'avatar': 'assets/images/avatars/Shepard.jpg',
|
||||
'status': 'online',
|
||||
'mood' : 'I don\'t speak Spanish, but I\'m pretty sure \'Dora\' means \'annoying\'',
|
||||
},
|
||||
{
|
||||
'id' : '5725a680cd7efa56a45aea5d',
|
||||
'name' : 'Tillman',
|
||||
'avatar': 'assets/images/avatars/Tillman.jpg',
|
||||
'status': 'do-not-disturb',
|
||||
'mood' : '',
|
||||
},
|
||||
{
|
||||
'id' : '5725a680fb65c91a82cb35e2',
|
||||
'name' : 'Trevino',
|
||||
'avatar': 'assets/images/avatars/Trevino.jpg',
|
||||
'status': 'away',
|
||||
'mood' : 'Apparently, a rat and a plastic tube does not count as a DIY abortion kit.',
|
||||
},
|
||||
{
|
||||
'id' : '5725a68018c663044be49cbf',
|
||||
'name' : 'Tyson',
|
||||
'avatar': 'assets/images/avatars/Tyson.jpg',
|
||||
'status': 'do-not-disturb',
|
||||
'mood' : 'I\'m wondering why life keeps teaching me lessons I have no desire to learn...',
|
||||
},
|
||||
{
|
||||
'id' : '5725a6809413bf8a0a5272b1',
|
||||
'name' : 'Velazquez',
|
||||
'avatar': 'assets/images/avatars/Velazquez.jpg',
|
||||
'status': 'online',
|
||||
'mood' : 'Modulation in all things.',
|
||||
}
|
||||
];
|
||||
|
||||
public static chats = [
|
||||
{
|
||||
'id' : '1725a680b3249760ea21de52',
|
||||
'dialog': [
|
||||
{
|
||||
'who' : '5725a680b3249760ea21de52',
|
||||
'message': 'Quickly come to the meeting room 1B, we have a big server issue',
|
||||
'time' : '2017-03-22T08:54:28.299Z'
|
||||
},
|
||||
{
|
||||
'who' : '5725a6802d10e277a0f35724',
|
||||
'message': 'I’m having breakfast right now, can’t you wait for 10 minutes?',
|
||||
'time' : '2017-03-22T08:55:28.299Z'
|
||||
},
|
||||
{
|
||||
'who' : '5725a680b3249760ea21de52',
|
||||
'message': 'We are losing money! Quick!',
|
||||
'time' : '2017-03-22T09:00:28.299Z'
|
||||
},
|
||||
{
|
||||
'who' : '5725a6802d10e277a0f35724',
|
||||
'message': 'It’s not my money, you know. I will eat my breakfast and then I will come to the meeting room.',
|
||||
'time' : '2017-03-22T09:02:28.299Z'
|
||||
},
|
||||
{
|
||||
'who' : '5725a680b3249760ea21de52',
|
||||
'message': 'You are the worst!',
|
||||
'time' : '2017-03-22T09:05:28.299Z'
|
||||
},
|
||||
{
|
||||
'who' : '5725a680b3249760ea21de52',
|
||||
'message': 'We are losing money! Quick!',
|
||||
'time' : '2017-03-22T09:15:28.299Z'
|
||||
},
|
||||
{
|
||||
'who' : '5725a6802d10e277a0f35724',
|
||||
'message': 'It’s not my money, you know. I will eat my breakfast and then I will come to the meeting room.',
|
||||
'time' : '2017-03-22T09:20:28.299Z'
|
||||
},
|
||||
{
|
||||
'who' : '5725a680b3249760ea21de52',
|
||||
'message': 'You are the worst!',
|
||||
'time' : '2017-03-22T09:22:28.299Z'
|
||||
},
|
||||
{
|
||||
'who' : '5725a680b3249760ea21de52',
|
||||
'message': 'We are losing money! Quick!',
|
||||
'time' : '2017-03-22T09:25:28.299Z'
|
||||
},
|
||||
{
|
||||
'who' : '5725a6802d10e277a0f35724',
|
||||
'message': 'It’s not my money, you know. I will eat my breakfast and then I will come to the meeting room.',
|
||||
'time' : '2017-03-22T09:27:28.299Z'
|
||||
},
|
||||
{
|
||||
'who' : '5725a680b3249760ea21de52',
|
||||
'message': 'You are the worst!',
|
||||
'time' : '2017-03-22T09:33:28.299Z'
|
||||
},
|
||||
{
|
||||
'who' : '5725a680b3249760ea21de52',
|
||||
'message': 'We are losing money! Quick!',
|
||||
'time' : '2017-03-22T09:35:28.299Z'
|
||||
},
|
||||
{
|
||||
'who' : '5725a6802d10e277a0f35724',
|
||||
'message': 'It’s not my money, you know. I will eat my breakfast and then I will come to the meeting room.',
|
||||
'time' : '2017-03-22T09:45:28.299Z'
|
||||
},
|
||||
{
|
||||
'who' : '5725a680b3249760ea21de52',
|
||||
'message': 'You are the worst!',
|
||||
'time' : '2017-03-22T10:00:28.299Z'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id' : '2725a680b8d240c011dd2243',
|
||||
'dialog': [
|
||||
{
|
||||
'who' : '5725a680b8d240c011dd224b',
|
||||
'message': 'Quickly come to the meeting room 1B, we have a big server issue',
|
||||
'time' : '2017-04-22T01:00:00.299Z'
|
||||
},
|
||||
{
|
||||
'who' : '5725a6802d10e277a0f35724',
|
||||
'message': 'I’m having breakfast right now, can’t you wait for 10 minutes?',
|
||||
'time' : '2017-04-22T01:05:00.299Z'
|
||||
},
|
||||
{
|
||||
'who' : '5725a680b8d240c011dd224b',
|
||||
'message': 'We are losing money! Quick!',
|
||||
'time' : '2017-04-22T01:10:00.299Z'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id' : '3725a6809413bf8a0a5272b4',
|
||||
'dialog': [
|
||||
{
|
||||
'who' : '5725a6809413bf8a0a5272b1',
|
||||
'message': 'Quickly come to the meeting room 1B, we have a big server issue',
|
||||
'time' : '2017-04-22T02:10:00.299Z'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
public static user = [
|
||||
{
|
||||
'id' : '5725a6802d10e277a0f35724',
|
||||
'name' : 'John Doe',
|
||||
'avatar' : 'assets/images/avatars/profile.jpg',
|
||||
'status' : 'online',
|
||||
'mood' : 'it\'s a status....not your diary...',
|
||||
'chatList': [
|
||||
{
|
||||
'id' : '1725a680b3249760ea21de52',
|
||||
'contactId' : '5725a680b3249760ea21de52',
|
||||
'name' : 'Alice Freeman',
|
||||
'unread' : 4,
|
||||
'lastMessageTime': '2017-06-12T02:10:18.931Z'
|
||||
},
|
||||
{
|
||||
'id' : '2725a680b8d240c011dd2243',
|
||||
'contactId' : '5725a680b8d240c011dd224b',
|
||||
'name' : 'Josefina',
|
||||
'unread' : null,
|
||||
'lastMessageTime': '2017-02-18T10:30:18.931Z'
|
||||
},
|
||||
{
|
||||
'id' : '3725a6809413bf8a0a5272b4',
|
||||
'contactId' : '5725a6809413bf8a0a5272b1',
|
||||
'name' : 'Velazquez',
|
||||
'unread' : 2,
|
||||
'lastMessageTime': '2017-03-18T12:30:18.931Z'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import {InMemoryDbService} from 'angular-in-memory-web-api';
|
||||
import {MailFakeDb} from './mail';
|
||||
import {ChatFakeDb} from './chat';
|
||||
|
||||
export class FuseFakeDbService implements InMemoryDbService
|
||||
{
|
||||
|
@ -7,8 +8,11 @@ export class FuseFakeDbService implements InMemoryDbService
|
|||
{
|
||||
return {
|
||||
'mail-mails' : MailFakeDb.mails,
|
||||
'mail-folders': MailFakeDb.folders,
|
||||
'mail-labels' : MailFakeDb.labels
|
||||
'mail-folders' : MailFakeDb.folders,
|
||||
'mail-labels' : MailFakeDb.labels,
|
||||
'chat-contacts': ChatFakeDb.contacts,
|
||||
'chat-chats' : ChatFakeDb.chats,
|
||||
'chat-user' : ChatFakeDb.user,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
17
src/app/main/apps/chat/chat-start/chat-start.component.html
Normal file
17
src/app/main/apps/chat/chat-start/chat-start.component.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
<div fxFlex fxLayout="column" fxLayoutAlign="center center">
|
||||
|
||||
<div class="big-circle mat-elevation-z1" fxLayout="column" fxLayoutAlign="center center">
|
||||
|
||||
<md-icon class="s-128">chat</md-icon>
|
||||
|
||||
</div>
|
||||
|
||||
<span class="app-title my-24">Chat App</span>
|
||||
|
||||
<span fxHide fxShow.gt-md class="app-message">Select contact to start the chat!..</span>
|
||||
|
||||
<button md-raised-button fxHide.gt-md class="" fuseMdSidenavToggler="chat-left-sidenav">
|
||||
Select contact to start the chat!..
|
||||
</button>
|
||||
|
||||
</div>
|
30
src/app/main/apps/chat/chat-start/chat-start.component.scss
Normal file
30
src/app/main/apps/chat/chat-start/chat-start.component.scss
Normal file
|
@ -0,0 +1,30 @@
|
|||
@import "src/app/core/scss/fuse";
|
||||
|
||||
:host {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.6) 20%, rgba(255, 255, 255, 0.8));
|
||||
|
||||
.big-circle {
|
||||
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.6) 20%, rgba(255, 255, 255, 0.8));
|
||||
border-radius: 50%;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
line-height: 300px;
|
||||
text-align: center;
|
||||
|
||||
md-icon{
|
||||
color: mat-color($accent);
|
||||
}
|
||||
}
|
||||
|
||||
.app-title {
|
||||
font-weight: 500;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.secondary-text {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
19
src/app/main/apps/chat/chat-start/chat-start.component.ts
Normal file
19
src/app/main/apps/chat/chat-start/chat-start.component.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector : 'fuse-chat-start',
|
||||
templateUrl: './chat-start.component.html',
|
||||
styleUrls : ['./chat-start.component.scss']
|
||||
})
|
||||
export class ChatStartComponent implements OnInit
|
||||
{
|
||||
|
||||
constructor()
|
||||
{
|
||||
}
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
115
src/app/main/apps/chat/chat-view/chat-view.component.html
Normal file
115
src/app/main/apps/chat/chat-view/chat-view.component.html
Normal file
|
@ -0,0 +1,115 @@
|
|||
<!-- CHAT -->
|
||||
<div class="chat" fxFlex fxLayout="column">
|
||||
|
||||
<!-- CHAT TOOLBAR -->
|
||||
<md-toolbar class="chat-toolbar">
|
||||
|
||||
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center">
|
||||
|
||||
<div fxLayout="row" fxLayoutAlign="start center">
|
||||
|
||||
<!-- RESPONSIVE CHATS BUTTON-->
|
||||
<div md-button fxHide.gt-md class="responsive-chats-button mat-icon-button mr-16"
|
||||
fuseMdSidenavToggler="chat-left-sidenav"
|
||||
aria-label="chats button">
|
||||
<md-icon class="s-36">chat</md-icon>
|
||||
</div>
|
||||
<!-- / RESPONSIVE CHATS BUTTON-->
|
||||
|
||||
<!-- CHAT CONTACT-->
|
||||
<div class="chat-contact" fxLayout="row" fxLayoutAlign="start center"
|
||||
fuseMdSidenavToggler="chat-right-sidenav" (click)="selectContact()">
|
||||
|
||||
<div class="avatar-wrapper">
|
||||
|
||||
<img [src]="contact.avatar"
|
||||
class="avatar"
|
||||
alt="{{contact.name}}"/>
|
||||
|
||||
<md-icon class="s-16 status"
|
||||
[ngClass]="contact.status">
|
||||
</md-icon>
|
||||
</div>
|
||||
|
||||
<div class="chat-contact-name">
|
||||
{{contact.name}}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- / CHAT CONTACT-->
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button md-button class="mat-icon-button" [mdMenuTriggerFor]="contactMenu"
|
||||
aria-label="more">
|
||||
<md-icon>more_vert</md-icon>
|
||||
</button>
|
||||
|
||||
<md-menu #contactMenu="mdMenu">
|
||||
<button md-menu-item fuseMdSidenavToggler="chat-right-sidenav" (click)="selectContact()">
|
||||
Contact Info
|
||||
</button>
|
||||
</md-menu>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<!-- / CHAT TOOLBAR -->
|
||||
|
||||
<!-- CHAT CONTENT -->
|
||||
<div id="chat-content" fxFlex perfect-scrollbar>
|
||||
|
||||
<!-- CHAT MESSAGES -->
|
||||
<div class="chat-messages">
|
||||
|
||||
<!-- MESSAGE -->
|
||||
<div fxLayout="row" *ngFor="let message of dialog" class="message-row"
|
||||
[ngClass]="{'user' :message.who === user.id}">
|
||||
|
||||
<img *ngIf="message.who === contact.id"
|
||||
src="{{contact.avatar}}"
|
||||
class="avatar"
|
||||
alt="{{contact.name}}"/>
|
||||
|
||||
<img *ngIf="message.who ===user.id" class="avatar" src="{{user.avatar}}">
|
||||
|
||||
<div class="bubble">
|
||||
<div class="message">{{message.message}}</div>
|
||||
<div class="time secondary-text">{{message.time | date:'medium'}}</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- / MESSAGE -->
|
||||
|
||||
</div>
|
||||
<!-- CHAT MESSAGES -->
|
||||
|
||||
</div>
|
||||
<!-- / CHAT CONTENT -->
|
||||
|
||||
<!-- CHAT FOOTER -->
|
||||
<div class="chat-footer" fxLayout="row" fxLayoutAlign="center center">
|
||||
|
||||
<!-- REPLY FORM -->
|
||||
<form #replyForm="ngForm"
|
||||
(ngSubmit)="reply($event)"
|
||||
(keyup.enter)="reply($event)"
|
||||
fxFlex class="reply-form"
|
||||
fxLayout="row"
|
||||
fxLayoutAlign="start center">
|
||||
|
||||
<md-input-container class="" fxFlex floatPlaceholder="never">
|
||||
<textarea mdInput #replyInput placeholder="Type and hit enter to send message"
|
||||
ngModel name="message"></textarea>
|
||||
</md-input-container>
|
||||
|
||||
<button md-fab class="" type="submit" aria-label="Send message">
|
||||
<md-icon>send</md-icon>
|
||||
</button>
|
||||
|
||||
</form>
|
||||
<!-- / REPLY FORM -->
|
||||
</div>
|
||||
<!-- / CHAT FOOTER-->
|
||||
</div>
|
||||
<!-- / CHAT -->
|
135
src/app/main/apps/chat/chat-view/chat-view.component.scss
Normal file
135
src/app/main/apps/chat/chat-view/chat-view.component.scss
Normal file
|
@ -0,0 +1,135 @@
|
|||
:host {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.6) 20%, rgba(255, 255, 255, 0.8));
|
||||
overflow: hidden;
|
||||
|
||||
.chat {
|
||||
height: 100%;
|
||||
|
||||
.chat-toolbar {
|
||||
min-height: 64px;
|
||||
background-color: #F3F4F5;
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
border-bottom: 1px solid rgba(0, 0, 0, .08);
|
||||
|
||||
.responsive-chats-button {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.chat-contact {
|
||||
cursor: pointer;
|
||||
|
||||
.avatar {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.chat-contact-name {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#chat-content {
|
||||
background: transparent;
|
||||
|
||||
.message-row {
|
||||
padding: 16px;
|
||||
|
||||
.bubble {
|
||||
position: relative;
|
||||
padding: 6px 7px 8px 9px;
|
||||
background-color: #FFF;
|
||||
box-shadow: 0 1px .5px rgba(0, 0, 0, .13);
|
||||
border-radius: 6px;
|
||||
|
||||
&:before {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAmCAMAAADp2asXAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAADGUExURQAAAP////b29vn5+f///wAAAP///wAAAAAAAP///9ra2v////j4+PHx8fv7++Hh4fHx8f////////////////39/QAAAP////////z8/P////39/f39/fz8/P////////////z8/P////////////z8/P////////////v7+/Hx8f///9bW1vz8/K2trf////39/f39/WJiYgAAAExMTFtbWwAAAN3d3cjIyPr6+vX19QAAAO7u7vz8/NTU1Ofn5zMzM////zGPlXsAAABBdFJOUwAcm/kREh4CCDWL1SneR6TfAQffhMYK/A5nRrLWfRc5DW2ih5f+19Kn+9v4g/1LCJuXHwQUKgahcXS6DNnlDMMKKzPoTgAAAKBJREFUKM+V08USwmAQA+C/0NIWd3d3d8/7vxTMcIPkQK7f7CG7s8bQAOY/SCuwFYQU1P+eiCqIK2gpWCmoCrAgoKQgJ8CHgIqAMjg0MxxSQ3DogEMWFBZtUPAHYGB1CyDQWE6AH7BrfXzlAxGAQhECTGAmwN1Okz0Gb/LW4fEItIfrOfNELMh3tck7u+PhcT2zQ7l77/K8iY8yJwV3BeYFqpc/uSyPGdAAAAAASUVORK5CYII=);
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -11px;
|
||||
bottom: 3px;
|
||||
width: 12px;
|
||||
height: 19px;
|
||||
background-position: 50% 50%;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.message {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 11px;
|
||||
margin-top: 8px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
&.contact {
|
||||
|
||||
.avatar {
|
||||
margin: 0 16px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.user {
|
||||
align-items: flex-end;
|
||||
|
||||
.avatar {
|
||||
order: 2;
|
||||
margin: 0 0 0 16px;
|
||||
}
|
||||
|
||||
.bubble {
|
||||
margin-left: auto;
|
||||
background-color: #E8F5E9;
|
||||
border: 1px solid #DFEBE0;
|
||||
order: 1;
|
||||
&:before {
|
||||
right: -11px;
|
||||
left: auto;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAmCAMAAADp2asXAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAD2UExURQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRsXAAAANzwzNPmxNrtyau5oIWRedDkwNntyczgwdfpyJ+/n97wzsLWtNjsytvwzczfvtPmxau6nNjqxtrtyio1KtzwzNjryAAAANzwzgAAANzwzK7Aor/Us9Lnw8vevAAAAMzevtbpxrvMrX+IdwAAAEROOi45Lr3MrZGjf9LoxX+MctnqydLkwhgYGMzfv9vuyQAAANzwzNvuy9zxy7vMu7XGqNvtzKKykwAAANruzKq6nLnMriQkGMXXuL3PsNjsySgzKAAAANLkw83fvd3vy9z4xtzwzRpFmIEAAABQdFJOUwAXChEGBAMBAgwhDvJ7k0YqMc0Zmwj6apf2kjU0+dkw/swh/CP9j2Wr2gndvaYeBRoxQg6gUPt/FaHJGdTj9A9k7XQLeE6iFcN12xkSt9r4NKizowAAAMFJREFUKM+V0sdywlAMBVDbMX7PQCihQ+iQ0HsJvfem/P/PwBIzugu0PXNnNNJVyPmhsIPhhoB2COwIGuLdhAcl3AhCBoBoHUC6BCBbA0C/EkBFB5D/FjxQwQYg1RI8UKINgDoSAPUlAPqUAMgfAEBfXsEDBV0+Hogi4Zhg4THj9YwHoqEBYOrgYTI3GVgMNn8r+Qq94k9yZNosW/3Hy9VuTjWfHkOX6367bGZUU7de66ieHZrO1OGg8Z1WTgYAFLgD5S1PCkzo1B0AAAAASUVORK5CYII=);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-footer {
|
||||
min-height: 64px;
|
||||
max-height: 96px;
|
||||
background-color: #F3F4F5;
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
border-top: 1px solid rgba(0, 0, 0, .08);
|
||||
padding: 8px 8px 8px 16px;
|
||||
|
||||
.reply-form {
|
||||
|
||||
md-input-container {
|
||||
margin: 0;
|
||||
padding-right: 16px;
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
max-height: 80px;
|
||||
transition: height 200ms ease;
|
||||
&.grow {
|
||||
height: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
.md-errors-spacer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.md-button {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
101
src/app/main/apps/chat/chat-view/chat-view.component.ts
Normal file
101
src/app/main/apps/chat/chat-view/chat-view.component.ts
Normal file
|
@ -0,0 +1,101 @@
|
|||
import { AfterViewInit, Component, OnInit, ViewChild, ViewChildren } from '@angular/core';
|
||||
import { ChatService } from '../chat.service';
|
||||
import { NgForm } from '@angular/forms';
|
||||
import { PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
|
||||
|
||||
@Component({
|
||||
selector : 'fuse-chat-view',
|
||||
templateUrl: './chat-view.component.html',
|
||||
styleUrls : ['./chat-view.component.scss']
|
||||
})
|
||||
export class ChatViewComponent implements OnInit, AfterViewInit
|
||||
{
|
||||
user: any;
|
||||
chat: any;
|
||||
dialog: any;
|
||||
contact: any;
|
||||
replyInput: any;
|
||||
selectedChat: any;
|
||||
@ViewChild(PerfectScrollbarDirective) directiveScroll: PerfectScrollbarDirective;
|
||||
@ViewChildren('replyInput') replyInputField;
|
||||
@ViewChild('replyForm') replyForm: NgForm;
|
||||
|
||||
constructor(private chatService: ChatService)
|
||||
{
|
||||
}
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
this.user = this.chatService.user;
|
||||
this.chatService.onChatSelected
|
||||
.subscribe(chatData => {
|
||||
if ( chatData )
|
||||
{
|
||||
this.selectedChat = chatData;
|
||||
this.contact = chatData.contact;
|
||||
this.dialog = chatData.dialog;
|
||||
this.readyToReply();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterViewInit()
|
||||
{
|
||||
this.replyInput = this.replyInputField.first.nativeElement;
|
||||
this.readyToReply();
|
||||
}
|
||||
|
||||
selectContact()
|
||||
{
|
||||
this.chatService.selectContact(this.contact);
|
||||
}
|
||||
|
||||
readyToReply()
|
||||
{
|
||||
setTimeout(() => {
|
||||
this.replyForm.reset();
|
||||
this.focusReplyInput();
|
||||
this.scrollToBottom();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
focusReplyInput()
|
||||
{
|
||||
setTimeout(() => {
|
||||
this.replyInput.focus();
|
||||
});
|
||||
}
|
||||
|
||||
scrollToBottom(speed?: number)
|
||||
{
|
||||
speed = speed || 400;
|
||||
if ( this.directiveScroll )
|
||||
{
|
||||
setTimeout(() => {
|
||||
this.directiveScroll.scrollToBottom(0, speed);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
reply(event)
|
||||
{
|
||||
// Message
|
||||
const message = {
|
||||
who : this.user.id,
|
||||
message: this.replyForm.form.value.message,
|
||||
time : new Date().toISOString()
|
||||
};
|
||||
|
||||
// Add the message to the chat
|
||||
this.dialog.push(message);
|
||||
|
||||
// Update the server
|
||||
this.chatService.updateDialog(this.selectedChat.chatId, this.dialog).then(response => {
|
||||
this.readyToReply();
|
||||
});
|
||||
|
||||
}
|
||||
}
|
|
@ -1,3 +1,43 @@
|
|||
<p>
|
||||
chat works!
|
||||
</p>
|
||||
<div class="page-layout carded fullwidth">
|
||||
|
||||
<!-- TOP BACKGROUND -->
|
||||
<div class="top-bg md-accent-bg"></div>
|
||||
<!-- / TOP BACKGROUND -->
|
||||
|
||||
<!-- CENTER -->
|
||||
<div class="center">
|
||||
|
||||
<!-- CONTENT CARD -->
|
||||
<div class="content-card">
|
||||
|
||||
<md-sidenav-container>
|
||||
|
||||
<!-- LEFT SIDENAV -->
|
||||
<md-sidenav class="sidenav mat-sidenav-opened" align="start" opened="true" mode="side"
|
||||
fuseMdSidenavHelper="chat-left-sidenav" md-is-locked-open="gt-md">
|
||||
<fuse-chat-left-sidenav></fuse-chat-left-sidenav>
|
||||
</md-sidenav>
|
||||
<!-- / LEFT SIDENAV -->
|
||||
|
||||
<!-- CONTENT -->
|
||||
<fuse-chat-start *ngIf="!selectedChat"></fuse-chat-start>
|
||||
|
||||
<fuse-chat-view *ngIf="selectedChat"></fuse-chat-view>
|
||||
<!-- / CONTENT -->
|
||||
|
||||
<!-- RIGHT SIDENAV -->
|
||||
<md-sidenav class="sidenav mat-sidenav-opened" align="end" opened="false" mode="over"
|
||||
fuseMdSidenavHelper="chat-right-sidenav">
|
||||
<fuse-chat-right-sidenav></fuse-chat-right-sidenav>
|
||||
</md-sidenav>
|
||||
<!-- / RIGHT SIDENAV -->
|
||||
|
||||
</md-sidenav-container>
|
||||
|
||||
</div>
|
||||
<!-- / CONTENT CARD -->
|
||||
|
||||
</div>
|
||||
<!-- / CENTER -->
|
||||
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
:host {
|
||||
height: 100% !important;
|
||||
|
||||
.center {
|
||||
padding: 32px 32px 0 32px;
|
||||
max-width: 1400px;
|
||||
height: 100%;
|
||||
margin: 0 auto;
|
||||
|
||||
.content-card {
|
||||
position: relative;
|
||||
background: url('/assets/images/patterns/rain-grey.png') repeat;
|
||||
height: 100%;
|
||||
|
||||
.mat-sidenav-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
|
||||
md-sidenav {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 400px;
|
||||
max-width: 90%;
|
||||
box-shadow: 0 0 1px rgba(0, 0, 0, .37);
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,25 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { ChatService } from './chat.service';
|
||||
|
||||
@Component({
|
||||
selector: 'fuse-chat',
|
||||
templateUrl: './chat.component.html',
|
||||
styleUrls: ['./chat.component.scss']
|
||||
selector : 'fuse-chat',
|
||||
templateUrl: './chat.component.html',
|
||||
styleUrls : ['./chat.component.scss']
|
||||
})
|
||||
export class ChatComponent implements OnInit {
|
||||
export class ChatComponent implements OnInit
|
||||
{
|
||||
selectedChat: any;
|
||||
|
||||
constructor() { }
|
||||
constructor(private chatService: ChatService)
|
||||
{
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
ngOnInit()
|
||||
{
|
||||
this.chatService.onChatSelected
|
||||
.subscribe(chatData => {
|
||||
this.selectedChat = chatData;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,10 +2,21 @@ import {NgModule} from '@angular/core';
|
|||
import {SharedModule} from '../../../core/modules/shared.module';
|
||||
import {RouterModule, Routes} from '@angular/router';
|
||||
import {ChatComponent} from './chat.component';
|
||||
import {ChatService} from './chat.service';
|
||||
import { ChatViewComponent } from './chat-view/chat-view.component';
|
||||
import { ChatStartComponent } from './chat-start/chat-start.component';
|
||||
import {ChatsSidenavComponent} from './sidenavs/left/chats/chats.component';
|
||||
import { UserSidenavComponent } from './sidenavs/left/user/user.component';
|
||||
import { LeftSidenavComponent } from './sidenavs/left/left.component';
|
||||
import { RightSidenavComponent } from './sidenavs/right/right.component';
|
||||
import { ContactSidenavComponent } from './sidenavs/right/contact/contact.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: 'apps/chat', component: ChatComponent, children: []
|
||||
path : 'apps/chat', component: ChatComponent, children: [],
|
||||
resolve: {
|
||||
chat: ChatService
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -15,7 +26,17 @@ const routes: Routes = [
|
|||
RouterModule.forChild(routes)
|
||||
],
|
||||
declarations: [
|
||||
ChatComponent
|
||||
ChatComponent,
|
||||
ChatViewComponent,
|
||||
ChatStartComponent,
|
||||
ChatsSidenavComponent,
|
||||
UserSidenavComponent,
|
||||
LeftSidenavComponent,
|
||||
RightSidenavComponent,
|
||||
ContactSidenavComponent
|
||||
],
|
||||
providers : [
|
||||
ChatService
|
||||
]
|
||||
})
|
||||
export class ChatModule
|
||||
|
|
259
src/app/main/apps/chat/chat.service.ts
Normal file
259
src/app/main/apps/chat/chat.service.ts
Normal file
|
@ -0,0 +1,259 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Http } from '@angular/http';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
|
||||
@Injectable()
|
||||
export class ChatService implements Resolve<any>
|
||||
{
|
||||
contacts: any[];
|
||||
chats: any[];
|
||||
user: any;
|
||||
onChatSelected = new BehaviorSubject<any>(null);
|
||||
onContactSelected = new BehaviorSubject<any>(null);
|
||||
onChatsUpdated = new Subject<any>();
|
||||
onUserUpdated = new Subject<any>();
|
||||
onLeftSidenavViewChanged = new Subject<any>();
|
||||
onRightSidenavViewChanged = new Subject<any>();
|
||||
|
||||
constructor(private http: Http)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get chat
|
||||
* @param contactId
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
getChat(contactId)
|
||||
{
|
||||
const chatItem = this.user.chatList.find((item) => {
|
||||
return item.contactId === contactId;
|
||||
});
|
||||
|
||||
/**
|
||||
* Create new chat, if it's not created yet.
|
||||
*/
|
||||
if ( !chatItem )
|
||||
{
|
||||
this.createNewChat(contactId).then((newChats) => {
|
||||
this.getChat(contactId);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.http.get('api/chat-chats/' + chatItem.id)
|
||||
.subscribe(response => {
|
||||
const chat = response.json().data;
|
||||
|
||||
const chatContact = this.contacts.find((contact) => {
|
||||
return contact.id === contactId;
|
||||
});
|
||||
|
||||
const chatData = {
|
||||
chatId : chat.id,
|
||||
dialog : chat.dialog,
|
||||
contact: chatContact
|
||||
};
|
||||
|
||||
this.onChatSelected.next({...chatData});
|
||||
|
||||
}, reject);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create New Chat
|
||||
* @param contactId
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
createNewChat(contactId)
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
const contact = this.contacts.find((item) => {
|
||||
return item.id === contactId;
|
||||
});
|
||||
|
||||
const chatId = this.guidGenerator();
|
||||
|
||||
const chat = {
|
||||
id : chatId,
|
||||
dialog: []
|
||||
};
|
||||
|
||||
const chatListItem = {
|
||||
'id' : chatId,
|
||||
'contactId' : contactId,
|
||||
'unread' : null,
|
||||
'lastMessageTime': '2017-02-18T10:30:18.931Z'
|
||||
};
|
||||
|
||||
/**
|
||||
* Add new chat list item to the user's chat list
|
||||
*/
|
||||
this.user.chatList.push(chatListItem);
|
||||
|
||||
/**
|
||||
* Post the created chat
|
||||
*/
|
||||
this.http.post('api/chat-chats', {...chat})
|
||||
.subscribe(response => {
|
||||
|
||||
/**
|
||||
* Post the new the user data
|
||||
*/
|
||||
this.http.post('api/chat-user/' + this.user.id, this.user)
|
||||
.subscribe(addedlistItem => {
|
||||
|
||||
/**
|
||||
* Update the user data from server
|
||||
*/
|
||||
this.getUser().then(updatedUser => {
|
||||
this.onUserUpdated.next(updatedUser);
|
||||
resolve(updatedUser);
|
||||
});
|
||||
});
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Select Contact
|
||||
* @param contact
|
||||
*/
|
||||
selectContact(contact)
|
||||
{
|
||||
this.onContactSelected.next(contact);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set user status
|
||||
* @param status
|
||||
*/
|
||||
setUserStatus(status)
|
||||
{
|
||||
this.user.status = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update user data
|
||||
* @param userData
|
||||
*/
|
||||
updateUserData(userData)
|
||||
{
|
||||
this.http.post('api/chat-user/' + this.user.id, userData)
|
||||
.subscribe(response => {
|
||||
this.user = userData;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the chat dialog
|
||||
* @param chatId
|
||||
* @param dialog
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
updateDialog(chatId, dialog): Promise<any>
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
const newData = {
|
||||
id : chatId,
|
||||
dialog: dialog
|
||||
};
|
||||
|
||||
this.http.post('api/chat-chats/' + chatId, newData)
|
||||
.subscribe(updatedChat => {
|
||||
resolve(updatedChat);
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The Mail App Main Resolver
|
||||
* @param {ActivatedRouteSnapshot} route
|
||||
* @param {RouterStateSnapshot} state
|
||||
* @returns {Observable<any> | Promise<any> | any}
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
Promise.all([
|
||||
this.getContacts(),
|
||||
this.getChats(),
|
||||
this.getUser()
|
||||
]).then(
|
||||
([contacts, chats, user]) => {
|
||||
this.contacts = contacts;
|
||||
this.chats = chats;
|
||||
this.user = user;
|
||||
resolve();
|
||||
},
|
||||
reject
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Contacts
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
getContacts(): Promise<any>
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
this.http.get('api/chat-contacts')
|
||||
.subscribe(response => {
|
||||
resolve(response.json().data);
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Chats
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
getChats(): Promise<any>
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
this.http.get('api/chat-chats')
|
||||
.subscribe(response => {
|
||||
resolve(response.json().data);
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get User
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
getUser(): Promise<any>
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
this.http.get('api/chat-user')
|
||||
.subscribe(response => {
|
||||
resolve(response.json().data[0]);
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Random ID Generator
|
||||
* @returns {string}
|
||||
*/
|
||||
guidGenerator()
|
||||
{
|
||||
function S4()
|
||||
{
|
||||
return (((1 + Math.random()) * 0x10000) || 0).toString(16).substring(1);
|
||||
}
|
||||
|
||||
return (S4() + S4());
|
||||
}
|
||||
}
|
183
src/app/main/apps/chat/sidenavs/left/chats/chats.component.html
Normal file
183
src/app/main/apps/chat/sidenavs/left/chats/chats.component.html
Normal file
|
@ -0,0 +1,183 @@
|
|||
<!-- SIDENAV HEADER -->
|
||||
<div class="sidenav-header">
|
||||
<!-- CHATS TOOLBAR -->
|
||||
<md-toolbar>
|
||||
|
||||
<!-- TOOLBAR TOP -->
|
||||
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center">
|
||||
|
||||
<!-- USER AVATAR WRAPPER -->
|
||||
<div class="avatar-wrapper">
|
||||
|
||||
<!-- USER AVATAR -->
|
||||
<img (click)="changeLeftSidenavView('user')"
|
||||
src="{{user.avatar}}"
|
||||
class="md-avatar avatar"
|
||||
alt="{{user.name}}"/>
|
||||
<!-- / USER AVATAR -->
|
||||
|
||||
<md-icon class="s-16 status" [ngClass]="user.status" [mdMenuTriggerFor]="userStatusMenu"></md-icon>
|
||||
|
||||
<!-- USER STATUS -->
|
||||
<md-menu id="user-status-menu" #userStatusMenu="mdMenu">
|
||||
|
||||
<button md-menu-item (click)="setUserStatus('online')">
|
||||
<div fxLayout="row" fxLayoutAlign="start center">
|
||||
<md-icon class="s-16 status online"></md-icon>
|
||||
<span>Online</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button md-menu-item (click)="setUserStatus('away')">
|
||||
<div fxLayout="row" fxLayoutAlign="start center">
|
||||
<md-icon class="s-16 status away"></md-icon>
|
||||
<span>Away</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button md-menu-item (click)="setUserStatus('do-not-disturb')">
|
||||
<div fxLayout="row" fxLayoutAlign="start center">
|
||||
<md-icon class="s-16 status do-not-disturb"></md-icon>
|
||||
<span>Do not disturb</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button md-menu-item (click)="setUserStatus('offline')">
|
||||
<div fxLayout="row" fxLayoutAlign="start center">
|
||||
<md-icon class="s-16 status offline"></md-icon>
|
||||
<span>Offline</span>
|
||||
</div>
|
||||
</button>
|
||||
</md-menu>
|
||||
<!-- / USER STATUS -->
|
||||
|
||||
</div>
|
||||
<!-- / USER AVATAR -->
|
||||
|
||||
<div>
|
||||
<button md-button class="mat-icon-button"
|
||||
[mdMenuTriggerFor]="userMenu"
|
||||
aria-label="more">
|
||||
<md-icon>more_vert</md-icon>
|
||||
</button>
|
||||
<md-menu #userMenu="mdMenu">
|
||||
<button md-menu-item (click)="changeLeftSidenavView('user')">
|
||||
Profile
|
||||
</button>
|
||||
<button md-menu-item (click)="logout()">
|
||||
Logout
|
||||
</button>
|
||||
</md-menu>
|
||||
</div>
|
||||
</div>
|
||||
<!-- / TOOLBAR TOP -->
|
||||
|
||||
<!-- TOOLBAR BOTTOM -->
|
||||
<md-toolbar-row>
|
||||
|
||||
<!-- SEARCH -->
|
||||
<div class="search-wrapper" fxFlex fxLayout="row" fxLayoutAlign="start center">
|
||||
|
||||
<div class="search" fxFlex fxLayout="row" fxLayoutAlign="start center">
|
||||
|
||||
<md-icon>search</md-icon>
|
||||
|
||||
<input [(ngModel)]="chatSearch.name" type="text" placeholder="Search or start new chat" fxFlex>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- / SEARCH -->
|
||||
|
||||
</md-toolbar-row>
|
||||
<!-- / TOOLBAR BOTTOM -->
|
||||
|
||||
</md-toolbar>
|
||||
<!-- / CHATS TOOLBAR -->
|
||||
|
||||
</div>
|
||||
<!-- / SIDENAV HEADER -->
|
||||
|
||||
<!-- SIDENAV CONTENT -->
|
||||
<div class="sidenav-content" perfect-scrollbar fxFlex>
|
||||
|
||||
<!-- CHATS CONTENT -->
|
||||
<div>
|
||||
|
||||
<!-- CHATS LIST-->
|
||||
<div class="chat-list" fxLayout="column">
|
||||
|
||||
<div md-subheader *ngIf="(user.chatList | filterBy: chatSearch).length > 0">
|
||||
Chats
|
||||
</div>
|
||||
|
||||
<button md-button class="contact"
|
||||
*ngFor="let chat of user.chatList | filterBy: chatSearch"
|
||||
(click)="getChat(chat.contactId)" ngClass="{'unread':contact.unread}">
|
||||
|
||||
<div fxLayout="row" fxLayoutAlign="start center">
|
||||
|
||||
<div class="avatar-wrapper" fxFlex="0 1 auto" fxLayoutAlign="center center">
|
||||
<img [src]="contacts |getById:chat.contactId:'avatar'"
|
||||
class="avatar"
|
||||
alt="{{contacts |getById:chat.contactId:'name'}}"/>
|
||||
<md-icon class="s-16 status" [ngClass]="contacts |getById:chat.contactId:'status'"></md-icon>
|
||||
</div>
|
||||
|
||||
<div fxLayout="row" fxFlex>
|
||||
|
||||
<div class="" fxFlex fxLayout="column" fxLayoutAlign="center start">
|
||||
<div class="contact-name">{{contacts |getById:chat.contactId:'name'}}</div>
|
||||
<p class="contact-last-message text-truncate">{{chat.lastMessage}}</p>
|
||||
</div>
|
||||
|
||||
<div fxLayout="column" fxLayoutAlign="center end">
|
||||
<div class="contact-last-message-time">
|
||||
{{chat.lastMessageTime | date}}
|
||||
</div>
|
||||
<div *ngIf="chat.unread" class="unread-message-count">{{chat.unread}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
<!-- / CHATS LIST-->
|
||||
|
||||
<!-- CONTACTS LIST-->
|
||||
<div class="contact-list" fxLayout="column">
|
||||
|
||||
<div md-subheader *ngIf="(contacts| filterBy: chatSearch).length > 0">
|
||||
Contacts
|
||||
</div>
|
||||
|
||||
<button md-button class="contact"
|
||||
ng-show="chatSearch"
|
||||
*ngFor="let contact of contacts| filterBy: chatSearch"
|
||||
(click)="getChat(contact.id)">
|
||||
|
||||
<div fxLayout="row" fxLayoutAlign="start center">
|
||||
<div class="avatar-wrapper" fxFlex="0 1 auto">
|
||||
<img src="{{contact.avatar}}" class="md-avatar avatar" alt="{{contact.name}}"/>
|
||||
<md-icon class="s-16 status" [ngClass]="contact.status"></md-icon>
|
||||
</div>
|
||||
|
||||
<div class="" fxLayout="column" fxLayoutAlign="center start">
|
||||
<div class="contact-name">{{contact.name}}</div>
|
||||
<p class="contact-mood">{{contact.mood}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<!-- / CONTACTS LIST-->
|
||||
|
||||
<!-- NO RESULTS MESSAGE -->
|
||||
<div *ngIf="(contacts| filterBy: chatSearch).length === 0" class="no-results-message">
|
||||
No results..
|
||||
</div>
|
||||
<!-- NO RESULTS MESSAGE-->
|
||||
|
||||
</div>
|
||||
<!-- / CHATS CONTENT -->
|
||||
|
||||
</div>
|
||||
<!-- / SIDENAV CONTENT -->
|
110
src/app/main/apps/chat/sidenavs/left/chats/chats.component.scss
Normal file
110
src/app/main/apps/chat/sidenavs/left/chats/chats.component.scss
Normal file
|
@ -0,0 +1,110 @@
|
|||
@import "src/app/core/scss/fuse";
|
||||
|
||||
:host {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
|
||||
.sidenav-header {
|
||||
|
||||
md-toolbar {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, .08);
|
||||
|
||||
.avatar-wrapper {
|
||||
|
||||
.avatar, .status {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.search {
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
padding: 8px;
|
||||
background: #FFFFFF;
|
||||
font-size: 13px;
|
||||
@include mat-elevation(1);
|
||||
|
||||
.icon {
|
||||
margin: 0;
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
}
|
||||
|
||||
input {
|
||||
padding-left: 12px;
|
||||
height: 36px;
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sidenav-content {
|
||||
.contact-list, .chat-list {
|
||||
|
||||
.mat-subheader {
|
||||
padding-left: 16px;
|
||||
font-size: 20px;
|
||||
font-weight: 300;
|
||||
height: 88px;
|
||||
line-height: 88px;
|
||||
color: mat-color($accent);
|
||||
}
|
||||
|
||||
.contact {
|
||||
white-space: normal;
|
||||
text-align: left;
|
||||
letter-spacing: .010em;
|
||||
min-height: 88px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
|
||||
padding: 16px;
|
||||
font-weight: 400;
|
||||
|
||||
.avatar-wrapper {
|
||||
|
||||
.avatar {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.contact-name {
|
||||
font-size: 16px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.contact-last-message {
|
||||
line-height: 1.6em;
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
}
|
||||
|
||||
.contact-mood {
|
||||
|
||||
}
|
||||
|
||||
.unread-message-count {
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
background-color: mat-color($accent);
|
||||
color: map-get($accent, default-contrast);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.no-results-message {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 88px;
|
||||
padding: 16px;
|
||||
background: #FFFFFF;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { ChatService } from '../../../chat.service';
|
||||
import { FuseMdSidenavHelperService } from '../../../../../../core/directives/md-sidenav-helper/md-sidenav-helper.service';
|
||||
import { ObservableMedia } from '@angular/flex-layout';
|
||||
|
||||
@Component({
|
||||
selector : 'fuse-chat-chats-sidenav',
|
||||
templateUrl: './chats.component.html',
|
||||
styleUrls : ['./chats.component.scss']
|
||||
})
|
||||
export class ChatsSidenavComponent implements OnInit
|
||||
{
|
||||
user: any;
|
||||
chats: any[];
|
||||
contacts: any[];
|
||||
chatSearch: any;
|
||||
|
||||
constructor(private chatService: ChatService,
|
||||
private fuseMdSidenavService: FuseMdSidenavHelperService,
|
||||
public media: ObservableMedia)
|
||||
{
|
||||
this.chatSearch = {
|
||||
name: ''
|
||||
};
|
||||
}
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
this.user = this.chatService.user;
|
||||
this.chats = this.chatService.chats;
|
||||
this.contacts = this.chatService.contacts;
|
||||
|
||||
this.chatService.onChatsUpdated.subscribe(updatedChats => {
|
||||
this.chats = updatedChats;
|
||||
});
|
||||
|
||||
this.chatService.onUserUpdated.subscribe(updatedUser => {
|
||||
this.user = updatedUser;
|
||||
});
|
||||
}
|
||||
|
||||
getChat(contact)
|
||||
{
|
||||
this.chatService.getChat(contact);
|
||||
|
||||
if ( !this.media.isActive('gt-md') )
|
||||
{
|
||||
this.fuseMdSidenavService.getSidenav('chat-left-sidenav').toggle();
|
||||
}
|
||||
}
|
||||
|
||||
setUserStatus(status)
|
||||
{
|
||||
this.chatService.setUserStatus(status);
|
||||
}
|
||||
|
||||
changeLeftSidenavView(view)
|
||||
{
|
||||
this.chatService.onLeftSidenavViewChanged.next(view);
|
||||
}
|
||||
|
||||
logout()
|
||||
{
|
||||
console.info('logout triggered');
|
||||
}
|
||||
}
|
11
src/app/main/apps/chat/sidenavs/left/left.component.html
Normal file
11
src/app/main/apps/chat/sidenavs/left/left.component.html
Normal file
|
@ -0,0 +1,11 @@
|
|||
<div [ngSwitch]="view" class="views">
|
||||
<fuse-chat-chats-sidenav class="view"
|
||||
*ngSwitchCase="'chats'"
|
||||
[@slideInRight]>
|
||||
</fuse-chat-chats-sidenav>
|
||||
|
||||
<fuse-chat-user-sidenav class="view"
|
||||
*ngSwitchCase="'user'"
|
||||
[@slideInLeft]>
|
||||
</fuse-chat-user-sidenav>
|
||||
</div>
|
20
src/app/main/apps/chat/sidenavs/left/left.component.scss
Normal file
20
src/app/main/apps/chat/sidenavs/left/left.component.scss
Normal file
|
@ -0,0 +1,20 @@
|
|||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
.views {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
.view {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
}
|
27
src/app/main/apps/chat/sidenavs/left/left.component.ts
Normal file
27
src/app/main/apps/chat/sidenavs/left/left.component.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Animations } from '../../../../../core/animations';
|
||||
import { ChatService } from '../../chat.service';
|
||||
|
||||
@Component({
|
||||
selector : 'fuse-chat-left-sidenav',
|
||||
templateUrl: './left.component.html',
|
||||
styleUrls : ['./left.component.scss'],
|
||||
animations : [Animations.slideInLeft, Animations.slideInRight]
|
||||
})
|
||||
export class LeftSidenavComponent implements OnInit
|
||||
{
|
||||
view: string;
|
||||
|
||||
constructor(private chatService: ChatService)
|
||||
{
|
||||
this.view = 'chats';
|
||||
}
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
this.chatService.onLeftSidenavViewChanged.subscribe(view => {
|
||||
this.view = view;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
<!-- SIDENAV HEADER -->
|
||||
<div class="sidenav-header">
|
||||
|
||||
<!-- USER TOOLBAR -->
|
||||
<md-toolbar>
|
||||
|
||||
<!-- TOOLBAR TOP -->
|
||||
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center">
|
||||
|
||||
<button md-button class="mat-icon-button" (click)="changeLeftSidenavView('chats')" aria-label="back">
|
||||
<md-icon>arrow_back</md-icon>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
<!-- / TOOLBAR TOP -->
|
||||
|
||||
<!-- TOOLBAR BOTTOM -->
|
||||
<md-toolbar-row class="toolbar-bottom" fxLayout="column" fxLayoutAlign="center center">
|
||||
|
||||
<img [src]="user.avatar" class="avatar user-avatar huge" alt="{{user.name}}"/>
|
||||
|
||||
<div class="user-name my-8">{{user.name}}</div>
|
||||
|
||||
</md-toolbar-row>
|
||||
<!-- / TOOLBAR BOTTOM -->
|
||||
|
||||
</md-toolbar>
|
||||
<!-- / USER TOOLBAR -->
|
||||
</div>
|
||||
|
||||
<!-- SIDENAV CONTENT -->
|
||||
<div class="sidenav-content p-16" perfect-scrollbar fxFlex>
|
||||
|
||||
<!-- USER MOOD -->
|
||||
<md-card>
|
||||
|
||||
<form [formGroup]="userForm" fxLayout="column">
|
||||
|
||||
<md-input-container class="mb-24" fxFlex>
|
||||
<textarea mdInput placeholder="Mood" name="mood"
|
||||
formControlName="mood" rows="3"></textarea>
|
||||
</md-input-container>
|
||||
|
||||
<md-radio-group formControlName="status" fxLayout="column">
|
||||
|
||||
<md-radio-button value="online" class="py-8">
|
||||
<div fxLayout="row" fxLayoutAlign="start center">
|
||||
<md-icon class="status online mr-8"></md-icon>
|
||||
<span class="mat-h4 m-0">Online</span>
|
||||
</div>
|
||||
</md-radio-button>
|
||||
|
||||
<md-radio-button value="away" class="py-8">
|
||||
<div fxLayout="row" fxLayoutAlign="start center">
|
||||
<md-icon class="status away mr-8"></md-icon>
|
||||
<span class="mat-h4 m-0">Away</span>
|
||||
</div>
|
||||
</md-radio-button>
|
||||
|
||||
<md-radio-button value="do-not-disturb" class="py-8">
|
||||
<div fxLayout="row" fxLayoutAlign="start center">
|
||||
<md-icon class="status do-not-disturb mr-8"></md-icon>
|
||||
<span class="mat-h4 m-0">Do not disturb</span>
|
||||
</div>
|
||||
</md-radio-button>
|
||||
|
||||
<md-radio-button value="offline" class="py-8">
|
||||
<div fxLayout="row" fxLayoutAlign="start center">
|
||||
<md-icon class="status offline mr-8"></md-icon>
|
||||
<span class="mat-h4 m-0">Offline</span>
|
||||
</div>
|
||||
</md-radio-button>
|
||||
</md-radio-group>
|
||||
|
||||
</form>
|
||||
</md-card>
|
||||
<!-- / USER MOOD -->
|
||||
</div>
|
|
@ -0,0 +1,21 @@
|
|||
@import "src/app/core/scss/fuse";
|
||||
|
||||
:host {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
|
||||
md-toolbar {
|
||||
background-color: mat-color($accent);
|
||||
color: map-get($accent, default-contrast);
|
||||
|
||||
.toolbar-bottom {
|
||||
height: 240px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.sidenav-content{
|
||||
background: whitesmoke;
|
||||
}
|
||||
}
|
47
src/app/main/apps/chat/sidenavs/left/user/user.component.ts
Normal file
47
src/app/main/apps/chat/sidenavs/left/user/user.component.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ChatService } from '../../../chat.service';
|
||||
import { FormControl, FormGroup } from '@angular/forms';
|
||||
import 'rxjs/Rx';
|
||||
|
||||
@Component({
|
||||
selector : 'fuse-chat-user-sidenav',
|
||||
templateUrl: './user.component.html',
|
||||
styleUrls : ['./user.component.scss']
|
||||
})
|
||||
export class UserSidenavComponent implements OnInit, OnDestroy
|
||||
{
|
||||
user: any;
|
||||
onFormChange: any;
|
||||
userForm: FormGroup;
|
||||
|
||||
constructor(private chatService: ChatService)
|
||||
{
|
||||
this.user = this.chatService.user;
|
||||
this.userForm = new FormGroup({
|
||||
mood : new FormControl(this.user.mood),
|
||||
status: new FormControl(this.user.status)
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
this.onFormChange = this.userForm.valueChanges
|
||||
.debounceTime(500)
|
||||
.distinctUntilChanged()
|
||||
.subscribe(data => {
|
||||
this.user.mood = data.mood;
|
||||
this.user.status = data.status;
|
||||
this.chatService.updateUserData(this.user);
|
||||
});
|
||||
}
|
||||
|
||||
changeLeftSidenavView(view)
|
||||
{
|
||||
this.chatService.onLeftSidenavViewChanged.next(view);
|
||||
}
|
||||
|
||||
ngOnDestroy()
|
||||
{
|
||||
this.onFormChange.unsubscribe();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<!-- SIDENAV HEADER -->
|
||||
<div class="sidenav-header" *ngIf="contact">
|
||||
|
||||
<!-- CONTACT TOOLBAR -->
|
||||
<md-toolbar>
|
||||
|
||||
<!-- TOOLBAR TOP -->
|
||||
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center">
|
||||
|
||||
<div>Contact Info</div>
|
||||
|
||||
<button md-button class="mat-icon-button" fuseMdSidenavToggler="chat-right-sidenav" aria-label="close">
|
||||
<md-icon>close</md-icon>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
<!-- / TOOLBAR TOP -->
|
||||
|
||||
<!-- TOOLBAR BOTTOM -->
|
||||
<md-toolbar-row class="toolbar-bottom" fxLayout="column" fxLayoutAlign="center center">
|
||||
|
||||
<img [src]="contact.avatar" class="avatar contact-avatar huge" alt="{{contact.name}}"/>
|
||||
|
||||
<div class="contact-name my-8">{{contact.name}}</div>
|
||||
|
||||
</md-toolbar-row>
|
||||
<!-- / TOOLBAR BOTTOM -->
|
||||
|
||||
</md-toolbar>
|
||||
<!-- / CONTACT TOOLBAR -->
|
||||
</div>
|
||||
|
||||
<!-- SIDENAV CONTENT -->
|
||||
<div class="sidenav-content p-16" perfect-scrollbar fxFlex *ngIf="contact">
|
||||
|
||||
<!-- CONTACT MOOD -->
|
||||
<md-card>
|
||||
|
||||
<md-input-container fxFlex>
|
||||
<textarea mdInput placeholder="Mood" name="mood"
|
||||
[value]="contact.mood" rows="3" disabled>
|
||||
</textarea>
|
||||
</md-input-container>
|
||||
|
||||
</md-card>
|
||||
<!-- / CONTACT MOOD -->
|
||||
</div>
|
|
@ -0,0 +1,21 @@
|
|||
@import "src/app/core/scss/fuse";
|
||||
|
||||
:host {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
|
||||
md-toolbar {
|
||||
background-color: mat-color($accent);
|
||||
color: map-get($accent, default-contrast);
|
||||
|
||||
.toolbar-bottom {
|
||||
height: 240px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.sidenav-content{
|
||||
background: whitesmoke;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { ChatService } from '../../../chat.service';
|
||||
|
||||
@Component({
|
||||
selector : 'fuse-chat-contact-sidenav',
|
||||
templateUrl: './contact.component.html',
|
||||
styleUrls : ['./contact.component.scss']
|
||||
})
|
||||
export class ContactSidenavComponent implements OnInit
|
||||
{
|
||||
contact: any;
|
||||
|
||||
constructor(private chatService: ChatService)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
this.chatService.onContactSelected.subscribe(contact => {
|
||||
this.contact = contact;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<div [ngSwitch]="view" class="views">
|
||||
|
||||
<fuse-chat-contact-sidenav class="view"
|
||||
*ngSwitchCase="'contact'"
|
||||
[@slideInRight]>
|
||||
</fuse-chat-contact-sidenav>
|
||||
|
||||
</div>
|
20
src/app/main/apps/chat/sidenavs/right/right.component.scss
Normal file
20
src/app/main/apps/chat/sidenavs/right/right.component.scss
Normal file
|
@ -0,0 +1,20 @@
|
|||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
.views {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
.view {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
}
|
27
src/app/main/apps/chat/sidenavs/right/right.component.ts
Normal file
27
src/app/main/apps/chat/sidenavs/right/right.component.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Animations } from '../../../../../core/animations';
|
||||
import { ChatService } from '../../chat.service';
|
||||
|
||||
@Component({
|
||||
selector : 'fuse-chat-right-sidenav',
|
||||
templateUrl: './right.component.html',
|
||||
styleUrls : ['./right.component.scss'],
|
||||
animations : [Animations.slideInLeft, Animations.slideInRight]
|
||||
})
|
||||
export class RightSidenavComponent implements OnInit
|
||||
{
|
||||
view: string;
|
||||
|
||||
constructor(private chatService: ChatService)
|
||||
{
|
||||
this.view = 'contact';
|
||||
}
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
this.chatService.onRightSidenavViewChanged.subscribe(view => {
|
||||
this.view = view;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -26,7 +26,7 @@
|
|||
"label-position": true,
|
||||
"max-line-length": [
|
||||
true,
|
||||
120
|
||||
180
|
||||
],
|
||||
"member-access": false,
|
||||
"member-ordering": [
|
||||
|
|
Loading…
Reference in New Issue
Block a user