Merge branch 'master' into new-org
This commit is contained in:
commit
62af089e67
BIN
config/build/win/bin/config.dat
Normal file
BIN
config/build/win/bin/config.dat
Normal file
Binary file not shown.
|
@ -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"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -27,7 +27,8 @@ export enum ElectronBrowserWindowChannel {
|
|||
Close = 'close',
|
||||
Closed = 'closed',
|
||||
ReadyToShow = 'ready-to-show',
|
||||
Focus = 'focus'
|
||||
Focus = 'focus',
|
||||
Blur = 'blur'
|
||||
}
|
||||
|
||||
export enum ElectronWebContentsChannel {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -11,6 +11,7 @@ export class Storage {
|
|||
|
||||
constructor() {
|
||||
this.store = new ElectronStore({
|
||||
cwd: path.join(__dirname, '..', '..', '..', '/bin/'),
|
||||
schema: {
|
||||
options: {
|
||||
type: 'object',
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -192,6 +192,14 @@ export const decodeUrlInfoDaesang: APIDecoder<DaesangUrlInfoResponse> = (
|
|||
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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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';
|
||||
|
|
|
@ -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: []
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
import { AppGlobalErrorhandler } from './global.errorhandler';
|
||||
import { ErrorHandler } from '@angular/core';
|
||||
|
||||
export const ERRORHANDLER = [
|
||||
{ provide: ErrorHandler, useClass: AppGlobalErrorhandler }
|
||||
];
|
|
@ -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(
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<ucap-file-viewer
|
||||
[fileInfo]="fileInfo"
|
||||
[fileDownloadUrl]="fileDownloadUrl"
|
||||
[imageOnly]="imageOnly"
|
||||
[imageOnlyData]="imageOnlyData"
|
||||
(download)="onDownload($event)"
|
||||
(saveAs)="onSaveAs($event)"
|
||||
(closed)="onClosedViewer()"
|
||||
></ucap-file-viewer>
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<div fxLayout="column" class="rightDrawer-userlist">
|
||||
<div class="search-list">
|
||||
<perfect-scrollbar class="room-user-scrollbar">
|
||||
<cdk-virtual-scroll-viewport itemSize="60" perfectScrollbar fxFlexFill>
|
||||
<ucap-profile-user-list-item
|
||||
*ngFor="let userInfo of userInfoList"
|
||||
*cdkVirtualFor="let userInfo of userInfoList"
|
||||
[userInfo]="userInfo"
|
||||
[presence]="getStatusBulkInfo(userInfo) | async"
|
||||
[sessionVerinfo]="sessionVerinfo"
|
||||
|
@ -10,11 +10,9 @@
|
|||
(openProfile)="onClickOpenProfile(userInfo.seq)"
|
||||
>
|
||||
</ucap-profile-user-list-item>
|
||||
</perfect-scrollbar>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
</div>
|
||||
<div
|
||||
class="btn-box"
|
||||
>
|
||||
<div class="btn-box">
|
||||
<button mat-flat-button class="mat-primary" (click)="onClickAddMember()">
|
||||
{{ 'chat.addMemberToRoom' | translate }}
|
||||
</button>
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
|
|
@ -255,15 +255,55 @@
|
|||
[selected]="getChipsRemoveYn(userInfo)"
|
||||
(removed)="onClickDeleteUser(userInfo)"
|
||||
>
|
||||
{{ userInfo.name }}
|
||||
{{ userInfo | ucapTranslate: 'name' }}
|
||||
<mat-icon matChipRemove *ngIf="getChipsRemoveYn(userInfo)"
|
||||
>clear</mat-icon
|
||||
>
|
||||
</mat-chip>
|
||||
</mat-chip-list>
|
||||
</div>
|
||||
<span>
|
||||
{{ selectedUserList.length }}
|
||||
{{ 'common.units.persons' | translate }}
|
||||
</span>
|
||||
<ng-container
|
||||
*ngIf="
|
||||
data.type === UserSelectDialogType.NewChat;
|
||||
then newchatcount;
|
||||
else defaultcount
|
||||
"
|
||||
></ng-container>
|
||||
<ng-template #newchatcount>
|
||||
<span
|
||||
[ngClass]="
|
||||
selectedUserList.length >=
|
||||
environment.productConfig.CommonSetting.maxChatRoomUser
|
||||
? 'text-warn-color'
|
||||
: ''
|
||||
"
|
||||
>
|
||||
{{ selectedUserList.length }} /
|
||||
{{ environment.productConfig.CommonSetting.maxChatRoomUser - 1 }}
|
||||
{{ 'common.units.persons' | translate }}
|
||||
</span>
|
||||
<span
|
||||
class="text-warn-color"
|
||||
style="float: right;"
|
||||
*ngIf="
|
||||
selectedUserList.length >=
|
||||
environment.productConfig.CommonSetting.maxChatRoomUser
|
||||
"
|
||||
>
|
||||
({{
|
||||
'chat.errors.maxCountOfRoomMemberWith'
|
||||
| translate
|
||||
: {
|
||||
maxCount:
|
||||
environment.productConfig.CommonSetting.maxChatRoomUser - 1
|
||||
}
|
||||
}})
|
||||
</span>
|
||||
</ng-template>
|
||||
<ng-template #defaultcount>
|
||||
<span>
|
||||
{{ selectedUserList.length }}
|
||||
{{ 'common.units.persons' | translate }}
|
||||
</span>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)"
|
||||
|
|
|
@ -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<AppUserInfo>(
|
||||
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({
|
||||
|
|
|
@ -124,13 +124,6 @@
|
|||
</div>
|
||||
</mat-card-content>
|
||||
<mat-card-actions class="button-form flex-row">
|
||||
<!-- <button
|
||||
mat-stroked-button
|
||||
(click)="onClickClearSettingAndLogout()"
|
||||
class="mat-primary"
|
||||
>
|
||||
Clear Setting & Logout
|
||||
</button> -->
|
||||
<button
|
||||
mat-stroked-button
|
||||
(click)="onClickChoice(false)"
|
||||
|
|
|
@ -1,20 +1,12 @@
|
|||
import { Component, OnInit, Inject, Renderer2, OnDestroy } from '@angular/core';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import {
|
||||
KEY_LOGIN_RES_INFO,
|
||||
KEY_VER_INFO,
|
||||
KEY_LOGIN_INFO,
|
||||
KEY_URL_INFO,
|
||||
KEY_AUTH_INFO,
|
||||
KEY_LOGOUT_INFO
|
||||
} from '@app/types';
|
||||
import { KEY_LOGIN_RES_INFO, KEY_VER_INFO } from '@app/types';
|
||||
import {
|
||||
SessionStorageService,
|
||||
LocalStorageService
|
||||
} from '@ucap-webmessenger/web-storage';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
import * as AuthenticationStore from '@app/store/account/authentication';
|
||||
|
||||
import clone from 'clone';
|
||||
|
||||
|
@ -210,17 +202,4 @@ export class MessengerSettingsDialogComponent implements OnInit, OnDestroy {
|
|||
|
||||
return modSettings;
|
||||
}
|
||||
|
||||
onClickClearSettingAndLogout() {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,6 +103,15 @@
|
|||
>
|
||||
<span class="weblink Personal-news">NEWS</span>
|
||||
</button>
|
||||
<button
|
||||
mat-icon-button
|
||||
*ngSwitchCase="WebLinkType.Erp"
|
||||
class="button"
|
||||
[matTooltip]="link.title"
|
||||
(click)="onClickWebLink(link)"
|
||||
>
|
||||
<span class="weblink erp">ERP</span>
|
||||
</button>
|
||||
<button
|
||||
mat-icon-button
|
||||
*ngSwitchCase="WebLinkType.Mail"
|
||||
|
@ -447,10 +456,13 @@
|
|||
</mat-menu>
|
||||
|
||||
<mat-menu #informationMenu="matMenu">
|
||||
<ng-template matMenuContent>
|
||||
<ng-template matMenuContent let-isShowClearBtn="false">
|
||||
<div class="version-info-container menu-item">
|
||||
<div class="version-info-now" (click)="$event.stopPropagation()">
|
||||
<span class="version-info-item">
|
||||
<span
|
||||
class="version-info-item"
|
||||
(auxclick)="isShowClearBtn = !isShowClearBtn"
|
||||
>
|
||||
{{ 'information.installedVersion' | translate }}:<span
|
||||
class="info-content"
|
||||
>{{ appVersion }}</span
|
||||
|
@ -491,5 +503,14 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="isShowClearBtn" class="version-info-container menu-item">
|
||||
<button
|
||||
mat-flat-button
|
||||
class="mat-primary"
|
||||
(click)="onClickClearSettingAndLogout($event)"
|
||||
>
|
||||
Clear setting & Logout
|
||||
</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
</mat-menu>
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,15 +53,16 @@
|
|||
</div>
|
||||
-->
|
||||
<ucap-account-login
|
||||
[companyList]="companyList$ | async"
|
||||
[companyList]="companyList"
|
||||
[curCompanyCode]="fixedCompany"
|
||||
[notiText]="fixedNotiBtnText"
|
||||
[loginBtnEnable]="loginBtnEnable"
|
||||
[loginBtnText]="loginBtnText"
|
||||
[companyCode]="appUserInfo?.companyCode"
|
||||
[loginId]="appUserInfo?.loginId"
|
||||
[loginPw]="loginPw"
|
||||
[rememberMe]="appUserInfo?.rememberMe"
|
||||
[autoLogin]="appUserInfo?.settings?.general?.autoLogin"
|
||||
[autoLogin]="autologin"
|
||||
[useRememberMe]="useRememberMe"
|
||||
[useAutoLogin]="useAutoLogin"
|
||||
(login)="onLogin($event)"
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
import { Component, OnInit, OnDestroy, Inject } from '@angular/core';
|
||||
import { Component, OnInit, OnDestroy, Inject, NgZone } from '@angular/core';
|
||||
|
||||
import { Store, select } from '@ngrx/store';
|
||||
|
||||
import { Company } from '@ucap-webmessenger/api-external';
|
||||
import {
|
||||
Company,
|
||||
ExternalApiService,
|
||||
CompanyListRequest
|
||||
} from '@ucap-webmessenger/api-external';
|
||||
import { ServerErrorCode, ProtocolService } from '@ucap-webmessenger/protocol';
|
||||
|
||||
import * as AppStore from '@app/store';
|
||||
import * as AuthenticationStore from '@app/store/account/authentication';
|
||||
import * as CompanyStore from '@app/store/setting/company';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { Observable, Subscription, of } from 'rxjs';
|
||||
import { map, take, catchError } from 'rxjs/operators';
|
||||
import {
|
||||
DialogService,
|
||||
AlertDialogComponent,
|
||||
|
@ -34,6 +38,7 @@ import { AppAuthenticationService } from '@app/services/authentication.service';
|
|||
import { logoutInitialize } from '@app/store/account/authentication';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native';
|
||||
import { StatusCode } from '@ucap-webmessenger/api';
|
||||
|
||||
@Component({
|
||||
selector: 'app-page-account-login',
|
||||
|
@ -44,7 +49,8 @@ export class LoginPageComponent implements OnInit, OnDestroy {
|
|||
fixedCompany: string;
|
||||
fixedNotiBtnText: string;
|
||||
|
||||
companyList$: Observable<Company[]>;
|
||||
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() {
|
||||
|
|
|
@ -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<AppUserInfo>(
|
||||
KEY_APP_USER_INFO,
|
||||
appUserInfo,
|
||||
environment.customConfig.appKey
|
||||
);
|
||||
this.localStorageService.encSet<AppUserInfo>(
|
||||
KEY_APP_USER_INFO,
|
||||
appUserInfo,
|
||||
environment.customConfig.appKey
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<RoomInfo>
|
||||
)
|
||||
),
|
||||
this.store.pipe(
|
||||
select(
|
||||
(state: any) =>
|
||||
state.messenger.sync.buddy2.entities as Dictionary<UserInfo>
|
||||
)
|
||||
),
|
||||
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$
|
||||
|
|
|
@ -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<AppUserInfo>(
|
||||
KEY_APP_USER_INFO,
|
||||
appUserInfo,
|
||||
environment.customConfig.appKey
|
||||
);
|
||||
|
||||
// Logout reason setting.
|
||||
this.sessionStorageService.set<LogoutInfo>(
|
||||
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<AppUserInfo>(
|
||||
KEY_APP_USER_INFO,
|
||||
appUserInfo,
|
||||
environment.customConfig.appKey
|
||||
);
|
||||
|
||||
// Logout reason setting.
|
||||
this.sessionStorageService.set<LogoutInfo>(
|
||||
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,
|
||||
|
|
|
@ -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));
|
||||
})
|
||||
|
|
|
@ -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<InfoRequest>()
|
||||
);
|
||||
|
|
|
@ -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<LoginInfo>(
|
||||
KEY_LOGIN_INFO
|
||||
);
|
||||
|
||||
if (!!roomInfo && roomInfo.roomSeq === action.noti.roomSeq) {
|
||||
const loginInfo = this.sessionStorageService.get<LoginInfo>(
|
||||
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<any>,
|
||||
private roomProtocolService: RoomProtocolService,
|
||||
private sessionStorageService: SessionStorageService,
|
||||
private translateService: TranslateService,
|
||||
private dialogService: DialogService,
|
||||
private logger: NGXLogger
|
||||
) {}
|
||||
|
|
|
@ -72,6 +72,7 @@ export const reducer = createReducer(
|
|||
|
||||
on(updateSuccess, (state, action) => {
|
||||
const curRoomInfo = state.roomInfo;
|
||||
|
||||
if (!curRoomInfo) {
|
||||
return { ...state };
|
||||
}
|
||||
|
|
|
@ -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(() =>
|
||||
|
|
|
@ -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]
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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. <br/> Check your password <br/> Please try again later."
|
||||
"loginFailOverTry": "Password error count exceeded. <br/> Check your password <br/> Please try again later.",
|
||||
"networkFailedAndExit": "Please exit the program due to a network problem. <br/> Please check the network and try again.",
|
||||
"networkFailedAndRetry": "Cannot run due to network problem. <br/> 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": {
|
||||
|
|
|
@ -45,7 +45,9 @@
|
|||
"failToChangePassword": "비밀번호 변경에 실패하였습니다.",
|
||||
"loginFailed": "로그인에 실패하였습니다.",
|
||||
"loginFailedIdPw": "아이디 또는 패스워드가<br/>일치하지 않습니다.",
|
||||
"loginFailOverTry": "비밀번호 오류 횟수 초과입니다.<br/>비밀번호를 확인하신 후<br/>잠시 후 다시 시작해 주세요."
|
||||
"loginFailOverTry": "비밀번호 오류 횟수 초과입니다.<br/>비밀번호를 확인하신 후<br/>잠시 후 다시 시작해 주세요.",
|
||||
"networkFailedAndExit": "네트워크 문제로 프로그램을 종료합니다.<br/>네트워크 확인후 다시 시도해 주세요.",
|
||||
"networkFailedAndRetry": "네트워크 문제로 실행할 수 없습니다.<br/>네트워크 확인후 다시 시도해 주세요."
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
|
|
BIN
projects/ucap-webmessenger-app/src/assets/images/no_image.png
Normal file
BIN
projects/ucap-webmessenger-app/src/assets/images/no_image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
|
@ -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;
|
||||
|
|
|
@ -28,5 +28,7 @@ export enum WebLinkType {
|
|||
/** 결재링크 URL */
|
||||
Payment = 'WebLinkPayment',
|
||||
/** 비밀번호변경 URL ; PC 메신저만 해당 비밀번호 만료시 */
|
||||
ChgPassword = 'WebLinkChgPassword'
|
||||
ChgPassword = 'WebLinkChgPassword',
|
||||
/** Erp */
|
||||
Erp = 'WebLinkERP'
|
||||
}
|
||||
|
|
|
@ -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
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<number> {
|
||||
return new Promise<number>((resolve, reject) => {
|
||||
|
|
|
@ -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<UpdateInfo> | null = null;
|
||||
private backgroundCheckForUpdates$: Observable<UpdateInfo> | 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<number> {
|
||||
return new Promise<number>((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;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<number>;
|
||||
abstract appExit(): void;
|
||||
abstract appLogging(error: any): void;
|
||||
|
||||
abstract idleStateChanged(): Observable<WindowIdle>;
|
||||
abstract idleStateStop(): void;
|
||||
|
|
|
@ -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<Login2Request> = (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<Login2Response> = (res: any) => {
|
||||
|
|
|
@ -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<UserSeqRequest> = (
|
|||
{ 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 }
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -98,9 +98,7 @@
|
|||
|
||||
<div class="message-main-container">
|
||||
<div class="message-main">
|
||||
<div class="chat-name">
|
||||
{{ senderName }}
|
||||
</div>
|
||||
<div class="chat-name">{{ senderName }} {{ senderGrade }}</div>
|
||||
|
||||
<div class="bubble">
|
||||
<ng-container
|
||||
|
|
|
@ -51,6 +51,9 @@ export class MessageBoxComponent implements OnInit, AfterViewInit {
|
|||
@Input()
|
||||
senderName: string;
|
||||
|
||||
@Input()
|
||||
senderGrade: string;
|
||||
|
||||
@Input()
|
||||
profileImageRoot: string;
|
||||
|
||||
|
|
|
@ -11,9 +11,12 @@
|
|||
<span class="bg-accent-dark">{{ 'chat.sentDate' | translate }}</span>
|
||||
{{ message.sentMessageJson.postDate | ucapDate: 'YYYY.MM.DD a hh:mm' }}
|
||||
</li>
|
||||
<li class="event-content">
|
||||
{{ message.sentMessageJson.content }}
|
||||
</li>
|
||||
<li
|
||||
class="event-content"
|
||||
[innerHTML]="
|
||||
message.sentMessageJson.content | ucapSafeHtml | linefeedtohtml | linky
|
||||
"
|
||||
></li>
|
||||
</ul>
|
||||
<!-- <div class="btn-box">
|
||||
<button mat-button (click)="onClickSave()">상세보기</button>
|
||||
|
|
|
@ -1,16 +1,45 @@
|
|||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
Input,
|
||||
AfterViewInit,
|
||||
ElementRef,
|
||||
Inject
|
||||
} from '@angular/core';
|
||||
import { Info, AllimEventJson } from '@ucap-webmessenger/protocol-event';
|
||||
import { UCAP_NATIVE_SERVICE, NativeService } from '@ucap-webmessenger/native';
|
||||
|
||||
@Component({
|
||||
selector: 'ucap-chat-message-box-allim',
|
||||
templateUrl: './allim.component.html',
|
||||
styleUrls: ['./allim.component.scss']
|
||||
})
|
||||
export class AllimComponent implements OnInit {
|
||||
export class AllimComponent implements OnInit, AfterViewInit {
|
||||
@Input()
|
||||
message: Info<AllimEventJson>;
|
||||
|
||||
constructor() {}
|
||||
constructor(
|
||||
private elementRef: ElementRef,
|
||||
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService
|
||||
) {}
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
if (
|
||||
!!this.elementRef.nativeElement &&
|
||||
!!this.elementRef.nativeElement.querySelector('a')
|
||||
) {
|
||||
const elements = this.elementRef.nativeElement.querySelectorAll('a');
|
||||
elements.forEach(element => {
|
||||
element.addEventListener('click', this.onClickEvent.bind(this));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onClickEvent(event: MouseEvent) {
|
||||
this.nativeService.openDefaultBrowser(
|
||||
(event.target as HTMLAnchorElement).text
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
"
|
||||
[dateChanged]="getDateSplitter(message)"
|
||||
[senderName]="getUserName(message.senderSeq)"
|
||||
[senderGrade]="getUserGrade(message.senderSeq)"
|
||||
[profileImageRoot]="profileImageRoot"
|
||||
[profileImage]="getUserProfile(message.senderSeq)"
|
||||
[roomInfo]="roomInfo"
|
||||
|
|
|
@ -322,6 +322,19 @@ export class MessagesComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
return '(알수없는 사용자)';
|
||||
}
|
||||
getUserGrade(seq: number): string {
|
||||
if (!this.userInfos) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const userInfo: UserInfo[] = this.userInfos.filter(
|
||||
user => user.seq === seq
|
||||
);
|
||||
if (!!userInfo && userInfo.length > 0) {
|
||||
return userInfo[0].grade;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
getUserProfile(seq: number): string {
|
||||
if (!this.userInfos) {
|
||||
return '';
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
<mat-card class="example-card profile mat-elevation-z">
|
||||
<mat-card-header>
|
||||
<div class="profile-img">
|
||||
<div class="profile-img-mask">
|
||||
<div
|
||||
class="profile-img-mask"
|
||||
(click)="onClickProfileImageView()"
|
||||
style="cursor: pointer;"
|
||||
>
|
||||
<img
|
||||
ucapImage
|
||||
[base]="profileImageRoot"
|
||||
|
@ -24,7 +28,26 @@
|
|||
>
|
||||
<i class="mid mdi-camera"></i>
|
||||
</button>
|
||||
|
||||
<!-- <button
|
||||
mat-mini-fab
|
||||
class="mat-elevation-z6 icon-button btn-elephant"
|
||||
*ngIf="!!enableElephantButton"
|
||||
matTooltip="칭찬 코끼리 보내기"
|
||||
(click)="onClickSendElephant()"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 30 30"
|
||||
stroke-width="1.5"
|
||||
stroke="#357abc"
|
||||
fill="#98d7e2"
|
||||
>
|
||||
<path
|
||||
d="M29,17.3a21.32,21.32,0,0,0-.63-4.39A10.59,10.59,0,0,0,28,11.85c0-.14-.1-.27-.15-.41l-.16-.39a13,13,0,0,0-.81-1.52,11.29,11.29,0,0,0-1.59-2c-.2-.2-.4-.4-.62-.58s-.43-.36-.66-.53a10.83,10.83,0,0,0-1.82-1.09l-.39-.18c-.26-.11-.53-.21-.81-.31A11.06,11.06,0,0,0,6.62,13.71l0,.09v0s0,.07,0,.1v0a.52.52,0,0,0,0,.11h0a.43.43,0,0,1,0,.11h0a.32.32,0,0,1,0,.09v0s0,.05,0,.08h0a.1.1,0,0,1,0,0h0l-.05,0h0l0,0a1.42,1.42,0,0,1-.56-.62,1.84,1.84,0,0,1-.13-1.17A4,4,0,0,1,6,11.55L2.14,9.83h0a6.75,6.75,0,0,0-.84,1.78c-.05.14-.09.29-.13.45A6.18,6.18,0,0,0,1,13.31a5,5,0,0,0,.78,2.84c.08.13.17.26.26.38l.06.08c.09.11.17.22.27.33l0,0,.26.28.07.06.28.25,0,0a2.48,2.48,0,0,0,.26.19l.06,0,.3.19.05,0,.27.14.06,0,.29.13.06,0,.25.09,0,0,.29.09h.06l.24.06h0l.26.05h0l.23,0h0l.23,0h.94l.13.43c.07.19.15.38.23.57s.17.38.27.56a11.26,11.26,0,0,0,1,1.51c.13.16.25.32.39.48a5.34,5.34,0,0,0,.39.44A11.78,11.78,0,0,0,10.9,24.1c.27.2.54.39.83.57a11.72,11.72,0,0,0,1.78,1l.16.06h.19l.13,0h0l.1-.06h0a.39.39,0,0,0,.08-.1h0l.05-.11v0l0-.1h0s0-.06,0-.09v-.08h0v-1a3.27,3.27,0,0,1,.41-1.59,3.36,3.36,0,0,1,2.94-1.76h0a3.31,3.31,0,0,1,1,.16l.3.11a3.15,3.15,0,0,1,.57.31,2.48,2.48,0,0,1,.26.19A3.34,3.34,0,0,1,21,24.08v.79h0v.29l0,0v0a.43.43,0,0,1,0,0v0a.25.25,0,0,0,0,.07h0a.21.21,0,0,0,0,.06v0l0,0,0,0,0,0,0,0,0,0h.18l.09,0a11,11,0,0,0,1.65-.82c.27-.16.53-.34.78-.52a10.65,10.65,0,0,0,1.07-.93c.11-.12.22-.23.32-.35a7.25,7.25,0,0,0,.58-.76l.24-.4a6.43,6.43,0,0,0,.41-.88l.15-.46.15.64c0,.09,0,.18.05.27v.09l0,.18v.1l0,.19V22l0,.2v.05l0,.2v.07c0,.05,0,.09,0,.14v.07a.69.69,0,0,1,0,.13V23a.94.94,0,0,1,0,.16v0a.56.56,0,0,0,0,.12v0l0,.08v0l0,.06,0,0a.1.1,0,0,0,.05,0h.07l.05,0s0,0,0,0a.12.12,0,0,0,.05,0l0,0a.52.52,0,0,0,.08-.11A9.41,9.41,0,0,0,29,18.52C29,18.12,29,17.71,29,17.3ZM10.55,11.59a.86.86,0,1,1,0-1.71.85.85,0,0,1,.85.85A.86.86,0,0,1,10.55,11.59Zm9.79,4.79A5.27,5.27,0,0,1,17,18.07a2.67,2.67,0,0,1-2-.95,7.21,7.21,0,0,1-1.69-4.84c.07-1.18.76-4,5.58-4.94a4.18,4.18,0,0,1,.61,0v0a3.43,3.43,0,0,1,3.11,2.29C23.5,11.73,22.63,14.27,20.34,16.38Z"
|
||||
transform="translate(-1 -4.29)"
|
||||
/>
|
||||
</svg>
|
||||
</button> -->
|
||||
<span
|
||||
*ngIf="getWorkstatus(userInfo).length > 0"
|
||||
class="work-status"
|
||||
|
@ -180,6 +203,12 @@
|
|||
<dt class="division">{{ 'profile.fieldCompany' | translate }}</dt>
|
||||
<dd>{{ userInfo.companyName | ucapStringEmptycheck }}</dd>
|
||||
</li>
|
||||
<li class="employeeNum">
|
||||
<dt class="division">
|
||||
{{ 'profile.fieldEmployeeNumber' | translate }}
|
||||
</dt>
|
||||
<dd>{{ userInfo.employeeNum | slice: 2 }}</dd>
|
||||
</li>
|
||||
<li class="deptName">
|
||||
<dt class="division">{{ 'search.fieldDeptartment' | translate }}</dt>
|
||||
<dd>{{ userInfo | ucapTranslate: 'deptName' }}</dd>
|
||||
|
@ -379,6 +408,31 @@
|
|||
}}</span>
|
||||
</div>
|
||||
|
||||
<!-- <div class="button-text-item" *ngIf="!!enableElephantButton">
|
||||
<button
|
||||
mat-mini-fab
|
||||
class="mat-elevation-z bg-accent-darkest"
|
||||
*ngIf="!isMe"
|
||||
matTooltip="칭찬 코끼리"
|
||||
matTooltipPosition="above"
|
||||
(click)="onClickSendElephant()"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 30 30"
|
||||
stroke-width="1.5"
|
||||
stroke="#357abc"
|
||||
fill="#98d7e2"
|
||||
>
|
||||
<path
|
||||
d="M29,17.3a21.32,21.32,0,0,0-.63-4.39A10.59,10.59,0,0,0,28,11.85c0-.14-.1-.27-.15-.41l-.16-.39a13,13,0,0,0-.81-1.52,11.29,11.29,0,0,0-1.59-2c-.2-.2-.4-.4-.62-.58s-.43-.36-.66-.53a10.83,10.83,0,0,0-1.82-1.09l-.39-.18c-.26-.11-.53-.21-.81-.31A11.06,11.06,0,0,0,6.62,13.71l0,.09v0s0,.07,0,.1v0a.52.52,0,0,0,0,.11h0a.43.43,0,0,1,0,.11h0a.32.32,0,0,1,0,.09v0s0,.05,0,.08h0a.1.1,0,0,1,0,0h0l-.05,0h0l0,0a1.42,1.42,0,0,1-.56-.62,1.84,1.84,0,0,1-.13-1.17A4,4,0,0,1,6,11.55L2.14,9.83h0a6.75,6.75,0,0,0-.84,1.78c-.05.14-.09.29-.13.45A6.18,6.18,0,0,0,1,13.31a5,5,0,0,0,.78,2.84c.08.13.17.26.26.38l.06.08c.09.11.17.22.27.33l0,0,.26.28.07.06.28.25,0,0a2.48,2.48,0,0,0,.26.19l.06,0,.3.19.05,0,.27.14.06,0,.29.13.06,0,.25.09,0,0,.29.09h.06l.24.06h0l.26.05h0l.23,0h0l.23,0h.94l.13.43c.07.19.15.38.23.57s.17.38.27.56a11.26,11.26,0,0,0,1,1.51c.13.16.25.32.39.48a5.34,5.34,0,0,0,.39.44A11.78,11.78,0,0,0,10.9,24.1c.27.2.54.39.83.57a11.72,11.72,0,0,0,1.78,1l.16.06h.19l.13,0h0l.1-.06h0a.39.39,0,0,0,.08-.1h0l.05-.11v0l0-.1h0s0-.06,0-.09v-.08h0v-1a3.27,3.27,0,0,1,.41-1.59,3.36,3.36,0,0,1,2.94-1.76h0a3.31,3.31,0,0,1,1,.16l.3.11a3.15,3.15,0,0,1,.57.31,2.48,2.48,0,0,1,.26.19A3.34,3.34,0,0,1,21,24.08v.79h0v.29l0,0v0a.43.43,0,0,1,0,0v0a.25.25,0,0,0,0,.07h0a.21.21,0,0,0,0,.06v0l0,0,0,0,0,0,0,0,0,0h.18l.09,0a11,11,0,0,0,1.65-.82c.27-.16.53-.34.78-.52a10.65,10.65,0,0,0,1.07-.93c.11-.12.22-.23.32-.35a7.25,7.25,0,0,0,.58-.76l.24-.4a6.43,6.43,0,0,0,.41-.88l.15-.46.15.64c0,.09,0,.18.05.27v.09l0,.18v.1l0,.19V22l0,.2v.05l0,.2v.07c0,.05,0,.09,0,.14v.07a.69.69,0,0,1,0,.13V23a.94.94,0,0,1,0,.16v0a.56.56,0,0,0,0,.12v0l0,.08v0l0,.06,0,0a.1.1,0,0,0,.05,0h.07l.05,0s0,0,0,0a.12.12,0,0,0,.05,0l0,0a.52.52,0,0,0,.08-.11A9.41,9.41,0,0,0,29,18.52C29,18.12,29,17.71,29,17.3ZM10.55,11.59a.86.86,0,1,1,0-1.71.85.85,0,0,1,.85.85A.86.86,0,0,1,10.55,11.59Zm9.79,4.79A5.27,5.27,0,0,1,17,18.07a2.67,2.67,0,0,1-2-.95,7.21,7.21,0,0,1-1.69-4.84c.07-1.18.76-4,5.58-4.94a4.18,4.18,0,0,1,.61,0v0a3.43,3.43,0,0,1,3.11,2.29C23.5,11.73,22.63,14.27,20.34,16.38Z"
|
||||
transform="translate(-1 -4.29)"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<span class="button-text">칭찬 코끼리</span>
|
||||
</div> -->
|
||||
|
||||
<div class="button-text-item" *ngIf="!isMe">
|
||||
<button
|
||||
mat-mini-fab
|
||||
|
|
|
@ -74,6 +74,7 @@ $login-max-height: 800px;
|
|||
|
||||
.work-status {
|
||||
display: inline-flex;
|
||||
margin-left: 20px;
|
||||
height: 24px;
|
||||
border: 1px solid #ffffff;
|
||||
padding: 4px 14px;
|
||||
|
@ -227,6 +228,26 @@ $login-max-height: 800px;
|
|||
}
|
||||
}
|
||||
|
||||
.btn-elephant {
|
||||
position: absolute;
|
||||
background-color: #ffffff !important;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
left: 60px;
|
||||
top: 46px;
|
||||
svg {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
&:hover {
|
||||
background-color: #98d7e2 !important;
|
||||
opacity: 1;
|
||||
svg {
|
||||
fill: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.userInfo-call {
|
||||
position: relative;
|
||||
display: flex;
|
||||
|
|
|
@ -44,7 +44,11 @@ export class ProfileComponent implements OnInit {
|
|||
useBuddyToggleButton: boolean;
|
||||
@Input()
|
||||
authInfo: AuthResponse;
|
||||
@Input()
|
||||
enableElephantButton: boolean; // 칭찬코끼리 버튼 활성화 대상 여부.
|
||||
|
||||
@Output()
|
||||
profileImageView = new EventEmitter<void>();
|
||||
@Output()
|
||||
openChat = new EventEmitter<UserInfoSS>();
|
||||
@Output()
|
||||
|
@ -70,6 +74,8 @@ export class ProfileComponent implements OnInit {
|
|||
@Output()
|
||||
updateIntro = new EventEmitter<string>();
|
||||
@Output()
|
||||
sendElephant = new EventEmitter();
|
||||
@Output()
|
||||
close = new EventEmitter();
|
||||
|
||||
@ViewChild('profileImageFileInput', { static: false })
|
||||
|
@ -86,6 +92,14 @@ export class ProfileComponent implements OnInit {
|
|||
|
||||
ngOnInit() {}
|
||||
|
||||
onClickProfileImageView() {
|
||||
this.profileImageView.emit();
|
||||
}
|
||||
|
||||
onClickSendElephant() {
|
||||
this.sendElephant.emit();
|
||||
}
|
||||
|
||||
onClickOpenChat() {
|
||||
this.openChat.emit(this.userInfo);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,10 @@
|
|||
*ngSwitchCase="FileViewerType.Image"
|
||||
[fileInfo]="fileInfo"
|
||||
[fileDownloadUrl]="fileDownloadUrl"
|
||||
[imageOnly]="imageOnly"
|
||||
[imageOnlyData]="imageOnlyData"
|
||||
(download)="onDownload($event)"
|
||||
(saveAs)="onSaveAs($event)"
|
||||
(closed)="onClosedViewer()"
|
||||
></ucap-image-viewer>
|
||||
<ucap-sound-viewer
|
||||
|
@ -28,6 +31,7 @@
|
|||
[fileInfo]="fileInfo"
|
||||
[fileDownloadUrl]="fileDownloadUrl"
|
||||
(download)="onDownload($event)"
|
||||
(saveAs)="onSaveAs($event)"
|
||||
(closed)="onClosedViewer()"
|
||||
></ucap-video-viewer>
|
||||
<ucap-binary-viewer
|
||||
|
@ -35,6 +39,7 @@
|
|||
[fileInfo]="fileInfo"
|
||||
[fileDownloadUrl]="fileDownloadUrl"
|
||||
(download)="onDownload($event)"
|
||||
(saveAs)="onSaveAs($event)"
|
||||
(closed)="onClosedViewer()"
|
||||
></ucap-binary-viewer>
|
||||
</div>
|
||||
|
|
|
@ -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<FileDownloadItem>();
|
||||
@Output()
|
||||
saveAs = new EventEmitter<FileDownloadItem>();
|
||||
|
||||
@Output()
|
||||
closed = new EventEmitter<void>();
|
||||
|
@ -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();
|
||||
|
|
|
@ -44,6 +44,31 @@
|
|||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
mat-icon-button
|
||||
class="ucap-image-viewer-action"
|
||||
matTooltip="{{ 'common.file.saveAs' | translate }}"
|
||||
matTooltipPosition="below"
|
||||
aria-label=""
|
||||
(click)="onClickSaveAs()"
|
||||
>
|
||||
<!--<mat-icon>get_app</mat-icon>-->
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="21"
|
||||
height="21"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="butt"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path
|
||||
d="M3 15v4c0 1.1.9 2 2 2h14a2 2 0 0 0 2-2v-4M17 9l-5 5-5-5M12 12.8V2.5"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<span class="stroke-bar"></span>
|
||||
<button
|
||||
mat-icon-button
|
||||
|
@ -94,7 +119,7 @@
|
|||
<div class="guide-msg">
|
||||
{{ 'common.file.errors.noPreview' | translate }}
|
||||
</div>
|
||||
<div>
|
||||
<div class="btn-group">
|
||||
<button
|
||||
colori
|
||||
mat-raised-button
|
||||
|
@ -103,6 +128,14 @@
|
|||
>
|
||||
{{ 'common.file.download' | translate }}
|
||||
</button>
|
||||
<button
|
||||
colori
|
||||
mat-raised-button
|
||||
aria-label=""
|
||||
(click)="onClickSaveAs()"
|
||||
>
|
||||
{{ 'common.file.saveAs' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -60,5 +60,10 @@
|
|||
margin: 30px;
|
||||
color: #ffffff;
|
||||
}
|
||||
.btn-group {
|
||||
button {
|
||||
margin: 0 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ export class BinaryViewerComponent implements OnInit {
|
|||
|
||||
@Output()
|
||||
download = new EventEmitter<FileDownloadItem>();
|
||||
@Output()
|
||||
saveAs = new EventEmitter<FileDownloadItem>();
|
||||
|
||||
@Output()
|
||||
closed = new EventEmitter<void>();
|
||||
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -17,10 +17,21 @@
|
|||
<circle cx="8.5" cy="8.5" r="1.5" />
|
||||
<path d="M20.4 14.5L16 10 4 20" />
|
||||
</svg>
|
||||
<span class="ucap-image-viewer-title">{{ fileInfo.fileName }}</span>
|
||||
<span class="ucap-image-viewer-title">
|
||||
<ng-container
|
||||
*ngIf="imageOnly; then imageOnlyName; else defaultName"
|
||||
></ng-container>
|
||||
<ng-template #imageOnlyName>
|
||||
{{ imageOnlyData.fileName }}
|
||||
</ng-template>
|
||||
<ng-template #defaultName>
|
||||
{{ fileInfo.fileName }}
|
||||
</ng-template>
|
||||
</span>
|
||||
<span class="ucap-image-viewer-spacer"></span>
|
||||
|
||||
<button
|
||||
*ngIf="!imageOnly"
|
||||
mat-icon-button
|
||||
class="ucap-image-viewer-action"
|
||||
matTooltip="{{ 'common.messages.zoomReset' | translate }}"
|
||||
|
@ -47,6 +58,7 @@
|
|||
</svg>
|
||||
</button>
|
||||
<button
|
||||
*ngIf="!imageOnly"
|
||||
mat-icon-button
|
||||
class="ucap-image-viewer-action"
|
||||
matTooltip="{{ 'common.messages.zoomOut' | translate }}"
|
||||
|
@ -72,6 +84,7 @@
|
|||
</svg>
|
||||
</button>
|
||||
<button
|
||||
*ngIf="!imageOnly"
|
||||
mat-icon-button
|
||||
class="ucap-image-viewer-action"
|
||||
matTooltip="{{ 'common.messages.zoomIn' | translate }}"
|
||||
|
@ -98,6 +111,7 @@
|
|||
</svg>
|
||||
</button>
|
||||
<button
|
||||
*ngIf="!imageOnly"
|
||||
mat-icon-button
|
||||
class="ucap-image-viewer-action"
|
||||
matTooltip="{{ 'common.file.download' | translate }}"
|
||||
|
@ -122,6 +136,32 @@
|
|||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
*ngIf="!imageOnly"
|
||||
mat-icon-button
|
||||
class="ucap-image-viewer-action"
|
||||
matTooltip="{{ 'common.file.saveAs' | translate }}"
|
||||
matTooltipPosition="below"
|
||||
aria-label=""
|
||||
(click)="onClickSaveAs()"
|
||||
>
|
||||
<!--<mat-icon>get_app</mat-icon>-->
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="21"
|
||||
height="21"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="butt"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path
|
||||
d="M3 15v4c0 1.1.9 2 2 2h14a2 2 0 0 0 2-2v-4M17 9l-5 5-5-5M12 12.8V2.5"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<span class="stroke-bar"></span>
|
||||
<button
|
||||
mat-icon-button
|
||||
|
@ -166,14 +206,29 @@
|
|||
fxFlexFill
|
||||
fxLayoutAlign="center center"
|
||||
>
|
||||
<img
|
||||
#downloadImage
|
||||
*ngIf="fileDownloadUrl"
|
||||
[src]="fileDownloadUrl"
|
||||
[style.width]="'auto'"
|
||||
[style.height]="imageHeight + 'px'"
|
||||
(load)="onLoadFileDownloadUrl(downloadImage)"
|
||||
/>
|
||||
<ng-container
|
||||
*ngIf="imageOnly; then imageOnlyImg; else defaultImg"
|
||||
></ng-container>
|
||||
<ng-template #imageOnlyImg>
|
||||
<img
|
||||
ucapImage
|
||||
[base]="imageOnlyData.imageRootUrl"
|
||||
[path]="imageOnlyData.imageUrl"
|
||||
[default]="imageOnlyData.defaultImage"
|
||||
style="cursor: pointer; width: auto;"
|
||||
(click)="onClickClose()"
|
||||
/>
|
||||
</ng-template>
|
||||
<ng-template #defaultImg>
|
||||
<img
|
||||
#downloadImage
|
||||
*ngIf="fileDownloadUrl"
|
||||
[src]="fileDownloadUrl"
|
||||
[style.width]="'auto'"
|
||||
[style.height]="imageHeight + 'px'"
|
||||
(load)="onLoadFileDownloadUrl(downloadImage)"
|
||||
/>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
import { ucapAnimations } from '../../animations';
|
||||
import { FileEventJson } from '@ucap-webmessenger/protocol-event';
|
||||
import { FileDownloadItem } from '@ucap-webmessenger/api';
|
||||
import { ImageOnlyDataInfo } from '../../models/image-only-data-info';
|
||||
|
||||
@Component({
|
||||
selector: 'ucap-image-viewer',
|
||||
|
@ -28,11 +29,18 @@ export class ImageViewerComponent implements OnInit {
|
|||
@Input()
|
||||
fileDownloadUrl: string;
|
||||
|
||||
@Input()
|
||||
imageOnly = false;
|
||||
@Input()
|
||||
imageOnlyData?: ImageOnlyDataInfo;
|
||||
|
||||
@Output()
|
||||
closed = new EventEmitter<void>();
|
||||
|
||||
@Output()
|
||||
download = new EventEmitter<FileDownloadItem>();
|
||||
@Output()
|
||||
saveAs = new EventEmitter<FileDownloadItem>();
|
||||
|
||||
@ViewChild('imageContainer', { static: false })
|
||||
imageContainer: ElementRef<HTMLElement>;
|
||||
|
@ -67,6 +75,11 @@ export class ImageViewerComponent implements OnInit {
|
|||
this.download.emit(this.fileDownloadItem);
|
||||
}
|
||||
|
||||
onClickSaveAs(): void {
|
||||
this.fileDownloadItem = new FileDownloadItem();
|
||||
this.saveAs.emit(this.fileDownloadItem);
|
||||
}
|
||||
|
||||
onClickClose(): void {
|
||||
this.closed.emit();
|
||||
}
|
||||
|
|
|
@ -28,6 +28,31 @@
|
|||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
mat-icon-button
|
||||
class="ucap-image-viewer-action"
|
||||
matTooltip="{{ 'common.file.saveAs' | translate }}"
|
||||
matTooltipPosition="below"
|
||||
aria-label=""
|
||||
(click)="onClickSaveAs()"
|
||||
>
|
||||
<!--<mat-icon>get_app</mat-icon>-->
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="21"
|
||||
height="21"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="butt"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path
|
||||
d="M3 15v4c0 1.1.9 2 2 2h14a2 2 0 0 0 2-2v-4M17 9l-5 5-5-5M12 12.8V2.5"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<span class="stroke-bar"></span>
|
||||
<button
|
||||
mat-icon-button
|
||||
|
|
|
@ -27,6 +27,8 @@ export class SoundViewerComponent implements OnInit {
|
|||
|
||||
@Output()
|
||||
download = new EventEmitter<FileDownloadItem>();
|
||||
@Output()
|
||||
saveAs = new EventEmitter<FileDownloadItem>();
|
||||
|
||||
@Output()
|
||||
closed = new EventEmitter<void>();
|
||||
|
@ -95,6 +97,11 @@ export class SoundViewerComponent implements OnInit {
|
|||
this.download.emit(this.fileDownloadItem);
|
||||
}
|
||||
|
||||
onClickSaveAs(): void {
|
||||
this.fileDownloadItem = new FileDownloadItem();
|
||||
this.saveAs.emit(this.fileDownloadItem);
|
||||
}
|
||||
|
||||
onClickClose(): void {
|
||||
this.closed.emit();
|
||||
}
|
||||
|
|
|
@ -49,6 +49,31 @@
|
|||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
mat-icon-button
|
||||
class="ucap-image-viewer-action"
|
||||
matTooltip="{{ 'common.file.saveAs' | translate }}"
|
||||
matTooltipPosition="below"
|
||||
aria-label=""
|
||||
(click)="onClickSaveAs()"
|
||||
>
|
||||
<!--<mat-icon>get_app</mat-icon>-->
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="21"
|
||||
height="21"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="butt"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path
|
||||
d="M3 15v4c0 1.1.9 2 2 2h14a2 2 0 0 0 2-2v-4M17 9l-5 5-5-5M12 12.8V2.5"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<span class="stroke-bar"></span>
|
||||
<button
|
||||
mat-icon-button
|
||||
|
|
|
@ -30,6 +30,8 @@ export class VideoViewerComponent implements OnInit {
|
|||
|
||||
@Output()
|
||||
download = new EventEmitter<FileDownloadItem>();
|
||||
@Output()
|
||||
saveAs = new EventEmitter<FileDownloadItem>();
|
||||
|
||||
@Output()
|
||||
closed = new EventEmitter<void>();
|
||||
|
@ -116,6 +118,11 @@ export class VideoViewerComponent implements OnInit {
|
|||
this.download.emit(this.fileDownloadItem);
|
||||
}
|
||||
|
||||
onClickSaveAs(): void {
|
||||
this.fileDownloadItem = new FileDownloadItem();
|
||||
this.saveAs.emit(this.fileDownloadItem);
|
||||
}
|
||||
|
||||
onClickClose(): void {
|
||||
this.closed.emit();
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@
|
|||
</th>
|
||||
<td mat-cell *matCellDef="let element" class="lineNumber">
|
||||
<div class="lineNumber">
|
||||
{{ element.lineNumber }}
|
||||
{{ element.lineNumber | ucapStringFormatterPhone }}
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
@ -140,7 +140,7 @@
|
|||
</th>
|
||||
<td mat-cell *matCellDef="let element" class="hpNumber">
|
||||
<div class="hpNumber">
|
||||
{{ element.hpNumber }}
|
||||
{{ element.hpNumber | ucapStringFormatterPhone }}
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
export interface ImageOnlyDataInfo {
|
||||
imageUrl: string;
|
||||
imageRootUrl: string;
|
||||
defaultImage: string;
|
||||
fileName: string;
|
||||
}
|
|
@ -30,6 +30,8 @@ export * from './lib/directives/click-outside.directive';
|
|||
export * from './lib/directives/file-upload-for.directive';
|
||||
export * from './lib/directives/image.directive';
|
||||
|
||||
export * from './lib/models/image-only-data-info';
|
||||
|
||||
export * from './lib/services/bottom-sheet.service';
|
||||
export * from './lib/services/clipboard.service';
|
||||
export * from './lib/services/dialog.service';
|
||||
|
|
Loading…
Reference in New Issue
Block a user