diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 3e978b4..52e8597 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,5 +1,5 @@ import { NgModule } from '@angular/core'; -import { Routes, RouterModule } from '@angular/router'; +import { Routes, RouterModule, UrlSegment } from '@angular/router'; import { DefaultLayoutComponent } from '@app/layouts/components/default.layout.component'; import { NoNaviLayoutComponent } from '@app/layouts/components/no-navi.layout.component'; @@ -7,6 +7,19 @@ import { NoNaviLayoutComponent } from '@app/layouts/components/no-navi.layout.co import { AppAuthenticationGuard } from '@app/guards/app-authentication.guard'; import { AppAuthenticationResolver } from './resolvers/app-authentication.resolver'; +export function appMatcher(url: UrlSegment[]) { + if (1 === url.length) { + if ( + ['organization', 'group', 'chat', 'message'].some( + (p) => p === url[0].path + ) + ) { + return { consumed: url }; + } + } + return null; +} + const routes: Routes = [ { path: 'account', @@ -25,59 +38,59 @@ const routes: Routes = [ ) }, { - path: '', + matcher: appMatcher, component: DefaultLayoutComponent, canActivate: [AppAuthenticationGuard], resolve: { authentication: AppAuthenticationResolver - }, - children: [ - { path: '', redirectTo: '/organization', pathMatch: 'full' }, - { - path: 'organization', - loadChildren: () => - import('./pages/organization/organization.page.module').then( - (m) => m.AppOrganizationPageModule - ) - }, - { - path: 'group', - loadChildren: () => - import('./pages/group/group.page.module').then( - (m) => m.AppGroupPageModule - ) - }, - { - path: 'group', - outlet: 'subContent', - loadChildren: () => - import('./pages/group/group.page.module').then( - (m) => m.AppGroupPageModule - ) - }, - { - path: 'chat', - loadChildren: () => - import('./pages/chat/chat.page.module').then( - (m) => m.AppChatPageModule - ) - }, - { - path: 'call', - loadChildren: () => - import('./pages/call/call.page.module').then( - (m) => m.AppCallPageModule - ) - }, - { - path: 'message', - loadChildren: () => - import('./pages/message/message.page.module').then( - (m) => m.AppMessagePageModule - ) - } - ] + } }, + // { + // path: '', + // component: DefaultLayoutComponent, + // canActivate: [AppAuthenticationGuard], + // resolve: { + // authentication: AppAuthenticationResolver + // }, + // children: [ + // { path: '', redirectTo: '/organization', pathMatch: 'full' }, + // { + // path: 'organization', + // loadChildren: () => + // import('./pages/organization/organization.page.module').then( + // (m) => m.AppOrganizationPageModule + // ) + // }, + // { + // path: 'group', + // loadChildren: () => + // import('./pages/group/group.page.module').then( + // (m) => m.AppGroupPageModule + // ) + // }, + // { + // path: 'chat', + // loadChildren: () => + // import('./pages/chat/chat.page.module').then( + // (m) => m.AppChatPageModule + // ) + // }, + // { + // path: 'call', + // loadChildren: () => + // import('./pages/call/call.page.module').then( + // (m) => m.AppCallPageModule + // ) + // }, + // { + // path: 'message', + // loadChildren: () => + // import('./pages/message/message.page.module').then( + // (m) => m.AppMessagePageModule + // ) + // } + // ] + // }, { path: '**', redirectTo: '/common/e404' diff --git a/src/app/app.component.ts b/src/app/app.component.ts index e03a4b7..4cd4364 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,13 +1,44 @@ -import { Component } from '@angular/core'; -import { CommonApiService } from '@ucap/ng-api-common'; +import { Component, OnDestroy, OnInit } from '@angular/core'; + +import { Store } from '@ngrx/store'; + +import { AppActions } from '@app/store/actions'; +import { fromEvent, interval, Subscription } from 'rxjs'; +import { debounce } from 'rxjs/operators'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) -export class AppComponent { - title = 'ucap-lg-web'; +export class AppComponent implements OnInit, OnDestroy { + private resizeWindowSubscription: Subscription; - constructor(private commonApiService: CommonApiService) {} + constructor(private store: Store) { + this.resizeWindowSubscription = fromEvent(window, 'resize') + .pipe(debounce(() => interval(100))) + .subscribe((event: any) => { + this.dispatchWindowSize({ + width: event.target.innerWidth, + height: event.target.innerHeight + }); + }); + } + + ngOnInit(): void { + this.dispatchWindowSize({ + width: window.innerWidth, + height: window.innerHeight + }); + } + + ngOnDestroy(): void { + if (!!this.resizeWindowSubscription) { + this.resizeWindowSubscription.unsubscribe(); + } + } + + private dispatchWindowSize(size: { width: number; height: number }) { + this.store.dispatch(AppActions.windowResized(size)); + } } diff --git a/src/app/layouts/components/default.layout.component.html b/src/app/layouts/components/default.layout.component.html index b41bdb4..284aca4 100644 --- a/src/app/layouts/components/default.layout.component.html +++ b/src/app/layouts/components/default.layout.component.html @@ -93,7 +93,16 @@ -
- +
+ + + + + +
+ + +
+
diff --git a/src/app/layouts/components/default.layout.component.scss b/src/app/layouts/components/default.layout.component.scss index 12469c4..4891a9d 100644 --- a/src/app/layouts/components/default.layout.component.scss +++ b/src/app/layouts/components/default.layout.component.scss @@ -4,4 +4,19 @@ .navi-container { width: 70px; } + + .content-container { + .left-sidenav { + display: flex; + flex-direction: column; + width: 370px; + height: 100%; + max-width: 90%; + overflow: hidden; + } + + .content-drawer { + flex: 0 0 auto; + } + } } diff --git a/src/app/layouts/components/default.layout.component.ts b/src/app/layouts/components/default.layout.component.ts index c69c3c7..69241e4 100644 --- a/src/app/layouts/components/default.layout.component.ts +++ b/src/app/layouts/components/default.layout.component.ts @@ -1,31 +1,83 @@ -import { Component } from '@angular/core'; +import { Component, ViewChild, OnDestroy, OnInit, Type } from '@angular/core'; import { Router } from '@angular/router'; import { MatTabChangeEvent } from '@angular/material/tabs'; +import { MatSidenav } from '@angular/material/sidenav'; + +import { Store, select } from '@ngrx/store'; + +import { AppSelector } from '@app/store/state'; +import { Subscription } from 'rxjs'; @Component({ selector: 'app-layouts-default', templateUrl: './default.layout.component.html', styleUrls: ['./default.layout.component.scss'] }) -export class DefaultLayoutComponent { - constructor(private router: Router) {} +export class DefaultLayoutComponent implements OnInit, OnDestroy { + @ViewChild('leftSidenav', { static: true }) + leftSidenav: MatSidenav; + + leftSectionComponent: Type; + contentSectionComponent: Type; + + private windowSizeSubscription: Subscription; + + constructor(private router: Router, private store: Store) {} + + ngOnInit(): void { + this.windowSizeSubscription = this.store + .pipe(select(AppSelector.windowSize)) + .subscribe((size) => { + if (size.width < 780) { + if (this.leftSidenav.opened) { + this.leftSidenav.close(); + } + } else { + if (!this.leftSidenav.opened) { + this.leftSidenav.open(); + } + } + }); + } + + ngOnDestroy(): void { + if (!this.windowSizeSubscription) { + this.windowSizeSubscription.unsubscribe(); + } + } onSelectedTabChange(event: MatTabChangeEvent) { switch (event.index) { case 1: - this.router.navigate(['/chat']); + // this.router.navigate(['/chat']); break; case 2: - this.router.navigate(['/organization']); + import('@app/sections/organization/organization.section.module').then( + (m) => { + this.leftSectionComponent = m.TreeListSectionComponent; + } + ); + import('@app/sections/organization/organization.section.module').then( + (m) => { + this.contentSectionComponent = m.MembersSectionComponent; + } + ); break; case 3: - this.router.navigate(['/message']); + // this.router.navigate(['/message']); break; default: - this.router.navigate(['/group']); + // this.router.navigate(['/group']); break; } - console.log('onClickButton', event.index); + } + + onClickToggleLeftSidenav() { + if (this.leftSidenav.opened) { + this.leftSidenav.close(); + } else { + this.leftSidenav.open(); + } } } diff --git a/src/app/layouts/layouts.module.ts b/src/app/layouts/layouts.module.ts index 1c89908..43eaf73 100644 --- a/src/app/layouts/layouts.module.ts +++ b/src/app/layouts/layouts.module.ts @@ -7,6 +7,7 @@ import { FlexLayoutModule } from '@angular/flex-layout'; import { MatIconModule } from '@angular/material/icon'; import { MatTabsModule } from '@angular/material/tabs'; +import { MatSidenavModule } from '@angular/material/sidenav'; import { MatToolbarModule } from '@angular/material/toolbar'; import { UiModule } from '@ucap/ng-ui'; @@ -24,6 +25,7 @@ import { DIALOGS } from './dialogs'; MatIconModule, MatTabsModule, + MatSidenavModule, MatToolbarModule, UiModule diff --git a/src/app/pages/call/call-routing.page.module.ts b/src/app/pages/call/call-routing.page.module.ts index 6560bb4..32029df 100644 --- a/src/app/pages/call/call-routing.page.module.ts +++ b/src/app/pages/call/call-routing.page.module.ts @@ -5,7 +5,7 @@ import { IndexPageComponent } from './components/index.page.component'; const routes: Routes = [ { - path: '', + path: 'index', component: IndexPageComponent } ]; diff --git a/src/app/pages/chat/chat-routing.page.module.ts b/src/app/pages/chat/chat-routing.page.module.ts index 22cb304..3c1663f 100644 --- a/src/app/pages/chat/chat-routing.page.module.ts +++ b/src/app/pages/chat/chat-routing.page.module.ts @@ -2,11 +2,17 @@ import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { IndexPageComponent } from './components/index.page.component'; +import { RoomListPageComponent } from './components/room-list.page.component'; const routes: Routes = [ { - path: '**', + path: 'index', component: IndexPageComponent + }, + { + path: 'room-list', + component: RoomListPageComponent, + outlet: 'outlet-content' } ]; diff --git a/src/app/pages/chat/components/index.ts b/src/app/pages/chat/components/index.ts index be6f642..235ca39 100644 --- a/src/app/pages/chat/components/index.ts +++ b/src/app/pages/chat/components/index.ts @@ -1,3 +1,4 @@ import { IndexPageComponent } from './index.page.component'; +import { RoomListPageComponent } from './room-list.page.component'; -export const COMPONENTS = [IndexPageComponent]; +export const COMPONENTS = [IndexPageComponent, RoomListPageComponent]; diff --git a/src/app/pages/chat/components/room-list.page.component.html b/src/app/pages/chat/components/room-list.page.component.html new file mode 100644 index 0000000..95f3bf9 --- /dev/null +++ b/src/app/pages/chat/components/room-list.page.component.html @@ -0,0 +1 @@ +Room list page of chat is works! diff --git a/src/app/pages/chat/components/room-list.page.component.scss b/src/app/pages/chat/components/room-list.page.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/pages/chat/components/room-list.page.component.spec.ts b/src/app/pages/chat/components/room-list.page.component.spec.ts new file mode 100644 index 0000000..f3f2f9e --- /dev/null +++ b/src/app/pages/chat/components/room-list.page.component.spec.ts @@ -0,0 +1,32 @@ +import { TestBed, async } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { RoomListPageComponent } from './room-list.page.component'; + +describe('app::pages::chat::RoomListPageComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule], + declarations: [RoomListPageComponent] + }).compileComponents(); + })); + + it('should create the app', () => { + const fixture = TestBed.createComponent(RoomListPageComponent); + const app = fixture.componentInstance; + expect(app).toBeTruthy(); + }); + + it(`should have as title 'ucap-lg-web'`, () => { + const fixture = TestBed.createComponent(RoomListPageComponent); + const app = fixture.componentInstance; + }); + + it('should render title', () => { + const fixture = TestBed.createComponent(RoomListPageComponent); + fixture.detectChanges(); + const compiled = fixture.nativeElement; + expect(compiled.querySelector('.content span').textContent).toContain( + 'ucap-lg-web app is running!' + ); + }); +}); diff --git a/src/app/pages/chat/components/room-list.page.component.ts b/src/app/pages/chat/components/room-list.page.component.ts new file mode 100644 index 0000000..7fc118f --- /dev/null +++ b/src/app/pages/chat/components/room-list.page.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-pages-chat-room-list', + templateUrl: './room-list.page.component.html', + styleUrls: ['./room-list.page.component.scss'] +}) +export class RoomListPageComponent { + constructor() {} +} diff --git a/src/app/pages/group/group-routing.page.module.ts b/src/app/pages/group/group-routing.page.module.ts index 49c068e..05e05b0 100644 --- a/src/app/pages/group/group-routing.page.module.ts +++ b/src/app/pages/group/group-routing.page.module.ts @@ -5,7 +5,7 @@ import { IndexPageComponent } from './components/index.page.component'; const routes: Routes = [ { - path: '', + path: 'index', component: IndexPageComponent } ]; diff --git a/src/app/pages/message/message-routing.page.module.ts b/src/app/pages/message/message-routing.page.module.ts index 72addff..8e65be6 100644 --- a/src/app/pages/message/message-routing.page.module.ts +++ b/src/app/pages/message/message-routing.page.module.ts @@ -5,7 +5,7 @@ import { IndexPageComponent } from './components/index.page.component'; const routes: Routes = [ { - path: '', + path: 'index', component: IndexPageComponent } ]; diff --git a/src/app/pages/organization/organization-routing.page.module.ts b/src/app/pages/organization/organization-routing.page.module.ts index 2dfba45..dce135a 100644 --- a/src/app/pages/organization/organization-routing.page.module.ts +++ b/src/app/pages/organization/organization-routing.page.module.ts @@ -5,7 +5,7 @@ import { IndexPageComponent } from './components/index.page.component'; const routes: Routes = [ { - path: '', + path: 'index', component: IndexPageComponent } ]; diff --git a/src/app/sections/organization/components/members.section.component.html b/src/app/sections/organization/components/members.section.component.html new file mode 100644 index 0000000..b93f0f9 --- /dev/null +++ b/src/app/sections/organization/components/members.section.component.html @@ -0,0 +1,3 @@ +
+ members +
diff --git a/src/app/sections/organization/components/members.section.component.scss b/src/app/sections/organization/components/members.section.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/sections/organization/components/members.section.component.spec.ts b/src/app/sections/organization/components/members.section.component.spec.ts new file mode 100644 index 0000000..6e9df29 --- /dev/null +++ b/src/app/sections/organization/components/members.section.component.spec.ts @@ -0,0 +1,32 @@ +import { TestBed, async } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { MembersSectionComponent } from './members.section.component'; + +describe('app::sections::organization::MembersSectionComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule], + declarations: [MembersSectionComponent] + }).compileComponents(); + })); + + it('should create the app', () => { + const fixture = TestBed.createComponent(MembersSectionComponent); + const app = fixture.componentInstance; + expect(app).toBeTruthy(); + }); + + it(`should have as title 'ucap-lg-web'`, () => { + const fixture = TestBed.createComponent(MembersSectionComponent); + const app = fixture.componentInstance; + }); + + it('should render title', () => { + const fixture = TestBed.createComponent(MembersSectionComponent); + fixture.detectChanges(); + const compiled = fixture.nativeElement; + expect(compiled.querySelector('.content span').textContent).toContain( + 'ucap-lg-web app is running!' + ); + }); +}); diff --git a/src/app/sections/organization/components/members.section.component.ts b/src/app/sections/organization/components/members.section.component.ts new file mode 100644 index 0000000..490c137 --- /dev/null +++ b/src/app/sections/organization/components/members.section.component.ts @@ -0,0 +1,19 @@ +import { Subscription } from 'rxjs'; +import { take, filter } from 'rxjs/operators'; + +import { Component, OnInit, OnDestroy, Input, ViewChild } from '@angular/core'; + +import { LogService } from '@ucap/ng-logger'; + +@Component({ + selector: 'app-sections-organization-members', + templateUrl: './members.section.component.html', + styleUrls: ['./members.section.component.scss'] +}) +export class MembersSectionComponent implements OnInit, OnDestroy { + constructor(private logService: LogService) {} + + ngOnInit(): void {} + + ngOnDestroy(): void {} +} diff --git a/src/app/sections/organization/components/tree-list.section.component.html b/src/app/sections/organization/components/tree-list.section.component.html new file mode 100644 index 0000000..8361593 --- /dev/null +++ b/src/app/sections/organization/components/tree-list.section.component.html @@ -0,0 +1,3 @@ +
+ tree list +
diff --git a/src/app/sections/organization/components/tree-list.section.component.scss b/src/app/sections/organization/components/tree-list.section.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/sections/organization/components/tree-list.section.component.spec.ts b/src/app/sections/organization/components/tree-list.section.component.spec.ts new file mode 100644 index 0000000..bd664c4 --- /dev/null +++ b/src/app/sections/organization/components/tree-list.section.component.spec.ts @@ -0,0 +1,32 @@ +import { TestBed, async } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { TreeListSectionComponent } from './tree-list.section.component'; + +describe('app::sections::organization::TreeListSectionComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule], + declarations: [TreeListSectionComponent] + }).compileComponents(); + })); + + it('should create the app', () => { + const fixture = TestBed.createComponent(TreeListSectionComponent); + const app = fixture.componentInstance; + expect(app).toBeTruthy(); + }); + + it(`should have as title 'ucap-lg-web'`, () => { + const fixture = TestBed.createComponent(TreeListSectionComponent); + const app = fixture.componentInstance; + }); + + it('should render title', () => { + const fixture = TestBed.createComponent(TreeListSectionComponent); + fixture.detectChanges(); + const compiled = fixture.nativeElement; + expect(compiled.querySelector('.content span').textContent).toContain( + 'ucap-lg-web app is running!' + ); + }); +}); diff --git a/src/app/sections/organization/components/tree-list.section.component.ts b/src/app/sections/organization/components/tree-list.section.component.ts new file mode 100644 index 0000000..1177582 --- /dev/null +++ b/src/app/sections/organization/components/tree-list.section.component.ts @@ -0,0 +1,19 @@ +import { Subscription } from 'rxjs'; +import { take, filter } from 'rxjs/operators'; + +import { Component, OnInit, OnDestroy, Input, ViewChild } from '@angular/core'; + +import { LogService } from '@ucap/ng-logger'; + +@Component({ + selector: 'app-sections-organization-tree-list', + templateUrl: './tree-list.section.component.html', + styleUrls: ['./tree-list.section.component.scss'] +}) +export class TreeListSectionComponent implements OnInit, OnDestroy { + constructor(private logService: LogService) {} + + ngOnInit(): void {} + + ngOnDestroy(): void {} +} diff --git a/src/app/sections/organization/organization.section.module.ts b/src/app/sections/organization/organization.section.module.ts new file mode 100644 index 0000000..2b67a39 --- /dev/null +++ b/src/app/sections/organization/organization.section.module.ts @@ -0,0 +1,37 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { FlexLayoutModule } from '@angular/flex-layout'; + +import { MatCheckboxModule } from '@angular/material/checkbox'; + +import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n'; + +import { AuthenticationUiModule } from '@ucap/ng-ui-authentication'; + +import { MembersSectionComponent } from './components/members.section.component'; +import { TreeListSectionComponent } from './components/tree-list.section.component'; + +const COMPONENTS = [MembersSectionComponent, TreeListSectionComponent]; + +export { MembersSectionComponent, TreeListSectionComponent }; + +@NgModule({ + imports: [ + CommonModule, + FlexLayoutModule, + MatCheckboxModule, + I18nModule, + AuthenticationUiModule + ], + exports: [...COMPONENTS], + declarations: [...COMPONENTS], + entryComponents: [], + providers: [ + { + provide: UCAP_I18N_NAMESPACE, + useValue: ['organization'] + } + ] +}) +export class AppOrganizationSectionModule {} diff --git a/src/app/store/app/actions.ts b/src/app/store/app/actions.ts index a137c99..668fe3d 100644 --- a/src/app/store/app/actions.ts +++ b/src/app/store/app/actions.ts @@ -1,3 +1,8 @@ import { createAction, props } from '@ngrx/store'; export const init = createAction('[ucap::LG::app] init'); + +export const windowResized = createAction( + '[ucap::LG::app] windowResized', + props<{ width: number; height: number }>() +); diff --git a/src/app/store/app/reducers.ts b/src/app/store/app/reducers.ts index 70e7e20..0601c79 100644 --- a/src/app/store/app/reducers.ts +++ b/src/app/store/app/reducers.ts @@ -1,4 +1,16 @@ import { createReducer, on } from '@ngrx/store'; import { initialState } from './state'; +import { windowResized } from './actions'; -export const reducer = createReducer(initialState); +export const reducer = createReducer( + initialState, + on(windowResized, (state, action) => { + return { + ...state, + windowSize: { + width: action.width, + height: action.height + } + }; + }) +); diff --git a/src/app/store/app/state.ts b/src/app/store/app/state.ts index 1e05bd0..24978b0 100644 --- a/src/app/store/app/state.ts +++ b/src/app/store/app/state.ts @@ -1,9 +1,21 @@ import { Selector, createSelector } from '@ngrx/store'; -export interface State {} +export interface State { + windowSize: { + width: number; + height: number; + }; +} -export const initialState: State = {}; +export const initialState: State = { + windowSize: { + width: 0, + height: 0 + } +}; export function selectors(selector: Selector) { - return {}; + return { + windowSize: createSelector(selector, (state: State) => state.windowSize) + }; } diff --git a/src/app/store/state.ts b/src/app/store/state.ts index eec17da..508bee6 100644 --- a/src/app/store/state.ts +++ b/src/app/store/state.ts @@ -8,15 +8,20 @@ import * as fromRouter from '@ngrx/router-store'; import { environment } from '@environments'; -import { State as AppState } from './app/state'; -import { State as AppAuthenticationState } from './authentication/state'; +import * as AppState from './app/state'; +import * as AppAuthenticationState from './authentication/state'; export interface State { appRouter: fromRouter.RouterReducerState; - app: AppState; - appAuthentication: AppAuthenticationState; + app: AppState.State; + appAuthentication: AppAuthenticationState.State; } export const metaReducers: MetaReducer[] = !environment.production ? [] : []; + +export const AppSelector = AppState.selectors((state: State) => state.app); +export const AppAuthenticationSelector = AppAuthenticationState.selectors( + (state: State) => state.appAuthentication +);