diff --git a/angular.json b/angular.json index b8a09b85..f2ff4b21 100644 --- a/angular.json +++ b/angular.json @@ -398,6 +398,44 @@ } } }, + "ucap-webmessenger-ui-chat": { + "projectType": "library", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "root": "projects/ucap-webmessenger-ui-chat", + "sourceRoot": "projects/ucap-webmessenger-ui-chat/src", + "prefix": "ucap-chat", + "architect": { + "build": { + "builder": "@angular-devkit/build-ng-packagr:build", + "options": { + "tsConfig": "projects/ucap-webmessenger-ui-chat/tsconfig.lib.json", + "project": "projects/ucap-webmessenger-ui-chat/ng-package.json" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/ucap-webmessenger-ui-chat/src/test.ts", + "tsConfig": "projects/ucap-webmessenger-ui-chat/tsconfig.spec.json", + "karmaConfig": "projects/ucap-webmessenger-ui-chat/karma.conf.js" + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "projects/ucap-webmessenger-ui-chat/tsconfig.lib.json", + "projects/ucap-webmessenger-ui-chat/tsconfig.spec.json" + ], + "exclude": ["**/node_modules/**"] + } + } + } + }, "ucap-webmessenger-ui-messenger": { "projectType": "library", "schematics": { diff --git a/projects/ucap-webmessenger-app/src/app/app-routing.module.ts b/projects/ucap-webmessenger-app/src/app/app-routing.module.ts index ce7e1aa9..8498a642 100644 --- a/projects/ucap-webmessenger-app/src/app/app-routing.module.ts +++ b/projects/ucap-webmessenger-app/src/app/app-routing.module.ts @@ -1,21 +1,22 @@ import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; -import { AuthGuard } from './guards/auth.guard'; -import { MessengerResolver } from './resolvers/messenger.resolver'; +import { AppAuthGuard } from './guards/auth.guard'; +import { AppMessengerResolver } from './resolvers/messenger.resolver'; const routes: Routes = [ { path: '', redirectTo: '/messenger', pathMatch: 'full' }, { path: 'messenger', - loadChildren: './pages/messenger/messenger.page.module#MessengerPageModule', - canActivate: [AuthGuard], + loadChildren: + './pages/messenger/messenger.page.module#AppMessengerPageModule', + canActivate: [AppAuthGuard], resolve: { - protocol: MessengerResolver + protocol: AppMessengerResolver } }, { path: 'account', - loadChildren: './pages/account/account.page.module#AccountPageModule' + loadChildren: './pages/account/account.page.module#AppAccountPageModule' } ]; diff --git a/projects/ucap-webmessenger-app/src/app/guards/auth.guard.ts b/projects/ucap-webmessenger-app/src/app/guards/auth.guard.ts index 0feb87b1..145d6bde 100644 --- a/projects/ucap-webmessenger-app/src/app/guards/auth.guard.ts +++ b/projects/ucap-webmessenger-app/src/app/guards/auth.guard.ts @@ -15,7 +15,7 @@ import { AppAuthenticationService } from '../services/authentication.service'; @Injectable({ providedIn: 'root' }) -export class AuthGuard implements CanActivate { +export class AppAuthGuard implements CanActivate { constructor( private router: Router, private appAuthenticationService: AppAuthenticationService diff --git a/projects/ucap-webmessenger-app/src/app/guards/index.ts b/projects/ucap-webmessenger-app/src/app/guards/index.ts index d2030c3c..04d11727 100644 --- a/projects/ucap-webmessenger-app/src/app/guards/index.ts +++ b/projects/ucap-webmessenger-app/src/app/guards/index.ts @@ -1,3 +1,3 @@ -import { AuthGuard } from './auth.guard'; +import { AppAuthGuard } from './auth.guard'; -export const GUARDS = [AuthGuard]; +export const GUARDS = [AppAuthGuard]; diff --git a/projects/ucap-webmessenger-app/src/app/interceptors/loader.interceptor.ts b/projects/ucap-webmessenger-app/src/app/interceptors/loader.interceptor.ts index e0fd1e7b..ec560327 100644 --- a/projects/ucap-webmessenger-app/src/app/interceptors/loader.interceptor.ts +++ b/projects/ucap-webmessenger-app/src/app/interceptors/loader.interceptor.ts @@ -10,7 +10,7 @@ import { finalize, delay } from 'rxjs/operators'; import { AppLoaderService } from '../services/loader.service'; @Injectable() -export class LoaderInterceptor implements HttpInterceptor { +export class AppLoaderInterceptor implements HttpInterceptor { constructor(private injector: Injector) {} intercept( diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/chat.layout.module.ts b/projects/ucap-webmessenger-app/src/app/layouts/chat/chat.layout.module.ts new file mode 100644 index 00000000..6250f4e9 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/chat.layout.module.ts @@ -0,0 +1,26 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { MatIconModule } from '@angular/material/icon'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatToolbarModule } from '@angular/material/toolbar'; + +import { UCapUiChatModule } from '@ucap-webmessenger/ui-chat'; + +import { COMPONENTS } from './components'; + +@NgModule({ + imports: [ + CommonModule, + MatIconModule, + MatMenuModule, + MatTabsModule, + MatToolbarModule, + UCapUiChatModule + ], + exports: [...COMPONENTS], + declarations: [...COMPONENTS], + entryComponents: [] +}) +export class AppChatLayoutModule {} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/index.ts b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/index.ts new file mode 100644 index 00000000..259d82f4 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/index.ts @@ -0,0 +1,15 @@ +import { IntroComponent } from './intro.component'; +import { LeftSidenavComponent } from './left-sidenav.component'; +import { MessagesComponent } from './messages.component'; +import { RightSidenavComponent } from './right-sidenav.component'; + +import { LEFT_SIDENAV_COMPONENTS } from './left-sidenav'; + +export const COMPONENTS = [ + IntroComponent, + LeftSidenavComponent, + MessagesComponent, + RightSidenavComponent, + + ...LEFT_SIDENAV_COMPONENTS +]; diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/intro.component.html b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/intro.component.html new file mode 100644 index 00000000..17d12ef1 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/intro.component.html @@ -0,0 +1,34 @@ +
+ + + + Chat App + + + + Select a contact to start a chat! + + + +
diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/intro.component.scss b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/intro.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/intro.component.spec.ts b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/intro.component.spec.ts new file mode 100644 index 00000000..bc679487 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/intro.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { IntroComponent } from './intro.component'; + +describe('Chat::IntroComponent', () => { + let component: IntroComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [IntroComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(IntroComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/intro.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/intro.component.ts new file mode 100644 index 00000000..a87db74a --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/intro.component.ts @@ -0,0 +1,14 @@ +import { Component, OnInit } from '@angular/core'; +import { ucapAnimations } from '@ucap-webmessenger/ui'; + +@Component({ + selector: 'app-layout-chat-intro', + templateUrl: './intro.component.html', + styleUrls: ['./intro.component.scss'], + animations: ucapAnimations +}) +export class IntroComponent implements OnInit { + constructor() {} + + ngOnInit() {} +} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav.component.html b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav.component.html new file mode 100644 index 00000000..ff7eb089 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav.component.html @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + thumb_up + + + + + + + + thumb_up + + + + + + diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav.component.scss b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav.component.spec.ts b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav.component.spec.ts new file mode 100644 index 00000000..62d733b1 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LeftSidenavComponent } from './left-sidenav.component'; + +describe('Chat::LeftSidenavComponent', () => { + let component: LeftSidenavComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [LeftSidenavComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LeftSidenavComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav.component.ts new file mode 100644 index 00000000..741fe3b8 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav.component.ts @@ -0,0 +1,14 @@ +import { Component, OnInit } from '@angular/core'; +import { ucapAnimations } from '@ucap-webmessenger/ui'; + +@Component({ + selector: 'app-layout-chat-left-sidenav', + templateUrl: './left-sidenav.component.html', + styleUrls: ['./left-sidenav.component.scss'], + animations: ucapAnimations +}) +export class LeftSidenavComponent implements OnInit { + constructor() {} + + ngOnInit() {} +} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/call.component.html b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/call.component.html new file mode 100644 index 00000000..2d973ab8 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/call.component.html @@ -0,0 +1 @@ +Call diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/call.component.scss b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/call.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/call.component.spec.ts b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/call.component.spec.ts new file mode 100644 index 00000000..1345ade5 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/call.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CallComponent } from './call.component'; + +describe('Chat::LeftSidenav::CallComponent', () => { + let component: CallComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [CallComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CallComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/call.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/call.component.ts new file mode 100644 index 00000000..a5f771c0 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/call.component.ts @@ -0,0 +1,14 @@ +import { Component, OnInit } from '@angular/core'; +import { ucapAnimations } from '@ucap-webmessenger/ui'; + +@Component({ + selector: 'app-layout-chat-left-sidenav-call', + templateUrl: './call.component.html', + styleUrls: ['./call.component.scss'], + animations: ucapAnimations +}) +export class CallComponent implements OnInit { + constructor() {} + + ngOnInit() {} +} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/chat.component.html b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/chat.component.html new file mode 100644 index 00000000..eb05330a --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/chat.component.html @@ -0,0 +1 @@ +Chat diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/chat.component.scss b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/chat.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/chat.component.spec.ts b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/chat.component.spec.ts new file mode 100644 index 00000000..74d4ee87 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/chat.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ChatComponent } from './chat.component'; + +describe('Chat::LeftSidenav::ChatComponent', () => { + let component: ChatComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ChatComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ChatComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/chat.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/chat.component.ts new file mode 100644 index 00000000..3ed1c460 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/chat.component.ts @@ -0,0 +1,14 @@ +import { Component, OnInit } from '@angular/core'; +import { ucapAnimations } from '@ucap-webmessenger/ui'; + +@Component({ + selector: 'app-layout-chat-left-sidenav-chat', + templateUrl: './chat.component.html', + styleUrls: ['./chat.component.scss'], + animations: ucapAnimations +}) +export class ChatComponent implements OnInit { + constructor() {} + + ngOnInit() {} +} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/group.component.html b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/group.component.html new file mode 100644 index 00000000..e5664964 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/group.component.html @@ -0,0 +1 @@ +Group diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/group.component.scss b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/group.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/group.component.spec.ts b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/group.component.spec.ts new file mode 100644 index 00000000..12e99468 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/group.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { GroupComponent } from './group.component'; + +describe('Chat::LeftSidenav::GroupComponent', () => { + let component: GroupComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [GroupComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(GroupComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/group.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/group.component.ts new file mode 100644 index 00000000..bee719e7 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/group.component.ts @@ -0,0 +1,14 @@ +import { Component, OnInit } from '@angular/core'; +import { ucapAnimations } from '@ucap-webmessenger/ui'; + +@Component({ + selector: 'app-layout-chat-left-sidenav-group', + templateUrl: './group.component.html', + styleUrls: ['./group.component.scss'], + animations: ucapAnimations +}) +export class GroupComponent implements OnInit { + constructor() {} + + ngOnInit() {} +} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/index.ts b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/index.ts new file mode 100644 index 00000000..791d7ecb --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/index.ts @@ -0,0 +1,11 @@ +import { CallComponent } from './call.component'; +import { ChatComponent } from './chat.component'; +import { GroupComponent } from './group.component'; +import { OrganizationComponent } from './organization.component'; + +export const LEFT_SIDENAV_COMPONENTS = [ + CallComponent, + ChatComponent, + GroupComponent, + OrganizationComponent +]; diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/organization.component.html b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/organization.component.html new file mode 100644 index 00000000..21cac3c7 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/organization.component.html @@ -0,0 +1 @@ +Organization diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/organization.component.scss b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/organization.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/organization.component.spec.ts b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/organization.component.spec.ts new file mode 100644 index 00000000..01b3330d --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/organization.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { OrganizationComponent } from './organization.component'; + +describe('Chat::LeftSidenav::OrganizationComponent', () => { + let component: OrganizationComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [OrganizationComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(OrganizationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/organization.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/organization.component.ts new file mode 100644 index 00000000..e22cd0d4 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/left-sidenav/organization.component.ts @@ -0,0 +1,14 @@ +import { Component, OnInit } from '@angular/core'; +import { ucapAnimations } from '@ucap-webmessenger/ui'; + +@Component({ + selector: 'app-layout-chat-left-sidenav-organization', + templateUrl: './organization.component.html', + styleUrls: ['./organization.component.scss'], + animations: ucapAnimations +}) +export class OrganizationComponent implements OnInit { + constructor() {} + + ngOnInit() {} +} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/messages.component.html b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/messages.component.html new file mode 100644 index 00000000..6cadb5a8 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/messages.component.html @@ -0,0 +1,63 @@ + +
+ + +
+
+ + + + + + + +
+ +
+ + + + + +
+
+
+ + + +
+ + + +
+ + + + + +
+ diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/messages.component.scss b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/messages.component.scss new file mode 100644 index 00000000..898dc404 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/messages.component.scss @@ -0,0 +1,208 @@ +:host { + display: flex; + flex: 1 0 auto; + overflow: hidden; + max-width: 100%; + + .chat { + .chat-toolbar { + min-height: 64px; + border-bottom: 1px solid; + + .responsive-chats-button { + padding: 0; + } + + .chat-contact { + cursor: pointer; + + .avatar { + margin-right: 16px; + } + } + } + + #chat-content { + background: transparent; + overflow: auto; + -webkit-overflow-scrolling: touch; + + .chat-messages { + position: relative; + padding: 16px 0 40px 40px; + + .message-row { + position: relative; + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: flex-end; + padding: 0 16px 4px 16px; + + .avatar { + position: absolute; + left: -32px; + margin: 0; + } + + .bubble { + position: relative; + display: flex; + align-items: center; + justify-content: center; + padding: 12px; + max-width: 100%; + + .message { + white-space: pre-wrap; + line-height: 1.2; + } + + .time { + position: absolute; + display: none; + width: 100%; + font-size: 11px; + margin-top: 8px; + top: 100%; + left: 0; + white-space: nowrap; + } + } + + &.contact { + .bubble { + border-top-left-radius: 5px; + border-bottom-left-radius: 5px; + + border-top-right-radius: 20px; + border-bottom-right-radius: 20px; + + .time { + margin-left: 12px; + } + } + + &.first-of-group { + .bubble { + border-top-left-radius: 20px; + } + } + + &.last-of-group { + .bubble { + border-bottom-left-radius: 20px; + } + } + } + + &.me { + padding-left: 40px; + + .avatar { + order: 2; + margin: 0 0 0 16px; + } + + .bubble { + margin-left: auto; + + border-top-left-radius: 20px; + border-bottom-left-radius: 20px; + + border-top-right-radius: 5px; + border-bottom-right-radius: 5px; + + .time { + justify-content: flex-end; + right: 0; + margin-right: 12px; + } + } + + &.first-of-group { + .bubble { + border-top-right-radius: 20px; + } + } + + &.last-of-group { + .bubble { + border-bottom-right-radius: 20px; + } + } + } + + &.contact + .me, + &.me + .contact { + padding-top: 20px; + margin-top: 20px; + } + + &.first-of-group { + .bubble { + border-top-left-radius: 20px; + padding-top: 13px; + } + } + + &.last-of-group { + .bubble { + border-bottom-left-radius: 20px; + padding-bottom: 13px; + + .time { + display: flex; + } + } + } + } + } + } + + .chat-footer { + border-top: 1px solid; + padding: 8px 8px 8px 16px; + + .reply-form { + position: relative; + + .message-text { + padding: 16px 8px; + + .mat-form-field-wrapper { + padding: 0; + + .mat-form-field-flex { + padding: 0; + + .mat-form-field-infix { + padding: 0; + border: none; + border-radius: 20px; + border: 1px solid; + + textarea { + overflow: hidden; + margin: 16px 48px 16px 16px; + width: calc(100% - 64px); + padding: 0; + } + } + } + + .mat-form-field-underline { + display: none !important; + } + } + } + + .send-message-button { + position: absolute; + right: 16px; + bottom: 21px; + } + } + } + } +} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/messages.component.spec.ts b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/messages.component.spec.ts new file mode 100644 index 00000000..0be9fce8 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/messages.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MessagesComponent } from './messages.component'; + +describe('Chat::MessagesComponent', () => { + let component: MessagesComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [MessagesComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MessagesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/messages.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/messages.component.ts new file mode 100644 index 00000000..90095e04 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/messages.component.ts @@ -0,0 +1,16 @@ +import { Component, OnInit } from '@angular/core'; +import { ucapAnimations } from '@ucap-webmessenger/ui'; + +@Component({ + selector: 'app-layout-chat-messages', + templateUrl: './messages.component.html', + styleUrls: ['./messages.component.scss'], + animations: ucapAnimations +}) +export class MessagesComponent implements OnInit { + constructor() {} + + ngOnInit() {} + + selectContact() {} +} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/right-sidenav.component.html b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/right-sidenav.component.html new file mode 100644 index 00000000..e69de29b diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/right-sidenav.component.scss b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/right-sidenav.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/right-sidenav.component.spec.ts b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/right-sidenav.component.spec.ts new file mode 100644 index 00000000..85fcdd20 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/right-sidenav.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RightSidenavComponent } from './right-sidenav.component'; + +describe('Chat::RightSidenavComponent', () => { + let component: RightSidenavComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [RightSidenavComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(RightSidenavComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-app/src/app/layouts/chat/components/right-sidenav.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/right-sidenav.component.ts new file mode 100644 index 00000000..64d01951 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/chat/components/right-sidenav.component.ts @@ -0,0 +1,14 @@ +import { Component, OnInit } from '@angular/core'; +import { ucapAnimations } from '@ucap-webmessenger/ui'; + +@Component({ + selector: 'app-layout-chat-right-sidenav', + templateUrl: './right-sidenav.component.html', + styleUrls: ['./right-sidenav.component.scss'], + animations: ucapAnimations +}) +export class RightSidenavComponent implements OnInit { + constructor() {} + + ngOnInit() {} +} diff --git a/projects/ucap-webmessenger-app/src/app/pages/account/account-routing.page.module.ts b/projects/ucap-webmessenger-app/src/app/pages/account/account-routing.page.module.ts index a524597e..e1d9ad79 100644 --- a/projects/ucap-webmessenger-app/src/app/pages/account/account-routing.page.module.ts +++ b/projects/ucap-webmessenger-app/src/app/pages/account/account-routing.page.module.ts @@ -15,4 +15,4 @@ const routes: Routes = [ imports: [RouterModule.forChild(routes)], exports: [RouterModule] }) -export class AccountRoutingPageModule {} +export class AppAccountRoutingPageModule {} diff --git a/projects/ucap-webmessenger-app/src/app/pages/account/account.page.module.ts b/projects/ucap-webmessenger-app/src/app/pages/account/account.page.module.ts index caf1f47c..71c2fcf4 100644 --- a/projects/ucap-webmessenger-app/src/app/pages/account/account.page.module.ts +++ b/projects/ucap-webmessenger-app/src/app/pages/account/account.page.module.ts @@ -3,13 +3,13 @@ import { CommonModule } from '@angular/common'; import { UCapUiAccountModule } from '@ucap-webmessenger/ui-account'; -import { AccountRoutingPageModule } from './account-routing.page.module'; +import { AppAccountRoutingPageModule } from './account-routing.page.module'; import { COMPONENTS } from './components'; @NgModule({ - imports: [CommonModule, UCapUiAccountModule, AccountRoutingPageModule], + imports: [CommonModule, UCapUiAccountModule, AppAccountRoutingPageModule], declarations: [...COMPONENTS], entryComponents: [] }) -export class AccountPageModule {} +export class AppAccountPageModule {} diff --git a/projects/ucap-webmessenger-app/src/app/pages/messenger/components/main.page.component.html b/projects/ucap-webmessenger-app/src/app/pages/messenger/components/main.page.component.html index f1a27735..dfcaaad8 100644 --- a/projects/ucap-webmessenger-app/src/app/pages/messenger/components/main.page.component.html +++ b/projects/ucap-webmessenger-app/src/app/pages/messenger/components/main.page.component.html @@ -16,21 +16,20 @@ mode="side" matIsLockedOpen="gt-md" > - - dsafsdfsdf + - + - asdfsdfsdfs - - - safsdf + + diff --git a/projects/ucap-webmessenger-app/src/app/pages/messenger/components/main.page.component.ts b/projects/ucap-webmessenger-app/src/app/pages/messenger/components/main.page.component.ts index 9eace471..7de5914b 100644 --- a/projects/ucap-webmessenger-app/src/app/pages/messenger/components/main.page.component.ts +++ b/projects/ucap-webmessenger-app/src/app/pages/messenger/components/main.page.component.ts @@ -7,11 +7,14 @@ import { AuthenticationProtocolService } from '@ucap-webmessenger/protocol-authe styleUrls: ['./main.page.component.scss'] }) export class MainPageComponent implements OnInit { + selectedChat: boolean; + constructor( private authenticationProtocolService: AuthenticationProtocolService ) {} ngOnInit(): void { // this.authenticationProtocolService.login({}); + this.selectedChat = true; } } diff --git a/projects/ucap-webmessenger-app/src/app/pages/messenger/messenger-routing.page.module.ts b/projects/ucap-webmessenger-app/src/app/pages/messenger/messenger-routing.page.module.ts index 399220eb..9c5d16ac 100644 --- a/projects/ucap-webmessenger-app/src/app/pages/messenger/messenger-routing.page.module.ts +++ b/projects/ucap-webmessenger-app/src/app/pages/messenger/messenger-routing.page.module.ts @@ -15,4 +15,4 @@ const routes: Routes = [ imports: [RouterModule.forChild(routes)], exports: [RouterModule] }) -export class MessengerRoutingPageModule {} +export class AppMessengerRoutingPageModule {} diff --git a/projects/ucap-webmessenger-app/src/app/pages/messenger/messenger.page.module.ts b/projects/ucap-webmessenger-app/src/app/pages/messenger/messenger.page.module.ts index 9519a902..1ecd9244 100644 --- a/projects/ucap-webmessenger-app/src/app/pages/messenger/messenger.page.module.ts +++ b/projects/ucap-webmessenger-app/src/app/pages/messenger/messenger.page.module.ts @@ -3,13 +3,20 @@ import { CommonModule } from '@angular/common'; import { MatSidenavModule } from '@angular/material/sidenav'; -import { MessengerRoutingPageModule } from './messenger-routing.page.module'; +import { AppChatLayoutModule } from '@app/layouts/chat/chat.layout.module'; + +import { AppMessengerRoutingPageModule } from './messenger-routing.page.module'; import { COMPONENTS } from './components'; @NgModule({ - imports: [CommonModule, MatSidenavModule, MessengerRoutingPageModule], + imports: [ + CommonModule, + MatSidenavModule, + AppChatLayoutModule, + AppMessengerRoutingPageModule + ], declarations: [...COMPONENTS], entryComponents: [] }) -export class MessengerPageModule {} +export class AppMessengerPageModule {} diff --git a/projects/ucap-webmessenger-app/src/app/resolvers/index.ts b/projects/ucap-webmessenger-app/src/app/resolvers/index.ts index 93ac0e7b..b91df641 100644 --- a/projects/ucap-webmessenger-app/src/app/resolvers/index.ts +++ b/projects/ucap-webmessenger-app/src/app/resolvers/index.ts @@ -1,3 +1,3 @@ -import { MessengerResolver } from './messenger.resolver'; +import { AppMessengerResolver } from './messenger.resolver'; -export const RESOLVERS = [MessengerResolver]; +export const RESOLVERS = [AppMessengerResolver]; diff --git a/projects/ucap-webmessenger-app/src/app/resolvers/messenger.resolver.ts b/projects/ucap-webmessenger-app/src/app/resolvers/messenger.resolver.ts index 5cd7b167..248bc6b2 100644 --- a/projects/ucap-webmessenger-app/src/app/resolvers/messenger.resolver.ts +++ b/projects/ucap-webmessenger-app/src/app/resolvers/messenger.resolver.ts @@ -24,7 +24,7 @@ import { import { LocaleCode } from '@ucap-webmessenger/core'; @Injectable() -export class MessengerResolver implements Resolve { +export class AppMessengerResolver implements Resolve { constructor( private store: Store, private sessionStorageService: SessionStorageService, @@ -57,6 +57,7 @@ export class MessengerResolver implements Resolve { this.innerProtocolService .conn({}) .pipe( + take(1), map(connRes => { this.authenticationProtocolService .login({ @@ -79,6 +80,7 @@ export class MessengerResolver implements Resolve { productName: 'EZMessenger' }) .pipe( + take(1), map(loginRes => { console.log('loginRes', loginRes); }) diff --git a/projects/ucap-webmessenger-app/src/assets/images/icons/material/material-outline-icons.svg b/projects/ucap-webmessenger-app/src/assets/images/icons/material/material-outline-icons.svg new file mode 100644 index 00000000..946f5481 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/assets/images/icons/material/material-outline-icons.svg @@ -0,0 +1,2037 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/projects/ucap-webmessenger-app/src/assets/images/icons/material/material-outline-icons.ttf b/projects/ucap-webmessenger-app/src/assets/images/icons/material/material-outline-icons.ttf new file mode 100644 index 00000000..b59d861b Binary files /dev/null and b/projects/ucap-webmessenger-app/src/assets/images/icons/material/material-outline-icons.ttf differ diff --git a/projects/ucap-webmessenger-app/src/assets/images/icons/material/material-outline-icons.woff b/projects/ucap-webmessenger-app/src/assets/images/icons/material/material-outline-icons.woff new file mode 100644 index 00000000..36d55262 Binary files /dev/null and b/projects/ucap-webmessenger-app/src/assets/images/icons/material/material-outline-icons.woff differ diff --git a/projects/ucap-webmessenger-protocol-authentication/src/lib/services/authentication-protocol.service.ts b/projects/ucap-webmessenger-protocol-authentication/src/lib/services/authentication-protocol.service.ts index e3f50a32..57810761 100644 --- a/projects/ucap-webmessenger-protocol-authentication/src/lib/services/authentication-protocol.service.ts +++ b/projects/ucap-webmessenger-protocol-authentication/src/lib/services/authentication-protocol.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { map, take } from 'rxjs/operators'; import { ProtocolService } from '@ucap-webmessenger/protocol'; import { @@ -39,13 +39,19 @@ export class AuthenticationProtocolService { public login(req: LoginRequest): Observable { return this.protocolService .call(SVC_TYPE_LOGIN, SSVC_TYPE_LOGIN_REQ, ...encodeLogin(req)) - .pipe(map(res => decodeLogin(res))); + .pipe( + take(1), + map(res => decodeLogin(res)) + ); } public logout(req: LogoutRequest): Observable { return this.protocolService .call(SVC_TYPE_LOGOUT, SSVC_TYPE_LOGOUT_REQ, ...encodeLogout(req)) - .pipe(map(res => decodeLogout(res))); + .pipe( + take(1), + map(res => decodeLogout(res)) + ); } public logoutRemote( @@ -57,6 +63,9 @@ export class AuthenticationProtocolService { SSVC_TYPE_LOGOUT_REMOTE_REQ, ...encodeLogoutRemote(req) ) - .pipe(map(res => decodeLogoutRemote(res))); + .pipe( + take(1), + map(res => decodeLogoutRemote(res)) + ); } } diff --git a/projects/ucap-webmessenger-protocol-event/src/lib/models/info.ts b/projects/ucap-webmessenger-protocol-event/src/lib/models/info.ts new file mode 100644 index 00000000..a7f5e172 --- /dev/null +++ b/projects/ucap-webmessenger-protocol-event/src/lib/models/info.ts @@ -0,0 +1,96 @@ +import { + ProtocolRequest, + ProtocolResponse, + ProtocolEncoder, + PacketBody, + ProtocolDecoder, + ProtocolMessage, + ProtocolStream, + PacketBodyValue +} from '@ucap-webmessenger/protocol'; +import { EventType } from '../types/event.type'; + +export interface InfoRequest extends ProtocolRequest { + // 대화방SEQ(s) + roomSeq: string; + // 기준이벤트SEQ(n) + baseSeq: number; + // 요청이벤트개수(n) + requestCount: number; +} + +export interface Info { + // 이벤트SEQ + seq: number; + // 이벤트타입 + type: EventType; + // 발신자SEQ + senderSeq: number; + // 발신일시 + sendDate: Date; + // 발신내용 + sentMessage: string; + // 수신자수 + receiverCount: number; +} + +export interface InfoData extends ProtocolStream { + // 대화방SEQ(s) + roomSeq: string; + infoList: Info[]; +} + +export interface InfoResponse extends ProtocolResponse { + // 대화방SEQ(s) + roomSeq: string; + // 기준이벤트SEQ(n) + baseSeq: number; + // 유효한파일기준이벤트SEQ(n) + validFileBaseSeq: number; + // 이벤트정보 개수(n) + count: number; +} + +export const encodeInfo: ProtocolEncoder = (req: InfoRequest) => { + const bodyList: PacketBody[] = []; + + bodyList.push( + { type: PacketBodyValue.String, value: req.roomSeq }, + { type: PacketBodyValue.Integer, value: req.baseSeq }, + { type: PacketBodyValue.Integer, value: req.requestCount } + ); + + return bodyList; +}; + +export const decodeInfoData: ProtocolDecoder = ( + message: ProtocolMessage +) => { + const infoList: Info[] = []; + for (let i = 1; i < message.bodyList.length; ) { + infoList.push({ + seq: message.bodyList[i++], + type: message.bodyList[i++], + senderSeq: message.bodyList[i++], + sendDate: message.bodyList[i++], + sentMessage: message.bodyList[i++], + receiverCount: message.bodyList[i++] + }); + } + + return { + roomSeq: message.bodyList[0], + infoList + } as InfoData; +}; + +export const decodeInfo: ProtocolDecoder = ( + message: ProtocolMessage +) => { + return { + roomSeq: message.bodyList[0], + baseSeq: message.bodyList[1], + validFileBaseSeq: message.bodyList[2], + count: message.bodyList[3] + } as InfoResponse; +}; diff --git a/projects/ucap-webmessenger-protocol-event/src/lib/services/event-protocol.service.ts b/projects/ucap-webmessenger-protocol-event/src/lib/services/event-protocol.service.ts index 38c8034d..73f11c46 100644 --- a/projects/ucap-webmessenger-protocol-event/src/lib/services/event-protocol.service.ts +++ b/projects/ucap-webmessenger-protocol-event/src/lib/services/event-protocol.service.ts @@ -1,8 +1,42 @@ import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { map, takeWhile, timeout } from 'rxjs/operators'; + +import { ProtocolService } from '@ucap-webmessenger/protocol'; + +import { + InfoRequest, + InfoResponse, + encodeInfo, + decodeInfo, + decodeInfoData, + InfoData +} from '../models/info'; +import { + SVC_TYPE_EVENT, + SSVC_TYPE_EVENT_INFO_REQ, + SSVC_TYPE_EVENT_INFO_RES, + SSVC_TYPE_EVENT_INFO_DATA +} from '../types/service'; + @Injectable({ providedIn: 'root' }) export class EventProtocolService { - constructor() {} + constructor(private protocolService: ProtocolService) {} + + public info(req: InfoRequest): Observable { + return this.protocolService + .call(SVC_TYPE_EVENT, SSVC_TYPE_EVENT_INFO_REQ, ...encodeInfo(req)) + .pipe( + takeWhile(res => SSVC_TYPE_EVENT_INFO_RES !== res.subServiceType), + map(res => { + if (SSVC_TYPE_EVENT_INFO_DATA === res.subServiceType) { + return decodeInfoData(res); + } + return decodeInfo(res); + }) + ); + } } diff --git a/projects/ucap-webmessenger-protocol-event/src/lib/types/event.type.ts b/projects/ucap-webmessenger-protocol-event/src/lib/types/event.type.ts new file mode 100644 index 00000000..d1af4254 --- /dev/null +++ b/projects/ucap-webmessenger-protocol-event/src/lib/types/event.type.ts @@ -0,0 +1,48 @@ +export enum EventType { + // J: 대화참여 + Join = 'J', + // C: 대화전송 + Character = 'C', + // F: 파일전송 + File = 'F', + // S: 스티커 + Sticker = 'S', + // T: Mass Text + MassText = 'T', + // E: 대화퇴장 + Exit = 'E', + // P: 일정 + Plan = 'P', + // V: 화상회의 + VideoConference = 'V', + // L: 링크(Legacy) + Link = 'L', + // R: 대화방명변경 + RenameRoom = '', + // Y: 번역 + Translation = 'Y', + // Z: 대용량 번역 + MassTranslation = 'Z', + // U: 회수된 메시지 + RecalledMessage = 'U', + // N: 방Timer 변경 안내 + GuideForRoomTimerChanged = 'N', + // X: iOS캡쳐알림 + NotificationForiOSCapture = 'X', + // B: 타이머대화방알림 + NotificationForTimerRoom = 'B', + // Q: 2개월전 메시지 + Before2MonthsAgo = 'Q', + // G: 강제 퇴장 + ForcedExit = 'G', + // H: 챗봇대화 시작 + ChatbotStart = 'H', + // K: 챗봇대화 종료 + ChatbotEnd = 'K', + // I: 챗봇대화 전송 + ChatbotSend = 'I', + // A: 챗봇 대용량 대화 전송 + ChatbotSendMass = 'A', + // M: 동영상 스트리밍 + VideoStreamming = 'M' +} diff --git a/projects/ucap-webmessenger-protocol-event/src/lib/types/service.ts b/projects/ucap-webmessenger-protocol-event/src/lib/types/service.ts new file mode 100644 index 00000000..6db7a25e --- /dev/null +++ b/projects/ucap-webmessenger-protocol-event/src/lib/types/service.ts @@ -0,0 +1,39 @@ +export const SVC_TYPE_EVENT = 20; + +export const SSVC_TYPE_EVENT_INFO_REQ = 1; // 이벤트 정보 요청 +export const SSVC_TYPE_EVENT_INFO_DATA = 2; // 이벤트 정보 +export const SSVC_TYPE_EVENT_INFO_RES = 3; // 이벤트 정보 완료 +export const SSVC_TYPE_EVENT_SEND_REQ = 11; // 이벤트 전송 요청 +export const SSVC_TYPE_EVENT_SEND_RES = 12; // 이벤트 전송 응답 +export const SSVC_TYPE_EVENT_SEND_NOTI = 13; // 이벤트 전송 알림 +export const SSVC_TYPE_EVENT_PUSH_REQ = 14; // (푸시 요청) +export const SSVC_TYPE_EVENT_PUSH_CL_REQ = 15; // (AWAY PUSH 요청) +export const SSVC_TYPE_EVENT_INPUT_STATUS_REQ = 16; // 이벤트 입력 알림 요청 +export const SSVC_TYPE_EVENT_INPUT_STATUS_RES = 17; // 이벤트 입력 알림 응답 +export const SSVC_TYPE_EVENT_READ_REQ = 21; // 이벤트 확인 요청 +export const SSVC_TYPE_EVENT_READ_RES = 22; // 이벤트 확인 응답 +export const SSVC_TYPE_EVENT_READ_NOTI = 23; // 에벤트 확인 알림 +export const SSVC_TYPE_EVENT_UNREAD_CNT_REQ = 25; // 이벤트 UnreadCnt 요청 +export const SSVC_TYPE_EVENT_UNREAD_CNT_RES = 26; // 이벤트 UnreadCnt 응답 +export const SSVC_TYPE_EVENT_USER_UNREAD_CNT_REQ = 27; // 사용자 UnreadCnt 요청 +export const SSVC_TYPE_EVENT_USER_UNREAD_CNT_RES = 28; // 사용자 UnreadCnt 응답 +export const SSVC_TYPE_EVENT_DEL_REQ = 31; // 이벤트 삭제 요청 +export const SSVC_TYPE_EVENT_DEL_RES = 32; // 이벤트 삭제 응답 +export const SSVC_TYPE_EVENT_CANCEL_REQ = 33; // 이벤트 회수 요청 +export const SSVC_TYPE_EVENT_CANCEL_NOTI = 34; // 이벤트 회수 알림 +export const SSVC_TYPE_EVENT_CANCEL_RES = 35; // 이벤트 회수 응답 +export const SSVC_TYPE_EVENT_BOT_NOTI_REQ = 41; // BOT방에 이벤트 전송 요청 +export const SSVC_TYPE_EVENT_BOT_NOTI_RES = 42; // BOT방에 이벤트 전송 응답 +export const SSVC_TYPE_EVENT_CAPTURE_NOTI_REQ = 43; // iOS에서 캡쳐 시,알림 요청 +export const SSVC_TYPE_EVENT_CAPTURE_NOTI_RES = 44; // iOS에서 캡쳐 시,알림 응답 +export const SSVC_TYPE_EVENT_SEND_PLAN_REQ = 51; // 일정 정보 전송 요청 +export const SSVC_TYPE_EVENT_SEND_PLAN_RES = 52; // 일정 정보 전송 응답 +export const SSVC_TYPE_EVENT_SEND_CONF_REQ = 61; // 화상회의 정보 전송 요청 +export const SSVC_TYPE_EVENT_SEND_CONF_RES = 62; // 화상회의 정보 전송 응답 +export const SSVC_TYPE_EVENT_BOT_SMS_REQ = 71; // UCAP SMS이벤트 전송 요청 +export const SSVC_TYPE_EVENT_BOT_SMS_RES = 72; // UCAP SMS이벤트 전송 응답 +export const SSVC_TYPE_EVENT_PUSH_UMG_CL_REQ = 81; // (AWAY 쪽지 PUSH 요청) +export const SSVC_TYPE_EVENT_PUSH_CHECK_REQ = 91; // 푸시 체크 요청 +export const SSVC_TYPE_EVENT_PUSH_CHECK_RES = 92; // 푸시 체크 응답 +export const SSVC_TYPE_EVENT_SEND_STREAM_REQ = 101; // 동영상 스트리밍 전송 요청 +export const SSVC_TYPE_EVENT_SEND_STREAM_RES = 102; // 동영상 스트리밍 전송 응답 diff --git a/projects/ucap-webmessenger-protocol-inner/src/lib/services/inner-protocol.service.ts b/projects/ucap-webmessenger-protocol-inner/src/lib/services/inner-protocol.service.ts index 12835ac5..fee603f5 100644 --- a/projects/ucap-webmessenger-protocol-inner/src/lib/services/inner-protocol.service.ts +++ b/projects/ucap-webmessenger-protocol-inner/src/lib/services/inner-protocol.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { map, take } from 'rxjs/operators'; import { ProtocolService } from '@ucap-webmessenger/protocol'; @@ -22,6 +22,9 @@ export class InnerProtocolService { public conn(req: ConnRequest): Observable { return this.protocolService .call(SVC_TYPE_INNER, SSVC_TYPE_CONN_REQ, ...encodeConn(req)) - .pipe(map(res => decodeConn(res))); + .pipe( + take(1), + map(res => decodeConn(res)) + ); } } diff --git a/projects/ucap-webmessenger-protocol/src/lib/models/protocol.ts b/projects/ucap-webmessenger-protocol/src/lib/models/protocol.ts index 6f68868e..43ebde70 100644 --- a/projects/ucap-webmessenger-protocol/src/lib/models/protocol.ts +++ b/projects/ucap-webmessenger-protocol/src/lib/models/protocol.ts @@ -8,6 +8,10 @@ export interface ProtocolResponse { _id?: string; } +export interface ProtocolStream { + _id?: string; +} + // tslint:disable-next-line: no-empty-interface export interface ProtocolNotification {} diff --git a/projects/ucap-webmessenger-protocol/src/lib/services/protocol.service.ts b/projects/ucap-webmessenger-protocol/src/lib/services/protocol.service.ts index d18f3605..1fa02eb8 100644 --- a/projects/ucap-webmessenger-protocol/src/lib/services/protocol.service.ts +++ b/projects/ucap-webmessenger-protocol/src/lib/services/protocol.service.ts @@ -1,7 +1,7 @@ import { Injectable, Inject } from '@angular/core'; import { Subscription, Observable, Subject } from 'rxjs'; -import { share, switchMap, retryWhen, delay } from 'rxjs/operators'; +import { share, switchMap, retryWhen, delay, finalize } from 'rxjs/operators'; import { QueueingSubject } from 'queueing-subject'; @@ -83,7 +83,6 @@ export class ProtocolService { let requestState: RequestState | null = null; if (res.requestId) { requestState = this.pendingRequests.get(res.requestId); - this.pendingRequests.delete(res.requestId); } if (SSVC_TYPE_ERROR_RES === res.message.subServiceType) { @@ -162,7 +161,18 @@ export class ProtocolService { { type: PacketBodyValue.RequestId, value: requestId } ]); - responseSubject = new Subject(); + responseSubject = new Subject().pipe( + finalize(() => { + if (this.pendingRequests.has(requestId)) { + this.pendingRequests.delete(requestId); + } + console.log( + 'ProtocolService::pendingRequests.size', + this.pendingRequests.size + ); + }) + ) as Subject; + this.pendingRequests.set(requestId, { subject: responseSubject, request: { serviceType, subServiceType, bodyList } diff --git a/projects/ucap-webmessenger-ui-chat/README.md b/projects/ucap-webmessenger-ui-chat/README.md new file mode 100644 index 00000000..a54ab405 --- /dev/null +++ b/projects/ucap-webmessenger-ui-chat/README.md @@ -0,0 +1,24 @@ +# UcapWebmessengerUiChat + +This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.2.5. + +## Code scaffolding + +Run `ng generate component component-name --project ucap-webmessenger-ui-chat` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project ucap-webmessenger-ui-chat`. +> Note: Don't forget to add `--project ucap-webmessenger-ui-chat` or else it will be added to the default project in your `angular.json` file. + +## Build + +Run `ng build ucap-webmessenger-ui-chat` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Publishing + +After building your library with `ng build ucap-webmessenger-ui-chat`, go to the dist folder `cd dist/ucap-webmessenger-ui-chat` and run `npm publish`. + +## Running unit tests + +Run `ng test ucap-webmessenger-ui-chat` to execute the unit tests via [Karma](https://karma-runner.github.io). + +## Further help + +To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). diff --git a/projects/ucap-webmessenger-ui-chat/karma.conf.js b/projects/ucap-webmessenger-ui-chat/karma.conf.js new file mode 100644 index 00000000..78c5df43 --- /dev/null +++ b/projects/ucap-webmessenger-ui-chat/karma.conf.js @@ -0,0 +1,32 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage-istanbul-reporter'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + coverageIstanbulReporter: { + dir: require('path').join(__dirname, '../../coverage/ucap-webmessenger-ui-chat'), + reports: ['html', 'lcovonly', 'text-summary'], + fixWebpackSourcePaths: true + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true + }); +}; diff --git a/projects/ucap-webmessenger-ui-chat/ng-package.json b/projects/ucap-webmessenger-ui-chat/ng-package.json new file mode 100644 index 00000000..97ed819b --- /dev/null +++ b/projects/ucap-webmessenger-ui-chat/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/ucap-webmessenger-ui-chat", + "lib": { + "entryFile": "src/public-api.ts" + } +} \ No newline at end of file diff --git a/projects/ucap-webmessenger-ui-chat/package.json b/projects/ucap-webmessenger-ui-chat/package.json new file mode 100644 index 00000000..dab499cf --- /dev/null +++ b/projects/ucap-webmessenger-ui-chat/package.json @@ -0,0 +1,8 @@ +{ + "name": "@ucap-webmessenger/ui-chat", + "version": "0.0.1", + "peerDependencies": { + "@angular/common": "^8.2.5", + "@angular/core": "^8.2.5" + } +} diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/form.component.html b/projects/ucap-webmessenger-ui-chat/src/lib/components/form.component.html new file mode 100644 index 00000000..5666fa63 --- /dev/null +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/form.component.html @@ -0,0 +1,39 @@ +
+
+ + + + + +
+
diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/form.component.scss b/projects/ucap-webmessenger-ui-chat/src/lib/components/form.component.scss new file mode 100644 index 00000000..a5ca2a4e --- /dev/null +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/form.component.scss @@ -0,0 +1,39 @@ +.chat-form { + position: relative; + + .message-text { + padding: 16px 8px; + + .mat-form-field-wrapper { + padding: 0; + + .mat-form-field-flex { + padding: 0; + + .mat-form-field-infix { + padding: 0; + border: none; + border-radius: 20px; + border: 1px solid; + + textarea { + overflow: hidden; + margin: 16px 48px 16px 16px; + width: calc(100% - 64px); + padding: 0; + } + } + } + + .mat-form-field-underline { + display: none !important; + } + } + } + + .send-message-button { + position: absolute; + right: 16px; + bottom: 21px; + } +} diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/form.component.spec.ts b/projects/ucap-webmessenger-ui-chat/src/lib/components/form.component.spec.ts new file mode 100644 index 00000000..2a56479e --- /dev/null +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/form.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FormComponent } from './form.component'; + +describe('Chat::FormComponent', () => { + let component: FormComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [FormComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(FormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/form.component.ts b/projects/ucap-webmessenger-ui-chat/src/lib/components/form.component.ts new file mode 100644 index 00000000..3a2c959e --- /dev/null +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/form.component.ts @@ -0,0 +1,14 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'ucap-chat-form', + templateUrl: './form.component.html', + styleUrls: ['./form.component.scss'] +}) +export class FormComponent implements OnInit { + constructor() {} + + ngOnInit() {} + + send() {} +} diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/intro.component.html b/projects/ucap-webmessenger-ui-chat/src/lib/components/intro.component.html new file mode 100644 index 00000000..e69de29b diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/intro.component.scss b/projects/ucap-webmessenger-ui-chat/src/lib/components/intro.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/intro.component.spec.ts b/projects/ucap-webmessenger-ui-chat/src/lib/components/intro.component.spec.ts new file mode 100644 index 00000000..bc679487 --- /dev/null +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/intro.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { IntroComponent } from './intro.component'; + +describe('Chat::IntroComponent', () => { + let component: IntroComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [IntroComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(IntroComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/intro.component.ts b/projects/ucap-webmessenger-ui-chat/src/lib/components/intro.component.ts new file mode 100644 index 00000000..670899c1 --- /dev/null +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/intro.component.ts @@ -0,0 +1,12 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'ucap-chat-intro', + templateUrl: './intro.component.html', + styleUrls: ['./intro.component.scss'] +}) +export class IntroComponent implements OnInit { + constructor() {} + + ngOnInit() {} +} diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/messages.component.html b/projects/ucap-webmessenger-ui-chat/src/lib/components/messages.component.html new file mode 100644 index 00000000..6441b2be --- /dev/null +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/messages.component.html @@ -0,0 +1,20 @@ +
+ +
+ + +
+
{{ message.message }}
+
{{ message.time | date: 'short' }}
+
+
+
diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/messages.component.scss b/projects/ucap-webmessenger-ui-chat/src/lib/components/messages.component.scss new file mode 100644 index 00000000..7e3d7833 --- /dev/null +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/messages.component.scss @@ -0,0 +1,131 @@ +.chat-messages { + position: relative; + padding: 16px 0 40px 40px; + + .message-row { + position: relative; + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: flex-end; + padding: 0 16px 4px 16px; + + .avatar { + position: absolute; + left: -32px; + margin: 0; + } + + .bubble { + position: relative; + display: flex; + align-items: center; + justify-content: center; + padding: 12px; + max-width: 100%; + + .message { + white-space: pre-wrap; + line-height: 1.2; + } + + .time { + position: absolute; + display: none; + width: 100%; + font-size: 11px; + margin-top: 8px; + top: 100%; + left: 0; + white-space: nowrap; + } + } + + &.contact { + .bubble { + border-top-left-radius: 5px; + border-bottom-left-radius: 5px; + + border-top-right-radius: 20px; + border-bottom-right-radius: 20px; + + .time { + margin-left: 12px; + } + } + + &.first-of-group { + .bubble { + border-top-left-radius: 20px; + } + } + + &.last-of-group { + .bubble { + border-bottom-left-radius: 20px; + } + } + } + + &.me { + padding-left: 40px; + + .avatar { + order: 2; + margin: 0 0 0 16px; + } + + .bubble { + margin-left: auto; + + border-top-left-radius: 20px; + border-bottom-left-radius: 20px; + + border-top-right-radius: 5px; + border-bottom-right-radius: 5px; + + .time { + justify-content: flex-end; + right: 0; + margin-right: 12px; + } + } + + &.first-of-group { + .bubble { + border-top-right-radius: 20px; + } + } + + &.last-of-group { + .bubble { + border-bottom-right-radius: 20px; + } + } + } + + &.contact + .me, + &.me + .contact { + padding-top: 20px; + margin-top: 20px; + } + + &.first-of-group { + .bubble { + border-top-left-radius: 20px; + padding-top: 13px; + } + } + + &.last-of-group { + .bubble { + border-bottom-left-radius: 20px; + padding-bottom: 13px; + + .time { + display: flex; + } + } + } + } +} diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/messages.component.spec.ts b/projects/ucap-webmessenger-ui-chat/src/lib/components/messages.component.spec.ts new file mode 100644 index 00000000..0be9fce8 --- /dev/null +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/messages.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MessagesComponent } from './messages.component'; + +describe('Chat::MessagesComponent', () => { + let component: MessagesComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [MessagesComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MessagesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/messages.component.ts b/projects/ucap-webmessenger-ui-chat/src/lib/components/messages.component.ts new file mode 100644 index 00000000..6a1f843c --- /dev/null +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/messages.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit, Input } from '@angular/core'; + +@Component({ + selector: 'ucap-chat-messages', + templateUrl: './messages.component.html', + styleUrls: ['./messages.component.scss'] +}) +export class MessagesComponent implements OnInit { + @Input() + messages: any[]; + + constructor() {} + + ngOnInit() {} +} diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/ucap-ui-chat.module.ts b/projects/ucap-webmessenger-ui-chat/src/lib/ucap-ui-chat.module.ts new file mode 100644 index 00000000..d28ee6d2 --- /dev/null +++ b/projects/ucap-webmessenger-ui-chat/src/lib/ucap-ui-chat.module.ts @@ -0,0 +1,38 @@ +import { NgModule, ModuleWithProviders } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; + +import { FlexLayoutModule } from '@angular/flex-layout'; + +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; + +import { FormComponent } from './components/form.component'; +import { IntroComponent } from './components/intro.component'; +import { MessagesComponent } from './components/messages.component'; + +const COMPONENTS = [FormComponent, IntroComponent, MessagesComponent]; +const SERVICES = []; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + ReactiveFormsModule, + FlexLayoutModule, + MatFormFieldModule, + MatIconModule, + MatInputModule + ], + exports: [...COMPONENTS], + declarations: [...COMPONENTS] +}) +export class UCapUiChatModule { + public static forRoot(): ModuleWithProviders { + return { + ngModule: UCapUiChatModule, + providers: [...SERVICES] + }; + } +} diff --git a/projects/ucap-webmessenger-ui-chat/src/public-api.ts b/projects/ucap-webmessenger-ui-chat/src/public-api.ts new file mode 100644 index 00000000..d747a444 --- /dev/null +++ b/projects/ucap-webmessenger-ui-chat/src/public-api.ts @@ -0,0 +1,9 @@ +/* + * Public API Surface of ucap-webmessenger-ui-chat + */ + +export * from './lib/components/form.component'; +export * from './lib/components/intro.component'; +export * from './lib/components/messages.component'; + +export * from './lib/ucap-ui-chat.module'; diff --git a/projects/ucap-webmessenger-ui-chat/src/test.ts b/projects/ucap-webmessenger-ui-chat/src/test.ts new file mode 100644 index 00000000..978c64fb --- /dev/null +++ b/projects/ucap-webmessenger-ui-chat/src/test.ts @@ -0,0 +1,21 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js/dist/zone'; +import 'zone.js/dist/zone-testing'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +declare const require: any; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/projects/ucap-webmessenger-ui-chat/tsconfig.lib.json b/projects/ucap-webmessenger-ui-chat/tsconfig.lib.json new file mode 100644 index 00000000..bd23948e --- /dev/null +++ b/projects/ucap-webmessenger-ui-chat/tsconfig.lib.json @@ -0,0 +1,26 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/lib", + "target": "es2015", + "declaration": true, + "inlineSources": true, + "types": [], + "lib": [ + "dom", + "es2018" + ] + }, + "angularCompilerOptions": { + "annotateForClosureCompiler": true, + "skipTemplateCodegen": true, + "strictMetadataEmit": true, + "fullTemplateTypeCheck": true, + "strictInjectionParameters": true, + "enableResourceInlining": true + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/projects/ucap-webmessenger-ui-chat/tsconfig.spec.json b/projects/ucap-webmessenger-ui-chat/tsconfig.spec.json new file mode 100644 index 00000000..16da33db --- /dev/null +++ b/projects/ucap-webmessenger-ui-chat/tsconfig.spec.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec", + "types": [ + "jasmine", + "node" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/projects/ucap-webmessenger-ui-chat/tslint.json b/projects/ucap-webmessenger-ui-chat/tslint.json new file mode 100644 index 00000000..f01e1f14 --- /dev/null +++ b/projects/ucap-webmessenger-ui-chat/tslint.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tslint.json", + "rules": { + "directive-selector": [ + true, + "attribute", + "ucapChat", + "camelCase" + ], + "component-selector": [ + true, + "element", + "ucap-chat", + "kebab-case" + ] + } +} diff --git a/projects/ucap-webmessenger-ui/src/lib/animations/index.ts b/projects/ucap-webmessenger-ui/src/lib/animations/index.ts new file mode 100644 index 00000000..ac5a437f --- /dev/null +++ b/projects/ucap-webmessenger-ui/src/lib/animations/index.ts @@ -0,0 +1,552 @@ +import { + sequence, + trigger, + animate, + style, + group, + query, + transition, + animateChild, + state, + animation, + useAnimation, + stagger +} from '@angular/animations'; + +const customAnimation = animation( + [ + style({ + opacity: '{{opacity}}', + transform: 'scale({{scale}}) translate3d({{x}}, {{y}}, {{z}})' + }), + animate('{{duration}} {{delay}} cubic-bezier(0.0, 0.0, 0.2, 1)', style('*')) + ], + { + params: { + duration: '200ms', + delay: '0ms', + opacity: '0', + scale: '1', + x: '0', + y: '0', + z: '0' + } + } +); + +export const ucapAnimations = [ + trigger('animate', [ + transition('void => *', [useAnimation(customAnimation)]) + ]), + + trigger('animateStagger', [ + state('50', style('*')), + state('100', style('*')), + state('200', style('*')), + + transition( + 'void => 50', + query('@*', [stagger('50ms', [animateChild()])], { optional: true }) + ), + transition( + 'void => 100', + query('@*', [stagger('100ms', [animateChild()])], { optional: true }) + ), + transition( + 'void => 200', + query('@*', [stagger('200ms', [animateChild()])], { optional: true }) + ) + ]), + + trigger('fadeInOut', [ + state( + '0, void', + style({ + opacity: 0 + }) + ), + state( + '1, *', + style({ + opacity: 1 + }) + ), + transition('1 => 0', animate('300ms ease-out')), + transition('0 => 1', animate('300ms ease-in')), + transition('void <=> *', animate('300ms ease-in')) + ]), + + trigger('slideInOut', [ + state( + '0', + style({ + height: '0px' + }) + ), + state( + '1', + style({ + height: '*' + }) + ), + transition('1 => 0', animate('300ms ease-out')), + transition('0 => 1', animate('300ms ease-in')) + ]), + + trigger('slideIn', [ + transition('void => left', [ + style({ + transform: 'translateX(100%)' + }), + animate( + '300ms ease-in', + style({ + transform: 'translateX(0)' + }) + ) + ]), + transition('left => void', [ + style({ + transform: 'translateX(0)' + }), + animate( + '300ms ease-in', + style({ + transform: 'translateX(-100%)' + }) + ) + ]), + transition('void => right', [ + style({ + transform: 'translateX(-100%)' + }), + animate( + '300ms ease-in', + style({ + transform: 'translateX(0)' + }) + ) + ]), + transition('right => void', [ + style({ + transform: 'translateX(0)' + }), + animate( + '300ms ease-in', + style({ + transform: 'translateX(100%)' + }) + ) + ]) + ]), + + trigger('slideInLeft', [ + state( + 'void', + style({ + transform: 'translateX(-100%)' + }) + ), + state( + '*', + style({ + transform: 'translateX(0)' + }) + ), + transition('void => *', animate('300ms')), + transition('* => void', animate('300ms')) + ]), + + trigger('slideInRight', [ + state( + 'void', + style({ + transform: 'translateX(100%)' + }) + ), + state( + '*', + style({ + transform: 'translateX(0)' + }) + ), + transition('void => *', animate('300ms')), + transition('* => void', animate('300ms')) + ]), + + trigger('slideInTop', [ + state( + 'void', + style({ + transform: 'translateY(-100%)' + }) + ), + state( + '*', + style({ + transform: 'translateY(0)' + }) + ), + transition('void => *', animate('300ms')), + transition('* => void', animate('300ms')) + ]), + + trigger('slideInBottom', [ + state( + 'void', + style({ + transform: 'translateY(100%)' + }) + ), + state( + '*', + style({ + transform: 'translateY(0)' + }) + ), + transition('void => *', animate('300ms')), + transition('* => void', animate('300ms')) + ]), + + trigger('expandCollapse', [ + state( + 'void', + style({ + height: '0px' + }) + ), + state( + '*', + style({ + height: '*' + }) + ), + transition('void => *', animate('300ms ease-out')), + transition('* => void', animate('300ms ease-in')) + ]), + + // ----------------------------------------------------------------------------------------------------- + // @ Router animations + // ----------------------------------------------------------------------------------------------------- + + trigger('routerTransitionLeft', [ + transition('* => *', [ + query( + 'content > :enter, content > :leave', + [ + style({ + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0 + }) + ], + { optional: true } + ), + query( + 'content > :enter', + [ + style({ + transform: 'translateX(100%)', + opacity: 0 + }) + ], + { optional: true } + ), + sequence([ + group([ + query( + 'content > :leave', + [ + style({ + transform: 'translateX(0)', + opacity: 1 + }), + animate( + '600ms cubic-bezier(0.0, 0.0, 0.2, 1)', + style({ + transform: 'translateX(-100%)', + opacity: 0 + }) + ) + ], + { optional: true } + ), + query( + 'content > :enter', + [ + style({ transform: 'translateX(100%)' }), + animate( + '600ms cubic-bezier(0.0, 0.0, 0.2, 1)', + style({ + transform: 'translateX(0%)', + opacity: 1 + }) + ) + ], + { optional: true } + ) + ]), + query('content > :leave', animateChild(), { optional: true }), + query('content > :enter', animateChild(), { optional: true }) + ]) + ]) + ]), + + trigger('routerTransitionRight', [ + transition('* => *', [ + query( + 'content > :enter, content > :leave', + [ + style({ + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0 + }) + ], + { optional: true } + ), + query( + 'content > :enter', + [ + style({ + transform: 'translateX(-100%)', + opacity: 0 + }) + ], + { optional: true } + ), + sequence([ + group([ + query( + 'content > :leave', + [ + style({ + transform: 'translateX(0)', + opacity: 1 + }), + animate( + '600ms cubic-bezier(0.0, 0.0, 0.2, 1)', + style({ + transform: 'translateX(100%)', + opacity: 0 + }) + ) + ], + { optional: true } + ), + query( + 'content > :enter', + [ + style({ transform: 'translateX(-100%)' }), + animate( + '600ms cubic-bezier(0.0, 0.0, 0.2, 1)', + style({ + transform: 'translateX(0%)', + opacity: 1 + }) + ) + ], + { optional: true } + ) + ]), + query('content > :leave', animateChild(), { optional: true }), + query('content > :enter', animateChild(), { optional: true }) + ]) + ]) + ]), + + trigger('routerTransitionUp', [ + transition('* => *', [ + query( + 'content > :enter, content > :leave', + [ + style({ + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0 + }) + ], + { optional: true } + ), + query( + 'content > :enter', + [ + style({ + transform: 'translateY(100%)', + opacity: 0 + }) + ], + { optional: true } + ), + group([ + query( + 'content > :leave', + [ + style({ + transform: 'translateY(0)', + opacity: 1 + }), + animate( + '600ms cubic-bezier(0.0, 0.0, 0.2, 1)', + style({ + transform: 'translateY(-100%)', + opacity: 0 + }) + ) + ], + { optional: true } + ), + query( + 'content > :enter', + [ + style({ transform: 'translateY(100%)' }), + animate( + '600ms cubic-bezier(0.0, 0.0, 0.2, 1)', + style({ + transform: 'translateY(0%)', + opacity: 1 + }) + ) + ], + { optional: true } + ) + ]), + query('content > :leave', animateChild(), { optional: true }), + query('content > :enter', animateChild(), { optional: true }) + ]) + ]), + + trigger('routerTransitionDown', [ + transition('* => *', [ + query( + 'content > :enter, content > :leave', + [ + style({ + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0 + }) + ], + { optional: true } + ), + query( + 'content > :enter', + [ + style({ + transform: 'translateY(-100%)', + opacity: 0 + }) + ], + { optional: true } + ), + sequence([ + group([ + query( + 'content > :leave', + [ + style({ + transform: 'translateY(0)', + opacity: 1 + }), + animate( + '600ms cubic-bezier(0.0, 0.0, 0.2, 1)', + style({ + transform: 'translateY(100%)', + opacity: 0 + }) + ) + ], + { optional: true } + ), + query( + 'content > :enter', + [ + style({ transform: 'translateY(-100%)' }), + animate( + '600ms cubic-bezier(0.0, 0.0, 0.2, 1)', + style({ + transform: 'translateY(0%)', + opacity: 1 + }) + ) + ], + { optional: true } + ) + ]), + query('content > :leave', animateChild(), { optional: true }), + query('content > :enter', animateChild(), { optional: true }) + ]) + ]) + ]), + + trigger('routerTransitionFade', [ + transition( + '* => *', + group([ + query( + 'content > :enter, content > :leave ', + [ + style({ + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0 + }) + ], + { optional: true } + ), + + query( + 'content > :enter', + [ + style({ + opacity: 0 + }) + ], + { optional: true } + ), + query( + 'content > :leave', + [ + style({ + opacity: 1 + }), + animate( + '300ms cubic-bezier(0.0, 0.0, 0.2, 1)', + style({ + opacity: 0 + }) + ) + ], + { optional: true } + ), + query( + 'content > :enter', + [ + style({ + opacity: 0 + }), + animate( + '300ms cubic-bezier(0.0, 0.0, 0.2, 1)', + style({ + opacity: 1 + }) + ) + ], + { optional: true } + ), + query('content > :enter', animateChild(), { optional: true }), + query('content > :leave', animateChild(), { optional: true }) + ]) + ) + ]) +]; diff --git a/projects/ucap-webmessenger-ui/src/public-api.ts b/projects/ucap-webmessenger-ui/src/public-api.ts index a9e50357..cb5b8ca0 100644 --- a/projects/ucap-webmessenger-ui/src/public-api.ts +++ b/projects/ucap-webmessenger-ui/src/public-api.ts @@ -2,6 +2,8 @@ * Public API Surface of ucap-webmessenger-ui */ +export * from './lib/animations'; + export * from './lib/dialogs/alert.dialog.component'; export * from './lib/dialogs/confirm.dialog.component'; diff --git a/tsconfig.json b/tsconfig.json index a6e377fa..89c8c0e9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,6 +15,8 @@ "typeRoots": ["node_modules/@types"], "lib": ["es2018", "dom"], "paths": { + "@app/*": ["projects/ucap-webmessenger-app/src/app/*"], + "@ucap-webmessenger/core": [ "projects/ucap-webmessenger-core/src/public-api" ], @@ -35,6 +37,9 @@ "@ucap-webmessenger/ui-account": [ "projects/ucap-webmessenger-ui-account/src/public-api" ], + "@ucap-webmessenger/ui-chat": [ + "projects/ucap-webmessenger-ui-chat/src/public-api" + ], "@ucap-webmessenger/ui-messenger": [ "projects/ucap-webmessenger-ui-messenger/src/public-api" ],