0710 sync
This commit is contained in:
parent
92da6c71ce
commit
98ff58c39e
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
@ -12,5 +12,5 @@
|
|||
},
|
||||
"go.testFlags": ["-v"],
|
||||
"go.testTimeout": "100s",
|
||||
"debug.node.autoAttach": "off"
|
||||
"debug.node.autoAttach": "on"
|
||||
}
|
||||
|
|
41
angular.json
41
angular.json
|
@ -37,6 +37,10 @@
|
|||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
},
|
||||
{
|
||||
"replace": "src/environments/native/native.ts",
|
||||
"with": "src/environments/native/native.browser.prod.ts"
|
||||
}
|
||||
],
|
||||
"optimization": true,
|
||||
|
@ -51,7 +55,7 @@
|
|||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
"maximumError": "6mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
|
@ -60,16 +64,19 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"production-es5": {
|
||||
"production-renderer": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
},
|
||||
{
|
||||
"replace": "src/environments/native/native.ts",
|
||||
"with": "src/environments/native/native.renderer.prod.ts"
|
||||
}
|
||||
],
|
||||
"es5BrowserSupport": true,
|
||||
"outputPath": "dist/ucap-lg-web-es5",
|
||||
"tsConfig": "tsconfig.app.es5.json",
|
||||
"outputPath": "dist/ucap-lg-renderer",
|
||||
"tsConfig": "tsconfig.app.renderer.json",
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
|
@ -82,7 +89,7 @@
|
|||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
"maximumError": "6mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
|
@ -96,18 +103,26 @@
|
|||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.hmr.ts"
|
||||
},
|
||||
{
|
||||
"replace": "src/environments/native/native.ts",
|
||||
"with": "src/environments/native/native.browser.ts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"hmr-es5": {
|
||||
"hmr-renderer": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.hmr.ts"
|
||||
},
|
||||
{
|
||||
"replace": "src/environments/native/native.ts",
|
||||
"with": "src/environments/native/native.renderer.ts"
|
||||
}
|
||||
],
|
||||
"es5BrowserSupport": true,
|
||||
"tsConfig": "tsconfig.app.es5.json"
|
||||
"es5BrowserSupport": false,
|
||||
"tsConfig": "tsconfig.app.renderer.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -121,16 +136,16 @@
|
|||
"production": {
|
||||
"browserTarget": "ucap-lg-web:build:production"
|
||||
},
|
||||
"production-5": {
|
||||
"browserTarget": "ucap-lg-web:build:production-es5"
|
||||
"production-renderer": {
|
||||
"browserTarget": "ucap-lg-web:build:production-renderer"
|
||||
},
|
||||
"hmr": {
|
||||
"hmr": true,
|
||||
"browserTarget": "ucap-lg-web:build:hmr"
|
||||
},
|
||||
"hmr-es5": {
|
||||
"hmr-renderer": {
|
||||
"hmr": true,
|
||||
"browserTarget": "ucap-lg-web:build:hmr-es5"
|
||||
"browserTarget": "ucap-lg-web:build:hmr-renderer"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
const path = require('path');
|
||||
|
||||
module.exports = (config, options) => {
|
||||
const PRODUCTION = process.env.NODE_ENV === 'production';
|
||||
const PRODUCTION =
|
||||
!!process.env.NODE_ENV && 'production' === process.env.NODE_ENV;
|
||||
const RENDERER =
|
||||
!!process.env.NATIVE_ENV && 'renderer' === process.env.NATIVE_ENV;
|
||||
|
||||
if (RENDERER) {
|
||||
config.target = 'electron-renderer';
|
||||
} else {
|
||||
config.target = 'web';
|
||||
config.node = {
|
||||
global: true,
|
||||
fs: 'empty'
|
||||
};
|
||||
}
|
||||
|
||||
config.resolve.alias = {
|
||||
...config.resolve.alias,
|
||||
|
|
1161
package-lock.json
generated
1161
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
95
package.json
95
package.json
|
@ -5,42 +5,41 @@
|
|||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"start:hmr": "ng serve --configuration hmr",
|
||||
"start:hmr-es5": "ng serve --configuration hmr-es5",
|
||||
"start:hmr-renderer": "ng serve --configuration hmr-renderer",
|
||||
"build": "ng build",
|
||||
"build:production": "ng build --prod",
|
||||
"build:production-es5": "ng build --configuration production-es5",
|
||||
"build:production-renderer": "cross-env NATIVE_ENV=renderer ng build --prod --configuration production-renderer --base-href ./",
|
||||
"test": "ng test",
|
||||
"lint": "ng lint",
|
||||
"e2e": "ng e2e"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~9.0.6",
|
||||
"@angular/cdk": "^9.1.3",
|
||||
"@angular/common": "~9.0.6",
|
||||
"@angular/compiler": "~9.0.6",
|
||||
"@angular/core": "~9.0.6",
|
||||
"@angular/flex-layout": "^9.0.0-beta.29",
|
||||
"@angular/forms": "~9.0.6",
|
||||
"@angular/material": "^9.1.3",
|
||||
"@angular/material-moment-adapter": "^9.1.3",
|
||||
"@angular/platform-browser": "~9.0.6",
|
||||
"@angular/platform-browser-dynamic": "~9.0.6",
|
||||
"@angular/router": "~9.0.6",
|
||||
"@ngrx/effects": "^9.0.0",
|
||||
"@ngrx/entity": "^9.0.0",
|
||||
"@ngrx/router-store": "^9.0.0",
|
||||
"@ngrx/store": "^9.0.0",
|
||||
"@ucap/api": "~0.0.2",
|
||||
"@ucap/api-common": "~0.0.5",
|
||||
"@angular/animations": "^9.1.11",
|
||||
"@angular/cdk": "^9.2.4",
|
||||
"@angular/common": "^9.1.11",
|
||||
"@angular/compiler": "^9.1.11",
|
||||
"@angular/core": "^9.1.11",
|
||||
"@angular/flex-layout": "^9.0.0-beta.31",
|
||||
"@angular/forms": "^9.1.11",
|
||||
"@angular/material": "^9.2.4",
|
||||
"@angular/material-moment-adapter": "^9.2.4",
|
||||
"@angular/platform-browser": "^9.1.11",
|
||||
"@angular/platform-browser-dynamic": "^9.1.11",
|
||||
"@angular/router": "^9.1.11",
|
||||
"@ngrx/effects": "^9.2.0",
|
||||
"@ngrx/entity": "^9.2.0",
|
||||
"@ngrx/router-store": "^9.2.0",
|
||||
"@ngrx/store": "^9.2.0",
|
||||
"@ucap/api": "~0.0.4",
|
||||
"@ucap/api-common": "~0.0.11",
|
||||
"@ucap/api-external": "~0.0.5",
|
||||
"@ucap/api-message": "~0.0.3",
|
||||
"@ucap/api-prompt": "~0.0.3",
|
||||
"@ucap/api-public": "~0.0.4",
|
||||
"@ucap/core": "~0.0.10",
|
||||
"@ucap/logger": "~0.0.12",
|
||||
"@ucap/native": "~0.0.6",
|
||||
"@ucap/native-browser": "~0.0.5",
|
||||
"@ucap/core": "~0.0.14",
|
||||
"@ucap/logger": "~0.0.13",
|
||||
"@ucap/native": "~0.0.19",
|
||||
"@ucap/ng-api-common": "~0.0.1",
|
||||
"@ucap/ng-api-external": "~0.0.1",
|
||||
"@ucap/ng-api-message": "~0.0.1",
|
||||
|
@ -49,8 +48,7 @@
|
|||
"@ucap/ng-core": "~0.0.7",
|
||||
"@ucap/ng-logger": "~0.0.2",
|
||||
"@ucap/ng-i18n": "~0.0.6",
|
||||
"@ucap/ng-native": "~0.0.1",
|
||||
"@ucap/ng-native-browser": "~0.0.1",
|
||||
"@ucap/ng-native": "~0.0.5",
|
||||
"@ucap/ng-pi": "~0.0.1",
|
||||
"@ucap/ng-protocol": "~0.0.3",
|
||||
"@ucap/ng-protocol-authentication": "~0.0.3",
|
||||
|
@ -68,35 +66,35 @@
|
|||
"@ucap/ng-protocol-status": "~0.0.3",
|
||||
"@ucap/ng-protocol-sync": "~0.0.3",
|
||||
"@ucap/ng-protocol-umg": "~0.0.3",
|
||||
"@ucap/ng-store-authentication": "~0.0.11",
|
||||
"@ucap/ng-store-chat": "~0.0.16",
|
||||
"@ucap/ng-store-group": "~0.0.14",
|
||||
"@ucap/ng-store-organization": "~0.0.8",
|
||||
"@ucap/ng-store-authentication": "~0.0.14",
|
||||
"@ucap/ng-store-chat": "~0.0.66",
|
||||
"@ucap/ng-store-group": "~0.0.22",
|
||||
"@ucap/ng-store-organization": "~0.0.20",
|
||||
"@ucap/ng-web-socket": "~0.0.2",
|
||||
"@ucap/ng-web-storage": "~0.0.3",
|
||||
"@ucap/ng-ui": "~0.0.19",
|
||||
"@ucap/ng-ui-organization": "~0.0.83",
|
||||
"@ucap/ng-ui-authentication": "~0.0.25",
|
||||
"@ucap/ng-ui-group": "~0.0.33",
|
||||
"@ucap/ng-ui-chat": "~0.0.12",
|
||||
"@ucap/ng-ui": "0.0.97",
|
||||
"@ucap/ng-ui-organization": "~0.0.202",
|
||||
"@ucap/ng-ui-authentication": "~0.0.29",
|
||||
"@ucap/ng-ui-group": "~0.0.78",
|
||||
"@ucap/ng-ui-chat": "~0.0.72",
|
||||
"@ucap/ng-ui-material": "~0.0.4",
|
||||
"@ucap/ng-ui-skin-default": "~0.0.1",
|
||||
"@ucap/pi": "~0.0.5",
|
||||
"@ucap/pi": "~0.0.8",
|
||||
"@ucap/protocol": "~0.0.17",
|
||||
"@ucap/protocol-authentication": "~0.0.5",
|
||||
"@ucap/protocol-buddy": "~0.0.5",
|
||||
"@ucap/protocol-event": "~0.0.5",
|
||||
"@ucap/protocol-file": "~0.0.4",
|
||||
"@ucap/protocol-event": "~0.0.6",
|
||||
"@ucap/protocol-file": "~0.0.6",
|
||||
"@ucap/protocol-group": "~0.0.5",
|
||||
"@ucap/protocol-info": "~0.0.6",
|
||||
"@ucap/protocol-info": "~0.0.9",
|
||||
"@ucap/protocol-inner": "~0.0.4",
|
||||
"@ucap/protocol-option": "~0.0.7",
|
||||
"@ucap/protocol-ping": "~0.0.6",
|
||||
"@ucap/protocol-query": "~0.0.5",
|
||||
"@ucap/protocol-room": "~0.0.6",
|
||||
"@ucap/protocol-room": "~0.0.7",
|
||||
"@ucap/protocol-service": "~0.0.4",
|
||||
"@ucap/protocol-status": "~0.0.5",
|
||||
"@ucap/protocol-sync": "~0.0.4",
|
||||
"@ucap/protocol-sync": "~0.0.6",
|
||||
"@ucap/protocol-umg": "~0.0.5",
|
||||
"@ucap/ui-scss": "~0.0.5",
|
||||
"@ucap/web-socket": "~0.0.10",
|
||||
|
@ -109,13 +107,13 @@
|
|||
"file-type": "^14.1.4",
|
||||
"i18next": "^19.3.3",
|
||||
"i18next-browser-languagedetector": "^4.0.2",
|
||||
"i18next-node-fs-backend": "^2.1.3",
|
||||
"i18next-fs-backend": "^1.0.6",
|
||||
"i18next-xhr-backend": "^3.2.2",
|
||||
"libphonenumber-js": "^1.7.47",
|
||||
"memoize-one": "^5.1.1",
|
||||
"moment": "^2.24.0",
|
||||
"moment-timezone": "^0.5.28",
|
||||
"ngx-perfect-scrollbar": "^9.0.0",
|
||||
"ngx-virtual-scroller": "^4.0.3",
|
||||
"pino": "^6.0.0",
|
||||
"queueing-subject": "^0.3.4",
|
||||
"rxjs": "~6.5.4",
|
||||
|
@ -126,16 +124,16 @@
|
|||
"devDependencies": {
|
||||
"@angular-builders/custom-webpack": "^9.0.0",
|
||||
"@angular-devkit/build-angular": "~0.900.6",
|
||||
"@angular/cli": "~9.0.6",
|
||||
"@angular/compiler-cli": "~9.0.6",
|
||||
"@angular/language-service": "~9.0.6",
|
||||
"@angular/cli": "^9.1.9",
|
||||
"@angular/compiler-cli": "^9.1.11",
|
||||
"@angular/language-service": "^9.1.11",
|
||||
"@angularclass/hmr": "^2.1.3",
|
||||
"@ngrx/store-devtools": "^9.0.0",
|
||||
"@types/i18next-node-fs-backend": "^2.1.0",
|
||||
"@types/jasmine": "~3.5.0",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/node": "^12.11.1",
|
||||
"codelyzer": "^5.1.2",
|
||||
"cross-env": "^7.0.2",
|
||||
"fs-extra": "^9.0.0",
|
||||
"jasmine-core": "~3.5.0",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
|
@ -148,6 +146,7 @@
|
|||
"ts-node": "~8.3.0",
|
||||
"tslint": "~5.18.0",
|
||||
"typescript": "~3.7.5",
|
||||
"webpack-bundle-analyzer": "^3.7.0"
|
||||
"webpack-bundle-analyzer": "^3.7.0",
|
||||
"webpack-node-externals": "^1.7.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,10 +12,13 @@ import { AppAuthenticationGuard } from './guards/app-authentication.guard';
|
|||
import { AppSessionResolver } from './resolvers/app-session.resolver';
|
||||
|
||||
import { AppAuthenticationService } from './services/app-authentication.service';
|
||||
import { AppNotificationService } from './services/app-notification.service';
|
||||
import { AppNativeService } from './services/app-native.service';
|
||||
import { AppService } from './services/app.service';
|
||||
import { AppChatService } from './services/app-chat.service';
|
||||
import { AppFileService } from './services/app-file.service';
|
||||
import { AppGroupService } from './services/app-group.service';
|
||||
import { AppAccountService } from './services/app-account.service';
|
||||
|
||||
const GUARDS = [AppAuthenticationGuard];
|
||||
const RESOLVERS = [AppSessionResolver];
|
||||
|
@ -24,7 +27,10 @@ const SERVICES = [
|
|||
AppAuthenticationService,
|
||||
AppNativeService,
|
||||
AppFileService,
|
||||
AppChatService
|
||||
AppChatService,
|
||||
AppNotificationService,
|
||||
AppGroupService,
|
||||
AppAccountService
|
||||
];
|
||||
|
||||
const axiosFactory = () => {
|
||||
|
@ -35,6 +41,9 @@ const axiosFactory = () => {
|
|||
return i;
|
||||
};
|
||||
|
||||
const nativeServiceFactory = (nativeService: any) =>
|
||||
new environment.productConfig.nativeServiceClass(nativeService);
|
||||
|
||||
const appInit = (appService: AppService) => {
|
||||
return () => appService.initialize();
|
||||
};
|
||||
|
@ -51,7 +60,7 @@ const appInit = (appService: AppService) => {
|
|||
},
|
||||
{
|
||||
provide: UCAP_NATIVE_SERVICE,
|
||||
useClass: environment.productConfig.nativeServiceClass,
|
||||
useFactory: nativeServiceFactory,
|
||||
deps: [AXIOS_INSTANCE],
|
||||
multi: false
|
||||
},
|
||||
|
|
|
@ -81,7 +81,9 @@ const routes: Routes = [
|
|||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
imports: [
|
||||
RouterModule.forRoot(routes, { useHash: true, enableTracing: false })
|
||||
],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule {}
|
||||
|
|
|
@ -1,28 +1,48 @@
|
|||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
AfterViewInit,
|
||||
Renderer2
|
||||
} 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';
|
||||
import { fromEvent, interval, Subject } from 'rxjs';
|
||||
import { debounce, takeUntil } from 'rxjs/operators';
|
||||
import { AppAuthenticationService } from './services/app-authentication.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss']
|
||||
})
|
||||
export class AppComponent implements OnInit, OnDestroy {
|
||||
private resizeWindowSubscription: Subscription;
|
||||
export class AppComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
private ngOnDestroySubject: Subject<void> = new Subject();
|
||||
|
||||
constructor(private store: Store<any>) {
|
||||
this.resizeWindowSubscription = fromEvent(window, 'resize')
|
||||
.pipe(debounce(() => interval(100)))
|
||||
constructor(
|
||||
private renderer2: Renderer2,
|
||||
private store: Store<any>,
|
||||
private appAuthenticationService: AppAuthenticationService
|
||||
) {
|
||||
fromEvent(window, 'resize')
|
||||
.pipe(
|
||||
takeUntil(this.ngOnDestroySubject),
|
||||
debounce(() => interval(100))
|
||||
)
|
||||
.subscribe((event: any) => {
|
||||
this.dispatchWindowSize({
|
||||
width: event.target.innerWidth,
|
||||
height: event.target.innerHeight
|
||||
});
|
||||
});
|
||||
|
||||
// fromEvent(window, 'beforeunload')
|
||||
// .pipe(takeUntil(this.ngOnDestroySubject))
|
||||
// .subscribe((event: any) => {
|
||||
// this.appAuthenticationService.logout();
|
||||
// });
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
@ -33,11 +53,19 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.resizeWindowSubscription) {
|
||||
this.resizeWindowSubscription.unsubscribe();
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.next();
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
const preloader = this.renderer2.selectRootElement(
|
||||
'#ucap-lg-web-preloader'
|
||||
);
|
||||
this.renderer2.setStyle(preloader, 'display', 'none');
|
||||
}
|
||||
|
||||
private dispatchWindowSize(size: { width: number; height: number }) {
|
||||
this.store.dispatch(AppActions.windowResized(size));
|
||||
}
|
||||
|
|
|
@ -59,6 +59,8 @@ import { effects } from '@app/store/effects';
|
|||
import { ROOT_REDUCERS } from '@app/store/reducers';
|
||||
import { metaReducers } from '@app/store/state';
|
||||
|
||||
import { AppAccountDialogModule } from '@app/dialogs/account/account.dialog.module';
|
||||
|
||||
import { environment } from '@environments';
|
||||
|
||||
@NgModule({
|
||||
|
@ -69,6 +71,56 @@ import { environment } from '@environments';
|
|||
|
||||
FlexLayoutModule,
|
||||
|
||||
/**
|
||||
* StoreModule.forRoot is imported once in the root module, accepting a reducer
|
||||
* function or object map of reducer functions. If passed an object of
|
||||
* reducers, combineReducers will be run creating your application
|
||||
* meta-reducer. This returns all providers for an @ngrx/store
|
||||
* based application.
|
||||
*/
|
||||
StoreModule.forRoot(ROOT_REDUCERS, {
|
||||
metaReducers,
|
||||
runtimeChecks: {
|
||||
strictStateImmutability: true,
|
||||
strictActionImmutability: true,
|
||||
strictStateSerializability: true,
|
||||
strictActionSerializability: true
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* @ngrx/router-store keeps router state up-to-date in the store.
|
||||
*/
|
||||
StoreRouterConnectingModule.forRoot({
|
||||
routerState: RouterState.Minimal
|
||||
}),
|
||||
|
||||
/**
|
||||
* Store devtools instrument the store retaining past versions of state
|
||||
* and recalculating new states. This enables powerful time-travel
|
||||
* debugging.
|
||||
*
|
||||
* To use the debugger, install the Redux Devtools extension for either
|
||||
* Chrome or Firefox
|
||||
*
|
||||
* See: https://github.com/zalmoxisus/redux-devtools-extension
|
||||
*/
|
||||
StoreDevtoolsModule.instrument({
|
||||
name: 'UCAP Store App'
|
||||
|
||||
// In a production build you would want to disable the Store Devtools
|
||||
// logOnly: environment.production,
|
||||
}),
|
||||
|
||||
/**
|
||||
* EffectsModule.forRoot() is imported once in the root module and
|
||||
* sets up the effects class to be initialized immediately when the
|
||||
* application starts.
|
||||
*
|
||||
* See: https://ngrx.io/guide/effects#registering-root-effects
|
||||
*/
|
||||
EffectsModule.forRoot([...effects]),
|
||||
|
||||
LoggerModule.forRoot({}),
|
||||
|
||||
CommonApiModule.forRoot(environment.commonApiModuleConfig),
|
||||
|
@ -118,55 +170,7 @@ import { environment } from '@environments';
|
|||
|
||||
AppLayoutsModule,
|
||||
|
||||
/**
|
||||
* StoreModule.forRoot is imported once in the root module, accepting a reducer
|
||||
* function or object map of reducer functions. If passed an object of
|
||||
* reducers, combineReducers will be run creating your application
|
||||
* meta-reducer. This returns all providers for an @ngrx/store
|
||||
* based application.
|
||||
*/
|
||||
StoreModule.forRoot(ROOT_REDUCERS, {
|
||||
metaReducers,
|
||||
runtimeChecks: {
|
||||
strictStateImmutability: true,
|
||||
strictActionImmutability: true,
|
||||
strictStateSerializability: true,
|
||||
strictActionSerializability: true
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* @ngrx/router-store keeps router state up-to-date in the store.
|
||||
*/
|
||||
StoreRouterConnectingModule.forRoot({
|
||||
routerState: RouterState.Minimal
|
||||
}),
|
||||
|
||||
/**
|
||||
* Store devtools instrument the store retaining past versions of state
|
||||
* and recalculating new states. This enables powerful time-travel
|
||||
* debugging.
|
||||
*
|
||||
* To use the debugger, install the Redux Devtools extension for either
|
||||
* Chrome or Firefox
|
||||
*
|
||||
* See: https://github.com/zalmoxisus/redux-devtools-extension
|
||||
*/
|
||||
StoreDevtoolsModule.instrument({
|
||||
name: 'UCAP Store App'
|
||||
|
||||
// In a production build you would want to disable the Store Devtools
|
||||
// logOnly: environment.production,
|
||||
}),
|
||||
|
||||
/**
|
||||
* EffectsModule.forRoot() is imported once in the root module and
|
||||
* sets up the effects class to be initialized immediately when the
|
||||
* application starts.
|
||||
*
|
||||
* See: https://ngrx.io/guide/effects#registering-root-effects
|
||||
*/
|
||||
EffectsModule.forRoot([...effects])
|
||||
AppAccountDialogModule
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
|
|
|
@ -22,7 +22,7 @@ $typography: mat-typography-config(
|
|||
// Define the palettes for your theme using the Material Design palettes available in palette.scss
|
||||
// (imported above). For each palette, you can optionally specify a default, lighter, and darker
|
||||
// hue. Available color palettes: https://material.io/design/color/
|
||||
$lgRed-app-primary: mat-palette($ucap-color-primary);
|
||||
$lgRed-app-primary: mat-palette($ucap-color-primary, 600);
|
||||
$lgRed-app-accent: mat-palette($ucap-color-accent, 700);
|
||||
|
||||
// The warn palette is optional (defaults to red).
|
||||
|
|
48
src/app/dialogs/account/account.dialog.module.ts
Normal file
48
src/app/dialogs/account/account.dialog.module.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatRadioModule } from '@angular/material/radio';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
|
||||
import { I18nModule } from '@ucap/ng-i18n';
|
||||
import { UiModule } from '@ucap/ng-ui';
|
||||
|
||||
import { AppLayoutsModule } from '@app/layouts/layouts.module';
|
||||
import { AppAccountSectionModule } from '@app/sections/account/account.section.module';
|
||||
|
||||
import { COMPONENTS } from './components';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
ReactiveFormsModule,
|
||||
FlexLayoutModule,
|
||||
|
||||
MatButtonModule,
|
||||
MatCheckboxModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatRadioModule,
|
||||
MatSelectModule,
|
||||
MatTabsModule,
|
||||
|
||||
I18nModule,
|
||||
|
||||
UiModule,
|
||||
|
||||
AppLayoutsModule,
|
||||
AppAccountSectionModule
|
||||
],
|
||||
exports: [...COMPONENTS],
|
||||
declarations: [...COMPONENTS],
|
||||
entryComponents: [...COMPONENTS]
|
||||
})
|
||||
export class AppAccountDialogModule {}
|
3
src/app/dialogs/account/components/index.ts
Normal file
3
src/app/dialogs/account/components/index.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
import { SettingsDialogComponent } from './settings.dialog.component';
|
||||
|
||||
export const COMPONENTS = [SettingsDialogComponent];
|
|
@ -0,0 +1,471 @@
|
|||
<div class="dialog-container">
|
||||
<app-layouts-default-dialog
|
||||
[disableClose]="false"
|
||||
(closed)="onClosed($event)"
|
||||
class="create-dialog-container"
|
||||
>
|
||||
<div appLayoutsDefaultDialog="header">
|
||||
{{ 'organization:settings.label' | ucapI18n }}
|
||||
</div>
|
||||
<div class="dialog-body" appLayoutsDefaultDialog="body">
|
||||
<mat-tab-group mat-stretch-tabs>
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<p>{{ 'organization:settings.sub.general' | ucapI18n }}</p>
|
||||
</ng-template>
|
||||
<div class="default-settings-area">
|
||||
<!-- 기본설정 -->
|
||||
<div
|
||||
*ngIf="'electron' === platform"
|
||||
class="messenger-settings-area login-setting-box"
|
||||
>
|
||||
<div class="title-settings-subject">
|
||||
<span>{{
|
||||
'authentication:login.settings.login' | ucapI18n
|
||||
}}</span>
|
||||
<button mat-icon-button color="accent" class="btn-subject-info">
|
||||
<mat-icon>info_outline</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="settings-contents">
|
||||
<ul>
|
||||
<li>
|
||||
<mat-checkbox>
|
||||
{{
|
||||
'authentication:login.settings.autoStartOnBoot'
|
||||
| ucapI18n
|
||||
}}
|
||||
</mat-checkbox>
|
||||
</li>
|
||||
<li>
|
||||
<mat-checkbox>
|
||||
{{ 'authentication:login.settings.autoLogin' | ucapI18n }}
|
||||
</mat-checkbox>
|
||||
</li>
|
||||
<li>
|
||||
<mat-checkbox>
|
||||
{{ 'authentication:login.settings.autoHide' | ucapI18n }}
|
||||
</mat-checkbox>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="messenger-settings-area language-setting-box">
|
||||
<div class="title-settings-subject">
|
||||
<span>{{
|
||||
'organization:settings.language.messenger' | ucapI18n
|
||||
}}</span>
|
||||
<button mat-icon-button color="accent" class="btn-subject-info">
|
||||
<mat-icon>info_outline</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="settings-contents">
|
||||
<mat-form-field
|
||||
color="accent"
|
||||
appearance="standard"
|
||||
class="setting-select-obj ucap-mat-input-container"
|
||||
>
|
||||
<mat-select
|
||||
[value]="generalSetting.locale"
|
||||
(selectionChange)="onSelectionChangeLanguage($event)"
|
||||
>
|
||||
<mat-option
|
||||
*ngFor="let languageCode of supportedLanguages"
|
||||
[value]="languageCode"
|
||||
>
|
||||
{{ 'locale:languages.' + languageCode | ucapI18n }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="messenger-settings-area language-setting-box">
|
||||
<div class="title-settings-subject">
|
||||
<span>{{
|
||||
'organization:settings.language.hr' | ucapI18n
|
||||
}}</span>
|
||||
<button mat-icon-button color="accent" class="btn-subject-info">
|
||||
<mat-icon>info_outline</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="settings-contents">
|
||||
<mat-form-field
|
||||
color="accent"
|
||||
appearance="standard"
|
||||
class="setting-select-obj ucap-mat-input-container"
|
||||
>
|
||||
<mat-select
|
||||
[value]="generalSetting.hrInfoLocale"
|
||||
(selectionChange)="onSelectionChangeHrLanguage($event)"
|
||||
>
|
||||
<mat-option
|
||||
*ngFor="let languageCode of supportedHrLanguages"
|
||||
[value]="languageCode"
|
||||
>
|
||||
{{ 'locale:languages.' + languageCode | ucapI18n }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="messenger-settings-area time-setting-box">
|
||||
<div class="title-settings-subject">
|
||||
<span>{{ 'organization:settings.timezone' | ucapI18n }}</span>
|
||||
<button mat-icon-button color="accent" class="btn-subject-info">
|
||||
<mat-icon>info_outline</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="settings-contents">
|
||||
<mat-form-field
|
||||
color="accent"
|
||||
appearance="standard"
|
||||
class="setting-select-obj ucap-mat-input-container"
|
||||
>
|
||||
<mat-select
|
||||
#selectForTimezone
|
||||
[formControl]="formControlForTimezone"
|
||||
[placeholder]="timezonePlaceholder"
|
||||
[value]="generalSetting.timezone"
|
||||
(openedChange)="onOpenedChangeTimezone($event)"
|
||||
>
|
||||
<ucap-virtual-scroll-viewport
|
||||
class="general-timezone-viewport"
|
||||
#vsTimezone
|
||||
itemSize="48"
|
||||
[style.height.px]="5 * 48"
|
||||
>
|
||||
<mat-option
|
||||
*ucapVirtualFor="let timezone of timezoneList"
|
||||
[value]="timezone.name"
|
||||
(onSelectionChange)="onSelectionChangeTimezone($event)"
|
||||
>
|
||||
{{ timezone.displayName }}
|
||||
</mat-option>
|
||||
</ucap-virtual-scroll-viewport>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-tab>
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<p>{{ 'organization:settings.sub.notification' | ucapI18n }}</p>
|
||||
</ng-template>
|
||||
<div class="allim-settings-area">
|
||||
<!-- 알림 -->
|
||||
<div class="messenger-settings-area">
|
||||
<div class="title-settings-subject">
|
||||
{{ 'organization:settings.notification.receival' | ucapI18n }}
|
||||
</div>
|
||||
<div class="settings-contents">
|
||||
<mat-radio-group
|
||||
aria-label="Select an type of alarm"
|
||||
class="settings-radio-group"
|
||||
>
|
||||
<mat-radio-button value="true">
|
||||
{{
|
||||
'organization:settings.notification.receive' | ucapI18n
|
||||
}}
|
||||
</mat-radio-button>
|
||||
<mat-radio-button value="false">
|
||||
{{
|
||||
'organization:settings.notification.notReceive' | ucapI18n
|
||||
}}
|
||||
</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="messenger-settings-area allim-way-box">
|
||||
<div class="title-settings-subject">
|
||||
{{ 'organization:settings.notification.method' | ucapI18n }}
|
||||
</div>
|
||||
<div class="settings-contents">
|
||||
<mat-form-field
|
||||
color="accent"
|
||||
appearance="standard"
|
||||
class="setting-select-obj ucap-mat-input-container"
|
||||
>
|
||||
<mat-select>
|
||||
<mat-option value="sound">
|
||||
{{
|
||||
'organization:settings.notification.methodTypeSound'
|
||||
| ucapI18n
|
||||
}}
|
||||
</mat-option>
|
||||
<mat-option value="alert">
|
||||
{{
|
||||
'organization:settings.notification.methodTypeAlert'
|
||||
| ucapI18n
|
||||
}}
|
||||
</mat-option>
|
||||
<mat-option value="soundAndAlert">
|
||||
{{
|
||||
'organization:settings.notification.methodTypeSoundAndAlert'
|
||||
| ucapI18n
|
||||
}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="messenger-settings-area allim-time-box">
|
||||
<div class="title-settings-subject">
|
||||
{{
|
||||
'organization:settings.notification.settingOfAlertWindow'
|
||||
| ucapI18n
|
||||
}}
|
||||
</div>
|
||||
<div class="settings-contents">
|
||||
<mat-form-field
|
||||
color="accent"
|
||||
appearance="standard"
|
||||
class="setting-select-obj ucap-mat-input-container"
|
||||
>
|
||||
<mat-select>
|
||||
<mat-option value="5">
|
||||
5{{ 'common:units.second' | ucapI18n }}
|
||||
</mat-option>
|
||||
<mat-option value="10">
|
||||
10{{ 'common:units.second' | ucapI18n }}
|
||||
</mat-option>
|
||||
<mat-option value="15">
|
||||
15{{ 'common:units.second' | ucapI18n }}
|
||||
</mat-option>
|
||||
<mat-option value="20">
|
||||
20{{ 'common:units.second' | ucapI18n }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="messenger-settings-area">
|
||||
<div class="title-settings-subject">
|
||||
{{
|
||||
'organization:settings.notification.receiveForMobile'
|
||||
| ucapI18n
|
||||
}}
|
||||
</div>
|
||||
<div class="settings-contents">
|
||||
<ul>
|
||||
<li>
|
||||
<mat-checkbox>
|
||||
{{
|
||||
'organization:settings.notification.receiveForMessageTypePopup'
|
||||
| ucapI18n
|
||||
}}
|
||||
</mat-checkbox>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-tab>
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<p>{{ 'chat:settings.label' | ucapI18n }}</p>
|
||||
</ng-template>
|
||||
<div class="chat-settings-area">
|
||||
<!-- 대화 -->
|
||||
<div class="messenger-settings-area">
|
||||
<div class="title-settings-subject">파일 전송</div>
|
||||
<div class="settings-contents02">
|
||||
<div class="subtitle-settings-info">
|
||||
다운로드 폴더
|
||||
</div>
|
||||
<div class="settings-sub-content sub-set-content">
|
||||
<mat-form-field
|
||||
color="accent"
|
||||
class="setting-input-obj input-set-obj ucap-mat-input-container"
|
||||
>
|
||||
<input
|
||||
matInput
|
||||
placeholder=""
|
||||
value=""
|
||||
[readonly]="'browser' === platform"
|
||||
/>
|
||||
<button
|
||||
mat-button
|
||||
matSuffix
|
||||
mat-icon-button
|
||||
aria-label="file"
|
||||
[disabled]="'browser' === platform"
|
||||
>
|
||||
<mat-icon>folder</mat-icon>
|
||||
</button>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<button
|
||||
mat-stroked-button
|
||||
color="primary"
|
||||
class="btn-folder-first"
|
||||
[disabled]="'browser' === platform"
|
||||
>
|
||||
폴더 초기화
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-tab>
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<p>{{ 'call:settings.label' | ucapI18n }}</p>
|
||||
</ng-template>
|
||||
<div class="call-settings-area">
|
||||
<!-- 통화 -->
|
||||
<div class="messenger-settings-area">
|
||||
<div class="title-settings-subject">
|
||||
<span>Click to Call</span>
|
||||
<em>
|
||||
| PC Messenger에서 Click to Call 기능을 사용할 기기 설정</em
|
||||
>
|
||||
</div>
|
||||
<div class="settings-contents">
|
||||
<mat-radio-group
|
||||
aria-label="Select an type of alarm"
|
||||
class="settings-radio-group"
|
||||
>
|
||||
<mat-radio-button value="true">휴대폰</mat-radio-button>
|
||||
<mat-radio-button value="false">사무실</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-tab>
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<p>{{ 'authentication:password.settings.label' | ucapI18n }}</p>
|
||||
</ng-template>
|
||||
<div class="secret-num-settings-area">
|
||||
<!-- 비밀번호 -->
|
||||
<div class="messenger-settings-area">
|
||||
<div class="title-settings-subject">
|
||||
{{ 'authentication:password.fields.changePassword' | ucapI18n }}
|
||||
</div>
|
||||
<div class="settings-contents02">
|
||||
<div class="subtitle-settings-info">
|
||||
{{
|
||||
'authentication:password.fields.currentPassword' | ucapI18n
|
||||
}}
|
||||
</div>
|
||||
<div class="settings-sub-content">
|
||||
<mat-form-field
|
||||
color="accent"
|
||||
class="setting-input-obj ucap-mat-input-container"
|
||||
>
|
||||
<input
|
||||
matInput
|
||||
placeholder="{{
|
||||
'authentication:password.placeholder.currentPassword'
|
||||
| ucapI18n
|
||||
}}"
|
||||
value=""
|
||||
/>
|
||||
<button
|
||||
mat-button
|
||||
matSuffix
|
||||
mat-icon-button
|
||||
aria-label="action"
|
||||
>
|
||||
<mat-icon>done</mat-icon>
|
||||
</button>
|
||||
<mat-hint>Hint</mat-hint>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="settings-contents02">
|
||||
<div class="subtitle-settings-info">
|
||||
{{ 'authentication:password.fields.newPassword' | ucapI18n }}
|
||||
</div>
|
||||
<div class="settings-sub-content">
|
||||
<mat-form-field
|
||||
color="accent"
|
||||
class="setting-input-obj ucap-mat-input-container"
|
||||
>
|
||||
<input
|
||||
matInput
|
||||
placeholder="{{
|
||||
'authentication:password.placeholder.newPassword'
|
||||
| ucapI18n
|
||||
}}"
|
||||
value=""
|
||||
/>
|
||||
<button
|
||||
mat-button
|
||||
matSuffix
|
||||
mat-icon-button
|
||||
aria-label="action"
|
||||
>
|
||||
<mat-icon>done</mat-icon>
|
||||
</button>
|
||||
<mat-hint
|
||||
>반드시 영어 소문자, 숫자, 특수문자 중 2가지 이상 사용해야
|
||||
합니다.</mat-hint
|
||||
>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field
|
||||
color="accent"
|
||||
class="setting-input-obj ucap-mat-input-container"
|
||||
>
|
||||
<input
|
||||
matInput
|
||||
placeholder="{{
|
||||
'authentication:password.placeholder.newPasswordConfirm'
|
||||
| ucapI18n
|
||||
}}"
|
||||
value=""
|
||||
/>
|
||||
<button
|
||||
mat-button
|
||||
matSuffix
|
||||
mat-icon-button
|
||||
aria-label="action"
|
||||
>
|
||||
<mat-icon>done</mat-icon>
|
||||
</button>
|
||||
<mat-error>Error</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pass-info-box">
|
||||
<dl>
|
||||
<dt>
|
||||
<mat-icon color="accent" class="bullet-ico-info"
|
||||
>info_outline</mat-icon
|
||||
>
|
||||
{{ 'authentication:password.notice.condition' | ucapI18n }}
|
||||
</dt>
|
||||
<dd>
|
||||
{{ 'authentication:password.notice.condition1' | ucapI18n }}
|
||||
</dd>
|
||||
<dd>
|
||||
{{ 'authentication:password.notice.condition2' | ucapI18n }}
|
||||
</dd>
|
||||
<dd>
|
||||
{{ 'authentication:password.notice.condition3' | ucapI18n }}
|
||||
</dd>
|
||||
<dd>
|
||||
{{ 'authentication:password.notice.condition4' | ucapI18n }}
|
||||
</dd>
|
||||
<dd>
|
||||
{{ 'authentication:password.notice.condition5' | ucapI18n }}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</div>
|
||||
<div appLayoutsDefaultDialog="action" class="btn-box">
|
||||
<button mat-stroked-button class="mat-basic" (click)="onCancel()">
|
||||
{{ 'common:messages.cancel' | ucapI18n }}
|
||||
</button>
|
||||
<button mat-flat-button class="bg-primary-darkest" (click)="onConfirm()">
|
||||
{{ 'common:messages.confirm' | ucapI18n }}
|
||||
</button>
|
||||
</div>
|
||||
</app-layouts-default-dialog>
|
||||
</div>
|
|
@ -0,0 +1,224 @@
|
|||
@import '~@ucap/lg-scss/mixins';
|
||||
.dialog-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.dialog-body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.messenger-settings-area {
|
||||
border-top: 10px solid #f1f2f6;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
padding: 10px 16px 9px;
|
||||
&:first-of-type {
|
||||
border-top: 0;
|
||||
padding: 20px 16px 9px;
|
||||
}
|
||||
.title-settings-subject {
|
||||
color: #5c444b;
|
||||
font-size: 1.071em;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
padding: 6px 0 7px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
min-height: 30px;
|
||||
span {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
em {
|
||||
flex: 0 1 auto;
|
||||
justify-self: start;
|
||||
font-style: normal;
|
||||
font-size: 0.929em;
|
||||
color: #999;
|
||||
font-weight: 400;
|
||||
@include wordBreak();
|
||||
@include ellipsis-column(1);
|
||||
width: 100%;
|
||||
}
|
||||
.btn-subject-info {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
.settings-contents {
|
||||
ul {
|
||||
li {
|
||||
padding: 6px 0 7px;
|
||||
}
|
||||
}
|
||||
.settings-radio-group {
|
||||
padding: 6px 0 7px;
|
||||
height: 42px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
.mat-radio-button {
|
||||
margin-left: 20%;
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.settings-contents02 {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-top: 25px;
|
||||
@include screen(xs) {
|
||||
flex-direction: column;
|
||||
}
|
||||
.subtitle-settings-info {
|
||||
font-size: 0.929em;
|
||||
color: #262626;
|
||||
align-self: baseline;
|
||||
@include screen(xs) {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
}
|
||||
.settings-sub-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 25px;
|
||||
width: 60%;
|
||||
@include screen(xs) {
|
||||
flex: 0 1 auto;
|
||||
width: 100% !important;
|
||||
margin-left: 0;
|
||||
}
|
||||
.setting-input-obj {
|
||||
@include ucapMatFormField(0, 0, 0%, 100%, 100%, 60px, 11px);
|
||||
overflow: hidden;
|
||||
margin-top: 15px;
|
||||
&:first-of-type {
|
||||
margin-top: 0;
|
||||
@include screen(xs) {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
.mat-hint {
|
||||
line-height: 1.3;
|
||||
}
|
||||
}
|
||||
&.sub-set-content {
|
||||
width: auto;
|
||||
flex: 1 1 auto;
|
||||
.input-set-obj {
|
||||
@include ucapMatFormField(0, 0, 0%, 100%, 100%, 55px, 11px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.default-settings-area {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
.login-setting-box {
|
||||
}
|
||||
.language-setting-box,
|
||||
.time-setting-box {
|
||||
.settings-contents {
|
||||
width: 50%;
|
||||
height: 50px;
|
||||
@include screen(mid) {
|
||||
width: 70%;
|
||||
}
|
||||
@include screen(xs) {
|
||||
width: 100%;
|
||||
}
|
||||
.setting-select-obj {
|
||||
@include ucapMatFormField(0, 0, 100%, 100%, 100%, 40px, 28px);
|
||||
//@include ucapMatSelect(25px, 0 0);
|
||||
|
||||
.general-timezone-viewport {
|
||||
height: 250px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.allim-settings-area {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
.allim-way-box,
|
||||
.allim-time-box {
|
||||
.settings-contents {
|
||||
width: 50%;
|
||||
height: 50px;
|
||||
@include screen(mid) {
|
||||
width: 70%;
|
||||
}
|
||||
@include screen(xs) {
|
||||
width: 100%;
|
||||
}
|
||||
.setting-select-obj {
|
||||
@include ucapMatFormField(0, 0, 100%, 100%, 100%, 40px, 28px);
|
||||
//@include ucapMatSelect(25px, 0 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.chat-settings-area {
|
||||
}
|
||||
.call-settings-area {
|
||||
}
|
||||
.secret-num-settings-area {
|
||||
.pass-info-box {
|
||||
dl {
|
||||
border: 1px solid #dfe0e8;
|
||||
background-color: #f7f8fa;
|
||||
dt {
|
||||
height: 30px;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
font-size: 0.857em;
|
||||
font-weight: 600;
|
||||
.bullet-ico-info {
|
||||
flex: 0 0 36px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
}
|
||||
dd {
|
||||
font-size: 0.857em;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
margin: 2px 36px 3px;
|
||||
&:first-of-type {
|
||||
margin-top: 10px;
|
||||
}
|
||||
&:last-of-type {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.btn-box {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
button {
|
||||
@include ucap-button-flat-stroked(120px);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { DebugElement } from '@angular/core';
|
||||
|
||||
import { SettingsDialogComponent } from './settings.dialog.component';
|
||||
|
||||
describe('app::account::SettingsDialogComponent', () => {
|
||||
let component: SettingsDialogComponent;
|
||||
let fixture: ComponentFixture<SettingsDialogComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [SettingsDialogComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SettingsDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
189
src/app/dialogs/account/components/settings.dialog.component.ts
Normal file
189
src/app/dialogs/account/components/settings.dialog.component.ts
Normal file
|
@ -0,0 +1,189 @@
|
|||
import moment from 'moment';
|
||||
import 'moment-timezone';
|
||||
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Inject,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
|
||||
import {
|
||||
MatDialogRef,
|
||||
MAT_DIALOG_DATA,
|
||||
MatDialog
|
||||
} from '@angular/material/dialog';
|
||||
import { MatSelectChange, MatSelect } from '@angular/material/select';
|
||||
|
||||
import { NativeService, NativeType } from '@ucap/native';
|
||||
|
||||
import { UCAP_NATIVE_SERVICE } from '@ucap/ng-native';
|
||||
|
||||
import { environment } from '@environments';
|
||||
|
||||
import {
|
||||
Settings,
|
||||
GeneralSetting,
|
||||
NotificationSetting,
|
||||
ChatSetting,
|
||||
PresenceSetting
|
||||
} from '@app/models/settings';
|
||||
import { I18nService } from '@ucap/ng-i18n';
|
||||
import { VirtualScrollViewportComponent } from '@ucap/ng-ui';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { MatOptionSelectionChange } from '@angular/material/core';
|
||||
|
||||
export interface TimezoneData {
|
||||
displayName: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface SettingsDialogData {
|
||||
settings: Settings;
|
||||
}
|
||||
export interface SettingsDialogResult {}
|
||||
|
||||
@Component({
|
||||
selector: 'app-sections-account-settings',
|
||||
templateUrl: './settings.dialog.component.html',
|
||||
styleUrls: ['./settings.dialog.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class SettingsDialogComponent implements OnInit, OnDestroy {
|
||||
@ViewChild('vsTimezone', { static: false })
|
||||
vsTimezone: VirtualScrollViewportComponent;
|
||||
|
||||
@ViewChild('selectForTimezone', { static: false })
|
||||
selectForTimezone: MatSelect;
|
||||
|
||||
formControlForTimezone = new FormControl();
|
||||
|
||||
platform: 'browser' | 'electron' = 'electron';
|
||||
|
||||
generalSetting: GeneralSetting;
|
||||
notificationSetting: NotificationSetting;
|
||||
chatSetting: ChatSetting;
|
||||
presenceSetting: PresenceSetting;
|
||||
|
||||
timezoneList: TimezoneData[];
|
||||
timezonePlaceholder: string;
|
||||
|
||||
supportedLanguages = environment.productConfig.supportedLanguages;
|
||||
supportedHrLanguages =
|
||||
environment.productConfig.organization.supportedLanguages;
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<SettingsDialogData, SettingsDialogResult>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: SettingsDialogData,
|
||||
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
|
||||
private i18nService: I18nService,
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
public matDialog: MatDialog
|
||||
) {
|
||||
this.nativeService.platform_nativeType().then((type) => {
|
||||
switch (type) {
|
||||
case NativeType.Browser:
|
||||
this.platform = 'browser';
|
||||
break;
|
||||
case NativeType.Electron:
|
||||
this.platform = 'electron';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
this.generalSetting = data.settings.general;
|
||||
this.notificationSetting = data.settings.notification;
|
||||
this.chatSetting = data.settings.chat;
|
||||
this.presenceSetting = data.settings.presence;
|
||||
}
|
||||
|
||||
private ngOnDestroySubject: Subject<void> = new Subject();
|
||||
|
||||
ngOnInit(): void {
|
||||
this.generateTimezoneData();
|
||||
|
||||
this.i18nService.languageChanged$
|
||||
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||
.subscribe((lang) => {
|
||||
this.generateTimezoneData();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.next();
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
|
||||
onSelectionChangeLanguage(event: MatSelectChange) {}
|
||||
|
||||
onSelectionChangeHrLanguage(event: MatSelectChange) {}
|
||||
|
||||
onOpenedChangeTimezone(opened: boolean) {
|
||||
if (opened) {
|
||||
this.setTimezoneData();
|
||||
this.vsTimezone.checkViewportSize();
|
||||
}
|
||||
}
|
||||
|
||||
onSelectionChangeTimezone(event: MatOptionSelectionChange) {
|
||||
if (!event.isUserInput) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
onClosed(event: MouseEvent): void {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
onCancel() {}
|
||||
|
||||
onConfirm() {}
|
||||
|
||||
private generateTimezoneData() {
|
||||
const timezoneData = this.i18nService.t('locale:timezone', {
|
||||
returnObjects: true
|
||||
});
|
||||
|
||||
let timezoneList: TimezoneData[] = [];
|
||||
for (const name of moment.tz.names()) {
|
||||
const displayName = `(UTC${moment.tz(name).format('Z')}) ${
|
||||
timezoneData[name]
|
||||
}`;
|
||||
timezoneList.push({
|
||||
displayName,
|
||||
name
|
||||
});
|
||||
if (name === this.generalSetting.timezone) {
|
||||
this.timezonePlaceholder = displayName;
|
||||
}
|
||||
}
|
||||
timezoneList = timezoneList.sort((a: TimezoneData, b: TimezoneData) => {
|
||||
return a.displayName.localeCompare(b.displayName);
|
||||
});
|
||||
|
||||
this.timezoneList = timezoneList;
|
||||
}
|
||||
|
||||
private setTimezoneData() {
|
||||
const timezoneIndex = this.timezoneList.findIndex(
|
||||
(t) => t.name === this.generalSetting.timezone
|
||||
);
|
||||
|
||||
if (-1 !== timezoneIndex) {
|
||||
if (!!this.vsTimezone && !!this.selectForTimezone) {
|
||||
this.vsTimezone.scrollToIndex(timezoneIndex);
|
||||
this.selectForTimezone.value = this.timezoneList[timezoneIndex].name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,17 @@
|
|||
<div class="layout-container" fxLayout="column">
|
||||
<div class="layout-header" fxFlex="60px" fxLayout="row">
|
||||
<div class="layout-header" fxFlex="50px" fxLayout="row">
|
||||
<div fxFlex="1 1 auto">
|
||||
<ng-content
|
||||
class="layout-header-content"
|
||||
select="[appLayoutsDefaultDialog='header']"
|
||||
></ng-content>
|
||||
</div>
|
||||
<div fxFlex="1 0 auto">
|
||||
<ng-content
|
||||
class="layout-sub-header-content"
|
||||
select="[appLayoutsDefaultDialog='sub-header']"
|
||||
></ng-content>
|
||||
</div>
|
||||
<div fxFlex="30px" *ngIf="!disableClose">
|
||||
<button
|
||||
class="icon-button btn-dialog-close"
|
||||
|
@ -15,13 +21,11 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-body" fxFlex="1 1 auto">
|
||||
<perfect-scrollbar style="width: 100%; height: 100%;">
|
||||
<div class="layout-body" fxFlex="1 1 auto" perfectScrollbar>
|
||||
<ng-content
|
||||
class="layout-body-content"
|
||||
select="[appLayoutsDefaultDialog='body']"
|
||||
></ng-content>
|
||||
</perfect-scrollbar>
|
||||
</div>
|
||||
<div class="layout-action" fxFlex="60px">
|
||||
<ng-content
|
||||
|
|
|
@ -3,6 +3,13 @@
|
|||
height: 100%;
|
||||
|
||||
.layout-header {
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
font-size: 1.143em;
|
||||
border-bottom: 1px solid #666;
|
||||
margin: 0 16px;
|
||||
.layout-header-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@ -17,6 +24,9 @@
|
|||
}
|
||||
}
|
||||
.layout-action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
.layout-action-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
|
|
@ -4,14 +4,8 @@ import {
|
|||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Inject,
|
||||
ViewChild,
|
||||
ComponentFactoryResolver,
|
||||
ViewContainerRef,
|
||||
ComponentRef,
|
||||
Input,
|
||||
EventEmitter,
|
||||
Output
|
||||
|
@ -40,6 +34,7 @@ export class DefaultDialogLayoutComponent implements OnInit, OnDestroy {
|
|||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.next();
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<div class="layout-container" fxLayout="column">
|
||||
<div class="layout-header" fxFlex="60px" fxLayout="row">
|
||||
<div fxFlex="1 1 auto">
|
||||
<ng-content
|
||||
class="layout-header-content"
|
||||
select="[appLayoutsDefaultDrawer='header']"
|
||||
></ng-content>
|
||||
</div>
|
||||
<div fxFlex="30px" *ngIf="!disableClose">
|
||||
<button
|
||||
class="icon-button btn-dialog-close"
|
||||
(click)="onClickClose($event)"
|
||||
>
|
||||
<i class="mdi mdi-window-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-body" fxFlex="1 1 auto">
|
||||
<perfect-scrollbar style="width: 100%; height: 100%;">
|
||||
<ng-content
|
||||
class="layout-body-content"
|
||||
select="[appLayoutsDefaultDrawer='body']"
|
||||
></ng-content>
|
||||
</perfect-scrollbar>
|
||||
</div>
|
||||
<div class="layout-action" fxFlex="60px">
|
||||
<ng-content
|
||||
class="layout-action-content"
|
||||
select="[appLayoutsDefaultDrawer='action']"
|
||||
></ng-content>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,35 @@
|
|||
.layout-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.layout-header {
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
font-size: 1.143em;
|
||||
border-bottom: 1px solid #333;
|
||||
margin: 0 16px;
|
||||
.layout-header-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.layout-body {
|
||||
overflow: auto;
|
||||
|
||||
.layout-body-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.layout-action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
.layout-action-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { DebugElement } from '@angular/core';
|
||||
|
||||
import { DefaultDrawerLayoutComponent } from './default-drawer.layout.component';
|
||||
|
||||
describe('app::layouts::DefaultDrawerLayoutComponent', () => {
|
||||
let component: DefaultDrawerLayoutComponent;
|
||||
let fixture: ComponentFixture<DefaultDrawerLayoutComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [DefaultDrawerLayoutComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DefaultDrawerLayoutComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,45 @@
|
|||
import { Subject } from 'rxjs';
|
||||
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Input,
|
||||
EventEmitter,
|
||||
Output
|
||||
} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-layouts-default-drawer',
|
||||
templateUrl: './default-drawer.layout.component.html',
|
||||
styleUrls: ['./default-drawer.layout.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class DefaultDrawerLayoutComponent implements OnInit, OnDestroy {
|
||||
@Input()
|
||||
disableClose = false;
|
||||
|
||||
@Output()
|
||||
closed = new EventEmitter<MouseEvent>();
|
||||
|
||||
constructor(private changeDetectorRef: ChangeDetectorRef) {}
|
||||
|
||||
private ngOnDestroySubject: Subject<boolean>;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.ngOnDestroySubject = new Subject<boolean>();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.next();
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
|
||||
onClickClose(event: MouseEvent): void {
|
||||
this.closed.emit(event);
|
||||
}
|
||||
}
|
|
@ -3,12 +3,13 @@
|
|||
<div class="gnb">
|
||||
<mat-toolbar class="mat-gnb-toolbar"
|
||||
><img
|
||||
src="../../../assets/images/logo_140.png"
|
||||
src="assets/images/logo_140.png"
|
||||
alt=""
|
||||
class="img-logo"
|
||||
width="32"
|
||||
/></mat-toolbar>
|
||||
<mat-tab-group
|
||||
#navTabGroup
|
||||
mat-stretch-tabs
|
||||
animationDuration="0ms"
|
||||
backgroundColor="transparent"
|
||||
|
@ -17,7 +18,12 @@
|
|||
>
|
||||
<mat-tab aria-label="Group">
|
||||
<ng-template mat-tab-label>
|
||||
<div class="icon-item" matTooltip="그룹" matTooltipPosition="after">
|
||||
<div
|
||||
class="icon-item"
|
||||
[matTooltip]="'common:tooltip.group' | ucapI18n"
|
||||
matTooltipPosition="after"
|
||||
(click)="onClickToggleLeftSidenav()"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16.871"
|
||||
|
@ -63,10 +69,13 @@
|
|||
<ng-template mat-tab-label>
|
||||
<div
|
||||
class="icon-item"
|
||||
matBadgeHidden="false"
|
||||
matBadge="275"
|
||||
[matTooltip]="'common:tooltip.chat' | ucapI18n"
|
||||
matTooltipPosition="after"
|
||||
[matBadgeHidden]="unreadCountChat <= 0"
|
||||
[matBadge]="unreadCountChat"
|
||||
matBadgeColor="accent"
|
||||
matBadgePosition="above after"
|
||||
(click)="onClickToggleLeftSidenav()"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
@ -95,7 +104,12 @@
|
|||
</mat-tab>
|
||||
<mat-tab aria-label="Organization">
|
||||
<ng-template mat-tab-label>
|
||||
<div class="icon-item">
|
||||
<div
|
||||
class="icon-item"
|
||||
[matTooltip]="'common:tooltip.organization' | ucapI18n"
|
||||
matTooltipPosition="after"
|
||||
(click)="onClickToggleLeftSidenav()"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="21"
|
||||
|
@ -146,7 +160,12 @@
|
|||
|
||||
<mat-tab aria-label="Message">
|
||||
<ng-template mat-tab-label>
|
||||
<div class="icon-item">
|
||||
<div
|
||||
class="icon-item"
|
||||
[matTooltip]="'common:tooltip.message' | ucapI18n"
|
||||
matTooltipPosition="after"
|
||||
(click)="onClickToggleLeftSidenav()"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="21.096"
|
||||
|
@ -180,7 +199,12 @@
|
|||
|
||||
<mat-tab aria-label="Call">
|
||||
<ng-template mat-tab-label>
|
||||
<div class="icon-item">
|
||||
<div
|
||||
class="icon-item"
|
||||
[matTooltip]="'common:tooltip.call' | ucapI18n"
|
||||
matTooltipPosition="after"
|
||||
(click)="onClickToggleLeftSidenav()"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="18"
|
||||
|
@ -208,27 +232,159 @@
|
|||
</mat-tab-group>
|
||||
|
||||
<div class="btn-homepage-area">
|
||||
<button mat-button aria-label="">
|
||||
<em>Homepage</em>
|
||||
<button mat-button aria-label="Homepage" matTooltip="Homepage">
|
||||
<!--<em>Homepage</em>-->
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<ucap-float-action-button
|
||||
*ngIf="fabButtonShow"
|
||||
[buttons]="fabButtons"
|
||||
(buttonClick)="onClickFab($event)"
|
||||
>
|
||||
</ucap-float-action-button>
|
||||
</div>
|
||||
<div class="content-page" fxFlex="1 1 auto" fxLayout="column">
|
||||
<div class="content-body" fxFlex="1 1 auto">
|
||||
<mat-sidenav-container autosize="true">
|
||||
<mat-sidenav-container hasBackdrop="false" autosize="true">
|
||||
<mat-sidenav
|
||||
#leftSidenav
|
||||
class="left-sidenav"
|
||||
mode="side"
|
||||
opened="true"
|
||||
[disableClose]="true"
|
||||
>
|
||||
<ucap-float-action-button
|
||||
*ngIf="fabButtonShow"
|
||||
[buttons]="fabButtons"
|
||||
[useCustomDefaultIcon]="fabUseCustomDefaultIcon"
|
||||
(buttonClick)="onClickFab($event)"
|
||||
>
|
||||
<div *ngIf="tabIndex === 'group'" ucapFloatActionButton="mainIcon">
|
||||
<mat-icon class="ico-font-float">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="44"
|
||||
height="44"
|
||||
viewBox="0 0 44 44"
|
||||
>
|
||||
<g transform="translate(4 7)">
|
||||
<path
|
||||
fill="none"
|
||||
d="M0 0H44V44H0z"
|
||||
transform="translate(-4 -7)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0L12 0"
|
||||
class="prefix_cls-2"
|
||||
transform="translate(24.357 7.26)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0L12 0"
|
||||
class="prefix_cls-2"
|
||||
transform="rotate(90 14.549 16.049)"
|
||||
/>
|
||||
<path
|
||||
fill="rgba(0,0,0,0)"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="bevel"
|
||||
stroke-miterlimit="10"
|
||||
stroke-width="3px"
|
||||
d="M26.25 24.125a11.125 11.125 0 1 0-22.25 0"
|
||||
class="prefix_cls-3"
|
||||
transform="translate(-3.5 2.657)"
|
||||
/>
|
||||
<g fill="none" stroke="none">
|
||||
<path
|
||||
d="M7.08 0a7.08 7.08 0 0 1 7.08 7.08 7.318 7.318 0 0 1-2.753 5.6 6.7 6.7 0 0 1-4.327 1.479A7.08 7.08 0 0 1 7.08 0z"
|
||||
transform="translate(5 1)"
|
||||
/>
|
||||
<path
|
||||
d="M7.08 3C4.83 3 3 4.83 3 7.08c0 2.249 1.83 4.08 4.08 4.08 1.164 0 2.06-.466 2.289-.678l.026-.024.026-.024c1.558-1.374 1.738-2.573 1.738-3.354C11.16 4.83 9.33 3 7.08 3m0-3c3.91 0 7.08 3.17 7.08 7.08 0 2.095-.91 3.978-2.753 5.604-.857.793-2.512 1.475-4.326 1.475C3.17 14.16 0 10.99 0 7.08S3.17 0 7.08 0z"
|
||||
class="prefix_cls-4"
|
||||
transform="translate(5 1)"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</mat-icon>
|
||||
</div>
|
||||
<div *ngIf="tabIndex === 'chat'" ucapFloatActionButton="mainIcon">
|
||||
<mat-icon class="ico-font-float">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="44"
|
||||
height="44"
|
||||
viewBox="0 0 44 44"
|
||||
>
|
||||
<g transform="translate(-10 -10)">
|
||||
<path
|
||||
fill="none"
|
||||
d="M0 0H44V44H0z"
|
||||
transform="translate(10 10)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0L12 0"
|
||||
class="prefix_cls-2"
|
||||
transform="translate(40.625 22.552)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0L12 0"
|
||||
class="prefix_cls-2"
|
||||
transform="rotate(90 15.036 31.829)"
|
||||
/>
|
||||
<path
|
||||
fill="rgba(102,102,102,0)"
|
||||
class="prefix_cls-3"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="3px"
|
||||
d="M50.353 28.582a13.829 13.829 0 0 0-17.546 0 10.012 10.012 0 0 0 0 15.588 13.406 13.406 0 0 0 8.773 3.139c.337 0 .676-.012 1.013-.036l2.129 3.737a.837.837 0 0 0 .7.433h.02a.832.832 0 0 0 .7-.4l4.511-7.143a9.995 9.995 0 0 0-.3-15.319z"
|
||||
data-name="icon_float_chat"
|
||||
transform="translate(-16.272 -6.943)"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</mat-icon>
|
||||
</div>
|
||||
<div
|
||||
*ngIf="tabIndex === 'message'"
|
||||
ucapFloatActionButton="mainIcon"
|
||||
>
|
||||
<mat-icon class="ico-font-float">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="44"
|
||||
height="44"
|
||||
viewBox="0 0 44 44"
|
||||
>
|
||||
<g transform="translate(-10 -9.5)">
|
||||
<path
|
||||
fill="none"
|
||||
d="M0 0H44V44H0z"
|
||||
transform="translate(10 9.5)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0L12 0"
|
||||
class="prefix_cls-2"
|
||||
transform="translate(40.5 38.26)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0L12 0"
|
||||
class="prefix_cls-2"
|
||||
transform="rotate(90 7.12 39.62)"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
class="prefix_cls-3"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="3px"
|
||||
d="M22.425 3.3a3.851 3.851 0 0 1 5.447 5.447L9.489 27.129 2 29.172l2.043-7.489z"
|
||||
data-name="icon_float_message"
|
||||
transform="translate(11.5 14.328)"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</mat-icon>
|
||||
</div>
|
||||
<div *ngIf="tabIndex === 'call'" ucapFloatActionButton="mainIcon">
|
||||
<mat-icon class="ico-font-dialpad">dialpad</mat-icon>
|
||||
</div>
|
||||
</ucap-float-action-button>
|
||||
<div class="left-sidenav-container" fxLayout="column">
|
||||
<div class="top-bar" fxFlex="0 0 40px">
|
||||
M-Messenger
|
||||
|
@ -242,7 +398,41 @@
|
|||
<mat-sidenav-content>
|
||||
<div class="content-sidenav-container" fxLayout="column">
|
||||
<div class="content-sidenav-top-bar" fxFlex="0 0 40px">
|
||||
<app-layouts-top-bar></app-layouts-top-bar>
|
||||
<app-layouts-top-bar>
|
||||
<div class="content-sidenav-top-bar-content">
|
||||
<div class="toolbar-info-area date-info">
|
||||
<span>Today</span>{{ moment().format('YYYY.MM.DD') }}
|
||||
</div>
|
||||
<div class="toolbar-info-area toolbar-ctrl">
|
||||
<!--Search-->
|
||||
<div class="topbar-search">
|
||||
<button
|
||||
mat-icon-button
|
||||
aria-label="search icon"
|
||||
(click)="onClickSearch($event)"
|
||||
>
|
||||
<mat-icon class="ico-search-icon">search</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!--My Profile -->
|
||||
<div
|
||||
class="my-profile"
|
||||
matTooltip="프로필 버튼"
|
||||
matTooltipPosition="below"
|
||||
matTooltipHideDelay="1000"
|
||||
[matMenuTriggerFor]="profileMenu"
|
||||
#profileMenuTrigger="matMenuTrigger"
|
||||
>
|
||||
<app-organization-profile-image-01
|
||||
[userInfo]="user.info"
|
||||
[versionInfo]="versionInfo2Res"
|
||||
(openProfile)="onOpenProfile($event)"
|
||||
></app-organization-profile-image-01>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</app-layouts-top-bar>
|
||||
</div>
|
||||
<div class="content-sidenav-body" fxFlex="1 1 auto">
|
||||
<router-outlet name="content"></router-outlet>
|
||||
|
@ -269,3 +459,16 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<mat-menu #profileMenu="matMenu" class="profile-menu-panel">
|
||||
<ng-template matMenuContent>
|
||||
<app-organization-profile-menu-01
|
||||
(selectedProfileManage)="onSelectedProfileManage()"
|
||||
(selectedNotice)="onSelectedNotice()"
|
||||
(selectedSettings)="onSelectedSettings()"
|
||||
(selectedLogout)="onSelectedLogout()"
|
||||
(selectedExit)="onSelectedExit()"
|
||||
(done)="onDoneForProfileMenu()"
|
||||
></app-organization-profile-menu-01>
|
||||
</ng-template>
|
||||
</mat-menu>
|
||||
|
|
|
@ -12,18 +12,20 @@
|
|||
.navitab-page {
|
||||
//GNB /////////////////////////////////////
|
||||
.gnb {
|
||||
background-color: $gray-ref0;
|
||||
//background-color: $gray-ref0;
|
||||
background-color: #f1f2f6;
|
||||
width: 60px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-right: 1px solid rgba(204, 204, 204, 0.8);
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.2);
|
||||
.mat-gnb-toolbar {
|
||||
flex-basis: 64px;
|
||||
flex-basis: 40px;
|
||||
padding: 2px 12px 10px;
|
||||
.img-logo {
|
||||
margin: 9px 0 5px;
|
||||
margin: 6px 0 0 1px;
|
||||
}
|
||||
}
|
||||
.left-container {
|
||||
|
@ -33,13 +35,14 @@
|
|||
}
|
||||
.global-menu {
|
||||
width: 100%;
|
||||
background-color: $gray-ref0;
|
||||
//background-color: $gray-ref0;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.btn-homepage-area {
|
||||
flex-flow: column-reverse;
|
||||
position: relative;
|
||||
button {
|
||||
/*
|
||||
padding: 30px 0 12px;
|
||||
&::before {
|
||||
content: '';
|
||||
|
@ -51,6 +54,21 @@
|
|||
top: 9px;
|
||||
left: calc(50% - 10px);
|
||||
}
|
||||
*/
|
||||
padding: 30px 0 12px;
|
||||
border-radius: 21px;
|
||||
min-width: 42px;
|
||||
&::before {
|
||||
content: '';
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background-image: url(../../../assets/images/ico/btn_gnb_hompage.svg);
|
||||
background-size: 30px;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
left: calc(50% - 15px);
|
||||
}
|
||||
em {
|
||||
font-style: normal;
|
||||
font-size: 8px;
|
||||
|
@ -61,139 +79,6 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
::ng-deep .global-menu {
|
||||
//display: flex;
|
||||
//flex-direction: row;
|
||||
.mat-tab-header {
|
||||
border-bottom: none !important;
|
||||
width: 100%;
|
||||
}
|
||||
.mat-tab-label-container {
|
||||
.mat-tab-list {
|
||||
.mat-tab-labels {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: space-around;
|
||||
height: 272px;
|
||||
border-bottom: none;
|
||||
|
||||
.mat-tab-label {
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
padding: 0;
|
||||
min-width: 0 !important;
|
||||
.mat-tab-label-content {
|
||||
.icon-item {
|
||||
display: inline-flex;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
//transform: scale(0.9);
|
||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0, 1);
|
||||
|
||||
svg {
|
||||
//width: 24px;
|
||||
//height: 24px;
|
||||
stroke: $gray-re9;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: square;
|
||||
stroke-linejoin: miter;
|
||||
fill: none;
|
||||
g {
|
||||
&#icon_gnb_organiztion_g32 {
|
||||
.prefix__cls-1,
|
||||
.prefix__cls-4 {
|
||||
fill: none;
|
||||
}
|
||||
.prefix__cls-1 {
|
||||
stroke: #999;
|
||||
stroke-width: 2px;
|
||||
}
|
||||
.prefix__cls-3 {
|
||||
stroke: none;
|
||||
}
|
||||
}
|
||||
&#icon_gnb_message_g32 {
|
||||
.prefix__cls-1 {
|
||||
fill: none;
|
||||
stroke: #999;
|
||||
stroke-width: 2px;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.mat-badge-content {
|
||||
right: -9px !important;
|
||||
border: 1px solid #ffbf2a;
|
||||
//width: 24px;
|
||||
//height: 24px;
|
||||
box-sizing: content-box;
|
||||
top: -10px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.mat-tab-label-active {
|
||||
opacity: 0;
|
||||
svg {
|
||||
stroke: #fff !important;
|
||||
g {
|
||||
&#prefix_23,
|
||||
&#icon_gnb_chat_g32,
|
||||
&#icon_gnb_call_g32 {
|
||||
path {
|
||||
&:nth-child(2) {
|
||||
fill: #fff !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
&#icon_gnb_organiztion_g32 {
|
||||
.prefix__cls-1 {
|
||||
stroke: #fff !important;
|
||||
}
|
||||
path {
|
||||
&:nth-last-of-type(2) {
|
||||
stroke: #fff !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
&#icon_gnb_message_g32 {
|
||||
.prefix__cls-1 {
|
||||
stroke: #fff !important;
|
||||
}
|
||||
path {
|
||||
&:nth-child(3) {
|
||||
stroke: #fff !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&[aria-selected='true'] {
|
||||
opacity: 1;
|
||||
.mat-tab-label-content {
|
||||
.icon-item {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.mat-ink-bar {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.mat-tab-body-wrapper {
|
||||
.mat-tab-body {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/////////////////////////////////////GNB //
|
||||
}
|
||||
|
@ -213,6 +98,7 @@
|
|||
.left-sidenav {
|
||||
width: 370px;
|
||||
max-width: 90%;
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.2);
|
||||
|
||||
.left-sidenav-container {
|
||||
width: 100%;
|
||||
|
@ -222,8 +108,10 @@
|
|||
size: 13px;
|
||||
color: $gray-re70;
|
||||
}
|
||||
line-height: 15px;
|
||||
padding: 25px 0 0 17px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -232,6 +120,96 @@
|
|||
.content-sidenav-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden; //20200611
|
||||
.content-sidenav-top-bar {
|
||||
.content-sidenav-top-bar-content {
|
||||
height: 40px;
|
||||
width: auto;
|
||||
background-color: transparent;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
font-size: 12px;
|
||||
justify-content: space-between;
|
||||
.toolbar-info-area {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
align-items: center;
|
||||
&.date-info {
|
||||
@include font-family($font-light);
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
color: $gray-re70;
|
||||
padding-left: 30px;
|
||||
@include screen(mid) {
|
||||
padding-left: 16px;
|
||||
display: none;
|
||||
}
|
||||
span {
|
||||
width: 54px;
|
||||
height: 16px;
|
||||
border-radius: 30px;
|
||||
border: solid 1px $lipstick;
|
||||
background-color: #ffffff;
|
||||
font-size: 11px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: $lipstick;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
&.toolbar-ctrl {
|
||||
flex-flow: row-reverse;
|
||||
.topbar-search {
|
||||
order: 2;
|
||||
margin-right: 8px;
|
||||
.ico-search-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
font-size: 18px;
|
||||
line-height: 18px;
|
||||
color: #707070;
|
||||
}
|
||||
}
|
||||
.my-profile {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
margin-right: 20px;
|
||||
order: 1;
|
||||
//profile /////////////
|
||||
.user-profile-thumb {
|
||||
@include profile-avatar-default(
|
||||
0,
|
||||
14,
|
||||
$green,
|
||||
18px
|
||||
); //오른 아래 공간, 모바일 온라인 아이콘 크기, 모바일 아이콘 색, 모바일 아이콘 bg크기
|
||||
|
||||
.presence {
|
||||
//PC 상태
|
||||
@include presence-state(10px); //원크기
|
||||
position: relative;
|
||||
align-self: flex-end;
|
||||
margin-left: -10px;
|
||||
order: 2;
|
||||
}
|
||||
.profileImage {
|
||||
@include avatar-img(30px, 0); //아바타 크기, 왼쪽공간
|
||||
order: 1;
|
||||
}
|
||||
}
|
||||
///// profile//
|
||||
}
|
||||
.app-layout-native-title-bar-actions {
|
||||
order: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content-sidenav-body {
|
||||
overflow: auto;
|
||||
|
@ -250,7 +228,7 @@
|
|||
justify-content: space-between;
|
||||
width: 100%;
|
||||
height: 38px;
|
||||
border-top: 1px solid $line-color-gray01;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.2);
|
||||
background-color: $white;
|
||||
.foot-info {
|
||||
display: flex;
|
||||
|
@ -264,6 +242,13 @@
|
|||
.var-txt {
|
||||
padding-left: 8px;
|
||||
color: $gray-re70;
|
||||
text-align: center;
|
||||
@include screen(custom, max, 414) {
|
||||
padding-left: 22px;
|
||||
flex-basis: 38%;
|
||||
flex-grow: 0;
|
||||
line-height: 1.2;
|
||||
}
|
||||
&::before {
|
||||
content: '';
|
||||
width: 1px;
|
||||
|
@ -271,10 +256,19 @@
|
|||
display: inline-block;
|
||||
background-color: #d4d4d4;
|
||||
margin-right: 8px;
|
||||
@include screen(custom, max, 414) {
|
||||
margin-left: -10px;
|
||||
}
|
||||
}
|
||||
&:first-of-type {
|
||||
@include screen(custom, max, 414) {
|
||||
padding-left: 0;
|
||||
}
|
||||
&::before {
|
||||
width: 0;
|
||||
@include screen(custom, max, 414) {
|
||||
margin-left: -4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.new-var {
|
||||
|
@ -287,11 +281,13 @@
|
|||
padding-right: 20px;
|
||||
p {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
span {
|
||||
color: $lipstick;
|
||||
}
|
||||
em {
|
||||
margin-left: 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -301,3 +297,41 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Float action button
|
||||
.ico-font-float {
|
||||
svg {
|
||||
.prefix_cls-2 {
|
||||
fill: transparent;
|
||||
stroke-width: 2px;
|
||||
stroke: rgba(255, 255, 255, 1);
|
||||
}
|
||||
.prefix_cls-3 {
|
||||
stroke: rgba(255, 255, 255, 1);
|
||||
}
|
||||
.prefix_cls-4 {
|
||||
fill: rgba(255, 255, 255, 1);
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
svg {
|
||||
.prefix_cls-2 {
|
||||
stroke: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
.prefix_cls-3 {
|
||||
stroke: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
.prefix_cls-4 {
|
||||
fill: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ico-font-dialpad {
|
||||
font-size: 28px !important;
|
||||
line-height: 44px;
|
||||
&:hover {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,40 @@
|
|||
import { Subscription } from 'rxjs';
|
||||
import moment from 'moment';
|
||||
|
||||
import { Subject, of } from 'rxjs';
|
||||
import { takeUntil, filter, take, map, catchError } from 'rxjs/operators';
|
||||
|
||||
import { Component, ViewChild, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import {
|
||||
Router,
|
||||
RouterEvent,
|
||||
NavigationEnd,
|
||||
PRIMARY_OUTLET,
|
||||
ActivatedRoute,
|
||||
Params
|
||||
} from '@angular/router';
|
||||
|
||||
import { Store, select } from '@ngrx/store';
|
||||
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatMenuTrigger } from '@angular/material/menu';
|
||||
import { MatTabChangeEvent, MatTabGroup } from '@angular/material/tabs';
|
||||
import { MatSidenav } from '@angular/material/sidenav';
|
||||
|
||||
import { VersionInfo2Response } from '@ucap/api-public';
|
||||
import { UserInfoSS } from '@ucap/protocol-query';
|
||||
|
||||
import { LogService } from '@ucap/ng-logger';
|
||||
import { UserSelector } from '@ucap/ng-store-organization';
|
||||
import { ConfigurationSelector } from '@ucap/ng-store-authentication';
|
||||
import { RoomSelector } from '@ucap/ng-store-chat';
|
||||
|
||||
import { AppSelector } from '@app/store/state';
|
||||
import { AppChatService } from '@app/services/app-chat.service';
|
||||
import { QueryParams as ChatQueryParams } from '@app/pages/chat/types/params.type';
|
||||
import { CreateDialogComponent } from '@app/sections/group/dialogs/create.dialog.component';
|
||||
import { AppAccountService } from '@app/services/app-account.service';
|
||||
import { QueryParams as OrganizationParams } from '@app/pages/organization/types/params.type';
|
||||
import { User } from '@ucap/protocol-info';
|
||||
|
||||
const NAVS = ['group', 'chat', 'organization', 'message'];
|
||||
|
||||
|
@ -27,79 +50,170 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
|
|||
@ViewChild('leftSidenav', { static: true })
|
||||
leftSidenav: MatSidenav;
|
||||
|
||||
isShowLeftSideNav = false;
|
||||
|
||||
@ViewChild('profileMenuTrigger', { static: true })
|
||||
profileMenuTrigger: MatMenuTrigger;
|
||||
|
||||
showStatusbar = true;
|
||||
|
||||
tabIndex: string;
|
||||
queryParams: Params;
|
||||
|
||||
unreadCountChat = 0;
|
||||
|
||||
/** FAB */
|
||||
fabButtonShow = true;
|
||||
fabUseCustomDefaultIcon = true; // default in this prj
|
||||
fabButtons: { icon: string; tooltip?: string; divisionType?: string }[];
|
||||
versionInfo2Res: VersionInfo2Response;
|
||||
user: User;
|
||||
|
||||
private windowSizeSubscription: Subscription;
|
||||
moment = moment;
|
||||
|
||||
private ngOnDestroySubject: Subject<void> = new Subject();
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private store: Store<any>,
|
||||
private appAccountService: AppAccountService,
|
||||
private appChatService: AppChatService,
|
||||
private logService: LogService
|
||||
) {}
|
||||
private logService: LogService,
|
||||
public dialog: MatDialog
|
||||
) {
|
||||
this.setFabInitial(NAVS[0]);
|
||||
this.router.events
|
||||
.pipe(
|
||||
takeUntil(this.ngOnDestroySubject),
|
||||
filter((event) => event instanceof RouterEvent)
|
||||
)
|
||||
.subscribe((event) => {
|
||||
switch (event.constructor) {
|
||||
case NavigationEnd:
|
||||
{
|
||||
const t = this.router.parseUrl((event as NavigationEnd).url);
|
||||
const p = t.root.children[PRIMARY_OUTLET];
|
||||
if (!p || !p.segments || 0 === p.segments.length) {
|
||||
break;
|
||||
}
|
||||
const index = p.segments[0].path;
|
||||
this.setTabGroup(index);
|
||||
this.setFabInitial(index);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
this.activatedRoute.queryParams
|
||||
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||
.subscribe((params) => {
|
||||
this.queryParams = params;
|
||||
});
|
||||
|
||||
this.store
|
||||
.pipe(
|
||||
takeUntil(this.ngOnDestroySubject),
|
||||
select(ConfigurationSelector.versionInfo2Response)
|
||||
)
|
||||
.subscribe((versionInfo2Res) => {
|
||||
this.versionInfo2Res = versionInfo2Res;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.windowSizeSubscription = this.store
|
||||
.pipe(select(AppSelector.windowSize))
|
||||
this.store
|
||||
.pipe(takeUntil(this.ngOnDestroySubject), select(AppSelector.windowSize))
|
||||
.subscribe((size) => {
|
||||
if (size.width < 780) {
|
||||
if (this.leftSidenav.opened) {
|
||||
this.leftSidenav.close();
|
||||
}
|
||||
this.isShowLeftSideNav = false;
|
||||
this.leftSidenav.mode = 'over';
|
||||
} else {
|
||||
if (!this.leftSidenav.opened) {
|
||||
this.leftSidenav.open();
|
||||
}
|
||||
this.isShowLeftSideNav = true;
|
||||
this.leftSidenav.mode = 'side';
|
||||
}
|
||||
});
|
||||
|
||||
this.setTabGroup(this.router.url);
|
||||
this.store
|
||||
.pipe(takeUntil(this.ngOnDestroySubject), select(UserSelector.user))
|
||||
.subscribe((user) => {
|
||||
this.user = user;
|
||||
});
|
||||
|
||||
this.setFabInitial(NAVS[0]);
|
||||
this.store
|
||||
.pipe(
|
||||
takeUntil(this.ngOnDestroySubject),
|
||||
select(RoomSelector.unreadTotal)
|
||||
)
|
||||
.subscribe((unreadTotal) => {
|
||||
this.unreadCountChat = unreadTotal;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (!this.windowSizeSubscription) {
|
||||
this.windowSizeSubscription.unsubscribe();
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.next();
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
|
||||
onSelectedTabChange(event: MatTabChangeEvent) {
|
||||
if (4 === event.index) {
|
||||
this.router.navigate(
|
||||
['group', { outlets: { content: 'chat/index' } }],
|
||||
{}
|
||||
);
|
||||
return;
|
||||
onOpenProfile(userInfo: UserInfoSS) {
|
||||
this.profileMenuTrigger.openMenu();
|
||||
}
|
||||
this.router.navigate([
|
||||
|
||||
onSelectedTabChange(event: MatTabChangeEvent) {
|
||||
const commands: any = [
|
||||
NAVS[event.index],
|
||||
{ outlets: { content: 'index' } }
|
||||
]);
|
||||
];
|
||||
const orgInitialParams: Params = {};
|
||||
|
||||
if (
|
||||
event.index === 1 && // is chat.
|
||||
!!this.queryParams &&
|
||||
!!this.queryParams[ChatQueryParams.ROOM_ID]
|
||||
) {
|
||||
// 다른 화면에서 채팅으로 바로 유입할 경우에는 navigate 초기화를 무시한다.
|
||||
this.queryParams = undefined;
|
||||
return;
|
||||
}
|
||||
// if (!!this.tabIndex && this.tabIndex === 'chat') {
|
||||
// if (!!this.queryParams && !!this.queryParams[ChatQueryParams.ROOM_ID]) {
|
||||
// return;
|
||||
// } else {
|
||||
|
||||
// }
|
||||
// } else {
|
||||
// }
|
||||
|
||||
if (event.index === 2 && !!this.user) {
|
||||
orgInitialParams[OrganizationParams.DEPT_SEQ] = String(
|
||||
this.user.departmentCode
|
||||
);
|
||||
}
|
||||
this.router.navigate(commands, { queryParams: orgInitialParams });
|
||||
if (!this.isShowLeftSideNav) {
|
||||
this.leftSidenav.open();
|
||||
}
|
||||
this.setFabInitial(NAVS[event.index]);
|
||||
}
|
||||
|
||||
onClickToggleLeftSidenav() {
|
||||
if (this.leftSidenav.opened) {
|
||||
this.leftSidenav.close();
|
||||
} else {
|
||||
this.leftSidenav.open();
|
||||
}
|
||||
}
|
||||
|
||||
private setTabGroup(url: string) {
|
||||
if (!!this.navTabGroup) {
|
||||
this.navTabGroup.selectedIndex = NAVS.findIndex((v) =>
|
||||
url.startsWith(`/${v}`)
|
||||
);
|
||||
if (!this.isShowLeftSideNav) {
|
||||
this.leftSidenav.toggle();
|
||||
}
|
||||
}
|
||||
|
||||
setFabInitial(type: string) {
|
||||
this.tabIndex = type;
|
||||
switch (type) {
|
||||
case 'group':
|
||||
{
|
||||
|
@ -172,7 +286,20 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
|
|||
switch (btn.divisionType) {
|
||||
case 'GROUP_NEW_ADD':
|
||||
{
|
||||
this.logService.debug('GROUP_NEW_ADD');
|
||||
const dialogRef = this.dialog.open(CreateDialogComponent, {
|
||||
panelClass: 'max-create-dialog'
|
||||
});
|
||||
|
||||
dialogRef
|
||||
.afterClosed()
|
||||
.pipe(
|
||||
take(1),
|
||||
map((result) => {}),
|
||||
catchError((err) => {
|
||||
return of(err);
|
||||
})
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
break;
|
||||
case 'CAHT_NEW_ADD':
|
||||
|
@ -195,4 +322,37 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onClickSearch(event: Event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
onSelectedProfileManage() {
|
||||
this.profileMenuTrigger.closeMenu();
|
||||
}
|
||||
|
||||
onSelectedNotice() {
|
||||
this.profileMenuTrigger.closeMenu();
|
||||
}
|
||||
|
||||
onSelectedSettings() {
|
||||
this.profileMenuTrigger.closeMenu();
|
||||
this.appAccountService.dialogForSettings();
|
||||
}
|
||||
|
||||
onSelectedLogout() {
|
||||
this.router.navigate(['/account/logout']);
|
||||
}
|
||||
|
||||
onSelectedExit() {}
|
||||
|
||||
onDoneForProfileMenu() {
|
||||
this.profileMenuTrigger.closeMenu();
|
||||
}
|
||||
|
||||
private setTabGroup(url: string) {
|
||||
if (!!this.navTabGroup) {
|
||||
this.navTabGroup.selectedIndex = NAVS.findIndex((v) => url === v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import { DefaultLayoutComponent } from './default.layout.component';
|
|||
import { NoNaviLayoutComponent } from './no-navi.layout.component';
|
||||
|
||||
import { DefaultDialogLayoutComponent } from './default-dialog.layout.component';
|
||||
import { DefaultDrawerLayoutComponent } from './default-drawer.layout.component';
|
||||
import { SelectorLayoutComponent } from './selector.layout.component';
|
||||
|
||||
export const COMPONENTS = [
|
||||
|
@ -12,6 +13,7 @@ export const COMPONENTS = [
|
|||
NoNaviLayoutComponent,
|
||||
|
||||
DefaultDialogLayoutComponent,
|
||||
DefaultDrawerLayoutComponent,
|
||||
|
||||
SelectorLayoutComponent
|
||||
];
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
.layout-container {
|
||||
background-color: #f3f4f5;
|
||||
}
|
||||
|
|
|
@ -9,9 +9,10 @@
|
|||
matSuffix
|
||||
aria-label="Clear"
|
||||
class="btn-close"
|
||||
color="accent"
|
||||
color="primary"
|
||||
(click)="onClickClose($event)"
|
||||
>
|
||||
<mat-icon>highlight_off</mat-icon>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="selector-contents">
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
.selector {
|
||||
border-bottom: 1px solid #ccc;
|
||||
.selector-title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 5px 0 16px;
|
||||
//border-top: 1px solid #ccc;
|
||||
}
|
||||
.selector-contents {
|
||||
background-color: #fff;
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
.footer {
|
||||
}
|
||||
}
|
|
@ -2,9 +2,10 @@ import {
|
|||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
Input,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef
|
||||
ChangeDetectorRef,
|
||||
EventEmitter,
|
||||
Output
|
||||
} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
|
@ -14,9 +15,16 @@ import {
|
|||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class SelectorLayoutComponent implements OnInit, OnDestroy {
|
||||
@Output()
|
||||
closed = new EventEmitter<void>();
|
||||
|
||||
constructor(private changeDetectorRef: ChangeDetectorRef) {}
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
||||
ngOnDestroy(): void {}
|
||||
|
||||
onClickClose(event: MouseEvent): void {
|
||||
this.closed.emit();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<div class="title-bar">
|
||||
<ucap-title-bar
|
||||
[platform]="platform"
|
||||
[native]="native"
|
||||
(closed)="onClosedTitleBar()"
|
||||
(maximized)="onMaximizedTitleBar()"
|
||||
(minimized)="onMinimizedTitleBar()"
|
||||
>
|
||||
<ng-content></ng-content>
|
||||
</ucap-title-bar>
|
||||
</div>
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
background-color: #ffffff;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,16 @@
|
|||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
import { Component, OnInit, OnDestroy, Inject } from '@angular/core';
|
||||
|
||||
import * as detectBrowser from 'detect-browser';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { NativeService, WindowState, NativeType } from '@ucap/native';
|
||||
|
||||
import { UCAP_NATIVE_SERVICE } from '@ucap/ng-native';
|
||||
|
||||
@Component({
|
||||
selector: 'app-layouts-top-bar',
|
||||
templateUrl: './top-bar.component.html',
|
||||
|
@ -9,13 +18,54 @@ import { Store } from '@ngrx/store';
|
|||
})
|
||||
export class TopBarComponent implements OnInit, OnDestroy {
|
||||
platform = 'win32';
|
||||
native = true;
|
||||
windowState: WindowState;
|
||||
|
||||
constructor(private store: Store<any>) {}
|
||||
private ngOnDestroySubject: Subject<void> = new Subject();
|
||||
|
||||
ngOnInit() {}
|
||||
constructor(
|
||||
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
|
||||
private store: Store<any>
|
||||
) {
|
||||
this.nativeService.platform_nativeType().then((type) => {
|
||||
switch (type) {
|
||||
case NativeType.Browser:
|
||||
this.platform = 'browser';
|
||||
break;
|
||||
case NativeType.Electron:
|
||||
{
|
||||
const info = detectBrowser.detect();
|
||||
if (info.os.startsWith('Windows')) {
|
||||
this.platform = 'win32';
|
||||
} else if (info.os.startsWith('Mac OS')) {
|
||||
this.platform = 'darwin';
|
||||
} else if (info.os.startsWith('Linux')) {
|
||||
this.platform = 'linux';
|
||||
} else {
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
ngOnDestroy(): void {}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.nativeService
|
||||
.window_onState$()
|
||||
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||
.subscribe((windowState) => {
|
||||
this.windowState = windowState;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.next();
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
|
||||
onClosedTitleBar() {}
|
||||
|
||||
|
|
|
@ -5,21 +5,25 @@ import { RouterModule } from '@angular/router';
|
|||
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
|
||||
import { MatBadgeModule } from '@angular/material/badge';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
|
||||
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
|
||||
|
||||
import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
|
||||
import { UiModule } from '@ucap/ng-ui';
|
||||
|
||||
import { AppOrganizationModule } from '@app/ucap/organization/organization.module';
|
||||
|
||||
import { COMPONENTS } from './components';
|
||||
import { DIALOGS } from './dialogs';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -28,17 +32,22 @@ import { MatSelectModule } from '@angular/material/select';
|
|||
|
||||
FlexLayoutModule,
|
||||
|
||||
MatBadgeModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatMenuModule,
|
||||
MatSelectModule,
|
||||
MatSidenavModule,
|
||||
MatTabsModule,
|
||||
MatToolbarModule,
|
||||
MatSelectModule,
|
||||
MatTooltipModule,
|
||||
|
||||
PerfectScrollbarModule,
|
||||
|
||||
I18nModule,
|
||||
UiModule
|
||||
UiModule,
|
||||
|
||||
AppOrganizationModule
|
||||
],
|
||||
exports: [...COMPONENTS, ...DIALOGS],
|
||||
declarations: [...COMPONENTS, ...DIALOGS],
|
||||
|
@ -46,7 +55,7 @@ import { MatSelectModule } from '@angular/material/select';
|
|||
providers: [
|
||||
{
|
||||
provide: UCAP_I18N_NAMESPACE,
|
||||
useValue: ['chat', 'common']
|
||||
useValue: ['common']
|
||||
}
|
||||
]
|
||||
})
|
||||
|
|
4
src/app/models/group-open-info.ts
Normal file
4
src/app/models/group-open-info.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export interface GroupOpenInfo {
|
||||
lastGroupSeq: number;
|
||||
groupSeqs: number[];
|
||||
}
|
|
@ -1,8 +1,10 @@
|
|||
import { LoginSession as UCAPLoginSession } from '@ucap/core';
|
||||
import { GroupOpenInfo } from './group-open-info';
|
||||
|
||||
export interface LoginSession extends UCAPLoginSession {
|
||||
loginPw?: string;
|
||||
initPw?: boolean;
|
||||
encData?: string;
|
||||
alive?: boolean;
|
||||
groupInfo?: GroupOpenInfo;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import { Routes, RouterModule } from '@angular/router';
|
|||
|
||||
import { ForgotPasswordPageComponent } from './components/forgot-password.page.component';
|
||||
import { LoginPageComponent } from './components/login.page.component';
|
||||
import { LogoutPageComponent } from './components/logout.page.component';
|
||||
import { ResetPasswordPageComponent } from './components/reset-password.page.component';
|
||||
|
||||
const routes: Routes = [
|
||||
|
@ -17,6 +18,10 @@ const routes: Routes = [
|
|||
{
|
||||
path: 'reset_password',
|
||||
component: ResetPasswordPageComponent
|
||||
},
|
||||
{
|
||||
path: 'logout',
|
||||
component: LogoutPageComponent
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -25,13 +25,11 @@ export class LoginPageComponent implements OnInit, OnDestroy {
|
|||
|
||||
readonly fixedCompanyCode = environment.companyConfig.fixedCompanyCode;
|
||||
|
||||
private ngOnDestroySubject = new Subject<boolean>();
|
||||
private ngOnDestroySubject: Subject<void> = new Subject();
|
||||
|
||||
constructor(private localStorageService: LocalStorageService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.ngOnDestroySubject = new Subject<boolean>();
|
||||
|
||||
this.localStorageService
|
||||
.encGet$<UserStore>(
|
||||
AppKey.UserStore,
|
||||
|
@ -43,6 +41,7 @@ export class LoginPageComponent implements OnInit, OnDestroy {
|
|||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.next();
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<div class="logout-container">Logout</div>
|
|
@ -0,0 +1,6 @@
|
|||
@import '~@ucap/lg-scss/mixins';
|
||||
|
||||
.logout-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
|
@ -1,28 +1,28 @@
|
|||
import { TestBed, async } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { LoginSectionComponent } from './login.section.component';
|
||||
import { LogoutPageComponent } from './logout.page.component';
|
||||
|
||||
describe('app::sections::account::LoginSectionComponent', () => {
|
||||
describe('app::pages::account::LogoutPageComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [RouterTestingModule],
|
||||
declarations: [LoginSectionComponent]
|
||||
declarations: [LogoutPageComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(LoginSectionComponent);
|
||||
const fixture = TestBed.createComponent(LogoutPageComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have as title 'ucap-lg-web'`, () => {
|
||||
const fixture = TestBed.createComponent(LoginSectionComponent);
|
||||
const fixture = TestBed.createComponent(LogoutPageComponent);
|
||||
const app = fixture.componentInstance;
|
||||
});
|
||||
|
||||
it('should render title', () => {
|
||||
const fixture = TestBed.createComponent(LoginSectionComponent);
|
||||
const fixture = TestBed.createComponent(LogoutPageComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.nativeElement;
|
||||
expect(compiled.querySelector('.content span').textContent).toContain(
|
30
src/app/pages/account/components/logout.page.component.ts
Normal file
30
src/app/pages/account/components/logout.page.component.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
|
||||
import { LocalStorageService } from '@ucap/ng-web-storage';
|
||||
|
||||
import { environment } from '@environments';
|
||||
|
||||
import { UserStore } from '@app/models/user-store';
|
||||
import { AppKey } from '@app/types/app-key.type';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'app-pages-account-logout',
|
||||
templateUrl: './logout.page.component.html',
|
||||
styleUrls: ['./logout.page.component.scss']
|
||||
})
|
||||
export class LogoutPageComponent implements OnInit, OnDestroy {
|
||||
private ngOnDestroySubject: Subject<void> = new Subject();
|
||||
|
||||
constructor(private localStorageService: LocalStorageService) {}
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.next();
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ import { MatMenuModule } from '@angular/material/menu';
|
|||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
|
||||
import { AppChatSectionModule } from '@app/sections/chat/chat.section.module';
|
||||
|
||||
|
@ -27,6 +28,7 @@ import { UCAP_I18N_NAMESPACE, I18nModule } from '@ucap/ng-i18n';
|
|||
MatCheckboxModule,
|
||||
MatButtonModule,
|
||||
MatSidenavModule,
|
||||
MatTooltipModule,
|
||||
|
||||
AppChatSectionModule,
|
||||
AppChatRoutingPageModule,
|
||||
|
|
|
@ -3,22 +3,72 @@
|
|||
<app-sections-chat-info
|
||||
[roomId]="roomId"
|
||||
(openChatSearch)="isChatSearch = true"
|
||||
(rightDrawerToggle)="onRightDrawerToggle()"
|
||||
(rightDrawerToggle)="onRightDrawerToggle($event)"
|
||||
></app-sections-chat-info>
|
||||
</div>
|
||||
<app-sections-chat-chat-search
|
||||
*ngIf="isChatSearch"
|
||||
[isChatSearch]="isChatSearch"
|
||||
(closeChatSearch)="isChatSearch = false"
|
||||
></app-sections-chat-chat-search>
|
||||
<mat-drawer-container autosize fxFlex="1 1 auto" fxLayout="column">
|
||||
<div class="message-box-container" fxFlex="1 1 auto" fxLayout="column">
|
||||
<div class="message-area" fxFlex="1 1 auto">
|
||||
<app-sections-chat-message [roomId]="roomId"></app-sections-chat-message>
|
||||
<app-sections-chat-message
|
||||
[roomId]="roomId"
|
||||
[translationSimpleview]="translationSimpleview"
|
||||
[eventSendTrigger$]="eventSendTriggerSubject.asObservable()"
|
||||
></app-sections-chat-message>
|
||||
</div>
|
||||
<div class="message-input" fxFlex="0 0 auto">
|
||||
<app-sections-chat-form [roomId]="roomId"></app-sections-chat-form>
|
||||
<app-sections-chat-form
|
||||
[roomId]="roomId"
|
||||
(changeTranslationSimpleview)="translationSimpleview = $event"
|
||||
(eventSendTrigger)="eventSendTriggerSubject.next($event)"
|
||||
></app-sections-chat-form>
|
||||
</div>
|
||||
</div>
|
||||
<mat-drawer #chatRightDrawer mode="side" position="end" class="rightDrawer">
|
||||
Right Sections.
|
||||
<ng-container [ngSwitch]="drawerType">
|
||||
<app-drawer-chat-attach-data
|
||||
*ngSwitchCase="
|
||||
[
|
||||
ChatDrawType.AttachImage,
|
||||
ChatDrawType.AttachVideo,
|
||||
ChatDrawType.AttachFile
|
||||
].includes(drawerType)
|
||||
? drawerType
|
||||
: ''
|
||||
"
|
||||
[roomId]="roomId"
|
||||
[drawerType]="drawerType"
|
||||
(closed)="onRightDrawerClose()"
|
||||
></app-drawer-chat-attach-data>
|
||||
<app-drawer-chat-users
|
||||
*ngSwitchCase="ChatDrawType.RoomUsers"
|
||||
[roomId]="roomId"
|
||||
(closed)="onRightDrawerClose()"
|
||||
(rightDrawerToggle)="onRightDrawerToggle($event)"
|
||||
></app-drawer-chat-users>
|
||||
<app-drawer-chat-add-users
|
||||
*ngSwitchCase="ChatDrawType.Invite"
|
||||
[roomId]="roomId"
|
||||
[returnChatDrawerType]="returnDrawerType"
|
||||
(closed)="onRightDrawerClose()"
|
||||
(rightDrawerToggle)="onRightDrawerToggle($event)"
|
||||
></app-drawer-chat-add-users>
|
||||
<app-drawer-chat-add-group
|
||||
*ngSwitchCase="ChatDrawType.AddGroup"
|
||||
[roomId]="roomId"
|
||||
[returnChatDrawerType]="returnDrawerType"
|
||||
(closed)="onRightDrawerClose()"
|
||||
(rightDrawerToggle)="onRightDrawerToggle($event)"
|
||||
></app-drawer-chat-add-group>
|
||||
<app-drawer-chat-setting
|
||||
*ngSwitchCase="ChatDrawType.Setting"
|
||||
[roomId]="roomId"
|
||||
(closed)="onRightDrawerClose()"
|
||||
></app-drawer-chat-setting>
|
||||
</ng-container>
|
||||
</mat-drawer>
|
||||
</mat-drawer-container>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
@import '~@ucap/lg-scss/mixins';
|
||||
|
||||
.contents-main {
|
||||
position: relative;
|
||||
.subtitle {
|
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.16);
|
||||
position: relative;
|
||||
z-index: 4;
|
||||
}
|
||||
.message-box-container {
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
.message-area {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.message-input {
|
||||
max-height: 70%;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding: 1px 0;
|
||||
background-color: $white;
|
||||
}
|
||||
}
|
||||
.rightDrawer {
|
||||
min-width: 360px;
|
||||
max-width: 100%;
|
||||
@include screen(xs) {
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,8 +3,13 @@ import { ActivatedRoute, Params } from '@angular/router';
|
|||
|
||||
import { MatDrawer } from '@angular/material/sidenav';
|
||||
|
||||
import { Subscription } from 'rxjs';
|
||||
import { Subscription, Subject, BehaviorSubject } from 'rxjs';
|
||||
import { QueryParams } from '../types/params.type';
|
||||
import { ChatDrawType } from '../types/chat-draw.type';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { DrawInfo } from '../models/draw-info';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { RoomActions, ChattingActions } from '@ucap/ng-store-chat';
|
||||
|
||||
@Component({
|
||||
selector: 'app-pages-chat-room',
|
||||
|
@ -12,30 +17,65 @@ import { QueryParams } from '../types/params.type';
|
|||
styleUrls: ['./chat-room.page.component.scss']
|
||||
})
|
||||
export class ChatRoomPageComponent implements OnInit, OnDestroy {
|
||||
private paramsSubscription: Subscription;
|
||||
isChatSearch = false;
|
||||
roomId: string;
|
||||
translationSimpleview = false;
|
||||
|
||||
drawerType: ChatDrawType | null;
|
||||
returnDrawerType: ChatDrawType | null;
|
||||
eventSendTriggerSubject: BehaviorSubject<any> = new BehaviorSubject<any>(0);
|
||||
|
||||
@ViewChild('chatRightDrawer', { static: false })
|
||||
chatRightDrawer: MatDrawer;
|
||||
|
||||
constructor(private activatedRoute: ActivatedRoute) {}
|
||||
ChatDrawType = ChatDrawType;
|
||||
|
||||
private ngOnDestroySubject: Subject<void> = new Subject();
|
||||
|
||||
constructor(
|
||||
private store: Store<any>,
|
||||
private activatedRoute: ActivatedRoute
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.paramsSubscription = this.activatedRoute.queryParams.subscribe(
|
||||
(params: Params) => {
|
||||
this.activatedRoute.queryParams
|
||||
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||
.subscribe((params: Params) => {
|
||||
const seqParam = params[QueryParams.ROOM_ID];
|
||||
this.roomId = !!seqParam ? seqParam : undefined;
|
||||
// initializing by roomId Change.
|
||||
if (this.roomId !== seqParam) {
|
||||
if (!!this.chatRightDrawer) {
|
||||
this.chatRightDrawer.close();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// setting roomId.
|
||||
this.roomId = !!seqParam ? seqParam : undefined;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.paramsSubscription) {
|
||||
this.paramsSubscription.unsubscribe();
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.next();
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
|
||||
if (!!this.eventSendTriggerSubject) {
|
||||
this.eventSendTriggerSubject.complete();
|
||||
}
|
||||
onRightDrawerToggle(): void {
|
||||
this.chatRightDrawer.toggle();
|
||||
|
||||
// this.store.dispatch(RoomActions.clearSelectedRoom({ roomId: this.roomId }));
|
||||
this.store.dispatch(ChattingActions.clearActiveRoomId({}));
|
||||
}
|
||||
|
||||
onRightDrawerToggle(type: DrawInfo | null): void {
|
||||
this.drawerType = type.chatDrawType;
|
||||
this.returnDrawerType = !!type.returnDrawType ? type.returnDrawType : null;
|
||||
this.chatRightDrawer.open();
|
||||
}
|
||||
onRightDrawerClose(): void {
|
||||
this.drawerType = null;
|
||||
this.returnDrawerType = null;
|
||||
this.chatRightDrawer.close();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1,39 @@
|
|||
Index page of chat is works!
|
||||
<div class="index-page-chat-info">
|
||||
<div class="ico-page-chat">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 166 142">
|
||||
<g transform="translate(-152.267 -127.865)">
|
||||
<path
|
||||
d="M0 0H166V142H0z"
|
||||
data-name="square-01"
|
||||
transform="translate(152.267 127.865)"
|
||||
style="fill: none;"
|
||||
/>
|
||||
<path
|
||||
d="M45.694 2a43.788 43.788 0 0 1 33.84 71.495l8.744 8.756a4.269 4.269 0 0 1-3.235 7.311H45.694a43.781 43.781 0 0 1 0-87.562z"
|
||||
data-name="comment-dots-01"
|
||||
transform="translate(199.283 158.998)"
|
||||
style="stroke: #bababa; stroke-width: 4px; fill: #fff;"
|
||||
/>
|
||||
<path
|
||||
d="M26.015 2A24.012 24.012 0 0 0 7.458 41.206l-4.8 4.8a2.341 2.341 0 0 0 1.774 4.009h21.583a24.009 24.009 0 0 0 0-48.015z"
|
||||
data-name="comment-dots-02"
|
||||
transform="translate(167.028 170.336)"
|
||||
style="fill: #999;"
|
||||
/>
|
||||
<path
|
||||
d="M33.725 5.917a5.628 5.628 0 1 1 11.242 0 5.628 5.628 0 1 1-11.242 0zm-16.862 0A5.775 5.775 0 0 1 22.483 0 5.775 5.775 0 0 1 28.1 5.917a5.774 5.774 0 0 1-5.621 5.917 5.774 5.774 0 0 1-5.617-5.917zM0 5.917A5.775 5.775 0 0 1 5.621 0a5.775 5.775 0 0 1 5.621 5.917 5.774 5.774 0 0 1-5.621 5.917A5.774 5.774 0 0 1 0 5.917z"
|
||||
transform="translate(223.585 197.682)"
|
||||
style="fill: #bababa;"
|
||||
/>
|
||||
<path
|
||||
d="M13.476 22.894l-7.509-6.82L3.41 18.38l10.066 9.143L35.086 7.9l-2.539-2.31z"
|
||||
transform="translate(174.515 180.942)"
|
||||
style="fill: #fff;"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="chat-index-copy">
|
||||
{{ 'chat:room.noSelectRoom' | ucapI18n }}
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
.index-page-chat-info {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.ico-page-chat {
|
||||
width: 166px;
|
||||
height: 142px;
|
||||
margin-top: -80px;
|
||||
}
|
||||
.chat-index-copy {
|
||||
font-size: 1.429em;
|
||||
color: #666;
|
||||
padding: 10px 20px;
|
||||
border-top: 1px solid #ccc;
|
||||
border-bottom: 1px solid #ccc;
|
||||
margin: 50px 0 0;
|
||||
}
|
||||
}
|
|
@ -1,13 +1,41 @@
|
|||
<div fxFlexFill class="sidenav-container">
|
||||
<div class="chat-header">
|
||||
<h3>{{ 'label.chat' | ucapI18n }}</h3>
|
||||
<h3>{{ 'chat:label.chat' | ucapI18n }}</h3>
|
||||
<div class="chat-menu-btn">
|
||||
<button
|
||||
mat-icon-button
|
||||
aria-label="exit-room"
|
||||
[matTooltip]="'chat:label.exitFromRoom' | ucapI18n"
|
||||
(click)="onToggleChackable(true)"
|
||||
>
|
||||
<mat-icon>exit_to_app</mat-icon>
|
||||
<mat-icon>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g transform="translate(-1)">
|
||||
<path
|
||||
d="M0 0h24v24H0z"
|
||||
transform="translate(1)"
|
||||
style="fill: none;"
|
||||
/>
|
||||
<g transform="translate(3 3)">
|
||||
<path
|
||||
d="M8.963 3h8.693a1.962 1.962 0 0 1 1.963 1.963v3.926h-1.963V4.963H8.963V18.7h8.693v-3.922h1.963V18.7a1.962 1.962 0 0 1-1.963 1.963H8.963A1.969 1.969 0 0 1 7 18.7V4.963A1.969 1.969 0 0 1 8.963 3z"
|
||||
style="fill: #666;"
|
||||
transform="translate(-7 -3)"
|
||||
/>
|
||||
<path
|
||||
d="M13.459 14.542l1.384 1.384 4.907-4.907-4.907-4.908-1.384 1.384 2.532 2.542H6.5V12h9.491z"
|
||||
style="fill: #666;"
|
||||
transform="translate(-.19 -2.326)"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
mat-icon-button
|
||||
|
@ -25,15 +53,19 @@
|
|||
[style.display]="checkable ? 'none' : 'block'"
|
||||
></app-sections-chat-search>
|
||||
<div class="exitRoomInfo" *ngIf="checkable">
|
||||
<div>
|
||||
<strong>{{ this.selectedRoomList.length }}</strong
|
||||
<div class="chat-exit-info">
|
||||
{{ 'chat:label.exitFromRoom' | ucapI18n }}
|
||||
<span
|
||||
>( <strong>{{ this.selectedRoomList.length }}</strong
|
||||
>/{{
|
||||
!!this.searchObj.isShowSearch
|
||||
? this.searchResultList.length
|
||||
: this.roomList.length
|
||||
}}
|
||||
)</span
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<div class="chat-exit-checkbox">
|
||||
<mat-checkbox
|
||||
#allCheck
|
||||
aria-label="all select exit room "
|
||||
|
@ -41,7 +73,6 @@
|
|||
(change)="onToggleAllItem(allCheck.checked)"
|
||||
(click)="$event.stopPropagation()"
|
||||
>
|
||||
전체선택
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
@import '~@ucap/lg-scss/mixins';
|
||||
|
||||
.sidenav-container {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-content: flex-start;
|
||||
background-color: #f1f2f6;
|
||||
.chat-header {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: space-between;
|
||||
padding: 0 5px 0 17px;
|
||||
background-color: $white;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
h3 {
|
||||
@include font-family-txt(18, left, $lipstick);
|
||||
align-items: center;
|
||||
font-weight: 600;
|
||||
}
|
||||
.chat-menu-btn {
|
||||
justify-self: end;
|
||||
}
|
||||
}
|
||||
.exitRoomInfo {
|
||||
height: 50px;
|
||||
min-height: 50px;
|
||||
max-height: 50px;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: space-between;
|
||||
padding: 0 17px;
|
||||
background-color: $white;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
.chat-exit-info {
|
||||
font-size: 1em;
|
||||
color: $gray-re3;
|
||||
@include font-family($font-semibold, normal);
|
||||
font-weight: 600;
|
||||
span {
|
||||
color: $gray-re9;
|
||||
strong {
|
||||
color: $lipstick;
|
||||
}
|
||||
}
|
||||
}
|
||||
.chat-exit-checkbox {
|
||||
justify-self: self-end;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,6 +13,12 @@ import {
|
|||
ConfirmDialogResult
|
||||
} from '@ucap/ng-ui';
|
||||
import { I18nService } from '@ucap/ng-i18n';
|
||||
import { SearchInfo } from '../models/search-info';
|
||||
import { ActivatedRoute, Params } from '@angular/router';
|
||||
import { QueryParams } from '../types/params.type';
|
||||
import { AppChatService } from '@app/services/app-chat.service';
|
||||
import { SessionStorageService } from '@ucap/ng-web-storage';
|
||||
import { AppKey } from '@app/types';
|
||||
|
||||
@Component({
|
||||
selector: 'app-pages-chat-sidenav',
|
||||
|
@ -20,32 +26,56 @@ import { I18nService } from '@ucap/ng-i18n';
|
|||
styleUrls: ['./sidenav.page.component.scss']
|
||||
})
|
||||
export class SidenavPageComponent implements OnInit, OnDestroy {
|
||||
searchObj: any = {
|
||||
searchObj: SearchInfo = {
|
||||
isShowSearch: false,
|
||||
searchWord: ''
|
||||
};
|
||||
|
||||
checkable = false;
|
||||
|
||||
historyRoomId: string;
|
||||
|
||||
roomList: RoomInfo[];
|
||||
selectedRoomList: RoomInfo[] = [];
|
||||
searchResultList: RoomInfo[] = [];
|
||||
|
||||
private ngOnDestroySubject: Subject<boolean>;
|
||||
private ngOnDestroySubject: Subject<void> = new Subject();
|
||||
|
||||
constructor(
|
||||
private store: Store<any>,
|
||||
private dialog: MatDialog,
|
||||
private i18nService: I18nService,
|
||||
private logService: LogService,
|
||||
private sessionStorageService: SessionStorageService,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private appChatService: AppChatService,
|
||||
private changeDetectorRef: ChangeDetectorRef
|
||||
) {}
|
||||
) {
|
||||
this.historyRoomId = this.sessionStorageService.get<string>(
|
||||
AppKey.HistoryRoomId
|
||||
);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.ngOnDestroySubject = new Subject<boolean>();
|
||||
this.activatedRoute.queryParams
|
||||
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||
.subscribe((params: Params) => {
|
||||
const seqParam = params[QueryParams.ROOM_ID];
|
||||
|
||||
// language setting
|
||||
this.i18nService.setDefaultNamespace('chat');
|
||||
this.sessionStorageService.set<string>(
|
||||
AppKey.HistoryRoomId,
|
||||
!!seqParam ? seqParam : undefined
|
||||
);
|
||||
|
||||
if (
|
||||
seqParam === undefined &&
|
||||
!!this.historyRoomId &&
|
||||
this.historyRoomId.length > 0
|
||||
) {
|
||||
// exist history roomId.
|
||||
this.appChatService.openRoombyRoomId(this.historyRoomId);
|
||||
}
|
||||
});
|
||||
|
||||
this.store
|
||||
.pipe(takeUntil(this.ngOnDestroySubject), select(RoomSelector.rooms))
|
||||
|
@ -57,6 +87,7 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
|
|||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.next();
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
|
@ -64,14 +95,16 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
|
|||
onToggleChackable(checkable: boolean): void {
|
||||
if (!!checkable) {
|
||||
if (!!this.selectedRoomList && this.selectedRoomList.length > 0) {
|
||||
const self = this;
|
||||
const dialogRef = this.dialog.open<
|
||||
ConfirmDialogComponent,
|
||||
ConfirmDialogData,
|
||||
ConfirmDialogResult
|
||||
>(ConfirmDialogComponent, {
|
||||
panelClass: 'min-create-dialog',
|
||||
data: {
|
||||
title: this.i18nService.t('dialog.title.exitFromRoom'),
|
||||
html: this.i18nService.t('dialog.confirmExitFromRoom')
|
||||
title: this.i18nService.t('chat:label.exitFromRoom'),
|
||||
html: this.i18nService.t('chat:dialog.confirmExitFromRoom')
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -89,6 +122,8 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
|
|||
} as ExitAllRequest
|
||||
})
|
||||
);
|
||||
self.selectedRoomList = [];
|
||||
self.checkable = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -131,7 +166,7 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
|
|||
} else {
|
||||
this.selectedRoomList = [];
|
||||
}
|
||||
this.changeDetectorRef.detectChanges();
|
||||
this.changeDetectorRef.markForCheck();
|
||||
}
|
||||
|
||||
onToggleItem(event: { checked: boolean; roomInfo: RoomInfo }): void {
|
||||
|
@ -160,16 +195,16 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
|
|||
this.searchObj = {
|
||||
isShowSearch: true,
|
||||
searchWord: params.searchWord
|
||||
};
|
||||
this.changeDetectorRef.detectChanges();
|
||||
} as SearchInfo;
|
||||
this.changeDetectorRef.markForCheck();
|
||||
}
|
||||
/** Searching cancel */
|
||||
onClickCancel() {
|
||||
this.searchObj = {
|
||||
isShowSearch: false,
|
||||
searchWord: ''
|
||||
};
|
||||
this.changeDetectorRef.detectChanges();
|
||||
} as SearchInfo;
|
||||
this.changeDetectorRef.markForCheck();
|
||||
}
|
||||
|
||||
onSearchResultList(searchResultList: RoomInfo[]) {
|
||||
|
|
6
src/app/pages/chat/models/draw-info.ts
Normal file
6
src/app/pages/chat/models/draw-info.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { ChatDrawType } from '../types/chat-draw.type';
|
||||
|
||||
export interface DrawInfo {
|
||||
chatDrawType: ChatDrawType;
|
||||
returnDrawType?: ChatDrawType;
|
||||
}
|
4
src/app/pages/chat/models/search-info.ts
Normal file
4
src/app/pages/chat/models/search-info.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export interface SearchInfo {
|
||||
isShowSearch: boolean;
|
||||
searchWord: string;
|
||||
}
|
9
src/app/pages/chat/types/chat-draw.type.ts
Normal file
9
src/app/pages/chat/types/chat-draw.type.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
export enum ChatDrawType {
|
||||
AttachImage = 'ATTACHIMAGE',
|
||||
AttachVideo = 'ATTACHVIDEO',
|
||||
AttachFile = 'ATTACHFILE',
|
||||
Invite = 'INVITE',
|
||||
Setting = 'SETTING',
|
||||
RoomUsers = 'ROOMUSERS',
|
||||
AddGroup = 'ADDGROUP'
|
||||
}
|
|
@ -1,11 +1,45 @@
|
|||
<div fxLayout="column" class="index-container">
|
||||
<div class="subtitle" fxFlex="60px">Welcome to M-Messenger</div>
|
||||
<!--<div class="subtitle" fxFlex="60px">Welcome to M-Messenger</div>-->
|
||||
<div class="content-container" fxFlex="1 1 auto" fxLayout="row">
|
||||
<div class="profile-container" fxFlex="44%">
|
||||
<app-organization-profile-01 [userSeq]="userSeq">
|
||||
<nav mat-tab-nav-bar color="accent" class="group-main-nav">
|
||||
<a
|
||||
mat-tab-link
|
||||
(click)="activeLink = 0"
|
||||
[active]="activeLink === 0 ? 'link' : undefined"
|
||||
>{{ profileName }}</a
|
||||
>
|
||||
<a
|
||||
mat-tab-link
|
||||
(click)="activeLink = 1"
|
||||
[active]="activeLink === 1 ? 'link' : undefined"
|
||||
>{{ tabName }}</a
|
||||
>
|
||||
</nav>
|
||||
<div
|
||||
class="profile-container"
|
||||
[ngClass]="activeLink === 0 ? 'active' : ''"
|
||||
fxFlex="44%"
|
||||
>
|
||||
<app-organization-profile-01
|
||||
[userSeq]="userSeq"
|
||||
(openChat)="onOpenCaht($event)"
|
||||
(sendMessage)="onSendMessage($event)"
|
||||
(sendCall)="onSendCall($event)"
|
||||
(sendSms)="onSendSms($event)"
|
||||
(createConference)="onCreateConference($event)"
|
||||
(toggleFavorit)="onToggleFavorit($event)"
|
||||
(toggleBuddy)="onToggleBuddy($event)"
|
||||
(uploadProfileImage)="onUploadProfileImage($event)"
|
||||
(updateIntro)="onUpdateIntro($event)"
|
||||
(updateNickname)="onUpdateNickname($event)"
|
||||
>
|
||||
</app-organization-profile-01>
|
||||
</div>
|
||||
<div class="group-info-container" fxFlex="1 1 auto">
|
||||
<div
|
||||
class="group-info-container"
|
||||
[ngClass]="activeLink === 1 ? 'active' : ''"
|
||||
fxFlex="1 1 auto"
|
||||
>
|
||||
<app-sections-group-info [userSeq]="userSeq"></app-sections-group-info>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,8 +2,18 @@
|
|||
|
||||
.index-container {
|
||||
height: 100%;
|
||||
padding: 0 30px 30px;
|
||||
padding: 30px 30px;
|
||||
overflow: auto;
|
||||
@include screen(lg) {
|
||||
padding: 0 20px 30px;
|
||||
}
|
||||
@include screen(mid) {
|
||||
padding: 0 15px 30px;
|
||||
}
|
||||
|
||||
@include screen(xs) {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
|
@ -18,12 +28,91 @@
|
|||
color: $gray-re4;
|
||||
}
|
||||
|
||||
.profile-container {
|
||||
.content-container {
|
||||
@include screen(lg) {
|
||||
display: flex;
|
||||
flex-direction: column !important;
|
||||
}
|
||||
@include screen(mid) {
|
||||
display: flex;
|
||||
flex-direction: column !important;
|
||||
}
|
||||
.group-main-nav {
|
||||
display: none;
|
||||
}
|
||||
@include screen(xs) {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
.group-main-nav {
|
||||
display: block;
|
||||
position: fixed;
|
||||
height: 49px;
|
||||
top: 40px;
|
||||
left: 75px;
|
||||
z-index: 10;
|
||||
width: calc(100% - 90px);
|
||||
a {
|
||||
width: 50%;
|
||||
text-decoration: none;
|
||||
color: rgba(51, 51, 51, 0.6);
|
||||
font-weight: 600;
|
||||
&.mat-tab-label-active {
|
||||
color: rgba(51, 51, 51, 1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.profile-container {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
flex: 1 0 460px !important;
|
||||
max-width: inherit !important;
|
||||
border-radius: 2px;
|
||||
@include screen(custom, min, 1540) {
|
||||
flex: 1 0 44% !important;
|
||||
}
|
||||
@include screen(lg) {
|
||||
flex: 1 0 auto !important;
|
||||
max-width: 100% !important;
|
||||
height: auto;
|
||||
}
|
||||
@include screen(mid) {
|
||||
flex: 1 0 auto !important;
|
||||
max-width: 100% !important;
|
||||
height: auto;
|
||||
}
|
||||
@include screen(xs) {
|
||||
display: none;
|
||||
&.active {
|
||||
display: block;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.group-info-container {
|
||||
.group-info-container {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
flex: 5 1 auto !important;
|
||||
@include screen(lg) {
|
||||
flex: 1 0 auto !important;
|
||||
max-width: 100% !important;
|
||||
height: auto;
|
||||
}
|
||||
@include screen(mid) {
|
||||
flex: 1 0 auto !important;
|
||||
max-width: 100% !important;
|
||||
height: auto;
|
||||
}
|
||||
@include screen(xs) {
|
||||
display: none;
|
||||
&.active {
|
||||
display: block;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,40 +1,194 @@
|
|||
import { Subscription } from 'rxjs';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
ChangeDetectorRef,
|
||||
ChangeDetectionStrategy
|
||||
} from '@angular/core';
|
||||
import { Router, ActivatedRoute, Params } from '@angular/router';
|
||||
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
|
||||
import { Store, select } from '@ngrx/store';
|
||||
|
||||
import { FileUploadItem } from '@ucap/api';
|
||||
import { FileProfileSaveRequest } from '@ucap/api-common';
|
||||
import { VersionInfo2Response } from '@ucap/api-public';
|
||||
import { UserInfoSS, UserInfoF } from '@ucap/protocol-query';
|
||||
import { UserInfoUpdateType, User } from '@ucap/protocol-info';
|
||||
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||
|
||||
import { LogService } from '@ucap/ng-logger';
|
||||
import { I18nService } from '@ucap/ng-i18n';
|
||||
import { UserSelector } from '@ucap/ng-store-organization';
|
||||
import {
|
||||
LoginSelector,
|
||||
ConfigurationSelector
|
||||
} from '@ucap/ng-store-authentication';
|
||||
|
||||
import { UserInfoTypes } from '@app/types';
|
||||
import { AppFileService } from '@app/services/app-file.service';
|
||||
import { AppAuthenticationService } from '@app/services/app-authentication.service';
|
||||
import { AppGroupService } from '@app/services/app-group.service';
|
||||
|
||||
import { QueryParams } from '../types/params.type';
|
||||
import { AppChatService } from '@app/services/app-chat.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-pages-group-index',
|
||||
templateUrl: './index.page.component.html',
|
||||
styleUrls: ['./index.page.component.scss']
|
||||
styleUrls: ['./index.page.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class IndexPageComponent implements OnInit, OnDestroy {
|
||||
private paramsSubscription: Subscription;
|
||||
private ngOnDestroySubject: Subject<void> = new Subject();
|
||||
|
||||
constructor(
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private store: Store<any>,
|
||||
private router: Router,
|
||||
private logService: LogService
|
||||
) {}
|
||||
private logService: LogService,
|
||||
private dialog: MatDialog,
|
||||
private i18nService: I18nService,
|
||||
private appFileServie: AppFileService,
|
||||
private appAuthenticationService: AppAuthenticationService,
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private appChatService: AppChatService,
|
||||
private appGroupService: AppGroupService
|
||||
) {
|
||||
this.profileName = this.i18nService.t('organization:profile.me');
|
||||
this.tabName = this.i18nService.t('organization:profile.chatWithUs');
|
||||
}
|
||||
|
||||
userSeq: string;
|
||||
userSeq: string = undefined;
|
||||
user: User;
|
||||
loginRes: LoginResponse;
|
||||
versionInfo2Res: VersionInfo2Response;
|
||||
activeLink = 0;
|
||||
tabName: string;
|
||||
profileName: string;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.paramsSubscription = this.activatedRoute.queryParams.subscribe(
|
||||
(params: Params) => {
|
||||
this.activatedRoute.queryParams
|
||||
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||
.subscribe((params: Params) => {
|
||||
const seqParam = params[QueryParams.ID];
|
||||
this.userSeq = !!seqParam ? seqParam : undefined;
|
||||
if (!!seqParam) {
|
||||
this.userSeq = seqParam;
|
||||
} else {
|
||||
this.userSeq = '';
|
||||
}
|
||||
);
|
||||
this._refreshProfile();
|
||||
});
|
||||
|
||||
this.store
|
||||
.pipe(takeUntil(this.ngOnDestroySubject), select(UserSelector.user))
|
||||
.subscribe((user) => {
|
||||
this.user = user;
|
||||
this._refreshProfile();
|
||||
});
|
||||
this.store
|
||||
.pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
|
||||
.subscribe((loginRes) => {
|
||||
this.loginRes = loginRes;
|
||||
});
|
||||
|
||||
this.store
|
||||
.pipe(
|
||||
takeUntil(this.ngOnDestroySubject),
|
||||
select(ConfigurationSelector.versionInfo2Response)
|
||||
)
|
||||
.subscribe((versionInfo2Res) => {
|
||||
this.versionInfo2Res = versionInfo2Res;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.paramsSubscription) {
|
||||
this.paramsSubscription.unsubscribe();
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.next();
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
|
||||
onOpenCaht(userInfo: UserInfoSS) {
|
||||
this.appChatService.newOpenRoom(
|
||||
[Number(userInfo.seq) as any],
|
||||
false,
|
||||
this.user
|
||||
);
|
||||
}
|
||||
onSendMessage(userInfo: UserInfoSS) {}
|
||||
onSendCall(call: string) {}
|
||||
onSendSms(employeeNum: string) {}
|
||||
onCreateConference(userSeq: number) {}
|
||||
onToggleFavorit(params: { userInfo: UserInfoSS; isFavorite: boolean }) {
|
||||
this.appGroupService.updateBuddy(params.userInfo, params.isFavorite);
|
||||
}
|
||||
onToggleBuddy(params: { userInfo: UserInfoSS; isBuddy: boolean }) {
|
||||
this.appGroupService
|
||||
.updateBuddyByToggle(params)
|
||||
.then((isRemoveBuddy) => {
|
||||
if (isRemoveBuddy) {
|
||||
this.router.navigate(
|
||||
[
|
||||
'group',
|
||||
{
|
||||
outlets: { content: 'index' }
|
||||
}
|
||||
],
|
||||
{
|
||||
queryParams: { id: Number(params.userInfo.seq) }
|
||||
}
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((reson) => this.logService.error(reson));
|
||||
}
|
||||
onUploadProfileImage(profileImageFileUploadItem: FileUploadItem) {
|
||||
const loginSession = this.appAuthenticationService.getLoginSession();
|
||||
|
||||
const profile = {
|
||||
userSeq: String(this.user.info.seq),
|
||||
deviceType: loginSession.deviceType,
|
||||
token: this.loginRes.tokenString,
|
||||
file: profileImageFileUploadItem.file,
|
||||
fileUploadItem: profileImageFileUploadItem
|
||||
} as FileProfileSaveRequest;
|
||||
|
||||
this.appFileServie.fileProfileSave(
|
||||
profile,
|
||||
this.versionInfo2Res.profileUploadUrl
|
||||
);
|
||||
}
|
||||
onUpdateIntro(intro: string) {
|
||||
this.appGroupService.updateIntro(intro, UserInfoUpdateType.Intro);
|
||||
}
|
||||
|
||||
onUpdateNickname(params: { userInfo: UserInfoTypes; nickname: string }) {
|
||||
this.appGroupService.updateNickname(
|
||||
params.userInfo as UserInfoF,
|
||||
params.nickname
|
||||
);
|
||||
}
|
||||
|
||||
private _refreshProfile() {
|
||||
if (!this.user || undefined === this.userSeq) {
|
||||
return;
|
||||
}
|
||||
if ('' === this.userSeq) {
|
||||
this.userSeq = String(this.user.info.seq);
|
||||
}
|
||||
|
||||
if (String(this.user.info.seq) === String(this.userSeq)) {
|
||||
this.profileName = this.i18nService.t('organization:profile.me');
|
||||
this.tabName = this.i18nService.t('organization:profile.unreadChat');
|
||||
} else {
|
||||
this.profileName = this.i18nService.t('organization:profile.other');
|
||||
this.tabName = this.i18nService.t('organization:profile.chatWithUs');
|
||||
}
|
||||
this.changeDetectorRef.markForCheck();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<div class="sidenav-container" fxFlexFill fxLayout="column">
|
||||
<div class="sidenav-container group" fxFlexFill fxLayout="column">
|
||||
<div class="title-section" fxFlex="0 0 50px" fxLayout="row">
|
||||
<div class="title">
|
||||
<h3 fxFlex="1 1 auto">그룹</h3>
|
||||
<div class="menu-btn" fxFlex="80px">
|
||||
<h3 fxFlex="1 1 auto">{{ 'group:label.group' | ucapI18n }}</h3>
|
||||
<div class="menu-btn">
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="groupViewMenu"
|
||||
|
@ -21,43 +21,58 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="extra-box" fxFlex="0 0 50px">
|
||||
<app-organization-search-for-tenant [(searchData)]="companySearchData">
|
||||
<app-organization-search-for-tenant
|
||||
[(searchData)]="companySearchData"
|
||||
(canceled)="onSearchCancel()"
|
||||
>
|
||||
</app-organization-search-for-tenant>
|
||||
</div>
|
||||
|
||||
<div fxFlex="1 1 auto">
|
||||
<app-sections-group-list
|
||||
#sectionGroupList
|
||||
fxFlexFill
|
||||
[searchData]="companySearchData"
|
||||
[showType]="showType"
|
||||
(clickUser)="onClickUser($event)"
|
||||
></app-sections-group-list>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<mat-menu #groupMenu="matMenu">
|
||||
<button mat-menu-item (click)="onClickGroupMenu('GROUP_NEW')">
|
||||
{{ 'moreMenu.group.addNew' | ucapI18n }}
|
||||
<mat-icon matPrefix class="material-icons-outlined">person_add</mat-icon
|
||||
>{{ 'group:contextMenu.addNewGroup' | ucapI18n }}
|
||||
</button>
|
||||
<button mat-menu-item (click)="onClickGroupMenu('GROUP_EXPAND_MORE')">
|
||||
{{ 'moreMenu.group.expandMore' | ucapI18n }}
|
||||
<mat-icon matPrefix>keyboard_arrow_down</mat-icon
|
||||
>{{ 'group:contextMenu.expandMore' | ucapI18n }}
|
||||
</button>
|
||||
<button mat-menu-item (click)="onClickGroupMenu('GROUP_EXPAND_LESS')">
|
||||
{{ 'moreMenu.group.expandLess' | ucapI18n }}
|
||||
</button>
|
||||
<button mat-menu-item (click)="onClickGroupMenu('GROUP_CHANGE_ODER')">
|
||||
{{ 'moreMenu.group.changeOrder' | ucapI18n }}
|
||||
<mat-icon matPrefix>keyboard_arrow_up</mat-icon
|
||||
>{{ 'group:contextMenu.expandLess' | ucapI18n }}
|
||||
</button>
|
||||
<!-- <button mat-menu-item (click)="onClickGroupMenu('GROUP_CHANGE_ODER')">
|
||||
<mat-icon matPrefix>low_priority</mat-icon
|
||||
>{{ 'group:contextMenu.changeOrder' | ucapI18n }}
|
||||
</button> -->
|
||||
</mat-menu>
|
||||
|
||||
<mat-menu #groupViewMenu="matMenu">
|
||||
<button mat-menu-item (click)="onClickShowGroupMenu('ALL')">
|
||||
{{ 'moreMenu.show.all' | ucapI18n }}
|
||||
<button mat-menu-item (click)="onClickShowGroupMenu(sortViewType.all)">
|
||||
<mat-icon matPrefix>{{ showGroupMenuIcon(sortViewType.all) }}</mat-icon
|
||||
>{{ 'group:contextMenu.all' | ucapI18n }}
|
||||
</button>
|
||||
<button mat-menu-item (click)="onClickShowGroupMenu('ONLINE_BUDDY')">
|
||||
{{ 'moreMenu.show.onlineBuddy' | ucapI18n }}
|
||||
<button
|
||||
mat-menu-item
|
||||
(click)="onClickShowGroupMenu(sortViewType.onlineBuddy)"
|
||||
>
|
||||
<mat-icon matPrefix>{{
|
||||
showGroupMenuIcon(sortViewType.onlineBuddy)
|
||||
}}</mat-icon
|
||||
>{{ 'group:contextMenu.onlineBuddy' | ucapI18n }}
|
||||
</button>
|
||||
<button mat-menu-item (click)="onClickShowGroupMenu('ON_OFF')">
|
||||
{{ 'moreMenu.show.onOff' | ucapI18n }}
|
||||
<button mat-menu-item (click)="onClickShowGroupMenu(sortViewType.onOff)">
|
||||
<mat-icon matPrefix>{{ showGroupMenuIcon(sortViewType.onOff) }}</mat-icon
|
||||
>{{ 'group:contextMenu.onOffBuddy' | ucapI18n }}
|
||||
</button>
|
||||
</mat-menu>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
@import '~@ucap/lg-scss/mixins';
|
||||
|
||||
.sidenav-container {
|
||||
.sidenav-container.group {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
|
@ -24,9 +24,6 @@
|
|||
align-items: center;
|
||||
font-weight: 600;
|
||||
}
|
||||
.menu-btn {
|
||||
justify-self: end;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,39 +1,46 @@
|
|||
import { of, Subject } from 'rxjs';
|
||||
import { take, map, catchError, takeUntil } from 'rxjs/operators';
|
||||
import { take, map, catchError } from 'rxjs/operators';
|
||||
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
ChangeDetectorRef,
|
||||
ViewChild
|
||||
ViewChild,
|
||||
ChangeDetectionStrategy
|
||||
} from '@angular/core';
|
||||
import { ActivatedRoute, Router, Params } from '@angular/router';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
|
||||
import { ParamsUtil } from '@ucap/ng-core';
|
||||
import { LogService } from '@ucap/ng-logger';
|
||||
import { I18nService } from '@ucap/ng-i18n';
|
||||
|
||||
import { SearchData } from '@app/ucap/organization/models/search-data';
|
||||
|
||||
import { CreateDialogComponent } from '@app/sections/group/dialogs/create.dialog.component';
|
||||
import { ListSectionComponent } from '@app/sections/group/components/list.section.component';
|
||||
import { SearchData } from '@app/ucap/organization/models/search-data';
|
||||
import { QueryParams } from '@app/pages/organization/types/params.type';
|
||||
import { UserInfo } from '@ucap/protocol-sync';
|
||||
import { SortViewType } from '../types/sort-view.type';
|
||||
|
||||
@Component({
|
||||
selector: 'app-pages-group-sidenav',
|
||||
templateUrl: './sidenav.page.component.html',
|
||||
styleUrls: ['./sidenav.page.component.scss']
|
||||
styleUrls: ['./sidenav.page.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class SidenavPageComponent implements OnInit, OnDestroy {
|
||||
@ViewChild('sectionGroupList', { static: false })
|
||||
sectionGroupList: ListSectionComponent;
|
||||
|
||||
set companySearchData(searchData: SearchData) {
|
||||
this._companySearchData = searchData;
|
||||
if (!!searchData && searchData.searchWord !== '') {
|
||||
this._companySearchData = { ...searchData, bySearch: true };
|
||||
} else {
|
||||
this._companySearchData = { ...searchData, bySearch: false };
|
||||
}
|
||||
}
|
||||
get companySearchData() {
|
||||
return this._companySearchData;
|
||||
|
@ -41,9 +48,10 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
|
|||
// tslint:disable-next-line: variable-name
|
||||
_companySearchData: SearchData;
|
||||
|
||||
showType: string;
|
||||
showType: SortViewType;
|
||||
sortViewType = SortViewType;
|
||||
|
||||
private ngOnDestroySubject: Subject<boolean>;
|
||||
private ngOnDestroySubject: Subject<void> = new Subject();
|
||||
|
||||
constructor(
|
||||
private activatedRoute: ActivatedRoute,
|
||||
|
@ -53,17 +61,19 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
|
|||
private store: Store<any>,
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
public dialog: MatDialog
|
||||
) {
|
||||
this.i18nService.setDefaultNamespace('group');
|
||||
}
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.ngOnDestroySubject = new Subject<boolean>();
|
||||
|
||||
this.showType = 'ALL';
|
||||
this.showType = SortViewType.all;
|
||||
this.showGroupMenuIcon(SortViewType.all);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {}
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.next();
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
|
||||
onClickFab(event: MouseEvent) {}
|
||||
|
||||
|
@ -72,8 +82,7 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
|
|||
case 'GROUP_NEW':
|
||||
{
|
||||
const dialogRef = this.dialog.open(CreateDialogComponent, {
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
panelClass: 'max-create-dialog'
|
||||
});
|
||||
|
||||
dialogRef
|
||||
|
@ -105,23 +114,48 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
onClickShowGroupMenu(menuType: string) {
|
||||
onClickShowGroupMenu(menuType: SortViewType) {
|
||||
switch (menuType) {
|
||||
case 'ALL':
|
||||
case SortViewType.all:
|
||||
{
|
||||
this.showType = 'ALL';
|
||||
this.showType = SortViewType.all;
|
||||
}
|
||||
break;
|
||||
case 'ONLINE_BUDDY':
|
||||
case SortViewType.onlineBuddy:
|
||||
{
|
||||
this.showType = 'ONLINE_BUDDY';
|
||||
this.showType = SortViewType.onlineBuddy;
|
||||
}
|
||||
break;
|
||||
case 'ON_OFF':
|
||||
case SortViewType.onOff:
|
||||
{
|
||||
this.showType = 'ON_OFF';
|
||||
this.showType = SortViewType.onOff;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
onSearchCancel() {
|
||||
this.companySearchData = { ...this.companySearchData, searchWord: '' };
|
||||
}
|
||||
|
||||
showGroupMenuIcon(menuType: SortViewType): string {
|
||||
if (this.showType === menuType) {
|
||||
return 'check_circle';
|
||||
}
|
||||
|
||||
return 'check_circle_outline';
|
||||
}
|
||||
|
||||
onClickUser(userInfo: UserInfo) {
|
||||
this.router.navigate(
|
||||
[
|
||||
'group',
|
||||
{
|
||||
outlets: { content: 'index' }
|
||||
}
|
||||
],
|
||||
{
|
||||
queryParams: { id: Number(userInfo.seq) }
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import { FlexLayoutModule } from '@angular/flex-layout';
|
|||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
|
||||
import { UiModule } from '@ucap/ng-ui';
|
||||
|
||||
|
@ -26,6 +27,7 @@ import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
|
|||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatMenuModule,
|
||||
MatTabsModule,
|
||||
|
||||
AppOrganizationModule,
|
||||
|
||||
|
|
5
src/app/pages/group/types/sort-view.type.ts
Normal file
5
src/app/pages/group/types/sort-view.type.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export enum SortViewType {
|
||||
all = 'ALL',
|
||||
onlineBuddy = 'ONLINE_BUDDY',
|
||||
onOff = 'ON_OFF'
|
||||
}
|
|
@ -1 +1,8 @@
|
|||
Index page of message is works!
|
||||
<!--Index page of message is works!-->
|
||||
<div class="index-page-empty">
|
||||
<div class="ico-coming-soon"></div>
|
||||
<div class="coming-soon-index-copy">
|
||||
<span>Coming Soon</span>
|
||||
<span class="guide-text">곧 새로운 모습으로 찾아 뵙겠습니다.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
.index-page-empty {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.ico-coming-soon {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
margin-top: -100px;
|
||||
background-image: url(../../../../assets/images/ico/img_coming_soon.png);
|
||||
background-size: 100% auto;
|
||||
}
|
||||
.coming-soon-index-copy {
|
||||
text-align: center;
|
||||
font-size: 2.4em;
|
||||
color: #666;
|
||||
font-weight: 600;
|
||||
padding: 0 20px;
|
||||
//border-top: 1px solid #ccc;
|
||||
//border-bottom: 1px solid #ccc;
|
||||
span {
|
||||
display: block;
|
||||
}
|
||||
.guide-text {
|
||||
padding-top: 10px;
|
||||
font-size: 0.54em;
|
||||
color: #999;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,10 @@
|
|||
<div class="index-page-container" fxLayout="column">
|
||||
<!-- search start-->
|
||||
<div fxFlex="0 0 50px">
|
||||
<app-organization-search-for-tenant [(searchData)]="companySearchData">
|
||||
<app-organization-search-for-tenant
|
||||
[(searchData)]="companySearchData"
|
||||
(canceled)="onCanceledSearch()"
|
||||
>
|
||||
</app-organization-search-for-tenant>
|
||||
</div>
|
||||
<!-- search end-->
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
.index-page-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
|
|
@ -1,21 +1,28 @@
|
|||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
import { Component, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
ChangeDetectorRef,
|
||||
ChangeDetectionStrategy
|
||||
} from '@angular/core';
|
||||
import { ActivatedRoute, Router, Params } from '@angular/router';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { ParamsUtil } from '@ucap/ng-core';
|
||||
|
||||
import { AppAuthenticationService } from '@app/services/app-authentication.service';
|
||||
import { SearchData } from '@app/ucap/organization/models/search-data';
|
||||
|
||||
import { QueryParams } from '../types/params.type';
|
||||
import { UserStore } from '@app/models/user-store';
|
||||
|
||||
@Component({
|
||||
selector: 'app-pages-organization-index',
|
||||
templateUrl: './index.page.component.html',
|
||||
styleUrls: ['./index.page.component.scss']
|
||||
styleUrls: ['./index.page.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class IndexPageComponent implements OnInit, OnDestroy {
|
||||
set companySearchData(searchData: SearchData) {
|
||||
|
@ -32,18 +39,19 @@ export class IndexPageComponent implements OnInit, OnDestroy {
|
|||
|
||||
deptSeq: string;
|
||||
|
||||
private ngOnDestroySubject: Subject<boolean>;
|
||||
private ngOnDestroySubject: Subject<void> = new Subject();
|
||||
private userStore: UserStore;
|
||||
|
||||
constructor(
|
||||
private store: Store<any>,
|
||||
private appAuthenticationService: AppAuthenticationService,
|
||||
private router: Router,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private changeDetectorRef: ChangeDetectorRef
|
||||
) {}
|
||||
) {
|
||||
this.userStore = this.appAuthenticationService.getUserStore();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.ngOnDestroySubject = new Subject<boolean>();
|
||||
|
||||
this.activatedRoute.queryParams
|
||||
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||
.subscribe((params) => {
|
||||
|
@ -57,22 +65,31 @@ export class IndexPageComponent implements OnInit, OnDestroy {
|
|||
false
|
||||
);
|
||||
|
||||
if (!!deptSeq && this.deptSeq !== deptSeq) {
|
||||
this.deptSeq = deptSeq;
|
||||
}
|
||||
|
||||
this.deptSearchData = {
|
||||
deptSeq: bySearch ? undefined : deptSeq,
|
||||
companyCode: bySearch ? companyCode : undefined,
|
||||
searchWord: bySearch ? searchWord : undefined,
|
||||
companyCode: !!companyCode
|
||||
? companyCode
|
||||
: this.userStore.companyCode,
|
||||
searchWord: bySearch ? decodeURIComponent(searchWord) : undefined,
|
||||
bySearch
|
||||
};
|
||||
|
||||
this._companySearchData = {
|
||||
...this.deptSearchData
|
||||
};
|
||||
|
||||
this.changeDetectorRef.markForCheck();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.next();
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
|
@ -80,9 +97,21 @@ export class IndexPageComponent implements OnInit, OnDestroy {
|
|||
onChangedCompanySearch() {
|
||||
const queryParams: Params = {};
|
||||
queryParams[QueryParams.COMPANY_CODE] = this._companySearchData.companyCode;
|
||||
queryParams[QueryParams.SEARCH_WORD] = this._companySearchData.searchWord;
|
||||
queryParams[QueryParams.SEARCH_WORD] = encodeURIComponent(
|
||||
this._companySearchData.searchWord
|
||||
);
|
||||
queryParams[QueryParams.BY_SEARCH] = String(true);
|
||||
|
||||
this._navigate(queryParams);
|
||||
}
|
||||
|
||||
onCanceledSearch() {
|
||||
const queryParams: Params = {};
|
||||
queryParams[QueryParams.DEPT_SEQ] = String(this.deptSeq);
|
||||
this._navigate(queryParams);
|
||||
}
|
||||
|
||||
private _navigate(queryParams: Params = {}) {
|
||||
this.router.navigate(
|
||||
[
|
||||
'organization',
|
||||
|
|
|
@ -1,22 +1,12 @@
|
|||
<div class="sidenav-container" fxFlexFill fxLayout="column">
|
||||
<div class="sub-header" fxFlex="50px" fxLayout="row">
|
||||
<div fxFlex="1 1 auto">
|
||||
<h3>조직도</h3>
|
||||
</div>
|
||||
|
||||
<div class="menu-btn" fxFlex="0 0 40px">
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="organizationMenu"
|
||||
aria-label="organization menu"
|
||||
>
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<h3>{{ 'organization:label.organization' | ucapI18n }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="extra-box" fxFlex="0 0 50px">
|
||||
<mat-icon matPrefix class="ico-business">business</mat-icon>LG CNS
|
||||
<div *ngIf="!!displayRootDept" class="extra-box" fxFlex="0 0 50px">
|
||||
<mat-icon matPrefix class="ico-business">business</mat-icon
|
||||
>{{ displayRootDept | ucapOrganizationTranslate: 'name' }}
|
||||
</div>
|
||||
|
||||
<div fxFlex="1 1 auto">
|
||||
|
@ -26,8 +16,3 @@
|
|||
></app-organization-tree>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<mat-menu #organizationMenu="matMenu">
|
||||
<button mat-menu-item>Item 1</button>
|
||||
<button mat-menu-item>Item 2</button>
|
||||
</mat-menu>
|
||||
|
|
|
@ -1,27 +1,44 @@
|
|||
import { Subject } from 'rxjs';
|
||||
import { Subject, combineLatest } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
import { Component, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
ChangeDetectorRef,
|
||||
ChangeDetectionStrategy
|
||||
} from '@angular/core';
|
||||
import { Router, ActivatedRoute, Params } from '@angular/router';
|
||||
|
||||
import { Store, select } from '@ngrx/store';
|
||||
|
||||
import { LogService } from '@ucap/ng-logger';
|
||||
import { DeptInfo } from '@ucap/protocol-query';
|
||||
|
||||
import { DepartmentSelector, UserSelector } from '@ucap/ng-store-organization';
|
||||
import { LoginSelector } from '@ucap/ng-store-authentication';
|
||||
|
||||
import { environment } from '@environments';
|
||||
|
||||
import { QueryParams } from '../types/params.type';
|
||||
|
||||
@Component({
|
||||
selector: 'app-pages-ogranization-sidenav',
|
||||
templateUrl: './sidenav.page.component.html',
|
||||
styleUrls: ['./sidenav.page.component.scss']
|
||||
styleUrls: ['./sidenav.page.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class SidenavPageComponent implements OnInit, OnDestroy {
|
||||
initialExpanded: number;
|
||||
displayRoot = false;
|
||||
displayRootDept: DeptInfo;
|
||||
|
||||
private ngOnDestroySubject: Subject<void>;
|
||||
private ngOnDestroySubject: Subject<void> = new Subject();
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private store: Store<any>,
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private logService: LogService
|
||||
) {
|
||||
|
@ -29,22 +46,56 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.ngOnDestroySubject = new Subject<void>();
|
||||
// this.activatedRoute.queryParams
|
||||
// .pipe(takeUntil(this.ngOnDestroySubject))
|
||||
// .subscribe((params) => {
|
||||
// if (!!params) {
|
||||
// const deptSeq = params[QueryParams.DEPT_SEQ];
|
||||
// if (!!deptSeq) {
|
||||
// this.initialExpanded = Number(deptSeq);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
this.activatedRoute.queryParams
|
||||
combineLatest([
|
||||
this.activatedRoute.queryParams,
|
||||
this.store.pipe(select(UserSelector.user))
|
||||
])
|
||||
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||
.subscribe((params) => {
|
||||
.subscribe(([params, user]) => {
|
||||
let existParams = false;
|
||||
if (!!params) {
|
||||
const deptSeq = params[QueryParams.DEPT_SEQ];
|
||||
if (!!deptSeq) {
|
||||
existParams = true;
|
||||
this.initialExpanded = Number(deptSeq);
|
||||
}
|
||||
}
|
||||
|
||||
if (!existParams) {
|
||||
this.initialExpanded = user.departmentCode;
|
||||
}
|
||||
});
|
||||
|
||||
this.store
|
||||
.pipe(
|
||||
takeUntil(this.ngOnDestroySubject),
|
||||
select(DepartmentSelector.departmentInfoList)
|
||||
)
|
||||
.subscribe((deptInfoList) => {
|
||||
if (!environment.productConfig.organization.displayRoot) {
|
||||
if (!!deptInfoList && deptInfoList.length > 0) {
|
||||
this.displayRootDept = deptInfoList.find(
|
||||
(item) => item.type === 'R'
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.next();
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +103,6 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
|
|||
onClickedTree(node: DeptInfo) {
|
||||
const queryParams: Params = {};
|
||||
queryParams[QueryParams.DEPT_SEQ] = String(node.seq);
|
||||
queryParams[QueryParams.BY_SEARCH] = String(false);
|
||||
|
||||
this.router.navigate(
|
||||
[
|
||||
|
|
|
@ -12,6 +12,9 @@ import { AppOrganizationSectionModule } from '@app/sections/organization/organiz
|
|||
import { AppOrganizationRoutingPageModule } from './organization-routing.page.module';
|
||||
|
||||
import { COMPONENTS } from './components';
|
||||
import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
|
||||
import { UiModule } from '@ucap/ng-ui';
|
||||
import { OrganizationUiModule } from '@ucap/ng-ui-organization';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -24,9 +27,19 @@ import { COMPONENTS } from './components';
|
|||
|
||||
AppOrganizationModule,
|
||||
AppOrganizationSectionModule,
|
||||
AppOrganizationRoutingPageModule
|
||||
AppOrganizationRoutingPageModule,
|
||||
|
||||
I18nModule,
|
||||
OrganizationUiModule,
|
||||
UiModule
|
||||
],
|
||||
declarations: [...COMPONENTS],
|
||||
entryComponents: []
|
||||
entryComponents: [],
|
||||
providers: [
|
||||
{
|
||||
provide: UCAP_I18N_NAMESPACE,
|
||||
useValue: ['organization', 'common']
|
||||
}
|
||||
]
|
||||
})
|
||||
export class AppOrganizationPageModule {}
|
||||
|
|
|
@ -4,67 +4,49 @@ import { ReactiveFormsModule } from '@angular/forms';
|
|||
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
|
||||
import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
|
||||
import { AuthenticationUiModule } from '@ucap/ng-ui-authentication';
|
||||
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatRadioModule } from '@angular/material/radio';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { MatSliderModule } from '@angular/material/slider';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
|
||||
import { I18nModule } from '@ucap/ng-i18n';
|
||||
import { UiModule } from '@ucap/ng-ui';
|
||||
import { AuthenticationUiModule } from '@ucap/ng-ui-authentication';
|
||||
import { OrganizationUiModule } from '@ucap/ng-ui-organization';
|
||||
|
||||
import { AppLayoutsModule } from '@app/layouts/layouts.module';
|
||||
import { AppOrganizationModule } from '@app/ucap/organization/organization.module';
|
||||
|
||||
import { COMPONENTS } from './components';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
FlexLayoutModule,
|
||||
MatCheckboxModule,
|
||||
I18nModule,
|
||||
AuthenticationUiModule,
|
||||
|
||||
ReactiveFormsModule,
|
||||
FlexLayoutModule,
|
||||
|
||||
MatButtonModule,
|
||||
MatButtonToggleModule,
|
||||
MatCardModule,
|
||||
MatDatepickerModule,
|
||||
MatDialogModule,
|
||||
MatCheckboxModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatMenuModule,
|
||||
MatProgressBarModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatCheckboxModule,
|
||||
MatRadioModule,
|
||||
MatSelectModule,
|
||||
MatSidenavModule,
|
||||
MatSliderModule,
|
||||
MatTabsModule,
|
||||
MatTooltipModule,
|
||||
MatToolbarModule,
|
||||
MatFormFieldModule,
|
||||
MatSelectModule
|
||||
|
||||
I18nModule,
|
||||
|
||||
UiModule,
|
||||
AuthenticationUiModule,
|
||||
OrganizationUiModule,
|
||||
|
||||
AppLayoutsModule,
|
||||
AppOrganizationModule
|
||||
],
|
||||
exports: [...COMPONENTS],
|
||||
declarations: [...COMPONENTS],
|
||||
entryComponents: [],
|
||||
providers: [
|
||||
{
|
||||
provide: UCAP_I18N_NAMESPACE,
|
||||
useValue: ['authentication']
|
||||
}
|
||||
]
|
||||
entryComponents: []
|
||||
})
|
||||
export class AppAccountSectionModule {}
|
||||
|
|
|
@ -1,127 +0,0 @@
|
|||
<div class="login-box">
|
||||
<ng-content select="[ucapAuthenticationLogin='header']"></ng-content>
|
||||
|
||||
<div class="login-content">
|
||||
<form name="loginForm" [formGroup]="loginForm" novalidate>
|
||||
<mat-form-field
|
||||
[style.display]="!!fixedCompanyCode ? 'none' : 'block'"
|
||||
class="login-company"
|
||||
appearance="none"
|
||||
>
|
||||
<mat-select
|
||||
[formControl]="companyCodeFormControl"
|
||||
[value]="companyCode || fixedCompanyCode"
|
||||
placeholder="{{ 'login.labels.selectCompany' | ucapI18n }}"
|
||||
class="login-input-area login-select-form"
|
||||
>
|
||||
<mat-option
|
||||
*ngFor="let company of companyList"
|
||||
[value]="company.companyCode"
|
||||
>{{ company.companyName }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<div class="login-input-area idpass-type">
|
||||
<mat-form-field
|
||||
class="login-idpass-txt"
|
||||
appearance="none"
|
||||
floatLabel=""
|
||||
>
|
||||
<!-- <mat-label>{{ 'login.fields.loginId' | ucapI18n }}</mat-label> -->
|
||||
<input
|
||||
matInput
|
||||
[formControl]="loginIdFormControl"
|
||||
placeholder="{{ 'login.fields.loginId' | ucapI18n }}"
|
||||
/>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="login-input-area idpass-type pass-type">
|
||||
<mat-form-field class="login-idpass-txt" appearance="none">
|
||||
<!-- <mat-label>{{ 'login.fields.loginPw' | ucapI18n }}</mat-label> -->
|
||||
<input
|
||||
matInput
|
||||
type="password"
|
||||
[formControl]="loginPwFormControl"
|
||||
placeholder="{{ 'login.fields.loginPw' | ucapI18n }}"
|
||||
#loginPw
|
||||
/>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="error-container">
|
||||
<mat-error
|
||||
*ngIf="
|
||||
companyCodeFormControl.dirty &&
|
||||
companyCodeFormControl.invalid &&
|
||||
companyCodeFormControl.hasError('required')
|
||||
"
|
||||
>
|
||||
{{ 'login.errors.requireCompany' | ucapI18n }}
|
||||
</mat-error>
|
||||
<mat-error
|
||||
*ngIf="
|
||||
loginIdFormControl.dirty &&
|
||||
loginIdFormControl.invalid &&
|
||||
loginIdFormControl.hasError('required')
|
||||
"
|
||||
>
|
||||
{{ 'login.errors.requireLoginId' | ucapI18n }}
|
||||
</mat-error>
|
||||
<mat-error
|
||||
*ngIf="
|
||||
loginPwFormControl.dirty &&
|
||||
loginPwFormControl.invalid &&
|
||||
loginPwFormControl.hasError('required')
|
||||
"
|
||||
>
|
||||
{{ 'login.errors.requireLoginPw' | ucapI18n }}
|
||||
</mat-error>
|
||||
<mat-error *ngIf="loginFailed">
|
||||
{{ 'login.errors.failed' | ucapI18n }}
|
||||
</mat-error>
|
||||
</div>
|
||||
|
||||
<button
|
||||
mat-raised-button
|
||||
class="login-input-submit"
|
||||
aria-label="LOG IN"
|
||||
[disabled]="
|
||||
loginForm.invalid ||
|
||||
disable ||
|
||||
(!!loginTry && !!loginTry.remainTimeForNextTry)
|
||||
"
|
||||
(click)="onClickLogin()"
|
||||
>
|
||||
<ng-container
|
||||
*ngIf="!processing && (!loginTry || !loginTry.remainTimeForNextTry)"
|
||||
>
|
||||
{{ 'login.labels.doLogin' | ucapI18n }}
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="!!loginTry && !!loginTry.remainTimeForNextTry">
|
||||
{{ 'login.errors.attemptsExceeded' | ucapI18n }}
|
||||
(
|
||||
{{
|
||||
moment
|
||||
.utc(
|
||||
moment
|
||||
.duration(loginTry.remainTimeForNextTry, 'seconds')
|
||||
.asMilliseconds()
|
||||
)
|
||||
.format('mm:ss')
|
||||
}}
|
||||
)
|
||||
</ng-container>
|
||||
|
||||
<mat-spinner
|
||||
*ngIf="processing && (!loginTry || !loginTry.remainTimeForNextTry)"
|
||||
>
|
||||
</mat-spinner>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<ng-content select="[ucapAuthenticationLogin='footer']"></ng-content>
|
||||
</div>
|
||||
</div>
|
|
@ -1,160 +0,0 @@
|
|||
@import '../../../../../assets/scss/components';
|
||||
|
||||
.login-box {
|
||||
@extend %clearfix;
|
||||
padding: 0 0 45px;
|
||||
width: 420px;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
flex-basis: auto;
|
||||
align-items: center;
|
||||
.logo-img {
|
||||
display: block;
|
||||
text-align: center;
|
||||
img {
|
||||
margin-bottom: 7px;
|
||||
vertical-align: top;
|
||||
@include screen(mid) {
|
||||
width: 120px;
|
||||
}
|
||||
@include screen(xs) {
|
||||
width: 100px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@extend %guideline;
|
||||
|
||||
.login-content {
|
||||
@extend %guideline2; //Guide Line2
|
||||
margin: 30px auto 0;
|
||||
.login-input-area {
|
||||
border: 1px solid #cccccc;
|
||||
border-radius: 2px;
|
||||
width: 100%;
|
||||
max-width: 420px;
|
||||
min-width: 150px;
|
||||
height: 60px;
|
||||
background-color: $white;
|
||||
margin-top: 10px;
|
||||
&.login-select-form {
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
padding: 0 16px;
|
||||
@include screen(mid) {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
}
|
||||
@include screen(xs) {
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
}
|
||||
}
|
||||
&:first-of-type {
|
||||
margin-top: 0px;
|
||||
}
|
||||
&.idpass-type {
|
||||
padding-left: 50px;
|
||||
position: relative;
|
||||
&::before {
|
||||
font-family: 'material Icons';
|
||||
font-size: 24px;
|
||||
text-align: center;
|
||||
line-height: 60px;
|
||||
content: 'perm_identity';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 16px;
|
||||
@include screen(mid) {
|
||||
line-height: 50px;
|
||||
}
|
||||
@include screen(xs) {
|
||||
line-height: 42px;
|
||||
}
|
||||
}
|
||||
&.pass-type {
|
||||
&::before {
|
||||
content: 'https';
|
||||
}
|
||||
}
|
||||
.login-idpass-txt {
|
||||
width: 368px;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
font-size: 14px;
|
||||
@include screen(mid) {
|
||||
width: 358 - 60 + px;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
font-size: 14px;
|
||||
}
|
||||
@include screen(xs) {
|
||||
width: 308 - 60 + px;
|
||||
font-size: 14px;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
}
|
||||
input {
|
||||
font-size: 18px;
|
||||
line-height: 58px;
|
||||
margin-top: 0;
|
||||
vertical-align: top;
|
||||
background-color: $white;
|
||||
padding: 0 10px 0 5px;
|
||||
@include screen(mid) {
|
||||
font-size: 16px;
|
||||
line-height: 48px;
|
||||
}
|
||||
@include screen(xs) {
|
||||
font-size: 14px;
|
||||
line-height: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@include screen(mid) {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
.login-input-submit {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
background-color: $black;
|
||||
border-radius: 2px;
|
||||
color: $white;
|
||||
font-size: 20px;
|
||||
@include font-family($font-semibold);
|
||||
border: 0;
|
||||
margin-top: 12px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
@include screen(mid) {
|
||||
margin-top: 8px;
|
||||
font-size: 16px;
|
||||
height: 50px;
|
||||
}
|
||||
@include screen(xs) {
|
||||
font-size: 14px;
|
||||
height: 42px;
|
||||
}
|
||||
}
|
||||
@include screen(mid) {
|
||||
margin-top: 23px;
|
||||
width: 350px;
|
||||
.login-input-area {
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
@include screen(xs) {
|
||||
margin-top: 23px;
|
||||
width: 300px;
|
||||
.login-input-area {
|
||||
height: 42px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.login-company {
|
||||
width: 100%;
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LoginComponent } from './login.component';
|
||||
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ChangeDetectorRef } from '@angular/core';
|
||||
import { I18nService, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
|
||||
import { AuthenticationUiModule } from '../authentication-ui.module';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { Company } from '@ucap/api-external';
|
||||
import { LogService } from '@ucap/ng-logger';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
|
||||
describe('ui::authentication::LoginComponent', () => {
|
||||
let component: LoginComponent;
|
||||
let fixture: ComponentFixture<LoginComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
BrowserAnimationsModule,
|
||||
|
||||
CommonModule,
|
||||
ReactiveFormsModule,
|
||||
|
||||
MatButtonModule,
|
||||
MatCheckboxModule,
|
||||
MatFormFieldModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatSelectModule
|
||||
],
|
||||
providers: [
|
||||
AuthenticationUiModule,
|
||||
// { provide: FormBuilder, useValue: new FormBuilder() },
|
||||
// { provide: ChangeDetectorRef, useValue: ChangeDetectorRef },
|
||||
{ provide: I18nService, useValue: new I18nService(new LogService({})) },
|
||||
{
|
||||
provide: UCAP_I18N_NAMESPACE,
|
||||
useValue: 'authentication'
|
||||
}
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LoginComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
component.companyList = [
|
||||
{ companyName: 'LG CNS', companyCode: 'GUC100' },
|
||||
{ companyName: 'LG UCAP', companyCode: 'GUC101' }
|
||||
] as Company[];
|
||||
component.loginId = 'test';
|
||||
component.companyCode = 'GUC100';
|
||||
fixture.detectChanges();
|
||||
|
||||
component.ngOnInit();
|
||||
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('login', (done) => {
|
||||
component.companyList = [
|
||||
{ companyName: 'LG CNS', companyCode: 'GUC100' },
|
||||
{ companyName: 'LG UCAP', companyCode: 'GUC101' }
|
||||
] as Company[];
|
||||
component.loginId = 'test';
|
||||
component.companyCode = 'GUC100';
|
||||
|
||||
component.ngOnInit();
|
||||
|
||||
component.login.subscribe((value) => {
|
||||
console.log(value);
|
||||
done();
|
||||
});
|
||||
|
||||
component.onClickLogin();
|
||||
});
|
||||
});
|
|
@ -1,110 +0,0 @@
|
|||
import moment from 'moment';
|
||||
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
Input,
|
||||
Output,
|
||||
EventEmitter,
|
||||
ViewChild,
|
||||
ElementRef,
|
||||
ChangeDetectorRef
|
||||
} from '@angular/core';
|
||||
import {
|
||||
FormGroup,
|
||||
FormBuilder,
|
||||
Validators,
|
||||
FormControl,
|
||||
ValidatorFn
|
||||
} from '@angular/forms';
|
||||
import { Company } from '@ucap/api-external';
|
||||
import { LoginTry } from '@ucap/pi';
|
||||
|
||||
@Component({
|
||||
selector: 'ucap-authentication-login-local',
|
||||
templateUrl: './login.component.html',
|
||||
styleUrls: ['./login.component.scss']
|
||||
})
|
||||
export class LoginComponent implements OnInit {
|
||||
@Input()
|
||||
companyList: Company[];
|
||||
|
||||
@Input()
|
||||
fixedCompanyCode: string;
|
||||
|
||||
@Input()
|
||||
companyCode: string;
|
||||
|
||||
@Input()
|
||||
loginId: string;
|
||||
|
||||
@Input()
|
||||
disable = false;
|
||||
|
||||
@Input()
|
||||
processing = false;
|
||||
|
||||
@Input()
|
||||
loginTry: LoginTry;
|
||||
|
||||
@Output()
|
||||
login = new EventEmitter<{
|
||||
companyCode: string;
|
||||
loginId: string;
|
||||
loginPw: string;
|
||||
notValid: () => void;
|
||||
}>();
|
||||
|
||||
@ViewChild('loginPw', { static: true }) loginPwElementRef: ElementRef;
|
||||
|
||||
loginForm: FormGroup;
|
||||
companyCodeFormControl = new FormControl('');
|
||||
loginIdFormControl = new FormControl('');
|
||||
loginPwFormControl = new FormControl('');
|
||||
loginFailed = false;
|
||||
|
||||
moment = moment;
|
||||
|
||||
constructor(
|
||||
private formBuilder: FormBuilder,
|
||||
private changeDetectorRef: ChangeDetectorRef
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
const companyCodeValidators: ValidatorFn[] = [Validators.required];
|
||||
this.companyCodeFormControl.setValidators(companyCodeValidators);
|
||||
if (!!this.fixedCompanyCode) {
|
||||
this.companyCodeFormControl.setValue(this.fixedCompanyCode);
|
||||
}
|
||||
if (!!this.companyCode) {
|
||||
this.companyCodeFormControl.setValue(this.companyCode);
|
||||
}
|
||||
const loginIdValidators: ValidatorFn[] = [Validators.required];
|
||||
this.loginIdFormControl.setValidators(loginIdValidators);
|
||||
if (!!this.loginId) {
|
||||
this.loginIdFormControl.setValue(this.loginId);
|
||||
}
|
||||
const loginPwValidators: ValidatorFn[] = [Validators.required];
|
||||
this.loginPwFormControl.setValidators(loginPwValidators);
|
||||
|
||||
this.loginForm = this.formBuilder.group({
|
||||
companyCodeFormControl: this.companyCodeFormControl,
|
||||
loginIdFormControl: this.loginIdFormControl,
|
||||
loginPwFormControl: this.loginPwFormControl
|
||||
});
|
||||
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
onClickLogin() {
|
||||
this.login.emit({
|
||||
companyCode: this.loginForm.get('companyCodeFormControl').value,
|
||||
loginId: this.loginForm.get('loginIdFormControl').value,
|
||||
loginPw: this.loginForm.get('loginPwFormControl').value,
|
||||
notValid: () => {
|
||||
this.loginFailed = true;
|
||||
this.loginPwElementRef.nativeElement.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
<div class="login-section-container">
|
||||
<ucap-authentication-login-local
|
||||
[companyList]="companyList"
|
||||
[fixedCompanyCode]="fixedCompanyCode"
|
||||
[companyCode]="userStore?.companyCode"
|
||||
[loginId]="userStore?.loginId"
|
||||
[disable]="disableLoginForm"
|
||||
[processing]="loginProcessing"
|
||||
[loginTry]="loginTry"
|
||||
(login)="onLogin($event)"
|
||||
>
|
||||
<div ucapAuthenticationLogin="header">
|
||||
<div class="logo-img">
|
||||
<img src="../../../assets/images/logo_140.png" alt="" />
|
||||
</div>
|
||||
<h1>Welcome to Messenger</h1>
|
||||
</div>
|
||||
|
||||
<div ucapAuthenticationLogin="footer">
|
||||
<div class="login-chk-area">
|
||||
<div>
|
||||
<mat-checkbox
|
||||
#chkUseRememberMe
|
||||
*ngIf="useRememberMe"
|
||||
aria-label="Remember Me"
|
||||
[checked]="!!userStore && userStore.rememberMe"
|
||||
>
|
||||
{{ 'login.labels.rememberMe' | ucapI18n }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-checkbox
|
||||
#chkUseAutoLogin
|
||||
*ngIf="useAutoLogin"
|
||||
aria-label="Auto Login"
|
||||
[checked]="
|
||||
!!userStore &&
|
||||
!!userStore.settings &&
|
||||
!!userStore.settings.general &&
|
||||
userStore.settings.general.autoLogin
|
||||
"
|
||||
>
|
||||
{{ 'login.labels.autoLogin' | ucapI18n }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
<div class="login-pass-info">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="">{{ 'login.labels.forgotPassword' | ucapI18n }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="" class="fir-pass">{{
|
||||
'login.labels.resetPassword' | ucapI18n
|
||||
}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="login-button-area">
|
||||
<button type="button">
|
||||
{{ 'login.labels.notesOnUse' | ucapI18n }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ucap-authentication-login-local>
|
||||
</div>
|
|
@ -1,117 +0,0 @@
|
|||
@import '../../../../assets/scss/components';
|
||||
|
||||
h1 {
|
||||
@include font-family($font-light);
|
||||
font-size: 24px;
|
||||
text-align: center;
|
||||
color: $txt-color01;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
@include screen(mid) {
|
||||
font-size: 19px;
|
||||
}
|
||||
@include screen(xs) {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.login-section-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.login-chk-area {
|
||||
margin-top: 6px;
|
||||
font-size: 13px;
|
||||
text-align: left;
|
||||
@include screen(xs) {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.login-pass-info {
|
||||
overflow: hidden;
|
||||
margin-top: 83px;
|
||||
ul {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
li {
|
||||
height: 24px;
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0 12% 0 8%;
|
||||
&::before {
|
||||
content: '';
|
||||
height: 11px;
|
||||
width: 1px;
|
||||
display: flex;
|
||||
background-color: $gray-re4a;
|
||||
position: absolute;
|
||||
top: 6.5px;
|
||||
left: 0;
|
||||
}
|
||||
&:first-child {
|
||||
padding-left: 0;
|
||||
&::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
&:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
a {
|
||||
line-height: 24px;
|
||||
font-size: 12px;
|
||||
color: $gray-re4a;
|
||||
padding-left: 34px;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
&::before {
|
||||
font-family: 'material Icons';
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
content: 'search';
|
||||
color: $white;
|
||||
display: block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
background-color: $black;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
&.fir-pass {
|
||||
&::before {
|
||||
content: 'sync';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.login-button-area {
|
||||
margin-top: 14px;
|
||||
@include screen(xs) {
|
||||
margin-top: 20px;
|
||||
}
|
||||
button {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 46px;
|
||||
border-radius: 4px;
|
||||
background-color: #e0e3e7;
|
||||
font-size: 12px;
|
||||
color: $gray-re4a;
|
||||
cursor: pointer;
|
||||
@include screen(mid) {
|
||||
height: 38px;
|
||||
}
|
||||
@include screen(xs) {
|
||||
height: 34px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,174 +0,0 @@
|
|||
import { Subject } from 'rxjs';
|
||||
import { take, takeUntil } from 'rxjs/operators';
|
||||
|
||||
import { Component, OnInit, OnDestroy, Input, ViewChild } from '@angular/core';
|
||||
|
||||
import { MatCheckbox } from '@angular/material/checkbox';
|
||||
|
||||
import { Store, select } from '@ngrx/store';
|
||||
|
||||
import { Company } from '@ucap/api-external';
|
||||
import { LoginTry } from '@ucap/pi';
|
||||
|
||||
import { LogService } from '@ucap/ng-logger';
|
||||
import { I18nService } from '@ucap/ng-i18n';
|
||||
import { SessionStorageService } from '@ucap/ng-web-storage';
|
||||
import { PiService } from '@ucap/ng-pi';
|
||||
import { ProtocolService } from '@ucap/ng-protocol';
|
||||
import { CompanyActions, CompanySelector } from '@ucap/ng-store-organization';
|
||||
import { LoginActions } from '@ucap/ng-store-authentication';
|
||||
|
||||
import { UserStore } from '@app/models/user-store';
|
||||
import { LoginSession } from '@app/models/login-session';
|
||||
import { AppKey } from '@app/types/app-key.type';
|
||||
import { AppAuthenticationService } from '@app/services/app-authentication.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-sections-account-login',
|
||||
templateUrl: './login.section.component.html',
|
||||
styleUrls: ['./login.section.component.scss']
|
||||
})
|
||||
export class LoginSectionComponent implements OnInit, OnDestroy {
|
||||
@Input()
|
||||
companyGroupCode: string;
|
||||
|
||||
@Input()
|
||||
fixedCompanyCode: string;
|
||||
|
||||
@Input()
|
||||
userStore: UserStore;
|
||||
|
||||
@Input()
|
||||
useRememberMe: boolean;
|
||||
|
||||
@Input()
|
||||
useAutoLogin: boolean;
|
||||
|
||||
@ViewChild('chkUseRememberMe', { static: false })
|
||||
chkUseRememberMe: MatCheckbox;
|
||||
|
||||
@ViewChild('chkUseAutoLogin', { static: false })
|
||||
chkUseAutoLogin: MatCheckbox;
|
||||
|
||||
loginSession: LoginSession;
|
||||
companyList: Company[];
|
||||
disableLoginForm = false;
|
||||
loginProcessing = false;
|
||||
loginTry: LoginTry;
|
||||
|
||||
private ngOnDestroySubject = new Subject<boolean>();
|
||||
|
||||
constructor(
|
||||
private piService: PiService,
|
||||
private protocolService: ProtocolService,
|
||||
private sessionStorageService: SessionStorageService,
|
||||
private i18nService: I18nService,
|
||||
private store: Store<any>,
|
||||
private appAuthenticationService: AppAuthenticationService,
|
||||
private logService: LogService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.ngOnDestroySubject = new Subject<boolean>();
|
||||
|
||||
this.appAuthenticationService
|
||||
.getLoginSession$()
|
||||
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||
.subscribe((loginSession) => (this.loginSession = loginSession));
|
||||
|
||||
this.sessionStorageService
|
||||
.get$<LoginTry>(AppKey.LoginTry)
|
||||
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||
.subscribe((loginTry) => (this.loginTry = loginTry));
|
||||
|
||||
this.protocolService.disconnect();
|
||||
|
||||
this.store.dispatch(
|
||||
CompanyActions.companies({
|
||||
req: { companyGroupCode: this.companyGroupCode }
|
||||
})
|
||||
);
|
||||
|
||||
this.store
|
||||
.pipe(
|
||||
takeUntil(this.ngOnDestroySubject),
|
||||
select(CompanySelector.companyList)
|
||||
)
|
||||
.subscribe((companyList) => {
|
||||
this.companyList = companyList;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
|
||||
onLogin(event: {
|
||||
companyCode: string;
|
||||
loginId: string;
|
||||
loginPw: string;
|
||||
notValid: () => void;
|
||||
}) {
|
||||
const useRememberMe: boolean = this.chkUseRememberMe.checked;
|
||||
const useAutoLogin: boolean = this.chkUseAutoLogin.checked;
|
||||
|
||||
this.disableLoginForm = true;
|
||||
this.loginProcessing = true;
|
||||
|
||||
this.piService
|
||||
.login2({
|
||||
companyCode: event.companyCode,
|
||||
loginId: event.loginId,
|
||||
loginPw: event.loginPw,
|
||||
deviceType: this.loginSession.deviceType
|
||||
})
|
||||
.pipe(take(1))
|
||||
.subscribe(
|
||||
(res) => {
|
||||
if ('success' !== res.status.toLowerCase()) {
|
||||
this.onWebLoginFailure(event, res.status);
|
||||
return;
|
||||
} else {
|
||||
this.store.dispatch(
|
||||
LoginActions.webLoginSuccess({
|
||||
companyCode: event.companyCode,
|
||||
loginId: event.loginId,
|
||||
loginPw: event.loginPw,
|
||||
autoLogin: useAutoLogin,
|
||||
rememberMe: useRememberMe,
|
||||
login2Response: res
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
this.onWebLoginFailure(event, error);
|
||||
},
|
||||
() => {
|
||||
this.disableLoginForm = false;
|
||||
this.loginProcessing = false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
onClickForgotPassword(lng: string) {
|
||||
this.i18nService.changeLanguage(lng);
|
||||
}
|
||||
|
||||
private onWebLoginFailure(
|
||||
event: {
|
||||
companyCode: string;
|
||||
loginId: string;
|
||||
loginPw: string;
|
||||
notValid: () => void;
|
||||
},
|
||||
error: any
|
||||
) {
|
||||
this.store.dispatch(LoginActions.webLoginFailure({ error }));
|
||||
|
||||
event.notValid();
|
||||
}
|
||||
}
|
|
@ -21,18 +21,28 @@ import { MatTreeModule } from '@angular/material/tree';
|
|||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { MatStepperModule } from '@angular/material/stepper';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||
import { MatRadioModule } from '@angular/material/radio';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
|
||||
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
|
||||
|
||||
import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
|
||||
import { UiModule } from '@ucap/ng-ui';
|
||||
import { ChatUiModule } from '@ucap/ng-ui-chat';
|
||||
import { OrganizationUiModule } from '@ucap/ng-ui-organization';
|
||||
import { AppOrganizationModule } from '@app/ucap/organization/organization.module';
|
||||
|
||||
import { AppChatModule } from '@app/ucap/chat/chat.module';
|
||||
import { AppLayoutsModule } from '@app/layouts/layouts.module';
|
||||
import { AppGroupSectionModule } from '../group/group.section.module';
|
||||
|
||||
import { COMPONENTS } from './components';
|
||||
import { DIALOGS } from './dialogs';
|
||||
import { DRAWERS } from './drawers';
|
||||
import { AppGroupModule } from '@app/ucap/group/group.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -56,6 +66,11 @@ import { DIALOGS } from './dialogs';
|
|||
MatTreeModule,
|
||||
MatTooltipModule,
|
||||
MatStepperModule,
|
||||
MatDividerModule,
|
||||
MatSlideToggleModule,
|
||||
MatRadioModule,
|
||||
MatTabsModule,
|
||||
MatProgressBarModule,
|
||||
|
||||
PerfectScrollbarModule,
|
||||
ScrollingModule,
|
||||
|
@ -65,12 +80,16 @@ import { DIALOGS } from './dialogs';
|
|||
|
||||
AppLayoutsModule,
|
||||
AppGroupSectionModule,
|
||||
AppOrganizationModule,
|
||||
ChatUiModule,
|
||||
AppChatModule
|
||||
OrganizationUiModule,
|
||||
|
||||
AppChatModule,
|
||||
AppGroupModule
|
||||
],
|
||||
exports: [...COMPONENTS, ...DIALOGS],
|
||||
declarations: [...COMPONENTS, ...DIALOGS],
|
||||
entryComponents: [...DIALOGS],
|
||||
exports: [...COMPONENTS, ...DIALOGS, ...DRAWERS],
|
||||
declarations: [...COMPONENTS, ...DIALOGS, ...DRAWERS],
|
||||
entryComponents: [...DIALOGS, ...DRAWERS],
|
||||
providers: [
|
||||
{
|
||||
provide: UCAP_I18N_NAMESPACE,
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
<div class="search-container">
|
||||
<div
|
||||
class="search-container"
|
||||
[ngClass]="!!isChatSearch ? 'chat-search-show' : ''"
|
||||
>
|
||||
<div class="searchbox ucap-mat-input-container">
|
||||
<mat-form-field floatLabel="never" appearance="none" class="search-in-box">
|
||||
<input
|
||||
matInput
|
||||
#searchWordInput
|
||||
type="text"
|
||||
maxlength="20"
|
||||
placeholder="솔루션"
|
||||
/>
|
||||
<mat-label>검색어를 입력하세요</mat-label>
|
||||
<input matInput #searchWordInput type="text" />
|
||||
<button
|
||||
mat-button
|
||||
matSuffix
|
||||
|
@ -17,7 +15,7 @@
|
|||
class="btn-close"
|
||||
color="accent"
|
||||
>
|
||||
<mat-icon>highlight_off</mat-icon>
|
||||
<mat-icon>cancel</mat-icon>
|
||||
</button>
|
||||
</mat-form-field>
|
||||
<button
|
||||
|
@ -38,14 +36,16 @@
|
|||
</button>
|
||||
</div>
|
||||
<div class="search-result">
|
||||
<span class="text"><strong>N</strong>건의 검색결과가 있습니다.</span>
|
||||
<span class="result-count"><strong>1</strong>/N</span>
|
||||
<span class="text"
|
||||
><strong class="txt-accent">N</strong>건의 검색결과가 있습니다.</span
|
||||
>
|
||||
<span class="result-count"><strong class="txt-accent">1</strong>/N</span>
|
||||
<div class="btn-area">
|
||||
<button mat-icon-button class="icon-button">
|
||||
<mat-icon>arrow_downward</mat-icon>
|
||||
<mat-icon inline>arrow_downward</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button class="icon-button">
|
||||
<mat-icon>arrow_upward</mat-icon>
|
||||
<mat-icon inline>arrow_upward</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
@import '~@ucap/lg-scss/mixins';
|
||||
|
||||
.search-container {
|
||||
padding: 0 16px;
|
||||
background-color: $white;
|
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.16);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
//position: relative;
|
||||
//포지션 변경
|
||||
position: absolute;
|
||||
z-index: 5;
|
||||
top: -80px;
|
||||
width: 100%;
|
||||
visibility: hidden;
|
||||
transition: all 0.2s linear;
|
||||
.searchbox {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
width: 100%;
|
||||
max-width: 900px;
|
||||
border: 1px solid $lipstick;
|
||||
background-color: $white;
|
||||
.search-in-box {
|
||||
@include ucapMatFormField(0, 0, 100%, auto, auto, 38px, 38px);
|
||||
padding-left: 10px;
|
||||
.btn-close {
|
||||
margin-top: 2px;
|
||||
color: #fd78a1 !important;
|
||||
}
|
||||
}
|
||||
.btn-ico-search {
|
||||
@include ucapMatButton(38px, 38px, 0, 20px);
|
||||
}
|
||||
}
|
||||
.search-result {
|
||||
width: 100%;
|
||||
max-width: 900px;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
height: 40px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.text {
|
||||
flex-grow: 1;
|
||||
font-size: 0.857em;
|
||||
color: $gray-re3;
|
||||
strong {
|
||||
font-size: 0.929em;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
.result-count {
|
||||
justify-self: self-end;
|
||||
font-size: 0.929em;
|
||||
}
|
||||
.btn-area {
|
||||
margin-right: -5px;
|
||||
margin-left: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
&:before {
|
||||
content: '';
|
||||
width: 1px;
|
||||
height: 10px;
|
||||
display: inline-block;
|
||||
background-color: $gray-rec;
|
||||
}
|
||||
}
|
||||
}
|
||||
//on
|
||||
&.chat-search-show {
|
||||
visibility: visible;
|
||||
transition: all 0.2s linear;
|
||||
top: 0;
|
||||
}
|
||||
}
|
|
@ -5,8 +5,10 @@ import {
|
|||
Output,
|
||||
EventEmitter,
|
||||
ChangeDetectorRef,
|
||||
ChangeDetectionStrategy
|
||||
ChangeDetectionStrategy,
|
||||
Input
|
||||
} from '@angular/core';
|
||||
import { SearchInfo } from '@app/pages/chat/models/search-info';
|
||||
|
||||
@Component({
|
||||
selector: 'app-sections-chat-chat-search',
|
||||
|
@ -15,20 +17,19 @@ import {
|
|||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ChatSearchSectionComponent implements OnInit, OnDestroy {
|
||||
searchObj: any = {
|
||||
isSearch: false,
|
||||
searchWord: ''
|
||||
};
|
||||
@Input()
|
||||
isChatSearch = false;
|
||||
|
||||
@Output()
|
||||
chatSearch = new EventEmitter<{
|
||||
isSearch: false;
|
||||
searchWord: '';
|
||||
}>();
|
||||
chatSearch = new EventEmitter<SearchInfo>();
|
||||
|
||||
@Output()
|
||||
closeChatSearch = new EventEmitter<void>();
|
||||
|
||||
searchObj: SearchInfo = {
|
||||
isShowSearch: false,
|
||||
searchWord: ''
|
||||
};
|
||||
constructor(private changeDetectorRef: ChangeDetectorRef) {}
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
@ -39,9 +40,9 @@ export class ChatSearchSectionComponent implements OnInit, OnDestroy {
|
|||
alert(searchWord);
|
||||
|
||||
this.searchObj = {
|
||||
isSearch: true,
|
||||
isShowSearch: true,
|
||||
searchWord
|
||||
};
|
||||
} as SearchInfo;
|
||||
this.chatSearch.emit(this.searchObj);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
<div class="chat-list-item">
|
||||
<div class="profileImage">
|
||||
<img
|
||||
class="thumbnail"
|
||||
ucapImage
|
||||
[base]="profileImageRoot"
|
||||
[path]="profileImage"
|
||||
[default]="defaultProfileImage"
|
||||
/>
|
||||
</div>
|
||||
<div class="roomName">
|
||||
{{ roomName }}
|
||||
<strong *ngIf="roomInfo.roomType === RoomType.Multi"
|
||||
>({{ roomInfo.joinUserCount }})</strong
|
||||
>
|
||||
</div>
|
||||
<div class="lastMessage">{{ roomInfo.finalEventMessage }}</div>
|
||||
<div class="date">{{ roomInfo.finalEventDate | ucapDate: 'LT' }}</div>
|
||||
|
||||
<span
|
||||
class="noti-sum"
|
||||
*ngIf="!!roomInfo.noReadCnt && roomInfo.noReadCnt > 0"
|
||||
[matBadgeHidden]="roomInfo.noReadCnt === 0"
|
||||
[matBadge]="roomInfo.noReadCnt"
|
||||
matBadgeOverlap="true"
|
||||
matBadgeColor="accent"
|
||||
matBadgePosition="below after"
|
||||
></span>
|
||||
|
||||
<button
|
||||
mat-icon-button
|
||||
aria-label="room-menu"
|
||||
*ngIf="!checkable"
|
||||
[matMenuTriggerFor]="roomMenu"
|
||||
(click)="$event.stopPropagation()"
|
||||
>
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-checkbox
|
||||
*ngIf="checkable"
|
||||
#checkbox
|
||||
[checked]="checked"
|
||||
(change)="onToggleItem(checkbox.checked)"
|
||||
(click)="$event.stopPropagation()"
|
||||
class="group-check"
|
||||
>
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
|
||||
<mat-menu #roomMenu="matMenu">
|
||||
<span class="manu-title">{{ roomName }}</span>
|
||||
<button mat-menu-item>
|
||||
<mat-icon>dialpad</mat-icon>
|
||||
<span>대화방 열기</span>
|
||||
</button>
|
||||
<button mat-menu-item>
|
||||
<mat-icon>dialpad</mat-icon>
|
||||
<span>알림 해제</span>
|
||||
</button>
|
||||
<button mat-menu-item>
|
||||
<mat-icon>dialpad</mat-icon>
|
||||
<span>대화방 나가기</span>
|
||||
</button>
|
||||
</mat-menu>
|
|
@ -1,25 +0,0 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ChatListItemComponent } from './chat-list-item.component';
|
||||
|
||||
describe('ChatListItemComponent', () => {
|
||||
let component: ChatListItemComponent;
|
||||
let fixture: ComponentFixture<ChatListItemComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ChatListItemComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ChatListItemComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -1,63 +0,0 @@
|
|||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
ChangeDetectionStrategy,
|
||||
Input,
|
||||
OnDestroy,
|
||||
EventEmitter,
|
||||
Output,
|
||||
ChangeDetectorRef
|
||||
} from '@angular/core';
|
||||
import { RoomInfo, RoomType } from '@ucap/protocol-room';
|
||||
|
||||
@Component({
|
||||
selector: 'app-chat-list-item',
|
||||
templateUrl: './chat-list-item.component.html',
|
||||
styleUrls: ['./chat-list-item.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ChatListItemComponent implements OnInit, OnDestroy {
|
||||
@Input()
|
||||
roomInfo: RoomInfo;
|
||||
|
||||
@Input()
|
||||
profileImageRoot: string;
|
||||
|
||||
@Input()
|
||||
defaultProfileImage: string;
|
||||
|
||||
@Input()
|
||||
profileImage: string;
|
||||
|
||||
@Input()
|
||||
roomName: string;
|
||||
|
||||
@Input()
|
||||
checkable = false;
|
||||
|
||||
@Input()
|
||||
checked = false;
|
||||
|
||||
@Output()
|
||||
toggleItem = new EventEmitter<{
|
||||
checked: boolean;
|
||||
roomInfo: RoomInfo;
|
||||
}>();
|
||||
|
||||
RoomType = RoomType;
|
||||
|
||||
constructor(private changeDetectorRef: ChangeDetectorRef) {}
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
||||
ngOnDestroy(): void {}
|
||||
|
||||
onToggleItem(value: boolean): void {
|
||||
this.toggleItem.emit({
|
||||
checked: value,
|
||||
roomInfo: this.roomInfo
|
||||
});
|
||||
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
<div class="ucap-group-expansion-container" fxFlexFill>
|
||||
<cdk-virtual-scroll-viewport #cvsvList perfectScrollbar fxFlexFill>
|
||||
<ng-container
|
||||
*cdkVirtualFor="
|
||||
let node of dataSource.expandedData$;
|
||||
templateCacheSize: 0
|
||||
"
|
||||
></ng-container>
|
||||
|
||||
<mat-tree #treeList [dataSource]="dataSource" [treeControl]="treeControl">
|
||||
<mat-tree-node
|
||||
*matTreeNodeDef="let node"
|
||||
[attr.node-type]="node?.nodeType"
|
||||
matRipple
|
||||
>
|
||||
<li>
|
||||
<div>
|
||||
<ng-container
|
||||
[ngTemplateOutlet]="nodeTemplate"
|
||||
[ngTemplateOutletContext]="{ $implicit: node?.node }"
|
||||
></ng-container>
|
||||
</div>
|
||||
</li>
|
||||
</mat-tree-node>
|
||||
|
||||
<mat-tree-node
|
||||
*matTreeNodeDef="let node; when: isHeader"
|
||||
class="tree-node-frame ucap-clickable"
|
||||
[attr.node-type]="node?.nodeType"
|
||||
matRipple
|
||||
>
|
||||
<li class="tree-node-header" matTreeNodeToggle>
|
||||
<div class="path">
|
||||
<button
|
||||
mat-icon-button
|
||||
[attr.aria-label]="'toggle '"
|
||||
class="btn-toggle"
|
||||
>
|
||||
<mat-icon class="mat-icon-rtl-mirror">
|
||||
{{
|
||||
treeControl.isExpanded(node) ? 'expand_less' : 'expand_more'
|
||||
}}
|
||||
</mat-icon>
|
||||
</button>
|
||||
<div class="group-info">
|
||||
<ng-container
|
||||
[ngTemplateOutlet]="headerTemplate"
|
||||
[ngTemplateOutletContext]="{ $implicit: node?.node }"
|
||||
>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
<ul [class.group-tree-node-invisible]="!treeControl.isExpanded(node)">
|
||||
<div *ngIf="treeControl.isExpanded(node)" class="boxnone">
|
||||
<div class="vertical-line"></div>
|
||||
<ng-container matTreeNodeOutlet></ng-container>
|
||||
</div>
|
||||
</ul>
|
||||
</li>
|
||||
</mat-tree-node>
|
||||
</mat-tree>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
</div>
|
|
@ -1,177 +0,0 @@
|
|||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
Input,
|
||||
ViewChild,
|
||||
ContentChild,
|
||||
TemplateRef,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Directive
|
||||
} from '@angular/core';
|
||||
|
||||
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||
import { FlatTreeControl } from '@angular/cdk/tree';
|
||||
|
||||
import { MatTreeFlattener, MatTree } from '@angular/material/tree';
|
||||
|
||||
import { VirtualScrollTreeFlatDataSource } from '@ucap/ng-ui';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
|
||||
import { RoomInfo } from '@ucap/protocol-room';
|
||||
|
||||
export interface ChatGroupNode {
|
||||
nodeType: string;
|
||||
roomInfo?: RoomInfo;
|
||||
children?: ChatGroupNode[];
|
||||
}
|
||||
|
||||
export interface FlatNode {
|
||||
expandable: boolean;
|
||||
level: number;
|
||||
node: ChatGroupNode;
|
||||
}
|
||||
|
||||
@Directive({
|
||||
selector: '[ucapChatExpansionNode]'
|
||||
})
|
||||
export class ExpansionNodeDirective {}
|
||||
|
||||
@Directive({
|
||||
selector: '[ucapChatExpansionHeader]'
|
||||
})
|
||||
export class ExpansionHeaderDirective {}
|
||||
|
||||
@Component({
|
||||
selector: 'ucap-chat-expansion',
|
||||
templateUrl: './expansion.component.html',
|
||||
styleUrls: ['./expansion.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ExpansionComponent implements OnInit, OnDestroy {
|
||||
@Input()
|
||||
set chatGroup(list: { division: string; roomList: RoomInfo[] }[]) {
|
||||
if (!list || 0 === list.length) {
|
||||
} else {
|
||||
list.sort((a, b) =>
|
||||
a.division < b.division ? 1 : a.division > b.division ? -1 : 0
|
||||
);
|
||||
|
||||
for (const item of list) {
|
||||
const nodeType = item.division;
|
||||
const node: ChatGroupNode = {
|
||||
nodeType,
|
||||
children: []
|
||||
};
|
||||
|
||||
item.roomList.sort((a, b) =>
|
||||
a.finalEventDate < b.finalEventDate
|
||||
? 1
|
||||
: a.finalEventDate > b.finalEventDate
|
||||
? -1
|
||||
: 0
|
||||
);
|
||||
|
||||
item.roomList.forEach((roomInfo) => {
|
||||
node.children.push({
|
||||
nodeType,
|
||||
roomInfo
|
||||
});
|
||||
});
|
||||
|
||||
if (!!this.nodeMap.get(item.division)) {
|
||||
this.nodeMap[item.division].push(node);
|
||||
} else {
|
||||
this.nodeMap.set(item.division, [node]);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.refreshNodes();
|
||||
}
|
||||
|
||||
@ViewChild('treeList', { static: false })
|
||||
treeList: MatTree<FlatNode>;
|
||||
|
||||
@ViewChild('cvsvList', { static: false })
|
||||
cvsvList: CdkVirtualScrollViewport;
|
||||
|
||||
@ViewChild(PerfectScrollbarDirective, { static: false })
|
||||
psDirectiveRef?: PerfectScrollbarDirective;
|
||||
|
||||
@ContentChild(ExpansionNodeDirective, {
|
||||
read: TemplateRef,
|
||||
static: false
|
||||
})
|
||||
nodeTemplate: TemplateRef<ExpansionNodeDirective>;
|
||||
|
||||
@ContentChild(ExpansionHeaderDirective, {
|
||||
read: TemplateRef,
|
||||
static: false
|
||||
})
|
||||
headerTemplate: TemplateRef<ExpansionHeaderDirective>;
|
||||
|
||||
treeControl: FlatTreeControl<FlatNode>;
|
||||
treeFlattener: MatTreeFlattener<ChatGroupNode, FlatNode>;
|
||||
dataSource: VirtualScrollTreeFlatDataSource<ChatGroupNode, FlatNode>;
|
||||
|
||||
private nodeMap: Map<string, ChatGroupNode[]> = new Map();
|
||||
// tslint:disable-next-line: variable-name
|
||||
private _ngOnDestroySubject: Subject<void>;
|
||||
|
||||
constructor(private changeDetectorRef: ChangeDetectorRef) {
|
||||
this.treeControl = new FlatTreeControl<FlatNode>(
|
||||
(node) => node.level,
|
||||
(node) => node.expandable
|
||||
);
|
||||
|
||||
this.treeFlattener = new MatTreeFlattener<ChatGroupNode, FlatNode>(
|
||||
(node: ChatGroupNode, level: number) => {
|
||||
return {
|
||||
expandable: !!node.children && node.children.length > 0,
|
||||
level,
|
||||
nodeType: node.nodeType,
|
||||
node
|
||||
};
|
||||
},
|
||||
(node) => node.level,
|
||||
(node) => node.expandable,
|
||||
(node) => node.children
|
||||
);
|
||||
|
||||
this.dataSource = new VirtualScrollTreeFlatDataSource<
|
||||
ChatGroupNode,
|
||||
FlatNode
|
||||
>(this.treeControl, this.treeFlattener);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this._ngOnDestroySubject = new Subject();
|
||||
|
||||
this.dataSource.cdkVirtualScrollViewport = this.cvsvList;
|
||||
this.treeControl.expansionModel.changed
|
||||
.pipe(takeUntil(this._ngOnDestroySubject))
|
||||
.subscribe(() => {
|
||||
this.cvsvList.checkViewportSize();
|
||||
this.psDirectiveRef.update();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this._ngOnDestroySubject) {
|
||||
this._ngOnDestroySubject.next();
|
||||
this._ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
|
||||
isHeader = (_: number, node: FlatNode) => 0 === node.level;
|
||||
|
||||
private refreshNodes() {
|
||||
const rootNode: ChatGroupNode[] = [];
|
||||
this.nodeMap.forEach((node) => rootNode.push(...node));
|
||||
|
||||
this.dataSource.data = rootNode;
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
import { ChatListItemComponent } from './chat-list-item.component';
|
||||
import {
|
||||
ExpansionComponent,
|
||||
ExpansionNodeDirective,
|
||||
ExpansionHeaderDirective
|
||||
} from './expansion.component';
|
||||
|
||||
export const COMPONENTS = [ChatListItemComponent, ExpansionComponent];
|
||||
|
||||
export const DIRECTIVES = [ExpansionNodeDirective, ExpansionHeaderDirective];
|
|
@ -1,42 +1,63 @@
|
|||
<ng-container [ngSwitch]="selectorType">
|
||||
<div class="ucap-chat-input-container">
|
||||
<ng-container [ngSwitch]="selectorType">
|
||||
<app-chat-selector-sticker
|
||||
*ngSwitchCase="SelectorType.STICKER"
|
||||
(selectedSticker)="onSelectedSticker($event)"
|
||||
(closeSticker)="selectorType = SelectorType.EMPTY"
|
||||
(closed)="selectorType = SelectorType.EMPTY"
|
||||
>
|
||||
</app-chat-selector-sticker>
|
||||
|
||||
<app-chat-selector-translation
|
||||
*ngSwitchCase="SelectorType.TRANSLATION"
|
||||
(closed)="
|
||||
selectorType = SelectorType.EMPTY; translationPreviewInfo = null
|
||||
"
|
||||
[destLocale]="destLocale"
|
||||
[simpleView]="translationSimpleview"
|
||||
[preView]="translationPreview"
|
||||
[translationPreviewInfo]="translationPreviewInfo"
|
||||
(changeTranslationSimpleview)="onChangeTranslationSimpleView($event)"
|
||||
(changeTranslationPreview)="onChangeTranslationPreView($event)"
|
||||
(changeDestLocale)="onChangeDestLocale($event)"
|
||||
(sendTranslationMessage)="onSendTranslationMessage($event)"
|
||||
(translationMessage)="send()"
|
||||
></app-chat-selector-translation>
|
||||
|
||||
<app-chat-selector-file-upload
|
||||
#fileUploadSelector
|
||||
*ngSwitchCase="SelectorType.FILEUPLOAD"
|
||||
(closed)="selectorType = SelectorType.EMPTY"
|
||||
>
|
||||
</app-chat-selector-file-upload>
|
||||
|
||||
<app-chat-selector-email-send
|
||||
*ngSwitchCase="SelectorType.EMAILSENDER"
|
||||
(sendEventEmail)="onSendEventEmail($event)"
|
||||
(closed)="selectorType = SelectorType.EMPTY"
|
||||
></app-chat-selector-email-send>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<div class="chat-form-area">
|
||||
<div class="chat-form-area ucap-mat-input-container">
|
||||
<mat-form-field
|
||||
class="message-text"
|
||||
fxFlex
|
||||
floatLabel="never"
|
||||
appearance="standard"
|
||||
appearance="none"
|
||||
>
|
||||
<textarea
|
||||
<mat-label>{{ 'chat:label.inputChatMessage' | ucapI18n }}</mat-label>
|
||||
<!-- <textarea
|
||||
matInput
|
||||
#messageInput
|
||||
placeholder=""
|
||||
name="message"
|
||||
[matTextareaAutosize]="true"
|
||||
[matAutosizeMaxRows]="20"
|
||||
(keydown)="onKeydown($event)"
|
||||
></textarea>
|
||||
(keydown.enter)="onKeydown($event)"
|
||||
></textarea> -->
|
||||
<input
|
||||
matInput
|
||||
#messageInput
|
||||
name="message"
|
||||
(keydown.enter)="onKeydown($event)"
|
||||
/>
|
||||
</mat-form-field>
|
||||
|
||||
<input
|
||||
|
@ -46,78 +67,93 @@
|
|||
multiple
|
||||
(change)="onChangeFileInput()"
|
||||
/>
|
||||
</div>
|
||||
<div class="button-area">
|
||||
</div>
|
||||
|
||||
<div class="button-area">
|
||||
<button
|
||||
mat-button
|
||||
mat-icon-button
|
||||
aria-label="attachFile"
|
||||
matTooltipPosition="above"
|
||||
matTooltip="{{ 'label.attachFile' | ucapI18n }}"
|
||||
(click)="clearSelector(); fileInput.click()"
|
||||
class="btn-icon-chat"
|
||||
(click)="clearSelector(); fileInput.accept = '*.*'; fileInput.click()"
|
||||
>
|
||||
첨부파일
|
||||
<mat-icon>attach_file</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
mat-button
|
||||
mat-icon-button
|
||||
aria-label="attachImage"
|
||||
matTooltipPosition="above"
|
||||
matTooltip="{{ 'label.attachImage' | ucapI18n }}"
|
||||
(click)="onOpenSelector(SelectorType.EMPTY)"
|
||||
class="btn-icon-chat"
|
||||
(click)="
|
||||
clearSelector(); fileInput.accept = 'image/*,video/*'; fileInput.click()
|
||||
"
|
||||
>
|
||||
이미지
|
||||
<mat-icon class="material-icons-outlined">image</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
mat-button
|
||||
<!-- <button
|
||||
mat-icon-button
|
||||
*ngIf="!!authRes && !!authRes.useCapturePcScreen"
|
||||
aria-label="screenshot"
|
||||
matTooltipPosition="above"
|
||||
matTooltip="{{ 'label.screenshot' | ucapI18n }}"
|
||||
class="btn-icon-chat"
|
||||
(click)="onOpenSelector(SelectorType.EMPTY)"
|
||||
>
|
||||
캡쳐 화면 전송
|
||||
</button>
|
||||
<mat-icon>filter_center_focus</mat-icon>
|
||||
</button> -->
|
||||
<button
|
||||
mat-button
|
||||
mat-icon-button
|
||||
aria-label="imoticon"
|
||||
matTooltipPosition="above"
|
||||
matTooltip="{{ 'label.imoticon' | ucapI18n }}"
|
||||
class="btn-icon-chat"
|
||||
(click)="onOpenSelector(SelectorType.STICKER)"
|
||||
>
|
||||
이모티콘
|
||||
<mat-icon>sentiment_satisfied</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
mat-button
|
||||
mat-icon-button
|
||||
*ngIf="!!authRes && !!authRes.canSendEmail"
|
||||
aria-label="emailSend"
|
||||
matTooltipPosition="above"
|
||||
matTooltip="{{ 'label.emailSend' | ucapI18n }}"
|
||||
class="btn-icon-chat"
|
||||
(click)="onOpenSelector(SelectorType.EMAILSENDER)"
|
||||
>
|
||||
대화내용 메일 전송
|
||||
<mat-icon class="material-icons-outlined">drafts</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
mat-button
|
||||
mat-icon-button
|
||||
*ngIf="!!authRes && !!authRes.canTranslation"
|
||||
aria-label="translation"
|
||||
matTooltipPosition="above"
|
||||
matTooltip="{{ 'label.translation' | ucapI18n }}"
|
||||
class="btn-icon-chat"
|
||||
(click)="onOpenSelector(SelectorType.TRANSLATION)"
|
||||
>
|
||||
대화내용 번역
|
||||
<mat-icon>translate</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
mat-button
|
||||
<!-- <button
|
||||
mat-icon-button
|
||||
*ngIf="!!authRes && !!authRes.useGams"
|
||||
aria-label="gams"
|
||||
matTooltipPosition="above"
|
||||
matTooltip="{{ 'label.gams' | ucapI18n }}"
|
||||
class="btn-icon-chat btn-icon-chat-gams"
|
||||
(click)="onOpenSelector(SelectorType.EMPTY)"
|
||||
>
|
||||
+GAMS
|
||||
</button>
|
||||
<strong>+GAMS</strong>
|
||||
</button> -->
|
||||
<button
|
||||
mat-fab
|
||||
color="accent"
|
||||
mat-mini-fab
|
||||
class="btn-message-send"
|
||||
aria-label="send"
|
||||
matTooltip="{{ 'label.send' | ucapI18n }}"
|
||||
(click)="send()"
|
||||
>
|
||||
<mat-icon>send</mat-icon>
|
||||
<mat-icon>arrow_upward</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
@import '~@ucap/lg-scss/mixins';
|
||||
|
||||
.ucap-chat-input-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
border-top: 1px solid #ccc;
|
||||
background-color: $white;
|
||||
.chat-form-area {
|
||||
max-height: 100%;
|
||||
min-height: 20px;
|
||||
background-color: $white;
|
||||
padding-left: 30px;
|
||||
font-size: 0.929em;
|
||||
margin: 8px 0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
flex: 1 1 auto;
|
||||
@include screen(xs) {
|
||||
padding-left: 16px;
|
||||
}
|
||||
textarea {
|
||||
min-height: 22px;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
.button-area {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
height: 34px;
|
||||
background-color: rgba(0, 0, 0, 0.02);
|
||||
border-top: 1px solid #eeeeee;
|
||||
padding: 0 16px;
|
||||
flex: 0 0 0;
|
||||
@include screen(xs) {
|
||||
padding: 0;
|
||||
}
|
||||
.btn-icon-chat {
|
||||
@include ucapMatButton(34px, 34px, 0, 34px);
|
||||
&.btn-icon-chat-gams {
|
||||
width: 60px;
|
||||
@include font-family($font-semibold);
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
.btn-message-send {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
box-shadow: 0px 2px 2px 0 rgba(0, 0, 0, 0.3);
|
||||
border: solid 3px #ffffff;
|
||||
background-image: linear-gradient(225deg, #fadfaa 5%, #f92465 95%);
|
||||
font-size: 1.714em;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
bottom: 25px;
|
||||
right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { Subject, of, Observable, forkJoin } from 'rxjs';
|
||||
import { Subject, of, merge } from 'rxjs';
|
||||
import { takeUntil, map, catchError, take } from 'rxjs/operators';
|
||||
|
||||
import {
|
||||
|
@ -9,51 +9,68 @@ import {
|
|||
ChangeDetectorRef,
|
||||
Input,
|
||||
ViewChild,
|
||||
ElementRef
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
Output
|
||||
} from '@angular/core';
|
||||
import { Store, select } from '@ngrx/store';
|
||||
import { Dictionary } from '@ngrx/entity';
|
||||
|
||||
import { Store, select } from '@ngrx/store';
|
||||
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
|
||||
import { StickerFilesInfo } from '@ucap/ng-core';
|
||||
import { StatusCode, FileUploadItem } from '@ucap/api';
|
||||
import {
|
||||
TranslationSaveResponse,
|
||||
TranslationSaveRequest
|
||||
} from '@ucap/api-common';
|
||||
import { VersionInfo2Response } from '@ucap/api-public';
|
||||
import {
|
||||
SendEventMailType,
|
||||
SendEventEmailRequest,
|
||||
SendEventEmailResponse,
|
||||
StatusCode as PiStatusCode
|
||||
} from '@ucap/pi';
|
||||
import { RoomInfo } from '@ucap/protocol-room';
|
||||
import {
|
||||
SendRequest as SendEventRequest,
|
||||
EventType
|
||||
EventType,
|
||||
MassTranslationEventJson,
|
||||
TranslationEventJson
|
||||
} from '@ucap/protocol-event';
|
||||
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||
import { AuthResponse } from '@ucap/protocol-query';
|
||||
import { User } from '@ucap/protocol-info';
|
||||
|
||||
import { ChattingActions } from '@ucap/ng-store-chat';
|
||||
import { I18nService } from '@ucap/ng-i18n';
|
||||
import { LogService } from '@ucap/ng-logger';
|
||||
import { CommonApiService } from '@ucap/ng-api-common';
|
||||
import { PiService } from '@ucap/ng-pi';
|
||||
|
||||
import { UserSelector } from '@ucap/ng-store-organization';
|
||||
import {
|
||||
LoginSelector,
|
||||
ConfigurationSelector
|
||||
ConfigurationSelector,
|
||||
AuthorizationSelector
|
||||
} from '@ucap/ng-store-authentication';
|
||||
import { StickerFilesInfo, KEY_STICKER_HISTORY } from '@ucap/ng-core';
|
||||
import { ChattingSelector, RoomSelector } from '@ucap/ng-store-chat';
|
||||
|
||||
import {
|
||||
AlertDialogComponent,
|
||||
AlertDialogData,
|
||||
AlertDialogResult
|
||||
AlertDialogResult,
|
||||
ConfirmDialogComponent,
|
||||
ConfirmDialogData,
|
||||
ConfirmDialogResult
|
||||
} from '@ucap/ng-ui';
|
||||
import { I18nService } from '@ucap/ng-i18n';
|
||||
import { LogService } from '@ucap/ng-logger';
|
||||
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import {
|
||||
TranslationSaveResponse,
|
||||
MassTalkSaveRequest,
|
||||
FileTalkSaveResponse,
|
||||
FileTalkSaveRequest
|
||||
} from '@ucap/api-common';
|
||||
import { environment } from '@environments';
|
||||
import { LocalStorageService } from '@ucap/ng-web-storage';
|
||||
import { CommonApiService } from '@ucap/ng-api-common';
|
||||
import { LoginSession } from '@app/models/login-session';
|
||||
import { AppAuthenticationService } from '@app/services/app-authentication.service';
|
||||
import { StatusCode, FileUploadItem } from '@ucap/api';
|
||||
import { AppFileService } from '@app/services/app-file.service';
|
||||
import { VersionInfo2Response } from '@ucap/api-public';
|
||||
import { FileUploadSelectorComponent } from '@app/ucap/chat/components/file-upload.selector.component';
|
||||
import { FileUtil } from '@ucap/core';
|
||||
import { AppChatService } from '@app/services/app-chat.service';
|
||||
|
||||
import { environment } from '@environments';
|
||||
|
||||
export enum SelectorType {
|
||||
EMPTY = '',
|
||||
STICKER = 'STICKER',
|
||||
|
@ -69,9 +86,17 @@ export enum SelectorType {
|
|||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class FormSectionComponent implements OnInit, OnDestroy {
|
||||
private roomIdSubject = new Subject<string>();
|
||||
private ngOnDestroySubject: Subject<void> = new Subject();
|
||||
@Input()
|
||||
set roomId(roomId: string) {
|
||||
if (!!roomId && this.roomId !== roomId) {
|
||||
this._roomId = roomId;
|
||||
|
||||
this.roomIdSubject.next(roomId);
|
||||
|
||||
this.initializeRoomData();
|
||||
}
|
||||
}
|
||||
get roomId(): string {
|
||||
return this._roomId;
|
||||
|
@ -79,9 +104,17 @@ export class FormSectionComponent implements OnInit, OnDestroy {
|
|||
// tslint:disable-next-line: variable-name
|
||||
_roomId: string;
|
||||
|
||||
@Output()
|
||||
changeTranslationSimpleview = new EventEmitter<boolean>();
|
||||
|
||||
@Output()
|
||||
eventSendTrigger = new EventEmitter<any>();
|
||||
|
||||
versionInfo2Res: VersionInfo2Response;
|
||||
loginSession: LoginSession;
|
||||
loginRes: LoginResponse;
|
||||
user: User;
|
||||
authRes: AuthResponse;
|
||||
|
||||
currentRoomInfo: RoomInfo;
|
||||
|
||||
|
@ -91,8 +124,9 @@ export class FormSectionComponent implements OnInit, OnDestroy {
|
|||
selectedSticker: StickerFilesInfo;
|
||||
|
||||
/** About Translation */
|
||||
translationSimpleview = false;
|
||||
translationPreview = false;
|
||||
isTranslationProcess = false;
|
||||
translationSimpleview = true;
|
||||
translationPreview = true;
|
||||
destLocale = 'en'; // default English :: en
|
||||
translationPreviewInfo: {
|
||||
previewInfo: TranslationSaveResponse | null;
|
||||
|
@ -108,14 +142,13 @@ export class FormSectionComponent implements OnInit, OnDestroy {
|
|||
|
||||
SelectorType = SelectorType;
|
||||
|
||||
private ngOnDestroySubject: Subject<boolean>;
|
||||
constructor(
|
||||
private piService: PiService,
|
||||
private appFileService: AppFileService,
|
||||
private appChatService: AppChatService,
|
||||
private store: Store<any>,
|
||||
private i18nService: I18nService,
|
||||
private dialog: MatDialog,
|
||||
private localStorageService: LocalStorageService,
|
||||
private logService: LogService,
|
||||
private appAuthenticationService: AppAuthenticationService,
|
||||
private commonApiService: CommonApiService,
|
||||
|
@ -123,8 +156,6 @@ export class FormSectionComponent implements OnInit, OnDestroy {
|
|||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.ngOnDestroySubject = new Subject<boolean>();
|
||||
|
||||
this.store
|
||||
.pipe(
|
||||
takeUntil(this.ngOnDestroySubject),
|
||||
|
@ -133,57 +164,84 @@ export class FormSectionComponent implements OnInit, OnDestroy {
|
|||
.subscribe((versionInfo2Res) => {
|
||||
this.versionInfo2Res = versionInfo2Res;
|
||||
});
|
||||
|
||||
this.store
|
||||
.pipe(takeUntil(this.ngOnDestroySubject), select(UserSelector.user))
|
||||
.subscribe((user) => {
|
||||
this.user = user;
|
||||
});
|
||||
|
||||
this.store
|
||||
.pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
|
||||
.subscribe((loginRes) => {
|
||||
this.loginRes = loginRes;
|
||||
});
|
||||
this.appAuthenticationService
|
||||
.getLoginSession$()
|
||||
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||
.subscribe((loginSession) => (this.loginSession = loginSession));
|
||||
|
||||
this.store
|
||||
.pipe(
|
||||
takeUntil(this.ngOnDestroySubject),
|
||||
select(
|
||||
(state: any) => state.chat.room.rooms.entities as Dictionary<RoomInfo>
|
||||
select(AuthorizationSelector.authResponse)
|
||||
)
|
||||
)
|
||||
.subscribe((rooms) => {
|
||||
if (!!this.roomId) {
|
||||
this.currentRoomInfo = rooms[this.roomId];
|
||||
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
.subscribe((authRes) => {
|
||||
this.authRes = authRes;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.next();
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
|
||||
if (!!this.roomIdSubject) {
|
||||
this.roomIdSubject.next();
|
||||
this.roomIdSubject.complete();
|
||||
}
|
||||
}
|
||||
|
||||
initializeRoomData() {
|
||||
if (!!this.messageInput) {
|
||||
this.messageInput.nativeElement.value = '';
|
||||
}
|
||||
this.selectorType = SelectorType.EMPTY;
|
||||
this.translationSimpleview = false;
|
||||
this.changeTranslationSimpleview.emit(false);
|
||||
this.translationPreview = true;
|
||||
this.destLocale = 'en'; // default English :: en
|
||||
this.translationPreviewInfo = null;
|
||||
|
||||
this.store
|
||||
.pipe(
|
||||
takeUntil(merge(this.ngOnDestroySubject, this.roomIdSubject)),
|
||||
select(RoomSelector.room, this.roomId)
|
||||
)
|
||||
.subscribe((room) => {
|
||||
this.currentRoomInfo = room;
|
||||
this.changeDetectorRef.markForCheck();
|
||||
});
|
||||
|
||||
this.loginSession = this.appAuthenticationService.getLoginSession();
|
||||
}
|
||||
/** About Selector */
|
||||
onOpenSelector(type: SelectorType): void {
|
||||
this.selectorType = type;
|
||||
this.changeDetectorRef.detectChanges();
|
||||
this.changeDetectorRef.markForCheck();
|
||||
}
|
||||
clearSelector(): void {
|
||||
this.selectorType = SelectorType.EMPTY;
|
||||
this.selectedSticker = null;
|
||||
this.changeDetectorRef.detectChanges();
|
||||
this.changeDetectorRef.markForCheck();
|
||||
}
|
||||
|
||||
/** Element Handling */
|
||||
focus(clearField: boolean = true): void {
|
||||
if (!!this.messageInput) {
|
||||
if (!!clearField) {
|
||||
this.messageInput.nativeElement.value = '';
|
||||
|
||||
if (this.selectorType !== SelectorType.TRANSLATION) {
|
||||
this.clearSelector();
|
||||
}
|
||||
this.messageInput.nativeElement.value = '';
|
||||
}
|
||||
this.messageInput.nativeElement.focus();
|
||||
}
|
||||
}
|
||||
|
@ -201,6 +259,7 @@ export class FormSectionComponent implements OnInit, OnDestroy {
|
|||
} else {
|
||||
// selector open
|
||||
self.onOpenSelector(SelectorType.FILEUPLOAD);
|
||||
self.changeDetectorRef.detectChanges();
|
||||
|
||||
// FileuploadItem Init. & FileSelector Init.
|
||||
const fileUploadItems = FileUploadItem.fromFiles(fileList);
|
||||
|
@ -213,6 +272,7 @@ export class FormSectionComponent implements OnInit, OnDestroy {
|
|||
self.appChatService
|
||||
.sendMessageOfAttachFile(
|
||||
self.loginRes,
|
||||
self.user,
|
||||
self.loginSession.deviceType,
|
||||
self.currentRoomInfo.roomId,
|
||||
fileUploadItems
|
||||
|
@ -226,10 +286,13 @@ export class FormSectionComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
alert(err);
|
||||
self.clearSelector();
|
||||
if (!!self.fileUploadSelector) {
|
||||
self.fileUploadSelector.onUploadComplete();
|
||||
}
|
||||
|
||||
const msg = this.i18nService.t('common:file.errors.failToUpload');
|
||||
alert(msg);
|
||||
});
|
||||
}
|
||||
})
|
||||
|
@ -240,23 +303,54 @@ export class FormSectionComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
onKeydown(event: KeyboardEvent) {
|
||||
if (event.key === 'PageUp' || event.key === 'PageDown') {
|
||||
event.preventDefault();
|
||||
return false;
|
||||
} else if (event.key === 'Enter' && !event.shiftKey) {
|
||||
// if (event.key === 'PageUp' || event.key === 'PageDown') {
|
||||
// event.preventDefault();
|
||||
// return false;
|
||||
// } else if (event.key === 'Enter' && !event.shiftKey) {
|
||||
// event.preventDefault();
|
||||
// this.send();
|
||||
// }
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.send();
|
||||
}
|
||||
}
|
||||
|
||||
onSelectedSticker(stickerInfo: StickerFilesInfo) {
|
||||
this.selectedSticker = stickerInfo;
|
||||
this.focus(false);
|
||||
}
|
||||
|
||||
onPasteReply(event: ClipboardEvent) {
|
||||
event.preventDefault();
|
||||
|
||||
// this.platform_readFromClipboard().then(async (data) => {
|
||||
// console.log(data);
|
||||
// console.log(data.html);
|
||||
// console.log(data.image);
|
||||
// console.log(data.imageDataUrl);
|
||||
// console.log(data.text);
|
||||
|
||||
// console.log(JSON.stringify(event.clipboardData.items));
|
||||
// if ((!!data.image && !!data.text) || !!data.image) {
|
||||
// }
|
||||
// });
|
||||
// for (const item of items) {
|
||||
// if (item.type.indexOf('image') === 0) {
|
||||
// blob = item.getAsFile();
|
||||
// }
|
||||
// }
|
||||
|
||||
// console.log(JSON.stringify(event.clipboardData.items));
|
||||
// this.nativeService.platform_readFromClipboard().then(async (data) => {
|
||||
// console.log(data);
|
||||
// console.log(JSON.stringify(event.clipboardData.items));
|
||||
// if ((!!data.image && !!data.text) || !!data.image) {
|
||||
// }
|
||||
// });
|
||||
}
|
||||
async send() {
|
||||
const roomId = this.currentRoomInfo.roomId;
|
||||
const userSeq = this.loginRes.userSeq;
|
||||
const userSeq = String(this.user.info.seq);
|
||||
let message = this.messageInput.nativeElement.value;
|
||||
|
||||
if (!!message || message.trim().length > 0) {
|
||||
|
@ -272,11 +366,11 @@ export class FormSectionComponent implements OnInit, OnDestroy {
|
|||
AlertDialogData,
|
||||
AlertDialogResult
|
||||
>(AlertDialogComponent, {
|
||||
panelClass: 'min-create-dialog',
|
||||
data: {
|
||||
title: this.i18nService.t('errors.label'),
|
||||
message: this.i18nService.t('errors.inputChatMessage')
|
||||
},
|
||||
panelClass: ''
|
||||
title: this.i18nService.t('chat:errors.label'),
|
||||
message: this.i18nService.t('chat:errors.inputChatMessage')
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -301,14 +395,7 @@ export class FormSectionComponent implements OnInit, OnDestroy {
|
|||
|
||||
this.clearSelector();
|
||||
} else {
|
||||
this.appChatService.sendMessageOfTranslate(
|
||||
this.loginRes,
|
||||
this.loginSession.deviceType,
|
||||
this.destLocale,
|
||||
roomId,
|
||||
message,
|
||||
this.selectedSticker
|
||||
);
|
||||
this.sendMessageOfTranslate(message);
|
||||
}
|
||||
} else if (!!this.selectedSticker) {
|
||||
/** CASE : Sticker */
|
||||
|
@ -326,6 +413,7 @@ export class FormSectionComponent implements OnInit, OnDestroy {
|
|||
/** CASE : MASS TEXT */
|
||||
this.appChatService.sendMessageOfMassText(
|
||||
this.loginRes,
|
||||
this.user,
|
||||
this.loginSession.deviceType,
|
||||
roomId,
|
||||
message
|
||||
|
@ -335,6 +423,205 @@ export class FormSectionComponent implements OnInit, OnDestroy {
|
|||
this.appChatService.sendMessageOfNormal(userSeq, roomId, message);
|
||||
}
|
||||
|
||||
this.eventSendTrigger.emit(0);
|
||||
this.focus();
|
||||
}
|
||||
|
||||
sendMessageOfTranslate(message: string) {
|
||||
if (!!this.isTranslationProcess) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isTranslationProcess = true;
|
||||
|
||||
this.commonApiService
|
||||
.translationSave({
|
||||
userSeq: String(this.user.info.seq),
|
||||
token: this.loginRes.tokenString,
|
||||
deviceType: this.loginSession.deviceType,
|
||||
original: message,
|
||||
roomId: this.roomId,
|
||||
srcLocale: '',
|
||||
destLocale: this.destLocale
|
||||
} as TranslationSaveRequest)
|
||||
.pipe(
|
||||
take(1),
|
||||
map((res) => {
|
||||
if (res.statusCode === StatusCode.Success) {
|
||||
let sentMessage = '';
|
||||
let eventType = EventType.Translation;
|
||||
let previewObject: TranslationEventJson | MassTranslationEventJson;
|
||||
if (res.translationSeq > 0) {
|
||||
// Mass Text Translation
|
||||
previewObject = res;
|
||||
sentMessage = res.returnJson;
|
||||
eventType = EventType.MassTranslation;
|
||||
} else {
|
||||
// Normal Text Translation
|
||||
previewObject = {
|
||||
locale: this.destLocale,
|
||||
original: message,
|
||||
translation: res.translation,
|
||||
stickername: '',
|
||||
stickerfile: !!this.selectedSticker
|
||||
? this.selectedSticker.index
|
||||
: ''
|
||||
};
|
||||
sentMessage = JSON.stringify(previewObject);
|
||||
eventType = EventType.Translation;
|
||||
}
|
||||
|
||||
if (!!this.translationPreview) {
|
||||
this.translationPreviewInfo = {
|
||||
previewInfo: res,
|
||||
translationType: eventType
|
||||
};
|
||||
this.changeDetectorRef.markForCheck();
|
||||
} else {
|
||||
this._sendTranslationMessage(sentMessage, eventType);
|
||||
}
|
||||
} else {
|
||||
// error
|
||||
this._translationError();
|
||||
}
|
||||
}),
|
||||
catchError((error) => {
|
||||
this._translationError();
|
||||
return of(this.logService.error('error', error));
|
||||
})
|
||||
)
|
||||
.subscribe(() => {
|
||||
this.isTranslationProcess = false;
|
||||
});
|
||||
}
|
||||
|
||||
onChangeTranslationSimpleView(value: boolean) {
|
||||
this.translationSimpleview = value;
|
||||
this.changeTranslationSimpleview.emit(value);
|
||||
}
|
||||
onChangeTranslationPreView(value: boolean) {
|
||||
this.translationPreview = value;
|
||||
}
|
||||
onChangeDestLocale(destLocale: string) {
|
||||
this.destLocale = destLocale;
|
||||
}
|
||||
onSendTranslationMessage(params: {
|
||||
previewInfo: TranslationSaveResponse | null;
|
||||
translationType: EventType.Translation | EventType.MassTranslation;
|
||||
}) {
|
||||
let sentMessage = '';
|
||||
if (params.translationType === EventType.MassTranslation) {
|
||||
// Mass Text Translation
|
||||
sentMessage = params.previewInfo.returnJson;
|
||||
} else {
|
||||
sentMessage = JSON.stringify({
|
||||
locale: params.previewInfo.destLocale,
|
||||
original: params.previewInfo.original,
|
||||
translation: params.previewInfo.translation,
|
||||
stickername: '',
|
||||
stickerfile: !!this.selectedSticker ? this.selectedSticker.index : ''
|
||||
});
|
||||
}
|
||||
|
||||
this._sendTranslationMessage(sentMessage, params.translationType);
|
||||
}
|
||||
|
||||
private _translationError() {
|
||||
this.isTranslationProcess = false;
|
||||
this.dialog.open<AlertDialogComponent, AlertDialogData, AlertDialogResult>(
|
||||
AlertDialogComponent,
|
||||
{
|
||||
data: {
|
||||
title: this.i18nService.t('chat:errors.label'),
|
||||
message: this.i18nService.t('chat:errors.translateServerError')
|
||||
},
|
||||
panelClass: 'min-create-dialog'
|
||||
}
|
||||
);
|
||||
}
|
||||
private _sendTranslationMessage(sentMessage: string, eventType: EventType) {
|
||||
this.appChatService.sendMessageOfTranslate(
|
||||
String(this.user.info.seq),
|
||||
this.roomId,
|
||||
eventType,
|
||||
sentMessage
|
||||
);
|
||||
this.translationPreviewInfo = undefined;
|
||||
this.focus();
|
||||
}
|
||||
|
||||
onSendEventEmail(type: SendEventMailType): void {
|
||||
this.store
|
||||
.pipe(take(1), select(ChattingSelector.eventList, this._roomId))
|
||||
.subscribe((eventList) => {
|
||||
if (!!eventList && eventList.length > 0) {
|
||||
const self = this;
|
||||
const dialogRef = this.dialog.open<
|
||||
ConfirmDialogComponent,
|
||||
ConfirmDialogData,
|
||||
ConfirmDialogResult
|
||||
>(ConfirmDialogComponent, {
|
||||
panelClass: 'min-create-dialog',
|
||||
data: {
|
||||
title: this.i18nService.t('chat:label.emailSend'),
|
||||
html: this.i18nService.t(
|
||||
type === SendEventMailType.ALL
|
||||
? 'chat:dialog.confirmSendEventEmailAll'
|
||||
: 'chat:dialog.confirmSendEventEmailMe'
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
dialogRef
|
||||
.afterClosed()
|
||||
.pipe(take(1))
|
||||
.subscribe((result) => {
|
||||
if (!!result && !!result.choice) {
|
||||
const req: SendEventEmailRequest = {
|
||||
userSeq: String(this.user.info.seq),
|
||||
deviceType: this.loginSession.deviceType,
|
||||
tokenKey: this.loginRes.tokenString,
|
||||
roomSeq: this._roomId,
|
||||
eventSeq: String(eventList[0].seq),
|
||||
sendType: type
|
||||
};
|
||||
this.piService
|
||||
.sendEventEmail(req)
|
||||
.pipe(take(1))
|
||||
.subscribe(
|
||||
(res: SendEventEmailResponse) => {
|
||||
if (res.intStatusCode === PiStatusCode.Success) {
|
||||
this.dialog.open<
|
||||
AlertDialogComponent,
|
||||
AlertDialogData,
|
||||
AlertDialogResult
|
||||
>(AlertDialogComponent, {
|
||||
panelClass: 'min-create-dialog',
|
||||
data: {
|
||||
title: this.i18nService.t('chat:label.emailSend'),
|
||||
message: this.i18nService.t(
|
||||
'chat:dialog.sendEventEmailSuccess'
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
self.selectorType = SelectorType.EMPTY;
|
||||
} else {
|
||||
this.logService.error(res.strErrorMessage);
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
this.logService.error(error);
|
||||
},
|
||||
() => {}
|
||||
);
|
||||
|
||||
this.logService.debug(
|
||||
this.i18nService.t('chat:dialog.sendEventEmailSuccess')
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,74 +1,163 @@
|
|||
<mat-toolbar class="info-chat-toolbar">
|
||||
<mat-toolbar-row>
|
||||
<div class="profileImage">
|
||||
<mat-toolbar-row class="info-chat-toolbar-content">
|
||||
<div class="chat-room-profile">
|
||||
<div class="profile-image">
|
||||
<img
|
||||
class="thumbnail"
|
||||
ucapImage
|
||||
[base]="versionInfo2Res?.profileRoot"
|
||||
[path]="roomImage"
|
||||
[default]="defaultProfileImage"
|
||||
[default]="
|
||||
currentRoomInfo?.roomType === RoomType.Multi
|
||||
? defaultProfileImageMulti
|
||||
: defaultProfileImage
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
>{{ roomName }}
|
||||
<span
|
||||
class="user-count"
|
||||
</div>
|
||||
<span class="chat-room-subject"
|
||||
><mat-icon
|
||||
*ngIf="!!currentRoomInfo?.isTimeRoom"
|
||||
class="ico-timer-unit material-icons-outlined"
|
||||
>timer</mat-icon
|
||||
><span>{{ roomName }}</span>
|
||||
<strong
|
||||
class="user-count text-accent-color"
|
||||
*ngIf="currentRoomInfo?.roomType === RoomType.Multi"
|
||||
>
|
||||
(<strong>{{ currentRoomInfo?.joinUserCount }}</strong
|
||||
>)
|
||||
</span>
|
||||
{{ currentRoomInfo?.joinUserCount }}
|
||||
</strong>
|
||||
</span>
|
||||
<span class="example-spacer"></span>
|
||||
<div>
|
||||
<div class="btn-chat-room-top">
|
||||
<button
|
||||
mat-mini-fab
|
||||
mat-icon-button
|
||||
color="primary"
|
||||
aria-label="search"
|
||||
matTooltip="{{ 'label.search' | ucapI18n }}"
|
||||
matTooltip="{{ 'chat:label.search' | ucapI18n }}"
|
||||
(click)="onOpenChatSearch()"
|
||||
>
|
||||
<mat-icon>search</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
mat-mini-fab
|
||||
mat-icon-button
|
||||
color="primary"
|
||||
aria-label="alarm"
|
||||
matTooltip="{{
|
||||
(currentRoomInfo?.receiveAlarm
|
||||
? 'label.notificationIsOn'
|
||||
: 'label.notificationIsOff'
|
||||
? 'chat:label.turnOnRoomAlert'
|
||||
: 'chat:label.turnOffRoomAlert'
|
||||
) | ucapI18n
|
||||
}}"
|
||||
(click)="onToggleAlarm(currentRoomInfo)"
|
||||
>
|
||||
<mat-icon *ngIf="currentRoomInfo?.receiveAlarm">notifications</mat-icon>
|
||||
<mat-icon
|
||||
*ngIf="currentRoomInfo?.receiveAlarm"
|
||||
class="material-icons-outlined"
|
||||
>notifications</mat-icon
|
||||
>
|
||||
|
||||
<mat-icon *ngIf="!currentRoomInfo?.receiveAlarm"
|
||||
<mat-icon
|
||||
*ngIf="!currentRoomInfo?.receiveAlarm"
|
||||
class="material-icons-outlined"
|
||||
>notifications_off</mat-icon
|
||||
>
|
||||
</button>
|
||||
<button
|
||||
mat-mini-fab
|
||||
<!-- <button
|
||||
mat-icon-button
|
||||
class="btn-icon-favorite"
|
||||
color="primary"
|
||||
aria-label="show room users"
|
||||
matTooltip="{{ 'label.showRoomUsers' | ucapI18n }}"
|
||||
(click)="onOpenRoomUserList(currentRoomInfo)"
|
||||
>
|
||||
<mat-icon>group</mat-icon>
|
||||
</button>
|
||||
<button mat-mini-fab color="primary" aria-label="">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
mat-mini-fab
|
||||
color="primary"
|
||||
aria-label="Menu"
|
||||
matTooltip="{{ 'label.menu' | ucapI18n }}"
|
||||
matTooltip="{{ 'chat:label.favorite' | ucapI18n }}"
|
||||
(click)="onRightDrawerToggle()"
|
||||
>
|
||||
<mat-icon>menu</mat-icon>
|
||||
<mat-icon>star_outline</mat-icon>
|
||||
</button> -->
|
||||
<button
|
||||
mat-icon-button
|
||||
color="primary"
|
||||
aria-label=""
|
||||
[matMenuTriggerFor]="chatRoomMenu"
|
||||
matTooltip="{{ 'chat:label.menu' | ucapI18n }}"
|
||||
>
|
||||
<mat-icon>more_horiz</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</mat-toolbar-row>
|
||||
</mat-toolbar>
|
||||
|
||||
<mat-menu #chatRoomMenu="matMenu" class="chat-menu-box">
|
||||
<div class="menu-title" (click)="$event.stopPropagation()">
|
||||
{{ roomName }}
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<div class="chat-menu-box-body">
|
||||
<div class="data">
|
||||
<div class="sub-title" (click)="$event.stopPropagation()">
|
||||
<mat-icon class="material-icons-outlined">inbox</mat-icon
|
||||
><span>{{ 'chat:label.data' | ucapI18n }}</span>
|
||||
<!--<mat-slide-toggle labelPosition="before" class="simpleview">
|
||||
</mat-slide-toggle>-->
|
||||
</div>
|
||||
<div class="btn">
|
||||
<button mat-button (click)="onClickRoomMenu('ATTACH_IMAGE')">
|
||||
{{ 'chat:label.image' | ucapI18n }}
|
||||
</button>
|
||||
<button mat-button (click)="onClickRoomMenu('ATTACH_VIDEO')">
|
||||
{{ 'chat:label.video' | ucapI18n }}
|
||||
</button>
|
||||
<button mat-button (click)="onClickRoomMenu('ATTACH_FILE')">
|
||||
{{ 'chat:label.attachFile' | ucapI18n }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <button
|
||||
mat-menu-item
|
||||
*ngIf="getShowContextMenu('EVENT')"
|
||||
(click)="onClickRoomMenu('EVENT')"
|
||||
>
|
||||
<mat-icon class="material-icons-outlined">calendar_today</mat-icon
|
||||
>{{ 'chat:label.event' | ucapI18n }}
|
||||
</button> -->
|
||||
<button
|
||||
mat-menu-item
|
||||
*ngIf="getShowContextMenu('ROOM_USERS')"
|
||||
(click)="onClickRoomMenu('ROOM_USERS')"
|
||||
>
|
||||
<mat-icon>person_outline</mat-icon
|
||||
>{{ 'chat:label.showRoomUsers' | ucapI18n }}
|
||||
</button>
|
||||
<button
|
||||
mat-menu-item
|
||||
*ngIf="getShowContextMenu('CHANGE_ROOM_USERS')"
|
||||
(click)="onClickRoomMenu('CHANGE_ROOM_USERS')"
|
||||
>
|
||||
<mat-icon class="material-icons-outlined">person_add</mat-icon
|
||||
>{{ 'chat:label.addRoomUsers' | ucapI18n }}
|
||||
</button>
|
||||
<button
|
||||
mat-menu-item
|
||||
*ngIf="getShowContextMenu('ADD_GROUP')"
|
||||
(click)="onClickRoomMenu('ADD_GROUP')"
|
||||
>
|
||||
<mat-icon class="material-icons-outlined">group_add</mat-icon
|
||||
>{{ 'chat:label.addGroup' | ucapI18n }}
|
||||
</button>
|
||||
<button
|
||||
mat-menu-item
|
||||
*ngIf="getShowContextMenu('SETTING')"
|
||||
(click)="onClickRoomMenu('SETTING')"
|
||||
>
|
||||
<mat-icon class="material-icons-outlined">settings</mat-icon
|
||||
>{{ 'chat:label.roomSetting' | ucapI18n }}
|
||||
</button>
|
||||
<button
|
||||
mat-menu-item
|
||||
class="btn-exit"
|
||||
*ngIf="getShowContextMenu('EXIT')"
|
||||
(click)="onClickRoomMenu('EXIT')"
|
||||
>
|
||||
{{ 'chat:label.exitFromRoom' | ucapI18n }}
|
||||
</button>
|
||||
</div>
|
||||
</mat-menu>
|
||||
|
|
|
@ -4,12 +4,73 @@
|
|||
height: 50px;
|
||||
min-height: 50px;
|
||||
background-color: $white;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-content: center;
|
||||
.mat-toolbar-row {
|
||||
.info-chat-toolbar-content {
|
||||
height: 50px;
|
||||
min-height: 50px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-content: center;
|
||||
justify-content: space-between;
|
||||
@include screen(xs) {
|
||||
padding-right: 0;
|
||||
}
|
||||
.chat-room-profile {
|
||||
align-self: center;
|
||||
margin-right: 15px;
|
||||
height: 38px;
|
||||
flex: 0 0 0;
|
||||
@include profile-avatar-default(
|
||||
0,
|
||||
14,
|
||||
$warm-pink,
|
||||
18px
|
||||
); //오른 아래 공간, 모바일 온라인 아이콘 크기, 모바일 아이콘 색, 모바일 아이콘 bg크기
|
||||
.profile-image {
|
||||
@include avatar-img(36px, 0); //아바타 크기, 왼쪽공간
|
||||
background-color: #d1f6ff;
|
||||
}
|
||||
}
|
||||
.chat-room-subject {
|
||||
flex: 1 1 auto;
|
||||
color: $gray-re3;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
display: flex !important;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
@include ellipsis-column(1);
|
||||
.ico-timer-unit {
|
||||
max-width: 18px !important;
|
||||
height: 18px !important;
|
||||
line-height: 18px;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
margin-right: 6px;
|
||||
flex: 0 0 24px;
|
||||
}
|
||||
span {
|
||||
flex: 0 1 auto;
|
||||
@include ellipsis(1);
|
||||
//width: ;
|
||||
}
|
||||
strong {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding-left: 4px;
|
||||
}
|
||||
}
|
||||
.btn-chat-room-top {
|
||||
flex: 1 0 160px;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: inline-flex;
|
||||
justify-content: flex-end;
|
||||
//대화방 즐겨찾기 - 개발보류
|
||||
.btn-icon-favorite {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import { combineLatest, Subject, merge } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
|
@ -8,22 +11,26 @@ import {
|
|||
EventEmitter,
|
||||
ChangeDetectorRef
|
||||
} from '@angular/core';
|
||||
import { Dictionary } from '@ngrx/entity';
|
||||
import { Store, select } from '@ngrx/store';
|
||||
import { combineLatest, Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
import { RoomSelector, RoomActions } from '@ucap/ng-store-chat';
|
||||
import {
|
||||
LoginSelector,
|
||||
ConfigurationSelector
|
||||
} from '@ucap/ng-store-authentication';
|
||||
import { Store, select } from '@ngrx/store';
|
||||
import { Dictionary } from '@ngrx/entity';
|
||||
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
|
||||
import { LocaleCode } from '@ucap/core';
|
||||
import { I18nService } from '@ucap/ng-i18n';
|
||||
import { VersionInfo2Response } from '@ucap/api-public';
|
||||
import { RoomInfo, RoomType, UpdateRequest } from '@ucap/protocol-room';
|
||||
import { User } from '@ucap/protocol-info';
|
||||
|
||||
import { UserSelector } from '@ucap/ng-store-organization';
|
||||
import { ConfigurationSelector } from '@ucap/ng-store-authentication';
|
||||
import {
|
||||
RoomSelector,
|
||||
RoomActions,
|
||||
RoomUserMap,
|
||||
RoomUserShortMap
|
||||
} from '@ucap/ng-store-chat/lib/store/room/state';
|
||||
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||
import { RoomInfo, RoomType, UpdateRequest } from '@ucap/protocol-room';
|
||||
} from '@ucap/ng-store-chat';
|
||||
|
||||
import {
|
||||
TranslatePipe as OrganizationTranslate,
|
||||
|
@ -31,9 +38,8 @@ import {
|
|||
} from '@ucap/ng-ui-organization';
|
||||
|
||||
import { AppChatService } from '@app/services/app-chat.service';
|
||||
import { I18nService } from '@ucap/ng-i18n';
|
||||
import { VersionInfo2Response } from '@ucap/api-public';
|
||||
import { LocaleCode } from '@ucap/core';
|
||||
import { ChatDrawType } from '@app/pages/chat/types/chat-draw.type';
|
||||
import { DrawInfo } from '@app/pages/chat/models/draw-info';
|
||||
|
||||
@Component({
|
||||
selector: 'app-sections-chat-info',
|
||||
|
@ -42,10 +48,17 @@ import { LocaleCode } from '@ucap/core';
|
|||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class InfoSectionComponent implements OnInit, OnDestroy {
|
||||
private roomIdSubject = new Subject<string>();
|
||||
private ngOnDestroySubject: Subject<void> = new Subject();
|
||||
|
||||
@Input()
|
||||
set roomId(roomId: string) {
|
||||
this._roomId = roomId;
|
||||
|
||||
this.roomIdSubject.next(roomId);
|
||||
|
||||
this.initializeRoomData();
|
||||
|
||||
// request selected room
|
||||
if (!!this.roomId) {
|
||||
this.store.dispatch(
|
||||
|
@ -55,8 +68,6 @@ export class InfoSectionComponent implements OnInit, OnDestroy {
|
|||
})
|
||||
);
|
||||
}
|
||||
|
||||
this.getRoomInfo();
|
||||
}
|
||||
get roomId(): string {
|
||||
return this._roomId;
|
||||
|
@ -68,31 +79,31 @@ export class InfoSectionComponent implements OnInit, OnDestroy {
|
|||
openChatSearch = new EventEmitter<void>();
|
||||
|
||||
@Output()
|
||||
rightDrawerToggle = new EventEmitter<void>();
|
||||
rightDrawerToggle = new EventEmitter<DrawInfo>();
|
||||
|
||||
versionInfo2Res: VersionInfo2Response;
|
||||
loginRes: LoginResponse;
|
||||
user: User;
|
||||
|
||||
defaultProfileImage: string;
|
||||
defaultProfileImageMulti: string;
|
||||
|
||||
currentRoomInfo: RoomInfo;
|
||||
roomList: RoomInfo[];
|
||||
roomUsersDictionary: Dictionary<RoomUserMap>;
|
||||
roomUsersShortDictionary: Dictionary<RoomUserShortMap>;
|
||||
roomUsersMap: RoomUserMap;
|
||||
roomUsersShortMap: RoomUserShortMap;
|
||||
|
||||
roomName: string;
|
||||
roomImage: string;
|
||||
|
||||
RoomType = RoomType;
|
||||
organizationTranslate: OrganizationTranslate;
|
||||
private ngOnDestroySubject: Subject<boolean>;
|
||||
|
||||
constructor(
|
||||
private store: Store<any>,
|
||||
private appChatService: AppChatService,
|
||||
private i18nService: I18nService,
|
||||
private translateService: TranslateService,
|
||||
private dialog: MatDialog,
|
||||
private changeDetectorRef: ChangeDetectorRef
|
||||
) {
|
||||
// language setting
|
||||
|
@ -105,12 +116,10 @@ export class InfoSectionComponent implements OnInit, OnDestroy {
|
|||
|
||||
// default image setting
|
||||
this.defaultProfileImage = this.appChatService.defaultProfileImage;
|
||||
this.defaultProfileImageMulti = this.appChatService.defaultProfileImage;
|
||||
this.defaultProfileImageMulti = this.appChatService.defaultProfileImageMulti;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.ngOnDestroySubject = new Subject<boolean>();
|
||||
|
||||
this.store
|
||||
.pipe(
|
||||
takeUntil(this.ngOnDestroySubject),
|
||||
|
@ -119,76 +128,58 @@ export class InfoSectionComponent implements OnInit, OnDestroy {
|
|||
.subscribe((versionInfo2Res) => {
|
||||
this.versionInfo2Res = versionInfo2Res;
|
||||
});
|
||||
|
||||
combineLatest([
|
||||
this.store.pipe(select(LoginSelector.loginRes)),
|
||||
this.store.pipe(select(RoomSelector.rooms)),
|
||||
this.store.pipe(
|
||||
select(
|
||||
(state: any) =>
|
||||
state.chat.room.roomUsers.entities as Dictionary<RoomUserMap>
|
||||
)
|
||||
),
|
||||
this.store.pipe(
|
||||
select(
|
||||
(state: any) =>
|
||||
state.chat.room.roomUsersShort.entities as Dictionary<
|
||||
RoomUserShortMap
|
||||
>
|
||||
)
|
||||
)
|
||||
])
|
||||
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||
.subscribe(([loginRes, rooms, roomUsers, roomUsersShort]) => {
|
||||
this.loginRes = loginRes;
|
||||
if (!!loginRes && !!this.roomId) {
|
||||
this.roomList = rooms;
|
||||
this.roomUsersDictionary = roomUsers;
|
||||
this.roomUsersShortDictionary = roomUsersShort;
|
||||
|
||||
this.getRoomInfo();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.next();
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
|
||||
if (!!this.roomIdSubject) {
|
||||
this.roomIdSubject.next();
|
||||
this.roomIdSubject.complete();
|
||||
}
|
||||
}
|
||||
|
||||
initializeRoomData() {
|
||||
combineLatest([
|
||||
this.store.pipe(select(UserSelector.user)),
|
||||
this.store.pipe(select(RoomSelector.room, this.roomId)),
|
||||
this.store.pipe(select(RoomSelector.roomUser, this.roomId)),
|
||||
this.store.pipe(select(RoomSelector.roomUserShort, this.roomId))
|
||||
])
|
||||
.pipe(takeUntil(merge(this.ngOnDestroySubject, this.roomIdSubject)))
|
||||
.subscribe(([user, room, roomUsers, roomUsersShort]) => {
|
||||
this.user = user;
|
||||
this.currentRoomInfo = room;
|
||||
this.roomUsersMap = roomUsers;
|
||||
this.roomUsersShortMap = roomUsersShort;
|
||||
|
||||
this.getRoomInfo();
|
||||
});
|
||||
}
|
||||
|
||||
getRoomInfo(): void {
|
||||
// room render.
|
||||
if (!this.roomList || this.roomList.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.roomName = '...';
|
||||
const idx = this.roomList.findIndex(
|
||||
(roomInfo) => roomInfo.roomId === this.roomId
|
||||
);
|
||||
if (idx > -1 && !!this.roomList && this.roomList.length > 0) {
|
||||
this.currentRoomInfo = this.roomList[idx];
|
||||
}
|
||||
|
||||
if (!!this.currentRoomInfo) {
|
||||
// Setting RoomName.
|
||||
this.roomName = this.appChatService.getRoomName(
|
||||
this.organizationTranslate,
|
||||
this.loginRes,
|
||||
this.user,
|
||||
this.currentRoomInfo,
|
||||
this.roomUsersDictionary,
|
||||
this.roomUsersShortDictionary
|
||||
this.roomUsersMap,
|
||||
this.roomUsersShortMap
|
||||
);
|
||||
|
||||
this.roomImage = this.appChatService.getRoomProfileImage(
|
||||
this.user,
|
||||
this.currentRoomInfo,
|
||||
this.loginRes,
|
||||
this.roomUsersDictionary,
|
||||
this.roomUsersShortDictionary
|
||||
this.roomUsersMap,
|
||||
this.roomUsersShortMap
|
||||
);
|
||||
}
|
||||
this.changeDetectorRef.detectChanges();
|
||||
this.changeDetectorRef.markForCheck();
|
||||
}
|
||||
|
||||
onToggleAlarm(roomInfo: RoomInfo): void {
|
||||
|
@ -209,11 +200,9 @@ export class InfoSectionComponent implements OnInit, OnDestroy {
|
|||
} as UpdateRequest
|
||||
})
|
||||
);
|
||||
this.changeDetectorRef.detectChanges();
|
||||
this.changeDetectorRef.markForCheck();
|
||||
}
|
||||
|
||||
onOpenRoomUserList(roomInfo: RoomInfo): void {}
|
||||
|
||||
onOpenChatSearch(): void {
|
||||
this.openChatSearch.emit();
|
||||
}
|
||||
|
@ -221,4 +210,64 @@ export class InfoSectionComponent implements OnInit, OnDestroy {
|
|||
onRightDrawerToggle(): void {
|
||||
this.rightDrawerToggle.emit();
|
||||
}
|
||||
|
||||
onClickRoomMenu(type: string): void {
|
||||
switch (type) {
|
||||
case 'ATTACH_IMAGE':
|
||||
this.rightDrawerToggle.emit({ chatDrawType: ChatDrawType.AttachImage });
|
||||
break;
|
||||
case 'ATTACH_VIDEO':
|
||||
this.rightDrawerToggle.emit({ chatDrawType: ChatDrawType.AttachVideo });
|
||||
break;
|
||||
case 'ATTACH_FILE':
|
||||
this.rightDrawerToggle.emit({ chatDrawType: ChatDrawType.AttachFile });
|
||||
break;
|
||||
case 'ADD_GROUP':
|
||||
this.rightDrawerToggle.emit({ chatDrawType: ChatDrawType.AddGroup });
|
||||
break;
|
||||
case 'ROOM_USERS':
|
||||
this.rightDrawerToggle.emit({ chatDrawType: ChatDrawType.RoomUsers });
|
||||
break;
|
||||
case 'CHANGE_ROOM_USERS':
|
||||
this.rightDrawerToggle.emit({ chatDrawType: ChatDrawType.Invite });
|
||||
break;
|
||||
case 'SETTING':
|
||||
this.rightDrawerToggle.emit({ chatDrawType: ChatDrawType.Setting });
|
||||
break;
|
||||
case 'EXIT':
|
||||
this.appChatService.exitRoomDialog(this.currentRoomInfo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
getShowContextMenu(menuType: string) {
|
||||
if (
|
||||
['EVENT', 'ROOM_USERS', 'CHANGE_ROOM_USERS', 'ADD_GROUP', 'SETTING'].some(
|
||||
(v) => v === menuType
|
||||
)
|
||||
) {
|
||||
if (
|
||||
!this.currentRoomInfo ||
|
||||
!this.currentRoomInfo.roomType ||
|
||||
[RoomType.Mytalk, RoomType.Allim, RoomType.Bot, RoomType.Link].some(
|
||||
(v) => v === this.currentRoomInfo.roomType
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// else if (['CHAT_EXPORT'].some((v) => v === menuType)) {
|
||||
// if (
|
||||
// !this.currentRoomInfo ||
|
||||
// !this.currentRoomInfo.roomType ||
|
||||
// [RoomType.Allim, RoomType.Bot, RoomType.Link].some(
|
||||
// (v) => v === this.currentRoomInfo.roomType
|
||||
// )
|
||||
// ) {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +1,12 @@
|
|||
<div
|
||||
*ngIf="!!searchObj && !searchObj.isShowSearch"
|
||||
fxFlexFill
|
||||
class="list-container"
|
||||
>
|
||||
<div fxFlexFill class="list-container">
|
||||
<app-chat-room-expansion
|
||||
[searchObj]="searchObj"
|
||||
[currentRoomId]="currentRoomId"
|
||||
[checkable]="checkable"
|
||||
[selectedRoomList]="selectedRoomList"
|
||||
(toggleItem)="onToggleItem($event)"
|
||||
(openChatRoom)="onOpenChatRoom($event)"
|
||||
(searchResultList)="onSearchResultList($event)"
|
||||
>
|
||||
</app-chat-room-expansion>
|
||||
</div>
|
||||
<div *ngIf="!!searchObj && searchObj.isShowSearch">
|
||||
<ucap-chat-room-list-item-01
|
||||
*ngFor="let roomInfo of searchRoomList"
|
||||
[roomInfo]="roomInfo"
|
||||
[roomName]="getRoomName(roomInfo)"
|
||||
[profileImageRoot]="versionInfo2Res?.profileRoot"
|
||||
[defaultProfileImage]="defaultProfileImage"
|
||||
[profileImage]="getRoomProfileImage(roomInfo)"
|
||||
[checked]="getChecked(roomInfo)"
|
||||
[checkable]="checkable"
|
||||
(toggleItem)="onToggleItem($event)"
|
||||
(openChatRoom)="onOpenChatRoom($event)"
|
||||
(toggleAlarm)="onToggleAlarm($event)"
|
||||
(delRoom)="onDelRoom($event)"
|
||||
(click)="onClickRoomItem($event, roomInfo)"
|
||||
></ucap-chat-room-list-item-01>
|
||||
</div>
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
.list-container {
|
||||
height: calc(100% - 90px) !important;
|
||||
min-height: auto !important;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Subject, combineLatest } from 'rxjs';
|
||||
import { takeUntil, take } from 'rxjs/operators';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
import {
|
||||
Component,
|
||||
|
@ -9,91 +9,24 @@ import {
|
|||
ChangeDetectorRef,
|
||||
Input,
|
||||
EventEmitter,
|
||||
Output,
|
||||
NgZone
|
||||
Output
|
||||
} from '@angular/core';
|
||||
|
||||
import { Store, select } from '@ngrx/store';
|
||||
|
||||
import {
|
||||
FixedSizeVirtualScrollStrategy,
|
||||
VIRTUAL_SCROLL_STRATEGY
|
||||
} from '@angular/cdk/scrolling';
|
||||
|
||||
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||
|
||||
import { LogService } from '@ucap/ng-logger';
|
||||
import { SessionStorageService } from '@ucap/ng-web-storage';
|
||||
import {
|
||||
LoginSelector,
|
||||
ConfigurationSelector
|
||||
} from '@ucap/ng-store-authentication';
|
||||
import { RoomSelector, RoomActions } from '@ucap/ng-store-chat';
|
||||
import {
|
||||
RoomInfo,
|
||||
RoomType,
|
||||
ExitAllRequest,
|
||||
UpdateRequest,
|
||||
ExitRequest
|
||||
} from '@ucap/protocol-room';
|
||||
import {
|
||||
RoomUserMap,
|
||||
RoomUserShortMap
|
||||
} from '@ucap/ng-store-chat/lib/store/room/state';
|
||||
import { Dictionary } from '@ngrx/entity';
|
||||
import {
|
||||
TranslatePipe as OrganizationTranslate,
|
||||
TranslateService
|
||||
} from '@ucap/ng-ui-organization';
|
||||
import { I18nService } from '@ucap/ng-i18n';
|
||||
import { AppChatService } from '@app/services/app-chat.service';
|
||||
import {
|
||||
DateService,
|
||||
ConfirmDialogComponent,
|
||||
ConfirmDialogData,
|
||||
ConfirmDialogResult
|
||||
} from '@ucap/ng-ui';
|
||||
import { VersionInfo2Response } from '@ucap/api-public';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
export class ChatVirtualScrollStrategy extends FixedSizeVirtualScrollStrategy {
|
||||
constructor() {
|
||||
super(60, 150, 200); // (itemSize, minBufferPx, maxBufferPx)
|
||||
}
|
||||
}
|
||||
import { RoomInfo } from '@ucap/protocol-room';
|
||||
import { Router, ActivatedRoute, Params } from '@angular/router';
|
||||
import { QueryParams } from '@app/pages/chat/types/params.type';
|
||||
import { SearchInfo } from '@app/pages/chat/models/search-info';
|
||||
|
||||
@Component({
|
||||
selector: 'app-sections-chat-list',
|
||||
templateUrl: './list.section.component.html',
|
||||
styleUrls: ['./list.section.component.scss'],
|
||||
providers: [
|
||||
{
|
||||
provide: VIRTUAL_SCROLL_STRATEGY,
|
||||
useClass: ChatVirtualScrollStrategy
|
||||
}
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ListSectionComponent implements OnInit, OnDestroy {
|
||||
@Input()
|
||||
set searchObj(obj: { isShowSearch: boolean; searchWord: string }) {
|
||||
this._searchObj = obj;
|
||||
|
||||
if (obj.isShowSearch && obj.searchWord.localeCompare('') !== 0) {
|
||||
this.onRoomSearch(obj);
|
||||
} else {
|
||||
this._searchObj.isShowSearch = false;
|
||||
this.searchRoomList = [];
|
||||
}
|
||||
this.searchResultList.emit(this.searchRoomList);
|
||||
}
|
||||
|
||||
get searchObj() {
|
||||
return this._searchObj;
|
||||
}
|
||||
// tslint:disable-next-line: variable-name
|
||||
_searchObj: any;
|
||||
searchObj: SearchInfo;
|
||||
|
||||
@Input()
|
||||
checkable = false;
|
||||
|
@ -110,212 +43,39 @@ export class ListSectionComponent implements OnInit, OnDestroy {
|
|||
roomInfo: RoomInfo;
|
||||
}>();
|
||||
|
||||
@Output()
|
||||
delRoom = new EventEmitter<RoomInfo>();
|
||||
|
||||
versionInfo2Res: VersionInfo2Response;
|
||||
loginRes: LoginResponse;
|
||||
|
||||
defaultProfileImage: string;
|
||||
defaultProfileImageMulti: string;
|
||||
|
||||
currentRoomId: string;
|
||||
roomList: RoomInfo[];
|
||||
roomUsersDictionary: Dictionary<RoomUserMap>;
|
||||
roomUsersShortDictionary: Dictionary<RoomUserShortMap>;
|
||||
|
||||
searchRoomList: RoomInfo[];
|
||||
|
||||
roomGroup: { division: string; roomList: RoomInfo[] }[];
|
||||
|
||||
organizationTranslate: OrganizationTranslate;
|
||||
|
||||
private ngOnDestroySubject: Subject<boolean>;
|
||||
private ngOnDestroySubject: Subject<void> = new Subject();
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private appChatService: AppChatService,
|
||||
private dateService: DateService,
|
||||
private sessionStorageService: SessionStorageService,
|
||||
private i18nService: I18nService,
|
||||
private translateService: TranslateService,
|
||||
private store: Store<any>,
|
||||
private dialog: MatDialog,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private ngZone: NgZone,
|
||||
private logService: LogService
|
||||
) {
|
||||
// default image setting
|
||||
this.defaultProfileImage = this.appChatService.defaultProfileImage;
|
||||
this.defaultProfileImageMulti = this.appChatService.defaultProfileImage;
|
||||
}
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.ngOnDestroySubject = new Subject<boolean>();
|
||||
|
||||
// language setting
|
||||
this.translateService.setDefaultLang(this.i18nService.currentLng);
|
||||
this.translateService.use(this.i18nService.currentLng);
|
||||
this.organizationTranslate = new OrganizationTranslate(
|
||||
this.translateService,
|
||||
this.changeDetectorRef
|
||||
);
|
||||
this.i18nService.setDefaultNamespace('chat');
|
||||
|
||||
this.store
|
||||
.pipe(
|
||||
takeUntil(this.ngOnDestroySubject),
|
||||
select(ConfigurationSelector.versionInfo2Response)
|
||||
)
|
||||
.subscribe((versionInfo2Res) => {
|
||||
this.versionInfo2Res = versionInfo2Res;
|
||||
});
|
||||
|
||||
this.store
|
||||
.pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
|
||||
.subscribe((loginRes) => {
|
||||
this.loginRes = loginRes;
|
||||
});
|
||||
|
||||
combineLatest([
|
||||
this.store.pipe(select(RoomSelector.rooms)),
|
||||
this.store.pipe(select(RoomSelector.standbyRooms))
|
||||
])
|
||||
this.activatedRoute.queryParams
|
||||
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||
.subscribe(([rooms, standbyRooms]) => {
|
||||
rooms = (rooms || []).filter((info) => {
|
||||
return (
|
||||
info.isJoinRoom &&
|
||||
!standbyRooms.find((standbyRoom) => standbyRoom === info.roomId)
|
||||
);
|
||||
});
|
||||
this.roomList = rooms;
|
||||
|
||||
this.changeDetectorRef.detectChanges();
|
||||
});
|
||||
|
||||
combineLatest([
|
||||
this.store.pipe(
|
||||
select(
|
||||
(state: any) =>
|
||||
state.chat.room.roomUsers.entities as Dictionary<RoomUserMap>
|
||||
)
|
||||
),
|
||||
this.store.pipe(
|
||||
select(
|
||||
(state: any) =>
|
||||
state.chat.room.roomUsersShort.entities as Dictionary<
|
||||
RoomUserShortMap
|
||||
>
|
||||
)
|
||||
)
|
||||
])
|
||||
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||
.subscribe(([roomUsers, roomUsersShort]) => {
|
||||
this.roomUsersDictionary = roomUsers;
|
||||
this.roomUsersShortDictionary = roomUsersShort;
|
||||
this.changeDetectorRef.detectChanges();
|
||||
.subscribe((params: Params) => {
|
||||
const seqParam = params[QueryParams.ROOM_ID];
|
||||
this.currentRoomId = !!seqParam ? seqParam : undefined;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.next();
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
|
||||
getRoomName(roomInfo: RoomInfo): string {
|
||||
if (!roomInfo) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const roomName = this.appChatService.getRoomName(
|
||||
this.organizationTranslate,
|
||||
this.loginRes,
|
||||
roomInfo,
|
||||
this.roomUsersDictionary,
|
||||
this.roomUsersShortDictionary
|
||||
);
|
||||
|
||||
return roomName;
|
||||
}
|
||||
|
||||
getRoomProfileImage(roomInfo: RoomInfo): string {
|
||||
let roomImage = '';
|
||||
if (!!roomInfo) {
|
||||
roomImage = this.appChatService.getRoomProfileImage(
|
||||
roomInfo,
|
||||
this.loginRes,
|
||||
this.roomUsersDictionary,
|
||||
this.roomUsersShortDictionary
|
||||
);
|
||||
}
|
||||
|
||||
return roomImage;
|
||||
}
|
||||
|
||||
isToday(date: any) {
|
||||
return this.dateService.isToday(date);
|
||||
}
|
||||
|
||||
onRoomSearch(obj: { isShowSearch: boolean; searchWord: string }) {
|
||||
const searchRoomList: RoomInfo[] = [];
|
||||
|
||||
this.roomList.forEach((roomInfo) => {
|
||||
if (roomInfo.roomName.indexOf(obj.searchWord) > -1) {
|
||||
searchRoomList.push(roomInfo);
|
||||
} else {
|
||||
const roomUsers = this.appChatService.getRoomUserList(
|
||||
this.loginRes,
|
||||
roomInfo.roomId,
|
||||
this.roomUsersDictionary,
|
||||
this.roomUsersShortDictionary
|
||||
);
|
||||
|
||||
if (
|
||||
roomUsers.existUsers &&
|
||||
roomUsers.users.filter(
|
||||
(userInfo) =>
|
||||
userInfo.name.indexOf(obj.searchWord) > -1 ||
|
||||
userInfo.nameEn.indexOf(obj.searchWord) > -1 ||
|
||||
userInfo.nameCn.indexOf(obj.searchWord) > -1
|
||||
).length > 0
|
||||
) {
|
||||
searchRoomList.push(roomInfo);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.searchRoomList = searchRoomList;
|
||||
}
|
||||
|
||||
getChecked(roomInfo: RoomInfo): boolean {
|
||||
if (this.selectedRoomList.some((info) => info.roomId === roomInfo.roomId)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
onToggleItem(event: { checked: boolean; roomInfo: RoomInfo }): void {
|
||||
this.toggleItem.emit(event);
|
||||
}
|
||||
|
||||
onClickRoomItem(event: MouseEvent, roomInfo: RoomInfo): void {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
if (!!this.checkable) {
|
||||
this.onToggleItem({
|
||||
checked: !this.getChecked(roomInfo),
|
||||
roomInfo
|
||||
});
|
||||
} else {
|
||||
this.onOpenChatRoom(roomInfo);
|
||||
}
|
||||
}
|
||||
|
||||
onOpenChatRoom(roomInfo: RoomInfo): void {
|
||||
this.ngZone.run(() => {
|
||||
this.router.navigate(
|
||||
[
|
||||
'chat',
|
||||
|
@ -327,60 +87,9 @@ export class ListSectionComponent implements OnInit, OnDestroy {
|
|||
queryParams: { roomId: roomInfo.roomId }
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
onToggleAlarm(roomInfo: RoomInfo): void {
|
||||
if (!roomInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.store.dispatch(
|
||||
RoomActions.update({
|
||||
req: {
|
||||
roomId: roomInfo.roomId,
|
||||
roomName:
|
||||
roomInfo.roomName.trim().length === 0
|
||||
? ' '
|
||||
: roomInfo.roomName.trim(),
|
||||
receiveAlarm: !roomInfo.receiveAlarm,
|
||||
syncAll: false
|
||||
} as UpdateRequest
|
||||
})
|
||||
);
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
onDelRoom(roomInfo: RoomInfo): void {
|
||||
if (!roomInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dialogRef = this.dialog.open<
|
||||
ConfirmDialogComponent,
|
||||
ConfirmDialogData,
|
||||
ConfirmDialogResult
|
||||
>(ConfirmDialogComponent, {
|
||||
data: {
|
||||
title: this.i18nService.t('dialog.title.exitFromRoom'),
|
||||
html: this.i18nService.t('dialog.confirmExitFromRoom')
|
||||
}
|
||||
});
|
||||
|
||||
dialogRef
|
||||
.afterClosed()
|
||||
.pipe(take(1))
|
||||
.subscribe((result) => {
|
||||
if (!!result && !!result.choice) {
|
||||
this.store.dispatch(
|
||||
RoomActions.del({
|
||||
req: {
|
||||
roomId: roomInfo.roomId
|
||||
} as ExitRequest
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
this.changeDetectorRef.detectChanges();
|
||||
onSearchResultList(searchResultList: RoomInfo[]) {
|
||||
this.searchResultList.emit(searchResultList);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
import { Observable, Subject } from 'rxjs';
|
||||
|
||||
import {
|
||||
VirtualScrollStrategy,
|
||||
CdkVirtualScrollViewport
|
||||
} from '@angular/cdk/scrolling';
|
||||
import { distinctUntilChanged } from 'rxjs/operators';
|
||||
|
||||
export class ChatGroupVirtualScrollStrategy implements VirtualScrollStrategy {
|
||||
scrolledIndexChange: Observable<number>;
|
||||
|
||||
private indexSubject = new Subject<number>();
|
||||
private viewport: CdkVirtualScrollViewport | null = null;
|
||||
|
||||
constructor() {
|
||||
this.scrolledIndexChange = this.indexSubject.pipe(distinctUntilChanged());
|
||||
}
|
||||
|
||||
attach(viewport: CdkVirtualScrollViewport): void {
|
||||
this.viewport = viewport;
|
||||
}
|
||||
detach(): void {
|
||||
this.indexSubject.complete();
|
||||
this.viewport = null;
|
||||
}
|
||||
onContentScrolled(): void {}
|
||||
onDataLengthChanged(): void {}
|
||||
onContentRendered(): void {}
|
||||
onRenderedOffsetChanged(): void {}
|
||||
scrollToIndex(index: number, behavior: ScrollBehavior): void {}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user