diff --git a/config/build/win/bin/config.dat b/config/build/win/bin/config.dat new file mode 100644 index 00000000..6d936083 Binary files /dev/null and b/config/build/win/bin/config.dat differ diff --git a/electron-builder.json b/electron-builder.json index 8dfc5ac5..68fdc38e 100644 --- a/electron-builder.json +++ b/electron-builder.json @@ -59,6 +59,10 @@ { "from": "./config/build/win/bin/AeroAdmin.exe", "to": "./bin/AeroAdmin.exe" + }, + { + "from": "./config/build/win/bin/config.dat", + "to": "./bin/config.dat" } ] }, diff --git a/electron-projects/ucap-webmessenger-electron-core/src/lib/types/channel.type.ts b/electron-projects/ucap-webmessenger-electron-core/src/lib/types/channel.type.ts index d9f55c52..0d7645e1 100644 --- a/electron-projects/ucap-webmessenger-electron-core/src/lib/types/channel.type.ts +++ b/electron-projects/ucap-webmessenger-electron-core/src/lib/types/channel.type.ts @@ -27,7 +27,8 @@ export enum ElectronBrowserWindowChannel { Close = 'close', Closed = 'closed', ReadyToShow = 'ready-to-show', - Focus = 'focus' + Focus = 'focus', + Blur = 'blur' } export enum ElectronWebContentsChannel { diff --git a/electron-projects/ucap-webmessenger-electron/src/app/AppWindow.ts b/electron-projects/ucap-webmessenger-electron/src/app/AppWindow.ts index 487c7a0d..6f69c305 100644 --- a/electron-projects/ucap-webmessenger-electron/src/app/AppWindow.ts +++ b/electron-projects/ucap-webmessenger-electron/src/app/AppWindow.ts @@ -14,8 +14,9 @@ import { ElectronWebContentsChannel } from '@ucap-webmessenger/electron-core'; -import { appStorage } from '../lib/storage'; import { now } from '../util/now'; +import { Storage } from '../lib/storage'; +import { WindowStateChannel } from '@ucap-webmessenger/native-electron'; export class AppWindow { private window: BrowserWindow | null = null; @@ -33,7 +34,7 @@ export class AppWindow { private defaultWidth = 1024; private defaultHeight = 768; - public constructor(private appIconPath: string) { + public constructor(private appIconPath: string, appStorage: Storage) { const savedWindowState = windowStateKeeper({ defaultWidth: this.defaultWidth, defaultHeight: this.defaultHeight @@ -60,6 +61,7 @@ export class AppWindow { }, acceptFirstMouse: true, icon: this.appIconPath, + fullscreenable: false, show: false }; @@ -84,6 +86,20 @@ export class AppWindow { event.returnValue = true; }); + // windows Focus or Blur state detacted. + this.window.on(ElectronBrowserWindowChannel.Focus, () => { + this.window.webContents.send( + WindowStateChannel.FocuseChanged, + ElectronBrowserWindowChannel.Focus + ); + }); + this.window.on(ElectronBrowserWindowChannel.Blur, () => { + this.window.webContents.send( + WindowStateChannel.FocuseChanged, + ElectronBrowserWindowChannel.Blur + ); + }); + // on macOS, when the user closes the window we really just hide it. This // lets us activate quickly and keep all our interesting logic in the // renderer. @@ -122,11 +138,14 @@ export class AppWindow { // can be tidied up once https://github.com/electron/electron/issues/12971 // has been confirmed as resolved this.window.once(ElectronBrowserWindowChannel.ReadyToShow, () => { - if (appStorage.startupHideWindow) { - this.window.close(); - } else { - this.window.show(); - } + this.window.close(); + setTimeout(() => { + if (!!appStorage && appStorage.startupHideWindow) { + this.window.close(); + } else { + this.window.show(); + } + }, 500); this.window.on(ElectronBrowserWindowChannel.Unmaximize, () => { setTimeout(() => { const bounds = this.window.getBounds(); diff --git a/electron-projects/ucap-webmessenger-electron/src/index.ts b/electron-projects/ucap-webmessenger-electron/src/index.ts index 458c55e0..2784fe61 100644 --- a/electron-projects/ucap-webmessenger-electron/src/index.ts +++ b/electron-projects/ucap-webmessenger-electron/src/index.ts @@ -61,7 +61,7 @@ import { import log from 'electron-log'; import { RendererUpdater } from './lib/renderer-updater'; -import { appStorage } from './lib/storage'; +import { Storage } from './lib/storage'; require('v8-compile-cache'); @@ -108,6 +108,8 @@ let updateWindowService: ElectronUpdateWindowService | null; tmp.setGracefulCleanup(); +let appStorage: Storage; + function handleUncaughtException(error: Error) { preventQuit = true; @@ -174,7 +176,12 @@ if (isDuplicateInstance) { } function createWindow() { - const window = new AppWindow(appIconPath); + try { + appStorage = new Storage(); + } catch (e) { + log.error('[appStorage = new Storage()]]', e); + } + const window = new AppWindow(appIconPath, appStorage); if (__DEV__) { // const { @@ -212,7 +219,8 @@ function createWindow() { }); window.onDidLoad(() => { - if (!appStorage.startupHideWindow) { + if (!!appStorage && !appStorage.startupHideWindow) { + // not used? window.show(); } else { window.hide(); @@ -440,7 +448,9 @@ ipcMain.on( ipcMain.on( MessengerChannel.ClearAppStorage, (event: IpcMainEvent, ...args: any[]) => { - appStorage.reset(); + if (!!!!appStorage) { + appStorage.reset(); + } } ); @@ -478,13 +488,15 @@ ipcMain.on( (event: IpcMainEvent, ...args: any[]) => { const isStartupHideWindow = args[0] as boolean; - appStorage.startupHideWindow = isStartupHideWindow; - log.info( - 'StartupHideWindow is changed from ', - !appStorage.startupHideWindow, - ' to ', - appStorage.startupHideWindow - ); + if (!!!!appStorage) { + appStorage.startupHideWindow = isStartupHideWindow; + log.info( + 'StartupHideWindow is changed from ', + !appStorage.startupHideWindow, + ' to ', + appStorage.startupHideWindow + ); + } event.returnValue = true; } ); @@ -494,7 +506,7 @@ ipcMain.on( (event: IpcMainEvent, ...args: any[]) => { const downloadPath = args[0] as string; - if (!!downloadPath && downloadPath.length > 0) { + if (!!appStorage && !!downloadPath && downloadPath.length > 0) { appStorage.downloadPath = downloadPath; log.info('downloadPath is changed to ', appStorage.downloadPath); @@ -565,9 +577,9 @@ ipcMain.on( let basePath = path.join( app.getPath('documents'), - appStorage.constDefaultDownloadFolder + !!appStorage ? appStorage.constDefaultDownloadFolder : '' ); - if (!!appStorage.downloadPath) { + if (!!appStorage && !!appStorage.downloadPath) { basePath = appStorage.downloadPath; } try { @@ -607,7 +619,7 @@ ipcMain.on( const make: boolean = args[1]; if (!folderItem) { let basePath = app.getPath('downloads'); - if (!!appStorage.downloadPath) { + if (!!appStorage && !!appStorage.downloadPath) { try { basePath = appStorage.downloadPath; } catch (err) { @@ -788,6 +800,10 @@ ipcMain.on( } }); + if (!appWindow.isVisible()) { + appWindow.browserWindow.minimize(); + } + appWindow.browserWindow.flashFrame(true); } ); @@ -821,6 +837,9 @@ ipcMain.on(ClipboardChannel.Read, (event: IpcMainEvent, ...args: any[]) => { ipcMain.on(AppChannel.Exit, (event: IpcMainEvent, ...args: any[]) => { appExit(); }); +ipcMain.on(AppChannel.Logging, (event: IpcMainEvent, ...args: any[]) => { + log.error('[G]', args[0]); +}); ipcMain.on(ExternalChannel.OpenUrl, (event: IpcMainEvent, ...args: any[]) => { const targetUrl = args[0]; diff --git a/electron-projects/ucap-webmessenger-electron/src/lib/storage.ts b/electron-projects/ucap-webmessenger-electron/src/lib/storage.ts index 0ece865b..7eb1c928 100644 --- a/electron-projects/ucap-webmessenger-electron/src/lib/storage.ts +++ b/electron-projects/ucap-webmessenger-electron/src/lib/storage.ts @@ -11,6 +11,7 @@ export class Storage { constructor() { this.store = new ElectronStore({ + cwd: path.join(__dirname, '..', '..', '..', '/bin/'), schema: { options: { type: 'object', diff --git a/package.json b/package.json index 82222157..c4dcd1f2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ucap-webmessenger", - "version": "1.0.4", + "version": "1.0.13", "author": { "name": "LG CNS", "email": "lgucap@lgcns.com" @@ -45,7 +45,7 @@ "@angular/core": "^8.2.14", "auto-launch": "^5.0.5", "electron-log": "^3.0.9", - "electron-store": "^5.1.0", + "electron-store": "^5.1.1", "electron-updater": "^4.2.0", "electron-window-state": "^5.0.3", "file-type": "^14.1.2", diff --git a/projects/ucap-webmessenger-api-external/src/lib/apis/url-info.ts b/projects/ucap-webmessenger-api-external/src/lib/apis/url-info.ts index a3abbff9..67153720 100644 --- a/projects/ucap-webmessenger-api-external/src/lib/apis/url-info.ts +++ b/projects/ucap-webmessenger-api-external/src/lib/apis/url-info.ts @@ -192,6 +192,14 @@ export const decodeUrlInfoDaesang: APIDecoder = ( url: arr.length > 1 ? arr[1] : arr[0] }); } + if (!!res.WebLinkERP) { + const arr = res.WebLinkERP.split(','); + webLink.push({ + key: WebLinkType.Erp, + title: arr.length > 1 ? arr[0] : '', + url: arr.length > 1 ? arr[1] : arr[0] + }); + } return { statusCode: res.StatusCode, diff --git a/projects/ucap-webmessenger-api/src/lib/apis/httpUrlEncodingCodec.ts b/projects/ucap-webmessenger-api/src/lib/apis/httpUrlEncodingCodec.ts new file mode 100644 index 00000000..a4a06373 --- /dev/null +++ b/projects/ucap-webmessenger-api/src/lib/apis/httpUrlEncodingCodec.ts @@ -0,0 +1,20 @@ +import { HttpParameterCodec } from '@angular/common/http'; + +export class HttpUrlEncodingCodec implements HttpParameterCodec { + encodeKey(k: string): string { + return this.standardEncoding(k); + } + encodeValue(v: string): string { + return this.standardEncoding(v); + } + decodeKey(k: string): string { + return decodeURIComponent(k); + } + decodeValue(v: string) { + return decodeURIComponent(v); + } + + standardEncoding(v: string): string { + return encodeURIComponent(v); + } +} diff --git a/projects/ucap-webmessenger-api/src/public-api.ts b/projects/ucap-webmessenger-api/src/public-api.ts index 25527679..3258d83d 100644 --- a/projects/ucap-webmessenger-api/src/public-api.ts +++ b/projects/ucap-webmessenger-api/src/public-api.ts @@ -3,6 +3,7 @@ */ export * from './lib/apis/api'; +export * from './lib/apis/httpUrlEncodingCodec'; export * from './lib/types/message-status-code.type'; export * from './lib/types/status-code.type'; diff --git a/projects/ucap-webmessenger-app/src/app/app.module.ts b/projects/ucap-webmessenger-app/src/app/app.module.ts index 9c356ee8..2e05dfad 100644 --- a/projects/ucap-webmessenger-app/src/app/app.module.ts +++ b/projects/ucap-webmessenger-app/src/app/app.module.ts @@ -50,6 +50,7 @@ import { AppMessengerLayoutModule } from './layouts/messenger/messenger.layout.m import { AppNativeLayoutModule } from './layouts/native/native.layout.module'; import { environment } from '../environments/environment'; +import { ERRORHANDLER } from './error-handler'; @NgModule({ imports: [ @@ -105,7 +106,7 @@ import { environment } from '../environments/environment'; level: NgxLoggerLevel.DEBUG }) ], - providers: [...GUARDS], + providers: [...GUARDS, ERRORHANDLER], declarations: [AppComponent], bootstrap: [AppComponent], entryComponents: [] diff --git a/projects/ucap-webmessenger-app/src/app/error-handler/global.errorhandler.ts b/projects/ucap-webmessenger-app/src/app/error-handler/global.errorhandler.ts new file mode 100644 index 00000000..cb8e856d --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/error-handler/global.errorhandler.ts @@ -0,0 +1,29 @@ +import { ErrorHandler, Injectable, Injector, NgZone } from '@angular/core'; +import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native'; +import { error } from 'console'; + +@Injectable({ + providedIn: 'root' +}) +export class AppGlobalErrorhandler implements ErrorHandler { + constructor(private injector: Injector, private zone: NgZone) {} + + // tslint:disable-next-line: no-shadowed-variable + handleError(error: Error) { + let nativeLogging: any; + try { + const nativeService: NativeService = this.injector.get( + UCAP_NATIVE_SERVICE + ); + nativeService.appLogging(error.stack); + nativeLogging = 'SUCCESS'; + } catch (e) { + nativeLogging = e; + } + + console.groupCollapsed('App Global Error Logging'); + console.log('App log', error); + console.log('Native Logging', nativeLogging); + console.groupEnd(); + } +} diff --git a/projects/ucap-webmessenger-app/src/app/error-handler/index.ts b/projects/ucap-webmessenger-app/src/app/error-handler/index.ts new file mode 100644 index 00000000..4ff44985 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/error-handler/index.ts @@ -0,0 +1,6 @@ +import { AppGlobalErrorhandler } from './global.errorhandler'; +import { ErrorHandler } from '@angular/core'; + +export const ERRORHANDLER = [ + { provide: ErrorHandler, useClass: AppGlobalErrorhandler } +]; diff --git a/projects/ucap-webmessenger-app/src/app/guards/auto-login.guard.ts b/projects/ucap-webmessenger-app/src/app/guards/auto-login.guard.ts index 038b77c8..c02afb9d 100644 --- a/projects/ucap-webmessenger-app/src/app/guards/auto-login.guard.ts +++ b/projects/ucap-webmessenger-app/src/app/guards/auto-login.guard.ts @@ -49,7 +49,7 @@ export class AppAutoLoginGuard implements CanActivate { if ( !!appUserInfo && - appUserInfo.settings.general.autoLogin && + !!appUserInfo.settings.general.autoLogin && !(!!personLogout && !!personLogout.personLogout) ) { this.store.dispatch( diff --git a/projects/ucap-webmessenger-app/src/app/layouts/common/dialogs/file-viewer.dialog.component.html b/projects/ucap-webmessenger-app/src/app/layouts/common/dialogs/file-viewer.dialog.component.html index 0092f806..759de027 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/common/dialogs/file-viewer.dialog.component.html +++ b/projects/ucap-webmessenger-app/src/app/layouts/common/dialogs/file-viewer.dialog.component.html @@ -1,6 +1,9 @@ diff --git a/projects/ucap-webmessenger-app/src/app/layouts/common/dialogs/file-viewer.dialog.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/common/dialogs/file-viewer.dialog.component.ts index ae30e0b1..431352bb 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/common/dialogs/file-viewer.dialog.component.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/common/dialogs/file-viewer.dialog.component.ts @@ -7,6 +7,9 @@ import { DeviceType } from '@ucap-webmessenger/core'; import { FileDownloadItem } from '@ucap-webmessenger/api'; import { CommonApiService } from '@ucap-webmessenger/api-common'; import { AppFileService } from '@app/services/file.service'; +import { ImageOnlyDataInfo, SnackBarService } from '@ucap-webmessenger/ui'; +import { UCAP_NATIVE_SERVICE, NativeService } from '@ucap-webmessenger/native'; +import { TranslateService } from '@ngx-translate/core'; export interface FileViewerDialogData { fileInfo: FileEventJson; @@ -14,6 +17,8 @@ export interface FileViewerDialogData { userSeq: number; deviceType: DeviceType; token: string; + imageOnly?: boolean; + imageOnlyData?: ImageOnlyDataInfo; } export interface FileViewerDialogResult {} @@ -32,6 +37,9 @@ export class FileViewerDialogComponent implements OnInit, OnDestroy { fileDownloadUrl: string; + imageOnly = false; + imageOnlyData: ImageOnlyDataInfo; + constructor( public dialogRef: MatDialogRef< FileViewerDialogData, @@ -40,6 +48,9 @@ export class FileViewerDialogComponent implements OnInit, OnDestroy { @Inject(MAT_DIALOG_DATA) public data: FileViewerDialogData, private commonApiService: CommonApiService, private appFileService: AppFileService, + private translateService: TranslateService, + private snackBarService: SnackBarService, + @Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService, private logger: NGXLogger ) { this.fileInfo = data.fileInfo; @@ -47,19 +58,28 @@ export class FileViewerDialogComponent implements OnInit, OnDestroy { this.userSeq = data.userSeq; this.deviceType = data.deviceType; this.token = data.token; - - this.fileDownloadUrl = this.commonApiService.urlForFileTalkDownload( - { - userSeq: this.userSeq, - deviceType: this.deviceType, - token: this.token, - attachmentsSeq: this.fileInfo.attachmentSeq - }, - this.downloadUrl - ); } - ngOnInit() {} + ngOnInit() { + if (!!this.data.imageOnly) { + this.imageOnly = this.data.imageOnly; + this.imageOnlyData = this.data.imageOnlyData; + } + + if (!!this.imageOnly) { + this.fileDownloadUrl = this.imageOnlyData.imageUrl; + } else { + this.fileDownloadUrl = this.commonApiService.urlForFileTalkDownload( + { + userSeq: this.userSeq, + deviceType: this.deviceType, + token: this.token, + attachmentsSeq: this.fileInfo.attachmentSeq + }, + this.downloadUrl + ); + } + } ngOnDestroy(): void {} @@ -76,6 +96,47 @@ export class FileViewerDialogComponent implements OnInit, OnDestroy { fileDownloadUrl: this.downloadUrl }); } + onSaveAs(fileDownloadItem: FileDownloadItem): void { + this.nativeService + .selectSaveFilePath(this.fileInfo.fileName) + .then(result => { + if (!result) { + return; + } + + if (result.canceled) { + // this.snackBarService.open( + // this.translateService.instant('common.file.results.canceled'), + // this.translateService.instant('common.file.errors.label'), + // { + // duration: 1000 + // } + // ); + } else { + this.saveFile(fileDownloadItem, result.filePath); + } + }) + .catch(reason => { + this.snackBarService.open( + this.translateService.instant('common.file.errors.failToSpecifyPath'), + this.translateService.instant('common.file.errors.label') + ); + }); + } + + saveFile(fileDownloadItem: FileDownloadItem, savePath?: string) { + this.appFileService.fileTalkDownlod({ + req: { + userSeq: this.userSeq, + deviceType: this.deviceType, + token: this.token, + attachmentsSeq: this.fileInfo.attachmentSeq, + fileDownloadItem + }, + fileName: this.fileInfo.fileName, + savePath + }); + } onClosedViewer(): void { this.dialogRef.close(); diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/chat.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/chat.component.ts index 0dd8874f..87c54e4c 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/chat.component.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/chat.component.ts @@ -20,7 +20,8 @@ import { Subscription, combineLatest, Observable } from 'rxjs'; import { RoomInfo, UserInfoShort, - UserInfo as RoomUserInfo + UserInfo as RoomUserInfo, + RoomType } from '@ucap-webmessenger/protocol-room'; import * as AppStore from '@app/store'; import * as ChatStore from '@app/store/messenger/chat'; @@ -288,9 +289,14 @@ export class ChatComponent implements OnInit, OnDestroy, AfterViewChecked { value => roomInfo.roomSeq === value.roomSeq ); if (-1 < i) { - return this.roomUserShortList[i].userInfos.filter( - user => user.isJoinRoom - ); + if (roomInfo.roomType === RoomType.Single) { + // Ignore type of joinRoom for Single Room. + return this.roomUserShortList[i].userInfos; + } else { + return this.roomUserShortList[i].userInfos.filter( + user => user.isJoinRoom + ); + } } } diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/main-contents/messages.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/main-contents/messages.component.ts index 9c4c8fd7..27fe6c3e 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/main-contents/messages.component.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/main-contents/messages.component.ts @@ -318,6 +318,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit { .pipe(select(AppStore.MessengerSelector.RoomSelector.selectUserinfolist)) .subscribe(userInfoList => { this.userInfoListSubject.next(userInfoList); + this.changeDetectorRef.detectChanges(); }); this.eventListProcessing$ = this.store.pipe( @@ -1585,7 +1586,32 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit { break; case 'ADD_MEMBER': { - const curRoomUser = this.userInfoListSubject.value.filter( + const userInfoList = this.userInfoListSubject.value; + if ( + !!userInfoList && + userInfoList.length >= + environment.productConfig.CommonSetting.maxChatRoomUser + ) { + this.dialogService.open< + AlertDialogComponent, + AlertDialogData, + AlertDialogResult + >(AlertDialogComponent, { + data: { + title: this.translateService.instant('chat.errors.label'), + html: this.translateService.instant( + 'chat.errors.maxCountOfRoomMemberWith', + { + maxCount: + environment.productConfig.CommonSetting.maxChatRoomUser + } + ) + } + }); + return; + } + + const curRoomUser = userInfoList.filter( user => user.seq !== this.loginResSubject.value.userSeq && user.isJoinRoom ); diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.html b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.html index 22b424b4..9f76c6b4 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.html +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.html @@ -1,8 +1,8 @@
- + - +
-
+
diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.ts index a7511be1..d0317acf 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.ts @@ -6,7 +6,7 @@ import { EventEmitter, ViewChild } from '@angular/core'; -import { Subscription } from 'rxjs'; +import { Subscription, combineLatest } from 'rxjs'; import { Store, select } from '@ngrx/store'; import { tap, map, take } from 'rxjs/operators'; @@ -21,7 +21,10 @@ import { DialogService, ConfirmDialogComponent, ConfirmDialogResult, - ConfirmDialogData + ConfirmDialogData, + AlertDialogComponent, + AlertDialogData, + AlertDialogResult } from '@ucap-webmessenger/ui'; import { SelectGroupDialogComponent, @@ -43,6 +46,7 @@ import { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; import { MatDialog } from '@angular/material/dialog'; import { MatMenuTrigger } from '@angular/material/menu'; import { TranslateService } from '@ngx-translate/core'; +import { environment } from 'projects/ucap-webmessenger-app/src/environments/environment.dev'; @Component({ selector: 'app-layout-chat-right-drawer-room-user-list', @@ -82,16 +86,22 @@ export class RoomUserListComponent implements OnInit, OnDestroy { } ngOnInit() { - this.userInfoListSubscription = this.store - .pipe( - select(AppStore.MessengerSelector.RoomSelector.selectUserinfolist), - tap(userInfoList => { - this.userInfoList = userInfoList - .filter(userInfo => userInfo.isJoinRoom === true) - .sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0)); + this.userInfoListSubscription = combineLatest([ + this.store.pipe( + select(AppStore.MessengerSelector.RoomSelector.selectUserinfolist) + ), + this.store.pipe(select(AppStore.MessengerSelector.RoomSelector.roomInfo)) + ]).subscribe(([userInfoList, roomInfo]) => { + this.userInfoList = userInfoList + .filter(userInfo => { + if (roomInfo.roomType === RoomType.Single) { + return true; + } else { + return userInfo.isJoinRoom === true; + } }) - ) - .subscribe(); + .sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0)); + }); this.roomInfoSubscription = this.store .pipe( @@ -139,6 +149,29 @@ export class RoomUserListComponent implements OnInit, OnDestroy { } async onClickAddMember() { + if ( + !!this.userInfoList && + this.userInfoList.length >= + environment.productConfig.CommonSetting.maxChatRoomUser + ) { + this.dialogService.open< + AlertDialogComponent, + AlertDialogData, + AlertDialogResult + >(AlertDialogComponent, { + data: { + title: this.translateService.instant('chat.errors.label'), + html: this.translateService.instant( + 'chat.errors.maxCountOfRoomMemberWith', + { + maxCount: environment.productConfig.CommonSetting.maxChatRoomUser + } + ) + } + }); + return; + } + const curRoomUser = this.userInfoList.filter( user => user.seq !== this.loginRes.userSeq && user.isJoinRoom ); diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/create-chat.dialog.component.html b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/create-chat.dialog.component.html index d16c2637..ba2f1d85 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/create-chat.dialog.component.html +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/create-chat.dialog.component.html @@ -255,15 +255,55 @@ [selected]="getChipsRemoveYn(userInfo)" (removed)="onClickDeleteUser(userInfo)" > - {{ userInfo.name }} + {{ userInfo | ucapTranslate: 'name' }} clear
- - {{ selectedUserList.length }} - {{ 'common.units.persons' | translate }} - + + + + {{ selectedUserList.length }} / + {{ environment.productConfig.CommonSetting.maxChatRoomUser - 1 }} + {{ 'common.units.persons' | translate }} + + + ({{ + 'chat.errors.maxCountOfRoomMemberWith' + | translate + : { + maxCount: + environment.productConfig.CommonSetting.maxChatRoomUser - 1 + } + }}) + + + + + {{ selectedUserList.length }} + {{ 'common.units.persons' | translate }} + + diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/create-chat.dialog.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/create-chat.dialog.component.ts index 53891855..58ceec7b 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/create-chat.dialog.component.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/create-chat.dialog.component.ts @@ -134,6 +134,7 @@ export class CreateChatDialogComponent implements OnInit, OnDestroy { currentTabIndex: number; UserSelectDialogType = UserSelectDialogType; + environment = environment; loginRes: LoginResponse; loginResSubscription: Subscription; @@ -740,8 +741,12 @@ export class CreateChatDialogComponent implements OnInit, OnDestroy { if (this.selectedUserList.length === 0 && !this.selectedRoom) { return true; } - return false; + } else if (this.data.type === UserSelectDialogType.NewChat) { + return ( + this.selectedUserList.length >= + this.environment.productConfig.CommonSetting.maxChatRoomUser + ); } else { return false; } diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/profile/profile.dialog.component.html b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/profile/profile.dialog.component.html index 0f1299ff..0ca72588 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/profile/profile.dialog.component.html +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/profile/profile.dialog.component.html @@ -9,6 +9,9 @@ [openProfileOptions]="data.openProfileOptions" [useBuddyToggleButton]="useBuddyToggleButton" [authInfo]="authInfo" + [enableElephantButton]="getEnableElephantButton()" + (profileImageView)="onClickProfileImageView()" + (sendElephant)="onClickSendElephant()" (openChat)="onClickChat($event)" (sendMessage)="onClickSendMessage($event)" (sendCall)="onClickSendClickToCall($event)" diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/profile/profile.dialog.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/profile/profile.dialog.component.ts index ef6bf2f5..2c820d40 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/profile/profile.dialog.component.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/profile/profile.dialog.component.ts @@ -4,9 +4,13 @@ import { KEY_LOGIN_RES_INFO, KEY_VER_INFO, KEY_AUTH_INFO, - MainMenu + MainMenu, + KEY_URL_INFO } from '@app/types'; -import { SessionStorageService } from '@ucap-webmessenger/web-storage'; +import { + SessionStorageService, + LocalStorageService +} from '@ucap-webmessenger/web-storage'; import { Store, select } from '@ngrx/store'; import * as AppStore from '@app/store'; @@ -30,7 +34,8 @@ import { SnackBarService, AlertDialogComponent, AlertDialogResult, - AlertDialogData + AlertDialogData, + ImageOnlyDataInfo } from '@ucap-webmessenger/ui'; import { VersionInfo2Response } from '@ucap-webmessenger/api-public'; import { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; @@ -57,11 +62,20 @@ import { ConferenceService } from '@ucap-webmessenger/api-prompt'; import { NGXLogger } from 'ngx-logger'; -import { SmsUtils } from '@ucap-webmessenger/daesang'; +import { SmsUtils, WebLinkType } from '@ucap-webmessenger/daesang'; import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native'; import { environment } from '../../../../../environments/environment'; import { TranslateService } from '@ngx-translate/core'; import { UserInfoUpdateType } from '@ucap-webmessenger/protocol-info'; +import { + FileViewerDialogComponent, + FileViewerDialogData, + FileViewerDialogResult +} from '@app/layouts/common/dialogs/file-viewer.dialog.component'; +import { FileEventJson } from '@ucap-webmessenger/protocol-event'; +import { FileType } from '@ucap-webmessenger/protocol-file'; +import { DaesangUrlInfoResponse } from '@ucap-webmessenger/api-external'; +import { AppUserInfo, KEY_APP_USER_INFO } from '@app/types/app-user-info.type'; export interface ProfileDialogData { userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN; @@ -99,6 +113,7 @@ export class ProfileDialogComponent implements OnInit, OnDestroy { @Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService, @Inject(MAT_DIALOG_DATA) public data: ProfileDialogData, private dialogService: DialogService, + private localStorageService: LocalStorageService, private sessionStorageService: SessionStorageService, private commonApiService: CommonApiService, private conferenceService: ConferenceService, @@ -464,6 +479,93 @@ export class ProfileDialogComponent implements OnInit, OnDestroy { ); } + onClickProfileImageView() { + if (!this.loginRes || !this.loginRes.userInfo.profileImageFile) { + return; + } + + const imageOnlyData: ImageOnlyDataInfo = { + imageUrl: this.userInfo.profileImageFile, + imageRootUrl: this.sessionVerinfo.profileRoot, + defaultImage: 'assets/images/no_image.png', + fileName: this.userInfo.name + }; + + this.dialogService.open< + FileViewerDialogComponent, + FileViewerDialogData, + FileViewerDialogResult + >(FileViewerDialogComponent, { + position: { + top: '50px' + }, + maxWidth: '100vw', + maxHeight: '100vh', + height: 'calc(100% - 50px)', + width: '100%', + panelClass: 'app-dialog-full', + data: { + imageOnly: true, + imageOnlyData, + fileInfo: {}, + downloadUrl: this.sessionVerinfo.downloadUrl, + deviceType: this.environmentsInfo.deviceType, + token: this.loginRes.tokenString, + userSeq: this.loginRes.userSeq + } + }); + } + + getEnableElephantButton() { + if (!this.loginRes) { + return false; + } + + const myEmployeeNum = this.loginRes.userInfo.employeeNum; + const profEmployeeNum = this.userInfo.employeeNum; + const trgtEmployeeCode = '10'; // only 10 + if ( + !this.isMe && + !!myEmployeeNum && + !!profEmployeeNum && + myEmployeeNum.slice(0, 2) === trgtEmployeeCode && + profEmployeeNum.slice(0, 2) === trgtEmployeeCode + ) { + return true; + } + return false; + } + + onClickSendElephant() { + const urlInfo: DaesangUrlInfoResponse = this.sessionStorageService.get< + DaesangUrlInfoResponse + >(KEY_URL_INFO); + + if (!!urlInfo && !!urlInfo.webLink) { + const links = urlInfo.webLink.filter( + link => link.key === WebLinkType.Elephant + ); + if (!!links && links.length > 0) { + const appUserInfo = this.localStorageService.encGet( + KEY_APP_USER_INFO, + environment.customConfig.appKey + ); + + let elephantUrl = links[0].url + .replace( + /(\(%USER_PASS%\))/g, + encodeURIComponent(appUserInfo.loginPw) + ) // exchange USER_PASS params + .replace('kind%3D3', 'kind%3D2'); // change value of 'kind' + elephantUrl += '%26empno%3D' + this.userInfo.employeeNum + ','; // add parameter of 'empno' + + console.log(elephantUrl); + + this.nativeService.openDefaultBrowser(elephantUrl); + } + } + } + onUpdateIntro(intro: string) { this.store.dispatch( AuthenticationStore.infoUser({ diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/settings/messenger-settings.dialog.component.html b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/settings/messenger-settings.dialog.component.html index 35793d93..712babdf 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/settings/messenger-settings.dialog.component.html +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/settings/messenger-settings.dialog.component.html @@ -124,13 +124,6 @@
- + +
diff --git a/projects/ucap-webmessenger-app/src/app/layouts/native/components/top-bar.component.scss b/projects/ucap-webmessenger-app/src/app/layouts/native/components/top-bar.component.scss index 2b7497e1..90326ba8 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/native/components/top-bar.component.scss +++ b/projects/ucap-webmessenger-app/src/app/layouts/native/components/top-bar.component.scss @@ -95,7 +95,8 @@ &.dsp { text-indent: 0; } - &.sms { + &.sms, + &.erp { text-indent: 0; letter-spacing: -1px; } @@ -110,6 +111,9 @@ font-size: 11px; letter-spacing: -1px; } + &.elephant { + background-position: 50% 90%; + } } &.app-layout-native-title-bar-logout, @@ -212,7 +216,7 @@ background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='#{$color}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-video'%3E%3Cpolygon points='23 7 16 12 23 17 23 7'%3E%3C/polygon%3E%3Crect x='1' y='5' width='15' height='14' rx='2' ry='2'%3E%3C/rect%3E%3C/svg%3E"); } .elephant { - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='#{$color}' stroke='none' xml:space='preserve'%3E%3Cpath d='M19.1,5c-1,0-2,0.4-2.7,1.1c-1.2-1-2.9-1.5-4.7-1.5c-1.7,0-3.3,0.5-4.5,1.4C6.6,5.3,5.7,5,4.8,5C2.6,5,0.9,6.7,0.9,8.8 c0,1.1,1.1,4.6,2.7,5.7c0.3,0.2,0.7,0.3,1.1,0.3c0.2,0,0.4,0,0.6-0.1c0.5-0.2,0.9-0.4,1.3-0.6c0.6,0.6,1.3,1,2.1,1.4l0.8,1.2 c0.3,0.5,0.5,1.1,0.6,1.7H9.2c-0.7,0-1.2,0.6-1.2,1.2s0.6,1.2,1.2,1.2h0.4c2.5,0,4.6-1.7,5.2-4.1l0.4-1.4c0.7-0.4,1.4-0.8,1.9-1.3 c0.4,0.3,0.9,0.5,1.5,0.8c0.2,0.1,0.4,0.1,0.6,0.1c0.4,0,0.7-0.1,1.1-0.3c1.6-1.1,2.7-4.6,2.7-5.7C22.9,6.7,21.2,5,19.1,5z M5,14.1 c-0.3,0.1-0.7,0.1-1-0.2C2.7,13,1.7,9.8,1.7,8.8c0-1.7,1.4-3.1,3.1-3.1c0.7,0,1.4,0.3,2,0.7C6.4,6.8,6.1,7.1,5.8,7.5 C5.5,7.3,5.1,7.2,4.8,7.2c-1.2,0-2.1,1-2.1,2.1c0,0.6,0.6,2.5,1.5,3.1c0.2,0.1,0.4,0.2,0.6,0.2c0.1,0,0.3,0,0.4-0.1 c0.1,0,0.2-0.1,0.3-0.1c0.2,0.4,0.4,0.8,0.7,1.1C5.8,13.8,5.4,13.9,5,14.1z M5.1,11.8c-0.1,0-0.2,0.1-0.3,0.1 c-0.1,0-0.2,0.1-0.4-0.1c-0.6-0.4-1.2-2-1.2-2.5c0-0.8,0.6-1.4,1.4-1.4C5,7.9,5.2,8,5.4,8.1c-0.3,0.7-0.5,1.4-0.5,2.2 C4.9,10.8,5,11.3,5.1,11.8z M14.7,14.8c-0.1,0-0.2,0.1-0.2,0.2l-0.4,1.6c-0.5,2.1-2.4,3.5-4.5,3.5H9.2c-0.3,0-0.5-0.2-0.5-0.5 c0-0.3,0.2-0.5,0.5-0.5h1.1c0.3,0,0.5-0.2,0.5-0.5c0-0.8-0.2-1.6-0.7-2.3L9.3,15c0-0.1-0.1-0.1-0.2-0.1c-0.8-0.3-1.5-0.8-2.1-1.3 c0,0,0-0.1-0.1-0.1c0,0-0.1-0.1-0.1-0.1c-0.4-0.4-0.7-0.9-0.9-1.4c0,0,0-0.1,0-0.1c-0.2-0.5-0.3-1-0.3-1.5c0-2.8,2.7-5,6.1-5 s6.1,2.3,6.1,5C17.9,12.2,16.7,13.9,14.7,14.8z M18.2,8.3c0.3-0.2,0.6-0.4,0.9-0.4c0.8,0,1.4,0.6,1.4,1.4c0,0.5-0.5,2.1-1.2,2.5 C19.2,12,19.1,12,19,11.9c-0.2-0.1-0.4-0.2-0.6-0.3c0.1-0.4,0.2-0.9,0.2-1.3C18.6,9.6,18.5,8.9,18.2,8.3z M19.8,13.9 c-0.3,0.2-0.7,0.3-1,0.2c-0.5-0.2-0.9-0.4-1.3-0.7c0.3-0.3,0.5-0.7,0.7-1.1c0.2,0.1,0.4,0.2,0.6,0.3c0.1,0.1,0.3,0.1,0.4,0.1 c0.2,0,0.4-0.1,0.6-0.2c0.9-0.6,1.5-2.5,1.5-3.1c0-1.2-1-2.1-2.1-2.1c-0.5,0-0.9,0.2-1.3,0.4c-0.2-0.4-0.5-0.7-0.9-1.1 c0.6-0.6,1.3-0.9,2.1-0.9c1.7,0,3.1,1.4,3.1,3.1C22.2,9.8,21.1,13,19.8,13.9z'/%3E%3Cpath d='M11.5,16.5h1c0.3,0,0.5-0.2,0.5-0.5s-0.2-0.5-0.5-0.5h-1c-0.3,0-0.5,0.2-0.5,0.5S11.2,16.5,11.5,16.5z'/%3E%3Cpath d='M13,17.6H11c-0.3,0-0.5,0.2-0.5,0.5s0.2,0.5,0.5,0.5H13c0.3,0,0.5-0.2,0.5-0.5S13.3,17.6,13,17.6z'/%3E%3Cpath d='M17,12.4c0,0.5-0.4,0.8-0.8,0.8c-0.5,0-0.8-0.4-0.8-0.8s0.4-0.8,0.8-0.8C16.7,11.6,17,12,17,12.4z'/%3E%3Cpath d='M8.5,12.4c0,0.5-0.4,0.8-0.8,0.8c-0.5,0-0.8-0.4-0.8-0.8s0.4-0.8,0.8-0.8C8.1,11.6,8.5,12,8.5,12.4z'/%3E%3C/svg%3E"); + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='#{$color}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' %3E%3Ctitle%3Eelephant%3C/title%3E%3Cpath d='M24,14a17.76,17.76,0,0,0-.54-3.76c-.09-.34-.18-.65-.27-.91s-.08-.23-.13-.35-.09-.23-.14-.34a10.08,10.08,0,0,0-.69-1.3A10.22,10.22,0,0,0,20.86,5.6c-.17-.17-.35-.33-.53-.49l-.57-.46a9.45,9.45,0,0,0-1.56-.93,2.52,2.52,0,0,0-.34-.15,7.07,7.07,0,0,0-.69-.27A9.49,9.49,0,0,0,4.81,10.89V11s0,.06,0,.09h0a.36.36,0,0,1,0,.1h0s0,.06,0,.09h0l0,.08h0l0,.07h0l0,0h0l0,0h0l0,0A1.18,1.18,0,0,1,4,11a1.56,1.56,0,0,1-.11-1A3.62,3.62,0,0,1,4.24,9L1,7.57H1A5.89,5.89,0,0,0,.25,9.09c0,.13-.07.26-.1.39A5.12,5.12,0,0,0,0,10.56,4.3,4.3,0,0,0,.67,13l.22.32,0,.07.23.28,0,0a2.61,2.61,0,0,0,.23.24l.05.05.25.22,0,0,.23.17.06,0,.25.16,0,0a1.27,1.27,0,0,0,.23.12l0,0,.25.11.05,0,.22.09h.05l.24.07.05,0,.21.05h0l.22,0H4l.2,0h0l.2,0h.8l.11.37c.07.17.13.33.2.49l.23.48a9.69,9.69,0,0,0,.82,1.29l.33.42.33.37A10,10,0,0,0,8.49,19.8c.22.17.46.34.7.49a11.25,11.25,0,0,0,1.53.82.48.48,0,0,0,.14.05H11l.11,0h0l.09,0h0A.16.16,0,0,0,11.3,21v0a.2.2,0,0,0,0-.08v0a.29.29,0,0,0,0-.09h0s0,0,0-.08h0v0h0v-.88a2.87,2.87,0,0,1,2.87-2.86h0a3,3,0,0,1,.85.12l.26.1a2.62,2.62,0,0,1,.49.27l.22.16a2.85,2.85,0,0,1,1,2.21v.68h0v.37l0,.06h0a.1.1,0,0,0,0,.05v0l0,0v0l0,0,0,0h.19l.08,0a9.24,9.24,0,0,0,1.41-.7,7.83,7.83,0,0,0,.67-.45,7,7,0,0,0,.92-.8,2.61,2.61,0,0,0,.27-.3,4.94,4.94,0,0,0,.49-.64l.22-.35a5.07,5.07,0,0,0,.34-.75c.05-.13.09-.26.13-.4l.13.55a2,2,0,0,1,0,.24v.07l0,.16v.08l0,.16V18l0,.17v0c0,.06,0,.11,0,.17v.06s0,.08,0,.12v.06s0,.08,0,.11v.05l0,.14v0l0,.1v0a.14.14,0,0,1,0,.06l0,0a.08.08,0,0,0,0,.05l0,0s0,0,0,0h.07l0,0h0l.05-.05,0,0a.86.86,0,0,0,.07-.1A8.15,8.15,0,0,0,24,15Q24,14.5,24,14ZM9,9.07A.73.73,0,0,1,9,7.61.73.73,0,1,1,9,9.07Zm8,4.28a3.66,3.66,0,0,1-2.35,1.28,1.88,1.88,0,0,1-1.41-.72A5.81,5.81,0,0,1,12,10.21c.05-.9.54-3.07,4-3.77a2.18,2.18,0,0,1,.44,0h0a2.46,2.46,0,0,1,2.21,1.75C19.21,9.79,18.59,11.73,17,13.35Z' transform='translate(0 -2.82)' style='fill:%23fff'/%3E%3C/svg%3E"); } .mail { background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='#{$color}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' %3E%3Cpath d='M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z'%3E%3C/path%3E%3Cpolyline points='22,6 12,13 2,6'%3E%3C/polyline%3E%3C/svg%3E"); diff --git a/projects/ucap-webmessenger-app/src/app/layouts/native/components/top-bar.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/native/components/top-bar.component.ts index 51bcd4d8..16a6c687 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/native/components/top-bar.component.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/native/components/top-bar.component.ts @@ -34,7 +34,10 @@ import { KEY_LOGIN_INFO, KEY_VER_INFO, EnvironmentsInfo, - KEY_ENVIRONMENTS_INFO + KEY_ENVIRONMENTS_INFO, + KEY_LOGIN_RES_INFO, + KEY_LOGOUT_INFO, + KEY_AUTH_INFO } from '@app/types'; import { WebLink, @@ -62,7 +65,12 @@ import { ProfileDialogResult, ProfileDialogData } from '@app/layouts/messenger/dialogs/profile/profile.dialog.component'; -import { DialogService } from '@ucap-webmessenger/ui'; +import { + DialogService, + ConfirmDialogComponent, + ConfirmDialogData, + ConfirmDialogResult +} from '@ucap-webmessenger/ui'; import { DOCUMENT } from '@angular/common'; import { MatMenu, MatMenuTrigger } from '@angular/material/menu'; import { StatusCode, StatusType, WindowUtil } from '@ucap-webmessenger/core'; @@ -110,6 +118,8 @@ export class TopBarComponent implements OnInit, OnDestroy { webLinkBadgeMail = 0; webLinkBadgePayment = 0; + webLinkbadgeEmailCountInterval: any; + appVersion: string; WebLinkType = WebLinkType; @@ -215,6 +225,10 @@ export class TopBarComponent implements OnInit, OnDestroy { this.zoomSubscription.unsubscribe(); this.zoomSubscription = undefined; } + + if (!!this.webLinkbadgeEmailCountInterval) { + clearInterval(this.webLinkbadgeEmailCountInterval); + } } initWebLink(loginRes: LoginResponse): void { @@ -246,9 +260,9 @@ export class TopBarComponent implements OnInit, OnDestroy { ); const WebLinkMailCnt = link[0]; - const loginPw = appUserInfo.loginPw; + const loginPw = encodeURIComponent(appUserInfo.loginPw); const loginPw2 = this.loginInfo.loginPw; - const loginId = this.loginInfo.loginId; + const loginId = encodeURIComponent(this.loginInfo.loginId); const token = loginRes.tokenString; const url = WebLinkMailCnt.url @@ -264,6 +278,19 @@ export class TopBarComponent implements OnInit, OnDestroy { catchError(error => of(this.logger.log(error))) ) .subscribe(); + // interval + if (!this.webLinkbadgeEmailCountInterval) { + this.webLinkbadgeEmailCountInterval = setInterval(() => { + this.daesangApiService + .retrieveMailCount(url) + .pipe( + take(1), + map(res => (this.webLinkBadgeMail = res.count)), + catchError(error => of(this.logger.log(error))) + ) + .subscribe(); + }, 5 * 60 * 1000); + } } } if (urlInfo.webLinkAllowedList.indexOf(WebLinkType.Payment) > -1) { @@ -278,9 +305,9 @@ export class TopBarComponent implements OnInit, OnDestroy { ); const WebLinkPaymentCnt = link[0]; - const loginPw = appUserInfo.loginPw; + const loginPw = encodeURIComponent(appUserInfo.loginPw); const loginPw2 = this.loginInfo.loginPw; - const loginId = this.loginInfo.loginId; + const loginId = encodeURIComponent(this.loginInfo.loginId); const token = loginRes.tokenString; const url = WebLinkPaymentCnt.url @@ -406,65 +433,74 @@ export class TopBarComponent implements OnInit, OnDestroy { environment.customConfig.appKey ); - const loginPw = appUserInfo.loginPw; + const loginPw = encodeURIComponent(appUserInfo.loginPw); const loginPw2 = this.loginInfo.loginPw; - const loginId = this.loginInfo.loginId; + const loginId = encodeURIComponent(this.loginInfo.loginId); const token = this.loginRes.tokenString; + const erpPw = this.daesangCipherService.encryptForSapErp( + 'aes256-daesang-key!!', + this.loginRes.userInfo.employeeNum + ); const url = link.url .replace(/(\(%USER_TOKEN%\))/g, token) .replace(/(\(%USER_ID%\))/g, loginId) - .replace(/(\(%USER_PASS%\))/g, loginPw); + .replace(/(\(%USER_PASS%\))/g, loginPw) + .replace(/(\(%ENC_PASSWD%\))/g, erpPw); let width = 1024; let height = 768; let openType = 'INNER-POPUP'; switch (link.key) { - case WebLinkType.Sms: - /** SMS URL */ - { - width = 685; - height = 640; - } - break; + // /** SMS URL */ + // case WebLinkType.Sms: + // { + // width = 685; + // height = 640; + // } + // break; + // /** IT서비스데스크 URL */ // case WebLinkType.Itsvcdesk: - // /** IT서비스데스크 URL */ // { // width = 1400; // height = 1000; // } // break; + /** 화상회의 URL */ case WebLinkType.Conf: - /** 화상회의 URL */ { } break; - case WebLinkType.Itsvcdesk: + /** SMS URL */ + case WebLinkType.Sms: /** IT서비스데스크 URL */ - case WebLinkType.Dsp: + case WebLinkType.Itsvcdesk: /** DSP URL */ - case WebLinkType.Webhard: + case WebLinkType.Dsp: /** 웹하드 URL */ - case WebLinkType.Ep: + case WebLinkType.Webhard: /** EP URL */ - case WebLinkType.Sop: + case WebLinkType.Ep: /** S&OP회의 URL */ - case WebLinkType.Som: + case WebLinkType.Sop: /** S&OM회의 URL */ - case WebLinkType.Elephant: + case WebLinkType.Som: /** 코끼리 URL */ - case WebLinkType.UrgntNews: + case WebLinkType.Elephant: /** 개인속보 URL */ - case WebLinkType.MailCnt: + case WebLinkType.UrgntNews: /** 메일Count URL */ - case WebLinkType.Mail: + case WebLinkType.MailCnt: /** 메일 링크 URL */ - case WebLinkType.PaymentCnt: + case WebLinkType.Mail: /** 결재Count URL */ - case WebLinkType.Payment: + case WebLinkType.PaymentCnt: /** 결재링크 URL */ + case WebLinkType.Payment: + /** Erp URL */ + case WebLinkType.Erp: + /** 비밀번호변경 URL ; PC 메신저만 해당 비밀번호 만료시 */ case WebLinkType.ChgPassword: - /** 비밀번호변경 URL ; PC 메신저만 해당 비밀번호 만료시 */ { openType = 'DEFAULT-BROWSER'; } @@ -692,4 +728,31 @@ export class TopBarComponent implements OnInit, OnDestroy { matDialogRef.removePanelClass('hideDialog'); } } + + async onClickClearSettingAndLogout() { + const result = await this.dialogService.open< + ConfirmDialogComponent, + ConfirmDialogData, + ConfirmDialogResult + >(ConfirmDialogComponent, { + width: '400px', + data: { + title: 'Clear & Logout?', + html: 'Clear General Setting And Logout?' + } + }); + + if (!!result && !!result.choice && result.choice) { + this.localStorageService.remove(KEY_APP_USER_INFO); + this.sessionStorageService.remove(KEY_LOGIN_RES_INFO); + this.sessionStorageService.remove(KEY_VER_INFO); + this.sessionStorageService.remove(KEY_LOGIN_INFO); + this.sessionStorageService.remove(KEY_URL_INFO); + this.sessionStorageService.remove(KEY_AUTH_INFO); + this.sessionStorageService.remove(KEY_LOGOUT_INFO); + this.nativeService.clearAppStorage(); + this.dialogService.closeAll(); + this.store.dispatch(AuthenticationStore.loginRedirect()); + } + } } diff --git a/projects/ucap-webmessenger-app/src/app/pages/account/components/login.page.component.html b/projects/ucap-webmessenger-app/src/app/pages/account/components/login.page.component.html index ceb4974d..88181d91 100644 --- a/projects/ucap-webmessenger-app/src/app/pages/account/components/login.page.component.html +++ b/projects/ucap-webmessenger-app/src/app/pages/account/components/login.page.component.html @@ -53,15 +53,16 @@ --> ; + companyList: Company[]; + companyListSubscription: Subscription; loginFailureCount: Subscription; @@ -57,6 +63,8 @@ export class LoginPageComponent implements OnInit, OnDestroy { waitingTime: number; appUserInfo: AppUserInfo; + loginPw: string; + autologin: boolean; useRememberMe: boolean; useAutoLogin: boolean; @@ -85,7 +93,9 @@ export class LoginPageComponent implements OnInit, OnDestroy { private protocolService: ProtocolService, private localStorageService: LocalStorageService, private sessionStorageService: SessionStorageService, - private appAuthenticationService: AppAuthenticationService + private externalApiService: ExternalApiService, + private appAuthenticationService: AppAuthenticationService, + private ngZone: NgZone ) { this.useRememberMe = environment.productConfig.authentication.rememberMe.use; @@ -95,6 +105,9 @@ export class LoginPageComponent implements OnInit, OnDestroy { KEY_APP_USER_INFO, environment.customConfig.appKey ); + if (!!this.appUserInfo) { + this.autologin = this.appUserInfo.settings.general.autoLogin || false; + } this.rotateInfomationIndex = new Date().getTime() % this.rotateInfomation.length; @@ -113,15 +126,36 @@ export class LoginPageComponent implements OnInit, OnDestroy { this.defatulLoginBtnText = this.translateService.instant('accounts.login'); this.defatulWaitingTime = 5 * 60; // sec - this.store.dispatch( - CompanyStore.companyList({ + this.externalApiService + .companyList({ companyGroupCode: environment.companyConfig.companyGroupCode - }) - ); + } as CompanyListRequest) + .pipe( + take(1), + map(res => { + if (res.statusCode === StatusCode.Success) { + this.store.dispatch(CompanyStore.companyListSuccess(res)); + } else { + this.store.dispatch( + CompanyStore.companyListFailure({ error: 'Failed' }) + ); + } + }), + catchError(error => { + console.log('network disconnected', error); + return of(); + }) + ) + .subscribe(); - this.companyList$ = this.store.pipe( - select(AppStore.SettingSelector.CompanySelector.companyList) - ); + this.companyListSubscription = this.store + .pipe( + select(AppStore.SettingSelector.CompanySelector.companyList), + map(companyList => { + this.companyList = companyList; + }) + ) + .subscribe(); this.loginFailureCount = this.store .pipe( @@ -189,6 +223,56 @@ export class LoginPageComponent implements OnInit, OnDestroy { this.nativeService.idleStateStop(); } + /** CASE :: 자동로그인 실패 (비밀번호 변경에 따른 실패, 네트워크 절체) */ + if (!!personLogout && !!personLogout.autoLogin) { + switch (personLogout.autoLogin.state) { + case 'IDPW_FAIL': + { + this.ngZone.run(() => { + this.dialogService.open< + AlertDialogComponent, + AlertDialogData, + AlertDialogResult + >(AlertDialogComponent, { + width: '360px', + data: { + title: '', + html: this.translateService.instant( + 'accounts.errors.loginFailedIdPw' + ) + } + }); + }); + } + break; + case 'NETWORK_FAIL': + { + this.loginPw = this.appUserInfo.loginPw; + this.autologin = true; + + this.ngZone.run(() => { + this.dialogService.open< + AlertDialogComponent, + AlertDialogData, + AlertDialogResult + >(AlertDialogComponent, { + width: '360px', + data: { + title: this.translateService.instant( + 'accounts.errors.loginFailed' + ), + html: this.translateService.instant( + 'accounts.errors.networkFailedAndRetry' + ) + } + }); + }); + } + break; + } + } + + /** CASE :: 중복 로그인, Remote 로그아웃 */ if (!!personLogout && !!personLogout.reasonCode) { let msg = this.translateService.instant('accounts.results.doLogout'); switch (personLogout.reasonCode) { @@ -234,6 +318,9 @@ export class LoginPageComponent implements OnInit, OnDestroy { } ngOnDestroy(): void { + if (!!this.companyListSubscription) { + this.companyListSubscription.unsubscribe(); + } if (!!this.loginFailureCount) { this.loginFailureCount.unsubscribe(); } @@ -268,23 +355,83 @@ export class LoginPageComponent implements OnInit, OnDestroy { autoLogin: boolean; notValid: () => void; }) { + this.sessionStorageService.remove(KEY_LOGOUT_INFO); + this.loginBtnEnable = false; setTimeout(() => { this.loginBtnEnable = true; }, 30 * 1000); - this.store.dispatch( - AuthenticationStore.webLogin({ - loginInfo: { - companyCode: value.companyCode, - companyGroupType: 'C', - loginId: value.loginId, - loginPw: value.loginPw - }, - rememberMe: value.rememberMe, - autoLogin: value.autoLogin - }) - ); + if (!this.companyList) { + this.externalApiService + .companyList({ + companyGroupCode: environment.companyConfig.companyGroupCode + } as CompanyListRequest) + .pipe( + take(1), + map(res => { + if (res.statusCode === StatusCode.Success) { + this.store.dispatch(CompanyStore.companyListSuccess(res)); + // Recursion function > onLogin + this.onLogin(value); + } else { + this.ngZone.run(() => { + this.dialogService.open< + AlertDialogComponent, + AlertDialogData, + AlertDialogResult + >(AlertDialogComponent, { + width: '360px', + data: { + title: this.translateService.instant( + 'accounts.errors.loginFailed' + ), + html: this.translateService.instant( + 'accounts.errors.networkFailedAndRetry' + ) + } + }); + this.loginBtnEnable = true; + }); + } + }), + catchError(error => { + this.ngZone.run(() => { + this.dialogService.open< + AlertDialogComponent, + AlertDialogData, + AlertDialogResult + >(AlertDialogComponent, { + width: '360px', + data: { + title: this.translateService.instant( + 'accounts.errors.loginFailed' + ), + html: this.translateService.instant( + 'accounts.errors.networkFailedAndRetry' + ) + } + }); + this.loginBtnEnable = true; + }); + return of(); + }) + ) + .subscribe(); + } else { + this.store.dispatch( + AuthenticationStore.webLogin({ + loginInfo: { + companyCode: value.companyCode, + companyGroupType: 'C', + loginId: value.loginId, + loginPw: value.loginPw + }, + rememberMe: value.rememberMe, + autoLogin: value.autoLogin + }) + ); + } } onClickNoti() { diff --git a/projects/ucap-webmessenger-app/src/app/services/authentication.service.ts b/projects/ucap-webmessenger-app/src/app/services/authentication.service.ts index 644221b3..d3f51518 100644 --- a/projects/ucap-webmessenger-app/src/app/services/authentication.service.ts +++ b/projects/ucap-webmessenger-app/src/app/services/authentication.service.ts @@ -79,6 +79,12 @@ export class AppAuthenticationService { } } }; + + if (!!environment.productConfig.defaultSettings.general.autoLaunch) { + this.nativeService.changeAutoLaunch( + environment.productConfig.defaultSettings.general.autoLaunch + ); + } } appUserInfo = { @@ -118,21 +124,23 @@ export class AppAuthenticationService { environment.customConfig.appKey ); - appUserInfo = { - ...appUserInfo, - settings: { - ...appUserInfo.settings, - general: { - ...appUserInfo.settings.general, - autoLogin: false + if (!!appUserInfo) { + appUserInfo = { + ...appUserInfo, + settings: { + ...appUserInfo.settings, + general: { + ...appUserInfo.settings.general, + autoLogin: false + } } - } - }; + }; - this.localStorageService.encSet( - KEY_APP_USER_INFO, - appUserInfo, - environment.customConfig.appKey - ); + this.localStorageService.encSet( + KEY_APP_USER_INFO, + appUserInfo, + environment.customConfig.appKey + ); + } } } diff --git a/projects/ucap-webmessenger-app/src/app/services/notification.service.ts b/projects/ucap-webmessenger-app/src/app/services/notification.service.ts index dbec144a..335b1586 100644 --- a/projects/ucap-webmessenger-app/src/app/services/notification.service.ts +++ b/projects/ucap-webmessenger-app/src/app/services/notification.service.ts @@ -1,7 +1,6 @@ -import { delGroupSuccess, buddy2 } from './../store/messenger/sync/actions'; import { Injectable, Inject } from '@angular/core'; -import { tap, withLatestFrom, take } from 'rxjs/operators'; +import { tap, withLatestFrom } from 'rxjs/operators'; import { Store, select } from '@ngrx/store'; @@ -95,7 +94,11 @@ import { NotificationType, WindowState } from '@ucap-webmessenger/native'; -import { StringUtil, DialogService } from '@ucap-webmessenger/ui'; +import { + StringUtil, + DialogService, + TranslateService as UcapTranslateService +} from '@ucap-webmessenger/ui'; import { UmgProtocolService, SSVC_TYPE_UMG_NOTI, @@ -110,10 +113,10 @@ import { import { AppUserInfo, KEY_APP_USER_INFO } from '@app/types/app-user-info.type'; import { environment } from '../../environments/environment'; -import { NotificationMethod } from '@ucap-webmessenger/core'; +import { NotificationMethod, LocaleCode } from '@ucap-webmessenger/core'; import { Dictionary } from '@ngrx/entity'; import { MessageType } from '@ucap-webmessenger/api-message'; -import { LogoutInfo, KEY_LOGOUT_INFO } from '@app/types'; +import { LogoutInfo, KEY_LOGOUT_INFO, KEY_VER_INFO } from '@app/types'; import { TranslateService } from '@ngx-translate/core'; import { deleteMessageSuccess } from '@app/store/messenger/message'; import { ServerErrorCode } from '@ucap-webmessenger/protocol'; @@ -130,6 +133,11 @@ import { ChatSetting } from '@ucap-webmessenger/ui-settings'; import clone from 'clone'; +import { ElectronBrowserWindowChannel } from '@ucap-webmessenger/electron-core'; +import { UserInfo, RoomUserData } from '@ucap-webmessenger/protocol-sync'; +import { VersionInfo2Response } from '@ucap-webmessenger/api-public'; +import { QueryProtocolService } from '@ucap-webmessenger/protocol-query'; +import { UserInfoListState } from '@app/store/messenger/room'; @Injectable() export class AppNotificationService { @@ -140,8 +148,10 @@ export class AppNotificationService { private roomProtocolService: RoomProtocolService, private groupProtocolService: GroupProtocolService, private buddyProtocolService: BuddyProtocolService, + private queryProtocolService: QueryProtocolService, private statusProtocolService: StatusProtocolService, private translateService: TranslateService, + private ucapTranslateService: UcapTranslateService, private optionProtocolService: OptionProtocolService, private umgProtocolService: UmgProtocolService, private localStorageService: LocalStorageService, @@ -204,150 +214,262 @@ export class AppNotificationService { this.store.pipe( select((state: any) => state.messenger.room.roomInfo as RoomInfo) ), + this.store.pipe( + select( + (state: any) => + state.messenger.room.userInfoList.entities as Dictionary< + UserInfoListState + > + ) + ), this.store.pipe( select( (state: any) => state.messenger.sync.room.entities as Dictionary ) + ), + this.store.pipe( + select( + (state: any) => + state.messenger.sync.buddy2.entities as Dictionary + ) + ), + this.store.pipe( + select( + (state: any) => + state.messenger.sync.roomUserShort.entities as Dictionary< + RoomUserData + > + ) ) ), - tap(([notiOrRes, curRoomInfo, roomList]) => { - switch (notiOrRes.SSVC_TYPE) { - case SSVC_TYPE_EVENT_SEND_RES: - case SSVC_TYPE_EVENT_SEND_NOTI: - { - const noti = notiOrRes as SendNotification; - this.logger.debug( - 'Notification::eventProtocolService::SendNotification', - noti - ); - - this.store.dispatch( - EventStore.sendNotification({ + tap( + ([ + notiOrRes, + curRoomInfo, + curRoomUserInfo, + roomList, + buddyList, + roomUserShorts + ]) => { + switch (notiOrRes.SSVC_TYPE) { + case SSVC_TYPE_EVENT_SEND_RES: + case SSVC_TYPE_EVENT_SEND_NOTI: + { + const noti = notiOrRes as SendNotification; + this.logger.debug( + 'Notification::eventProtocolService::SendNotification', noti - }) - ); + ); - // notification.. - if (notiOrRes.SSVC_TYPE === SSVC_TYPE_EVENT_SEND_NOTI) { - let doNoti = true; + this.store.dispatch( + EventStore.sendNotification({ + noti + }) + ); - // 방별 알림이 꺼져 있으면 노티 안함. - if ( - !!roomList[noti.roomSeq] && - !roomList[noti.roomSeq].receiveAlarm - ) { - doNoti = false; - } + // notification.. + if (notiOrRes.SSVC_TYPE === SSVC_TYPE_EVENT_SEND_NOTI) { + let doNoti = true; - const windowState = this.nativeService.getWindowState(); + const windowState = this.nativeService.getWindowState(); - // 현재 열려 있는 방일경우 노티 안함. - if ( - !!curRoomInfo && - !!curRoomInfo.roomSeq && - curRoomInfo.roomSeq === noti.roomSeq && - !!windowState && - windowState !== WindowState.Minimized && - windowState !== WindowState.Hidden - ) { - doNoti = false; - } + // 현재 열려 있는 방일경우 노티 안함. + if ( + !!curRoomInfo && + !!curRoomInfo.roomSeq && + curRoomInfo.roomSeq === noti.roomSeq && + !!windowState && + windowState.windowState !== WindowState.Minimized && + windowState.windowState !== WindowState.Hidden + ) { + doNoti = false; + } - if (doNoti) { - const appUserInfo = this.localStorageService.encGet< - AppUserInfo - >(KEY_APP_USER_INFO, environment.customConfig.appKey); + // // 포커스 아웃일때 무조건 노티. + // // Case 1 : 단순 포커스 아웃. + // // Case 2 : hidden 시 포커스 인 상태이지만 위에서 필터링 됨. + // console.log(windowState); + // if ( + // windowState.windowFocusState !== + // ElectronBrowserWindowChannel.Focus + // ) { + // doNoti = true; + // } - if (appUserInfo.settings.notification.use) { - if ( - appUserInfo.settings.notification.method === - NotificationMethod.Sound - ) { - const audio = new Audio( - 'assets/sounds/messageAlarm.mp3' - ); - audio.play(); - } else { - const contents = StringUtil.convertFinalEventMessage( - noti.eventType, - noti.info.sentMessageJson - ); + // 방별 알림이 꺼져 있으면 노티 안함. > 우선순위 최상위. + if ( + !!roomList[noti.roomSeq] && + !roomList[noti.roomSeq].receiveAlarm + ) { + doNoti = false; + } - if (!!contents) { - const notiReq: NotificationRequest = { - type: NotificationType.Event, - seq: noti.roomSeq, - title: this.translateService.instant( - 'notification.titleChatEventArrived' - ), - contents, - image: '', - useSound: [ - NotificationMethod.Sound, - NotificationMethod.SoundAndAlert - ].some( - n => - n === appUserInfo.settings.notification.method - ) - ? true - : false, - displayTime: - appUserInfo.settings.notification - .alertExposureTime * 1000 - }; - this.nativeService.notify(notiReq); + if (doNoti) { + const appUserInfo = this.localStorageService.encGet< + AppUserInfo + >(KEY_APP_USER_INFO, environment.customConfig.appKey); + + if (appUserInfo.settings.notification.use) { + if ( + appUserInfo.settings.notification.method === + NotificationMethod.Sound + ) { + const audio = new Audio( + 'assets/sounds/messageAlarm.mp3' + ); + audio.play(); + } else { + const contents = StringUtil.convertFinalEventMessage( + noti.eventType, + noti.info.sentMessageJson + ); + + if (!!contents) { + const notiReq: NotificationRequest = { + type: NotificationType.Event, + seq: noti.roomSeq, + title: this.translateService.instant( + 'notification.titleChatEventArrived' + ), + contents, + image: '', + useSound: [ + NotificationMethod.Sound, + NotificationMethod.SoundAndAlert + ].some( + n => + n === appUserInfo.settings.notification.method + ) + ? true + : false, + displayTime: + appUserInfo.settings.notification + .alertExposureTime * 1000 + }; + + // Sender Info setting + // STEP 1 >> In buddy group. + let senderInfo: any = buddyList[noti.SENDER_SEQ]; + + // STEP 2 >> In Current Room Users. + if (!senderInfo) { + senderInfo = curRoomUserInfo[noti.SENDER_SEQ]; + } + + // STEP 3 >> In All Room Users. + if (!senderInfo) { + for (const key in roomUserShorts) { + if (key === undefined) { + continue; + } + + if (roomUserShorts.hasOwnProperty(key)) { + const element = roomUserShorts[key]; + const filteredUserInfos = element.userInfos.filter( + info => info.seq === noti.SENDER_SEQ + ); + + if ( + !!filteredUserInfos && + filteredUserInfos.length > 0 + ) { + senderInfo = filteredUserInfos[0]; + break; + } + } + } + } + + // Sender Info setting. + if (!!senderInfo) { + // name set + let name = senderInfo.name; + let grade = senderInfo.grade; + + switch ( + this.ucapTranslateService.currentLang.toUpperCase() + ) { + case LocaleCode.English: + name = senderInfo.nameEn; + grade = senderInfo.gradeEn; + break; + case LocaleCode.Chinese: + name = senderInfo.nameCn; + grade = senderInfo.gradeCn; + break; + } + + notiReq.title = this.translateService.instant( + 'notification.titleChatEventArrivedByUser', + { + userInfo: !!grade ? `${name} ${grade}` : name + } + ); + + // Image set. + if (!!senderInfo.profileImageFile) { + const sessionVerinfo = this.sessionStorageService.get< + VersionInfo2Response + >(KEY_VER_INFO); + notiReq.image = `${sessionVerinfo.profileRoot}${senderInfo.profileImageFile}`; + } + } + + // express noti popup + this.nativeService.notify(notiReq); + } } } } } } - } - break; - case SSVC_TYPE_EVENT_READ_RES: - case SSVC_TYPE_EVENT_READ_NOTI: - { - // 대화방 unread count 처리. - const noti = notiOrRes as ReadNotification; - this.logger.debug( - 'Notification::eventProtocolService::ReadNotification', - noti - ); - this.store.dispatch(EventStore.readNotification(noti)); - } - break; - case SSVC_TYPE_EVENT_CANCEL_NOTI: - { - const noti = notiOrRes as CancelNotification; - this.logger.debug( - 'Notification::eventProtocolService::CancelNotification', - noti - ); - this.store.dispatch( - EventStore.cancelNotification({ + break; + case SSVC_TYPE_EVENT_READ_RES: + case SSVC_TYPE_EVENT_READ_NOTI: + { + // 대화방 unread count 처리. + const noti = notiOrRes as ReadNotification; + this.logger.debug( + 'Notification::eventProtocolService::ReadNotification', noti - }) - ); - } - break; - case SSVC_TYPE_EVENT_DEL_RES: - { - const noti = notiOrRes as DelNotification; - this.logger.debug( - 'Notification::eventProtocolService::DelNotification', - noti - ); - this.store.dispatch( - EventStore.delNotification({ + ); + this.store.dispatch(EventStore.readNotification(noti)); + } + break; + case SSVC_TYPE_EVENT_CANCEL_NOTI: + { + const noti = notiOrRes as CancelNotification; + this.logger.debug( + 'Notification::eventProtocolService::CancelNotification', noti - }) - ); - } - break; - default: - break; + ); + this.store.dispatch( + EventStore.cancelNotification({ + noti + }) + ); + } + break; + case SSVC_TYPE_EVENT_DEL_RES: + { + const noti = notiOrRes as DelNotification; + this.logger.debug( + 'Notification::eventProtocolService::DelNotification', + noti + ); + this.store.dispatch( + EventStore.delNotification({ + noti + }) + ); + } + break; + default: + break; + } } - }) + ) ) .subscribe(); this.infoProtocolService.notification$ diff --git a/projects/ucap-webmessenger-app/src/app/store/account/authentication/effects.ts b/projects/ucap-webmessenger-app/src/app/store/account/authentication/effects.ts index 3d00ab0b..116a3b19 100644 --- a/projects/ucap-webmessenger-app/src/app/store/account/authentication/effects.ts +++ b/projects/ucap-webmessenger-app/src/app/store/account/authentication/effects.ts @@ -76,7 +76,11 @@ import { ServiceProtocolService, UserPasswordSetResponse } from '@ucap-webmessenger/protocol-service'; -import { DaesangUrlInfoResponse } from '@ucap-webmessenger/api-external'; +import { + DaesangUrlInfoResponse, + ExternalApiService, + CompanyListRequest +} from '@ucap-webmessenger/api-external'; import { AppUserInfo, KEY_APP_USER_INFO } from '@app/types/app-user-info.type'; import { DaesangCipherService, WebLinkType } from '@ucap-webmessenger/daesang'; import { TranslateService } from '@ngx-translate/core'; @@ -84,44 +88,130 @@ import { InfoProtocolService, UserResponse } from '@ucap-webmessenger/protocol-info'; +import { StatusCode } from '@ucap-webmessenger/api'; @Injectable() export class Effects { - webLogin$ = createEffect(() => - this.actions$.pipe( - ofType(webLogin), - map(action => action), - exhaustMap( - (params: { - loginInfo: LoginInfo; - rememberMe: boolean; - autoLogin: boolean; - }) => - this.piService - .login2({ - loginId: params.loginInfo.loginId, - loginPw: params.loginInfo.loginPw, - companyCode: params.loginInfo.companyCode - }) - .pipe( - map((res: Login2Response) => { - if ('success' !== res.status.toLowerCase()) { - this.store.dispatch(increaseLoginFailCount({})); - return webLoginFailure({ error: 'Failed' }); - } else { - this.store.dispatch(initialLoginFailCount({})); - return webLoginSuccess({ - loginInfo: params.loginInfo, - rememberMe: params.rememberMe, - autoLogin: params.autoLogin, - login2Response: res - }); - } - }), - catchError(error => of(webLoginFailure({ error }))) - ) - ) - ) + retryCount = 0; + retryInterval = 3000; // ms + maxRetryCount = (1 * 60 * 1000) / this.retryInterval; // 20 count due to 1 min. + webLogin$ = createEffect( + () => + this.actions$.pipe( + ofType(webLogin), + map(action => action), + exhaustMap( + (params: { + loginInfo: LoginInfo; + rememberMe: boolean; + autoLogin: boolean; + }) => { + const selfParam = params; + + return this.piService + .login2({ + loginId: params.loginInfo.loginId, + loginPw: params.loginInfo.loginPw, + companyCode: params.loginInfo.companyCode + }) + .pipe( + map((res: Login2Response) => { + if ('success' !== res.status.toLowerCase()) { + if (!!params.autoLogin) { + // auto login Failure. + // clear setting for autologin. + // this.localStorageService.remove(KEY_APP_USER_INFO); + const appUserInfo = this.localStorageService.encGet< + AppUserInfo + >(KEY_APP_USER_INFO, environment.customConfig.appKey); + appUserInfo.settings.general.autoLogin = false; + this.localStorageService.encSet( + KEY_APP_USER_INFO, + appUserInfo, + environment.customConfig.appKey + ); + + // Logout reason setting. + this.sessionStorageService.set( + KEY_LOGOUT_INFO, + { + personLogout: true, + autoLogin: { + state: 'IDPW_FAIL' + } + } as LogoutInfo + ); + + this.router.navigateByUrl('/account/login'); + } else { + this.store.dispatch(increaseLoginFailCount({})); + this.store.dispatch(webLoginFailure({ error: 'Failed' })); + } + } else { + this.store.dispatch(initialLoginFailCount({})); + this.store.dispatch( + webLoginSuccess({ + loginInfo: params.loginInfo, + rememberMe: params.rememberMe, + autoLogin: params.autoLogin, + login2Response: res + }) + ); + } + }), + catchError(error => { + if (!!selfParam.autoLogin) { + if (this.maxRetryCount > this.retryCount) { + this.store.dispatch(logoutInitialize()); + setTimeout(() => { + // this.store.dispatch(webLogin(selfParam)); + this.router.navigateByUrl('/account/login'); + }, this.retryInterval); + + this.retryCount++; + console.log('retry', this.retryCount, this.maxRetryCount); + return of(webLoginFailure({ error })); + } else { + console.log( + 'retry End', + this.retryCount, + this.maxRetryCount + ); + + // clear setting for autologin. + const appUserInfo = this.localStorageService.encGet< + AppUserInfo + >(KEY_APP_USER_INFO, environment.customConfig.appKey); + appUserInfo.settings.general.autoLogin = false; + this.localStorageService.encSet( + KEY_APP_USER_INFO, + appUserInfo, + environment.customConfig.appKey + ); + + // Logout reason setting. + this.sessionStorageService.set( + KEY_LOGOUT_INFO, + { + personLogout: true, + autoLogin: { + state: 'NETWORK_FAIL' + } + } as LogoutInfo + ); + } + } + + this.router.navigateByUrl('/account/login'); + + console.log('not retry'); + return of(webLoginFailure({ error })); + }) + ); + } + ) + ), + { dispatch: false } ); webLoginSuccess$ = createEffect( @@ -500,6 +590,7 @@ export class Effects { private sessionStorageService: SessionStorageService, private piService: PiService, private appAuthenticationService: AppAuthenticationService, + private externalApiService: ExternalApiService, private protocolService: ProtocolService, private authenticationProtocolService: AuthenticationProtocolService, private infoProtocolService: InfoProtocolService, diff --git a/projects/ucap-webmessenger-app/src/app/store/messenger/event/effects.ts b/projects/ucap-webmessenger-app/src/app/store/messenger/event/effects.ts index aeadcdb2..3b6bdcea 100644 --- a/projects/ucap-webmessenger-app/src/app/store/messenger/event/effects.ts +++ b/projects/ucap-webmessenger-app/src/app/store/messenger/event/effects.ts @@ -925,20 +925,37 @@ export class Effects { }) ); } - } - // not opened room :: unread count increased - if ( - action.SVC_TYPE === SVC_TYPE_EVENT && - action.SSVC_TYPE === SSVC_TYPE_EVENT_SEND_RES - ) { - /** - * 다른 디바이스에서 대화를 송신 할경우 RES 가 noti 로 유입될 수 있다. - * 이때 unread count 를 중가하지 않는다. - */ + if (action.info.type === EventType.File) { + // File 정보 수집. + this.store.dispatch( + fileInfo({ + req: { + roomSeq: action.roomSeq, + // { 파일타입 } cf) I : 이미지 V: 동영상 F: 파일 "" 빈값이면 모든 타입을 내려줌 + type: FileType.All + } + }) + ); + } } else { - if (!roomInfo || roomInfo.roomSeq !== action.roomSeq) { - if (!!trgtRoomInfos && !!trgtRoomInfos[action.roomSeq]) { + // not opened room :: unread count increased + if ( + action.SVC_TYPE === SVC_TYPE_EVENT && + action.SSVC_TYPE === SSVC_TYPE_EVENT_SEND_RES + ) { + /** + * 다른 디바이스에서 대화를 송신 할경우 RES 가 noti 로 유입될 수 있다. + * 이때 unread count 를 중가하지 않는다. + */ + } else { + if ( + !!trgtRoomInfos && + !!trgtRoomInfos[action.roomSeq] && + action.info.type !== EventType.Join && + action.info.type !== EventType.Exit && + action.info.type !== EventType.ForcedExit + ) { const noReadCnt = trgtRoomInfos[action.roomSeq].noReadCnt; this.store.dispatch( SyncStore.updateUnreadCount({ @@ -950,19 +967,6 @@ export class Effects { } } - if (action.info.type === EventType.File) { - // File 정보 수집. - this.store.dispatch( - fileInfo({ - req: { - roomSeq: action.roomSeq, - // { 파일타입 } cf) I : 이미지 V: 동영상 F: 파일 "" 빈값이면 모든 타입을 내려줌 - type: FileType.All - } - }) - ); - } - // 대화 > 리스트 :: finalEventMessage refresh this.store.dispatch(ChatStore.newEventMessage(action)); }) diff --git a/projects/ucap-webmessenger-app/src/app/store/messenger/room/actions.ts b/projects/ucap-webmessenger-app/src/app/store/messenger/room/actions.ts index be56cadb..d6e5f084 100644 --- a/projects/ucap-webmessenger-app/src/app/store/messenger/room/actions.ts +++ b/projects/ucap-webmessenger-app/src/app/store/messenger/room/actions.ts @@ -188,3 +188,8 @@ export const exitFailure = createAction( '[Messenger::Room] Exit Failure', props<{ error: any }>() ); + +export const syncRoomRefreshByInvite = createAction( + '[Messenger::Room] Sync Room Refresh by invite', + props() +); diff --git a/projects/ucap-webmessenger-app/src/app/store/messenger/room/effects.ts b/projects/ucap-webmessenger-app/src/app/store/messenger/room/effects.ts index 9cf07234..9d8a4e81 100644 --- a/projects/ucap-webmessenger-app/src/app/store/messenger/room/effects.ts +++ b/projects/ucap-webmessenger-app/src/app/store/messenger/room/effects.ts @@ -5,6 +5,8 @@ import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Store, select } from '@ngrx/store'; import { NGXLogger } from 'ngx-logger'; +import { environment } from '../../../../environments/environment'; + import { of } from 'rxjs'; import { tap, @@ -68,7 +70,8 @@ import { exitForcingFailure, exitForcingSuccess, exitNotificationOthers, - clearRoomUser + clearRoomUser, + syncRoomRefreshByInvite } from './actions'; import { SessionStorageService } from '@ucap-webmessenger/web-storage'; import { LoginInfo, KEY_LOGIN_INFO, KEY_VER_INFO } from '@app/types'; @@ -79,6 +82,8 @@ import { AlertDialogData, AlertDialogResult } from '@ucap-webmessenger/ui'; +import { ActivatedRouteSnapshot } from '@angular/router'; +import { TranslateService } from '@ngx-translate/core'; @Injectable() export class Effects { @@ -285,6 +290,29 @@ export class Effects { ) ), exhaustMap(([action, roomInfo]) => { + if ( + environment.productConfig.CommonSetting.maxChatRoomUser < + action.req.userSeqs.length + ) { + this.dialogService.open< + AlertDialogComponent, + AlertDialogData, + AlertDialogResult + >(AlertDialogComponent, { + data: { + title: this.translateService.instant('chat.errors.label'), + html: this.translateService.instant( + 'chat.errors.maxCountOfRoomMemberWith', + { + maxCount: + environment.productConfig.CommonSetting.maxChatRoomUser + } + ) + } + }); + return of(inviteFailure({ error: 'over size room users !!' })); + } + if (roomInfo.roomType === RoomType.Single) { // Re Open return this.roomProtocolService.open(action.req).pipe( @@ -371,10 +399,11 @@ export class Effects { ) ), tap(([action, roomInfo]) => { + const loginInfo = this.sessionStorageService.get( + KEY_LOGIN_INFO + ); + if (!!roomInfo && roomInfo.roomSeq === action.noti.roomSeq) { - const loginInfo = this.sessionStorageService.get( - KEY_LOGIN_INFO - ); this.store.dispatch( info({ roomSeq: action.noti.roomSeq, @@ -383,6 +412,15 @@ export class Effects { }) ); } + + // room list refresh for exist. + this.store.dispatch( + syncRoomRefreshByInvite({ + roomSeq: action.noti.roomSeq, + isDetail: true, + localeCode: loginInfo.localeCode + }) + ); }) ); }, @@ -454,6 +492,7 @@ export class Effects { private store: Store, private roomProtocolService: RoomProtocolService, private sessionStorageService: SessionStorageService, + private translateService: TranslateService, private dialogService: DialogService, private logger: NGXLogger ) {} diff --git a/projects/ucap-webmessenger-app/src/app/store/messenger/room/reducers.ts b/projects/ucap-webmessenger-app/src/app/store/messenger/room/reducers.ts index b9d15f7c..2c03709e 100644 --- a/projects/ucap-webmessenger-app/src/app/store/messenger/room/reducers.ts +++ b/projects/ucap-webmessenger-app/src/app/store/messenger/room/reducers.ts @@ -72,6 +72,7 @@ export const reducer = createReducer( on(updateSuccess, (state, action) => { const curRoomInfo = state.roomInfo; + if (!curRoomInfo) { return { ...state }; } diff --git a/projects/ucap-webmessenger-app/src/app/store/messenger/sync/effects.ts b/projects/ucap-webmessenger-app/src/app/store/messenger/sync/effects.ts index 7e3ada7a..edc41612 100644 --- a/projects/ucap-webmessenger-app/src/app/store/messenger/sync/effects.ts +++ b/projects/ucap-webmessenger-app/src/app/store/messenger/sync/effects.ts @@ -540,6 +540,62 @@ export class Effects { }, { dispatch: false } ); + syncRoomRefreshByInvite$ = createEffect( + () => { + let roomInfo: RoomInfo; + let userInfoShortList: UserInfoShort[]; + let userInfoList: RoomUserInfo[]; + + return this.actions$.pipe( + ofType(RoomStore.syncRoomRefreshByInvite), + tap(() => { + roomInfo = null; + userInfoShortList = []; + userInfoList = []; + }), + withLatestFrom( + this.store.pipe( + select((state: any) => state.messenger.sync.room.ids as string[]) + ) + ), + switchMap(([req, roomSeqList]) => { + const index = roomSeqList.findIndex( + (roomSeq, i) => roomSeq === req.roomSeq + ); + if (index > -1) { + return this.roomProtocolService.info(req).pipe( + map(res => { + switch (res.SSVC_TYPE) { + case SSVC_TYPE_ROOM_INFO_ROOM: + roomInfo = (res as InfoData).roomInfo; + break; + case SSVC_TYPE_ROOM_INFO_USER: + userInfoShortList.push(...(res as UserShortData).userInfos); + break; + case SSVC_TYPE_ROOM_INFO_USER2: + userInfoList.push(...(res as UserData).userInfos); + break; + case SSVC_TYPE_ROOM_INFO_RES: + this.store.dispatch( + refreshRoomSuccess({ + roomInfo, + userInfoShortList, + userInfoList + }) + ); + break; + } + }), + catchError(error => of(refreshRoomFailure({ error }))) + ); + } else { + return of(); + } + }) + ); + }, + { dispatch: false } + ); // 대화상대 초대 성공 후 처리. inviteSuccess$ = createEffect(() => diff --git a/projects/ucap-webmessenger-app/src/app/store/messenger/sync/reducers.ts b/projects/ucap-webmessenger-app/src/app/store/messenger/sync/reducers.ts index 163eb95f..16e24260 100644 --- a/projects/ucap-webmessenger-app/src/app/store/messenger/sync/reducers.ts +++ b/projects/ucap-webmessenger-app/src/app/store/messenger/sync/reducers.ts @@ -105,6 +105,11 @@ export const reducer = createReducer( on(clearRoomUsers, (state, action) => { const roomInfo = state.room.entities[action.roomSeq]; + if (!roomInfo) { + // 방에 초대만 되고 대화가 발생하지 않아 방정보가 없을때, 방에서 강퇴된다면 roomInfo 가 없을 수 있다. + return { ...state }; + } + const roomUserList: RoomUserDetailData = { ...state.roomUser.entities[action.roomSeq] }; diff --git a/projects/ucap-webmessenger-app/src/app/types/logout-info.type.ts b/projects/ucap-webmessenger-app/src/app/types/logout-info.type.ts index b187b95f..b2cb68ab 100644 --- a/projects/ucap-webmessenger-app/src/app/types/logout-info.type.ts +++ b/projects/ucap-webmessenger-app/src/app/types/logout-info.type.ts @@ -2,8 +2,17 @@ export const KEY_LOGOUT_INFO = 'ucap::LOGOUT_INFO'; export interface LogoutInfo { personLogout: boolean; + + /** 중복로그인, Remote 로그아웃 시에 사용. */ reasonCode?: number; - ip?: string; - mac?: string; - forceType?: string; + ip?: string; // 중복로그인시 사용. + mac?: string; // 중복로그인시 사용. + forceType?: string; // remote 로그아웃 시 사용. + + /** 자동로그인 실패시. */ + autoLogin?: { + state: string; + id?: string; + pw?: string; + }; } diff --git a/projects/ucap-webmessenger-app/src/assets/i18n/en.json b/projects/ucap-webmessenger-app/src/assets/i18n/en.json index 4daee51b..62ac406a 100644 --- a/projects/ucap-webmessenger-app/src/assets/i18n/en.json +++ b/projects/ucap-webmessenger-app/src/assets/i18n/en.json @@ -45,7 +45,9 @@ "failToChangePassword": "Failed to change password.", "loginFailed": "Failed to login", "loginFailedIdPw": "Username or password do not match.", - "loginFailOverTry": "Password error count exceeded.
Check your password
Please try again later." + "loginFailOverTry": "Password error count exceeded.
Check your password
Please try again later.", + "networkFailedAndExit": "Please exit the program due to a network problem.
Please check the network and try again.", + "networkFailedAndRetry": "Cannot run due to network problem.
Please check the network and try again." } }, "profile": { @@ -57,6 +59,7 @@ "removeBuddy": "Remove a buddy", "remoteSupport": "Remote support", "fieldCompany": "Company", + "fieldEmployeeNumber": "Employee Number", "fieldResponsibilities": "Responsibilities", "fieldWorkplace": "Workplace", "fieldJob": "Job", @@ -388,7 +391,9 @@ "label": "Update" }, "notification": { + "titleChatEventArrivedByUser": "A Message of chat from {{userInfo}}.", "titleChatEventArrived": "A message of chat has arrived.", + "titleMessageArrivedByUser": "A Message from {{userInfo}}.", "titleMessageArrived": "A message has arrived." }, "common": { diff --git a/projects/ucap-webmessenger-app/src/assets/i18n/ko.json b/projects/ucap-webmessenger-app/src/assets/i18n/ko.json index ea7bf609..23100e33 100644 --- a/projects/ucap-webmessenger-app/src/assets/i18n/ko.json +++ b/projects/ucap-webmessenger-app/src/assets/i18n/ko.json @@ -45,7 +45,9 @@ "failToChangePassword": "비밀번호 변경에 실패하였습니다.", "loginFailed": "로그인에 실패하였습니다.", "loginFailedIdPw": "아이디 또는 패스워드가
일치하지 않습니다.", - "loginFailOverTry": "비밀번호 오류 횟수 초과입니다.
비밀번호를 확인하신 후
잠시 후 다시 시작해 주세요." + "loginFailOverTry": "비밀번호 오류 횟수 초과입니다.
비밀번호를 확인하신 후
잠시 후 다시 시작해 주세요.", + "networkFailedAndExit": "네트워크 문제로 프로그램을 종료합니다.
네트워크 확인후 다시 시도해 주세요.", + "networkFailedAndRetry": "네트워크 문제로 실행할 수 없습니다.
네트워크 확인후 다시 시도해 주세요." } }, "profile": { @@ -57,6 +59,7 @@ "removeBuddy": "동료삭제", "remoteSupport": "원격 지원", "fieldCompany": "회사", + "fieldEmployeeNumber": "사번", "fieldResponsibilities": "담당업무", "fieldWorkplace": "근무지", "fieldJob": "직무", @@ -388,7 +391,9 @@ "label": "업데이트" }, "notification": { + "titleChatEventArrivedByUser": "{{userInfo}} 님의 메세지.", "titleChatEventArrived": "메세지가 도착했습니다.", + "titleMessageArrivedByUser": "{{userInfo}} 님의 쪽지.", "titleMessageArrived": "쪽지가 도착했습니다." }, "common": { diff --git a/projects/ucap-webmessenger-app/src/assets/images/no_image.png b/projects/ucap-webmessenger-app/src/assets/images/no_image.png new file mode 100644 index 00000000..57ff3837 Binary files /dev/null and b/projects/ucap-webmessenger-app/src/assets/images/no_image.png differ diff --git a/projects/ucap-webmessenger-daesang/src/lib/services/daesang-cipher.service.ts b/projects/ucap-webmessenger-daesang/src/lib/services/daesang-cipher.service.ts index 498c4410..d5d81d96 100644 --- a/projects/ucap-webmessenger-daesang/src/lib/services/daesang-cipher.service.ts +++ b/projects/ucap-webmessenger-daesang/src/lib/services/daesang-cipher.service.ts @@ -50,7 +50,7 @@ export class DaesangCipherService { * const dec = this.daesangCipherService.decryptForSapErp('aes256-daesang-key!!',enc); * console.log('dec', dec); */ - encryptForSapErp(pvUserKey: string, employeeNum: number): string { + encryptForSapErp(pvUserKey: string, employeeNum: string): string { // const txt = '20200221090321_asdfghjk'; // 1QgLAiLqJ6Uo6bE4Qk1o3Yd6mfqxXSnmqXX%2FXLL7DoA%3D // const txt = '20200221101444_asdfghjk'; // Lz1TIdGTQQMui%2BBHMdj8fatYYhXbwJEL%2BJ91C7jUWEs%3D const str = moment().format('YYYYMMDDHHmmss') + '_' + employeeNum; diff --git a/projects/ucap-webmessenger-daesang/src/lib/types/web-link.type.ts b/projects/ucap-webmessenger-daesang/src/lib/types/web-link.type.ts index 9967295a..e6ab0aa7 100644 --- a/projects/ucap-webmessenger-daesang/src/lib/types/web-link.type.ts +++ b/projects/ucap-webmessenger-daesang/src/lib/types/web-link.type.ts @@ -28,5 +28,7 @@ export enum WebLinkType { /** 결재링크 URL */ Payment = 'WebLinkPayment', /** 비밀번호변경 URL ; PC 메신저만 해당 비밀번호 만료시 */ - ChgPassword = 'WebLinkChgPassword' + ChgPassword = 'WebLinkChgPassword', + /** Erp */ + Erp = 'WebLinkERP' } diff --git a/projects/ucap-webmessenger-daesang/src/lib/utils/SmsUtils.ts b/projects/ucap-webmessenger-daesang/src/lib/utils/SmsUtils.ts index cc178219..72422cf2 100644 --- a/projects/ucap-webmessenger-daesang/src/lib/utils/SmsUtils.ts +++ b/projects/ucap-webmessenger-daesang/src/lib/utils/SmsUtils.ts @@ -39,12 +39,12 @@ export class SmsUtils { openSendSms(token: string, employeeNum?: string) { const url = this.url.replace(/(\(%USER_TOKEN%\))/g, token); - // this.nativeService.openDefaultBrowser(url); - WindowUtil.popupOpen( - url + `&ruser=${employeeNum},`, - 'DaesangSMS', - 685, - 640 - ); + this.nativeService.openDefaultBrowser(url + `&ruser=${employeeNum},`); + // WindowUtil.popupOpen( + // url + `&ruser=${employeeNum},`, + // 'DaesangSMS', + // 685, + // 640 + // ); } } diff --git a/projects/ucap-webmessenger-native-browser/src/lib/services/browser-native.service.ts b/projects/ucap-webmessenger-native-browser/src/lib/services/browser-native.service.ts index bf95404f..dc284721 100644 --- a/projects/ucap-webmessenger-native-browser/src/lib/services/browser-native.service.ts +++ b/projects/ucap-webmessenger-native-browser/src/lib/services/browser-native.service.ts @@ -18,6 +18,7 @@ import { TranslateLoaderService } from '../translate/browser-loader'; import { NotificationService } from '../notification/notification.service'; import { Injectable } from '@angular/core'; import { FileUtil, StatusCode } from '@ucap-webmessenger/core'; +import { ElectronBrowserWindowChannel } from '@ucap-webmessenger/electron-core'; @Injectable({ providedIn: 'root' @@ -229,11 +230,22 @@ export class BrowserNativeService extends NativeService { windowMinimize(): void {} windowMaximize(): void {} - getWindowState(): WindowState { - return WindowState.Normal; + getWindowState(): { + windowState: WindowState; + windowFocusState: + | ElectronBrowserWindowChannel.Focus + | ElectronBrowserWindowChannel.Blur; + } { + return { + windowState: WindowState.Normal, + windowFocusState: ElectronBrowserWindowChannel.Focus + }; } appExit(): void {} + appLogging(error: any): void { + console.error('[G]', error); + } zoomTo(factor: number): Promise { return new Promise((resolve, reject) => { diff --git a/projects/ucap-webmessenger-native-electron/src/lib/services/electron-native.service.ts b/projects/ucap-webmessenger-native-electron/src/lib/services/electron-native.service.ts index 061d2d09..951fb55e 100644 --- a/projects/ucap-webmessenger-native-electron/src/lib/services/electron-native.service.ts +++ b/projects/ucap-webmessenger-native-electron/src/lib/services/electron-native.service.ts @@ -31,6 +31,7 @@ import { Injectable } from '@angular/core'; import { TranslateLoaderService } from '../translate/electron-loader'; import { TranslateLoader } from '@ngx-translate/core'; import { StatusCode } from '@ucap-webmessenger/core'; +import { ElectronBrowserWindowChannel } from '@ucap-webmessenger/electron-core'; @Injectable({ providedIn: 'root' @@ -65,6 +66,10 @@ export class ElectronNativeService implements NativeService { private backgroundCheckForUpdatesSubject: Subject | null = null; private backgroundCheckForUpdates$: Observable | null = null; + private windowFocusState: + | ElectronBrowserWindowChannel.Focus + | ElectronBrowserWindowChannel.Blur; + type(): NativeType { return NativeType.Electron; } @@ -395,25 +400,37 @@ export class ElectronNativeService implements NativeService { } } - getWindowState(): WindowState { + getWindowState(): { + windowState: WindowState; + windowFocusState: + | ElectronBrowserWindowChannel.Focus + | ElectronBrowserWindowChannel.Blur; + } { + let windowState = WindowState.Normal; if (!remote.getCurrentWindow().isVisible()) { - return WindowState.Hidden; + windowState = WindowState.Hidden; } else if (remote.getCurrentWindow().isMinimized()) { - return WindowState.Minimized; + windowState = WindowState.Minimized; } else if (remote.getCurrentWindow().isNormal()) { - return WindowState.Normal; + windowState = WindowState.Normal; } else if (remote.getCurrentWindow().isMaximized()) { - return WindowState.Maximized; + windowState = WindowState.Maximized; } else if (remote.getCurrentWindow().isFullScreen()) { - return WindowState.FullScreen; - } else { - return WindowState.Normal; + windowState = WindowState.FullScreen; } + + return { + windowState, + windowFocusState: this.windowFocusState + }; } appExit(): void { this.ipcRenderer.send(AppChannel.Exit); } + appLogging(error: any): void { + this.ipcRenderer.send(AppChannel.Logging, error); + } zoomTo(factor: number): Promise { return new Promise((resolve, reject) => { @@ -529,5 +546,17 @@ export class ElectronNativeService implements NativeService { this.shell = (window as any).require('electron').shell; this.webFrame = (window as any).require('electron').webFrame; } + + this.ipcRenderer.on( + WindowStateChannel.FocuseChanged, + ( + event: any, + status: + | ElectronBrowserWindowChannel.Focus + | ElectronBrowserWindowChannel.Blur + ) => { + this.windowFocusState = status; + } + ); } } diff --git a/projects/ucap-webmessenger-native-electron/src/lib/types/channel.type.ts b/projects/ucap-webmessenger-native-electron/src/lib/types/channel.type.ts index f5bae067..772b89d7 100644 --- a/projects/ucap-webmessenger-native-electron/src/lib/types/channel.type.ts +++ b/projects/ucap-webmessenger-native-electron/src/lib/types/channel.type.ts @@ -46,7 +46,8 @@ export enum ProcessChannel { } export enum WindowStateChannel { - Changed = 'UCAP::windowState::windowStateChanged' + Changed = 'UCAP::windowState::windowStateChanged', + FocuseChanged = 'UCAP::windowState::windowFocusStateChanged' } export enum IdleStateChannel { @@ -61,7 +62,8 @@ export enum ClipboardChannel { } export enum AppChannel { - Exit = 'UCAP::app::exit' + Exit = 'UCAP::app::exit', + Logging = 'UCAP::app::logging' } export enum ExternalChannel { diff --git a/projects/ucap-webmessenger-native/src/lib/services/native.service.ts b/projects/ucap-webmessenger-native/src/lib/services/native.service.ts index 961ba426..e052a398 100644 --- a/projects/ucap-webmessenger-native/src/lib/services/native.service.ts +++ b/projects/ucap-webmessenger-native/src/lib/services/native.service.ts @@ -7,6 +7,7 @@ import { TranslateLoader } from '@ngx-translate/core'; import { StatusCode } from '@ucap-webmessenger/core'; import { UpdateInfo, UpdateCheckConfig } from '../models/update-info'; import { NativeType } from '../types/native.type'; +import { ElectronBrowserWindowChannel } from '@ucap-webmessenger/electron-core'; export type NativePathName = | 'home' @@ -73,9 +74,15 @@ export abstract class NativeService { abstract windowClose(): void; abstract windowMinimize(): void; abstract windowMaximize(): void; - abstract getWindowState(): WindowState; + abstract getWindowState(): { + windowState: WindowState; + windowFocusState: + | ElectronBrowserWindowChannel.Focus + | ElectronBrowserWindowChannel.Blur; + }; abstract zoomTo(factor: number): Promise; abstract appExit(): void; + abstract appLogging(error: any): void; abstract idleStateChanged(): Observable; abstract idleStateStop(): void; diff --git a/projects/ucap-webmessenger-pi/src/lib/apis/login2.ts b/projects/ucap-webmessenger-pi/src/lib/apis/login2.ts index 540017ca..20aba5a9 100644 --- a/projects/ucap-webmessenger-pi/src/lib/apis/login2.ts +++ b/projects/ucap-webmessenger-pi/src/lib/apis/login2.ts @@ -1,6 +1,12 @@ -import { ParameterUtil, APIEncoder, APIDecoder } from '@ucap-webmessenger/api'; +import { + ParameterUtil, + APIEncoder, + APIDecoder, + HttpUrlEncodingCodec +} from '@ucap-webmessenger/api'; import { PIRequest, PIResponse } from './pi'; +import { HttpParams, HttpParameterCodec } from '@angular/common/http'; export interface Login2Request extends PIRequest { companyCode: string; @@ -26,7 +32,14 @@ const login2EncodeMap = { }; export const encodeLogin2: APIEncoder = (req: Login2Request) => { - return ParameterUtil.encode(login2EncodeMap, req); + let parameter: HttpParams = new HttpParams({ + encoder: new HttpUrlEncodingCodec() + }); + parameter = parameter.append('companyCd', req.companyCode); + parameter = parameter.append('loginId', req.loginId); + parameter = parameter.append('loginPw', req.loginPw); + return parameter; + // return ParameterUtil.encodeForm(login2EncodeMap, req); }; export const decodeLogin2: APIDecoder = (res: any) => { diff --git a/projects/ucap-webmessenger-protocol-query/src/lib/protocols/user-seq.ts b/projects/ucap-webmessenger-protocol-query/src/lib/protocols/user-seq.ts index 9a8ae379..43ea1644 100644 --- a/projects/ucap-webmessenger-protocol-query/src/lib/protocols/user-seq.ts +++ b/projects/ucap-webmessenger-protocol-query/src/lib/protocols/user-seq.ts @@ -22,7 +22,7 @@ export interface UserSeqRequest extends ProtocolRequest { // 기관코드(s) companyCode: string; // 상세정보여부(s) - isDetail: boolean; + detailType: string; // 발신자회사코드(s) senderCompanyCode: string; // 발신자임직원유형(s) @@ -51,7 +51,7 @@ export const encodeUserSeq: ProtocolEncoder = ( { type: PacketBodyValue.String, value: req.divCd }, { type: PacketBodyValue.String, value: req.loginIds.join(',') }, { type: PacketBodyValue.String, value: req.companyCode }, - { type: PacketBodyValue.String, value: req.isDetail ? 'Y' : 'N' }, + { type: PacketBodyValue.String, value: req.detailType }, { type: PacketBodyValue.String, value: req.senderCompanyCode }, { type: PacketBodyValue.String, value: req.senderEmployeeType } ); diff --git a/projects/ucap-webmessenger-ui-account/src/lib/components/login.component.ts b/projects/ucap-webmessenger-ui-account/src/lib/components/login.component.ts index fef9f5de..12350319 100644 --- a/projects/ucap-webmessenger-ui-account/src/lib/components/login.component.ts +++ b/projects/ucap-webmessenger-ui-account/src/lib/components/login.component.ts @@ -39,6 +39,8 @@ export class LoginComponent implements OnInit { @Input() loginId: string; @Input() + loginPw?: string; + @Input() rememberMe: boolean; @Input() autoLogin: boolean; @@ -85,6 +87,9 @@ export class LoginComponent implements OnInit { } const loginPwValidators: ValidatorFn[] = [Validators.required]; this.loginPwFormControl.setValidators(loginPwValidators); + if (!!this.loginPw) { + this.loginPwFormControl.setValue(this.loginPw); + } this.loginForm = this.formBuilder.group({ companyCodeFormControl: this.companyCodeFormControl, diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box.component.html b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box.component.html index c25bea9a..2b69b241 100644 --- a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box.component.html +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box.component.html @@ -98,9 +98,7 @@
-
- {{ senderName }} -
+
{{ senderName }} {{ senderGrade }}
{{ 'chat.sentDate' | translate }} {{ message.sentMessageJson.postDate | ucapDate: 'YYYY.MM.DD a hh:mm' }} -
  • - {{ message.sentMessageJson.content }} -
  • +
  • {{ 'profile.fieldCompany' | translate }}
    {{ userInfo.companyName | ucapStringEmptycheck }}
    +
  • +
    + {{ 'profile.fieldEmployeeNumber' | translate }} +
    +
    {{ userInfo.employeeNum | slice: 2 }}
    +
  • {{ 'search.fieldDeptartment' | translate }}
    {{ userInfo | ucapTranslate: 'deptName' }}
    @@ -379,6 +408,31 @@ }}
  • + +
    diff --git a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer.component.ts b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer.component.ts index adfc1065..f58d1dac 100644 --- a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer.component.ts +++ b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer.component.ts @@ -4,6 +4,7 @@ import { FileEventJson } from '@ucap-webmessenger/protocol-event'; import { FileViewerType } from '../types/file-viewer.type'; import { FileType } from '@ucap-webmessenger/protocol-file'; import { FileDownloadItem } from '@ucap-webmessenger/api'; +import { ImageOnlyDataInfo } from '../models/image-only-data-info'; @Component({ selector: 'ucap-file-viewer', @@ -18,8 +19,15 @@ export class FileViewerComponent implements OnInit { @Input() fileDownloadUrl: string; + @Input() + imageOnly = false; + @Input() + imageOnlyData?: ImageOnlyDataInfo; + @Output() download = new EventEmitter(); + @Output() + saveAs = new EventEmitter(); @Output() closed = new EventEmitter(); @@ -31,6 +39,10 @@ export class FileViewerComponent implements OnInit { ngOnInit() {} detectFileViewerType(fileInfo: FileEventJson): FileViewerType { + if (!!this.imageOnly) { + return FileViewerType.Image; + } + switch (fileInfo.fileType) { case FileType.Image: return FileViewerType.Image; @@ -48,6 +60,9 @@ export class FileViewerComponent implements OnInit { onDownload(fileDownloadItem: FileDownloadItem): void { this.download.emit(fileDownloadItem); } + onSaveAs(fileDownloadItem: FileDownloadItem): void { + this.saveAs.emit(fileDownloadItem); + } onClosedViewer(): void { this.closed.emit(); diff --git a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/binary-viewer.component.html b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/binary-viewer.component.html index 1f7d40a4..cda93e72 100644 --- a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/binary-viewer.component.html +++ b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/binary-viewer.component.html @@ -44,6 +44,31 @@ /> +
    -
    +
    +
    diff --git a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/binary-viewer.component.scss b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/binary-viewer.component.scss index 3385ef7e..a5762dba 100644 --- a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/binary-viewer.component.scss +++ b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/binary-viewer.component.scss @@ -60,5 +60,10 @@ margin: 30px; color: #ffffff; } + .btn-group { + button { + margin: 0 5px; + } + } } } diff --git a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/binary-viewer.component.ts b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/binary-viewer.component.ts index ab2b655a..9d449a99 100644 --- a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/binary-viewer.component.ts +++ b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/binary-viewer.component.ts @@ -18,6 +18,8 @@ export class BinaryViewerComponent implements OnInit { @Output() download = new EventEmitter(); + @Output() + saveAs = new EventEmitter(); @Output() closed = new EventEmitter(); @@ -33,6 +35,11 @@ export class BinaryViewerComponent implements OnInit { this.download.emit(this.fileDownloadItem); } + onClickSaveAs(): void { + this.fileDownloadItem = new FileDownloadItem(); + this.saveAs.emit(this.fileDownloadItem); + } + onClickClose(): void { this.closed.emit(); } diff --git a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/image-viewer.component.html b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/image-viewer.component.html index 2bc39a23..edc4f2ae 100644 --- a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/image-viewer.component.html +++ b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/image-viewer.component.html @@ -17,10 +17,21 @@ - {{ fileInfo.fileName }} + + + + {{ imageOnlyData.fileName }} + + + {{ fileInfo.fileName }} + + + + +