This commit is contained in:
Park Byung Eun 2020-08-10 14:05:32 +09:00
parent 98ff58c39e
commit eded98a6cf
284 changed files with 9356 additions and 3347 deletions

6
package-lock.json generated
View File

@ -2022,9 +2022,9 @@
"integrity": "sha512-GA9MDcwCvtxI0gOysgRm7DDHIfKfhCkDSa69QBFIEgWbob2OdGYKvxfymr6lGdl+vY7AqjlJXvSFkA1b0rTy1A==" "integrity": "sha512-GA9MDcwCvtxI0gOysgRm7DDHIfKfhCkDSa69QBFIEgWbob2OdGYKvxfymr6lGdl+vY7AqjlJXvSFkA1b0rTy1A=="
}, },
"@ucap/ng-core": { "@ucap/ng-core": {
"version": "0.0.7", "version": "0.0.8",
"resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/ng-core/-/ng-core-0.0.7.tgz", "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/ng-core/-/ng-core-0.0.8.tgz",
"integrity": "sha512-ZC6LE3A0bg+REGbzDI/i1ad7mGpKsw6X0UtZ+Q8TUthHNv0DfWEieHFCgfYTRY1u022XyQ4ViOsrq9KunU1vfw==" "integrity": "sha512-VMvitw2PnaGo4tsdD9EJcvzVi5Kt+xjzA6z09uLavPpAWViFXTx7P76N1nt7XMgACRAMOTMyNocWTbFs6oKsqA=="
}, },
"@ucap/ng-i18n": { "@ucap/ng-i18n": {
"version": "0.0.6", "version": "0.0.6",

View File

@ -31,24 +31,39 @@
"@ngrx/entity": "^9.2.0", "@ngrx/entity": "^9.2.0",
"@ngrx/router-store": "^9.2.0", "@ngrx/router-store": "^9.2.0",
"@ngrx/store": "^9.2.0", "@ngrx/store": "^9.2.0",
"@ucap/api": "~0.0.4", "@ucap/api": "~0.0.5",
"@ucap/api-common": "~0.0.11", "@ucap/api-common": "~0.0.12",
"@ucap/api-external": "~0.0.5", "@ucap/api-contact": "~0.0.5",
"@ucap/api-message": "~0.0.3", "@ucap/api-external": "~0.0.8",
"@ucap/api-prompt": "~0.0.3", "@ucap/api-message": "~0.0.7",
"@ucap/api-public": "~0.0.4", "@ucap/api-prompt": "~0.0.6",
"@ucap/core": "~0.0.14", "@ucap/api-public": "~0.0.6",
"@ucap/logger": "~0.0.13", "@ucap/api-webex": "~0.0.2",
"@ucap/native": "~0.0.19", "@ucap/core": "~0.0.15",
"@ucap/domain-authentication": "~0.0.4",
"@ucap/domain-authorization": "~0.0.3",
"@ucap/domain-call": "~0.0.4",
"@ucap/domain-chat": "~0.0.3",
"@ucap/domain-common": "~0.0.1",
"@ucap/domain-group": "~0.0.2",
"@ucap/domain-message": "~0.0.1",
"@ucap/domain-organization": "~0.0.1",
"@ucap/domain-status": "~0.0.1",
"@ucap/electron-native": "~0.0.19",
"@ucap/i18n": "~0.0.2",
"@ucap/logger": "~0.0.14",
"@ucap/native": "~0.0.27",
"@ucap/ng-api-common": "~0.0.1", "@ucap/ng-api-common": "~0.0.1",
"@ucap/ng-api-external": "~0.0.1", "@ucap/ng-api-external": "~0.0.1",
"@ucap/ng-api-message": "~0.0.1", "@ucap/ng-api-message": "~0.0.1",
"@ucap/ng-api-prompt": "~0.0.1", "@ucap/ng-api-prompt": "~0.0.1",
"@ucap/ng-api-public": "~0.0.1", "@ucap/ng-api-public": "~0.0.1",
"@ucap/ng-core": "~0.0.7", "@ucap/ng-api-webex": "~0.0.1",
"@ucap/ng-api-contact": "~0.0.2",
"@ucap/ng-core": "~0.0.9",
"@ucap/ng-logger": "~0.0.2", "@ucap/ng-logger": "~0.0.2",
"@ucap/ng-i18n": "~0.0.6", "@ucap/ng-i18n": "~0.0.8",
"@ucap/ng-native": "~0.0.5", "@ucap/ng-native": "~0.0.12",
"@ucap/ng-pi": "~0.0.1", "@ucap/ng-pi": "~0.0.1",
"@ucap/ng-protocol": "~0.0.3", "@ucap/ng-protocol": "~0.0.3",
"@ucap/ng-protocol-authentication": "~0.0.3", "@ucap/ng-protocol-authentication": "~0.0.3",
@ -66,35 +81,37 @@
"@ucap/ng-protocol-status": "~0.0.3", "@ucap/ng-protocol-status": "~0.0.3",
"@ucap/ng-protocol-sync": "~0.0.3", "@ucap/ng-protocol-sync": "~0.0.3",
"@ucap/ng-protocol-umg": "~0.0.3", "@ucap/ng-protocol-umg": "~0.0.3",
"@ucap/ng-store-authentication": "~0.0.14", "@ucap/ng-store-authentication": "~0.0.17",
"@ucap/ng-store-chat": "~0.0.66", "@ucap/ng-store-chat": "~0.0.74",
"@ucap/ng-store-group": "~0.0.22", "@ucap/ng-store-group": "~0.0.25",
"@ucap/ng-store-organization": "~0.0.20", "@ucap/ng-store-organization": "~0.0.23",
"@ucap/ng-store-call": "~0.0.7",
"@ucap/ng-web-socket": "~0.0.2", "@ucap/ng-web-socket": "~0.0.2",
"@ucap/ng-web-storage": "~0.0.3", "@ucap/ng-web-storage": "~0.0.3",
"@ucap/ng-ui": "0.0.97", "@ucap/ng-ui": "~0.0.108",
"@ucap/ng-ui-organization": "~0.0.202", "@ucap/ng-ui-organization": "~0.0.222",
"@ucap/ng-ui-authentication": "~0.0.29", "@ucap/ng-ui-authentication": "~0.0.32",
"@ucap/ng-ui-group": "~0.0.78", "@ucap/ng-ui-group": "~0.0.87",
"@ucap/ng-ui-chat": "~0.0.72", "@ucap/ng-ui-chat": "~0.0.80",
"@ucap/ng-ui-call": "~0.0.15",
"@ucap/ng-ui-material": "~0.0.4", "@ucap/ng-ui-material": "~0.0.4",
"@ucap/ng-ui-skin-default": "~0.0.1", "@ucap/ng-ui-skin-default": "~0.0.1",
"@ucap/pi": "~0.0.8", "@ucap/pi": "~0.0.9",
"@ucap/protocol": "~0.0.17", "@ucap/protocol": "~0.0.20",
"@ucap/protocol-authentication": "~0.0.5", "@ucap/protocol-authentication": "~0.0.7",
"@ucap/protocol-buddy": "~0.0.5", "@ucap/protocol-buddy": "~0.0.6",
"@ucap/protocol-event": "~0.0.6", "@ucap/protocol-event": "~0.0.11",
"@ucap/protocol-file": "~0.0.6", "@ucap/protocol-file": "~0.0.7",
"@ucap/protocol-group": "~0.0.5", "@ucap/protocol-group": "~0.0.6",
"@ucap/protocol-info": "~0.0.9", "@ucap/protocol-info": "~0.0.10",
"@ucap/protocol-inner": "~0.0.4", "@ucap/protocol-inner": "~0.0.5",
"@ucap/protocol-option": "~0.0.7", "@ucap/protocol-option": "~0.0.9",
"@ucap/protocol-ping": "~0.0.6", "@ucap/protocol-ping": "~0.0.7",
"@ucap/protocol-query": "~0.0.5", "@ucap/protocol-query": "~0.0.8",
"@ucap/protocol-room": "~0.0.7", "@ucap/protocol-room": "~0.0.9",
"@ucap/protocol-service": "~0.0.4", "@ucap/protocol-service": "~0.0.5",
"@ucap/protocol-status": "~0.0.5", "@ucap/protocol-status": "~0.0.6",
"@ucap/protocol-sync": "~0.0.6", "@ucap/protocol-sync": "~0.0.8",
"@ucap/protocol-umg": "~0.0.5", "@ucap/protocol-umg": "~0.0.5",
"@ucap/ui-scss": "~0.0.5", "@ucap/ui-scss": "~0.0.5",
"@ucap/web-socket": "~0.0.10", "@ucap/web-socket": "~0.0.10",

View File

@ -15,10 +15,13 @@ import { AppAuthenticationService } from './services/app-authentication.service'
import { AppNotificationService } from './services/app-notification.service'; import { AppNotificationService } from './services/app-notification.service';
import { AppNativeService } from './services/app-native.service'; import { AppNativeService } from './services/app-native.service';
import { AppService } from './services/app.service'; import { AppService } from './services/app.service';
import { AppCallService } from './services/app-call.service';
import { AppChatService } from './services/app-chat.service'; import { AppChatService } from './services/app-chat.service';
import { AppFileService } from './services/app-file.service'; import { AppFileService } from './services/app-file.service';
import { AppGroupService } from './services/app-group.service'; import { AppGroupService } from './services/app-group.service';
import { AppAccountService } from './services/app-account.service'; import { AppAccountService } from './services/app-account.service';
import { AppUiService } from './services/app-ui.service';
import { AppOrganizationService } from './services/app-organization.service';
const GUARDS = [AppAuthenticationGuard]; const GUARDS = [AppAuthenticationGuard];
const RESOLVERS = [AppSessionResolver]; const RESOLVERS = [AppSessionResolver];
@ -27,10 +30,13 @@ const SERVICES = [
AppAuthenticationService, AppAuthenticationService,
AppNativeService, AppNativeService,
AppFileService, AppFileService,
AppCallService,
AppChatService, AppChatService,
AppNotificationService, AppNotificationService,
AppOrganizationService,
AppGroupService, AppGroupService,
AppAccountService AppAccountService,
AppUiService
]; ];
const axiosFactory = () => { const axiosFactory = () => {

View File

@ -6,11 +6,12 @@ import { NoNaviLayoutComponent } from '@app/layouts/components/no-navi.layout.co
import { AppAuthenticationGuard } from '@app/guards/app-authentication.guard'; import { AppAuthenticationGuard } from '@app/guards/app-authentication.guard';
import { AppSessionResolver } from './resolvers/app-session.resolver'; import { AppSessionResolver } from './resolvers/app-session.resolver';
import { NavigationType } from './types';
const routes: Routes = [ const routes: Routes = [
{ {
path: '', path: '',
redirectTo: '/group/(content:index)', redirectTo: `/${NavigationType.Group}/(content:index)`,
pathMatch: 'full' pathMatch: 'full'
}, },
{ {
@ -38,35 +39,35 @@ const routes: Routes = [
}, },
children: [ children: [
{ {
path: 'organization', path: NavigationType.Organization,
loadChildren: () => loadChildren: () =>
import('./pages/organization/organization.page.module').then( import('./pages/organization/organization.page.module').then(
(m) => m.AppOrganizationPageModule (m) => m.AppOrganizationPageModule
) )
}, },
{ {
path: 'group', path: NavigationType.Group,
loadChildren: () => loadChildren: () =>
import('./pages/group/group.page.module').then( import('./pages/group/group.page.module').then(
(m) => m.AppGroupPageModule (m) => m.AppGroupPageModule
) )
}, },
{ {
path: 'chat', path: NavigationType.Chat,
loadChildren: () => loadChildren: () =>
import('./pages/chat/chat.page.module').then( import('./pages/chat/chat.page.module').then(
(m) => m.AppChatPageModule (m) => m.AppChatPageModule
) )
}, },
{ {
path: 'call', path: NavigationType.Call,
loadChildren: () => loadChildren: () =>
import('./pages/call/call.page.module').then( import('./pages/call/call.page.module').then(
(m) => m.AppCallPageModule (m) => m.AppCallPageModule
) )
}, },
{ {
path: 'message', path: NavigationType.Message,
loadChildren: () => loadChildren: () =>
import('./pages/message/message.page.module').then( import('./pages/message/message.page.module').then(
(m) => m.AppMessagePageModule (m) => m.AppMessagePageModule

View File

@ -1,30 +1,44 @@
import { fromEvent, interval, Subject } from 'rxjs';
import { debounce, takeUntil } from 'rxjs/operators';
import { import {
Component, Component,
OnDestroy, OnDestroy,
OnInit, OnInit,
AfterViewInit, AfterViewInit,
Renderer2 Renderer2,
Inject
} from '@angular/core'; } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { Router } from '@angular/router';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { NativeService } from '@ucap/native';
import { UCAP_NATIVE_SERVICE } from '@ucap/ng-native';
import { AppActions } from '@app/store/actions'; import { AppActions } from '@app/store/actions';
import { fromEvent, interval, Subject } from 'rxjs';
import { debounce, takeUntil } from 'rxjs/operators';
import { AppAuthenticationService } from './services/app-authentication.service'; import { AppAuthenticationService } from './services/app-authentication.service';
import { AnimationBuilder, style, animate } from '@angular/animations';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'] styleUrls: ['./app.component.scss']
}) })
export class AppComponent implements OnInit, OnDestroy, AfterViewInit { export class AppComponent implements OnInit, OnDestroy {
private ngOnDestroySubject: Subject<void> = new Subject(); private ngOnDestroySubject: Subject<void> = new Subject();
constructor( constructor(
private router: Router,
private renderer2: Renderer2, private renderer2: Renderer2,
private store: Store<any>, private store: Store<any>,
private appAuthenticationService: AppAuthenticationService private appAuthenticationService: AppAuthenticationService,
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
@Inject(DOCUMENT) private _document: any,
private _animationBuilder: AnimationBuilder
) { ) {
fromEvent(window, 'resize') fromEvent(window, 'resize')
.pipe( .pipe(
@ -38,6 +52,12 @@ export class AppComponent implements OnInit, OnDestroy, AfterViewInit {
}); });
}); });
fromEvent(window, 'unload')
.pipe(takeUntil(this.ngOnDestroySubject))
.subscribe((event: any) => {
this.nativeService.app_postDestroy();
});
// fromEvent(window, 'beforeunload') // fromEvent(window, 'beforeunload')
// .pipe(takeUntil(this.ngOnDestroySubject)) // .pipe(takeUntil(this.ngOnDestroySubject))
// .subscribe((event: any) => { // .subscribe((event: any) => {
@ -50,6 +70,53 @@ export class AppComponent implements OnInit, OnDestroy, AfterViewInit {
width: window.innerWidth, width: window.innerWidth,
height: window.innerHeight height: window.innerHeight
}); });
this.nativeService.app_postInit().then((info) => {
if (!info) {
return;
}
if (!!info.initUrl) {
this.router.navigateByUrl(info.initUrl);
}
});
const splash = this._document.body.querySelector('#ucap-lg-web-splash');
const player = this._animationBuilder
.build([
style({ opacity: '1' }),
animate(
'400ms ease',
style({
opacity: '0',
zIndex: '-10'
})
)
])
.create(splash);
setTimeout(() => {
player.play();
}, 0);
// const preloader = this.renderer2.selectRootElement(
// '#ucap-lg-web-preloader'
// );
// const fadeEffect = setInterval(() => {
// if (!preloader.style.opacity) {
// this.renderer2.setStyle(preloader, 'opacity', 1);
// }
// if (preloader.style.opacity > 0) {
// this.renderer2.setStyle(
// preloader,
// 'opacity',
// preloader.style.opacity - 0.1
// );
// } else {
// this.renderer2.setStyle(preloader, 'display', 'none');
// clearInterval(fadeEffect);
// }
// }, 200);
// this.renderer2.setStyle(preloader, 'display', 'none');
} }
ngOnDestroy(): void { ngOnDestroy(): void {
@ -59,13 +126,6 @@ export class AppComponent implements OnInit, OnDestroy, AfterViewInit {
} }
} }
ngAfterViewInit(): void {
const preloader = this.renderer2.selectRootElement(
'#ucap-lg-web-preloader'
);
this.renderer2.setStyle(preloader, 'display', 'none');
}
private dispatchWindowSize(size: { width: number; height: number }) { private dispatchWindowSize(size: { width: number; height: number }) {
this.store.dispatch(AppActions.windowResized(size)); this.store.dispatch(AppActions.windowResized(size));
} }

View File

@ -15,8 +15,10 @@ import { LoggerModule } from '@ucap/ng-logger';
import { CommonApiModule } from '@ucap/ng-api-common'; import { CommonApiModule } from '@ucap/ng-api-common';
import { PublicApiModule } from '@ucap/ng-api-public'; import { PublicApiModule } from '@ucap/ng-api-public';
import { ExternalApiModule } from '@ucap/ng-api-external'; import { ExternalApiModule } from '@ucap/ng-api-external';
import { ContactApiModule } from '@ucap/ng-api-contact';
import { MessageApiModule } from '@ucap/ng-api-message'; import { MessageApiModule } from '@ucap/ng-api-message';
import { PromptApiModule } from '@ucap/ng-api-prompt'; import { PromptApiModule } from '@ucap/ng-api-prompt';
import { WebexApiModule } from '@ucap/ng-api-webex';
import { PiModule } from '@ucap/ng-pi'; import { PiModule } from '@ucap/ng-pi';
@ -46,6 +48,7 @@ import { OrganizationStoreModule } from '@ucap/ng-store-organization';
import { AuthenticationStoreModule } from '@ucap/ng-store-authentication'; import { AuthenticationStoreModule } from '@ucap/ng-store-authentication';
import { GroupStoreModule } from '@ucap/ng-store-group'; import { GroupStoreModule } from '@ucap/ng-store-group';
import { ChatStoreModule } from '@ucap/ng-store-chat'; import { ChatStoreModule } from '@ucap/ng-store-chat';
import { CallStoreModule } from '@ucap/ng-store-call';
import { OrganizationUiModule } from '@ucap/ng-ui-organization'; import { OrganizationUiModule } from '@ucap/ng-ui-organization';
@ -62,6 +65,7 @@ import { metaReducers } from '@app/store/state';
import { AppAccountDialogModule } from '@app/dialogs/account/account.dialog.module'; import { AppAccountDialogModule } from '@app/dialogs/account/account.dialog.module';
import { environment } from '@environments'; import { environment } from '@environments';
import { MatDialogModule } from '@angular/material/dialog';
@NgModule({ @NgModule({
declarations: [AppComponent], declarations: [AppComponent],
@ -121,13 +125,17 @@ import { environment } from '@environments';
*/ */
EffectsModule.forRoot([...effects]), EffectsModule.forRoot([...effects]),
MatDialogModule,
LoggerModule.forRoot({}), LoggerModule.forRoot({}),
CommonApiModule.forRoot(environment.commonApiModuleConfig), CommonApiModule.forRoot(environment.commonApiModuleConfig),
PublicApiModule.forRoot(environment.publicApiModuleConfig), PublicApiModule.forRoot(environment.publicApiModuleConfig),
ExternalApiModule.forRoot(environment.externalApiModuleConfig), ExternalApiModule.forRoot(environment.externalApiModuleConfig),
ContactApiModule.forRoot(environment.contactApiModuleConfig),
MessageApiModule.forRoot(environment.messageApiModuleConfig), MessageApiModule.forRoot(environment.messageApiModuleConfig),
PromptApiModule.forRoot(environment.promptApiModuleConfig), PromptApiModule.forRoot(environment.promptApiModuleConfig),
WebexApiModule.forRoot(environment.webexApiModuleConfig),
PiModule.forRoot(environment.piModuleConfig), PiModule.forRoot(environment.piModuleConfig),
@ -162,6 +170,10 @@ import { environment } from '@environments';
eventRequestDefaultCount: eventRequestDefaultCount:
environment.productConfig.chat.eventRequestDefaultCount environment.productConfig.chat.eventRequestDefaultCount
}), }),
CallStoreModule.forRoot({
historyRequestDefaultCount:
environment.productConfig.call.historyRequestDefaultCount
}),
OrganizationUiModule.forRoot({}), OrganizationUiModule.forRoot({}),

View File

@ -13,11 +13,12 @@ import { MatSelectModule } from '@angular/material/select';
import { MatTabsModule } from '@angular/material/tabs'; import { MatTabsModule } from '@angular/material/tabs';
import { I18nModule } from '@ucap/ng-i18n'; import { I18nModule } from '@ucap/ng-i18n';
import { UiModule } from '@ucap/ng-ui';
import { UiscrollingModule } from '@ucap/ng-ui/scrolling';
import { AppLayoutsModule } from '@app/layouts/layouts.module'; import { AppLayoutsModule } from '@app/layouts/layouts.module';
import { AppAccountSectionModule } from '@app/sections/account/account.section.module'; import { AppAccountSectionModule } from '@app/sections/account/account.section.module';
import { AppAuthenticationModule } from '@app/ucap/authentication/authentication.module';
import { COMPONENTS } from './components'; import { COMPONENTS } from './components';
@NgModule({ @NgModule({
@ -36,7 +37,9 @@ import { COMPONENTS } from './components';
I18nModule, I18nModule,
UiModule, UiscrollingModule,
AppAuthenticationModule,
AppLayoutsModule, AppLayoutsModule,
AppAccountSectionModule AppAccountSectionModule

View File

@ -30,7 +30,10 @@
<div class="settings-contents"> <div class="settings-contents">
<ul> <ul>
<li> <li>
<mat-checkbox> <mat-checkbox
[value]="settings.general.autoLaunch"
(change)="onChangeForAutoStartOnBoot($event)"
>
{{ {{
'authentication:login.settings.autoStartOnBoot' 'authentication:login.settings.autoStartOnBoot'
| ucapI18n | ucapI18n
@ -38,12 +41,18 @@
</mat-checkbox> </mat-checkbox>
</li> </li>
<li> <li>
<mat-checkbox> <mat-checkbox
[value]="settings.general.autoLogin"
(change)="onChangeForAutoLogin($event)"
>
{{ 'authentication:login.settings.autoLogin' | ucapI18n }} {{ 'authentication:login.settings.autoLogin' | ucapI18n }}
</mat-checkbox> </mat-checkbox>
</li> </li>
<li> <li>
<mat-checkbox> <mat-checkbox
[value]="settings.general.startupHideWindow"
(change)="onChangeForAutoHide($event)"
>
{{ 'authentication:login.settings.autoHide' | ucapI18n }} {{ 'authentication:login.settings.autoHide' | ucapI18n }}
</mat-checkbox> </mat-checkbox>
</li> </li>
@ -66,7 +75,7 @@
class="setting-select-obj ucap-mat-input-container" class="setting-select-obj ucap-mat-input-container"
> >
<mat-select <mat-select
[value]="generalSetting.locale" [value]="settings.general.locale"
(selectionChange)="onSelectionChangeLanguage($event)" (selectionChange)="onSelectionChangeLanguage($event)"
> >
<mat-option <mat-option
@ -95,7 +104,7 @@
class="setting-select-obj ucap-mat-input-container" class="setting-select-obj ucap-mat-input-container"
> >
<mat-select <mat-select
[value]="generalSetting.hrInfoLocale" [value]="settings.general.hrInfoLocale"
(selectionChange)="onSelectionChangeHrLanguage($event)" (selectionChange)="onSelectionChangeHrLanguage($event)"
> >
<mat-option <mat-option
@ -122,10 +131,13 @@
class="setting-select-obj ucap-mat-input-container" class="setting-select-obj ucap-mat-input-container"
> >
<mat-select <mat-select
[class.ucap-select-placeholder-value]="
!!timezonePlaceholder
"
#selectForTimezone #selectForTimezone
[formControl]="formControlForTimezone" [formControl]="formControlForTimezone"
[placeholder]="timezonePlaceholder" [placeholder]="timezonePlaceholder"
[value]="generalSetting.timezone" [value]="settings.general.timezone"
(openedChange)="onOpenedChangeTimezone($event)" (openedChange)="onOpenedChangeTimezone($event)"
> >
<ucap-virtual-scroll-viewport <ucap-virtual-scroll-viewport
@ -162,6 +174,8 @@
<mat-radio-group <mat-radio-group
aria-label="Select an type of alarm" aria-label="Select an type of alarm"
class="settings-radio-group" class="settings-radio-group"
[value]="String(settings.notification.use)"
(change)="onChangeForReceiveNotification($event)"
> >
<mat-radio-button value="true"> <mat-radio-button value="true">
{{ {{
@ -186,20 +200,25 @@
appearance="standard" appearance="standard"
class="setting-select-obj ucap-mat-input-container" class="setting-select-obj ucap-mat-input-container"
> >
<mat-select> <mat-select
<mat-option value="sound"> [value]="settings.notification.method"
(selectionChange)="
onSelectionChangeMethodOfNotification($event)
"
>
<mat-option [value]="NotificationMethod.Sound">
{{ {{
'organization:settings.notification.methodTypeSound' 'organization:settings.notification.methodTypeSound'
| ucapI18n | ucapI18n
}} }}
</mat-option> </mat-option>
<mat-option value="alert"> <mat-option [value]="NotificationMethod.Alert">
{{ {{
'organization:settings.notification.methodTypeAlert' 'organization:settings.notification.methodTypeAlert'
| ucapI18n | ucapI18n
}} }}
</mat-option> </mat-option>
<mat-option value="soundAndAlert"> <mat-option [value]="NotificationMethod.SoundAndAlert">
{{ {{
'organization:settings.notification.methodTypeSoundAndAlert' 'organization:settings.notification.methodTypeSoundAndAlert'
| ucapI18n | ucapI18n
@ -222,7 +241,12 @@
appearance="standard" appearance="standard"
class="setting-select-obj ucap-mat-input-container" class="setting-select-obj ucap-mat-input-container"
> >
<mat-select> <mat-select
[value]="String(settings.notification.alertExposureTime)"
(selectionChange)="
onSelectionChangeAlertExposureTimeOfNotification($event)
"
>
<mat-option value="5"> <mat-option value="5">
5{{ 'common:units.second' | ucapI18n }} 5{{ 'common:units.second' | ucapI18n }}
</mat-option> </mat-option>
@ -249,7 +273,12 @@
<div class="settings-contents"> <div class="settings-contents">
<ul> <ul>
<li> <li>
<mat-checkbox> <mat-checkbox
[value]="settings.notification.receiveForMessage"
(change)="
onChangeForReceiveForMessageOfNotification($event)
"
>
{{ {{
'organization:settings.notification.receiveForMessageTypePopup' 'organization:settings.notification.receiveForMessageTypePopup'
| ucapI18n | ucapI18n
@ -269,7 +298,7 @@
<!-- 대화 --> <!-- 대화 -->
<div class="messenger-settings-area"> <div class="messenger-settings-area">
<div class="title-settings-subject">파일 전송</div> <div class="title-settings-subject">파일 전송</div>
<div class="settings-contents02"> <div class="settings-contents02" *ngIf="'electron' === platform">
<div class="subtitle-settings-info"> <div class="subtitle-settings-info">
다운로드 폴더 다운로드 폴더
</div> </div>
@ -278,18 +307,12 @@
color="accent" color="accent"
class="setting-input-obj input-set-obj ucap-mat-input-container" class="setting-input-obj input-set-obj ucap-mat-input-container"
> >
<input <input matInput placeholder="" value="" />
matInput
placeholder=""
value=""
[readonly]="'browser' === platform"
/>
<button <button
mat-button mat-button
matSuffix matSuffix
mat-icon-button mat-icon-button
aria-label="file" aria-label="file"
[disabled]="'browser' === platform"
> >
<mat-icon>folder</mat-icon> <mat-icon>folder</mat-icon>
</button> </button>
@ -299,7 +322,6 @@
mat-stroked-button mat-stroked-button
color="primary" color="primary"
class="btn-folder-first" class="btn-folder-first"
[disabled]="'browser' === platform"
> >
폴더 초기화 폴더 초기화
</button> </button>
@ -338,123 +360,7 @@
</ng-template> </ng-template>
<div class="secret-num-settings-area"> <div class="secret-num-settings-area">
<!-- 비밀번호 --> <!-- 비밀번호 -->
<div class="messenger-settings-area"> <app-authentication-change-password></app-authentication-change-password>
<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> </div>
</mat-tab> </mat-tab>
</mat-tab-group> </mat-tab-group>

View File

@ -12,10 +12,10 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
padding: 10px 16px 9px; padding: 10px 16px;
&:first-of-type { &:first-of-type {
border-top: 0; border-top: 0;
padding: 20px 16px 9px; padding: 20px 16px 10px;
} }
.title-settings-subject { .title-settings-subject {
color: #5c444b; color: #5c444b;
@ -52,11 +52,11 @@
.settings-contents { .settings-contents {
ul { ul {
li { li {
padding: 6px 0 7px; padding: 6px 0;
} }
} }
.settings-radio-group { .settings-radio-group {
padding: 6px 0 7px; padding: 6px 0;
height: 42px; height: 42px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;

View File

@ -13,17 +13,27 @@ import {
Inject, Inject,
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import { FormControl } from '@angular/forms';
import { import {
MatDialogRef, MatDialogRef,
MAT_DIALOG_DATA, MAT_DIALOG_DATA,
MatDialog MatDialog
} from '@angular/material/dialog'; } from '@angular/material/dialog';
import { MatOptionSelectionChange } from '@angular/material/core';
import { MatSelectChange, MatSelect } from '@angular/material/select'; import { MatSelectChange, MatSelect } from '@angular/material/select';
import { MatCheckboxChange } from '@angular/material/checkbox';
import { MatRadioChange } from '@angular/material/radio';
import { ObjectUtil } from '@ucap/core';
import { NotificationMethod } from '@ucap/domain-common';
import { NativeService, NativeType } from '@ucap/native'; import { NativeService, NativeType } from '@ucap/native';
import { UCAP_NATIVE_SERVICE } from '@ucap/ng-native'; import { UCAP_NATIVE_SERVICE } from '@ucap/ng-native';
import { I18nService } from '@ucap/ng-i18n';
import { DateService } from '@ucap/ng-ui/date';
import { VirtualScrollViewportComponent } from '@ucap/ng-ui/scrolling';
import { TranslateService } from '@ucap/ng-ui-organization';
import { environment } from '@environments'; import { environment } from '@environments';
@ -34,10 +44,6 @@ import {
ChatSetting, ChatSetting,
PresenceSetting PresenceSetting
} from '@app/models/settings'; } 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 { export interface TimezoneData {
displayName: string; displayName: string;
@ -47,7 +53,9 @@ export interface TimezoneData {
export interface SettingsDialogData { export interface SettingsDialogData {
settings: Settings; settings: Settings;
} }
export interface SettingsDialogResult {} export interface SettingsDialogResult {
settings: Settings;
}
@Component({ @Component({
selector: 'app-sections-account-settings', selector: 'app-sections-account-settings',
@ -66,10 +74,7 @@ export class SettingsDialogComponent implements OnInit, OnDestroy {
platform: 'browser' | 'electron' = 'electron'; platform: 'browser' | 'electron' = 'electron';
generalSetting: GeneralSetting; settings: Settings;
notificationSetting: NotificationSetting;
chatSetting: ChatSetting;
presenceSetting: PresenceSetting;
timezoneList: TimezoneData[]; timezoneList: TimezoneData[];
timezonePlaceholder: string; timezonePlaceholder: string;
@ -78,15 +83,24 @@ export class SettingsDialogComponent implements OnInit, OnDestroy {
supportedHrLanguages = supportedHrLanguages =
environment.productConfig.organization.supportedLanguages; environment.productConfig.organization.supportedLanguages;
String = String;
NotificationMethod = NotificationMethod;
private ngOnDestroySubject: Subject<void> = new Subject();
constructor( constructor(
public dialogRef: MatDialogRef<SettingsDialogData, SettingsDialogResult>, public dialogRef: MatDialogRef<SettingsDialogData, SettingsDialogResult>,
@Inject(MAT_DIALOG_DATA) public data: SettingsDialogData, @Inject(MAT_DIALOG_DATA) public data: SettingsDialogData,
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService, @Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
private dateService: DateService,
private i18nService: I18nService, private i18nService: I18nService,
private translateService: TranslateService,
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
public matDialog: MatDialog public matDialog: MatDialog
) { ) {
this.nativeService.platform_nativeType().then((type) => { this.nativeService.platform_nativeType().then((type) => {
// this.platform = 'electron';
// return;
switch (type) { switch (type) {
case NativeType.Browser: case NativeType.Browser:
this.platform = 'browser'; this.platform = 'browser';
@ -99,14 +113,9 @@ export class SettingsDialogComponent implements OnInit, OnDestroy {
} }
}); });
this.generalSetting = data.settings.general; this.settings = ObjectUtil.deepClone(data.settings);
this.notificationSetting = data.settings.notification;
this.chatSetting = data.settings.chat;
this.presenceSetting = data.settings.presence;
} }
private ngOnDestroySubject: Subject<void> = new Subject();
ngOnInit(): void { ngOnInit(): void {
this.generateTimezoneData(); this.generateTimezoneData();
@ -124,9 +133,27 @@ export class SettingsDialogComponent implements OnInit, OnDestroy {
} }
} }
onSelectionChangeLanguage(event: MatSelectChange) {} onChangeForAutoStartOnBoot(event: MatCheckboxChange) {
this.settings.general.autoLaunch = event.checked;
}
onSelectionChangeHrLanguage(event: MatSelectChange) {} onChangeForAutoLogin(event: MatCheckboxChange) {
this.settings.general.autoLogin = event.checked;
}
onChangeForAutoHide(event: MatCheckboxChange) {
this.settings.general.startupHideWindow = event.checked;
}
onSelectionChangeLanguage(event: MatSelectChange) {
this.i18nService.changeLanguage(event.value);
this.settings.general.locale = event.value;
}
onSelectionChangeHrLanguage(event: MatSelectChange) {
this.translateService.use(event.value);
this.settings.general.hrInfoLocale = event.value;
}
onOpenedChangeTimezone(opened: boolean) { onOpenedChangeTimezone(opened: boolean) {
if (opened) { if (opened) {
@ -139,15 +166,41 @@ export class SettingsDialogComponent implements OnInit, OnDestroy {
if (!event.isUserInput) { if (!event.isUserInput) {
return; return;
} }
this.dateService.use(event.source.value);
this.settings.general.timezone = event.source.value;
}
onChangeForReceiveNotification(event: MatRadioChange) {
const use = 'true' === event.value;
console.log('onChangeForReceiveNotification', use);
}
onSelectionChangeMethodOfNotification(event: MatSelectChange) {
console.log('onSelectionChangeMethodOfNotification', event.value);
}
onSelectionChangeAlertExposureTimeOfNotification(event: MatSelectChange) {
const v = Number(event.value);
console.log('onSelectionChangeAlertExposureTimeOfNotification', v);
}
onChangeForReceiveForMessageOfNotification(event: MatCheckboxChange) {
console.log('onChangeForReceiveForMessageOfNotification', event.checked);
} }
onClosed(event: MouseEvent): void { onClosed(event: MouseEvent): void {
this.dialogRef.close(); this.dialogRef.close({ settings: this.data.settings });
} }
onCancel() {} onCancel() {
this.dialogRef.close({ settings: this.data.settings });
}
onConfirm() {} onConfirm() {
this.dialogRef.close({
settings: this.settings
});
}
private generateTimezoneData() { private generateTimezoneData() {
const timezoneData = this.i18nService.t('locale:timezone', { const timezoneData = this.i18nService.t('locale:timezone', {
@ -159,11 +212,14 @@ export class SettingsDialogComponent implements OnInit, OnDestroy {
const displayName = `(UTC${moment.tz(name).format('Z')}) ${ const displayName = `(UTC${moment.tz(name).format('Z')}) ${
timezoneData[name] timezoneData[name]
}`; }`;
if (-1 < displayName.indexOf('undefined')) {
console.log('timezone', name);
}
timezoneList.push({ timezoneList.push({
displayName, displayName,
name name
}); });
if (name === this.generalSetting.timezone) { if (name === this.settings.general.timezone) {
this.timezonePlaceholder = displayName; this.timezonePlaceholder = displayName;
} }
} }
@ -176,12 +232,12 @@ export class SettingsDialogComponent implements OnInit, OnDestroy {
private setTimezoneData() { private setTimezoneData() {
const timezoneIndex = this.timezoneList.findIndex( const timezoneIndex = this.timezoneList.findIndex(
(t) => t.name === this.generalSetting.timezone (t) => t.name === this.settings.general.timezone
); );
if (-1 !== timezoneIndex) { if (-1 !== timezoneIndex) {
if (!!this.vsTimezone && !!this.selectForTimezone) { if (!!this.vsTimezone && !!this.selectForTimezone) {
this.vsTimezone.scrollToIndex(timezoneIndex); this.vsTimezone.scrollToIndex(timezoneIndex, 'start');
this.selectForTimezone.value = this.timezoneList[timezoneIndex].name; this.selectForTimezone.value = this.timezoneList[timezoneIndex].name;
} }
} }

View File

@ -1,6 +1,6 @@
<div class="layout-container" fxLayout="column"> <div class="layout-container" fxLayout="column">
<div class="layout-header" fxFlex="50px" fxLayout="row"> <div class="layout-header" fxFlex="50px" fxLayout="row">
<div fxFlex="1 1 auto"> <div fxFlex="1 1 auto" class="layout-header-container">
<ng-content <ng-content
class="layout-header-content" class="layout-header-content"
select="[appLayoutsDefaultDialog='header']" select="[appLayoutsDefaultDialog='header']"

View File

@ -10,6 +10,11 @@
font-size: 1.143em; font-size: 1.143em;
border-bottom: 1px solid #666; border-bottom: 1px solid #666;
margin: 0 16px; margin: 0 16px;
&-container {
flex: 1 1 auto;
box-sizing: border-box;
width: calc(100% - 30px);
}
.layout-header-content { .layout-header-content {
width: 100%; width: 100%;
height: 100%; height: 100%;

View File

@ -1,5 +1,9 @@
<div class="layout-container" fxLayout="row"> <div
<div class="navitab-page" fxFlex="0 0 60px"> class="layout-container"
fxLayout="row"
[ngClass]="'electron==' ? 'electron' : ''"
>
<div class="navitab-page">
<div class="gnb"> <div class="gnb">
<mat-toolbar class="mat-gnb-toolbar" <mat-toolbar class="mat-gnb-toolbar"
><img ><img
@ -16,7 +20,7 @@
class="global-menu" class="global-menu"
(selectedTabChange)="onSelectedTabChange($event)" (selectedTabChange)="onSelectedTabChange($event)"
> >
<mat-tab aria-label="Group"> <mat-tab [aria-label]="NavigationType.Group">
<ng-template mat-tab-label> <ng-template mat-tab-label>
<div <div
class="icon-item" class="icon-item"
@ -65,7 +69,7 @@
</div> </div>
</ng-template> </ng-template>
</mat-tab> </mat-tab>
<mat-tab aria-label="Chat"> <mat-tab [aria-label]="NavigationType.Chat">
<ng-template mat-tab-label> <ng-template mat-tab-label>
<div <div
class="icon-item" class="icon-item"
@ -102,7 +106,7 @@
</div> </div>
</ng-template> </ng-template>
</mat-tab> </mat-tab>
<mat-tab aria-label="Organization"> <mat-tab [aria-label]="NavigationType.Organization">
<ng-template mat-tab-label> <ng-template mat-tab-label>
<div <div
class="icon-item" class="icon-item"
@ -158,7 +162,7 @@
</ng-template> </ng-template>
</mat-tab> </mat-tab>
<mat-tab aria-label="Message"> <!-- <mat-tab [aria-label]="NavigationType.Message">
<ng-template mat-tab-label> <ng-template mat-tab-label>
<div <div
class="icon-item" class="icon-item"
@ -195,9 +199,9 @@
</svg> </svg>
</div> </div>
</ng-template> </ng-template>
</mat-tab> </mat-tab> -->
<mat-tab aria-label="Call"> <mat-tab [aria-label]="NavigationType.Call">
<ng-template mat-tab-label> <ng-template mat-tab-label>
<div <div
class="icon-item" class="icon-item"
@ -247,6 +251,7 @@
mode="side" mode="side"
opened="true" opened="true"
[disableClose]="true" [disableClose]="true"
(openedChange)="onOpenStart($event)"
> >
<ucap-float-action-button <ucap-float-action-button
*ngIf="fabButtonShow" *ngIf="fabButtonShow"
@ -254,7 +259,10 @@
[useCustomDefaultIcon]="fabUseCustomDefaultIcon" [useCustomDefaultIcon]="fabUseCustomDefaultIcon"
(buttonClick)="onClickFab($event)" (buttonClick)="onClickFab($event)"
> >
<div *ngIf="tabIndex === 'group'" ucapFloatActionButton="mainIcon"> <div
*ngIf="curNavi === NavigationType.Group"
ucapFloatActionButton="mainIcon"
>
<mat-icon class="ico-font-float"> <mat-icon class="ico-font-float">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -303,7 +311,10 @@
</svg> </svg>
</mat-icon> </mat-icon>
</div> </div>
<div *ngIf="tabIndex === 'chat'" ucapFloatActionButton="mainIcon"> <div
*ngIf="curNavi === NavigationType.Chat"
ucapFloatActionButton="mainIcon"
>
<mat-icon class="ico-font-float"> <mat-icon class="ico-font-float">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -341,7 +352,7 @@
</mat-icon> </mat-icon>
</div> </div>
<div <div
*ngIf="tabIndex === 'message'" *ngIf="curNavi === NavigationType.Message"
ucapFloatActionButton="mainIcon" ucapFloatActionButton="mainIcon"
> >
<mat-icon class="ico-font-float"> <mat-icon class="ico-font-float">
@ -381,7 +392,10 @@
</svg> </svg>
</mat-icon> </mat-icon>
</div> </div>
<div *ngIf="tabIndex === 'call'" ucapFloatActionButton="mainIcon"> <div
*ngIf="curNavi === NavigationType.Call"
ucapFloatActionButton="mainIcon"
>
<mat-icon class="ico-font-dialpad">dialpad</mat-icon> <mat-icon class="ico-font-dialpad">dialpad</mat-icon>
</div> </div>
</ucap-float-action-button> </ucap-float-action-button>
@ -400,9 +414,11 @@
<div class="content-sidenav-top-bar" fxFlex="0 0 40px"> <div class="content-sidenav-top-bar" fxFlex="0 0 40px">
<app-layouts-top-bar> <app-layouts-top-bar>
<div class="content-sidenav-top-bar-content"> <div class="content-sidenav-top-bar-content">
<div class="toolbar-info-area date-info"> <div class="toolbar-info-area date-info toolbar-drag-area">
<div class="today">
<span>Today</span>{{ moment().format('YYYY.MM.DD') }} <span>Today</span>{{ moment().format('YYYY.MM.DD') }}
</div> </div>
</div>
<div class="toolbar-info-area toolbar-ctrl"> <div class="toolbar-info-area toolbar-ctrl">
<!--Search--> <!--Search-->
<div class="topbar-search"> <div class="topbar-search">
@ -418,15 +434,12 @@
<!--My Profile --> <!--My Profile -->
<div <div
class="my-profile" class="my-profile"
matTooltip="프로필 버튼"
matTooltipPosition="below"
matTooltipHideDelay="1000"
[matMenuTriggerFor]="profileMenu" [matMenuTriggerFor]="profileMenu"
#profileMenuTrigger="matMenuTrigger" #profileMenuTrigger="matMenuTrigger"
> >
<app-organization-profile-image-01 <app-organization-profile-image-01
[userInfo]="user.info" [userInfo]="user.info"
[versionInfo]="versionInfo2Res" [versionInfo]="versionInfo"
(openProfile)="onOpenProfile($event)" (openProfile)="onOpenProfile($event)"
></app-organization-profile-image-01> ></app-organization-profile-image-01>
</div> </div>
@ -445,7 +458,9 @@
<!--Footer--> <!--Footer-->
<div class="footer"> <div class="footer">
<div class="foot-info version-info"> <div class="foot-info version-info">
<span class="var-txt current-ver">현재버전 0.0.11</span> <span class="var-txt current-ver" (click)="onClickForOpenRoom()"
>현재버전 0.0.11</span
>
<span class="var-txt new-var">최신버전 0.0.11 </span> <span class="var-txt new-var">최신버전 0.0.11 </span>
<button mat-icon-button aria-label="icon"> <button mat-icon-button aria-label="icon">
<mat-icon>get_app</mat-icon> <mat-icon>get_app</mat-icon>

View File

@ -8,13 +8,23 @@
.layout-container { .layout-container {
width: 100%; width: 100%;
height: 100%; height: 100%;
&.electron {
border: 1px solid #aaa;
}
.navitab-page { .navitab-page {
//GNB ///////////////////////////////////// //GNB /////////////////////////////////////
display: flex;
flex: 0 0 auto;
width: 60px;
@include screen(xs) {
width: 60px;
}
.gnb { .gnb {
//background-color: $gray-ref0; //background-color: $gray-ref0;
background-color: #f1f2f6; background-color: #f1f2f6;
width: 60px; width: 100%;
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -25,7 +35,7 @@
flex-basis: 40px; flex-basis: 40px;
padding: 2px 12px 10px; padding: 2px 12px 10px;
.img-logo { .img-logo {
margin: 6px 0 0 1px; margin: 10px 0 0 1px;
} }
} }
.left-container { .left-container {
@ -62,7 +72,7 @@
content: ''; content: '';
width: 30px; width: 30px;
height: 30px; height: 30px;
background-image: url(../../../assets/images/ico/btn_gnb_hompage.svg); background-image: url(/assets/images/ico/btn_gnb_hompage.svg);
background-size: 30px; background-size: 30px;
display: block; display: block;
position: absolute; position: absolute;
@ -112,6 +122,9 @@
display: flex; display: flex;
align-items: center; align-items: center;
padding: 0 16px; padding: 0 16px;
-webkit-app-region: drag;
-webkit-touch-callout: none;
-webkit-user-select: none;
} }
} }
} }
@ -134,18 +147,28 @@
justify-content: space-between; justify-content: space-between;
.toolbar-info-area { .toolbar-info-area {
display: flex; display: flex;
flex-grow: 1;
align-items: center; align-items: center;
&.toolbar-drag-area {
-webkit-app-region: drag;
-webkit-touch-callout: none;
-webkit-user-select: none;
}
&.date-info { &.date-info {
@include font-family($font-light); @include font-family($font-light);
font-weight: 600; font-weight: 600;
font-size: 12px; font-size: 12px;
color: $gray-re70; color: $gray-re70;
display: flex;
flex: 1 1 auto;
padding-left: 30px; padding-left: 30px;
height: 100%;
@include screen(mid) { @include screen(mid) {
padding-left: 16px; padding-left: 16px;
display: none;
} }
.today {
display: flex;
flex-direction: row;
align-items: center;
span { span {
width: 54px; width: 54px;
height: 16px; height: 16px;
@ -160,10 +183,17 @@
color: $lipstick; color: $lipstick;
margin-right: 8px; margin-right: 8px;
} }
@include screen(mid) {
display: none;
}
}
} }
&.toolbar-ctrl { &.toolbar-ctrl {
flex-flow: row-reverse; flex-flow: row-reverse;
margin-left: auto;
.topbar-search { .topbar-search {
//개발예정으로 개발후 오픈
display: none;
order: 2; order: 2;
margin-right: 8px; margin-right: 8px;
.ico-search-icon { .ico-search-icon {
@ -175,8 +205,8 @@
} }
} }
.my-profile { .my-profile {
height: 30px; height: 28px;
width: 30px; width: 28px;
margin-right: 20px; margin-right: 20px;
order: 1; order: 1;
//profile ///////////// //profile /////////////
@ -239,6 +269,7 @@
@include font-family($font-light); @include font-family($font-light);
font-weight: 600; font-weight: 600;
&.version-info { &.version-info {
display: none;
.var-txt { .var-txt {
padding-left: 8px; padding-left: 8px;
color: $gray-re70; color: $gray-re70;

View File

@ -3,7 +3,7 @@ import moment from 'moment';
import { Subject, of } from 'rxjs'; import { Subject, of } from 'rxjs';
import { takeUntil, filter, take, map, catchError } from 'rxjs/operators'; import { takeUntil, filter, take, map, catchError } from 'rxjs/operators';
import { Component, ViewChild, OnDestroy, OnInit } from '@angular/core'; import { Component, ViewChild, OnDestroy, OnInit, Inject } from '@angular/core';
import { import {
Router, Router,
RouterEvent, RouterEvent,
@ -20,23 +20,43 @@ import { MatMenuTrigger } from '@angular/material/menu';
import { MatTabChangeEvent, MatTabGroup } from '@angular/material/tabs'; import { MatTabChangeEvent, MatTabGroup } from '@angular/material/tabs';
import { MatSidenav } from '@angular/material/sidenav'; import { MatSidenav } from '@angular/material/sidenav';
import { VersionInfo2Response } from '@ucap/api-public'; import { User, UserInfoSS } from '@ucap/domain-organization';
import { UserInfoSS } from '@ucap/protocol-query';
import { LogService } from '@ucap/ng-logger'; import { LogService } from '@ucap/ng-logger';
import { UserSelector } from '@ucap/ng-store-organization'; import { UserSelector } from '@ucap/ng-store-organization';
import { ConfigurationSelector } from '@ucap/ng-store-authentication'; import { ConfigurationSelector } from '@ucap/ng-store-authentication';
import { RoomSelector } from '@ucap/ng-store-chat'; 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 { 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 { AppAccountService } from '@app/services/app-account.service';
import { AppCallService } from '@app/services/app-call.service';
import { AppUiService } from '@app/services/app-ui.service';
import { QueryParams as OrganizationParams } from '@app/pages/organization/types/params.type'; import { QueryParams as OrganizationParams } from '@app/pages/organization/types/params.type';
import { User } from '@ucap/protocol-info'; import { AddUserDialogComponent } from '@app/sections/group/dialogs/add-user.dialog.component';
import {
CreateDialogComponent as ChatCreateDialogComponent,
CreateDialogData as ChatCreateDialogData,
CreateDialogResult as ChatCreateDialogResult
} from '@app/sections/chat/dialogs/create.dialog.component';
import { UCAP_NATIVE_SERVICE } from '@ucap/ng-native';
import { NativeService } from '@ucap/native';
import { AppActions } from '@app/store/actions';
import { NavigationType } from '@app/types';
import { I18nService } from '@ucap/ng-i18n';
import {
DialpadDialogComponent,
DialpadDialogData,
DialpadDialogResult
} from '@app/sections/call/dialogs/dialpad.dialog.component';
import { VersionInfo } from '@ucap/domain-authentication';
const NAVS = ['group', 'chat', 'organization', 'message']; const NAVS = [
NavigationType.Group,
NavigationType.Chat,
NavigationType.Organization,
NavigationType.Message,
NavigationType.Call
];
@Component({ @Component({
selector: 'app-layouts-default', selector: 'app-layouts-default',
@ -48,16 +68,21 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
navTabGroup: MatTabGroup; navTabGroup: MatTabGroup;
@ViewChild('leftSidenav', { static: true }) @ViewChild('leftSidenav', { static: true })
leftSidenav: MatSidenav; set leftSidenav(leftSidenav: MatSidenav) {
this._leftSidenav = leftSidenav;
isShowLeftSideNav = false; this.appUiService.leftSidenav = leftSidenav;
}
get leftSidenav(): MatSidenav {
return this._leftSidenav;
}
// tslint:disable-next-line: variable-name
_leftSidenav: MatSidenav;
@ViewChild('profileMenuTrigger', { static: true }) @ViewChild('profileMenuTrigger', { static: true })
profileMenuTrigger: MatMenuTrigger; profileMenuTrigger: MatMenuTrigger;
showStatusbar = true; showStatusbar = true;
tabIndex: string;
queryParams: Params; queryParams: Params;
unreadCountChat = 0; unreadCountChat = 0;
@ -66,23 +91,30 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
fabButtonShow = true; fabButtonShow = true;
fabUseCustomDefaultIcon = true; // default in this prj fabUseCustomDefaultIcon = true; // default in this prj
fabButtons: { icon: string; tooltip?: string; divisionType?: string }[]; fabButtons: { icon: string; tooltip?: string; divisionType?: string }[];
versionInfo2Res: VersionInfo2Response; versionInfo: VersionInfo;
user: User; user: User;
/** Navigation */
NavigationType = NavigationType;
initMenu = NavigationType.Group;
curNavi: NavigationType;
moment = moment; moment = moment;
private ngOnDestroySubject: Subject<void> = new Subject(); private ngOnDestroySubject: Subject<void> = new Subject();
constructor( constructor(
private router: Router, private router: Router,
private i18nSevice: I18nService,
private activatedRoute: ActivatedRoute, private activatedRoute: ActivatedRoute,
private store: Store<any>, private store: Store<any>,
private appAccountService: AppAccountService, private appAccountService: AppAccountService,
private appChatService: AppChatService, private appCallService: AppCallService,
private appUiService: AppUiService,
private logService: LogService, private logService: LogService,
public dialog: MatDialog public dialog: MatDialog,
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService
) { ) {
this.setFabInitial(NAVS[0]); this.setFabInitial(this.initMenu);
this.router.events this.router.events
.pipe( .pipe(
takeUntil(this.ngOnDestroySubject), takeUntil(this.ngOnDestroySubject),
@ -97,7 +129,7 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
if (!p || !p.segments || 0 === p.segments.length) { if (!p || !p.segments || 0 === p.segments.length) {
break; break;
} }
const index = p.segments[0].path; const index = p.segments[0].path as NavigationType;
this.setTabGroup(index); this.setTabGroup(index);
this.setFabInitial(index); this.setFabInitial(index);
} }
@ -116,32 +148,14 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
this.store this.store
.pipe( .pipe(
takeUntil(this.ngOnDestroySubject), takeUntil(this.ngOnDestroySubject),
select(ConfigurationSelector.versionInfo2Response) select(ConfigurationSelector.versionInfo)
) )
.subscribe((versionInfo2Res) => { .subscribe((versionInfo) => {
this.versionInfo2Res = versionInfo2Res; this.versionInfo = versionInfo;
}); });
} }
ngOnInit(): void { ngOnInit(): void {
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.store this.store
.pipe(takeUntil(this.ngOnDestroySubject), select(UserSelector.user)) .pipe(takeUntil(this.ngOnDestroySubject), select(UserSelector.user))
.subscribe((user) => { .subscribe((user) => {
@ -156,6 +170,31 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
.subscribe((unreadTotal) => { .subscribe((unreadTotal) => {
this.unreadCountChat = unreadTotal; this.unreadCountChat = unreadTotal;
}); });
// this.store
// .pipe(
// takeUntil(this.ngOnDestroySubject),
// select(PresenceSelector.selectAllStatusBulkInfo)
// )
// .subscribe((allBulkInfo) => {
// // .includes(String(this.user.info.seq))
// if (!allBulkInfo || (!!allBulkInfo && allBulkInfo.length === 0)) {
// return;
// }
// const userStatusinfo = allBulkInfo.filter(
// (bulkInfo) => bulkInfo.userSeq === String(this.user.info.seq)
// )[0];
// const status = PresenceUtil.isOnline(userStatusinfo, PresenceType.PC);
// if (!status && userStatusinfo.pcStatus === StatusCode.Offline) {
// this.store.dispatch(
// PresenceActions.bulkInfo({
// divCd: 'myBulk',
// userSeqs: [String(this.user.info.seq)]
// })
// );
// }
// });
} }
ngOnDestroy(): void { ngOnDestroy(): void {
@ -170,14 +209,13 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
} }
onSelectedTabChange(event: MatTabChangeEvent) { onSelectedTabChange(event: MatTabChangeEvent) {
const commands: any = [ const naviMenu: NavigationType = event.tab.ariaLabel as NavigationType;
NAVS[event.index], const commands: any = [naviMenu, { outlets: { content: 'index' } }];
{ outlets: { content: 'index' } }
];
const orgInitialParams: Params = {}; const orgInitialParams: Params = {};
// CASE :: Chat.
if ( if (
event.index === 1 && // is chat. naviMenu === NavigationType.Chat && // is chat.
!!this.queryParams && !!this.queryParams &&
!!this.queryParams[ChatQueryParams.ROOM_ID] !!this.queryParams[ChatQueryParams.ROOM_ID]
) { ) {
@ -185,55 +223,45 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
this.queryParams = undefined; this.queryParams = undefined;
return; return;
} }
// if (!!this.tabIndex && this.tabIndex === 'chat') {
// if (!!this.queryParams && !!this.queryParams[ChatQueryParams.ROOM_ID]) {
// return;
// } else {
// } // CASE :: Organization.
// } else { if (naviMenu === NavigationType.Organization && !!this.user) {
// }
if (event.index === 2 && !!this.user) {
orgInitialParams[OrganizationParams.DEPT_SEQ] = String( orgInitialParams[OrganizationParams.DEPT_SEQ] = String(
this.user.departmentCode this.user.departmentCode
); );
} }
this.router.navigate(commands, { queryParams: orgInitialParams }); this.router.navigate(commands, { queryParams: orgInitialParams });
if (!this.isShowLeftSideNav) { this.appUiService.openLeftSidenavOnNarrowMode();
this.leftSidenav.open(); this.setFabInitial(naviMenu);
}
this.setFabInitial(NAVS[event.index]);
} }
onClickToggleLeftSidenav() { onClickToggleLeftSidenav() {
if (!this.isShowLeftSideNav) { this.appUiService.toggleLeftSidenavOnNarrowMode();
this.leftSidenav.toggle();
}
} }
setFabInitial(type: string) { setFabInitial(type: NavigationType) {
this.tabIndex = type; this.curNavi = type;
switch (type) { switch (type) {
case 'group': case NavigationType.Group:
{ {
this.fabButtonShow = true; this.fabButtonShow = true;
this.fabButtons = [ this.fabButtons = [
{ {
icon: 'add', icon: 'add',
tooltip: '그룹 추가', tooltip: this.i18nSevice.t('group:dialog.title.addNewBuddy'),
divisionType: 'GROUP_NEW_ADD' divisionType: 'GROUP_NEW_ADD'
} }
]; ];
} }
break; break;
case 'chat': case NavigationType.Chat:
{ {
this.fabButtonShow = true; this.fabButtonShow = true;
this.fabButtons = [ this.fabButtons = [
{ {
icon: 'chat', icon: 'chat',
tooltip: '대화 추가', tooltip: this.i18nSevice.t('chat:dialog.title.newChatRoom'),
divisionType: 'CAHT_NEW_ADD' divisionType: 'CAHT_NEW_ADD'
} }
]; ];
@ -247,12 +275,12 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
// } // }
} }
break; break;
case 'organization': case NavigationType.Organization:
{ {
this.fabButtonShow = false; this.fabButtonShow = false;
} }
break; break;
case 'message': case NavigationType.Message:
{ {
this.fabButtonShow = true; this.fabButtonShow = true;
this.fabButtons = [ this.fabButtons = [
@ -264,11 +292,18 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
]; ];
} }
break; break;
// case MainMenu.Call: case NavigationType.Call:
// { {
// this.fabButtonShow = false; this.fabButtonShow = true;
// } this.fabButtons = [
// break; {
icon: 'dialpad',
tooltip: this.i18nSevice.t('call:label.dialpad'),
divisionType: 'OPEN_DIALPAD'
}
];
}
break;
default: { default: {
this.fabButtonShow = false; this.fabButtonShow = false;
@ -286,10 +321,14 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
switch (btn.divisionType) { switch (btn.divisionType) {
case 'GROUP_NEW_ADD': case 'GROUP_NEW_ADD':
{ {
const dialogRef = this.dialog.open(CreateDialogComponent, { const dialogRef = this.dialog.open(AddUserDialogComponent, {
panelClass: 'max-create-dialog' panelClass: 'max-create-dialog'
}); });
dialogRef
.afterOpened()
.pipe(take(1))
.subscribe(() => dialogRef.componentInstance.psUpdate());
dialogRef dialogRef
.afterClosed() .afterClosed()
.pipe( .pipe(
@ -304,7 +343,14 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
break; break;
case 'CAHT_NEW_ADD': case 'CAHT_NEW_ADD':
{ {
this.appChatService.newOpenRoomDialog(); const dialogRef = this.dialog.open<
ChatCreateDialogComponent,
ChatCreateDialogData,
ChatCreateDialogResult
>(ChatCreateDialogComponent, {
panelClass: 'max-create-dialog',
data: {}
});
} }
break; break;
case 'CHAT_NEW_TIMER_ADD': case 'CHAT_NEW_TIMER_ADD':
@ -320,6 +366,19 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
this.logService.debug('MESSAGE_NEW'); this.logService.debug('MESSAGE_NEW');
} }
break; break;
case 'OPEN_DIALPAD':
{
this.dialog.open<
DialpadDialogComponent,
DialpadDialogData,
DialpadDialogResult
>(DialpadDialogComponent, {
panelClass: 'mid-create-dialog',
data: {}
});
}
break;
} }
} }
@ -350,9 +409,21 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
this.profileMenuTrigger.closeMenu(); this.profileMenuTrigger.closeMenu();
} }
onClickForOpenRoom() {
this.nativeService.chat_openRoom('/chat/chatroom?roomId=1000');
}
private setTabGroup(url: string) { private setTabGroup(url: string) {
if (!!this.navTabGroup) { if (!!this.navTabGroup) {
this.navTabGroup.selectedIndex = NAVS.findIndex((v) => url === v); this.navTabGroup.selectedIndex = NAVS.findIndex((v) => url === v);
} }
} }
onOpenStart(opened: boolean) {
this.store.dispatch(
AppActions.openedLiftSideNav({
opened
})
);
}
} }

View File

@ -1,6 +1,6 @@
<div class="layout-container" fxFlexFill fxLayout="column"> <div class="layout-container" fxFlexFill fxLayout="column">
<div *ngIf="showTopbar" class="top-bar" fxFlex="50px"> <div *ngIf="showTopbar" class="top-bar" fxFlex="50px">
<app-layouts-top-bar></app-layouts-top-bar> <app-layouts-top-bar class="no-navi-top-bar"></app-layouts-top-bar>
</div> </div>
<div class="layout-content" fxFlex="1 1 auto"> <div class="layout-content" fxFlex="1 1 auto">
<router-outlet></router-outlet> <router-outlet></router-outlet>

View File

@ -1,9 +1,10 @@
<div class="title-bar"> <div class="title-bar">
<ucap-title-bar <ucap-title-bar
[platform]="platform" [platform]="platform"
(closed)="onClosedTitleBar()" [windowState]="windowState"
(maximized)="onMaximizedTitleBar()" (closed)="onClosed()"
(minimized)="onMinimizedTitleBar()" (maximized)="onMaximized($event)"
(minimized)="onMinimized()"
> >
<ng-content></ng-content> <ng-content></ng-content>
</ucap-title-bar> </ucap-title-bar>

View File

@ -67,9 +67,15 @@ export class TopBarComponent implements OnInit, OnDestroy {
} }
} }
onClosedTitleBar() {} onClosed() {
this.nativeService.window_close();
}
onMaximizedTitleBar() {} onMaximized(altKey: boolean) {
this.nativeService.window_maximize(altKey);
}
onMinimizedTitleBar() {} onMinimized() {
this.nativeService.window_minimize();
}
} }

View File

@ -18,7 +18,9 @@ import { MatTooltipModule } from '@angular/material/tooltip';
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar'; import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n'; import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
import { UiModule } from '@ucap/ng-ui';
import { UiButtonModule } from '@ucap/ng-ui/button';
import { UiNativeModule } from '@ucap/ng-ui/native';
import { AppOrganizationModule } from '@app/ucap/organization/organization.module'; import { AppOrganizationModule } from '@app/ucap/organization/organization.module';
@ -45,7 +47,9 @@ import { DIALOGS } from './dialogs';
PerfectScrollbarModule, PerfectScrollbarModule,
I18nModule, I18nModule,
UiModule,
UiButtonModule,
UiNativeModule,
AppOrganizationModule AppOrganizationModule
], ],

View File

@ -1,4 +1,7 @@
import { SortViewType } from '@app/pages/group/types/sort-view.type';
export interface GroupOpenInfo { export interface GroupOpenInfo {
lastGroupSeq: number; lastGroupSeq: number;
groupSeqs: number[]; groupSeqs: number[];
showType: SortViewType;
} }

View File

@ -1,4 +1,4 @@
import { LoginSession as UCAPLoginSession } from '@ucap/core'; import { LoginSession as UCAPLoginSession } from '@ucap/domain-authentication';
import { GroupOpenInfo } from './group-open-info'; import { GroupOpenInfo } from './group-open-info';
export interface LoginSession extends UCAPLoginSession { export interface LoginSession extends UCAPLoginSession {

View File

@ -1,9 +1,9 @@
import { import {
GeneralSetting as UCAPGeneralSetting, GeneralSetting as UCAPGeneralSetting,
NotificationSetting as UCAPNotificationSetting, NotificationSetting as UCAPNotificationSetting
ChatSetting as UCAPChatSetting, } from '@ucap/domain-common';
PresenceSetting as UCAPPresenceSetting import { PresenceSetting as UCAPPresenceSetting } from '@ucap/domain-status';
} from '@ucap/protocol-option'; import { ChatSetting as UCAPChatSetting } from '@ucap/domain-chat';
// tslint:disable-next-line: no-empty-interface // tslint:disable-next-line: no-empty-interface
export interface GeneralSetting extends UCAPGeneralSetting {} export interface GeneralSetting extends UCAPGeneralSetting {}

View File

@ -1,4 +1,4 @@
import { UserStore as UCAPUserStore } from '@ucap/core'; import { UserStore as UCAPUserStore } from '@ucap/domain-authentication';
import { Settings } from './settings'; import { Settings } from './settings';

View File

@ -14,25 +14,25 @@ $login-bg-h: 100/1080;
justify-content: center; justify-content: center;
background-color: $bg-gray; background-color: $bg-gray;
background-image: url(../../../../assets/images/bg/bg_login_circle_square01.svg), background-image: url(/assets/images/bg/bg_login_circle_square01.svg),
url(../../../../assets/images/bg/bg_login_circle_stroke01.svg), url(/assets/images/bg/bg_login_circle_stroke01.svg),
url(../../../../assets/images/bg/bg_login_circle01.svg), url(/assets/images/bg/bg_login_circle01.svg),
url(../../../../assets/images/bg/bg_login_circle03.svg), url(/assets/images/bg/bg_login_circle03.svg),
url(../../../../assets/images/bg/bg_login_circle_diagonal01.svg), url(/assets/images/bg/bg_login_circle_diagonal01.svg),
url(../../../../assets/images/bg/bg_login_circle_square02.svg), url(/assets/images/bg/bg_login_circle_square02.svg),
url(../../../../assets/images/bg/bg_login_circle04.svg), url(/assets/images/bg/bg_login_circle04.svg),
url(../../../../assets/images/bg/bg_login_circle05.svg), url(/assets/images/bg/bg_login_circle05.svg),
url(../../../../assets/images/bg/bg_login_circle06.svg), url(/assets/images/bg/bg_login_circle06.svg),
url(../../../../assets/images/bg/bg_login_circle07.svg), url(/assets/images/bg/bg_login_circle07.svg),
url(../../../../assets/images/bg/bg_login_circle08.svg), url(/assets/images/bg/bg_login_circle08.svg),
url(../../../../assets/images/bg/bg_login_circle_diagonal02.svg), url(/assets/images/bg/bg_login_circle_diagonal02.svg),
url(../../../../assets/images/bg/bg_login_circle_diagonal03.svg), url(/assets/images/bg/bg_login_circle_diagonal03.svg),
url(../../../../assets/images/bg/bg_login_circle_stroke02.svg), url(/assets/images/bg/bg_login_circle_stroke02.svg),
url(../../../../assets/images/bg/bg_login_circle_stroke03.svg), url(/assets/images/bg/bg_login_circle_stroke03.svg),
url(../../../../assets/images/bg/bg_login_circle_stroke04.svg), url(/assets/images/bg/bg_login_circle_stroke04.svg),
url(../../../../assets/images/bg/bg_login_circle_stroke05.svg), url(/assets/images/bg/bg_login_circle_stroke05.svg),
url(../../../../assets/images/bg/bg_login_polygon01.svg), url(/assets/images/bg/bg_login_polygon01.svg),
url(../../../../assets/images/bg/bg_login_polygon02.svg); url(/assets/images/bg/bg_login_polygon02.svg);
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: unquote($login-bg-w * 323 + '%') background-position: unquote($login-bg-w * 323 + '%')
unquote($login-bg-h * 179 + '%'), unquote($login-bg-h * 179 + '%'),

View File

@ -1 +1,22 @@
<div class="logout-container">Logout</div> <div class="logout-container">
<div class="logout-content">
<div class="logout-title"></div>
<p class="guide-text">
서비스를 이용하시려면 다시 로그인하여 주시기 바랍니다.<br />
<span
>항상 더 좋은 서비스로 고객님께 보답하는 M-Messenger가 되겠습니다.</span
>
</p>
<!--<p class="guide-text">
다른 기기에서 동일한 계정으로 로그인되었습니다.<br />본인이 아닌 경우
비밀번호 변경을 하시기 바랍니다.
<span
>항상 더 좋은 서비스로 고객님께 보답하는 M-Messenger가 되겠습니다.</span
>
</p>-->
<span class="guide-move">OO초 후 로그인 페이지로 자동 이동 됩니다 </span>
</div>
</div>

View File

@ -3,4 +3,57 @@
.logout-container { .logout-container {
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex;
justify-content: center;
align-items: center;
background-color: #f3f4f5;
background-image: url(/assets/images/ico/img_logout.png);
background-position: 50% bottom;
background-repeat: no-repeat;
background-size: 90% auto;
@include screen(xs) {
background-size: 100% auto;
}
.logout-content {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
max-width: 80%;
transform: translateY(-70%);
.logout-title {
display: block;
width: 250px;
height: 62px;
text-align: center;
background: url(/assets/images/ico/img_logout_text.svg);
}
.guide-text {
font-size: 1.6em;
color: #666;
font-weight: 600;
text-align: center;
@include screen(xs) {
font-size: 1.4em;
}
span {
display: inline-flex;
font-size: 0.7em;
color: #999;
font-weight: normal;
margin-top: 0.7em;
}
}
.guide-move {
display: inline-flex;
padding: 0.8em 1.4em;
border-radius: 100px;
background-color: #fd578a;
color: #ffffff;
}
}
} }

View File

@ -3,12 +3,19 @@ import { Routes, RouterModule } from '@angular/router';
import { IndexPageComponent } from './components/index.page.component'; import { IndexPageComponent } from './components/index.page.component';
import { SidenavPageComponent } from './components/sidenav.page.component'; import { SidenavPageComponent } from './components/sidenav.page.component';
import { CallHistoryPageComponent } from './components/call-history.page.component';
const routes: Routes = [ const routes: Routes = [
{ {
path: 'index', path: 'index',
outlet: 'content', outlet: 'content',
component: IndexPageComponent // component: IndexPageComponent
component: CallHistoryPageComponent
},
{
path: 'callhistory',
outlet: 'content',
component: CallHistoryPageComponent
}, },
{ {
path: '', path: '',

View File

@ -3,18 +3,52 @@ import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@angular/flex-layout'; import { FlexLayoutModule } from '@angular/flex-layout';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatTabsModule } from '@angular/material/tabs';
import { UiDateModule } from '@ucap/ng-ui/date';
import { UiPhoneModule } from '@ucap/ng-ui/phone';
import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
import { OrganizationUiModule } from '@ucap/ng-ui-organization';
import { AppOrganizationModule } from '@app/ucap/organization/organization.module';
import { AppCallSectionModule } from '@app/sections/call/call.section.module';
import { AppCallRoutingPageModule } from './call-routing.page.module'; import { AppCallRoutingPageModule } from './call-routing.page.module';
import { COMPONENTS } from './components';
import { IndexPageComponent } from './components/index.page.component'; import { AppGroupModule } from '@app/ucap/group/group.module';
import { SidenavPageComponent } from './components/sidenav.page.component';
export const COMPONENTS = [IndexPageComponent, SidenavPageComponent];
export { IndexPageComponent, SidenavPageComponent };
@NgModule({ @NgModule({
imports: [CommonModule, FlexLayoutModule, AppCallRoutingPageModule], imports: [
CommonModule,
FlexLayoutModule,
MatButtonModule,
MatIconModule,
MatTabsModule,
I18nModule,
UiDateModule,
UiPhoneModule,
OrganizationUiModule,
AppOrganizationModule,
AppCallSectionModule,
AppCallRoutingPageModule,
AppGroupModule
],
declarations: [...COMPONENTS], declarations: [...COMPONENTS],
entryComponents: [] entryComponents: [],
providers: [
{
provide: UCAP_I18N_NAMESPACE,
useValue: ['call', 'common']
}
]
}) })
export class AppCallPageModule {} export class AppCallPageModule {}

View File

@ -0,0 +1,58 @@
<div class="contents-main" fxFlexFill fxLayout="column">
<div class="subtitle" fxFlex="0 0 50px" fxLayout="row">
<div class="title">{{ 'call:label.callHistory' | ucapI18n }}</div>
<div class="call-info" fxLayout="row">
<div class="info send">
<mat-icon>call_made</mat-icon>{{ 'call:label.send' | ucapI18n }}
</div>
<div class="info receive">
<mat-icon>call_received</mat-icon>{{ 'call:label.recieve' | ucapI18n }}
</div>
<div class="info away">
<mat-icon>call_missed</mat-icon>{{ 'call:label.missed' | ucapI18n }}
</div>
<div class="info unanswered">
<mat-icon>call_missed_outgoing</mat-icon
>{{ 'call:label.unanswered' | ucapI18n }}
</div>
</div>
</div>
<div class="user-title" fxFlex="0 0 90px" fxLayout="row">
<div class="user-profile-info">
<div class="profile-image">
<app-group-profile-image-01
[userInfo]="userInfo"
[versionInfo]="versionInfo"
(openProfile)="onOpenProfile($event)"
></app-group-profile-image-01>
</div>
<div class="user-info">
<div class="user-n-g">
<div class="user-name">
{{ userInfo | ucapOrganizationTranslate: 'name':undefined:'' }}
</div>
<div class="user-grade">
{{ userInfo | ucapOrganizationTranslate: 'grade':undefined:' ' }}
</div>
</div>
<div class="dept-name">
{{ userInfo | ucapOrganizationTranslate: 'deptName':undefined:' ' }}
</div>
</div>
</div>
<div class="user-call-info" fxLayout="row">
<div *ngIf="!!userInfo && !!userInfo.hpNumber" class="mobile">
{{ userInfo.hpNumber | ucapPhoneNumber }}
<img src="assets/images/ico/btn_list_mobile_a24.svg" alt="" />
</div>
<div *ngIf="!!userInfo && !!userInfo.lineNumber" class="lineNumber">
{{ userInfo.lineNumber | ucapPhoneNumber }}
<img src="assets/images/ico/btn_list_call_a24.svg" alt="" />
</div>
</div>
</div>
<div class="call-info-container" fxFlex="1 1 auto">
<app-sections-call-info></app-sections-call-info>
</div>
</div>

View File

@ -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%;
}
}
}

View File

@ -0,0 +1,32 @@
import { TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { CallHistoryPageComponent } from './call-history.page.component';
describe('app::pages::chat::CallHistoryPageComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule],
declarations: [CallHistoryPageComponent]
}).compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(CallHistoryPageComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'ucap-lg-web'`, () => {
const fixture = TestBed.createComponent(CallHistoryPageComponent);
const app = fixture.componentInstance;
});
it('should render title', () => {
const fixture = TestBed.createComponent(CallHistoryPageComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement;
expect(compiled.querySelector('.content span').textContent).toContain(
'ucap-lg-web app is running!'
);
});
});

View File

@ -0,0 +1,129 @@
import {
Component,
OnInit,
OnDestroy,
ChangeDetectionStrategy,
ChangeDetectorRef
} from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil, withLatestFrom } from 'rxjs/operators';
import { QueryParams } from '../types/params.type';
import { Store, select } from '@ngrx/store';
import { UserInfoSS } from '@ucap/domain-organization';
import { UserSelector } from '@ucap/ng-store-organization';
import { AppOrganizationService } from '@app/services/app-organization.service';
import { ConfigurationSelector } from '@ucap/ng-store-authentication';
import {
ProfileDialogComponent,
ProfileDialogData,
ProfileDialogResult
} from '@app/sections/organization/dialogs/profile.dialog.component';
import { MatDialog } from '@angular/material/dialog';
import { VersionInfo } from '@ucap/domain-authentication';
@Component({
selector: 'app-pages-call-history',
templateUrl: './call-history.page.component.html',
styleUrls: ['./call-history.page.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CallHistoryPageComponent implements OnInit, OnDestroy {
isMe: boolean;
userSeq: string;
userInfo: UserInfoSS;
versionInfo: VersionInfo;
private ngOnDestroySubject: Subject<void> = new Subject();
constructor(
private dialog: MatDialog,
private store: Store<any>,
private appOrganizationService: AppOrganizationService,
private activatedRoute: ActivatedRoute,
private changeDetectorRef: ChangeDetectorRef
) {}
ngOnInit(): void {
this.activatedRoute.queryParams
.pipe(
takeUntil(this.ngOnDestroySubject),
withLatestFrom(this.store.pipe(select(UserSelector.user)))
)
.subscribe(([params, user]) => {
const seqParam = params[QueryParams.ID];
// initializing by userSeq Change.
if (this.userSeq !== seqParam) {
}
// setting userSeq.
this.userSeq = !!seqParam
? seqParam
: !!user
? String(user.info.seq)
: undefined;
if (!!user && this.userSeq === String(user.info.seq)) {
this.isMe = true;
} else {
this.isMe = false;
}
if (!!this.userSeq) {
const self = this;
this.appOrganizationService
.getUserInfo({ userSeq: this.userSeq, user })
.then((result) => {
console.log('getUserInfo : ', result);
self.userInfo = result.userInfo;
})
.catch((e) => {
self.userInfo = undefined;
})
.finally(() => {
this.changeDetectorRef.markForCheck();
});
} else {
this.userInfo = undefined;
this.changeDetectorRef.markForCheck();
}
});
this.store
.pipe(
takeUntil(this.ngOnDestroySubject),
select(ConfigurationSelector.versionInfo)
)
.subscribe((versionInfo) => {
this.versionInfo = versionInfo;
});
}
ngOnDestroy(): void {
if (!!this.ngOnDestroySubject) {
this.ngOnDestroySubject.next();
this.ngOnDestroySubject.complete();
}
}
onOpenProfile(userInfo: UserInfoSS): void {
if (!!userInfo) {
this.dialog.open<
ProfileDialogComponent,
ProfileDialogData,
ProfileDialogResult
>(ProfileDialogComponent, {
panelClass: 'mid-create-dialog',
data: {
userSeq: userInfo.seq
}
});
}
}
}

View File

@ -1 +1,39 @@
Index page of call is works! <div class="index-page-call-info">
<div class="ico-page-call">
<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="call-index-copy">
{{ 'call:noSelectHistory' | ucapI18n }}
</p>
</div>

View File

@ -0,0 +1,33 @@
@import '~@ucap/lg-scss/mixins';
.index-page-call-info {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.ico-page-call {
width: 166px;
height: 142px;
margin-top: -80px;
}
.call-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;
}
@include screen(xs) {
.ico-page-call {
width: 120px;
height: auto;
margin-top: -40px;
}
.call-index-copy {
font-size: 1.2em;
margin: 20px 0 0;
}
}
}

View File

@ -1,4 +1,9 @@
import { IndexPageComponent } from './index.page.component'; import { IndexPageComponent } from './index.page.component';
import { SidenavPageComponent } from './sidenav.page.component'; import { SidenavPageComponent } from './sidenav.page.component';
import { CallHistoryPageComponent } from './call-history.page.component';
export const COMPONENTS = [IndexPageComponent, SidenavPageComponent]; export const COMPONENTS = [
IndexPageComponent,
SidenavPageComponent,
CallHistoryPageComponent
];

View File

@ -1,3 +1,21 @@
<div fxFlexFill> <div class="sidenav-container call" fxFlexFill fxLayout="column">
sidenav page of call is works! <div class="title-section" fxFlex="0 0 50px" fxLayout="row">
<div class="title">
<h3 fxFlex="1 1 auto">{{ 'call:label.call' | ucapI18n }}</h3>
</div>
</div>
<div class="extra-box" fxFlex="0 0 50px">
<app-organization-search-for-tenant
[isBackspaceCanceled]="isBackspaceCanceled"
[(searchData)]="companySearchData"
(canceled)="onSearchCancel()"
>
</app-organization-search-for-tenant>
</div>
<div fxFlex="1 1 auto">
<app-sections-call-list
[searchObj]="companySearchData"
></app-sections-call-list>
</div>
</div> </div>

View File

@ -0,0 +1,29 @@
@import '~@ucap/lg-scss/mixins';
.sidenav-container.call {
overflow: hidden;
display: flex;
flex-flow: column;
align-content: flex-start;
background-color: #f1f2f6;
.title-section {
display: flex;
flex-flow: column;
background-color: $white;
.title {
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
padding: 0 0 0 17px;
background-color: $white;
align-items: center;
width: 100%;
h3 {
@include font-family-txt(18, left, $lipstick);
align-items: center;
font-weight: 600;
}
}
}
}

View File

@ -1,13 +1,109 @@
import { Component, Inject } from '@angular/core'; import { Subject, combineLatest } from 'rxjs';
import { Router } from '@angular/router'; import { takeUntil } from 'rxjs/operators';
import {
Component,
OnInit,
OnDestroy,
ChangeDetectionStrategy,
ChangeDetectorRef
} from '@angular/core';
import { Store, select } from '@ngrx/store';
import { User } from '@ucap/domain-organization';
import { LoginSession, LoginInfo } from '@ucap/domain-authentication';
import { SearchType, SearchDirectionByDateType } from '@ucap/domain-call';
import { CallHistoryInfoRequest } from '@ucap/api-contact';
import { LogService } from '@ucap/ng-logger'; import { LogService } from '@ucap/ng-logger';
import { CallActions } from '@ucap/ng-store-call';
import { UserSelector } from '@ucap/ng-store-organization';
import { LoginSelector } from '@ucap/ng-store-authentication';
import { SearchData } from '@app/ucap/organization/models/search-data';
import { AppAuthenticationService } from '@app/services/app-authentication.service';
import moment from 'moment';
import { environment } from '@environments';
@Component({ @Component({
selector: 'app-pages-call-sidenav', selector: 'app-pages-call-sidenav',
templateUrl: './sidenav.page.component.html', templateUrl: './sidenav.page.component.html',
styleUrls: ['./sidenav.page.component.scss'] styleUrls: ['./sidenav.page.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class SidenavPageComponent { export class SidenavPageComponent implements OnInit, OnDestroy {
constructor(private logService: LogService) {} set companySearchData(searchData: SearchData) {
if (!!searchData && searchData.searchWord !== '') {
this._companySearchData = { ...searchData, bySearch: true };
} else {
this._companySearchData = { ...searchData, bySearch: false };
}
}
get companySearchData() {
return this._companySearchData;
}
// tslint:disable-next-line: variable-name
_companySearchData: SearchData;
isBackspaceCanceled = true;
user: User;
loginInfo: LoginInfo;
loginSession: LoginSession;
private ngOnDestroySubject: Subject<void> = new Subject();
constructor(
private store: Store<any>,
private appAuthenticationService: AppAuthenticationService,
private changeDetectorRef: ChangeDetectorRef,
private logService: LogService
) {}
ngOnInit(): void {
combineLatest([
this.store.pipe(select(UserSelector.user)),
this.store.pipe(select(LoginSelector.loginInfo))
])
.pipe(takeUntil(this.ngOnDestroySubject))
.subscribe(([user, loginInfo]) => {
if (!!user && !!loginInfo) {
this.user = user;
this.loginInfo = loginInfo;
this.loginSession = this.appAuthenticationService.getLoginSession();
this._retrieveCallHistory();
}
});
}
ngOnDestroy(): void {
if (!!this.ngOnDestroySubject) {
this.ngOnDestroySubject.next();
this.ngOnDestroySubject.complete();
}
}
onSearchCancel() {
this.companySearchData = { ...this.companySearchData, searchWord: '' };
}
private _retrieveCallHistory() {
const searchStartDate = moment(new Date()).format('YYYY-MM-DD_HH:mm:ss');
const req: CallHistoryInfoRequest = {
userSeq: String(this.user.info.seq),
deviceType: this.loginSession.deviceType,
token: this.loginInfo.tokenString,
searchType: SearchType.All,
searchUserSeq: '',
searchNumber: '',
searchDate: '',
searchStartDate,
searchListCount:
environment.productConfig.call.historyRequestDefaultCount,
searchDirection: SearchDirectionByDateType.Before
};
this.store.dispatch(CallActions.callHistory({ req }));
}
} }

View File

@ -0,0 +1,3 @@
export enum QueryParams {
ID = 'id'
}

View File

@ -14,7 +14,10 @@ import { AppChatSectionModule } from '@app/sections/chat/chat.section.module';
import { AppChatRoutingPageModule } from './chat-routing.page.module'; import { AppChatRoutingPageModule } from './chat-routing.page.module';
import { UiModule } from '@ucap/ng-ui'; import { UiCoreModule } from '@ucap/ng-ui/core';
import { UiDateModule } from '@ucap/ng-ui/date';
import { UiViewerModule } from '@ucap/ng-ui/viewer';
import { COMPONENTS } from './components'; import { COMPONENTS } from './components';
import { UCAP_I18N_NAMESPACE, I18nModule } from '@ucap/ng-i18n'; import { UCAP_I18N_NAMESPACE, I18nModule } from '@ucap/ng-i18n';
@ -30,11 +33,14 @@ import { UCAP_I18N_NAMESPACE, I18nModule } from '@ucap/ng-i18n';
MatSidenavModule, MatSidenavModule,
MatTooltipModule, MatTooltipModule,
UiCoreModule,
UiDateModule,
UiViewerModule,
AppChatSectionModule, AppChatSectionModule,
AppChatRoutingPageModule, AppChatRoutingPageModule,
I18nModule, I18nModule
UiModule
], ],
declarations: [...COMPONENTS], declarations: [...COMPONENTS],
entryComponents: [], entryComponents: [],

View File

@ -8,12 +8,23 @@
</div> </div>
<app-sections-chat-chat-search <app-sections-chat-chat-search
[isChatSearch]="isChatSearch" [isChatSearch]="isChatSearch"
(chatSearch)="onChatSearch($event)"
(closeChatSearch)="isChatSearch = false" (closeChatSearch)="isChatSearch = false"
></app-sections-chat-chat-search> ></app-sections-chat-chat-search>
<mat-drawer-container autosize fxFlex="1 1 auto" fxLayout="column"> <mat-drawer-container autosize fxFlex="1 1 auto" fxLayout="column">
<div class="message-box-container" fxFlex="1 1 auto" fxLayout="column"> <div
class="message-box-container"
fxFlex="1 1 auto"
fxLayout="column"
ucapFileUploadFor01
(fileSelected)="onFileSelected($event)"
(fileDragEnter)="onFileDragEnter($event)"
(fileDragOver)="onFileDragOver($event)"
(fileDragLeave)="onFileDragLeave($event)"
>
<div class="message-area" fxFlex="1 1 auto"> <div class="message-area" fxFlex="1 1 auto">
<app-sections-chat-message <app-sections-chat-message
#chatMessageSections
[roomId]="roomId" [roomId]="roomId"
[translationSimpleview]="translationSimpleview" [translationSimpleview]="translationSimpleview"
[eventSendTrigger$]="eventSendTriggerSubject.asObservable()" [eventSendTrigger$]="eventSendTriggerSubject.asObservable()"
@ -21,9 +32,11 @@
</div> </div>
<div class="message-input" fxFlex="0 0 auto"> <div class="message-input" fxFlex="0 0 auto">
<app-sections-chat-form <app-sections-chat-form
#chatForm
[roomId]="roomId" [roomId]="roomId"
(changeTranslationSimpleview)="translationSimpleview = $event" (changeTranslationSimpleview)="translationSimpleview = $event"
(eventSendTrigger)="eventSendTriggerSubject.next($event)" (eventSendTrigger)="eventSendTriggerSubject.next($event)"
(openFormSelector)="onOpenFormSelector()"
></app-sections-chat-form> ></app-sections-chat-form>
</div> </div>
</div> </div>

View File

@ -1,15 +1,25 @@
import { Subject, BehaviorSubject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Component, ViewChild, OnInit, OnDestroy } from '@angular/core'; import { Component, ViewChild, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router'; import { ActivatedRoute, Params } from '@angular/router';
import { Store } from '@ngrx/store';
import { MatDrawer } from '@angular/material/sidenav'; import { MatDrawer } from '@angular/material/sidenav';
import { Subscription, Subject, BehaviorSubject } from 'rxjs'; import { ChattingActions } from '@ucap/ng-store-chat';
import { MessageSectionComponent } from '@app/sections/chat/components/message.section.component';
import {
FormSectionComponent,
SelectorType
} from '@app/sections/chat/components/form.section.component';
import { QueryParams } from '../types/params.type'; import { QueryParams } from '../types/params.type';
import { ChatDrawType } from '../types/chat-draw.type'; import { ChatDrawType } from '../types/chat-draw.type';
import { takeUntil } from 'rxjs/operators';
import { DrawInfo } from '../models/draw-info'; import { DrawInfo } from '../models/draw-info';
import { Store } from '@ngrx/store'; import { SearchInfo } from '../models/search-info';
import { RoomActions, ChattingActions } from '@ucap/ng-store-chat';
@Component({ @Component({
selector: 'app-pages-chat-room', selector: 'app-pages-chat-room',
@ -18,6 +28,11 @@ import { RoomActions, ChattingActions } from '@ucap/ng-store-chat';
}) })
export class ChatRoomPageComponent implements OnInit, OnDestroy { export class ChatRoomPageComponent implements OnInit, OnDestroy {
isChatSearch = false; isChatSearch = false;
searchObj: SearchInfo = {
isShowSearch: false,
searchWord: ''
};
roomId: string; roomId: string;
translationSimpleview = false; translationSimpleview = false;
@ -25,9 +40,15 @@ export class ChatRoomPageComponent implements OnInit, OnDestroy {
returnDrawerType: ChatDrawType | null; returnDrawerType: ChatDrawType | null;
eventSendTriggerSubject: BehaviorSubject<any> = new BehaviorSubject<any>(0); eventSendTriggerSubject: BehaviorSubject<any> = new BehaviorSubject<any>(0);
@ViewChild('chatMessageSections', { static: false })
chatMessageSections: MessageSectionComponent;
@ViewChild('chatRightDrawer', { static: false }) @ViewChild('chatRightDrawer', { static: false })
chatRightDrawer: MatDrawer; chatRightDrawer: MatDrawer;
@ViewChild('chatForm', { static: false })
chatForm: FormSectionComponent;
ChatDrawType = ChatDrawType; ChatDrawType = ChatDrawType;
private ngOnDestroySubject: Subject<void> = new Subject(); private ngOnDestroySubject: Subject<void> = new Subject();
@ -42,11 +63,20 @@ export class ChatRoomPageComponent implements OnInit, OnDestroy {
.pipe(takeUntil(this.ngOnDestroySubject)) .pipe(takeUntil(this.ngOnDestroySubject))
.subscribe((params: Params) => { .subscribe((params: Params) => {
const seqParam = params[QueryParams.ROOM_ID]; const seqParam = params[QueryParams.ROOM_ID];
// initializing by roomId Change. // initializing by roomId Change.
if (this.roomId !== seqParam) { if (this.roomId !== seqParam) {
// close Right Drawer.
if (!!this.chatRightDrawer) { if (!!this.chatRightDrawer) {
this.chatRightDrawer.close(); this.chatRightDrawer.close();
} }
// close Chat Search area.
this.isChatSearch = false;
this.searchObj = {
isShowSearch: false,
searchWord: ''
};
} }
// setting roomId. // setting roomId.
@ -68,6 +98,14 @@ export class ChatRoomPageComponent implements OnInit, OnDestroy {
this.store.dispatch(ChattingActions.clearActiveRoomId({})); this.store.dispatch(ChattingActions.clearActiveRoomId({}));
} }
/** About Form Selector */
onOpenFormSelector() {
if (!!this.chatMessageSections) {
this.chatMessageSections.refreshAndScrollToBottom();
}
}
/** About Right drawer */
onRightDrawerToggle(type: DrawInfo | null): void { onRightDrawerToggle(type: DrawInfo | null): void {
this.drawerType = type.chatDrawType; this.drawerType = type.chatDrawType;
this.returnDrawerType = !!type.returnDrawType ? type.returnDrawType : null; this.returnDrawerType = !!type.returnDrawType ? type.returnDrawType : null;
@ -78,4 +116,28 @@ export class ChatRoomPageComponent implements OnInit, OnDestroy {
this.returnDrawerType = null; this.returnDrawerType = null;
this.chatRightDrawer.close(); this.chatRightDrawer.close();
} }
/** About File Drag & Drop */
onFileSelected(fileList: FileList) {
if (!!this.chatForm) {
this.chatForm.onDragAndDropFileupload(fileList); // direct upload
// this.chatForm.onDragAndDropStandbyFileupload(fileList); // standby and confirm upload.(complate not yet)
}
}
onFileDragEnter(event: DataTransferItemList) {
// if (!!this.chatForm) {
// this.chatForm.onOpenSelector(SelectorType.FILEUPLOAD);
// }
}
onFileDragOver(event: DragEvent) {}
onFileDragLeave(event: DragEvent) {
// if (!!this.chatForm) {
// this.chatForm.clearSelector();
// }
}
/** About Chat search. */
onChatSearch(search: SearchInfo) {
this.searchObj = search;
}
} }

View File

@ -1,3 +1,4 @@
@import '~@ucap/lg-scss/mixins';
.index-page-chat-info { .index-page-chat-info {
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -18,4 +19,15 @@
border-bottom: 1px solid #ccc; border-bottom: 1px solid #ccc;
margin: 50px 0 0; margin: 50px 0 0;
} }
@include screen(xs) {
.ico-page-chat {
width: 120px;
height: auto;
margin-top: -40px;
}
.chat-index-copy {
font-size: 1.2em;
margin: 20px 0 0;
}
}
} }

View File

@ -10,10 +10,12 @@
display: flex; display: flex;
flex-flow: row nowrap; flex-flow: row nowrap;
justify-content: space-between; justify-content: space-between;
padding: 0 5px 0 17px;
background-color: $white;
align-items: center; align-items: center;
width: 100%; width: 100%;
height: 50px;
min-height: 50px;
padding: 0 5px 0 17px;
background-color: $white;
h3 { h3 {
@include font-family-txt(18, left, $lipstick); @include font-family-txt(18, left, $lipstick);
align-items: center; align-items: center;

View File

@ -1,24 +1,32 @@
import { Subject, of } from 'rxjs';
import { takeUntil, take, map, catchError } from 'rxjs/operators';
import { Component, ChangeDetectorRef, OnInit, OnDestroy } from '@angular/core'; import { Component, ChangeDetectorRef, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { Store, select } from '@ngrx/store';
import { MatDialog } from '@angular/material/dialog';
import { RoomInfo } from '@ucap/domain-chat';
import { ExitAllRequest } from '@ucap/protocol-room';
import { LogService } from '@ucap/ng-logger'; import { LogService } from '@ucap/ng-logger';
import { Subject, of } from 'rxjs'; import { I18nService } from '@ucap/ng-i18n';
import { Store, select } from '@ngrx/store';
import { takeUntil, take, map, catchError } from 'rxjs/operators';
import { RoomSelector, RoomActions } from '@ucap/ng-store-chat'; import { RoomSelector, RoomActions } from '@ucap/ng-store-chat';
import { RoomInfo, ExitAllRequest } from '@ucap/protocol-room';
import { MatDialog } from '@angular/material/dialog';
import { import {
ConfirmDialogComponent, ConfirmDialogComponent,
ConfirmDialogData, ConfirmDialogData,
ConfirmDialogResult ConfirmDialogResult
} from '@ucap/ng-ui'; } from '@ucap/ng-ui/core';
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 { AppChatService } from '@app/services/app-chat.service';
import { SessionStorageService } from '@ucap/ng-web-storage'; import { AppRoomSelector } from '@app/store/state';
import { AppKey } from '@app/types';
import { SearchInfo } from '../models/search-info';
import { QueryParams } from '../types/params.type';
@Component({ @Component({
selector: 'app-pages-chat-sidenav', selector: 'app-pages-chat-sidenav',
@ -46,14 +54,18 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
private dialog: MatDialog, private dialog: MatDialog,
private i18nService: I18nService, private i18nService: I18nService,
private logService: LogService, private logService: LogService,
private sessionStorageService: SessionStorageService,
private activatedRoute: ActivatedRoute, private activatedRoute: ActivatedRoute,
private appChatService: AppChatService, private appChatService: AppChatService,
private changeDetectorRef: ChangeDetectorRef private changeDetectorRef: ChangeDetectorRef
) { ) {
this.historyRoomId = this.sessionStorageService.get<string>( this.store
AppKey.HistoryRoomId .pipe(
); takeUntil(this.ngOnDestroySubject),
select(AppRoomSelector.historyRoomId)
)
.subscribe((historyRoomId) => {
this.historyRoomId = historyRoomId;
});
} }
ngOnInit(): void { ngOnInit(): void {
@ -62,10 +74,9 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
.subscribe((params: Params) => { .subscribe((params: Params) => {
const seqParam = params[QueryParams.ROOM_ID]; const seqParam = params[QueryParams.ROOM_ID];
this.sessionStorageService.set<string>( if (!!seqParam) {
AppKey.HistoryRoomId, this.appChatService.setHistoryRoomId(seqParam);
!!seqParam ? seqParam : undefined }
);
if ( if (
seqParam === undefined && seqParam === undefined &&

View File

@ -22,7 +22,8 @@
> >
<app-organization-profile-01 <app-organization-profile-01
[userSeq]="userSeq" [userSeq]="userSeq"
(openChat)="onOpenCaht($event)" [companyList]="companyList"
(openChat)="onOpenChat($event)"
(sendMessage)="onSendMessage($event)" (sendMessage)="onSendMessage($event)"
(sendCall)="onSendCall($event)" (sendCall)="onSendCall($event)"
(sendSms)="onSendSms($event)" (sendSms)="onSendSms($event)"

View File

@ -48,9 +48,10 @@
position: fixed; position: fixed;
height: 49px; height: 49px;
top: 40px; top: 40px;
left: 75px; left: 60px;
z-index: 10; z-index: 10;
width: calc(100% - 90px); width: calc(100% - 60px);
background-color: rgba(255, 255, 255, 0.3);
a { a {
width: 50%; width: 50%;
text-decoration: none; text-decoration: none;
@ -66,11 +67,11 @@
.profile-container { .profile-container {
height: 100%; height: 100%;
overflow: auto; overflow: auto;
flex: 1 0 460px !important; flex: 0 0 460px !important;
max-width: inherit !important; max-width: inherit !important;
border-radius: 2px; border-radius: 2px;
@include screen(custom, min, 1540) { @include screen(custom, min, 1540) {
flex: 1 0 44% !important; flex: 0 0 44% !important;
} }
@include screen(lg) { @include screen(lg) {
flex: 1 0 auto !important; flex: 1 0 auto !important;

View File

@ -1,5 +1,5 @@
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { takeUntil, take, map } from 'rxjs/operators';
import { import {
Component, Component,
@ -14,22 +14,29 @@ import { MatDialog } from '@angular/material/dialog';
import { Store, select } from '@ngrx/store'; import { Store, select } from '@ngrx/store';
import { FileUploadItem } from '@ucap/api'; import { FileUploadItem } from '@ucap/domain-common';
import {
UserInfoSS,
UserInfoF,
UserInfoUpdateType,
User,
Company
} from '@ucap/domain-organization';
import { FileProfileSaveRequest } from '@ucap/api-common'; import { FileProfileSaveRequest } from '@ucap/api-common';
import { VersionInfo2Response } from '@ucap/api-public';
import { UserInfoSS, UserInfoF } from '@ucap/protocol-query'; import { ConferenceCreateRequest } from '@ucap/api-prompt';
import { UserInfoUpdateType, User } from '@ucap/protocol-info';
import { LoginResponse } from '@ucap/protocol-authentication';
import { LogService } from '@ucap/ng-logger'; import { LogService } from '@ucap/ng-logger';
import { I18nService } from '@ucap/ng-i18n'; import { I18nService } from '@ucap/ng-i18n';
import { UserSelector } from '@ucap/ng-store-organization'; import { UserSelector, CompanySelector } from '@ucap/ng-store-organization';
import { BuddySelector } from '@ucap/ng-store-group';
import { import {
LoginSelector, LoginSelector,
ConfigurationSelector ConfigurationSelector
} from '@ucap/ng-store-authentication'; } from '@ucap/ng-store-authentication';
import { UserInfoTypes } from '@app/types'; import { UserInfoTypes, GroupManageType } from '@app/types';
import { AppFileService } from '@app/services/app-file.service'; import { AppFileService } from '@app/services/app-file.service';
import { AppAuthenticationService } from '@app/services/app-authentication.service'; import { AppAuthenticationService } from '@app/services/app-authentication.service';
import { AppGroupService } from '@app/services/app-group.service'; import { AppGroupService } from '@app/services/app-group.service';
@ -37,6 +44,13 @@ import { AppGroupService } from '@app/services/app-group.service';
import { QueryParams } from '../types/params.type'; import { QueryParams } from '../types/params.type';
import { AppChatService } from '@app/services/app-chat.service'; import { AppChatService } from '@app/services/app-chat.service';
import {
EditUserDialogComponent,
EditUserDialogData,
EditUserDialogResult
} from '@app/sections/group/dialogs/edit-user.dialog.component';
import { LoginInfo, VersionInfo } from '@ucap/domain-authentication';
@Component({ @Component({
selector: 'app-pages-group-index', selector: 'app-pages-group-index',
templateUrl: './index.page.component.html', templateUrl: './index.page.component.html',
@ -65,11 +79,13 @@ export class IndexPageComponent implements OnInit, OnDestroy {
userSeq: string = undefined; userSeq: string = undefined;
user: User; user: User;
loginRes: LoginResponse; loginInfo: LoginInfo;
versionInfo2Res: VersionInfo2Response; versionInfo: VersionInfo;
activeLink = 0; activeLink = 0;
tabName: string; tabName: string;
profileName: string; profileName: string;
buddyList: UserInfoTypes[];
companyList: Company[];
ngOnInit(): void { ngOnInit(): void {
this.activatedRoute.queryParams this.activatedRoute.queryParams
@ -91,18 +107,36 @@ export class IndexPageComponent implements OnInit, OnDestroy {
this._refreshProfile(); this._refreshProfile();
}); });
this.store this.store
.pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes)) .pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginInfo))
.subscribe((loginRes) => { .subscribe((loginInfo) => {
this.loginRes = loginRes; this.loginInfo = loginInfo;
}); });
this.store this.store
.pipe( .pipe(
takeUntil(this.ngOnDestroySubject), takeUntil(this.ngOnDestroySubject),
select(ConfigurationSelector.versionInfo2Response) select(ConfigurationSelector.versionInfo)
) )
.subscribe((versionInfo2Res) => { .subscribe((versionInfo) => {
this.versionInfo2Res = versionInfo2Res; this.versionInfo = versionInfo;
});
this.store
.pipe(takeUntil(this.ngOnDestroySubject), select(BuddySelector.buddies))
.subscribe((buddies) => {
this.buddyList = buddies;
});
this.store
.pipe(
takeUntil(this.ngOnDestroySubject),
select(CompanySelector.companyList)
)
.subscribe((companyList) => {
if (!companyList) {
return;
}
this.companyList = companyList;
}); });
} }
@ -113,7 +147,7 @@ export class IndexPageComponent implements OnInit, OnDestroy {
} }
} }
onOpenCaht(userInfo: UserInfoSS) { onOpenChat(userInfo: UserInfoSS) {
this.appChatService.newOpenRoom( this.appChatService.newOpenRoom(
[Number(userInfo.seq) as any], [Number(userInfo.seq) as any],
false, false,
@ -123,15 +157,48 @@ export class IndexPageComponent implements OnInit, OnDestroy {
onSendMessage(userInfo: UserInfoSS) {} onSendMessage(userInfo: UserInfoSS) {}
onSendCall(call: string) {} onSendCall(call: string) {}
onSendSms(employeeNum: string) {} onSendSms(employeeNum: string) {}
onCreateConference(userSeq: number) {} onCreateConference(userSeq: number) {
const loginSession = this.appAuthenticationService.getLoginSession();
const req: ConferenceCreateRequest = {
userSeq: String(this.user.info.seq),
deviceType: loginSession.deviceType,
tokenKey: this.loginInfo.tokenString,
targetUserSeqs: [userSeq]
};
this.appChatService.openVideoConference(req);
}
onToggleFavorit(params: { userInfo: UserInfoSS; isFavorite: boolean }) { onToggleFavorit(params: { userInfo: UserInfoSS; isFavorite: boolean }) {
this.appGroupService.updateBuddy(params.userInfo, params.isFavorite); this.appGroupService.updateBuddy(params.userInfo, params.isFavorite);
} }
onToggleBuddy(params: { userInfo: UserInfoSS; isBuddy: boolean }) { onToggleBuddy(params: { userInfo: UserInfoSS; isBuddy: boolean }) {
this.appGroupService if (params.isBuddy) {
.updateBuddyByToggle(params) // 동료추가
.then((isRemoveBuddy) => { const dialogRef = this.dialog.open<
if (isRemoveBuddy) { EditUserDialogComponent,
EditUserDialogData,
EditUserDialogResult
>(EditUserDialogComponent, {
panelClass: 'max-create-dialog',
data: {
title: this.i18nService.t('group:dialog.title.addBuddy'),
type: GroupManageType.Add,
userInfos: [params.userInfo]
}
});
dialogRef
.afterClosed()
.pipe(
take(1),
map((result: EditUserDialogResult) => {
if (!!result) {
this.appGroupService.addBuddy(result);
}
})
)
.subscribe();
} else {
// 동료삭제
this.appGroupService.removeBuddy(params.userInfo).then(() => {
this.router.navigate( this.router.navigate(
[ [
'group', 'group',
@ -143,9 +210,8 @@ export class IndexPageComponent implements OnInit, OnDestroy {
queryParams: { id: Number(params.userInfo.seq) } queryParams: { id: Number(params.userInfo.seq) }
} }
); );
});
} }
})
.catch((reson) => this.logService.error(reson));
} }
onUploadProfileImage(profileImageFileUploadItem: FileUploadItem) { onUploadProfileImage(profileImageFileUploadItem: FileUploadItem) {
const loginSession = this.appAuthenticationService.getLoginSession(); const loginSession = this.appAuthenticationService.getLoginSession();
@ -153,14 +219,14 @@ export class IndexPageComponent implements OnInit, OnDestroy {
const profile = { const profile = {
userSeq: String(this.user.info.seq), userSeq: String(this.user.info.seq),
deviceType: loginSession.deviceType, deviceType: loginSession.deviceType,
token: this.loginRes.tokenString, token: this.loginInfo.tokenString,
file: profileImageFileUploadItem.file, file: profileImageFileUploadItem.file,
fileUploadItem: profileImageFileUploadItem fileUploadItem: profileImageFileUploadItem
} as FileProfileSaveRequest; } as FileProfileSaveRequest;
this.appFileServie.fileProfileSave( this.appFileServie.fileProfileSave(
profile, profile,
this.versionInfo2Res.profileUploadUrl this.versionInfo.profileUploadUrl
); );
} }
onUpdateIntro(intro: string) { onUpdateIntro(intro: string) {
@ -168,6 +234,15 @@ export class IndexPageComponent implements OnInit, OnDestroy {
} }
onUpdateNickname(params: { userInfo: UserInfoTypes; nickname: string }) { onUpdateNickname(params: { userInfo: UserInfoTypes; nickname: string }) {
const isBuddy = this.appGroupService.checkBuddy(
this.buddyList,
params.userInfo
);
if (!isBuddy) {
return;
}
this.appGroupService.updateNickname( this.appGroupService.updateNickname(
params.userInfo as UserInfoF, params.userInfo as UserInfoF,
params.nickname params.nickname

View File

@ -22,6 +22,7 @@
</div> </div>
<div class="extra-box" fxFlex="0 0 50px"> <div class="extra-box" fxFlex="0 0 50px">
<app-organization-search-for-tenant <app-organization-search-for-tenant
[isBackspaceCanceled]="isBackspaceCanceled"
[(searchData)]="companySearchData" [(searchData)]="companySearchData"
(canceled)="onSearchCancel()" (canceled)="onSearchCancel()"
> >
@ -34,6 +35,7 @@
[searchData]="companySearchData" [searchData]="companySearchData"
[showType]="showType" [showType]="showType"
(clickUser)="onClickUser($event)" (clickUser)="onClickUser($event)"
(openProfile)="onOpenProfile($event)"
></app-sections-group-list> ></app-sections-group-list>
</div> </div>
</div> </div>

View File

@ -1,5 +1,5 @@
import { of, Subject } from 'rxjs'; import { of, Subject } from 'rxjs';
import { take, map, catchError } from 'rxjs/operators'; import { take, map, catchError, takeUntil } from 'rxjs/operators';
import { import {
Component, Component,
@ -7,7 +7,8 @@ import {
OnDestroy, OnDestroy,
ChangeDetectorRef, ChangeDetectorRef,
ViewChild, ViewChild,
ChangeDetectionStrategy ChangeDetectionStrategy,
NgZone
} from '@angular/core'; } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
@ -15,14 +16,19 @@ import { Store } from '@ngrx/store';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { UserInfo } from '@ucap/domain-organization';
import { LogService } from '@ucap/ng-logger'; import { LogService } from '@ucap/ng-logger';
import { I18nService } from '@ucap/ng-i18n'; import { I18nService } from '@ucap/ng-i18n';
import { SearchData } from '@app/ucap/organization/models/search-data'; import { SearchData } from '@app/ucap/organization/models/search-data';
import { LoginSession } from '@app/models/login-session';
import { AppUiService } from '@app/services/app-ui.service';
import { AppAuthenticationService } from '@app/services/app-authentication.service';
import { CreateDialogComponent } from '@app/sections/group/dialogs/create.dialog.component'; import { CreateDialogComponent } from '@app/sections/group/dialogs/create.dialog.component';
import { ListSectionComponent } from '@app/sections/group/components/list.section.component'; import { ListSectionComponent } from '@app/sections/group/components/list.section.component';
import { UserInfo } from '@ucap/protocol-sync';
import { SortViewType } from '../types/sort-view.type'; import { SortViewType } from '../types/sort-view.type';
@Component({ @Component({
@ -47,25 +53,43 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
} }
// tslint:disable-next-line: variable-name // tslint:disable-next-line: variable-name
_companySearchData: SearchData; _companySearchData: SearchData;
isBackspaceCanceled = true;
showType: SortViewType; showType: SortViewType;
sortViewType = SortViewType; sortViewType = SortViewType;
private ngOnDestroySubject: Subject<void> = new Subject(); private ngOnDestroySubject: Subject<void> = new Subject();
private loginSession: LoginSession;
constructor( constructor(
private activatedRoute: ActivatedRoute, private activatedRoute: ActivatedRoute,
private router: Router, private router: Router,
private appAuthenticationService: AppAuthenticationService,
private logService: LogService, private logService: LogService,
private i18nService: I18nService, private i18nService: I18nService,
private store: Store<any>, private store: Store<any>,
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
public dialog: MatDialog public dialog: MatDialog,
private appUiService: AppUiService,
private ngZone: NgZone
) {} ) {}
ngOnInit(): void { ngOnInit(): void {
this.showType = SortViewType.all; this.showType = SortViewType.all;
this.showGroupMenuIcon(SortViewType.all); this.showGroupMenuIcon(SortViewType.all);
this.appAuthenticationService
.getLoginSession$()
.pipe(takeUntil(this.ngOnDestroySubject))
.subscribe((loginSession) => {
this.loginSession = loginSession;
if (
!!this.loginSession &&
!!this.loginSession.groupInfo &&
!!this.loginSession.groupInfo.showType
) {
this.showType = this.loginSession.groupInfo.showType;
this.showGroupMenuIcon(this.loginSession.groupInfo.showType);
}
});
} }
ngOnDestroy(): void { ngOnDestroy(): void {
@ -132,6 +156,21 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
} }
break; break;
} }
if (
!!this.loginSession &&
!!this.loginSession.groupInfo &&
!!this.loginSession.groupInfo.showType
) {
this.appAuthenticationService.setLoginSession({
...this.loginSession,
groupInfo: {
groupSeqs: [],
lastGroupSeq: 0,
showType: this.showType
}
});
}
} }
onSearchCancel() { onSearchCancel() {
this.companySearchData = { ...this.companySearchData, searchWord: '' }; this.companySearchData = { ...this.companySearchData, searchWord: '' };
@ -146,6 +185,7 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
} }
onClickUser(userInfo: UserInfo) { onClickUser(userInfo: UserInfo) {
this.ngZone.run(() => {
this.router.navigate( this.router.navigate(
[ [
'group', 'group',
@ -157,5 +197,12 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
queryParams: { id: Number(userInfo.seq) } queryParams: { id: Number(userInfo.seq) }
} }
); );
this.appUiService.closeLeftSidenavOnNarrowMode();
});
}
onOpenProfile(userInfo: UserInfo) {
this.onClickUser(userInfo);
} }
} }

View File

@ -8,7 +8,7 @@ import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu'; import { MatMenuModule } from '@angular/material/menu';
import { MatTabsModule } from '@angular/material/tabs'; import { MatTabsModule } from '@angular/material/tabs';
import { UiModule } from '@ucap/ng-ui'; // import { UiModule } from '@ucap/ng-ui';
import { AppOrganizationModule } from '@app/ucap/organization/organization.module'; import { AppOrganizationModule } from '@app/ucap/organization/organization.module';
@ -34,7 +34,6 @@ import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
AppGroupSectionModule, AppGroupSectionModule,
AppGroupRoutingPageModule, AppGroupRoutingPageModule,
UiModule,
I18nModule I18nModule
], ],
declarations: [...COMPONENTS], declarations: [...COMPONENTS],

View File

@ -9,7 +9,7 @@
width: 200px; width: 200px;
height: 200px; height: 200px;
margin-top: -100px; margin-top: -100px;
background-image: url(../../../../assets/images/ico/img_coming_soon.png); background-image: url(/assets/images/ico/img_coming_soon.png);
background-size: 100% auto; background-size: 100% auto;
} }
.coming-soon-index-copy { .coming-soon-index-copy {

View File

@ -2,6 +2,7 @@
<!-- search start--> <!-- search start-->
<div fxFlex="0 0 50px"> <div fxFlex="0 0 50px">
<app-organization-search-for-tenant <app-organization-search-for-tenant
[isBackspaceCanceled]="isBackspaceCanceled"
[(searchData)]="companySearchData" [(searchData)]="companySearchData"
(canceled)="onCanceledSearch()" (canceled)="onCanceledSearch()"
> >

View File

@ -12,11 +12,11 @@ import { ActivatedRoute, Router, Params } from '@angular/router';
import { ParamsUtil } from '@ucap/ng-core'; import { ParamsUtil } from '@ucap/ng-core';
import { UserStore } from '@app/models/user-store';
import { AppAuthenticationService } from '@app/services/app-authentication.service'; import { AppAuthenticationService } from '@app/services/app-authentication.service';
import { SearchData } from '@app/ucap/organization/models/search-data'; import { SearchData } from '@app/ucap/organization/models/search-data';
import { QueryParams } from '../types/params.type'; import { QueryParams } from '../types/params.type';
import { UserStore } from '@app/models/user-store';
@Component({ @Component({
selector: 'app-pages-organization-index', selector: 'app-pages-organization-index',
@ -36,7 +36,7 @@ export class IndexPageComponent implements OnInit, OnDestroy {
_companySearchData: SearchData; _companySearchData: SearchData;
deptSearchData: SearchData; deptSearchData: SearchData;
isBackspaceCanceled = true;
deptSeq: string; deptSeq: string;
private ngOnDestroySubject: Subject<void> = new Subject(); private ngOnDestroySubject: Subject<void> = new Subject();
@ -107,6 +107,7 @@ export class IndexPageComponent implements OnInit, OnDestroy {
onCanceledSearch() { onCanceledSearch() {
const queryParams: Params = {}; const queryParams: Params = {};
queryParams[QueryParams.DEPT_SEQ] = String(this.deptSeq); queryParams[QueryParams.DEPT_SEQ] = String(this.deptSeq);
this._navigate(queryParams); this._navigate(queryParams);
} }

View File

@ -12,6 +12,7 @@
<div fxFlex="1 1 auto"> <div fxFlex="1 1 auto">
<app-organization-tree <app-organization-tree
[initialExpanded]="initialExpanded" [initialExpanded]="initialExpanded"
[windowSizeMode]="windowSizeMode"
(clicked)="onClickedTree($event)" (clicked)="onClickedTree($event)"
></app-organization-tree> ></app-organization-tree>
</div> </div>

View File

@ -12,12 +12,17 @@ import { Router, ActivatedRoute, Params } from '@angular/router';
import { Store, select } from '@ngrx/store'; import { Store, select } from '@ngrx/store';
import { DeptInfo } from '@ucap/domain-organization';
import { LogService } from '@ucap/ng-logger'; import { LogService } from '@ucap/ng-logger';
import { DeptInfo } from '@ucap/protocol-query';
import { DepartmentSelector, UserSelector } from '@ucap/ng-store-organization'; import { DepartmentSelector, UserSelector } from '@ucap/ng-store-organization';
import { LoginSelector } from '@ucap/ng-store-authentication'; import { LoginSelector } from '@ucap/ng-store-authentication';
import { AppUiService } from '@app/services/app-ui.service';
import { AppSelector } from '@app/store/state';
import { WindowSizeMode } from '@app/types/window-size-mode.type';
import { environment } from '@environments'; import { environment } from '@environments';
import { QueryParams } from '../types/params.type'; import { QueryParams } from '../types/params.type';
@ -32,6 +37,7 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
initialExpanded: number; initialExpanded: number;
displayRoot = false; displayRoot = false;
displayRootDept: DeptInfo; displayRootDept: DeptInfo;
windowSizeMode: WindowSizeMode;
private ngOnDestroySubject: Subject<void> = new Subject(); private ngOnDestroySubject: Subject<void> = new Subject();
@ -40,6 +46,7 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
private activatedRoute: ActivatedRoute, private activatedRoute: ActivatedRoute,
private store: Store<any>, private store: Store<any>,
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
private appUiService: AppUiService,
private logService: LogService private logService: LogService
) { ) {
this.logService.info('app-pages-ogranization-sidenav'); this.logService.info('app-pages-ogranization-sidenav');
@ -91,6 +98,15 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
} }
} }
}); });
this.store
.pipe(
takeUntil(this.ngOnDestroySubject),
select(AppSelector.windowSizeMode)
)
.subscribe((windowSizeMode) => {
this.windowSizeMode = windowSizeMode;
});
} }
ngOnDestroy(): void { ngOnDestroy(): void {
@ -103,7 +119,6 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
onClickedTree(node: DeptInfo) { onClickedTree(node: DeptInfo) {
const queryParams: Params = {}; const queryParams: Params = {};
queryParams[QueryParams.DEPT_SEQ] = String(node.seq); queryParams[QueryParams.DEPT_SEQ] = String(node.seq);
this.router.navigate( this.router.navigate(
[ [
'organization', 'organization',
@ -115,5 +130,6 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
queryParams queryParams
} }
); );
this.appUiService.closeLeftSidenavOnNarrowMode();
} }
} }

View File

@ -13,7 +13,7 @@ import { AppOrganizationRoutingPageModule } from './organization-routing.page.mo
import { COMPONENTS } from './components'; import { COMPONENTS } from './components';
import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n'; import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
import { UiModule } from '@ucap/ng-ui'; // import { UiModule } from '@ucap/ng-ui';
import { OrganizationUiModule } from '@ucap/ng-ui-organization'; import { OrganizationUiModule } from '@ucap/ng-ui-organization';
@NgModule({ @NgModule({
@ -30,8 +30,7 @@ import { OrganizationUiModule } from '@ucap/ng-ui-organization';
AppOrganizationRoutingPageModule, AppOrganizationRoutingPageModule,
I18nModule, I18nModule,
OrganizationUiModule, OrganizationUiModule
UiModule
], ],
declarations: [...COMPONENTS], declarations: [...COMPONENTS],
entryComponents: [], entryComponents: [],

View File

@ -101,13 +101,13 @@ export class AppSessionResolver implements Resolve<void> {
this.store.dispatch( this.store.dispatch(
ConfigurationActions.versionInfo2Success({ ConfigurationActions.versionInfo2Success({
res: versionInfo2Res versionInfo: versionInfo2Res.versionInfo
}) })
); );
this.store.dispatch( this.store.dispatch(
ConfigurationActions.urlInfoSuccess({ ConfigurationActions.urlInfoSuccess({
res: urlInfoRes urlInfo: urlInfoRes.urlInfo
}) })
); );
@ -128,7 +128,9 @@ export class AppSessionResolver implements Resolve<void> {
} }
); );
this.protocolService.connect(versionInfo2Res.serverIp); this.protocolService.connect(
versionInfo2Res.versionInfo.serverIp
);
}, },
(error) => { (error) => {
reject(error); reject(error);

View File

@ -13,7 +13,7 @@ import { MatSelectModule } from '@angular/material/select';
import { MatTabsModule } from '@angular/material/tabs'; import { MatTabsModule } from '@angular/material/tabs';
import { I18nModule } from '@ucap/ng-i18n'; import { I18nModule } from '@ucap/ng-i18n';
import { UiModule } from '@ucap/ng-ui'; // import { UiModule } from '@ucap/ng-ui';
import { AuthenticationUiModule } from '@ucap/ng-ui-authentication'; import { AuthenticationUiModule } from '@ucap/ng-ui-authentication';
import { OrganizationUiModule } from '@ucap/ng-ui-organization'; import { OrganizationUiModule } from '@ucap/ng-ui-organization';
@ -38,7 +38,6 @@ import { COMPONENTS } from './components';
I18nModule, I18nModule,
UiModule,
AuthenticationUiModule, AuthenticationUiModule,
OrganizationUiModule, OrganizationUiModule,

View File

@ -0,0 +1,63 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@angular/flex-layout';
import { ScrollingModule } from '@angular/cdk/scrolling';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip';
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 { CallUiModule } from '@ucap/ng-ui-call';
import { OrganizationUiModule } from '@ucap/ng-ui-organization';
import { AppLayoutsModule } from '@app/layouts/layouts.module';
import { AppOrganizationModule } from '@app/ucap/organization/organization.module';
import { AppGroupSectionModule } from '../group/group.section.module';
import { COMPONENTS } from './components';
import { DIALOGS } from './dialogs';
import { AppCallModule } from '@app/ucap/call/call.module';
@NgModule({
imports: [
CommonModule,
FlexLayoutModule,
MatIconModule,
MatButtonModule,
MatTooltipModule,
MatTabsModule,
MatProgressBarModule,
PerfectScrollbarModule,
ScrollingModule,
I18nModule,
AppLayoutsModule,
AppGroupSectionModule,
AppOrganizationModule,
AppCallModule,
CallUiModule,
OrganizationUiModule
],
exports: [...COMPONENTS, ...DIALOGS],
declarations: [...COMPONENTS, ...DIALOGS],
entryComponents: [...DIALOGS],
providers: [
{
provide: UCAP_I18N_NAMESPACE,
useValue: ['call', 'common']
}
]
})
export class AppCallSectionModule {}

View File

@ -0,0 +1,4 @@
import { InfoSectionComponent } from './info.section.component';
import { ListSectionComponent } from './list.section.component';
export const COMPONENTS = [InfoSectionComponent, ListSectionComponent];

View File

@ -0,0 +1,28 @@
<div class="app-sections-call-info-container" fxLayout="row">
<div class="history-all" fxFlex="1 1 auto">
<div class="subtitle2">{{ 'call:label.callHistory' | ucapI18n }}</div>
<div class="list-container">
<app-call-time-line
[historyList]="originalHistoryList"
></app-call-time-line>
</div>
</div>
<div class="history-other" fxFlex="44%">
<div class="history-away">
<div class="subtitle2">{{ 'call:label.missed' | ucapI18n }}</div>
<div class="list-container">
<app-call-time-line
[historyList]="awayHistoryList"
></app-call-time-line>
</div>
</div>
<div class="history-noreply">
<div class="subtitle2">{{ 'call:label.unanswered' | ucapI18n }}</div>
<div class="list-container">
<app-call-time-line
[historyList]="unansweredHistoryList"
></app-call-time-line>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,32 @@
import { TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { InfoSectionComponent } from './info.section.component';
describe('app::sections::group::InfoSectionComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule],
declarations: [InfoSectionComponent]
}).compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(InfoSectionComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'ucap-lg-web'`, () => {
const fixture = TestBed.createComponent(InfoSectionComponent);
const app = fixture.componentInstance;
});
it('should render title', () => {
const fixture = TestBed.createComponent(InfoSectionComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement;
expect(compiled.querySelector('.content span').textContent).toContain(
'ucap-lg-web app is running!'
);
});
});

View File

@ -0,0 +1,106 @@
import {
Component,
OnInit,
OnDestroy,
ChangeDetectionStrategy,
ChangeDetectorRef,
Input
} from '@angular/core';
import { Store, select } from '@ngrx/store';
import { Subject, combineLatest } from 'rxjs';
import { takeUntil, withLatestFrom } from 'rxjs/operators';
import { CallSelector } from '@ucap/ng-store-call';
import { CallHistory, CallResultType } from '@ucap/domain-call';
import { ActivatedRoute } from '@angular/router';
import { UserSelector } from '@ucap/ng-store-organization';
import { QueryParams } from '@app/pages/call/types/params.type';
@Component({
selector: 'app-sections-call-info',
templateUrl: './info.section.component.html',
styleUrls: ['./info.section.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class InfoSectionComponent implements OnInit, OnDestroy {
isMe: boolean;
userSeq: string;
originalHistoryList: CallHistory[];
awayHistoryList: CallHistory[];
unansweredHistoryList: CallHistory[];
private ngOnDestroySubject: Subject<void> = new Subject();
constructor(
private store: Store<any>,
private activatedRoute: ActivatedRoute,
private changeDetectorRef: ChangeDetectorRef
) {}
ngOnInit(): void {
combineLatest([
this.activatedRoute.queryParams,
this.store.pipe(select(CallSelector.callHistory))
])
.pipe(
takeUntil(this.ngOnDestroySubject),
withLatestFrom(this.store.pipe(select(UserSelector.user)))
)
.subscribe(([[params, callHistory], user]) => {
let existParams = false;
if (!!params) {
const seqParam = params[QueryParams.ID];
// initializing by userSeq Change.
if (this.userSeq !== seqParam) {
}
// setting userSeq.
this.userSeq = !!seqParam
? seqParam
: !!user
? String(user.info.seq)
: undefined;
if (!!user && this.userSeq === String(user.info.seq)) {
this.isMe = true;
} else {
this.isMe = false;
}
this.originalHistoryList = callHistory;
this.getFiltered();
}
});
}
ngOnDestroy(): void {
if (!!this.ngOnDestroySubject) {
this.ngOnDestroySubject.next();
this.ngOnDestroySubject.complete();
}
}
getFiltered(): void {
// original
this.originalHistoryList = this.originalHistoryList.filter((item) => {
let result = true;
if (!this.isMe && !!this.userSeq) {
if (!!item.sendYn && item.sendYn === true) {
result = item.calledUserSeq === this.userSeq;
} else {
result = item.callingUserSeq === this.userSeq;
}
}
return result;
});
// away
this.awayHistoryList = this.originalHistoryList.filter(
(item) => item.callResult === CallResultType.Away
);
// unanswered
this.unansweredHistoryList = this.originalHistoryList.filter(
(item) => item.callResult === CallResultType.Missed
);
this.changeDetectorRef.markForCheck();
}
}

View File

@ -0,0 +1,132 @@
<div fxFlexFill class="list-container">
<div class="select-call-section-content">
<mat-tab-group
#selectUserTabGroup
mat-stretch-tabs
class="tap-container tab_num2"
>
<mat-tab>
<ng-template mat-tab-label>
<p>
<mat-icon>history</mat-icon>
</p>
</ng-template>
<ng-template matTabContent>
<div fxFlexFill class="select-tap">
<div class="sub-title">
{{ 'call:label.callHistory' | ucapI18n
}}<strong>{{ filteredCallHistory.length }}</strong>
</div>
<div class="btns">
<button
mat-button
[ngClass]="filteringByTypeClass(CallHistorySearchType.All)"
(click)="filteringByType(CallHistorySearchType.All)"
>
{{ 'call:label.all' | ucapI18n }}
</button>
<button
mat-button
[ngClass]="filteringByTypeClass(CallHistorySearchType.Send)"
(click)="filteringByType(CallHistorySearchType.Send)"
>
{{ 'call:label.send' | ucapI18n }}
</button>
<button
mat-button
[ngClass]="filteringByTypeClass(CallHistorySearchType.Receive)"
(click)="filteringByType(CallHistorySearchType.Receive)"
>
{{ 'call:label.recieve' | ucapI18n }}
</button>
<button
mat-button
[ngClass]="filteringByTypeClass(CallHistorySearchType.Missed)"
(click)="filteringByType(CallHistorySearchType.Missed)"
>
{{ 'call:label.missed' | ucapI18n }}
</button>
<button
mat-button
[ngClass]="
filteringByTypeClass(CallHistorySearchType.UnAnswered)
"
(click)="filteringByType(CallHistorySearchType.UnAnswered)"
>
{{ 'call:label.unanswered' | ucapI18n }}
</button>
</div>
<div class="list">
<app-call-history-expansion [itemList]="filteredCallHistory">
</app-call-history-expansion>
</div>
</div>
</ng-template>
</mat-tab>
<mat-tab>
<ng-template mat-tab-label>
<p>
<svg
xmlns="http://www.w3.org/2000/svg"
width="21"
height="19"
viewBox="0 0 21 19"
>
<g
id="icon_gnb_organiztion_g32"
transform="translate(-12.917 -220.25)"
>
<g class="prefix__cls-1" transform="translate(19.917 220.25)">
<circle cx="3.5" cy="3.5" r="3.5" class="prefix__cls-3" />
<circle cx="3.5" cy="3.5" r="2.5" class="prefix__cls-4" />
</g>
<g class="prefix__cls-1" transform="translate(12.917 232.25)">
<circle cx="3.5" cy="3.5" r="3.5" class="prefix__cls-3" />
<circle cx="3.5" cy="3.5" r="2.5" class="prefix__cls-4" />
</g>
<g class="prefix__cls-1" transform="translate(19.917 232.25)">
<circle cx="3.5" cy="3.5" r="3.5" class="prefix__cls-3" />
<circle cx="3.5" cy="3.5" r="2.5" class="prefix__cls-4" />
</g>
<g class="prefix__cls-1" transform="translate(26.917 232.25)">
<circle cx="3.5" cy="3.5" r="3.5" class="prefix__cls-3" />
<circle cx="3.5" cy="3.5" r="2.5" class="prefix__cls-4" />
</g>
<path
d="M16.5 233.312v-3.437h13.833v3.438"
transform="translate(0 -1.087)"
style="
stroke-linecap: round;
stroke-linejoin: round;
stroke: #999;
stroke-width: 2px;
fill: none;
"
/>
<path
d="M0 0L0 6"
class="prefix__cls-1"
transform="translate(23.417 226.75)"
/>
</g>
</svg>
</p>
</ng-template>
<ng-template matTabContent>
<div fxFlexFill class="select-tap">
<div class="sub-title">
{{ 'call:label.organization' | ucapI18n }}
</div>
<div class="list">
<app-organization-profile-navigation-list
[checkable]="false"
[useFAB]="true"
>
</app-organization-profile-navigation-list>
</div>
</div>
</ng-template>
</mat-tab>
</mat-tab-group>
</div>
</div>

View File

@ -0,0 +1,4 @@
.list-container {
height: calc(100% - 100px) !important;
min-height: auto !important;
}

View File

@ -0,0 +1,32 @@
import { TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { ListSectionComponent } from './list.section.component';
describe('app::sections::group::ListSectionComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule],
declarations: [ListSectionComponent]
}).compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(ListSectionComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'ucap-lg-web'`, () => {
const fixture = TestBed.createComponent(ListSectionComponent);
const app = fixture.componentInstance;
});
it('should render title', () => {
const fixture = TestBed.createComponent(ListSectionComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement;
expect(compiled.querySelector('.content span').textContent).toContain(
'ucap-lg-web app is running!'
);
});
});

View File

@ -0,0 +1,114 @@
import { Subject } from 'rxjs';
import {
Component,
OnInit,
OnDestroy,
ChangeDetectionStrategy,
ChangeDetectorRef,
Input
} from '@angular/core';
import { LogService } from '@ucap/ng-logger';
import { SearchData } from '@app/ucap/organization/models/search-data';
import { Store, select } from '@ngrx/store';
import { takeUntil } from 'rxjs/operators';
import { CallSelector } from '@ucap/ng-store-call';
import {
CallHistory,
CallHistorySearchType,
CallResultType
} from '@ucap/domain-call';
@Component({
selector: 'app-sections-call-list',
templateUrl: './list.section.component.html',
styleUrls: ['./list.section.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ListSectionComponent implements OnInit, OnDestroy {
@Input()
set searchObj(obj: SearchData) {
this._getFiltered();
}
get searchObj(): SearchData {
return this._searchObj;
}
// tslint:disable-next-line: variable-name
_searchObj: SearchData;
callHistory: CallHistory[];
filteredCallHistory: CallHistory[];
searchType = CallHistorySearchType.All;
CallHistorySearchType = CallHistorySearchType;
private ngOnDestroySubject: Subject<void> = new Subject();
constructor(
private store: Store<any>,
private changeDetectorRef: ChangeDetectorRef,
private logService: LogService
) {}
ngOnInit(): void {
this.store
.pipe(
takeUntil(this.ngOnDestroySubject),
select(CallSelector.callHistory)
)
.subscribe((callHistory) => {
this.callHistory = callHistory;
this._getFiltered();
});
}
ngOnDestroy(): void {
if (!!this.ngOnDestroySubject) {
this.ngOnDestroySubject.next();
this.ngOnDestroySubject.complete();
}
}
filteringByTypeClass(searchType: CallHistorySearchType): string {
return this.searchType === searchType ? 'cdk-active' : '';
}
filteringByType(searchType: CallHistorySearchType) {
this.searchType = searchType;
this._getFiltered();
}
private _getFiltered() {
if (!!this.callHistory && this.callHistory.length > 0) {
switch (this.searchType) {
case CallHistorySearchType.Send:
this.filteredCallHistory = this.callHistory.filter(
(item) => !!item.sendYn
);
break;
case CallHistorySearchType.Receive:
this.filteredCallHistory = this.callHistory.filter(
(item) => !item.sendYn
);
break;
case CallHistorySearchType.Missed:
this.filteredCallHistory = this.callHistory.filter(
(item) => item.callResult === CallResultType.Away
);
break;
case CallHistorySearchType.UnAnswered:
this.filteredCallHistory = this.callHistory.filter(
(item) => item.callResult === CallResultType.Missed
);
break;
default:
// case CallHistorySearchType.All:
this.filteredCallHistory = this.callHistory;
break;
}
} else {
this.filteredCallHistory = [];
}
this.changeDetectorRef.markForCheck();
}
}

View File

@ -0,0 +1,14 @@
<div class="dialog-container">
<app-layouts-default-dialog
[disableClose]="false"
(closed)="onClosed($event)"
class="ucap-dialog-call-dialpad-container"
>
<div appLayoutsDefaultDialog="header">
{{ 'call:label.dialpad' | ucapI18n }}
</div>
<div class="dialog-body" appLayoutsDefaultDialog="body">
<ucap-call-dialpad (sendCall)="onSendCall($event)"></ucap-call-dialpad>
</div>
</app-layouts-default-dialog>
</div>

View File

@ -0,0 +1,75 @@
@import '~@ucap/lg-scss/mixins';
.dialog-container {
width: 100%;
height: 100%;
.dialog-body {
width: 100%;
height: 100%;
.profile {
display: flex;
flex-direction: row;
padding-bottom: 10px;
margin-bottom: 10px;
border-bottom: 1px solid #ccc;
.profile-image {
border-radius: 50%;
overflow: hidden;
width: 36px;
height: 36px;
margin-left: 0;
background-color: #ffe8cb;
img {
max-width: 100%;
height: auto;
vertical-align: top;
border: none;
}
}
.user-info {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
padding-left: 16px;
.user-n-g {
display: flex;
flex-flow: row-reverse nowrap;
align-items: flex-end;
height: 22px;
.user-name {
@include ellipsis-column(1);
height: 22px;
font-size: 14px;
font-weight: 600;
order: 1;
-ms-flex-order: 1;
}
.user-grade {
@include ellipsis(1);
align-self: stretch;
font: {
size: 13px;
}
margin-left: 4px;
order: 0;
-ms-flex-order: 0;
}
.write-date {
font-size: 12px;
}
}
}
}
.contents {
width: 100%;
height: calc(100% - 60px);
overflow: hidden;
perfect-scrollbar {
width: 100%;
height: 100%;
padding-right: 10px;
}
}
}
}

View File

@ -0,0 +1,26 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { TextDetailDialogComponent } from './text-detail.dialog.component';
describe('ucap::ui-organization::CreateChatDialogComponent', () => {
let component: TextDetailDialogComponent;
let fixture: ComponentFixture<TextDetailDialogComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [TextDetailDialogComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TextDetailDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,86 @@
import {
Component,
OnInit,
OnDestroy,
ChangeDetectionStrategy,
ChangeDetectorRef,
Inject
} from '@angular/core';
import {
MatDialogRef,
MAT_DIALOG_DATA,
MatDialog
} from '@angular/material/dialog';
import { Subject } from 'rxjs';
import { Store, select } from '@ngrx/store';
import { takeUntil } from 'rxjs/operators';
import { User } from '@ucap/domain-organization';
import { UserSelector } from '@ucap/ng-store-organization';
import { LoginSelector } from '@ucap/ng-store-authentication';
import { AppCallService } from '@app/services/app-call.service';
import { AppAuthenticationService } from '@app/services/app-authentication.service';
import { LoginSession, LoginInfo } from '@ucap/domain-authentication';
export interface DialpadDialogData {}
export interface DialpadDialogResult {}
@Component({
selector: 'app-dialog-call-dialpad',
templateUrl: './dialpad.dialog.component.html',
styleUrls: ['./dialpad.dialog.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DialpadDialogComponent implements OnInit, OnDestroy {
loginSession: LoginSession;
loginInfo: LoginInfo;
user: User;
private ngOnDestroySubject: Subject<void> = new Subject();
constructor(
private store: Store<any>,
public dialogRef: MatDialogRef<DialpadDialogData, DialpadDialogResult>,
@Inject(MAT_DIALOG_DATA) public data: DialpadDialogData,
private appCallService: AppCallService,
private appAuthenticationService: AppAuthenticationService,
private changeDetectorRef: ChangeDetectorRef
) {}
ngOnInit(): void {
this.loginSession = this.appAuthenticationService.getLoginSession();
this.store
.pipe(takeUntil(this.ngOnDestroySubject), select(UserSelector.user))
.subscribe((user) => {
this.user = user;
});
this.store
.pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginInfo))
.subscribe((loginInfo) => {
this.loginInfo = loginInfo;
});
}
ngOnDestroy(): void {
if (!!this.ngOnDestroySubject) {
this.ngOnDestroySubject.next();
this.ngOnDestroySubject.complete();
}
}
onSendCall(calleeNumber: string) {
this.appCallService.sendClicktocall(
this.loginInfo,
this.user,
this.loginSession.deviceType,
calleeNumber
);
this.dialogRef.close();
}
onClosed(event: MouseEvent): void {
this.dialogRef.close();
}
}

View File

@ -0,0 +1,3 @@
import { DialpadDialogComponent } from './dialpad.dialog.component';
export const DIALOGS = [DialpadDialogComponent];

View File

@ -4,8 +4,6 @@ import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { FlexLayoutModule } from '@angular/flex-layout'; import { FlexLayoutModule } from '@angular/flex-layout';
import { ScrollingModule } from '@angular/cdk/scrolling';
import { MatRippleModule } from '@angular/material/core'; import { MatRippleModule } from '@angular/material/core';
import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatBadgeModule } from '@angular/material/badge'; import { MatBadgeModule } from '@angular/material/badge';
@ -30,7 +28,12 @@ import { MatProgressBarModule } from '@angular/material/progress-bar';
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar'; import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n'; import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
import { UiModule } from '@ucap/ng-ui';
import { UiCoreModule } from '@ucap/ng-ui/core';
import { UiDateModule } from '@ucap/ng-ui/date';
import { UiViewerModule } from '@ucap/ng-ui/viewer';
import { UiscrollingModule } from '@ucap/ng-ui/scrolling';
import { ChatUiModule } from '@ucap/ng-ui-chat'; import { ChatUiModule } from '@ucap/ng-ui-chat';
import { OrganizationUiModule } from '@ucap/ng-ui-organization'; import { OrganizationUiModule } from '@ucap/ng-ui-organization';
import { AppOrganizationModule } from '@app/ucap/organization/organization.module'; import { AppOrganizationModule } from '@app/ucap/organization/organization.module';
@ -42,6 +45,7 @@ import { AppGroupSectionModule } from '../group/group.section.module';
import { COMPONENTS } from './components'; import { COMPONENTS } from './components';
import { DIALOGS } from './dialogs'; import { DIALOGS } from './dialogs';
import { DRAWERS } from './drawers'; import { DRAWERS } from './drawers';
import { DIRECTIVES } from './directives';
import { AppGroupModule } from '@app/ucap/group/group.module'; import { AppGroupModule } from '@app/ucap/group/group.module';
@NgModule({ @NgModule({
@ -73,10 +77,13 @@ import { AppGroupModule } from '@app/ucap/group/group.module';
MatProgressBarModule, MatProgressBarModule,
PerfectScrollbarModule, PerfectScrollbarModule,
ScrollingModule,
I18nModule, I18nModule,
UiModule,
UiCoreModule,
UiDateModule,
UiViewerModule,
UiscrollingModule,
AppLayoutsModule, AppLayoutsModule,
AppGroupSectionModule, AppGroupSectionModule,
@ -87,8 +94,8 @@ import { AppGroupModule } from '@app/ucap/group/group.module';
AppChatModule, AppChatModule,
AppGroupModule AppGroupModule
], ],
exports: [...COMPONENTS, ...DIALOGS, ...DRAWERS], exports: [...COMPONENTS, ...DIALOGS, ...DRAWERS, ...DIRECTIVES],
declarations: [...COMPONENTS, ...DIALOGS, ...DRAWERS], declarations: [...COMPONENTS, ...DIALOGS, ...DRAWERS, ...DIRECTIVES],
entryComponents: [...DIALOGS, ...DRAWERS], entryComponents: [...DIALOGS, ...DRAWERS],
providers: [ providers: [
{ {

View File

@ -3,15 +3,13 @@
<app-chat-selector-sticker <app-chat-selector-sticker
*ngSwitchCase="SelectorType.STICKER" *ngSwitchCase="SelectorType.STICKER"
(selectedSticker)="onSelectedSticker($event)" (selectedSticker)="onSelectedSticker($event)"
(closed)="selectorType = SelectorType.EMPTY" (closed)="clearSelector()"
> >
</app-chat-selector-sticker> </app-chat-selector-sticker>
<app-chat-selector-translation <app-chat-selector-translation
*ngSwitchCase="SelectorType.TRANSLATION" *ngSwitchCase="SelectorType.TRANSLATION"
(closed)=" (closed)="clearSelector()"
selectorType = SelectorType.EMPTY; translationPreviewInfo = null
"
[destLocale]="destLocale" [destLocale]="destLocale"
[simpleView]="translationSimpleview" [simpleView]="translationSimpleview"
[preView]="translationPreview" [preView]="translationPreview"
@ -26,18 +24,36 @@
<app-chat-selector-file-upload <app-chat-selector-file-upload
#fileUploadSelector #fileUploadSelector
*ngSwitchCase="SelectorType.FILEUPLOAD" *ngSwitchCase="SelectorType.FILEUPLOAD"
(closed)="selectorType = SelectorType.EMPTY" (closed)="clearSelector()"
> >
</app-chat-selector-file-upload> </app-chat-selector-file-upload>
<app-chat-selector-email-send <app-chat-selector-email-send
*ngSwitchCase="SelectorType.EMAILSENDER" *ngSwitchCase="SelectorType.EMAILSENDER"
(sendEventEmail)="onSendEventEmail($event)" (sendEventEmail)="onSendEventEmail($event)"
(closed)="selectorType = SelectorType.EMPTY" (closed)="clearSelector()"
></app-chat-selector-email-send> ></app-chat-selector-email-send>
</ng-container> </ng-container>
<div class="chat-form-area ucap-mat-input-container"> <div class="chat-form-area ucap-mat-input-container">
<!-- <div class="message-text" fxFlex floatLabel="never" appearance="none">
<textarea
#messageInput
placeholder="{{ 'chat:label.inputChatMessage' | ucapI18n }}"
name="message"
(keydown)="textareaResize($event, messageInput)"
(keydown.enter)="onKeydown($event)"
></textarea>
</div> -->
<!-- <mat-form-field
class="message-text"
fxFlex
floatLabel="never"
appearance="none"
>
<mat-label>{{ 'chat:label.inputChatMessage' | ucapI18n }}</mat-label>
<textarea matInput #messageInput></textarea>
</mat-form-field> -->
<mat-form-field <mat-form-field
class="message-text" class="message-text"
fxFlex fxFlex
@ -45,19 +61,13 @@
appearance="none" appearance="none"
> >
<mat-label>{{ 'chat:label.inputChatMessage' | ucapI18n }}</mat-label> <mat-label>{{ 'chat:label.inputChatMessage' | ucapI18n }}</mat-label>
<!-- <textarea <textarea
matInput matInput
#messageInput #messageInput
name="message" name="message"
[matTextareaAutosize]="true" [matTextareaAutosize]="true"
(keydown.enter)="onKeydown($event)" (keydown.enter)="onKeydown($event)"
></textarea> --> ></textarea>
<input
matInput
#messageInput
name="message"
(keydown.enter)="onKeydown($event)"
/>
</mat-form-field> </mat-form-field>
<input <input
@ -94,7 +104,7 @@
</button> </button>
<!-- <button <!-- <button
mat-icon-button mat-icon-button
*ngIf="!!authRes && !!authRes.useCapturePcScreen" *ngIf="!!userPermission && !!userPermission.useCapturePcScreen"
aria-label="screenshot" aria-label="screenshot"
matTooltipPosition="above" matTooltipPosition="above"
matTooltip="{{ 'label.screenshot' | ucapI18n }}" matTooltip="{{ 'label.screenshot' | ucapI18n }}"
@ -115,7 +125,7 @@
</button> </button>
<button <button
mat-icon-button mat-icon-button
*ngIf="!!authRes && !!authRes.canSendEmail" *ngIf="!!userPermission && !!userPermission.canSendEmail"
aria-label="emailSend" aria-label="emailSend"
matTooltipPosition="above" matTooltipPosition="above"
matTooltip="{{ 'label.emailSend' | ucapI18n }}" matTooltip="{{ 'label.emailSend' | ucapI18n }}"
@ -126,7 +136,7 @@
</button> </button>
<button <button
mat-icon-button mat-icon-button
*ngIf="!!authRes && !!authRes.canTranslation" *ngIf="!!userPermission && !!userPermission.canTranslation"
aria-label="translation" aria-label="translation"
matTooltipPosition="above" matTooltipPosition="above"
matTooltip="{{ 'label.translation' | ucapI18n }}" matTooltip="{{ 'label.translation' | ucapI18n }}"
@ -137,7 +147,7 @@
</button> </button>
<!-- <button <!-- <button
mat-icon-button mat-icon-button
*ngIf="!!authRes && !!authRes.useGams" *ngIf="!!userPermission && !!userPermission.useGams"
aria-label="gams" aria-label="gams"
matTooltipPosition="above" matTooltipPosition="above"
matTooltip="{{ 'label.gams' | ucapI18n }}" matTooltip="{{ 'label.gams' | ucapI18n }}"

View File

@ -10,7 +10,7 @@
max-height: 100%; max-height: 100%;
min-height: 20px; min-height: 20px;
background-color: $white; background-color: $white;
padding-left: 30px; padding: 0 20px;
font-size: 0.929em; font-size: 0.929em;
margin: 8px 0; margin: 8px 0;
overflow-x: hidden; overflow-x: hidden;
@ -19,11 +19,15 @@
@include screen(xs) { @include screen(xs) {
padding-left: 16px; padding-left: 16px;
} }
.message-text {
@include font-family($font-regular);
textarea { textarea {
min-height: 22px; min-height: 22px;
margin: 0;
overflow: hidden; overflow: hidden;
} }
} }
}
.button-area { .button-area {
display: flex; display: flex;
flex-direction: row; flex-direction: row;

View File

@ -18,28 +18,29 @@ import { Store, select } from '@ngrx/store';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { FileUploadItem } from '@ucap/domain-common';
import { User } from '@ucap/domain-organization';
import { UserPermission } from '@ucap/domain-authorization';
import {
RoomInfo,
EventType,
MassTranslationEventJson,
TranslationEventJson
} from '@ucap/domain-chat';
import { StickerFilesInfo } from '@ucap/ng-core'; import { StickerFilesInfo } from '@ucap/ng-core';
import { StatusCode, FileUploadItem } from '@ucap/api'; import { StatusCode } from '@ucap/api';
import { import {
TranslationSaveResponse, TranslationSaveResponse,
TranslationSaveRequest TranslationSaveRequest
} from '@ucap/api-common'; } from '@ucap/api-common';
import { VersionInfo2Response } from '@ucap/api-public';
import { import {
SendEventMailType, SendEventMailType,
SendEventEmailRequest, SendEventEmailRequest,
SendEventEmailResponse, SendEventEmailResponse,
StatusCode as PiStatusCode StatusCode as PiStatusCode
} from '@ucap/pi'; } from '@ucap/pi';
import { RoomInfo } from '@ucap/protocol-room';
import {
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 { I18nService } from '@ucap/ng-i18n'; import { I18nService } from '@ucap/ng-i18n';
import { LogService } from '@ucap/ng-logger'; import { LogService } from '@ucap/ng-logger';
@ -61,7 +62,7 @@ import {
ConfirmDialogComponent, ConfirmDialogComponent,
ConfirmDialogData, ConfirmDialogData,
ConfirmDialogResult ConfirmDialogResult
} from '@ucap/ng-ui'; } from '@ucap/ng-ui/core';
import { LoginSession } from '@app/models/login-session'; import { LoginSession } from '@app/models/login-session';
import { AppAuthenticationService } from '@app/services/app-authentication.service'; import { AppAuthenticationService } from '@app/services/app-authentication.service';
@ -70,6 +71,7 @@ import { FileUploadSelectorComponent } from '@app/ucap/chat/components/file-uplo
import { AppChatService } from '@app/services/app-chat.service'; import { AppChatService } from '@app/services/app-chat.service';
import { environment } from '@environments'; import { environment } from '@environments';
import { LoginInfo, VersionInfo } from '@ucap/domain-authentication';
export enum SelectorType { export enum SelectorType {
EMPTY = '', EMPTY = '',
@ -107,14 +109,17 @@ export class FormSectionComponent implements OnInit, OnDestroy {
@Output() @Output()
changeTranslationSimpleview = new EventEmitter<boolean>(); changeTranslationSimpleview = new EventEmitter<boolean>();
@Output()
openFormSelector = new EventEmitter<string>();
@Output() @Output()
eventSendTrigger = new EventEmitter<any>(); eventSendTrigger = new EventEmitter<any>();
versionInfo2Res: VersionInfo2Response; versionInfo: VersionInfo;
loginSession: LoginSession; loginSession: LoginSession;
loginRes: LoginResponse; loginInfo: LoginInfo;
user: User; user: User;
authRes: AuthResponse; userPermission: UserPermission;
currentRoomInfo: RoomInfo; currentRoomInfo: RoomInfo;
@ -125,8 +130,8 @@ export class FormSectionComponent implements OnInit, OnDestroy {
/** About Translation */ /** About Translation */
isTranslationProcess = false; isTranslationProcess = false;
translationSimpleview = true; translationSimpleview = false;
translationPreview = true; translationPreview = false;
destLocale = 'en'; // default English :: en destLocale = 'en'; // default English :: en
translationPreviewInfo: { translationPreviewInfo: {
previewInfo: TranslationSaveResponse | null; previewInfo: TranslationSaveResponse | null;
@ -159,10 +164,10 @@ export class FormSectionComponent implements OnInit, OnDestroy {
this.store this.store
.pipe( .pipe(
takeUntil(this.ngOnDestroySubject), takeUntil(this.ngOnDestroySubject),
select(ConfigurationSelector.versionInfo2Response) select(ConfigurationSelector.versionInfo)
) )
.subscribe((versionInfo2Res) => { .subscribe((versionInfo) => {
this.versionInfo2Res = versionInfo2Res; this.versionInfo = versionInfo;
}); });
this.store this.store
@ -172,18 +177,18 @@ export class FormSectionComponent implements OnInit, OnDestroy {
}); });
this.store this.store
.pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes)) .pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginInfo))
.subscribe((loginRes) => { .subscribe((loginInfo) => {
this.loginRes = loginRes; this.loginInfo = loginInfo;
}); });
this.store this.store
.pipe( .pipe(
takeUntil(this.ngOnDestroySubject), takeUntil(this.ngOnDestroySubject),
select(AuthorizationSelector.authResponse) select(AuthorizationSelector.userPermission)
) )
.subscribe((authRes) => { .subscribe((userPermission) => {
this.authRes = authRes; this.userPermission = userPermission;
}); });
} }
@ -206,9 +211,10 @@ export class FormSectionComponent implements OnInit, OnDestroy {
this.selectorType = SelectorType.EMPTY; this.selectorType = SelectorType.EMPTY;
this.translationSimpleview = false; this.translationSimpleview = false;
this.changeTranslationSimpleview.emit(false); this.changeTranslationSimpleview.emit(false);
this.translationPreview = true; this.translationPreview = false;
this.destLocale = 'en'; // default English :: en this.destLocale = 'en'; // default English :: en
this.translationPreviewInfo = null; this.translationPreviewInfo = null;
this.selectedSticker = undefined;
this.store this.store
.pipe( .pipe(
@ -225,11 +231,19 @@ export class FormSectionComponent implements OnInit, OnDestroy {
/** About Selector */ /** About Selector */
onOpenSelector(type: SelectorType): void { onOpenSelector(type: SelectorType): void {
this.selectorType = type; this.selectorType = type;
this.changeDetectorRef.markForCheck();
this.selectedSticker = null;
this.translationPreviewInfo = null;
this.changeDetectorRef.detectChanges();
this.openFormSelector.emit(type);
} }
clearSelector(): void { clearSelector(): void {
this.selectorType = SelectorType.EMPTY; this.selectorType = SelectorType.EMPTY;
this.selectedSticker = null; this.selectedSticker = null;
this.translationPreviewInfo = null;
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
} }
@ -246,62 +260,6 @@ export class FormSectionComponent implements OnInit, OnDestroy {
} }
} }
onChangeFileInput(): void {
const self = this;
const fileList = this.fileInput.nativeElement.files;
this.appFileService
.validUploadFile(fileList, this.versionInfo2Res?.fileAllowSize)
.then((result) => {
if (!result) {
self.fileInput.nativeElement.value = '';
return;
} else {
// selector open
self.onOpenSelector(SelectorType.FILEUPLOAD);
self.changeDetectorRef.detectChanges();
// FileuploadItem Init. & FileSelector Init.
const fileUploadItems = FileUploadItem.fromFiles(fileList);
if (!!self.fileUploadSelector) {
self.fileUploadSelector.onFileSelected(fileUploadItems);
}
self.fileInput.nativeElement.value = '';
// File Upload..
self.appChatService
.sendMessageOfAttachFile(
self.loginRes,
self.user,
self.loginSession.deviceType,
self.currentRoomInfo.roomId,
fileUploadItems
)
.then((success) => {
if (!!success) {
self.clearSelector();
if (!!self.fileUploadSelector) {
self.fileUploadSelector.onUploadComplete();
}
}
})
.catch((err) => {
self.clearSelector();
if (!!self.fileUploadSelector) {
self.fileUploadSelector.onUploadComplete();
}
const msg = this.i18nService.t('common:file.errors.failToUpload');
alert(msg);
});
}
})
.catch((err) => {
self.fileInput.nativeElement.value = '';
self.logService.error(`validUploadFile ${err}`);
});
}
onKeydown(event: KeyboardEvent) { onKeydown(event: KeyboardEvent) {
// if (event.key === 'PageUp' || event.key === 'PageDown') { // if (event.key === 'PageUp' || event.key === 'PageDown') {
// event.preventDefault(); // event.preventDefault();
@ -311,10 +269,17 @@ export class FormSectionComponent implements OnInit, OnDestroy {
// this.send(); // this.send();
// } // }
event.preventDefault(); event.preventDefault();
event.stopPropagation();
this.send(); this.send();
} }
textareaResize(event: KeyboardEvent, obj) {
const self = obj;
setTimeout(() => {
self.style.height = '1px';
self.style.height = self.scrollHeight + 'px';
});
}
onSelectedSticker(stickerInfo: StickerFilesInfo) { onSelectedSticker(stickerInfo: StickerFilesInfo) {
this.selectedSticker = stickerInfo; this.selectedSticker = stickerInfo;
this.focus(false); this.focus(false);
@ -412,7 +377,7 @@ export class FormSectionComponent implements OnInit, OnDestroy {
) { ) {
/** CASE : MASS TEXT */ /** CASE : MASS TEXT */
this.appChatService.sendMessageOfMassText( this.appChatService.sendMessageOfMassText(
this.loginRes, this.loginInfo,
this.user, this.user,
this.loginSession.deviceType, this.loginSession.deviceType,
roomId, roomId,
@ -437,7 +402,7 @@ export class FormSectionComponent implements OnInit, OnDestroy {
this.commonApiService this.commonApiService
.translationSave({ .translationSave({
userSeq: String(this.user.info.seq), userSeq: String(this.user.info.seq),
token: this.loginRes.tokenString, token: this.loginInfo.tokenString,
deviceType: this.loginSession.deviceType, deviceType: this.loginSession.deviceType,
original: message, original: message,
roomId: this.roomId, roomId: this.roomId,
@ -547,6 +512,7 @@ export class FormSectionComponent implements OnInit, OnDestroy {
sentMessage sentMessage
); );
this.translationPreviewInfo = undefined; this.translationPreviewInfo = undefined;
this.eventSendTrigger.emit(0);
this.focus(); this.focus();
} }
@ -580,7 +546,7 @@ export class FormSectionComponent implements OnInit, OnDestroy {
const req: SendEventEmailRequest = { const req: SendEventEmailRequest = {
userSeq: String(this.user.info.seq), userSeq: String(this.user.info.seq),
deviceType: this.loginSession.deviceType, deviceType: this.loginSession.deviceType,
tokenKey: this.loginRes.tokenString, tokenKey: this.loginInfo.tokenString,
roomSeq: this._roomId, roomSeq: this._roomId,
eventSeq: String(eventList[0].seq), eventSeq: String(eventList[0].seq),
sendType: type sendType: type
@ -624,4 +590,100 @@ export class FormSectionComponent implements OnInit, OnDestroy {
} }
}); });
} }
/**
* About File Upload
*/
/** FileInput change event. */
onChangeFileInput(): void {
const fileList = this.fileInput.nativeElement.files;
this._fileSend(fileList);
}
/** DragAndDrop :: Add fileupload queue. not sending. */
onDragAndDropStandbyFileupload(fileList: FileList) {
const self = this;
this.appFileService
.validUploadFile(fileList, this.versionInfo?.fileAllowSize)
.then((result) => {
if (!result) {
self.fileInput.nativeElement.value = '';
return;
} else {
// selector open
self.onOpenSelector(SelectorType.FILEUPLOAD);
// FileuploadItem Init. & FileSelector Init.
const fileUploadItems = FileUploadItem.fromFiles(fileList);
if (!!self.fileUploadSelector) {
self.fileUploadSelector.onFileSelected(fileUploadItems);
}
self.fileInput.nativeElement.value = '';
}
})
.catch((err) => {
self.fileInput.nativeElement.value = '';
self.logService.error(`validUploadFile ${err}`);
});
}
/** DragAndDrop :: fileUpload direct. */
onDragAndDropFileupload(fileList: FileList): void {
this._fileSend(fileList);
}
private _fileSend(fileList: FileList) {
const self = this;
this.appFileService
.validUploadFile(fileList, this.versionInfo?.fileAllowSize)
.then((result) => {
if (!result) {
self.fileInput.nativeElement.value = '';
return;
} else {
// selector open
self.onOpenSelector(SelectorType.FILEUPLOAD);
self.changeDetectorRef.detectChanges();
// FileuploadItem Init. & FileSelector Init.
const fileUploadItems = FileUploadItem.fromFiles(fileList);
if (!!self.fileUploadSelector) {
self.fileUploadSelector.onFileSelected(fileUploadItems);
}
self.fileInput.nativeElement.value = '';
// File Upload..
self.appChatService
.sendMessageOfAttachFile(
self.loginInfo,
self.user,
self.loginSession.deviceType,
self.currentRoomInfo.roomId,
fileUploadItems
)
.then((success) => {
if (!!success) {
self.eventSendTrigger.emit(0);
self.clearSelector();
if (!!self.fileUploadSelector) {
self.fileUploadSelector.onUploadComplete();
}
}
})
.catch((err) => {
self.clearSelector();
if (!!self.fileUploadSelector) {
self.fileUploadSelector.onUploadComplete();
}
const msg = this.i18nService.t('common:file.errors.failToUpload');
alert(msg);
});
}
})
.catch((err) => {
self.fileInput.nativeElement.value = '';
self.logService.error(`validUploadFile ${err}`);
});
}
} }

View File

@ -1,11 +1,19 @@
<mat-toolbar class="info-chat-toolbar"> <mat-toolbar class="info-chat-toolbar">
<mat-toolbar-row class="info-chat-toolbar-content"> <mat-toolbar-row class="info-chat-toolbar-content">
<div class="chat-room-profile"> <div class="chat-room-profile">
<div class="profile-image"> <div
class="profile-image"
(click)="onOpenProfile()"
[style.cursor]="
!!currentRoomInfo && currentRoomInfo.roomType === RoomType.Single
? 'pointer'
: 'default'
"
>
<img <img
class="thumbnail" class="thumbnail"
ucapImage ucapImage
[base]="versionInfo2Res?.profileRoot" [base]="versionInfo?.profileRoot"
[path]="roomImage" [path]="roomImage"
[default]=" [default]="
currentRoomInfo?.roomType === RoomType.Multi currentRoomInfo?.roomType === RoomType.Multi

View File

@ -13,15 +13,16 @@ import {
} from '@angular/core'; } from '@angular/core';
import { Store, select } from '@ngrx/store'; import { Store, select } from '@ngrx/store';
import { Dictionary } from '@ngrx/entity';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { LocaleCode } from '@ucap/core'; import { LocaleCode } from '@ucap/domain-common';
import { User } from '@ucap/domain-organization';
import { RoomInfo, RoomType } from '@ucap/domain-chat';
import { I18nService } from '@ucap/ng-i18n'; import { I18nService } from '@ucap/ng-i18n';
import { VersionInfo2Response } from '@ucap/api-public';
import { RoomInfo, RoomType, UpdateRequest } from '@ucap/protocol-room'; import { UpdateRequest } from '@ucap/protocol-room';
import { User } from '@ucap/protocol-info';
import { UserSelector } from '@ucap/ng-store-organization'; import { UserSelector } from '@ucap/ng-store-organization';
import { ConfigurationSelector } from '@ucap/ng-store-authentication'; import { ConfigurationSelector } from '@ucap/ng-store-authentication';
@ -40,6 +41,12 @@ import {
import { AppChatService } from '@app/services/app-chat.service'; import { AppChatService } from '@app/services/app-chat.service';
import { ChatDrawType } from '@app/pages/chat/types/chat-draw.type'; import { ChatDrawType } from '@app/pages/chat/types/chat-draw.type';
import { DrawInfo } from '@app/pages/chat/models/draw-info'; import { DrawInfo } from '@app/pages/chat/models/draw-info';
import {
ProfileDialogComponent,
ProfileDialogData,
ProfileDialogResult
} from '@app/sections/organization/dialogs/profile.dialog.component';
import { VersionInfo } from '@ucap/domain-authentication';
@Component({ @Component({
selector: 'app-sections-chat-info', selector: 'app-sections-chat-info',
@ -53,11 +60,13 @@ export class InfoSectionComponent implements OnInit, OnDestroy {
@Input() @Input()
set roomId(roomId: string) { set roomId(roomId: string) {
if (this._roomId !== roomId) {
this._roomId = roomId; this._roomId = roomId;
this.roomIdSubject.next(roomId); this.roomIdSubject.next(roomId);
this.initializeRoomData(); this.initializeRoomData();
}
// request selected room // request selected room
if (!!this.roomId) { if (!!this.roomId) {
@ -81,7 +90,7 @@ export class InfoSectionComponent implements OnInit, OnDestroy {
@Output() @Output()
rightDrawerToggle = new EventEmitter<DrawInfo>(); rightDrawerToggle = new EventEmitter<DrawInfo>();
versionInfo2Res: VersionInfo2Response; versionInfo: VersionInfo;
user: User; user: User;
defaultProfileImage: string; defaultProfileImage: string;
@ -123,10 +132,10 @@ export class InfoSectionComponent implements OnInit, OnDestroy {
this.store this.store
.pipe( .pipe(
takeUntil(this.ngOnDestroySubject), takeUntil(this.ngOnDestroySubject),
select(ConfigurationSelector.versionInfo2Response) select(ConfigurationSelector.versionInfo)
) )
.subscribe((versionInfo2Res) => { .subscribe((versionInfo) => {
this.versionInfo2Res = versionInfo2Res; this.versionInfo = versionInfo;
}); });
} }
@ -240,6 +249,36 @@ export class InfoSectionComponent implements OnInit, OnDestroy {
} }
} }
onOpenProfile(): void {
if (
!!this.currentRoomInfo &&
this.currentRoomInfo.roomType === RoomType.Single
) {
const returnValue = this.appChatService.getRoomUserList01(
this.user,
this.roomUsersMap,
this.roomUsersShortMap
);
if (
!!returnValue &&
!!returnValue.existUsers &&
returnValue.users.length > 0
) {
const userSeq = String(returnValue.users[0].seq);
this.dialog.open<
ProfileDialogComponent,
ProfileDialogData,
ProfileDialogResult
>(ProfileDialogComponent, {
panelClass: 'mid-create-dialog',
data: {
userSeq
}
});
}
}
}
getShowContextMenu(menuType: string) { getShowContextMenu(menuType: string) {
if ( if (
['EVENT', 'ROOM_USERS', 'CHANGE_ROOM_USERS', 'ADD_GROUP', 'SETTING'].some( ['EVENT', 'ROOM_USERS', 'CHANGE_ROOM_USERS', 'ADD_GROUP', 'SETTING'].some(

View File

@ -1,4 +1,4 @@
.list-container { .list-container {
height: calc(100% - 90px) !important; height: calc(100% - 100px) !important;
min-height: auto !important; min-height: auto !important;
} }

View File

@ -11,12 +11,15 @@ import {
EventEmitter, EventEmitter,
Output Output
} from '@angular/core'; } from '@angular/core';
import { Router, ActivatedRoute, Params } from '@angular/router';
import { RoomInfo } from '@ucap/domain-chat';
import { LogService } from '@ucap/ng-logger'; import { LogService } from '@ucap/ng-logger';
import { RoomInfo } from '@ucap/protocol-room';
import { Router, ActivatedRoute, Params } from '@angular/router';
import { QueryParams } from '@app/pages/chat/types/params.type'; import { QueryParams } from '@app/pages/chat/types/params.type';
import { SearchInfo } from '@app/pages/chat/models/search-info'; import { SearchInfo } from '@app/pages/chat/models/search-info';
import { AppChatService } from '@app/services/app-chat.service';
@Component({ @Component({
selector: 'app-sections-chat-list', selector: 'app-sections-chat-list',
@ -51,6 +54,7 @@ export class ListSectionComponent implements OnInit, OnDestroy {
constructor( constructor(
private router: Router, private router: Router,
private activatedRoute: ActivatedRoute, private activatedRoute: ActivatedRoute,
private appChatService: AppChatService,
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
private logService: LogService private logService: LogService
) {} ) {}
@ -76,17 +80,7 @@ export class ListSectionComponent implements OnInit, OnDestroy {
} }
onOpenChatRoom(roomInfo: RoomInfo): void { onOpenChatRoom(roomInfo: RoomInfo): void {
this.router.navigate( this.appChatService.openRoombyRoomId(roomInfo.roomId);
[
'chat',
{
outlets: { content: 'chatroom' }
}
],
{
queryParams: { roomId: roomInfo.roomId }
}
);
} }
onSearchResultList(searchResultList: RoomInfo[]) { onSearchResultList(searchResultList: RoomInfo[]) {

View File

@ -1,13 +1,13 @@
<div #chatMessagesContainer class="ucap-message-section"> <!-- <div class="ucap-message-section"> -->
<button <button
mat-stroked-button mat-stroked-button
color="accent" color="accent"
class="icon-button-arrow top-position" class="icon-button-arrow top-position"
(click)="gotoScrollToBottom()" (click)="gotoScrollToBottom()"
> >
<mat-icon>arrow_downward</mat-icon> <mat-icon>arrow_downward</mat-icon>
</button> </button>
<!--대화창 위로 보내기 <!--대화창 위로 보내기
<button <button
mat-stroked-button mat-stroked-button
color="accent" color="accent"
@ -16,8 +16,21 @@
<mat-icon>arrow_upward</mat-icon> <mat-icon>arrow_upward</mat-icon>
</button>--> </button>-->
<div #chatMessagesList class="chat-area"> <ucap-virtual-scroll-viewport
<div class="ucap-chat-more-event" *ngIf="!!currentChatting?.remainEvent"> #vsList
fxFlexFill
class="ucap-message-section"
perfectScrollbar
measureSize
minBufferPx="200"
maxBufferPx="300"
(contentSizeChanged)="onContentSizeChanged($event)"
(measured)="onMeasured()"
(psYReachEnd)="isScrollReachBottom = true; resetRecentMessage()"
(psScrollUp)="isScrollReachBottom = false"
>
<ng-template ucapVirtualScrollHeader>
<div class="ucap-chat-more-event" *ngIf="!!isRemainEvent">
<button <button
mat-button mat-button
class="btn-more-pre bg-primary-chat" class="btn-more-pre bg-primary-chat"
@ -28,31 +41,79 @@
>{{ 'event.showPreviousEvents' | ucapI18n }} >{{ 'event.showPreviousEvents' | ucapI18n }}
</button> </button>
</div> </div>
</ng-template>
<div class="chat-area">
<app-chat-message-box <app-chat-message-box
*ngFor="let event of eventList" *ucapVirtualFor="
let event of eventList;
keyOf: keyOf;
templateCacheSize: 0
"
[message]="event" [message]="event"
[roomInfo]="currentRoomInfo" [roomInfo]="currentRoomInfo"
[isMe]="event.senderSeq + '' === String(user?.info?.seq) + ''" [loginSession]="loginSession"
[loginInfo]="loginInfo"
[user]="user"
[userPermission]="userPermission"
[isMe]="String(user?.info?.seq) === String(event.senderSeq)"
[translationSimpleview]="translationSimpleview" [translationSimpleview]="translationSimpleview"
[senderInfo]="getSenderInfo(event.senderSeq)" [senderInfo]="getSenderInfo(event.senderSeq)"
[defaultProfileImage]="defaultProfileImage" [defaultProfileImage]="defaultProfileImage"
[profileImageRoot]="versionInfo2Res?.profileRoot" [profileImageRoot]="versionInfo?.profileRoot"
[dateChanged]="getDateSplitter(event)"
[unreadCount]="getUnreadCount(event)"
(messageContextMenu)="onClickMessageContextMenu($event)"
(fileViewer)="onFileViewer($event)"
(fileSave)="onFileSave($event)"
(massTranslationDetail)="onMassTranslationDetail($event)"
(openProfile)="onOpenProfile($event)"
(joinConference)="onJoinConference($event)"
>
</app-chat-message-box>
<!-- <app-chat-message-box
*ngFor="let event of eventList; trackBy: trackByEvent"
[message]="event"
></app-chat-message-box> -->
<!-- <app-chat-message-box
*ngFor="let event of eventList; trackBy: trackByEvent"
[message]="event"
[roomInfo]="currentRoomInfo"
[loginInfo]="loginInfo"
[user]="user"
[loginSession]="loginSession"
[isMe]="String(user?.info?.seq) === String(event.senderSeq)"
[translationSimpleview]="translationSimpleview"
[senderInfo]="getSenderInfo(event.senderSeq)"
[defaultProfileImage]="defaultProfileImage"
[profileImageRoot]="versionInfo?.profileRoot"
[dateChanged]="getDateSplitter(event)" [dateChanged]="getDateSplitter(event)"
[unreadCount]="getUnreadCount(event)" [unreadCount]="getUnreadCount(event)"
(messageContextMenu)="onClickMessageContextMenu($event)" (messageContextMenu)="onClickMessageContextMenu($event)"
(fileViewer)="onFileViewer($event)" (fileViewer)="onFileViewer($event)"
(massTranslationDetail)="onMassTranslationDetail($event)" (massTranslationDetail)="onMassTranslationDetail($event)"
(openProfile)="onOpenProfile($event)" (openProfile)="onOpenProfile($event)"
></app-chat-message-box> ></app-chat-message-box> -->
<!-- <p *ngFor="let event of eventList; trackBy: trackByEvent">
<div *ngIf="false" class="recent-receive-message"> {{ event.seq }} / {{ event.type }}
<!-- 최근 메시지 영역 --> </p> -->
<!-- <div *ngIf="!!recentUserInfo" class="recent-receive-message">
<app-chat-recent-message <app-chat-recent-message
[senderInfo]="getSenderInfo(user?.info?.seq)" [senderInfo]="recentUserInfo"
[message]="recentMessage"
[defaultProfileImage]="defaultProfileImage" [defaultProfileImage]="defaultProfileImage"
[profileImageRoot]="versionInfo2Res?.profileRoot" [profileImageRoot]="versionInfo?.profileRoot"
(gotoBottom)="gotoScrollToBottom()"
></app-chat-recent-message> ></app-chat-recent-message>
</div> -->
</div> </div>
</div> </ucap-virtual-scroll-viewport>
<div *ngIf="!!recentUserInfo" class="recent-receive-message">
<app-chat-recent-message
[senderInfo]="recentUserInfo"
[message]="recentMessage"
[defaultProfileImage]="defaultProfileImage"
[profileImageRoot]="versionInfo?.profileRoot"
(gotoBottom)="gotoScrollToBottom()"
></app-chat-recent-message>
</div> </div>
<!-- </div> -->

View File

@ -1,5 +1,30 @@
@import '~@ucap/lg-scss/mixins'; @import '~@ucap/lg-scss/mixins';
.icon-button-arrow {
@include ucapMatButton(36px, 36px, 6px, 36px);
border-color: $lipstick;
background-color: rgba(255, 255, 255, 0.5);
box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.16);
// position: sticky;
position: absolute;
right: 20px;
z-index: 5;
width: 36px;
// align-self: flex-end;
// margin-right: -10px;
&.top-position {
bottom: 90%;
order: 2;
}
&.bottom-position {
bottom: 30px;
order: 3;
}
&:hover {
background-color: rgba(255, 255, 255, 0.8);
}
}
.ucap-message-section { .ucap-message-section {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -11,43 +36,30 @@
padding: 0 16px; padding: 0 16px;
min-width: 100%; min-width: 100%;
} }
.icon-button-arrow {
@include ucapMatButton(36px, 36px, 6px, 36px); .ucap-chat-more-event {
border-color: $lipstick; padding: 20px 30px 0;
background-color: rgba(255, 255, 255, 0.5); .btn-more-pre {
box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.16); @include ucapMatButton(100%, 30px, 2px, 30px);
position: sticky; font-size: 0.857em;
z-index: 5; @include screen(xs) {
width: 36px; padding: 0 16px;
align-self: flex-end;
margin-right: -10px;
&.top-position {
bottom: 90%;
order: 2;
} }
&.bottom-position {
bottom: 30px;
order: 3;
}
&:hover {
background-color: rgba(255, 255, 255, 0.8);
} }
} }
.chat-area { .chat-area {
flex-grow: 1; flex-grow: 1;
order: 1; order: 1;
padding-top: 30px; padding: 0 30px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.ucap-chat-more-event { @include screen(xs) {
padding: 20px 0 0; padding: 0 16px;
.btn-more-pre {
@include ucapMatButton(100%, 30px, 2px, 30px);
font-size: 0.857em;
} }
} }
.recent-receive-message { }
.recent-receive-message {
transition: all 0.4s; transition: all 0.4s;
position: sticky; position: sticky;
bottom: 0; bottom: 0;
@ -56,9 +68,8 @@
margin: 0 -30px; margin: 0 -30px;
background-color: rgba(0, 0, 0, 0.6); background-color: rgba(0, 0, 0, 0.6);
color: #fff; color: #fff;
width: 100%;
@include screen(xs) { @include screen(xs) {
margin: 0 -15px; margin: 0 -15px;
} }
}
}
} }

View File

@ -1,7 +1,23 @@
import moment from 'moment'; import moment from 'moment';
import { Subject, merge, combineLatest, Observable } from 'rxjs'; import {
import { takeUntil, take } from 'rxjs/operators'; Subject,
merge,
combineLatest,
Observable,
fromEvent,
interval
} from 'rxjs';
import {
takeUntil,
take,
tap,
withLatestFrom,
debounce,
debounceTime
} from 'rxjs/operators';
import { PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
import { import {
Component, Component,
@ -10,9 +26,9 @@ import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef, ChangeDetectorRef,
Input, Input,
AfterViewInit,
ViewChild, ViewChild,
ElementRef ElementRef,
Inject
} from '@angular/core'; } from '@angular/core';
import { Store, select } from '@ngrx/store'; import { Store, select } from '@ngrx/store';
@ -20,53 +36,72 @@ import { Dictionary } from '@ngrx/entity';
import { MatDialog, MatDialogRef } from '@angular/material/dialog'; import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { TransMassTalkDownloadRequest } from '@ucap/api-common'; import { DeviceType, FileDownloadItem } from '@ucap/domain-common';
import { VersionInfo2Response } from '@ucap/api-public'; import { User } from '@ucap/domain-organization';
import { import {
Info, Info,
EventJson, EventJson,
EventType, EventType,
FileType, FileType,
MassTranslationEventJson MassTranslationEventJson,
} from '@ucap/protocol-event'; FileEventJson,
import {
UserInfo as RoomUserInfo, UserInfo as RoomUserInfo,
UserInfoShort as RoomUserInfoShort, UserInfoShort as RoomUserInfoShort,
RoomInfo, RoomInfo,
UpdateTimerSetRequest, RoomType,
UpdateRequest, FileInfo
RoomType } from '@ucap/domain-chat';
} from '@ucap/protocol-room';
import { LoginResponse } from '@ucap/protocol-authentication'; import { TransMassTalkDownloadRequest } from '@ucap/api-common';
import { FileInfo } from '@ucap/protocol-file';
import { User } from '@ucap/protocol-info'; import {
ReadRequest,
SSVC_TYPE_EVENT_SEND_NOTI,
SendNotification
} from '@ucap/protocol-event';
import { UpdateTimerSetRequest, UpdateRequest } from '@ucap/protocol-room';
import { NativeService } from '@ucap/native';
import { I18nService } from '@ucap/ng-i18n'; import { I18nService } from '@ucap/ng-i18n';
import { UserSelector } from '@ucap/ng-store-organization'; import { UserSelector } from '@ucap/ng-store-organization';
import { import {
LoginSelector, LoginSelector,
ConfigurationSelector ConfigurationSelector,
AuthorizationSelector
} from '@ucap/ng-store-authentication'; } from '@ucap/ng-store-authentication';
import { import {
ChattingSelector, ChattingSelector,
RoomSelector, RoomSelector,
ChattingActions, ChattingActions,
RoomActions, RoomActions,
Chatting ChatUtil
} from '@ucap/ng-store-chat'; } from '@ucap/ng-store-chat';
import { import {
ConfirmDialogComponent, ConfirmDialogComponent,
ConfirmDialogData, ConfirmDialogData,
ConfirmDialogResult, ConfirmDialogResult,
ClipboardService, AlertDialogComponent,
SelectFileInfo AlertDialogData,
} from '@ucap/ng-ui'; AlertDialogResult,
ClipboardService
} from '@ucap/ng-ui/core';
import { SelectFileInfo } from '@ucap/ng-ui/viewer';
import {
VirtualScrollViewportComponent,
MeasureSizeVirtualScrollDirective
} from '@ucap/ng-ui/scrolling';
import { EventProtocolService } from '@ucap/ng-protocol-event';
import { UCAP_NATIVE_SERVICE } from '@ucap/ng-native';
import { LogService } from '@ucap/ng-logger';
import { LoginSession } from '@app/models/login-session'; import { LoginSession } from '@app/models/login-session';
import { AppChatService } from '@app/services/app-chat.service'; import { AppChatService } from '@app/services/app-chat.service';
import { AppAuthenticationService } from '@app/services/app-authentication.service'; import { AppAuthenticationService } from '@app/services/app-authentication.service';
import { AppOrganizationService } from '@app/services/app-organization.service';
import { AppFileService } from '@app/services/app-file.service';
import { import {
ProfileDialogComponent, ProfileDialogComponent,
ProfileDialogData, ProfileDialogData,
@ -78,16 +113,15 @@ import {
ForwardDialogData, ForwardDialogData,
ForwardDialogResult ForwardDialogResult
} from '../dialogs/forward.dialog.component'; } from '../dialogs/forward.dialog.component';
import { import { FileViewerDialogData } from '../dialogs/file-viewer.dialog.component';
FileViewerDialogComponent,
FileViewerDialogData,
FileViewerDialogResult
} from '../dialogs/file-viewer.dialog.component';
import { import {
SettingDialogComponent, SettingDialogComponent,
SettingDialogData, SettingDialogData,
SettingDialogResult SettingDialogResult
} from '../dialogs/setting.dialog.component'; } from '../dialogs/setting.dialog.component';
import { ConferenceJoinRequest } from '@ucap/api-prompt';
import { LoginInfo, VersionInfo } from '@ucap/domain-authentication';
import { UserPermission } from '@ucap/domain-authorization';
@Component({ @Component({
selector: 'app-sections-chat-message', selector: 'app-sections-chat-message',
@ -96,19 +130,20 @@ import {
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class MessageSectionComponent export class MessageSectionComponent implements OnInit, OnDestroy {
implements OnInit, OnDestroy, AfterViewInit {
private roomIdSubject = new Subject<string>(); private roomIdSubject = new Subject<string>();
private ngOnDestroySubject: Subject<void> = new Subject(); private ngOnDestroySubject: Subject<void> = new Subject();
@Input() @Input()
set roomId(roomId: string) { set roomId(roomId: string) {
if (this._roomId !== roomId) {
this._roomId = roomId; this._roomId = roomId;
this.roomIdSubject.next(roomId); this.roomIdSubject.next(roomId);
this.initializeRoomData(); this.initializeRoomData();
} }
}
get roomId(): string { get roomId(): string {
return this._roomId; return this._roomId;
} }
@ -121,13 +156,14 @@ export class MessageSectionComponent
@Input() @Input()
eventSendTrigger$: Observable<any>; eventSendTrigger$: Observable<any>;
versionInfo2Res: VersionInfo2Response; versionInfo: VersionInfo;
loginRes: LoginResponse; loginInfo: LoginInfo;
user: User; user: User;
loginSession: LoginSession; loginSession: LoginSession;
userPermission: UserPermission;
currentRoomInfo: RoomInfo; currentRoomInfo: RoomInfo;
currentChatting: Chatting; isRemainEvent = false;
currentFileInfoList: FileInfo[] = []; currentFileInfoList: FileInfo[] = [];
roomUsers: RoomUserInfoShort[] = []; roomUsers: RoomUserInfoShort[] = [];
// eventList$: Observable<Info<EventJson>[]>; // eventList$: Observable<Info<EventJson>[]>;
@ -137,6 +173,10 @@ export class MessageSectionComponent
EventType = EventType; EventType = EventType;
FileType = FileType; FileType = FileType;
/** 최근 메시지 */
recentMessage: Info<EventJson>;
recentUserInfo: RoomUserInfoShort | RoomUserInfo | undefined;
/** Timer 대화방의 대화 삭제를 위한 interval */ /** Timer 대화방의 대화 삭제를 위한 interval */
interval: any; interval: any;
@ -145,9 +185,25 @@ export class MessageSectionComponent
/** About Scroll */ /** About Scroll */
isInitScrollbottom = true; isInitScrollbottom = true;
@ViewChild('chatMessagesContainer', { static: false }) isScrollReachBottom = false;
@ViewChild('chatMessagesContainer', { static: true })
chatMessagesContainer: ElementRef<HTMLElement>; chatMessagesContainer: ElementRef<HTMLElement>;
@ViewChild('vsList', { static: false })
vsList: VirtualScrollViewportComponent;
@ViewChild('vsList', {
static: true,
read: MeasureSizeVirtualScrollDirective
})
measureSize: MeasureSizeVirtualScrollDirective;
@ViewChild(PerfectScrollbarDirective, {
static: true,
read: PerfectScrollbarDirective
})
psDirectiveRef: PerfectScrollbarDirective;
String = String; String = String;
constructor( constructor(
@ -156,32 +212,109 @@ export class MessageSectionComponent
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
private dialog: MatDialog, private dialog: MatDialog,
private i18nService: I18nService, private i18nService: I18nService,
private appAuthenticationService: AppAuthenticationService private eventProtocolService: EventProtocolService,
private appAuthenticationService: AppAuthenticationService,
private appOrganizationService: AppOrganizationService,
private appFileService: AppFileService,
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
private logService: LogService
) { ) {
this.defaultProfileImage = this.appChatService.defaultProfileImage; this.defaultProfileImage = this.appChatService.defaultProfileImage;
// scroll handling when window size changing.
fromEvent(window, 'resize')
.pipe(takeUntil(this.ngOnDestroySubject), debounceTime(300))
.subscribe((event: any) => {
this.refreshAndScrollToBottom();
});
this.eventProtocolService.notification$
.pipe(
takeUntil(this.ngOnDestroySubject),
withLatestFrom(this.store.pipe(select(RoomSelector.rooms))),
tap(([notiOrRes, roomList]) => {
switch (notiOrRes.SSVC_TYPE) {
case SSVC_TYPE_EVENT_SEND_NOTI:
{
const noti = notiOrRes as SendNotification;
const roomId = noti.roomId;
const eventInfo = noti.info;
const trgtRoom = roomList.find(
(roomInfo) => roomInfo.roomId === roomId
);
const contents = ChatUtil.convertFinalEventMessage(
noti.eventType,
noti.info.sentMessageJson
);
this.logService.debug(
'Notification::eventProtocolService::SendNotification in message.section.components',
noti
);
if (!!roomId && roomId === this.roomId) {
if (!!this.isScrollReachBottom) {
// Fires when I enter the event
this.isInitScrollbottom = true;
} else {
this.recentMessage = noti.info;
this.recentUserInfo = this.getSenderInfo(
Number(noti.SENDER_SEQ)
);
}
}
}
break;
default:
// ignore..
break;
}
})
)
.subscribe();
} }
ngOnInit(): void { ngOnInit(): void {
this.vsList
.elementScrolled()
.pipe(takeUntil(this.ngOnDestroySubject))
.subscribe(() => {
if (!!this.psDirectiveRef) {
this.psDirectiveRef.update();
}
});
this.store this.store
.pipe( .pipe(
takeUntil(this.ngOnDestroySubject), takeUntil(this.ngOnDestroySubject),
select(ConfigurationSelector.versionInfo2Response) select(AuthorizationSelector.userPermission)
) )
.subscribe((versionInfo2Res) => (this.versionInfo2Res = versionInfo2Res)); .subscribe((userPermission) => {
this.userPermission = userPermission;
this.store });
.pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
.subscribe((loginRes) => (this.loginRes = loginRes));
this.store
.pipe(takeUntil(this.ngOnDestroySubject), select(UserSelector.user))
.subscribe((user) => (this.user = user));
this.eventSendTrigger$ this.eventSendTrigger$
.pipe(takeUntil(this.ngOnDestroySubject)) .pipe(takeUntil(this.ngOnDestroySubject))
.subscribe((_) => { .subscribe((_) => {
// Fires when I enter the event // Fires when I enter the event
this.gotoScrollToBottom(); this.isInitScrollbottom = true;
});
this.nativeService
.window_onFocus$()
.pipe(takeUntil(this.ngOnDestroySubject))
.subscribe((isFocus) => {
if (!!this.roomId && !!this.eventList && this.eventList.length > 0) {
const roomId = this.roomId;
const lastReadSeq = this.eventList[this.eventList.length - 1].seq;
this.store.dispatch(
ChattingActions.read({
roomId,
lastReadSeq
} as ReadRequest)
);
}
}); });
} }
@ -202,35 +335,21 @@ export class MessageSectionComponent
} }
} }
ngAfterViewInit() {}
initializeRoomData() { initializeRoomData() {
/** About initialize roomId */ /** About initialize roomId */
this.isInitScrollbottom = true; this.isInitScrollbottom = true;
this.onClickInvalidateCache();
this.loginSession = this.appAuthenticationService.getLoginSession(); this.loginSession = this.appAuthenticationService.getLoginSession();
this.store this.store
.pipe( .pipe(take(1), select(ConfigurationSelector.versionInfo))
takeUntil(merge(this.ngOnDestroySubject, this.roomIdSubject)), .subscribe((versionInfo) => (this.versionInfo = versionInfo));
select(RoomSelector.room, this.roomId) this.store
) .pipe(take(1), select(LoginSelector.loginInfo))
.subscribe((room) => { .subscribe((loginInfo) => (this.loginInfo = loginInfo));
this.currentRoomInfo = room; this.store
.pipe(take(1), select(UserSelector.user))
// About Interval .subscribe((user) => (this.user = user));
if (!!this.interval) {
clearInterval(this.interval);
this.interval = undefined;
}
if (!!this.currentRoomInfo && !!this.currentRoomInfo.isTimeRoom) {
this.interval = setInterval(() => {
this.store.dispatch(
ChattingActions.intervalClearEvent({ roomId: this.roomId })
);
}, 1000);
}
});
this.store this.store
.pipe( .pipe(
@ -250,7 +369,12 @@ export class MessageSectionComponent
select(ChattingSelector.chatting, this.roomId) select(ChattingSelector.chatting, this.roomId)
) )
.subscribe((chatting) => { .subscribe((chatting) => {
this.currentChatting = chatting; if (!!chatting && !!chatting.remainEvent) {
this.isRemainEvent = chatting.remainEvent;
} else {
this.isRemainEvent = false;
}
if ( if (
!!chatting && !!chatting &&
!!chatting.fileInfoList && !!chatting.fileInfoList &&
@ -285,12 +409,6 @@ export class MessageSectionComponent
.subscribe((eventList) => { .subscribe((eventList) => {
if (!!eventList && eventList.length > 0) { if (!!eventList && eventList.length > 0) {
this.eventList = eventList; this.eventList = eventList;
this.changeDetectorRef.markForCheck();
if (!!this.isInitScrollbottom) {
this.gotoScrollToBottom();
this.isInitScrollbottom = false;
}
} }
}); });
@ -301,11 +419,46 @@ export class MessageSectionComponent
]) ])
.pipe(takeUntil(merge(this.ngOnDestroySubject, this.roomIdSubject))) .pipe(takeUntil(merge(this.ngOnDestroySubject, this.roomIdSubject)))
.subscribe(([roomInfo, standbyRooms]) => { .subscribe(([roomInfo, standbyRooms]) => {
// get room info.
if (!!roomInfo) {
if (
!!this.currentRoomInfo &&
this.currentRoomInfo.roomId === roomInfo.roomId
) {
this.currentRoomInfo = {
...this.currentRoomInfo,
roomName: roomInfo.roomName,
joinUserCount: roomInfo.joinUserCount,
isJoinRoom: roomInfo.isJoinRoom,
expiredFileStdSeq: roomInfo.expiredFileStdSeq,
timeRoomInterval: roomInfo.timeRoomInterval
};
} else {
// all refresh
this.currentRoomInfo = roomInfo;
}
// About Interval
if (!!this.interval) {
clearInterval(this.interval);
this.interval = undefined;
}
if (!!this.currentRoomInfo && !!this.currentRoomInfo.isTimeRoom) {
this.interval = setInterval(() => {
this.store.dispatch(
ChattingActions.intervalClearEvent({ roomId: this.roomId })
);
}, 1000);
}
}
// new room setting popup
if ( if (
!!roomInfo && !!roomInfo &&
roomInfo.roomId === this.roomId && roomInfo.roomId === this.roomId &&
roomInfo.roomType !== RoomType.Mytalk && roomInfo.roomType !== RoomType.Mytalk &&
roomInfo.roomType !== RoomType.Single && (roomInfo.roomType !== RoomType.Single ||
(roomInfo.roomType === RoomType.Single && !!roomInfo.isTimeRoom)) &&
!!standbyRooms && !!standbyRooms &&
standbyRooms.length > 0 && standbyRooms.length > 0 &&
standbyRooms.findIndex((roomId) => roomId === this.roomId) > -1 standbyRooms.findIndex((roomId) => roomId === this.roomId) > -1
@ -337,13 +490,57 @@ export class MessageSectionComponent
}); });
} }
/** About scrolling */
keyOf = (item: Info<EventJson>): string | number => {
return item.seq;
};
onMeasured() {
if (!!this.isInitScrollbottom) {
this.gotoScrollToBottom();
this.isInitScrollbottom = false;
}
}
onContentSizeChanged(size: number) {
if (!!this.psDirectiveRef) {
this.psDirectiveRef.update();
}
}
onClickScrollTo(i: string) {
const index = Number(i);
// const offset = this.vsList.offsetForIndex(index, 'start');
// this.psDirectiveRef.scrollToY(offset);
// console.log('offset', offset, this.psDirectiveRef.ps().element.scrollTop);
this.vsList.scrollToIndex(index, 'center');
}
onClickInvalidateCache() {
if (!!this.measureSize) {
this.measureSize.invalidateMeasurements();
}
}
gotoScrollToBottom() { gotoScrollToBottom() {
if (!!this.chatMessagesContainer) { // recent message reset;
const self = this; this.resetRecentMessage();
setTimeout(() => {
self.chatMessagesContainer.nativeElement.scrollTop = // scrolling.
self.chatMessagesContainer.nativeElement.scrollHeight; if (!!this.vsList && !!this.eventList && this.eventList.length > 0) {
}, 500); this.vsList.scrollToIndex(this.eventList.length - 1, 'end');
}
}
refreshAndScrollToBottom() {
// if (!!this.vsList && !!this.isScrollReachBottom) {
// this.isInitScrollbottom = false;
// this.vsList.checkViewportSize();
// }
if (!!this.psDirectiveRef && !!this.isScrollReachBottom) {
this.psDirectiveRef.update();
this.psDirectiveRef.scrollToBottom();
} }
} }
@ -353,7 +550,7 @@ export class MessageSectionComponent
): RoomUserInfoShort | RoomUserInfo | undefined { ): RoomUserInfoShort | RoomUserInfo | undefined {
if (!!this.roomUsers && this.roomUsers.length) { if (!!this.roomUsers && this.roomUsers.length) {
return this.roomUsers.find( return this.roomUsers.find(
(userInfo) => userInfo.seq + '' === senderSeq + '' (userInfo) => String(senderSeq) === String(userInfo.seq)
); );
} }
@ -366,8 +563,7 @@ export class MessageSectionComponent
if (curIndex === 0) { if (curIndex === 0) {
return true; return true;
} } else if (curIndex > 0) {
if (curIndex > 0) {
if (!this.eventList[curIndex]) { if (!this.eventList[curIndex]) {
return false; return false;
} }
@ -376,16 +572,18 @@ export class MessageSectionComponent
'day' 'day'
); );
} }
return false; return false;
} }
/** Unread Count calculation */ /** Unread Count calculation */
getUnreadCount(message: Info<EventJson>): string | number { getUnreadCount(message: Info<EventJson>): string | number {
const unreadCnt = this.roomUsers const unreadCnt = this.roomUsers.filter(
.filter( (user) =>
(user) => user.isJoinRoom && user.seq + '' !== message.senderSeq + '' user.isJoinRoom &&
) String(message.senderSeq) !== String(user.seq) &&
.filter((user) => user.lastReadEventSeq < message.seq).length; user.lastReadEventSeq < message.seq
).length;
return unreadCnt === 0 ? '' : unreadCnt; return unreadCnt === 0 ? '' : unreadCnt;
} }
@ -393,7 +591,7 @@ export class MessageSectionComponent
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
if (!!this.currentChatting.remainEvent) { if (!!this.isRemainEvent) {
this.store.dispatch( this.store.dispatch(
ChattingActions.moreEvents({ ChattingActions.moreEvents({
roomId: this.roomId roomId: this.roomId
@ -422,7 +620,7 @@ export class MessageSectionComponent
this.appChatService.massTextDownload({ this.appChatService.massTextDownload({
userSeq: String(this.user.info.seq), userSeq: String(this.user.info.seq),
deviceType: this.loginSession.deviceType, deviceType: this.loginSession.deviceType,
token: this.loginRes.tokenString, token: this.loginInfo.tokenString,
eventMassSeq: params.message.seq eventMassSeq: params.message.seq
}); });
} }
@ -525,7 +723,7 @@ export class MessageSectionComponent
break; break;
case 'FORWARD_TO_ME': case 'FORWARD_TO_ME':
{ {
if (this.loginRes.talkWithMeBotSeq > -1) { if (this.loginInfo.talkWithMeBotSeq > -1) {
const seqs = this.user.talkWithMeBotSeq as any; const seqs = this.user.talkWithMeBotSeq as any;
this.store.dispatch( this.store.dispatch(
ChattingActions.forward({ ChattingActions.forward({
@ -580,7 +778,7 @@ export class MessageSectionComponent
}) { }) {
const req = { const req = {
userSeq: String(this.user.info.seq), userSeq: String(this.user.info.seq),
token: this.loginRes.tokenString, token: this.loginInfo.tokenString,
deviceType: this.loginSession.deviceType, deviceType: this.loginSession.deviceType,
eventTransSeq: params.message.sentMessageJson.translationSeq.toString() eventTransSeq: params.message.sentMessageJson.translationSeq.toString()
} as TransMassTalkDownloadRequest; } as TransMassTalkDownloadRequest;
@ -592,16 +790,133 @@ export class MessageSectionComponent
); );
} }
onFileViewer(selectFileInfo: SelectFileInfo) { onFileViewer(params: {
selectFileInfo: SelectFileInfo;
senderInfo: RoomUserInfoShort | RoomUserInfo;
}) {
const self = this;
this.appOrganizationService
.getUserInfo({ userSeq: String(params.senderInfo.seq), user: this.user })
.then((result) => {
let isValid = false;
if (
!!result &&
!!result.userInfo &&
!!self.userPermission &&
self.userPermission.fileTransferAllowedCompanyList.indexOf(
result.userInfo.companyCode
) > -1
) {
isValid = true;
}
if (!!isValid) {
const data: FileViewerDialogData = { const data: FileViewerDialogData = {
fileInfos: this.currentFileInfoList, fileInfos: self.currentFileInfoList,
selectFileInfo, selectFileInfo: params.selectFileInfo,
downloadUrl: this.versionInfo2Res.downloadUrl, downloadUrl: self.versionInfo.downloadUrl,
deviceType: this.loginSession.deviceType, deviceType: self.loginSession.deviceType,
token: this.loginRes.tokenString, token: self.loginInfo.tokenString,
userSeq: String(this.user.info.seq) userSeq: String(self.user.info.seq)
}; };
this.appChatService.openFileviwer(data); self.appChatService.openFileviwer(data);
} else {
self._openAlert(
self.i18nService.t('common:file.errors.disapprovalCompany')
);
}
})
.catch((reason) => {
self._openAlert(
self.i18nService.t('common:file.errors.disapprovalCompany')
);
});
}
onFileSave(params: {
fileInfo: FileEventJson;
fileDownloadItem: FileDownloadItem;
type: string;
senderInfo: RoomUserInfoShort | RoomUserInfo;
}): void {
const self = this;
this.appOrganizationService
.getUserInfo({ userSeq: String(params.senderInfo.seq), user: this.user })
.then((rst) => {
let isValid = false;
if (
!!rst &&
!!rst.userInfo &&
!!self.userPermission &&
self.userPermission.fileTransferAllowedCompanyList.indexOf(
rst.userInfo.companyCode
) > -1
) {
isValid = true;
}
if (!!isValid) {
if (
params.type === 'saveAs' &&
self.loginSession.deviceType === DeviceType.PC
) {
self.nativeService
.file_selectForSave({ defaultPath: params.fileInfo.fileName })
.then((result) => {
if (!!result) {
if (!!result.canceled) {
// 취소함.
} else {
self.appFileService.saveFile(
{
fileInfo: params.fileInfo,
fileDownloadItem: params.fileDownloadItem,
type: params.type,
fileName: params.fileInfo.fileName,
fileDownloadUrl: undefined,
savePath: result.filePath
},
self.loginInfo,
self.user,
self.loginSession
);
}
}
})
.catch((reason) => {
// self.snackBarService.open(
// self.translateService.instant(
// 'common:file.errors.failToSpecifyPath'
// ),
// self.translateService.instant('common:file.errors.label')
// );
});
} else {
self.appFileService.saveFile(
{
fileInfo: params.fileInfo,
fileDownloadItem: params.fileDownloadItem,
type: params.type,
fileName: params.fileInfo.fileName,
fileDownloadUrl: undefined,
savePath: undefined
},
self.loginInfo,
self.user,
self.loginSession
);
}
} else {
self._openAlert(
self.i18nService.t('common:file.errors.disapprovalCompany')
);
}
})
.catch((reason) => {
self._openAlert(
self.i18nService.t('common:file.errors.disapprovalCompany')
);
});
} }
private _standByRoomSetting(result: SettingDialogResult) { private _standByRoomSetting(result: SettingDialogResult) {
@ -640,4 +955,33 @@ export class MessageSectionComponent
} }
}); });
} }
onJoinConference(conferenceSeq: number): void {
const loginSession = this.appAuthenticationService.getLoginSession();
const req: ConferenceJoinRequest = {
userSeq: String(this.user.info.seq),
deviceType: loginSession.deviceType,
tokenKey: this.loginInfo.tokenString,
conferenceSeq
};
this.appChatService.joinVideoConference(req);
}
private _openAlert(msg: string) {
this.dialog.open<AlertDialogComponent, AlertDialogData, AlertDialogResult>(
AlertDialogComponent,
{
panelClass: 'min-create-dialog',
data: {
title: this.i18nService.t('common:file.errors.title'),
message: msg
}
}
);
}
resetRecentMessage() {
this.recentMessage = undefined;
this.recentUserInfo = undefined;
}
} }

View File

@ -12,9 +12,17 @@
#searchWordInput #searchWordInput
type="text" type="text"
formControlName="searchInput" formControlName="searchInput"
[matAutocomplete]="auto" (keyup.backspace)="onKeyupBackspace($event)"
(keydown.enter)="onKeyDownEnter($event, searchWordInput.value)" (keydown.enter)="onKeyDownEnter($event, searchWordInput.value)"
/> />
<!-- <input
matInput
#searchWordInput
type="text"
formControlName="searchInput"
[matAutocomplete]="auto"
(keydown.enter)="onKeyDownEnter($event, searchWordInput.value)"
/> -->
<mat-autocomplete #auto="matAutocomplete"> <mat-autocomplete #auto="matAutocomplete">
<mat-option <mat-option
*ngFor="let filteredRecommendedWord of filteredRecommendedWordList" *ngFor="let filteredRecommendedWord of filteredRecommendedWordList"

View File

@ -9,7 +9,8 @@ import {
EventEmitter, EventEmitter,
ChangeDetectorRef, ChangeDetectorRef,
ChangeDetectionStrategy, ChangeDetectionStrategy,
ViewChild ViewChild,
ElementRef
} from '@angular/core'; } from '@angular/core';
import { FormGroup, FormBuilder } from '@angular/forms'; import { FormGroup, FormBuilder } from '@angular/forms';
@ -17,7 +18,7 @@ import { Store, select } from '@ngrx/store';
import { MatAutocompleteTrigger } from '@angular/material/autocomplete'; import { MatAutocompleteTrigger } from '@angular/material/autocomplete';
import { User } from '@ucap/protocol-info'; import { User } from '@ucap/domain-organization';
import { LogService } from '@ucap/ng-logger'; import { LogService } from '@ucap/ng-logger';
@ -45,6 +46,8 @@ export class SearchSectionComponent implements OnInit, OnDestroy {
recommendedWordList: string[]; recommendedWordList: string[];
filteredRecommendedWordList: string[]; filteredRecommendedWordList: string[];
@ViewChild('searchWordInput', { static: true })
searchWordInput: ElementRef<HTMLInputElement>;
@ViewChild(MatAutocompleteTrigger) autocomplete: MatAutocompleteTrigger; @ViewChild(MatAutocompleteTrigger) autocomplete: MatAutocompleteTrigger;
private ngOnDestroySubject: Subject<void> = new Subject(); private ngOnDestroySubject: Subject<void> = new Subject();
@ -148,11 +151,20 @@ export class SearchSectionComponent implements OnInit, OnDestroy {
} }
} }
onKeyupBackspace(event: Event) {
if (this.searchWordInput.nativeElement.value === '') {
this.searchCancel.emit();
}
event.stopPropagation();
}
onKeyDownEnter(event: KeyboardEvent, searchWord: string) { onKeyDownEnter(event: KeyboardEvent, searchWord: string) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
if (!!this.autocomplete) {
this.autocomplete.closePanel(); this.autocomplete.closePanel();
}
this.keyDownEnter.emit({ searchWord }); this.keyDownEnter.emit({ searchWord });
} }

View File

@ -6,19 +6,19 @@
> >
<div appLayoutsDefaultDialog="header"> <div appLayoutsDefaultDialog="header">
<span *ngIf="stepper.selectedIndex === 0"> <span *ngIf="stepper.selectedIndex === 0">
{{ 'dialog.title.newChatRoom' | ucapI18n }} {{ 'chat:dialog.title.newChatRoom' | ucapI18n }}
</span> </span>
<span *ngIf="stepper.selectedIndex !== 0"> <span *ngIf="stepper.selectedIndex !== 0">
{{ (!!isTimer ? 'dialog.timerRoom' : 'dialog.normalRoom') | ucapI18n }} {{ (!!isTimer ? 'chat:dialog.timerRoom' : 'chat:dialog.normalRoom') | ucapI18n }}
</span> </span>
<div appLayoutsDefaultDialog="sub-header" class="sub-header-tit"> <span appLayoutsDefaultDialog="sub-header" class="sub-header-tit">
<span *ngIf="stepper.selectedIndex === 0"> <span *ngIf="stepper.selectedIndex === 0">
{{ 'dialog.title.subSelectRoomType' | ucapI18n }} {{ 'chat:dialog.title.subSelectRoomType' | ucapI18n }}
</span> </span>
<span *ngIf="stepper.selectedIndex !== 0"> <span *ngIf="stepper.selectedIndex !== 0">
{{ 'dialog.title.subSelectUser' | ucapI18n }} {{ 'chat:dialog.title.subSelectUser' | ucapI18n }}
</span>
</span> </span>
</div>
</div> </div>
<div class="dialog-body" appLayoutsDefaultDialog="body"> <div class="dialog-body" appLayoutsDefaultDialog="body">
@ -31,12 +31,12 @@
<mat-step label="Select room type" fxFlexFill> <mat-step label="Select room type" fxFlexFill>
<div class="ucap-dialog-select-room-type"> <div class="ucap-dialog-select-room-type">
<div class="normal-room room-type"> <div class="normal-room room-type">
<span class="title">{{ 'dialog.normalRoom' | ucapI18n }}</span> <span class="title">{{ 'chat:dialog.normalRoom' | ucapI18n }}</span>
<div class="img"></div> <div class="img"></div>
<div <div
class="description" class="description"
[innerHTML]=" [innerHTML]="
'dialog.normalRoomDescription' 'chat:dialog.normalRoomDescription'
| ucapI18n: { maxCount: maxChatRoomUser } | ucapI18n: { maxCount: maxChatRoomUser }
" "
></div> ></div>
@ -69,12 +69,12 @@
</div> </div>
<div class="room-type timer-room"> <div class="room-type timer-room">
<span class="title">{{ 'dialog.timerRoom' | ucapI18n }}</span> <span class="title">{{ 'chat:dialog.timerRoom' | ucapI18n }}</span>
<div class="img"></div> <div class="img"></div>
<div <div
class="description" class="description"
[innerHTML]=" [innerHTML]="
'dialog.timerRoomDescription' 'chat:dialog.timerRoomDescription'
| ucapI18n: { maxCount: maxChatRoomUser } | ucapI18n: { maxCount: maxChatRoomUser }
" "
></div> ></div>
@ -113,8 +113,9 @@
[isDialog]="true" [isDialog]="true"
[checkable]="true" [checkable]="true"
[selectedUserList]="selectedUserList" [selectedUserList]="selectedUserList"
(toggleCheckUser)="onChangeUserList($event)" [filteredTimerChatAuth]="isTimer"
(toggleCheckGroup)="onChangeGroupList($event)" (toggleCheckUser)="onToggleCheckUser($event)"
(toggleCheckGroup)="onToggleCheckGroup($event)"
></app-group-select-user> ></app-group-select-user>
</div> </div>
<div class="ucap-dialog-organization-profile-selection"> <div class="ucap-dialog-organization-profile-selection">
@ -130,7 +131,7 @@
class="selected-head-area" class="selected-head-area"
> >
<p> <p>
{{ 'dialog.selectedUserList' | ucapI18n }} {{ 'chat:dialog.selectedUserList' | ucapI18n }}
</p> </p>
<span <span
>(<em class="number">{{ selectedUserList?.length }}</em >(<em class="number">{{ selectedUserList?.length }}</em
@ -144,7 +145,7 @@
</div> </div>
<div appLayoutsDefaultDialog="action" class="btn-box"> <div appLayoutsDefaultDialog="action" class="btn-box">
<button mat-button mat-stroked-button (click)="onCancel(stepper)"> <button mat-button mat-stroked-button (click)="onCancel(stepper)">
{{ 'dialog.button.cancel' | ucapI18n }} {{ 'chat:dialog.button.cancel' | ucapI18n }}
</button> </button>
<button <button
mat-flat-button mat-flat-button
@ -153,7 +154,7 @@
[disabled]="this.isTimer === undefined" [disabled]="this.isTimer === undefined"
(click)="onConfirm(stepper)" (click)="onConfirm(stepper)"
> >
{{ 'dialog.button.selectRoomUser' | ucapI18n }} {{ 'chat:dialog.button.selectRoomUser' | ucapI18n }}
</button> </button>
<button <button
mat-flat-button mat-flat-button
@ -162,7 +163,7 @@
[disabled]="selectedUserList.length === 0" [disabled]="selectedUserList.length === 0"
(click)="onOpenRoom(stepper)" (click)="onOpenRoom(stepper)"
> >
{{ 'dialog.button.openRoom' | ucapI18n }} {{ 'chat:dialog.button.openRoom' | ucapI18n }}
</button> </button>
</div> </div>
</app-layouts-default-dialog> </app-layouts-default-dialog>

View File

@ -80,7 +80,7 @@
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
width: 48%; width: 48%;
height: 210px; height: 200px;
border: 1px solid #999999; border: 1px solid #999999;
background-color: #f7f8fa; background-color: #f7f8fa;
text-align: center; text-align: center;
@ -98,7 +98,7 @@
width: 60px; width: 60px;
height: 60px; height: 60px;
margin-bottom: 10px; margin-bottom: 10px;
background-image: url(../../../../assets/images/ico/icon_normal_chat_g60.svg); background-image: url(/assets/images/ico/icon_normal_chat_g60.svg);
} }
} }
&.timer-room { &.timer-room {
@ -106,7 +106,7 @@
width: 60px; width: 60px;
height: 60px; height: 60px;
margin-bottom: 10px; margin-bottom: 10px;
background-image: url(../../../../assets/images/ico/icon_timer_chat_g60.svg); background-image: url(/assets/images/ico/icon_timer_chat_g60.svg);
} }
} }
.selecter { .selecter {

View File

@ -6,9 +6,7 @@ import {
OnDestroy, OnDestroy,
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef, ChangeDetectorRef,
Inject, Inject
Input,
ViewChild
} from '@angular/core'; } from '@angular/core';
import { import {
@ -17,32 +15,24 @@ import {
MatDialog MatDialog
} from '@angular/material/dialog'; } from '@angular/material/dialog';
import { UserInfo, GroupDetailData } from '@ucap/protocol-sync'; import { UserInfo, UserInfoSS } from '@ucap/domain-organization';
import { import { GroupInfoDetail } from '@ucap/domain-group';
UserInfoSS,
UserInfoF,
UserInfoDN,
AuthResponse
} from '@ucap/protocol-query';
import { UserInfo as RoomUserInfo } from '@ucap/protocol-room';
import { MatStepper } from '@angular/material/stepper'; import { MatStepper } from '@angular/material/stepper';
import { I18nService } from '@ucap/ng-i18n'; import { I18nService } from '@ucap/ng-i18n';
import { import {
AlertDialogComponent, AlertDialogComponent,
AlertDialogData, AlertDialogData,
AlertDialogResult AlertDialogResult
} from '@ucap/ng-ui'; } from '@ucap/ng-ui/core';
import { environment } from '@environments'; import { environment } from '@environments';
import { Store, select } from '@ngrx/store'; import { Store, select } from '@ngrx/store';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { AuthorizationSelector } from '@ucap/ng-store-authentication'; import { AuthorizationSelector } from '@ucap/ng-store-authentication';
import { AppGroupService } from '@app/services/app-group.service';
export type UserInfoTypes = import { UserInfoTypes } from '@app/types';
| UserInfo import { AppChatService } from '@app/services/app-chat.service';
| UserInfoSS import { UserPermission } from '@ucap/domain-authorization';
| UserInfoF
| UserInfoDN
| RoomUserInfo;
export interface CreateDialogData {} export interface CreateDialogData {}
export interface CreateDialogResult { export interface CreateDialogResult {
@ -62,7 +52,7 @@ export class CreateDialogComponent implements OnInit, OnDestroy {
isTimer: boolean | undefined; isTimer: boolean | undefined;
selectedUserList: UserInfoTypes[] = []; selectedUserList: UserInfoTypes[] = [];
authRes: AuthResponse; userPermission: UserPermission;
private ngOnDestroySubject: Subject<void> = new Subject(); private ngOnDestroySubject: Subject<void> = new Subject();
@ -72,7 +62,9 @@ export class CreateDialogComponent implements OnInit, OnDestroy {
private i18nService: I18nService, private i18nService: I18nService,
public dialog: MatDialog, public dialog: MatDialog,
private store: Store<any>, private store: Store<any>,
private changeDetectorRef: ChangeDetectorRef private changeDetectorRef: ChangeDetectorRef,
private appGroupService: AppGroupService,
private appChatService: AppChatService
) { ) {
this.maxChatRoomUser = environment.productConfig.chat.maxChatRoomUser; this.maxChatRoomUser = environment.productConfig.chat.maxChatRoomUser;
} }
@ -81,11 +73,11 @@ export class CreateDialogComponent implements OnInit, OnDestroy {
this.store this.store
.pipe( .pipe(
takeUntil(this.ngOnDestroySubject), takeUntil(this.ngOnDestroySubject),
select(AuthorizationSelector.authResponse) select(AuthorizationSelector.userPermission)
) )
.subscribe((authRes) => { .subscribe((userPermission) => {
this.authRes = authRes; this.userPermission = userPermission;
if (!!this.authRes && !this.authRes.canTimerChat) { if (!!this.userPermission && !this.userPermission.canTimerChat) {
this.isTimer = false; this.isTimer = false;
this.currentStep = 1; this.currentStep = 1;
} }
@ -103,7 +95,7 @@ export class CreateDialogComponent implements OnInit, OnDestroy {
this.dialogRef.close(); this.dialogRef.close();
} }
onCancel(stepper: MatStepper) { onCancel(stepper: MatStepper) {
if (!!this.authRes && !this.authRes.canTimerChat) { if (!!this.userPermission && !this.userPermission.canTimerChat) {
// close. // close.
} else if (stepper.selectedIndex > 0) { } else if (stepper.selectedIndex > 0) {
stepper.previous(); stepper.previous();
@ -135,93 +127,51 @@ export class CreateDialogComponent implements OnInit, OnDestroy {
this.selectedUserList.map((user) => userSeqs.push(user.seq.toString())); this.selectedUserList.map((user) => userSeqs.push(user.seq.toString()));
if (this.selectedUserList.length >= this.maxChatRoomUser) { // validation and openRoom
this.dialog.open< this.appChatService.newOpenRoom(userSeqs, this.isTimer);
AlertDialogComponent,
AlertDialogData,
AlertDialogResult
>(AlertDialogComponent, {
panelClass: 'min-create-dialog',
data: {
title: this.i18nService.t('chat:errors.label'),
html: this.i18nService.t('chat:errors.maxCountOfRoomMemberWith', {
maxCount: this.maxChatRoomUser
})
}
});
return;
}
// Open Room. if (this.selectedUserList.length >= this.maxChatRoomUser) {
// not close popup..
return;
} else {
this.dialogRef.close({ userSeqs, isTimer: this.isTimer }); this.dialogRef.close({ userSeqs, isTimer: this.isTimer });
} }
}
onChangeUserList(datas: { checked: boolean; userInfo: UserInfoSS }[]) { onToggleCheckUser(datas: { checked: boolean; userInfo: UserInfoSS }[]) {
if (!datas || 0 === datas.length) { const resList = this.appGroupService.getToggleCheckUser(
datas,
this.selectedUserList
);
if (!resList) {
return;
}
this.selectedUserList = resList;
}
onToggleCheckGroup(params: {
isChecked: boolean;
groupBuddyList: { group: GroupInfoDetail; buddyList: UserInfo[] };
}) {
const resList = this.appGroupService.getToggleCheckGroup(
params,
this.selectedUserList
);
if (!resList) {
return; return;
} }
const pushs: UserInfoSS[] = []; this.selectedUserList = resList;
const pops: UserInfoSS[] = [];
datas.forEach((d) => {
const i = this.selectedUserList.findIndex(
(u) => String(u.seq) === String(d.userInfo.seq)
);
if (d.checked) {
if (-1 === i) {
pushs.push(d.userInfo);
}
} else {
if (-1 < i) {
pops.push(d.userInfo);
}
}
});
if (0 < pushs.length) {
this.selectedUserList = [...this.selectedUserList, ...pushs];
}
if (0 < pops.length) {
this.selectedUserList = this.selectedUserList.filter(
(u) => -1 === pops.findIndex((p) => String(p.seq) === String(u.seq))
);
}
}
onChangeGroupList(params: {
isChecked: boolean;
groupBuddyList: { group: GroupDetailData; buddyList: UserInfo[] };
}) {
if (params.isChecked) {
params.groupBuddyList.buddyList.forEach((item) => {
if (
this.selectedUserList.filter(
(user) => String(user.seq) === String(item.seq)
).length === 0
) {
this.selectedUserList = [...this.selectedUserList, item];
}
});
} else {
this.selectedUserList = this.selectedUserList.filter(
(item) =>
params.groupBuddyList.buddyList.filter(
(del) => String(del.seq) === String(item.seq)
).length === 0
);
}
} }
onRemovedProfileSelection(userInfo: UserInfo) { onRemovedProfileSelection(userInfo: UserInfo) {
const i = this.selectedUserList.findIndex( const resUserList = this.appGroupService.removedProfileSelection(
(u) => String(u.seq) === String(userInfo.seq) userInfo,
this.selectedUserList
); );
if (-1 < i) { if (!resUserList) {
this.selectedUserList = this.selectedUserList.filter(
(u) => String(u.seq) !== String(userInfo.seq)
);
} }
this.selectedUserList = resUserList;
} }
removableForSelection = (userInfo: UserInfo) => { removableForSelection = (userInfo: UserInfo) => {
return true; return true;

View File

@ -7,23 +7,25 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { select, Store } from '@ngrx/store'; import { select, Store } from '@ngrx/store';
import { DeviceType, LoginSession } from '@ucap/core'; import { DeviceType, FileDownloadItem } from '@ucap/domain-common';
import { FileDownloadItem } from '@ucap/api'; import { User } from '@ucap/domain-organization';
import { LoginResponse } from '@ucap/protocol-authentication'; import { LoginSession, LoginInfo } from '@ucap/domain-authentication';
import { FileEventJson } from '@ucap/protocol-event'; import { FileEventJson, FileInfo, isMedia } from '@ucap/domain-chat';
import { FileInfo, isMedia } from '@ucap/protocol-file';
import { User } from '@ucap/protocol-info';
import { LogService } from '@ucap/ng-logger'; import { LogService } from '@ucap/ng-logger';
import { CommonApiService } from '@ucap/ng-api-common'; import { CommonApiService } from '@ucap/ng-api-common';
import { UserSelector } from '@ucap/ng-store-organization'; import { UserSelector } from '@ucap/ng-store-organization';
import { LoginSelector } from '@ucap/ng-store-authentication'; import {
LoginSelector,
AuthorizationSelector
} from '@ucap/ng-store-authentication';
import { SelectFileInfo } from '@ucap/ng-ui'; import { SelectFileInfo } from '@ucap/ng-ui/viewer';
import { AppFileService } from '@app/services/app-file.service'; import { AppFileService } from '@app/services/app-file.service';
import { AppAuthenticationService } from '@app/services/app-authentication.service'; import { AppAuthenticationService } from '@app/services/app-authentication.service';
import { UserPermission } from '@ucap/domain-authorization';
export interface FileViewerDialogData { export interface FileViewerDialogData {
fileInfos: FileInfo[]; fileInfos: FileInfo[];
@ -51,9 +53,10 @@ export class FileViewerDialogComponent implements OnInit, OnDestroy {
}; };
currentFileInfo: FileInfo; currentFileInfo: FileInfo;
loginRes: LoginResponse; loginInfo: LoginInfo;
user: User; user: User;
loginSession: LoginSession; loginSession: LoginSession;
userPermission: UserPermission;
constructor( constructor(
private store: Store<any>, private store: Store<any>,
@ -96,14 +99,54 @@ export class FileViewerDialogComponent implements OnInit, OnDestroy {
ngOnInit() { ngOnInit() {
this.store this.store
.pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes)) .pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginInfo))
.subscribe((loginRes) => (this.loginRes = loginRes)); .subscribe((loginInfo) => (this.loginInfo = loginInfo));
this.store this.store
.pipe(takeUntil(this.ngOnDestroySubject), select(UserSelector.user)) .pipe(takeUntil(this.ngOnDestroySubject), select(UserSelector.user))
.subscribe((user) => (this.user = user)); .subscribe((user) => (this.user = user));
this.loginSession = this.appAuthenticationService.getLoginSession(); this.loginSession = this.appAuthenticationService.getLoginSession();
// auth.fileTransferAllowedCompanyList check.
this.store
.pipe(
takeUntil(this.ngOnDestroySubject),
select(AuthorizationSelector.userPermission)
)
.subscribe((userPermission) => {
if (!!userPermission) {
this.userPermission = userPermission;
}
if (
!!userPermission &&
!!this.data.fileInfos &&
this.data.fileInfos.length > 0
) {
this.fileInfo = {
fileInfos: this.data.fileInfos
.filter((item) => {
// filtered userPermission.fileTransferAllowedCompanyList
if (
!!userPermission.fileTransferAllowedCompanyList &&
userPermission.fileTransferAllowedCompanyList.length > 0
) {
return (
userPermission.fileTransferAllowedCompanyList.indexOf(
item.sentMessageJson.companyCode
) > -1
);
}
return true;
})
.sort((a, b) =>
a.eventSeq < b.eventSeq ? 1 : a.eventSeq > b.eventSeq ? -1 : 0
),
selectFileInfo: this.data.selectFileInfo
};
}
});
} }
ngOnDestroy(): void { ngOnDestroy(): void {
@ -147,7 +190,7 @@ export class FileViewerDialogComponent implements OnInit, OnDestroy {
fileDownloadUrl: undefined, fileDownloadUrl: undefined,
savePath: undefined savePath: undefined
}, },
this.loginRes, this.loginInfo,
this.user, this.user,
this.loginSession this.loginSession
); );

View File

@ -10,9 +10,14 @@
<div class="dialog-body" appLayoutsDefaultDialog="body" fxFlexFill> <div class="dialog-body" appLayoutsDefaultDialog="body" fxFlexFill>
<div class="ucap-dialog-app-group-select-user"> <div class="ucap-dialog-app-group-select-user">
<!-- search start--> <!-- search start-->
<div class="ucap-dialog-search"> <div
*ngIf="currentTabIndex === 0"
class="ucap-dialog-search"
[ngClass]="currentTabIndex === 0 ? 'ucap-dialog-search-disable' : ''"
>
<app-organization-search-for-tenant <app-organization-search-for-tenant
placeholder="이름, 부서명, 전화번호, 이메일" placeholder="이름, 부서명, 전화번호, 이메일"
[isBackspaceCanceled]="isBackspaceCanceled"
[(searchData)]="companySearchData" [(searchData)]="companySearchData"
(canceled)="onCanceled()" (canceled)="onCanceled()"
class="select-user-section-search" class="select-user-section-search"
@ -24,7 +29,7 @@
<mat-tab-group <mat-tab-group
mat-stretch-tabs mat-stretch-tabs
class="tap-container tab_num2" class="tap-container tab_num2"
(selectedIndexChange)="onSelectedIndexChange($event)" [(selectedIndex)]="currentTabIndex"
> >
<!--[S]그룹--> <!--[S]그룹-->
<mat-tab> <mat-tab>
@ -57,6 +62,8 @@
<div fxFlexFill class="ucap-dialog-chat-tap"> <div fxFlexFill class="ucap-dialog-chat-tap">
<perfect-scrollbar style="width: 100%; height: 100%;"> <perfect-scrollbar style="width: 100%; height: 100%;">
<app-chat-room-list <app-chat-room-list
fxFlexFill
[selectedRoom]="selectedRoom"
[checkable]="true" [checkable]="true"
(toggleRoom)="onToggleRoom($event)" (toggleRoom)="onToggleRoom($event)"
></app-chat-room-list> ></app-chat-room-list>
@ -81,12 +88,13 @@
<div class="search-result-list"> <div class="search-result-list">
<perfect-scrollbar style="width: 100%; height: 100%;"> <perfect-scrollbar style="width: 100%; height: 100%;">
<app-group-profile-list <app-group-profile-list
#appProfileList
[searchData]="companySearchData" [searchData]="companySearchData"
[selectedUser]="selectedUserList" [selectedUser]="selectedUserList"
[checkable]="true" [checkable]="true"
[isDialog]="true" [isDialog]="true"
(searched)="onSearched($event)" (searched)="onSearched($event)"
(toggleCheck)="onToggleCheckUser($event)" (toggleCheckUser)="onToggleCheckUser($event)"
class="ucap-dialog-search-result-container" class="ucap-dialog-search-result-container"
></app-group-profile-list> ></app-group-profile-list>
</perfect-scrollbar> </perfect-scrollbar>
@ -127,12 +135,7 @@
<button <button
mat-flat-button mat-flat-button
class="bg-primary-darkest" class="bg-primary-darkest"
[disabled]=" [disabled]="checkDisableBtn()"
!(
!!selectedRoom ||
(!!selectedUserList && selectedUserList.length > 0)
)
"
(click)="onConfirm()" (click)="onConfirm()"
> >
{{ 'dialog.button.save' | ucapI18n }} {{ 'dialog.button.save' | ucapI18n }}

View File

@ -18,13 +18,16 @@
} }
.forwrad-dialog-content { .forwrad-dialog-content {
height: calc(100% - 50px); flex: 1 1 auto;
// height: calc(100% - 50px);
.tap-container { .tap-container {
height: 100%; height: 100%;
} }
} }
.ucap-dialog-app-group-select-user { .ucap-dialog-app-group-select-user {
display: flex;
flex-direction: column;
flex: 1 0 auto; flex: 1 0 auto;
//width: 60%; //width: 60%;
height: 100%; height: 100%;
@ -35,6 +38,9 @@
height: 78%; height: 78%;
margin-bottom: 2%; margin-bottom: 2%;
} }
.ucap-dialog-search {
flex: 0 0 50px;
}
} }
.ucap-dialog-organization-profile-selection { .ucap-dialog-organization-profile-selection {
position: relative; position: relative;

View File

@ -19,10 +19,13 @@ import {
import { Store, select } from '@ngrx/store'; import { Store, select } from '@ngrx/store';
import { VersionInfo2Response } from '@ucap/api-public'; import {
import { UserInfo, GroupDetailData } from '@ucap/protocol-sync'; UserInfo,
import { UserInfoSS, UserInfoF, UserInfoDN } from '@ucap/protocol-query'; UserInfoSS,
import { UserInfo as RoomUserInfo, RoomInfo } from '@ucap/protocol-room'; UserInfoF,
UserInfoDN
} from '@ucap/domain-organization';
import { UserInfo as RoomUserInfo, RoomInfo } from '@ucap/domain-chat';
import { I18nService } from '@ucap/ng-i18n'; import { I18nService } from '@ucap/ng-i18n';
@ -33,18 +36,16 @@ import {
AlertDialogComponent, AlertDialogComponent,
AlertDialogData, AlertDialogData,
AlertDialogResult AlertDialogResult
} from '@ucap/ng-ui'; } from '@ucap/ng-ui/core';
import { SearchData } from '@app/ucap/organization/models/search-data'; import { SearchData } from '@app/ucap/organization/models/search-data';
import { Expansion02Component as AppExpansion02Component } from '@app/ucap/group/components/expansion-02.component'; import { Expansion02Component as AppExpansion02Component } from '@app/ucap/group/components/expansion-02.component';
import { ProfileListComponent as AppProfileListComponent } from '@app/ucap/group/components/profile-list.component';
import { environment } from '@environments'; import { environment } from '@environments';
import { AppGroupService } from '@app/services/app-group.service';
export type UserInfoTypes = import { UserInfoTypes } from '@app/types';
| UserInfo import { VersionInfo } from '@ucap/domain-authentication';
| UserInfoSS import { GroupInfoDetail } from '@ucap/domain-group';
| UserInfoF
| UserInfoDN
| RoomUserInfo;
export interface ForwardDialogData {} export interface ForwardDialogData {}
export interface ForwardDialogResult { export interface ForwardDialogResult {
@ -63,6 +64,9 @@ export class ForwardDialogComponent implements OnInit, OnDestroy {
@ViewChild('appGroupExpansion', { static: false }) @ViewChild('appGroupExpansion', { static: false })
appGroupExpansion: AppExpansion02Component; appGroupExpansion: AppExpansion02Component;
@ViewChild('appProfileList', { static: false })
appProfileList: AppProfileListComponent;
set companySearchData(searchData: SearchData) { set companySearchData(searchData: SearchData) {
if (!!searchData && searchData.searchWord !== '') { if (!!searchData && searchData.searchWord !== '') {
this._companySearchData = { ...searchData, bySearch: true }; this._companySearchData = { ...searchData, bySearch: true };
@ -85,13 +89,23 @@ export class ForwardDialogComponent implements OnInit, OnDestroy {
selectedRoom: RoomInfo; selectedRoom: RoomInfo;
selectedUserList: UserInfoTypes[] = []; selectedUserList: UserInfoTypes[] = [];
versionInfo2Res: VersionInfo2Response; versionInfo: VersionInfo;
groupList: GroupDetailData[];
groupList: GroupInfoDetail[];
isBackspaceCanceled = true;
isSearch = false; isSearch = false;
searchedList: UserInfoSS[] = []; searchedList: UserInfoSS[] = [];
currentTabIndex: number;
set currentTabIndex(idx: number) {
this._currentTabIndex = idx;
// this._resetSelectedObject();
}
get currentTabIndex() {
return this._currentTabIndex;
}
// tslint:disable-next-line: variable-name
_currentTabIndex: number;
constructor( constructor(
public dialogRef: MatDialogRef<ForwardDialogData, ForwardDialogResult>, public dialogRef: MatDialogRef<ForwardDialogData, ForwardDialogResult>,
@ -99,7 +113,8 @@ export class ForwardDialogComponent implements OnInit, OnDestroy {
private store: Store<any>, private store: Store<any>,
private i18nService: I18nService, private i18nService: I18nService,
public dialog: MatDialog, public dialog: MatDialog,
private changeDetectorRef: ChangeDetectorRef private changeDetectorRef: ChangeDetectorRef,
private appGroupService: AppGroupService
) { ) {
this.maxChatRoomUser = environment.productConfig.chat.maxChatRoomUser; this.maxChatRoomUser = environment.productConfig.chat.maxChatRoomUser;
} }
@ -108,10 +123,10 @@ export class ForwardDialogComponent implements OnInit, OnDestroy {
this.store this.store
.pipe( .pipe(
takeUntil(this.ngOnDestroySubject), takeUntil(this.ngOnDestroySubject),
select(ConfigurationSelector.versionInfo2Response) select(ConfigurationSelector.versionInfo)
) )
.subscribe((versionInfo2Res) => { .subscribe((versionInfo) => {
this.versionInfo2Res = versionInfo2Res; this.versionInfo = versionInfo;
}); });
this.store this.store
@ -119,6 +134,7 @@ export class ForwardDialogComponent implements OnInit, OnDestroy {
.subscribe((groups) => { .subscribe((groups) => {
this.groupList = groups; this.groupList = groups;
}); });
this.currentTabIndex = 0;
} }
ngOnDestroy(): void { ngOnDestroy(): void {
@ -147,68 +163,49 @@ export class ForwardDialogComponent implements OnInit, OnDestroy {
onSearched(searchedUserInfos: UserInfoSS[]): void { onSearched(searchedUserInfos: UserInfoSS[]): void {
this.searchedList = searchedUserInfos; this.searchedList = searchedUserInfos;
if (!!this.appProfileList) {
this.appProfileList.psUpdate();
}
} }
onToggleCheckUser(datas: { checked: boolean; userInfo: UserInfoSS }[]) { onToggleCheckUser(datas: { checked: boolean; userInfo: UserInfoSS }[]) {
if (!datas || 0 === datas.length) { const resList = this.appGroupService.getToggleCheckUser(
datas,
this.selectedUserList
);
if (!resList) {
return; return;
} }
this.selectedUserList = resList;
const pushs: UserInfoSS[] = [];
const pops: UserInfoSS[] = [];
datas.forEach((d) => {
const i = this.selectedUserList.findIndex(
(u) => u.seq === d.userInfo.seq
);
if (d.checked) {
if (-1 === i) {
pushs.push(d.userInfo);
}
} else {
if (-1 < i) {
pops.push(d.userInfo);
}
}
});
if (0 < pushs.length) {
this.selectedUserList = [...this.selectedUserList, ...pushs];
}
if (0 < pops.length) {
this.selectedUserList = this.selectedUserList.filter(
(u) => -1 === pops.findIndex((p) => p.seq === u.seq)
);
}
} }
onToggleCheckGroup(params: { onToggleCheckGroup(params: {
isChecked: boolean; isChecked: boolean;
groupBuddyList: { group: GroupDetailData; buddyList: UserInfo[] }; groupBuddyList: { group: GroupInfoDetail; buddyList: UserInfo[] };
}) { }) {
if (params.isChecked) { const resList = this.appGroupService.getToggleCheckGroup(
params.groupBuddyList.buddyList.forEach((item) => { params,
if ( this.selectedUserList
this.selectedUserList.filter((user) => user.seq === item.seq)
.length === 0
) {
this.selectedUserList = [...this.selectedUserList, item];
}
});
} else {
this.selectedUserList = this.selectedUserList.filter(
(item) =>
params.groupBuddyList.buddyList.filter((del) => del.seq === item.seq)
.length === 0
); );
if (!resList) {
return;
} }
this.selectedUserList = resList;
} }
onCancel() { onCancel() {
this.dialogRef.close({ choice: false }); this.dialogRef.close({ choice: false });
} }
onConfirm() { onConfirm() {
if (this.currentTabIndex === 0) {
this.selectedRoom = undefined;
} else {
this.selectedUserList = [];
}
if ( if (
!this.selectedRoom && !this.selectedRoom &&
!!this.selectedUserList && !!this.selectedUserList &&
@ -238,58 +235,8 @@ export class ForwardDialogComponent implements OnInit, OnDestroy {
onToggleRoom(roomInfo: RoomInfo): void { onToggleRoom(roomInfo: RoomInfo): void {
if (!!roomInfo) { if (!!roomInfo) {
this.selectedRoom = roomInfo; this.selectedRoom = roomInfo;
}
}
getCheckedByRoomInfo(roomInfo: RoomInfo): boolean {
if (
!!this.selectedRoom &&
this.selectedRoom.roomId.localeCompare(roomInfo.roomId) === 0
) {
return true;
}
return false;
}
onChangeUserList(data: { checked: boolean; userInfo: UserInfoSS }) {
const i = this.selectedUserList.findIndex(
(u) => String(u.seq) === String(data.userInfo.seq)
);
if (data.checked) {
if (-1 === i) {
this.selectedUserList = [...this.selectedUserList, data.userInfo];
}
} else { } else {
if (-1 < i) { this.selectedRoom = undefined;
this.selectedUserList = this.selectedUserList.filter(
(u) => String(u.seq) !== String(data.userInfo.seq)
);
}
}
}
onChangeGroupList(params: {
isChecked: boolean;
groupBuddyList: { group: GroupDetailData; buddyList: UserInfo[] };
}) {
if (params.isChecked) {
params.groupBuddyList.buddyList.forEach((item) => {
if (
this.selectedUserList.filter(
(user) => String(user.seq) === String(item.seq)
).length === 0
) {
this.selectedUserList = [...this.selectedUserList, item];
}
});
} else {
this.selectedUserList = this.selectedUserList.filter(
(item) =>
params.groupBuddyList.buddyList.filter(
(del) => String(del.seq) === String(item.seq)
).length === 0
);
} }
} }
@ -321,31 +268,62 @@ export class ForwardDialogComponent implements OnInit, OnDestroy {
} }
onToggleSearchAllItem(value: boolean): void { onToggleSearchAllItem(value: boolean): void {
const pushs: UserInfoSS[] = [];
const pops: UserInfoSS[] = [];
this.searchedList.forEach((user) => {
const i = this.selectedUserList.findIndex((u) => u.seq === user.seq);
if (-1 === i) {
pushs.push(user);
}
if (-1 < i) {
pops.push(user);
}
});
if (!!value) { if (!!value) {
const targetRoomList = this.searchedList; if (0 < pushs.length) {
this.selectedUserList = targetRoomList.slice(); this.selectedUserList = [...this.selectedUserList, ...pushs];
}
} else { } else {
this.selectedUserList = []; if (0 < pops.length) {
this.selectedUserList = this.selectedUserList.filter(
(u) => -1 === pops.findIndex((p) => p.seq === u.seq)
);
}
} }
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
} }
onCanceled() { onCanceled() {
this.isSearch = false; this.isSearch = false;
this.searchedList = [];
this.companySearchData = { ...this.companySearchData, searchWord: '' }; this.companySearchData = { ...this.companySearchData, searchWord: '' };
} }
checkDisableBtn(): boolean {
if (
this.currentTabIndex === 0 &&
!!this.selectedUserList &&
this.selectedUserList.length > 0
) {
return false;
} else if (this.currentTabIndex === 1 && !!this.selectedRoom) {
return false;
}
return true;
}
onRemovedProfileSelection(userInfo: UserInfo) { onRemovedProfileSelection(userInfo: UserInfo) {
const i = this.selectedUserList.findIndex( const resUserList = this.appGroupService.removedProfileSelection(
(u) => String(u.seq) === String(userInfo.seq) userInfo,
this.selectedUserList
); );
if (-1 < i) { if (!resUserList) {
this.selectedUserList = this.selectedUserList.filter(
(u) => String(u.seq) !== String(userInfo.seq)
);
} }
this.selectedUserList = resUserList;
} }
removableForSelection = (userInfo: UserInfo) => { removableForSelection = (userInfo: UserInfo) => {
return true; return true;

View File

@ -1,4 +1,5 @@
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { import {
Component, Component,
@ -9,6 +10,8 @@ import {
Inject Inject
} from '@angular/core'; } from '@angular/core';
import { Store, select } from '@ngrx/store';
import { import {
MatDialogRef, MatDialogRef,
MAT_DIALOG_DATA, MAT_DIALOG_DATA,
@ -16,11 +19,9 @@ import {
MatDialogConfig MatDialogConfig
} from '@angular/material/dialog'; } from '@angular/material/dialog';
import { I18nService } from '@ucap/ng-i18n'; import { RoomInfo } from '@ucap/domain-chat';
import { Store, select } from '@ngrx/store'; import { I18nService } from '@ucap/ng-i18n';
import { takeUntil } from 'rxjs/operators';
import { RoomInfo } from '@ucap/protocol-room';
import { RoomSelector } from '@ucap/ng-store-chat'; import { RoomSelector } from '@ucap/ng-store-chat';
export interface SettingDialogData { export interface SettingDialogData {

View File

@ -12,7 +12,7 @@
<div class="profile-image"> <div class="profile-image">
<img <img
ucapImage ucapImage
[base]="versionInfo2Res?.profileRoot" [base]="versionInfo?.profileRoot"
[path]="userInfo.profileImageFile" [path]="userInfo.profileImageFile"
[default]="defaultProfileImage" [default]="defaultProfileImage"
/> />
@ -32,7 +32,7 @@
</div> </div>
</div> </div>
<div class="contents"> <div class="contents">
<perfect-scrollbar <perfect-scrollbar #perfectScroll
><div ><div
[innerHTML]=" [innerHTML]="
contents | ucapSafeHtml | ucapLinefeedToHtml | ucapLinky contents | ucapSafeHtml | ucapLinefeedToHtml | ucapLinky

View File

@ -5,7 +5,9 @@ import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef, ChangeDetectorRef,
Inject, Inject,
ElementRef ElementRef,
ViewChild,
AfterViewInit
} from '@angular/core'; } from '@angular/core';
import { Subject, of, combineLatest } from 'rxjs'; import { Subject, of, combineLatest } from 'rxjs';
@ -18,16 +20,18 @@ import {
MatDialog MatDialog
} from '@angular/material/dialog'; } from '@angular/material/dialog';
import { DeviceType } from '@ucap/core'; import { DeviceType } from '@ucap/domain-common';
import { StatusCode } from '@ucap/api';
import { NativeService } from '@ucap/native';
import { VersionInfo2Response } from '@ucap/api-public';
import { MassTalkDownloadRequest } from '@ucap/api-common';
import { import {
UserInfo as RoomUserInfo, UserInfo as RoomUserInfo,
UserInfoShort as RoomUserInfoShort UserInfoShort as RoomUserInfoShort,
} from '@ucap/protocol-room'; Info,
import { Info, EventJson } from '@ucap/protocol-event'; EventJson
} from '@ucap/domain-chat';
import { StatusCode } from '@ucap/api';
import { NativeService } from '@ucap/native';
import { MassTalkDownloadRequest } from '@ucap/api-common';
import { CommonApiService } from '@ucap/ng-api-common'; import { CommonApiService } from '@ucap/ng-api-common';
import { LogService } from '@ucap/ng-logger'; import { LogService } from '@ucap/ng-logger';
@ -36,6 +40,8 @@ import { RoomSelector } from '@ucap/ng-store-chat';
import { ConfigurationSelector } from '@ucap/ng-store-authentication'; import { ConfigurationSelector } from '@ucap/ng-store-authentication';
import { AppChatService } from '@app/services/app-chat.service'; import { AppChatService } from '@app/services/app-chat.service';
import { PerfectScrollbarComponent } from 'ngx-perfect-scrollbar';
import { VersionInfo } from '@ucap/domain-authentication';
export type UserInfoTypes = RoomUserInfo | RoomUserInfoShort; export type UserInfoTypes = RoomUserInfo | RoomUserInfoShort;
@ -55,13 +61,15 @@ export interface TextDetailDialogResult {}
styleUrls: ['./text-detail.dialog.component.scss'], styleUrls: ['./text-detail.dialog.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class TextDetailDialogComponent implements OnInit, OnDestroy { export class TextDetailDialogComponent
implements OnInit, OnDestroy, AfterViewInit {
userInfo: UserInfoTypes; userInfo: UserInfoTypes;
contents: string; contents = '';
defaultProfileImage: string; defaultProfileImage: string;
versionInfo2Res: VersionInfo2Response; versionInfo: VersionInfo;
@ViewChild('perfectScroll') perfectScroll: PerfectScrollbarComponent;
private ngOnDestroySubject: Subject<void> = new Subject(); private ngOnDestroySubject: Subject<void> = new Subject();
constructor( constructor(
@ -85,10 +93,10 @@ export class TextDetailDialogComponent implements OnInit, OnDestroy {
this.store this.store
.pipe( .pipe(
takeUntil(this.ngOnDestroySubject), takeUntil(this.ngOnDestroySubject),
select(ConfigurationSelector.versionInfo2Response) select(ConfigurationSelector.versionInfo)
) )
.subscribe((versionInfo2Res) => { .subscribe((versionInfo) => {
this.versionInfo2Res = versionInfo2Res; this.versionInfo = versionInfo;
}); });
this.commonApiService this.commonApiService
@ -107,6 +115,7 @@ export class TextDetailDialogComponent implements OnInit, OnDestroy {
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
setTimeout(() => { setTimeout(() => {
this.psUpdate();
if ( if (
!!this.elementRef.nativeElement && !!this.elementRef.nativeElement &&
!!this.elementRef.nativeElement.querySelector('a') !!this.elementRef.nativeElement.querySelector('a')
@ -154,6 +163,16 @@ export class TextDetailDialogComponent implements OnInit, OnDestroy {
} }
} }
ngAfterViewInit(): void {
this.psUpdate();
}
psUpdate() {
if (!!this.perfectScroll) {
this.perfectScroll.directiveRef.update();
}
}
onClickEvent(event: MouseEvent) { onClickEvent(event: MouseEvent) {
this.nativeService.platform_openDefaultBrowser( this.nativeService.platform_openDefaultBrowser(
(event.target as HTMLAnchorElement).text (event.target as HTMLAnchorElement).text

View File

@ -12,7 +12,7 @@
<div class="profile-image"> <div class="profile-image">
<img <img
ucapImage ucapImage
[base]="versionInfo2Res?.profileRoot" [base]="versionInfo?.profileRoot"
[path]="userInfo.profileImageFile" [path]="userInfo.profileImageFile"
[default]="defaultProfileImage" [default]="defaultProfileImage"
/> />

View File

@ -1,3 +1,6 @@
import { Subject, of, combineLatest } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { import {
Component, Component,
OnInit, OnInit,
@ -9,7 +12,7 @@ import {
AfterViewInit AfterViewInit
} from '@angular/core'; } from '@angular/core';
import { Subject, of, combineLatest } from 'rxjs'; import { Store, select } from '@ngrx/store';
import { import {
MatDialogRef, MatDialogRef,
@ -17,21 +20,21 @@ import {
MatDialog MatDialog
} from '@angular/material/dialog'; } from '@angular/material/dialog';
import { NativeService } from '@ucap/native';
import { VersionInfo2Response } from '@ucap/api-public';
import { import {
UserInfo as RoomUserInfo, UserInfo as RoomUserInfo,
UserInfoShort as RoomUserInfoShort UserInfoShort as RoomUserInfoShort,
} from '@ucap/protocol-room'; Info,
MassTranslationEventJson
} from '@ucap/domain-chat';
import { NativeService } from '@ucap/native';
import { LogService } from '@ucap/ng-logger'; import { LogService } from '@ucap/ng-logger';
import { UCAP_NATIVE_SERVICE } from '@ucap/ng-native'; import { UCAP_NATIVE_SERVICE } from '@ucap/ng-native';
import { Store, select } from '@ngrx/store';
import { takeUntil } from 'rxjs/operators';
import { ConfigurationSelector } from '@ucap/ng-store-authentication'; import { ConfigurationSelector } from '@ucap/ng-store-authentication';
import { RoomSelector } from '@ucap/ng-store-chat'; import { RoomSelector } from '@ucap/ng-store-chat';
import { Info, MassTranslationEventJson } from '@ucap/protocol-event'; import { VersionInfo } from '@ucap/domain-authentication';
export type UserInfoTypes = RoomUserInfo | RoomUserInfoShort; export type UserInfoTypes = RoomUserInfo | RoomUserInfoShort;
@ -55,7 +58,7 @@ export class TransDetailDialogComponent
contents: string; contents: string;
defaultProfileImage: string; defaultProfileImage: string;
versionInfo2Res: VersionInfo2Response; versionInfo: VersionInfo;
private ngOnDestroySubject: Subject<void> = new Subject(); private ngOnDestroySubject: Subject<void> = new Subject();
@ -78,10 +81,10 @@ export class TransDetailDialogComponent
this.store this.store
.pipe( .pipe(
takeUntil(this.ngOnDestroySubject), takeUntil(this.ngOnDestroySubject),
select(ConfigurationSelector.versionInfo2Response) select(ConfigurationSelector.versionInfo)
) )
.subscribe((versionInfo2Res) => { .subscribe((versionInfo) => {
this.versionInfo2Res = versionInfo2Res; this.versionInfo = versionInfo;
}); });
combineLatest([ combineLatest([
this.store.pipe(select(RoomSelector.roomUser, this.data.roomId)), this.store.pipe(select(RoomSelector.roomUser, this.data.roomId)),

View File

@ -0,0 +1,179 @@
import { Subject, fromEvent } from 'rxjs';
import {
Directive,
ElementRef,
EventEmitter,
HostListener,
Output,
Input,
AfterViewInit,
NgZone,
OnDestroy,
OnInit
} from '@angular/core';
import { FileUploadItem } from '@ucap/domain-common';
import { FileUploadQueueComponent } from '@ucap/ng-ui/file-upload';
import { takeUntil } from 'rxjs/operators';
@Directive({
selector: 'input[ucapFileUploadFor01], div[ucapFileUploadFor01]'
})
export class FileUploadForDirective
implements OnInit, OnDestroy, AfterViewInit {
@Input()
fileUploadQueue?: FileUploadQueueComponent;
@Input()
validator?: (fileList: FileList) => Promise<boolean>;
@Output()
public fileDragEnter = new EventEmitter<DataTransferItemList>();
@Output()
public fileDragOver = new EventEmitter<void>();
@Output()
public fileDragLeave = new EventEmitter<void>();
@Output()
public fileSelected = new EventEmitter<FileUploadItem[] | FileList>();
dragOver = false;
private ngOnDestroySubject: Subject<void> = new Subject();
constructor(
private elementRef: ElementRef,
private readonly ngZone: NgZone
) {}
ngOnInit(): void {
fromEvent(window, 'dragenter')
.pipe(takeUntil(this.ngOnDestroySubject))
.subscribe((event: DragEvent) => {
if (!this.isFileDrag(event.dataTransfer)) {
return;
}
if (!this.dragOver) {
this.fileDragEnter.emit(event.dataTransfer.items);
this.dragOver = true;
if (!!this.fileUploadQueue) {
this.fileUploadQueue.onDragEnter(event.dataTransfer.items);
}
}
});
fromEvent(window, 'dragover')
.pipe(takeUntil(this.ngOnDestroySubject))
.subscribe((event: DragEvent) => {
if (!this.isFileDrag(event.dataTransfer)) {
return;
}
// if (this.fileUploadQueue.isEventInElement(event)) {
// event.dataTransfer.dropEffect = 'copy';
// } else {
// event.dataTransfer.dropEffect = 'none';
// }
event.preventDefault();
});
fromEvent(window, 'dragleave')
.pipe(takeUntil(this.ngOnDestroySubject))
.subscribe((event: DragEvent) => {
if (!this.isFileDrag(event.dataTransfer)) {
return;
}
if (event && event.pageX === 0 && event.pageY === 0) {
this.fileDragLeave.emit();
this.dragOver = false;
if (!!this.fileUploadQueue) {
this.fileUploadQueue.onDragLeave();
}
}
});
fromEvent(window, 'drop')
.pipe(takeUntil(this.ngOnDestroySubject))
.subscribe((event: any) => {
if (!this.isFileDrag(event.dataTransfer)) {
return;
}
const self = this;
const files: FileList = event.dataTransfer.files;
event.preventDefault();
event.stopPropagation();
this.elementRef.nativeElement.value = ''; // Case defined directive in 'input' type.
this.dragOver = false;
if (!!this.validator) {
this.validator(files)
.then(async (result) => {
if (!result) {
if (!!this.fileUploadQueue) {
this.fileUploadQueue.onUploadComplete();
}
return;
} else {
const fileUploadItems = FileUploadItem.fromFiles(files);
self.fileSelected.emit(fileUploadItems);
if (!!self.fileUploadQueue) {
self.fileUploadQueue.onDrop(fileUploadItems);
}
}
})
.catch((err) => {
if (!!this.fileUploadQueue) {
this.fileUploadQueue.onUploadComplete();
}
});
} else {
const fileUploadItems = FileUploadItem.fromFiles(files);
this.fileSelected.emit(files);
if (!!this.fileUploadQueue) {
this.fileUploadQueue.onDrop(fileUploadItems);
}
}
});
}
ngOnDestroy(): void {
if (!!this.ngOnDestroySubject) {
this.ngOnDestroySubject.next();
this.ngOnDestroySubject.complete();
}
}
ngAfterViewInit(): void {}
// @HostListener('change')
// public onChange(): any {
// const files = this.elementRef.nativeElement.files;
// this.fileSelected.emit(FileUploadItem.fromFiles(files));
// this.elementRef.nativeElement.value = '';
// }
private isFileDrag(dataTransfer: DataTransfer): boolean {
if (0 >= dataTransfer.items.length) {
return false;
}
// tslint:disable-next-line: prefer-for-of
for (let i = 0; i < dataTransfer.items.length; i++) {
const element = dataTransfer.items[i];
if ('file' !== element.kind) {
return false;
}
}
return true;
}
}

Some files were not shown because too many files have changed in this diff Show More