Merge branch 'master' of http://10.81.13.221:6990/Web/next-ucap-messenger
This commit is contained in:
commit
e6cdde6c31
|
@ -2,35 +2,67 @@ import { DeviceType } from '@ucap-webmessenger/core';
|
||||||
import {
|
import {
|
||||||
APIRequest,
|
APIRequest,
|
||||||
APIResponse,
|
APIResponse,
|
||||||
APIEncoder,
|
|
||||||
APIDecoder,
|
APIDecoder,
|
||||||
ParameterUtil
|
ParameterUtil,
|
||||||
|
APIFormDataEncoder,
|
||||||
|
JsonAnalization,
|
||||||
|
StatusCode
|
||||||
} from '@ucap-webmessenger/api';
|
} from '@ucap-webmessenger/api';
|
||||||
|
import { FileUploadItem } from '../models/file-upload-item';
|
||||||
|
|
||||||
export interface FileProfileSaveRequest extends APIRequest {
|
export interface FileProfileSaveRequest extends APIRequest {
|
||||||
userSeq: number;
|
userSeq: number;
|
||||||
deviceType: DeviceType;
|
deviceType: DeviceType;
|
||||||
token: string;
|
token: string;
|
||||||
file?: File;
|
file?: File;
|
||||||
|
fileUploadItem: FileUploadItem;
|
||||||
intro?: string;
|
intro?: string;
|
||||||
initProfileImage?: boolean;
|
initProfileImage?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FileProfileSaveResponse extends APIResponse {
|
export interface FileProfileSaveResponse extends APIResponse {
|
||||||
ProfileURL?: string;
|
profileURL?: string;
|
||||||
ProfileSubDir?: string;
|
profileSubDir?: string;
|
||||||
|
returnJson?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileProfileEncodeMap = {};
|
const fileProfileEncodeMap = {
|
||||||
|
userSeq: 'p_user_seq',
|
||||||
|
deviceType: 'p_device_type',
|
||||||
|
token: 'p_token',
|
||||||
|
file: 'file',
|
||||||
|
intro: 'p_intro',
|
||||||
|
initProfileImage: 'p_init_profile_img_yn'
|
||||||
|
};
|
||||||
|
|
||||||
export const encodeFileProfileSave: APIEncoder<FileProfileSaveRequest> = (
|
export const encodeFileProfileSave: APIFormDataEncoder<FileProfileSaveRequest> = (
|
||||||
req: FileProfileSaveRequest
|
req: FileProfileSaveRequest
|
||||||
) => {
|
) => {
|
||||||
return ParameterUtil.encode(fileProfileEncodeMap, req);
|
const extraParams: any = {};
|
||||||
|
|
||||||
|
extraParams.userSeq = String(req.userSeq);
|
||||||
|
if (!!req.initProfileImage) {
|
||||||
|
extraParams.initProfileImage = req.initProfileImage ? 'Y' : 'N';
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParameterUtil.encodeFormData(fileProfileEncodeMap, req, extraParams);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const decodeFileProfileSave: APIDecoder<FileProfileSaveResponse> = (
|
export const decodeFileProfileSave: APIDecoder<FileProfileSaveResponse> = (
|
||||||
res: any
|
res: any
|
||||||
) => {
|
) => {
|
||||||
return {} as FileProfileSaveResponse;
|
try {
|
||||||
|
const json = JsonAnalization.receiveAnalization(res);
|
||||||
|
return {
|
||||||
|
statusCode: json.StatusCode,
|
||||||
|
profileURL: json.ProfileURL,
|
||||||
|
profileSubDir: json.ProfileSubDir,
|
||||||
|
returnJson: res
|
||||||
|
} as FileProfileSaveResponse;
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
statusCode: StatusCode.Fail,
|
||||||
|
errorMessage: e
|
||||||
|
} as FileProfileSaveResponse;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -94,15 +94,29 @@ export class CommonApiService {
|
||||||
req: FileProfileSaveRequest,
|
req: FileProfileSaveRequest,
|
||||||
fileProfileSaveUrl?: string
|
fileProfileSaveUrl?: string
|
||||||
): Observable<FileProfileSaveResponse> {
|
): Observable<FileProfileSaveResponse> {
|
||||||
return this.httpClient
|
const httpReq = new HttpRequest(
|
||||||
.post<any>(
|
'POST',
|
||||||
!!fileProfileSaveUrl ? fileProfileSaveUrl : this.urls.fileProfileSave,
|
!!fileProfileSaveUrl ? fileProfileSaveUrl : this.urls.fileProfileSave,
|
||||||
{},
|
encodeFileProfileSave(req),
|
||||||
{
|
{ reportProgress: true, responseType: 'text' as 'json' }
|
||||||
params: encodeFileProfileSave(req)
|
);
|
||||||
|
|
||||||
|
const progress = req.fileUploadItem.uploadStart();
|
||||||
|
|
||||||
|
return this.httpClient.request(httpReq).pipe(
|
||||||
|
filter(event => {
|
||||||
|
if (event instanceof HttpResponse) {
|
||||||
|
return true;
|
||||||
|
} else if (HttpEventType.UploadProgress === event.type) {
|
||||||
|
progress.next(Math.round((100 * event.loaded) / event.total));
|
||||||
}
|
}
|
||||||
)
|
return false;
|
||||||
.pipe(map(res => decodeFileProfileSave(res)));
|
}),
|
||||||
|
map((event: HttpResponse<any>) => {
|
||||||
|
req.fileUploadItem.uploadComplete();
|
||||||
|
return decodeFileProfileSave(event.body);
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public urlForFileTalkDownload(
|
public urlForFileTalkDownload(
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {
|
||||||
CreateChatDialogData,
|
CreateChatDialogData,
|
||||||
CreateChatDialogResult
|
CreateChatDialogResult
|
||||||
} from '@app/layouts/messenger/dialogs/chat/create-chat.dialog.component';
|
} from '@app/layouts/messenger/dialogs/chat/create-chat.dialog.component';
|
||||||
import { Observable, Subscription, of } from 'rxjs';
|
import { Subscription, of } from 'rxjs';
|
||||||
import { Store, select } from '@ngrx/store';
|
import { Store, select } from '@ngrx/store';
|
||||||
|
|
||||||
import * as AppStore from '@app/store';
|
import * as AppStore from '@app/store';
|
||||||
|
@ -28,8 +28,7 @@ import {
|
||||||
UserInfoF,
|
UserInfoF,
|
||||||
UserInfoDN
|
UserInfoDN
|
||||||
} from '@ucap-webmessenger/protocol-query';
|
} from '@ucap-webmessenger/protocol-query';
|
||||||
import { MatTabChangeEvent, MatTabGroup } from '@angular/material';
|
import { MatTabChangeEvent } from '@angular/material';
|
||||||
import { RightDrawer } from '@app/types';
|
|
||||||
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
|
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
|
||||||
import { SessionStorageService } from '@ucap-webmessenger/web-storage';
|
import { SessionStorageService } from '@ucap-webmessenger/web-storage';
|
||||||
import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type';
|
import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type';
|
||||||
|
@ -38,7 +37,7 @@ import { KEY_VER_INFO } from '@app/types/ver-info.type';
|
||||||
import { MessageApiService } from '@ucap-webmessenger/api-message';
|
import { MessageApiService } from '@ucap-webmessenger/api-message';
|
||||||
import { DeviceType } from '@ucap-webmessenger/core';
|
import { DeviceType } from '@ucap-webmessenger/core';
|
||||||
import { UnreadCountRequest } from 'projects/ucap-webmessenger-api-message/src/lib/apis/unread-count';
|
import { UnreadCountRequest } from 'projects/ucap-webmessenger-api-message/src/lib/apis/unread-count';
|
||||||
import { map, catchError } from 'rxjs/operators';
|
import { map, catchError, tap } from 'rxjs/operators';
|
||||||
import { MessageStatusCode } from '@ucap-webmessenger/api';
|
import { MessageStatusCode } from '@ucap-webmessenger/api';
|
||||||
import {
|
import {
|
||||||
EditMessageDialogComponent,
|
EditMessageDialogComponent,
|
||||||
|
@ -87,6 +86,7 @@ export class LeftSideComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
sessionVerinfo: VersionInfo2Response;
|
sessionVerinfo: VersionInfo2Response;
|
||||||
loginRes: LoginResponse;
|
loginRes: LoginResponse;
|
||||||
|
loginResSubscription: Subscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private store: Store<any>,
|
private store: Store<any>,
|
||||||
|
@ -95,9 +95,6 @@ export class LeftSideComponent implements OnInit, OnDestroy {
|
||||||
private messageApiService: MessageApiService,
|
private messageApiService: MessageApiService,
|
||||||
private logger: NGXLogger
|
private logger: NGXLogger
|
||||||
) {
|
) {
|
||||||
this.loginRes = this.sessionStorageService.get<LoginResponse>(
|
|
||||||
KEY_LOGIN_RES_INFO
|
|
||||||
);
|
|
||||||
this.sessionVerinfo = this.sessionStorageService.get<VersionInfo2Response>(
|
this.sessionVerinfo = this.sessionStorageService.get<VersionInfo2Response>(
|
||||||
KEY_VER_INFO
|
KEY_VER_INFO
|
||||||
);
|
);
|
||||||
|
@ -112,6 +109,15 @@ export class LeftSideComponent implements OnInit, OnDestroy {
|
||||||
this.badgeChatUnReadCount = count;
|
this.badgeChatUnReadCount = count;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.loginResSubscription = this.store
|
||||||
|
.pipe(
|
||||||
|
select(AppStore.AccountSelector.AuthenticationSelector.loginRes),
|
||||||
|
tap(loginRes => {
|
||||||
|
this.loginRes = loginRes;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
|
||||||
this.getMessageUnreadCount();
|
this.getMessageUnreadCount();
|
||||||
this.badgeMessageInterval = setInterval(
|
this.badgeMessageInterval = setInterval(
|
||||||
() => this.getMessageUnreadCount(),
|
() => this.getMessageUnreadCount(),
|
||||||
|
@ -129,6 +135,9 @@ export class LeftSideComponent implements OnInit, OnDestroy {
|
||||||
if (!!this.badgeMessageUnReadCountSubscription) {
|
if (!!this.badgeMessageUnReadCountSubscription) {
|
||||||
this.badgeMessageUnReadCountSubscription.unsubscribe();
|
this.badgeMessageUnReadCountSubscription.unsubscribe();
|
||||||
}
|
}
|
||||||
|
if (!!this.loginResSubscription) {
|
||||||
|
this.loginResSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
if (!!this.badgeMessageInterval) {
|
if (!!this.badgeMessageInterval) {
|
||||||
clearInterval(this.badgeMessageInterval);
|
clearInterval(this.badgeMessageInterval);
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<div fxFlex class="setting-tab">
|
<div fxFlex class="setting-tab">
|
||||||
<ucap-account-change-password
|
<ucap-account-change-password
|
||||||
|
[loginId]="data.loginId"
|
||||||
|
[phoneNumber]="data.phoneNumber"
|
||||||
|
[encryptedLoginPw]="data.encryptedLoginPw"
|
||||||
(changePassword)="onChangePassword($event)"
|
(changePassword)="onChangePassword($event)"
|
||||||
></ucap-account-change-password>
|
></ucap-account-change-password>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,24 +1,18 @@
|
||||||
import { Component, OnInit, Inject, OnDestroy } from '@angular/core';
|
import { Component, OnInit, Inject, OnDestroy } from '@angular/core';
|
||||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
|
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
|
||||||
import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type';
|
|
||||||
import { KEY_VER_INFO } from '@app/types/ver-info.type';
|
|
||||||
import { SessionStorageService } from '@ucap-webmessenger/web-storage';
|
|
||||||
|
|
||||||
import { Store } from '@ngrx/store';
|
|
||||||
|
|
||||||
import { DialogService } from '@ucap-webmessenger/ui';
|
import { DialogService } from '@ucap-webmessenger/ui';
|
||||||
import { VersionInfo2Response } from '@ucap-webmessenger/api-public';
|
|
||||||
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
|
|
||||||
import { AppAuthenticationService } from '@app/services/authentication.service';
|
|
||||||
import { StringUtil, CharactorType } from '@ucap-webmessenger/core';
|
|
||||||
|
|
||||||
import * as ServiceStore from '@app/store/messenger/service';
|
export interface ChangePasswordDialogData {
|
||||||
import CryptoJS from 'crypto-js';
|
loginId: string;
|
||||||
|
phoneNumber?: string;
|
||||||
export interface ChangePasswordDialogData {}
|
encryptedLoginPw?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ChangePasswordDialogResult {
|
export interface ChangePasswordDialogResult {
|
||||||
choice: boolean;
|
choice: boolean;
|
||||||
|
currentLoginPw?: string;
|
||||||
|
newLoginPw?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -27,170 +21,25 @@ export interface ChangePasswordDialogResult {
|
||||||
styleUrls: ['./change-password.dialog.component.scss']
|
styleUrls: ['./change-password.dialog.component.scss']
|
||||||
})
|
})
|
||||||
export class ChangePasswordDialogComponent implements OnInit, OnDestroy {
|
export class ChangePasswordDialogComponent implements OnInit, OnDestroy {
|
||||||
loginRes: LoginResponse;
|
|
||||||
sessionVerinfo: VersionInfo2Response;
|
|
||||||
isMe: boolean;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public dialogRef: MatDialogRef<
|
public dialogRef: MatDialogRef<
|
||||||
ChangePasswordDialogData,
|
ChangePasswordDialogData,
|
||||||
ChangePasswordDialogResult
|
ChangePasswordDialogResult
|
||||||
>,
|
>,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: ChangePasswordDialogData,
|
@Inject(MAT_DIALOG_DATA) public data: ChangePasswordDialogData,
|
||||||
private dialogService: DialogService,
|
private dialogService: DialogService
|
||||||
private sessionStorageService: SessionStorageService,
|
) {}
|
||||||
private appAuthenticationService: AppAuthenticationService,
|
|
||||||
private store: Store<any>
|
|
||||||
) {
|
|
||||||
this.sessionVerinfo = this.sessionStorageService.get<VersionInfo2Response>(
|
|
||||||
KEY_VER_INFO
|
|
||||||
);
|
|
||||||
this.loginRes = this.sessionStorageService.get<LoginResponse>(
|
|
||||||
KEY_LOGIN_RES_INFO
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {}
|
ngOnInit() {}
|
||||||
|
|
||||||
ngOnDestroy(): void {}
|
ngOnDestroy(): void {}
|
||||||
|
|
||||||
onChangePassword(info: {
|
onChangePassword(info: { currentLoginPw: string; newLoginPw: string }): void {
|
||||||
currentLoginPw: string;
|
this.dialogRef.close({
|
||||||
newLoginPw: string;
|
choice: true,
|
||||||
newConfirmLoginPw: string;
|
currentLoginPw: info.currentLoginPw,
|
||||||
notValid: () => void;
|
newLoginPw: info.newLoginPw
|
||||||
}): void {
|
});
|
||||||
if (!info.currentLoginPw || '' === info.currentLoginPw.trim()) {
|
|
||||||
// 현재 비밀번호를 입력해주세요
|
|
||||||
info.notValid();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this.appAuthenticationService.isSameForPassword(info.currentLoginPw)) {
|
|
||||||
// "현재 비밀번호가 다릅니다"
|
|
||||||
info.notValid();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!info.newLoginPw || '' === info.newLoginPw.trim()) {
|
|
||||||
// 신규 비밀번호를 입력해주세요
|
|
||||||
info.notValid();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (-1 < info.newLoginPw.indexOf(this.loginRes.userId)) {
|
|
||||||
// "사용자 ID를 비밀번호에 포함할 수 없습니다."
|
|
||||||
info.notValid();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const phoneNumberOnly = !!this.loginRes.userInfo.hpNumber
|
|
||||||
? this.loginRes.userInfo.hpNumber.trim().replace(new RegExp('-', 'g'), '')
|
|
||||||
: '';
|
|
||||||
|
|
||||||
switch (phoneNumberOnly.length) {
|
|
||||||
case 11:
|
|
||||||
if (-1 < info.newLoginPw.indexOf(phoneNumberOnly.substr(3, 4))) {
|
|
||||||
// "사용자 휴대폰번호를 비밀번호에 포함할 수 없습니다."
|
|
||||||
info.notValid();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (-1 < info.newLoginPw.indexOf(phoneNumberOnly.substr(7, 4))) {
|
|
||||||
// "사용자 휴대폰번호를 비밀번호에 포함할 수 없습니다."
|
|
||||||
info.notValid();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 10:
|
|
||||||
if (-1 < info.newLoginPw.indexOf(phoneNumberOnly.substr(3, 3))) {
|
|
||||||
// "사용자 휴대폰번호를 비밀번호에 포함할 수 없습니다."
|
|
||||||
info.notValid();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (-1 < info.newLoginPw.indexOf(phoneNumberOnly.substr(6, 4))) {
|
|
||||||
// "사용자 휴대폰번호를 비밀번호에 포함할 수 없습니다."
|
|
||||||
info.notValid();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!info.newConfirmLoginPw || '' === info.newConfirmLoginPw.trim()) {
|
|
||||||
// "신규 비밀번호 확인을 입력해주세요"
|
|
||||||
info.notValid();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (info.newLoginPw !== info.newConfirmLoginPw.trim()) {
|
|
||||||
// "신규 비밀번호와 신규 비밀번호 확인이 다릅니다"
|
|
||||||
info.notValid();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (info.currentLoginPw === info.newLoginPw.trim()) {
|
|
||||||
// "현재 비밀번호와 새로운 비밀번호가 같습니다."
|
|
||||||
info.notValid();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (0 < StringUtil.includes(info.newLoginPw, CharactorType.Space)) {
|
|
||||||
// "비밀번호에는 공백을 입력할 수 없습니다."
|
|
||||||
info.notValid();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (StringUtil.isRepeat(info.newLoginPw, 3)) {
|
|
||||||
// "숫자나 문자를 3번이상 반복적으로 사용할 수 없습니다."
|
|
||||||
info.notValid();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
StringUtil.isIncrements(info.newLoginPw, 3) ||
|
|
||||||
StringUtil.isDecrements(info.newLoginPw, 3)
|
|
||||||
) {
|
|
||||||
// "연속되는 숫자나 문자를 3번이상 사용할 수 없습니다."
|
|
||||||
info.notValid();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let combinationCount = 0;
|
|
||||||
if (0 < StringUtil.includes(info.newLoginPw, CharactorType.Special)) {
|
|
||||||
combinationCount++;
|
|
||||||
}
|
|
||||||
if (0 < StringUtil.includes(info.newLoginPw, CharactorType.Number)) {
|
|
||||||
combinationCount++;
|
|
||||||
}
|
|
||||||
if (0 < StringUtil.includes(info.newLoginPw, CharactorType.Charactor)) {
|
|
||||||
combinationCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
let minPwLen = 0;
|
|
||||||
if (2 < combinationCount) {
|
|
||||||
minPwLen = 8;
|
|
||||||
} else if (2 === combinationCount) {
|
|
||||||
minPwLen = 10;
|
|
||||||
} else if (2 > combinationCount) {
|
|
||||||
// "2종류 이상 조합을 해야 합니다."
|
|
||||||
info.notValid();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info.newLoginPw.length < minPwLen) {
|
|
||||||
// "비밀번호는 %d가지가 조합된 경우 %d자를 넘어야 합니다."
|
|
||||||
info.notValid();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.store.dispatch(
|
|
||||||
ServiceStore.userPasswordSet({
|
|
||||||
req: {
|
|
||||||
loginId: this.loginRes.userId,
|
|
||||||
companyCode: this.loginRes.companyCode,
|
|
||||||
oldLoginPw: CryptoJS.enc.Hex.stringify(
|
|
||||||
CryptoJS.SHA256(info.currentLoginPw)
|
|
||||||
),
|
|
||||||
newLoginPw: CryptoJS.enc.Hex.stringify(
|
|
||||||
CryptoJS.SHA256(info.newLoginPw)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
this.dialogRef.close({ choice: true });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onClickChoice(choice: boolean): void {
|
onClickChoice(choice: boolean): void {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<ucap-profile-profile
|
<ucap-profile-profile
|
||||||
[userInfo]="data.userInfo"
|
[userInfo]="userInfo"
|
||||||
[profileImageRoot]="sessionVerinfo.profileRoot"
|
[profileImageRoot]="sessionVerinfo.profileRoot"
|
||||||
[isMe]="isMe"
|
[isMe]="isMe"
|
||||||
[isBuddy]="isBuddy"
|
[isBuddy]="isBuddy"
|
||||||
|
@ -7,5 +7,6 @@
|
||||||
(openChat)="onClickChat($event)"
|
(openChat)="onClickChat($event)"
|
||||||
(toggleFavorit)="onClickToggleFavorit($event)"
|
(toggleFavorit)="onClickToggleFavorit($event)"
|
||||||
(toggleBuddy)="onClickToggleBuddy($event)"
|
(toggleBuddy)="onClickToggleBuddy($event)"
|
||||||
|
(uploadProfileImage)="onUploadProfileImage($event)"
|
||||||
>
|
>
|
||||||
</ucap-profile-profile>
|
</ucap-profile-profile>
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { Store, select } from '@ngrx/store';
|
||||||
import * as AppStore from '@app/store';
|
import * as AppStore from '@app/store';
|
||||||
import * as ChatStore from '@app/store/messenger/chat';
|
import * as ChatStore from '@app/store/messenger/chat';
|
||||||
import * as SyncStore from '@app/store/messenger/sync';
|
import * as SyncStore from '@app/store/messenger/sync';
|
||||||
|
import * as AuthenticationStore from '@app/store/account/authentication';
|
||||||
|
|
||||||
import { UserInfo, GroupDetailData } from '@ucap-webmessenger/protocol-sync';
|
import { UserInfo, GroupDetailData } from '@ucap-webmessenger/protocol-sync';
|
||||||
import {
|
import {
|
||||||
|
@ -15,12 +16,28 @@ import {
|
||||||
UserInfoF,
|
UserInfoF,
|
||||||
UserInfoDN
|
UserInfoDN
|
||||||
} from '@ucap-webmessenger/protocol-query';
|
} from '@ucap-webmessenger/protocol-query';
|
||||||
import { DialogService, ConfirmDialogComponent, ConfirmDialogData, ConfirmDialogResult } from '@ucap-webmessenger/ui';
|
import {
|
||||||
|
DialogService,
|
||||||
|
ConfirmDialogComponent,
|
||||||
|
ConfirmDialogData,
|
||||||
|
ConfirmDialogResult,
|
||||||
|
SnackBarService
|
||||||
|
} from '@ucap-webmessenger/ui';
|
||||||
import { VersionInfo2Response } from '@ucap-webmessenger/api-public';
|
import { VersionInfo2Response } from '@ucap-webmessenger/api-public';
|
||||||
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
|
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
|
||||||
import { map } from 'rxjs/operators';
|
import { map, take, finalize } from 'rxjs/operators';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { SelectGroupDialogComponent, SelectGroupDialogData, SelectGroupDialogResult } from '../group/select-group.dialog.component';
|
import {
|
||||||
|
SelectGroupDialogComponent,
|
||||||
|
SelectGroupDialogData,
|
||||||
|
SelectGroupDialogResult
|
||||||
|
} from '../group/select-group.dialog.component';
|
||||||
|
import {
|
||||||
|
FileUploadItem,
|
||||||
|
CommonApiService
|
||||||
|
} from '@ucap-webmessenger/api-common';
|
||||||
|
import { EnvironmentsInfo, KEY_ENVIRONMENTS_INFO } from '@app/types';
|
||||||
|
import { StatusCode } from '@ucap-webmessenger/api';
|
||||||
|
|
||||||
export interface ProfileDialogData {
|
export interface ProfileDialogData {
|
||||||
userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN;
|
userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN;
|
||||||
|
@ -34,8 +51,11 @@ export interface ProfileDialogResult {}
|
||||||
styleUrls: ['./profile.dialog.component.scss']
|
styleUrls: ['./profile.dialog.component.scss']
|
||||||
})
|
})
|
||||||
export class ProfileDialogComponent implements OnInit, OnDestroy {
|
export class ProfileDialogComponent implements OnInit, OnDestroy {
|
||||||
|
userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN;
|
||||||
loginRes: LoginResponse;
|
loginRes: LoginResponse;
|
||||||
sessionVerinfo: VersionInfo2Response;
|
sessionVerinfo: VersionInfo2Response;
|
||||||
|
environmentsInfo: EnvironmentsInfo;
|
||||||
|
|
||||||
isMe: boolean;
|
isMe: boolean;
|
||||||
isBuddy: boolean;
|
isBuddy: boolean;
|
||||||
isFavorit: boolean;
|
isFavorit: boolean;
|
||||||
|
@ -47,6 +67,8 @@ export class ProfileDialogComponent implements OnInit, OnDestroy {
|
||||||
@Inject(MAT_DIALOG_DATA) public data: ProfileDialogData,
|
@Inject(MAT_DIALOG_DATA) public data: ProfileDialogData,
|
||||||
private dialogService: DialogService,
|
private dialogService: DialogService,
|
||||||
private sessionStorageService: SessionStorageService,
|
private sessionStorageService: SessionStorageService,
|
||||||
|
private commonApiService: CommonApiService,
|
||||||
|
private snackBarService: SnackBarService,
|
||||||
private store: Store<any>
|
private store: Store<any>
|
||||||
) {
|
) {
|
||||||
this.sessionVerinfo = this.sessionStorageService.get<VersionInfo2Response>(
|
this.sessionVerinfo = this.sessionStorageService.get<VersionInfo2Response>(
|
||||||
|
@ -55,6 +77,11 @@ export class ProfileDialogComponent implements OnInit, OnDestroy {
|
||||||
this.loginRes = this.sessionStorageService.get<LoginResponse>(
|
this.loginRes = this.sessionStorageService.get<LoginResponse>(
|
||||||
KEY_LOGIN_RES_INFO
|
KEY_LOGIN_RES_INFO
|
||||||
);
|
);
|
||||||
|
this.environmentsInfo = this.sessionStorageService.get<EnvironmentsInfo>(
|
||||||
|
KEY_ENVIRONMENTS_INFO
|
||||||
|
);
|
||||||
|
|
||||||
|
this.userInfo = data.userInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
@ -156,8 +183,68 @@ export class ProfileDialogComponent implements OnInit, OnDestroy {
|
||||||
this.store.dispatch(
|
this.store.dispatch(
|
||||||
SyncStore.delBuddyAndClear({ seq: param.userInfo.seq })
|
SyncStore.delBuddyAndClear({ seq: param.userInfo.seq })
|
||||||
);
|
);
|
||||||
this.isBuddy = false
|
this.isBuddy = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onUploadProfileImage(profileImageFileUploadItem: FileUploadItem) {
|
||||||
|
this.commonApiService
|
||||||
|
.fileProfileSave(
|
||||||
|
{
|
||||||
|
userSeq: this.loginRes.userSeq,
|
||||||
|
deviceType: this.environmentsInfo.deviceType,
|
||||||
|
token: this.loginRes.tokenString,
|
||||||
|
file: profileImageFileUploadItem.file,
|
||||||
|
fileUploadItem: profileImageFileUploadItem
|
||||||
|
},
|
||||||
|
this.sessionVerinfo.profileUploadUrl
|
||||||
|
)
|
||||||
|
.pipe(
|
||||||
|
take(1),
|
||||||
|
map(res => {
|
||||||
|
if (!res) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (StatusCode.Success === res.statusCode) {
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
throw res;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
finalize(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
profileImageFileUploadItem.uploadingProgress$ = undefined;
|
||||||
|
}, 1000);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe(
|
||||||
|
res => {
|
||||||
|
const userInfo = {
|
||||||
|
...this.loginRes.userInfo,
|
||||||
|
profileImageFile: res.profileSubDir
|
||||||
|
};
|
||||||
|
|
||||||
|
this.store.dispatch(
|
||||||
|
AuthenticationStore.updateLoginRes({
|
||||||
|
loginRes: {
|
||||||
|
...this.loginRes,
|
||||||
|
userInfo
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
this.userInfo = userInfo as any;
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
this.snackBarService.open(
|
||||||
|
`프로필 이미지 변경중에 문제가 발생하였습니다.`,
|
||||||
|
'',
|
||||||
|
{
|
||||||
|
duration: 3000,
|
||||||
|
verticalPosition: 'bottom'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ export class LoginPageComponent implements OnInit, OnDestroy {
|
||||||
>(AlertDialogComponent, {
|
>(AlertDialogComponent, {
|
||||||
width: '360px',
|
width: '360px',
|
||||||
data: {
|
data: {
|
||||||
title: 'Alert',
|
title: '로그인',
|
||||||
html: `아이디 또는 패스워드가<br/>일치하지 않습니다.`
|
html: `아이디 또는 패스워드가<br/>일치하지 않습니다.`
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -83,7 +83,7 @@ export class LoginPageComponent implements OnInit, OnDestroy {
|
||||||
>(AlertDialogComponent, {
|
>(AlertDialogComponent, {
|
||||||
width: '360px',
|
width: '360px',
|
||||||
data: {
|
data: {
|
||||||
title: 'Alert',
|
title: '로그인',
|
||||||
html: `비밀번호 오류 횟수 초과입니다.<br/>비밀번호를 확인하신 후<br/>잠시 후 다시 시작해 주세요.`
|
html: `비밀번호 오류 횟수 초과입니다.<br/>비밀번호를 확인하신 후<br/>잠시 후 다시 시작해 주세요.`
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {
|
||||||
|
|
||||||
import { Store, select } from '@ngrx/store';
|
import { Store, select } from '@ngrx/store';
|
||||||
|
|
||||||
import { ProtocolService } from '@ucap-webmessenger/protocol';
|
import { ProtocolService, ServerErrorCode } from '@ucap-webmessenger/protocol';
|
||||||
import { SessionStorageService } from '@ucap-webmessenger/web-storage';
|
import { SessionStorageService } from '@ucap-webmessenger/web-storage';
|
||||||
import {
|
import {
|
||||||
PublicApiService,
|
PublicApiService,
|
||||||
|
@ -51,6 +51,7 @@ import { KEY_VER_INFO } from '@app/types/ver-info.type';
|
||||||
import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type';
|
import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type';
|
||||||
|
|
||||||
import { environment } from '../../environments/environment';
|
import { environment } from '../../environments/environment';
|
||||||
|
import { SnackBarService } from '@ucap-webmessenger/ui';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AppMessengerResolver implements Resolve<void> {
|
export class AppMessengerResolver implements Resolve<void> {
|
||||||
|
@ -63,6 +64,7 @@ export class AppMessengerResolver implements Resolve<void> {
|
||||||
private optionProtocolService: OptionProtocolService,
|
private optionProtocolService: OptionProtocolService,
|
||||||
private authenticationProtocolService: AuthenticationProtocolService,
|
private authenticationProtocolService: AuthenticationProtocolService,
|
||||||
private innerProtocolService: InnerProtocolService,
|
private innerProtocolService: InnerProtocolService,
|
||||||
|
private snackBarService: SnackBarService,
|
||||||
private logger: NGXLogger
|
private logger: NGXLogger
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
@ -101,7 +103,8 @@ export class AppMessengerResolver implements Resolve<void> {
|
||||||
}),
|
}),
|
||||||
switchMap(() => this.innerProtocolService.conn({})),
|
switchMap(() => this.innerProtocolService.conn({})),
|
||||||
switchMap(res => {
|
switchMap(res => {
|
||||||
return this.authenticationProtocolService.login({
|
return this.authenticationProtocolService
|
||||||
|
.login({
|
||||||
loginId: loginInfo.loginId,
|
loginId: loginInfo.loginId,
|
||||||
loginPw: loginInfo.loginPw,
|
loginPw: loginInfo.loginPw,
|
||||||
deviceType: environmentsInfo.deviceType,
|
deviceType: environmentsInfo.deviceType,
|
||||||
|
@ -119,7 +122,19 @@ export class AppMessengerResolver implements Resolve<void> {
|
||||||
userSpecificInformation: 'PRO_000482',
|
userSpecificInformation: 'PRO_000482',
|
||||||
productId: environment.productConfig.productId,
|
productId: environment.productConfig.productId,
|
||||||
productName: environment.productConfig.productName
|
productName: environment.productConfig.productName
|
||||||
});
|
})
|
||||||
|
.pipe(
|
||||||
|
catchError(err => {
|
||||||
|
switch (err as ServerErrorCode) {
|
||||||
|
case ServerErrorCode.ERRCD_IDPW:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return throwError(err);
|
||||||
|
})
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
switchMap(res => {
|
switchMap(res => {
|
||||||
loginRes = res;
|
loginRes = res;
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import CryptoJS from 'crypto-js';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SessionStorageService,
|
SessionStorageService,
|
||||||
LocalStorageService
|
LocalStorageService
|
||||||
|
@ -10,6 +8,7 @@ import { LocaleCode } from '@ucap-webmessenger/core';
|
||||||
import { LoginInfo, KEY_LOGIN_INFO } from '../types';
|
import { LoginInfo, KEY_LOGIN_INFO } from '../types';
|
||||||
import { KEY_VER_INFO } from '@app/types/ver-info.type';
|
import { KEY_VER_INFO } from '@app/types/ver-info.type';
|
||||||
import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type';
|
import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type';
|
||||||
|
import { PasswordUtil } from '@ucap-webmessenger/pi';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
|
@ -33,7 +32,7 @@ export class AppAuthenticationService {
|
||||||
this.sessionStorageService.set<LoginInfo>(KEY_LOGIN_INFO, {
|
this.sessionStorageService.set<LoginInfo>(KEY_LOGIN_INFO, {
|
||||||
...loginInfo,
|
...loginInfo,
|
||||||
initPw: loginInfo.loginId === loginInfo.loginPw,
|
initPw: loginInfo.loginId === loginInfo.loginPw,
|
||||||
loginPw: CryptoJS.enc.Hex.stringify(CryptoJS.SHA256(loginInfo.loginPw))
|
loginPw: PasswordUtil.encrypt(loginInfo.loginPw)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (rememberMe) {
|
if (rememberMe) {
|
||||||
|
@ -51,11 +50,4 @@ export class AppAuthenticationService {
|
||||||
this.sessionStorageService.remove(KEY_VER_INFO);
|
this.sessionStorageService.remove(KEY_VER_INFO);
|
||||||
this.sessionStorageService.remove(KEY_LOGIN_INFO);
|
this.sessionStorageService.remove(KEY_LOGIN_INFO);
|
||||||
}
|
}
|
||||||
|
|
||||||
isSameForPassword(pw: string): boolean {
|
|
||||||
const loginInfo = this.sessionStorageService.get<LoginInfo>(KEY_LOGIN_INFO);
|
|
||||||
return (
|
|
||||||
loginInfo.loginPw === CryptoJS.enc.Hex.stringify(CryptoJS.SHA256(pw))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,3 +96,27 @@ export const privacyAgreeFailure = createAction(
|
||||||
export const privacyDisagree = createAction(
|
export const privacyDisagree = createAction(
|
||||||
'[Account::Authentication] Privacy Disagree'
|
'[Account::Authentication] Privacy Disagree'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const userPasswordSet = createAction(
|
||||||
|
'[Account::Authentication] User Password Set',
|
||||||
|
props<{ req: UserPasswordSetRequest }>()
|
||||||
|
);
|
||||||
|
|
||||||
|
export const userPasswordSetSuccess = createAction(
|
||||||
|
'[Account::Authentication] User Password Set Success',
|
||||||
|
props<{
|
||||||
|
res: UserPasswordSetResponse;
|
||||||
|
}>()
|
||||||
|
);
|
||||||
|
|
||||||
|
export const userPasswordSetFailure = createAction(
|
||||||
|
'[Account::Authentication] User Password Set Failure',
|
||||||
|
props<{ error: any }>()
|
||||||
|
);
|
||||||
|
|
||||||
|
export const updateLoginRes = createAction(
|
||||||
|
'[Account::Authentication] Update LoginRes',
|
||||||
|
props<{
|
||||||
|
loginRes: LoginResponse;
|
||||||
|
}>()
|
||||||
|
);
|
||||||
|
|
|
@ -16,7 +16,11 @@ import {
|
||||||
DialogService,
|
DialogService,
|
||||||
ConfirmDialogComponent,
|
ConfirmDialogComponent,
|
||||||
ConfirmDialogData,
|
ConfirmDialogData,
|
||||||
ConfirmDialogResult
|
ConfirmDialogResult,
|
||||||
|
SnackBarService,
|
||||||
|
AlertDialogComponent,
|
||||||
|
AlertDialogData,
|
||||||
|
AlertDialogResult
|
||||||
} from '@ucap-webmessenger/ui';
|
} from '@ucap-webmessenger/ui';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -34,7 +38,10 @@ import {
|
||||||
privacyAgreeSuccess,
|
privacyAgreeSuccess,
|
||||||
increaseLoginFailCount,
|
increaseLoginFailCount,
|
||||||
initialLoginFailCount,
|
initialLoginFailCount,
|
||||||
logoutInitialize
|
logoutInitialize,
|
||||||
|
userPasswordSet,
|
||||||
|
userPasswordSetSuccess,
|
||||||
|
userPasswordSetFailure
|
||||||
} from './actions';
|
} from './actions';
|
||||||
import {
|
import {
|
||||||
LoginInfo,
|
LoginInfo,
|
||||||
|
@ -60,6 +67,10 @@ import {
|
||||||
ChangePasswordDialogData,
|
ChangePasswordDialogData,
|
||||||
ChangePasswordDialogResult
|
ChangePasswordDialogResult
|
||||||
} from '@app/layouts/messenger/dialogs/account/change-password.dialog.component';
|
} from '@app/layouts/messenger/dialogs/account/change-password.dialog.component';
|
||||||
|
import {
|
||||||
|
ServiceProtocolService,
|
||||||
|
UserPasswordSetResponse
|
||||||
|
} from '@ucap-webmessenger/protocol-service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class Effects {
|
export class Effects {
|
||||||
|
@ -273,10 +284,24 @@ export class Effects {
|
||||||
width: '500px',
|
width: '500px',
|
||||||
height: '500px',
|
height: '500px',
|
||||||
disableClose: false,
|
disableClose: false,
|
||||||
data: {}
|
data: {
|
||||||
|
loginId: loginInfo.loginId,
|
||||||
|
encryptedLoginPw: loginInfo.loginPw,
|
||||||
|
phoneNumber: loginRes.userInfo.hpNumber
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!!result && result.choice) {
|
if (!!result && result.choice) {
|
||||||
|
this.store.dispatch(
|
||||||
|
userPasswordSet({
|
||||||
|
req: {
|
||||||
|
companyCode: loginInfo.companyCode,
|
||||||
|
loginId: loginInfo.loginId,
|
||||||
|
oldLoginPw: result.currentLoginPw,
|
||||||
|
newLoginPw: result.newLoginPw
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -324,6 +349,67 @@ export class Effects {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
userPasswordSet$ = createEffect(() =>
|
||||||
|
this.actions$.pipe(
|
||||||
|
ofType(userPasswordSet),
|
||||||
|
map(action => action.req),
|
||||||
|
exhaustMap(req =>
|
||||||
|
this.serviceProtocolService.userPasswordSet(req).pipe(
|
||||||
|
map((res: UserPasswordSetResponse) => {
|
||||||
|
return userPasswordSetSuccess({
|
||||||
|
res
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
catchError(error => of(userPasswordSetFailure({ error })))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
userPasswordSetSuccess$ = createEffect(
|
||||||
|
() => {
|
||||||
|
return this.actions$.pipe(
|
||||||
|
ofType(userPasswordSetSuccess),
|
||||||
|
tap(async action => {
|
||||||
|
await this.dialogService.open<
|
||||||
|
AlertDialogComponent,
|
||||||
|
AlertDialogData,
|
||||||
|
AlertDialogResult
|
||||||
|
>(AlertDialogComponent, {
|
||||||
|
width: '360px',
|
||||||
|
disableClose: true,
|
||||||
|
data: {
|
||||||
|
title: '비밀번호 변경',
|
||||||
|
message: '비밀번호가 변경되었습니다. 다시 로그인하여 주십시오'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.store.dispatch(logout());
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ dispatch: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
userPasswordSetFailure$ = createEffect(
|
||||||
|
() => {
|
||||||
|
return this.actions$.pipe(
|
||||||
|
ofType(userPasswordSetFailure),
|
||||||
|
tap(action => {
|
||||||
|
this.snackBarService.open(
|
||||||
|
`비밀번호 변경 중에 문제가 발생하였습니다.`,
|
||||||
|
'',
|
||||||
|
{
|
||||||
|
duration: 3000,
|
||||||
|
verticalPosition: 'bottom'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ dispatch: false }
|
||||||
|
);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private actions$: Actions,
|
private actions$: Actions,
|
||||||
private ngZone: NgZone,
|
private ngZone: NgZone,
|
||||||
|
@ -334,8 +420,9 @@ export class Effects {
|
||||||
private appAuthenticationService: AppAuthenticationService,
|
private appAuthenticationService: AppAuthenticationService,
|
||||||
private protocolService: ProtocolService,
|
private protocolService: ProtocolService,
|
||||||
private authenticationProtocolService: AuthenticationProtocolService,
|
private authenticationProtocolService: AuthenticationProtocolService,
|
||||||
|
private serviceProtocolService: ServiceProtocolService,
|
||||||
private dialogService: DialogService,
|
private dialogService: DialogService,
|
||||||
|
private snackBarService: SnackBarService,
|
||||||
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
|
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
|
||||||
private logger: NGXLogger
|
private logger: NGXLogger
|
||||||
) {}
|
) {}
|
||||||
|
|
|
@ -5,7 +5,8 @@ import {
|
||||||
increaseLoginFailCount,
|
increaseLoginFailCount,
|
||||||
initialLoginFailCount,
|
initialLoginFailCount,
|
||||||
logout,
|
logout,
|
||||||
logoutInitialize
|
logoutInitialize,
|
||||||
|
updateLoginRes
|
||||||
} from './actions';
|
} from './actions';
|
||||||
|
|
||||||
export const reducer = createReducer(
|
export const reducer = createReducer(
|
||||||
|
@ -17,6 +18,13 @@ export const reducer = createReducer(
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
on(updateLoginRes, (state, action) => {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loginRes: action.loginRes
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
on(increaseLoginFailCount, (state, action) => {
|
on(increaseLoginFailCount, (state, action) => {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
|
|
@ -6,7 +6,6 @@ import * as EventStore from './event';
|
||||||
import * as OptionStore from './option';
|
import * as OptionStore from './option';
|
||||||
import * as QueryStore from './query';
|
import * as QueryStore from './query';
|
||||||
import * as RoomStore from './room';
|
import * as RoomStore from './room';
|
||||||
import * as ServiceStore from './service';
|
|
||||||
import * as StatusStore from './status';
|
import * as StatusStore from './status';
|
||||||
import * as SyncStore from './sync';
|
import * as SyncStore from './sync';
|
||||||
import * as SettingsStore from './settings';
|
import * as SettingsStore from './settings';
|
||||||
|
@ -17,7 +16,6 @@ export interface State {
|
||||||
option: OptionStore.State;
|
option: OptionStore.State;
|
||||||
query: QueryStore.State;
|
query: QueryStore.State;
|
||||||
room: RoomStore.State;
|
room: RoomStore.State;
|
||||||
service: ServiceStore.State;
|
|
||||||
status: StatusStore.State;
|
status: StatusStore.State;
|
||||||
sync: SyncStore.State;
|
sync: SyncStore.State;
|
||||||
settings: SettingsStore.State;
|
settings: SettingsStore.State;
|
||||||
|
@ -29,7 +27,6 @@ export const effects: Type<any>[] = [
|
||||||
OptionStore.Effects,
|
OptionStore.Effects,
|
||||||
QueryStore.Effects,
|
QueryStore.Effects,
|
||||||
RoomStore.Effects,
|
RoomStore.Effects,
|
||||||
ServiceStore.Effects,
|
|
||||||
StatusStore.Effects,
|
StatusStore.Effects,
|
||||||
SyncStore.Effects,
|
SyncStore.Effects,
|
||||||
SettingsStore.Effects
|
SettingsStore.Effects
|
||||||
|
@ -42,7 +39,6 @@ export function reducers(state: State | undefined, action: Action) {
|
||||||
option: OptionStore.reducer,
|
option: OptionStore.reducer,
|
||||||
query: QueryStore.reducer,
|
query: QueryStore.reducer,
|
||||||
room: RoomStore.reducer,
|
room: RoomStore.reducer,
|
||||||
service: ServiceStore.reducer,
|
|
||||||
status: StatusStore.reducer,
|
status: StatusStore.reducer,
|
||||||
sync: SyncStore.reducer,
|
sync: SyncStore.reducer,
|
||||||
settings: SettingsStore.reducer
|
settings: SettingsStore.reducer
|
||||||
|
@ -66,9 +62,6 @@ export function selectors<S>(selector: Selector<any, State>) {
|
||||||
QuerySelector: QueryStore.selectors(
|
QuerySelector: QueryStore.selectors(
|
||||||
createSelector(selector, (state: State) => state.query)
|
createSelector(selector, (state: State) => state.query)
|
||||||
),
|
),
|
||||||
ServiceSelector: ServiceStore.selectors(
|
|
||||||
createSelector(selector, (state: State) => state.service)
|
|
||||||
),
|
|
||||||
StatusSelector: StatusStore.selectors(
|
StatusSelector: StatusStore.selectors(
|
||||||
createSelector(selector, (state: State) => state.status)
|
createSelector(selector, (state: State) => state.status)
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
import { createAction, props } from '@ngrx/store';
|
|
||||||
|
|
||||||
import {
|
|
||||||
UserPasswordSetRequest,
|
|
||||||
UserPasswordSetResponse
|
|
||||||
} from '@ucap-webmessenger/protocol-service';
|
|
||||||
|
|
||||||
export const userPasswordSet = createAction(
|
|
||||||
'[Account::Authentication] User Password Set',
|
|
||||||
props<{ req: UserPasswordSetRequest }>()
|
|
||||||
);
|
|
||||||
|
|
||||||
export const userPasswordSetSuccess = createAction(
|
|
||||||
'[Account::Authentication] User Password Set Success',
|
|
||||||
props<{
|
|
||||||
res: UserPasswordSetResponse;
|
|
||||||
}>()
|
|
||||||
);
|
|
||||||
|
|
||||||
export const userPasswordSetFailure = createAction(
|
|
||||||
'[Account::Authentication] User Password Set Failure',
|
|
||||||
props<{ error: any }>()
|
|
||||||
);
|
|
|
@ -1,47 +0,0 @@
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
|
|
||||||
import { Actions, ofType, createEffect } from '@ngrx/effects';
|
|
||||||
|
|
||||||
import { Store } from '@ngrx/store';
|
|
||||||
|
|
||||||
import { NGXLogger } from 'ngx-logger';
|
|
||||||
import { catchError, exhaustMap, map } from 'rxjs/operators';
|
|
||||||
|
|
||||||
import { of } from 'rxjs';
|
|
||||||
|
|
||||||
import {
|
|
||||||
userPasswordSet,
|
|
||||||
userPasswordSetSuccess,
|
|
||||||
userPasswordSetFailure
|
|
||||||
} from './actions';
|
|
||||||
import {
|
|
||||||
ServiceProtocolService,
|
|
||||||
UserPasswordSetResponse
|
|
||||||
} from '@ucap-webmessenger/protocol-service';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class Effects {
|
|
||||||
userPasswordSet$ = createEffect(() =>
|
|
||||||
this.actions$.pipe(
|
|
||||||
ofType(userPasswordSet),
|
|
||||||
map(action => action.req),
|
|
||||||
exhaustMap(req =>
|
|
||||||
this.serviceProtocolService.userPasswordSet(req).pipe(
|
|
||||||
map((res: UserPasswordSetResponse) => {
|
|
||||||
return userPasswordSetSuccess({
|
|
||||||
res
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
catchError(error => of(userPasswordSetFailure({ error })))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private actions$: Actions,
|
|
||||||
private serviceProtocolService: ServiceProtocolService,
|
|
||||||
private store: Store<any>,
|
|
||||||
private logger: NGXLogger
|
|
||||||
) {}
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
export * from './actions';
|
|
||||||
export * from './effects';
|
|
||||||
export * from './reducers';
|
|
||||||
export * from './state';
|
|
|
@ -1,4 +0,0 @@
|
||||||
import { createReducer, on } from '@ngrx/store';
|
|
||||||
import { initialState } from './state';
|
|
||||||
|
|
||||||
export const reducer = createReducer(initialState);
|
|
|
@ -1,9 +0,0 @@
|
||||||
import { Selector, createSelector } from '@ngrx/store';
|
|
||||||
|
|
||||||
export interface State {}
|
|
||||||
|
|
||||||
export const initialState: State = {};
|
|
||||||
|
|
||||||
export function selectors<S>(selector: Selector<any, State>) {
|
|
||||||
return {};
|
|
||||||
}
|
|
137
projects/ucap-webmessenger-pi/src/lib/utils/password.util.ts
Normal file
137
projects/ucap-webmessenger-pi/src/lib/utils/password.util.ts
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
import CryptoJS from 'crypto-js';
|
||||||
|
import { StringUtil, CharactorType } from '@ucap-webmessenger/core';
|
||||||
|
|
||||||
|
export interface PasswordValidationOption {
|
||||||
|
userId?: string;
|
||||||
|
phoneNumber?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum PasswordValidationResult {
|
||||||
|
Valid = 'Valid',
|
||||||
|
Empty = 'Empty',
|
||||||
|
IncludeUserId = 'IncludeUserId',
|
||||||
|
IncludePhoneNumber = 'IncludePhoneNumber',
|
||||||
|
IncludeSpace = 'IncludeSpace',
|
||||||
|
IncludeRepeated = 'IncludeRepeated',
|
||||||
|
IncludeIncrements = 'IncludeIncrements',
|
||||||
|
IncludeDecrements = 'IncludeDecrements',
|
||||||
|
LackOfCombination = 'LackOfCombination',
|
||||||
|
TooShortCombinationLength = 'TooShortCombinationLength'
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PasswordUtil {
|
||||||
|
static encrypt(password: string): string {
|
||||||
|
return CryptoJS.enc.Hex.stringify(CryptoJS.SHA256(password));
|
||||||
|
}
|
||||||
|
|
||||||
|
static validate(
|
||||||
|
password: string,
|
||||||
|
option?: PasswordValidationOption
|
||||||
|
): { code: PasswordValidationResult; extra?: any } {
|
||||||
|
if (!password || '' === password.trim()) {
|
||||||
|
return {
|
||||||
|
code: PasswordValidationResult.Empty
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!!option && !!option.userId) {
|
||||||
|
if (-1 < password.indexOf(option.userId)) {
|
||||||
|
return {
|
||||||
|
code: PasswordValidationResult.IncludeUserId
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!!option && !!option.phoneNumber) {
|
||||||
|
const phoneNumberOnly = option.phoneNumber
|
||||||
|
.trim()
|
||||||
|
.replace(new RegExp('-', 'g'), '');
|
||||||
|
|
||||||
|
switch (phoneNumberOnly.length) {
|
||||||
|
case 11:
|
||||||
|
if (-1 < password.indexOf(phoneNumberOnly.substr(3, 4))) {
|
||||||
|
return {
|
||||||
|
code: PasswordValidationResult.IncludePhoneNumber
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (-1 < password.indexOf(phoneNumberOnly.substr(7, 4))) {
|
||||||
|
return {
|
||||||
|
code: PasswordValidationResult.IncludePhoneNumber
|
||||||
|
};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
if (-1 < password.indexOf(phoneNumberOnly.substr(3, 3))) {
|
||||||
|
return {
|
||||||
|
code: PasswordValidationResult.IncludePhoneNumber
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (-1 < password.indexOf(phoneNumberOnly.substr(6, 4))) {
|
||||||
|
return {
|
||||||
|
code: PasswordValidationResult.IncludePhoneNumber
|
||||||
|
};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 < StringUtil.includes(password, CharactorType.Space)) {
|
||||||
|
return {
|
||||||
|
code: PasswordValidationResult.IncludeSpace
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (StringUtil.isRepeat(password, 3)) {
|
||||||
|
return {
|
||||||
|
code: PasswordValidationResult.IncludeRepeated
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (StringUtil.isIncrements(password, 3)) {
|
||||||
|
return {
|
||||||
|
code: PasswordValidationResult.IncludeIncrements
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (StringUtil.isDecrements(password, 3)) {
|
||||||
|
return {
|
||||||
|
code: PasswordValidationResult.IncludeDecrements
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let combinationCount = 0;
|
||||||
|
if (0 < StringUtil.includes(password, CharactorType.Special)) {
|
||||||
|
combinationCount++;
|
||||||
|
}
|
||||||
|
if (0 < StringUtil.includes(password, CharactorType.Number)) {
|
||||||
|
combinationCount++;
|
||||||
|
}
|
||||||
|
if (0 < StringUtil.includes(password, CharactorType.Charactor)) {
|
||||||
|
combinationCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
let minPwLen = 0;
|
||||||
|
if (2 < combinationCount) {
|
||||||
|
minPwLen = 8;
|
||||||
|
} else if (2 === combinationCount) {
|
||||||
|
minPwLen = 10;
|
||||||
|
} else if (2 > combinationCount) {
|
||||||
|
return {
|
||||||
|
code: PasswordValidationResult.LackOfCombination
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password.length < minPwLen) {
|
||||||
|
return {
|
||||||
|
code: PasswordValidationResult.TooShortCombinationLength,
|
||||||
|
extra: {
|
||||||
|
combinationCount,
|
||||||
|
minPwLen
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
code: PasswordValidationResult.Valid
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,8 @@ export * from './lib/types/response-status.type';
|
||||||
|
|
||||||
export * from './lib/services/pi.service';
|
export * from './lib/services/pi.service';
|
||||||
|
|
||||||
|
export * from './lib/utils/password.util';
|
||||||
|
|
||||||
export * from './lib/ucap-pi.module';
|
export * from './lib/ucap-pi.module';
|
||||||
|
|
||||||
export * from './lib/config/urls';
|
export * from './lib/config/urls';
|
||||||
|
|
|
@ -10,16 +10,17 @@ import { NGXLogger } from 'ngx-logger';
|
||||||
import {
|
import {
|
||||||
makeWebSocketObservable,
|
makeWebSocketObservable,
|
||||||
GetWebSocketResponses,
|
GetWebSocketResponses,
|
||||||
NormalClosureMessage,
|
NormalClosureMessage
|
||||||
} from '@ucap-webmessenger/web-socket';
|
} from '@ucap-webmessenger/web-socket';
|
||||||
|
|
||||||
import { PacketBody } from '../protocols/packet';
|
import { PacketBody } from '../protocols/packet';
|
||||||
import {
|
import {
|
||||||
PacketBodyValueDivider,
|
PacketBodyValueDivider,
|
||||||
PacketBodyDivider,
|
PacketBodyDivider
|
||||||
} from '../types/packet-body-divider';
|
} from '../types/packet-body-divider';
|
||||||
import { PacketBodyValue } from '../types/packet-body-value.type';
|
import { PacketBodyValue } from '../types/packet-body-value.type';
|
||||||
import { SSVC_TYPE_ERROR_RES, ServerErrorCode } from '../types/service';
|
import { SSVC_TYPE_ERROR_RES } from '../types/service';
|
||||||
|
import { ServerErrorCode } from '../types/error-code';
|
||||||
import { ProtocolMessage } from '../protocols/protocol';
|
import { ProtocolMessage } from '../protocols/protocol';
|
||||||
|
|
||||||
import { _MODULE_CONFIG } from '../config/token';
|
import { _MODULE_CONFIG } from '../config/token';
|
||||||
|
@ -37,7 +38,7 @@ interface RequestState {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class ProtocolService {
|
export class ProtocolService {
|
||||||
readonly urls: Urls;
|
readonly urls: Urls;
|
||||||
|
@ -186,7 +187,7 @@ export class ProtocolService {
|
||||||
|
|
||||||
packet = this.encodePacket(serviceType, subServiceType, [
|
packet = this.encodePacket(serviceType, subServiceType, [
|
||||||
...bodyList,
|
...bodyList,
|
||||||
{ type: PacketBodyValue.RequestId, value: requestId },
|
{ type: PacketBodyValue.RequestId, value: requestId }
|
||||||
]);
|
]);
|
||||||
|
|
||||||
responseSubject = new Subject<ProtocolMessage>().pipe(
|
responseSubject = new Subject<ProtocolMessage>().pipe(
|
||||||
|
@ -209,7 +210,7 @@ export class ProtocolService {
|
||||||
|
|
||||||
this.pendingRequests.set(requestId, {
|
this.pendingRequests.set(requestId, {
|
||||||
subject: responseSubject,
|
subject: responseSubject,
|
||||||
request: { serviceType, subServiceType, bodyList },
|
request: { serviceType, subServiceType, bodyList }
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
packet = this.encodePacket(serviceType, subServiceType, bodyList);
|
packet = this.encodePacket(serviceType, subServiceType, bodyList);
|
||||||
|
@ -302,8 +303,8 @@ export class ProtocolService {
|
||||||
serviceType,
|
serviceType,
|
||||||
subServiceType,
|
subServiceType,
|
||||||
senderSeq,
|
senderSeq,
|
||||||
bodyList,
|
bodyList
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
export enum ServerErrorCode {
|
||||||
|
/** 서버측에서 연결을 종료했습니다 */
|
||||||
|
ERRCD_FORCE_CLOSE = 10,
|
||||||
|
/** 통신 프로토콜이 일치하지않습니다 (최신 프로그램으로 업데이트 해주세요) */
|
||||||
|
ERRCD_PROTOCOL = 11,
|
||||||
|
/** 클라이언트 버전이 일치하지않습니다 (최신 프로그램으로 업데이트 해주세요) */
|
||||||
|
ERRCD_VERSION = 12,
|
||||||
|
/** 인증되지않은 기기입니다 */
|
||||||
|
ERRCD_DID = 13,
|
||||||
|
/** 로그인 아이디 또는 패스워드가 일치하지않습니다 */
|
||||||
|
ERRCD_IDPW = 14,
|
||||||
|
/** 다른 디바이스에서 로그인했습니다 */
|
||||||
|
ERRCD_DUPLICATE = 15,
|
||||||
|
/** 인증 토큰이 만료되었습니다 */
|
||||||
|
ERRCD_TOKEN = 16,
|
||||||
|
/** 프로그램을 재설치 바랍니다 */
|
||||||
|
ERRCD_SETUP_MAIN = 17,
|
||||||
|
/** 권한자/관리자에 의해 종료되었습니다 */
|
||||||
|
ERRCD_FORCE_INIT = 18,
|
||||||
|
/** 사용권한이 없습니다 */
|
||||||
|
ERRCD_NEED_AUTH_CLIENT = 19,
|
||||||
|
/** DEMO 서비스 기간이 종료되었습니다 */
|
||||||
|
ERRCD_SVC_EXPIRE = 99,
|
||||||
|
/** 요청 처리를 실패했습니다 */
|
||||||
|
ERRCD_FAILED = 100,
|
||||||
|
/** 데이터 처리를 실패했습니다 */
|
||||||
|
ERRCD_DATABASE = 101,
|
||||||
|
/** 최대치 넘음 */
|
||||||
|
ERRCD_EXCESS = 102,
|
||||||
|
/** 인증이 필요한 요청입니다 */
|
||||||
|
ERRCD_NEED_AUTH = 103,
|
||||||
|
/** */
|
||||||
|
ERRCD_USERINFO = 104,
|
||||||
|
/** 요청 처리를 실패했습니다(에러코드) */
|
||||||
|
ERRCD_INVALID_PARAM = 105,
|
||||||
|
/** 대화방에 함께 참여할 수 없는 사용자가 있습니다 */
|
||||||
|
ERRCD_INVALID_INVITE_USER = 106,
|
||||||
|
/** 상대방이 온라인 상태가 아닙니다 */
|
||||||
|
ERRCD_NOT_ONLINE = 200,
|
||||||
|
/** 패스워드 유효 기간이 만료 되었습니다 */
|
||||||
|
ERRCD_PW_EXPIRED = 300,
|
||||||
|
/** 요청을 처리할 권한이 없습니다 */
|
||||||
|
ERRCD_AUTH_DENY = 1000,
|
||||||
|
/** 암호화 처리 오류 */
|
||||||
|
ERRCD_ENCRYPT = 1001,
|
||||||
|
/** 다른 IP로 접속 요청 */
|
||||||
|
ERRCD_RECON = 1002
|
||||||
|
}
|
|
@ -1,26 +1 @@
|
||||||
export const SSVC_TYPE_ERROR_RES = 1000;
|
export const SSVC_TYPE_ERROR_RES = 1000;
|
||||||
|
|
||||||
export enum ServerErrorCode {
|
|
||||||
ERRCD_FORCE_CLOSE = 10,
|
|
||||||
ERRCD_PROTOCOL = 11,
|
|
||||||
ERRCD_VERSION = 12,
|
|
||||||
ERRCD_DID = 13,
|
|
||||||
ERRCD_IDPW = 14,
|
|
||||||
ERRCD_DUPLICATE = 15,
|
|
||||||
ERRCD_TOKEN = 16,
|
|
||||||
ERRCD_SETUP_MAIN = 17,
|
|
||||||
ERRCD_FORCE_INIT = 18,
|
|
||||||
ERRCD_SSO_AUTH = 19,
|
|
||||||
ERRCD_SSO_VALI = 20,
|
|
||||||
ERRCD_DEV_NOT_SUPPORTED = 21,
|
|
||||||
ERRCD_NO_MOBILE_PID = 22,
|
|
||||||
ERRCD_SVC_EXPIRE = 99,
|
|
||||||
ERRCD_FAILED = 100,
|
|
||||||
ERRCD_DATABASE = 101,
|
|
||||||
ERRCD_EXCESS = 102,
|
|
||||||
ERRCD_NEED_AUTH = 103,
|
|
||||||
ERRCD_USERINFO = 104, // ucap 은 없음.
|
|
||||||
ERRCD_AUTH_DENY = 1000,
|
|
||||||
ERRCD_ENCRYPT = 1001, // 패킷 암호화 오류
|
|
||||||
ERRCD_RECON = 1002 // 다른 IP로 접속 요청
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ export * from './lib/services/protocol.service';
|
||||||
export * from './lib/types/packet-body-divider';
|
export * from './lib/types/packet-body-divider';
|
||||||
export * from './lib/types/packet-body-value.type';
|
export * from './lib/types/packet-body-value.type';
|
||||||
export * from './lib/types/service';
|
export * from './lib/types/service';
|
||||||
|
export * from './lib/types/error-code';
|
||||||
|
|
||||||
export * from './lib/ucap-protocol.module';
|
export * from './lib/ucap-protocol.module';
|
||||||
|
|
||||||
|
|
|
@ -5,43 +5,134 @@
|
||||||
<input
|
<input
|
||||||
matInput
|
matInput
|
||||||
type="password"
|
type="password"
|
||||||
formControlName="currentLoginPw"
|
[formControl]="currentLoginPwFormControl"
|
||||||
#currentLoginPw
|
|
||||||
/>
|
/>
|
||||||
<mat-error>
|
|
||||||
현재 패스워드를 입력해 주세요
|
|
||||||
</mat-error>
|
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-label>신규 패스워드</mat-label>
|
<mat-label>신규 패스워드</mat-label>
|
||||||
<input
|
<input matInput type="password" [formControl]="newLoginPwFormControl" />
|
||||||
matInput
|
|
||||||
type="password"
|
|
||||||
formControlName="newLoginPw"
|
|
||||||
#newLoginPw
|
|
||||||
/>
|
|
||||||
<mat-error>
|
|
||||||
신규 패스워드를 입력해 주세요
|
|
||||||
</mat-error>
|
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-label>신규 패스워드 확인</mat-label>
|
<mat-label>신규 패스워드 확인</mat-label>
|
||||||
<input
|
<input
|
||||||
matInput
|
matInput
|
||||||
type="password"
|
type="password"
|
||||||
formControlName="newConfirmLoginPw"
|
[formControl]="newConfirmLoginPwFormControl"
|
||||||
#newConfirmLoginPw
|
|
||||||
/>
|
/>
|
||||||
<mat-error>
|
|
||||||
신규 패스워드 확인을 입력해 주세요
|
|
||||||
</mat-error>
|
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div class="error-container">
|
||||||
|
<mat-error
|
||||||
|
*ngIf="
|
||||||
|
currentLoginPwFormControl.dirty &&
|
||||||
|
currentLoginPwFormControl.hasError('required')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
현재 비밀번호를 입력해 주세요
|
||||||
|
</mat-error>
|
||||||
|
<mat-error
|
||||||
|
*ngIf="
|
||||||
|
currentLoginPwFormControl.dirty &&
|
||||||
|
currentLoginPwFormControl.hasError('ucapPasswordSame')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
현재 비밀번호와 일치하지 않습니다
|
||||||
|
</mat-error>
|
||||||
|
|
||||||
|
<mat-error
|
||||||
|
*ngIf="
|
||||||
|
newLoginPwFormControl.dirty &&
|
||||||
|
newLoginPwFormControl.hasError('required')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
신규 비밀번호를 입력해 주세요
|
||||||
|
</mat-error>
|
||||||
|
<mat-error
|
||||||
|
*ngIf="
|
||||||
|
newLoginPwFormControl.dirty &&
|
||||||
|
newLoginPwFormControl.hasError('ucapNotSameWith')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
현재 비밀번호와 동일합니다
|
||||||
|
</mat-error>
|
||||||
|
<mat-error
|
||||||
|
*ngIf="
|
||||||
|
newLoginPwFormControl.dirty &&
|
||||||
|
newLoginPwFormControl.hasError('ucapPassword')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<ng-container
|
||||||
|
[ngSwitch]="
|
||||||
|
newLoginPwFormControl.getError('ucapPassword').result.code
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<ng-container *ngSwitchCase="PasswordValidationResult.IncludeSpace">
|
||||||
|
비밀번호에는 공백을 입력할 수 없습니다
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngSwitchCase="PasswordValidationResult.IncludeUserId">
|
||||||
|
사용자 ID를 비밀번호에 포함할 수 없습니다
|
||||||
|
</ng-container>
|
||||||
|
<ng-container
|
||||||
|
*ngSwitchCase="PasswordValidationResult.IncludePhoneNumber"
|
||||||
|
>
|
||||||
|
사용자 휴대폰번호를 비밀번호에 포함할 수 없습니다
|
||||||
|
</ng-container>
|
||||||
|
<ng-container
|
||||||
|
*ngSwitchCase="PasswordValidationResult.IncludeRepeated"
|
||||||
|
>
|
||||||
|
숫자나 문자를 3번이상 반복적으로 사용할 수 없습니다
|
||||||
|
</ng-container>
|
||||||
|
<ng-container
|
||||||
|
*ngSwitchCase="PasswordValidationResult.IncludeIncrements"
|
||||||
|
>
|
||||||
|
연속되는 숫자나 문자를 3번이상 사용할 수 없습니다
|
||||||
|
</ng-container>
|
||||||
|
<ng-container
|
||||||
|
*ngSwitchCase="PasswordValidationResult.IncludeDecrements"
|
||||||
|
>
|
||||||
|
연속되는 숫자나 문자를 3번이상 사용할 수 없습니다
|
||||||
|
</ng-container>
|
||||||
|
<ng-container
|
||||||
|
*ngSwitchCase="PasswordValidationResult.LackOfCombination"
|
||||||
|
>
|
||||||
|
문자, 숫자, 특수문자 중 2종류 이상 조합을 해야 합니다
|
||||||
|
</ng-container>
|
||||||
|
<ng-container
|
||||||
|
*ngSwitchCase="PasswordValidationResult.TooShortCombinationLength"
|
||||||
|
>
|
||||||
|
비밀번호는{{
|
||||||
|
newLoginPwFormControl.getError('ucapPassword').result.extra
|
||||||
|
.combinationCount
|
||||||
|
}}가지가 조합된 경우{{
|
||||||
|
newLoginPwFormControl.getError('ucapPassword').result.extra
|
||||||
|
.minPwLen
|
||||||
|
}}자를 넘어야 합니다
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
|
</mat-error>
|
||||||
|
<mat-error
|
||||||
|
*ngIf="
|
||||||
|
newConfirmLoginPwFormControl.dirty &&
|
||||||
|
newConfirmLoginPwFormControl.hasError('required')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
신규 비밀번호 확인을 입력해 주세요
|
||||||
|
</mat-error>
|
||||||
|
<mat-error
|
||||||
|
*ngIf="
|
||||||
|
newConfirmLoginPwFormControl.dirty &&
|
||||||
|
newConfirmLoginPwFormControl.hasError('ucapSameWith')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
신규 비밀번호와 신규 비밀번호 확인이 다릅니다
|
||||||
|
</mat-error>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
mat-raised-button
|
mat-raised-button
|
||||||
class="submit-button bg-accent-dark"
|
class="submit-button bg-accent-dark"
|
||||||
aria-label="패스워드 변경"
|
aria-label="패스워드 변경"
|
||||||
[disabled]="changePasswordForm.invalid || !changePasswordBtnEnable"
|
[disabled]="changePasswordForm.invalid"
|
||||||
(click)="onClickChangePassword()"
|
(click)="onClickChangePassword()"
|
||||||
>
|
>
|
||||||
패스워드 변경
|
패스워드 변경
|
||||||
|
|
|
@ -17,6 +17,11 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error-container {
|
||||||
|
height: 80px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.submit-button {
|
.submit-button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
|
|
@ -8,10 +8,66 @@ import {
|
||||||
ElementRef,
|
ElementRef,
|
||||||
ChangeDetectorRef
|
ChangeDetectorRef
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
import {
|
||||||
import { Company } from '@ucap-webmessenger/api-external';
|
FormControl,
|
||||||
import { LocalStorageService } from '@ucap-webmessenger/web-storage';
|
FormGroup,
|
||||||
import { LoginInfo, KEY_LOGIN_INFO } from '@app/types';
|
Validators,
|
||||||
|
ValidatorFn,
|
||||||
|
AbstractControl,
|
||||||
|
ValidationErrors,
|
||||||
|
FormBuilder
|
||||||
|
} from '@angular/forms';
|
||||||
|
import {
|
||||||
|
PasswordUtil,
|
||||||
|
PasswordValidationOption,
|
||||||
|
PasswordValidationResult
|
||||||
|
} from '@ucap-webmessenger/pi';
|
||||||
|
|
||||||
|
export function ucapPassword(option: PasswordValidationOption): ValidatorFn {
|
||||||
|
return (control: AbstractControl): ValidationErrors | null => {
|
||||||
|
const result = PasswordUtil.validate(control.value, option);
|
||||||
|
return PasswordValidationResult.Valid !== result.code
|
||||||
|
? { ucapPassword: { result } }
|
||||||
|
: null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ucapPasswordSame(value: string): ValidatorFn {
|
||||||
|
return (control: AbstractControl): ValidationErrors | null => {
|
||||||
|
return !!value && value !== PasswordUtil.encrypt(control.value)
|
||||||
|
? { ucapPasswordSame: true }
|
||||||
|
: null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ucapNotSameWith(targetControl: AbstractControl): ValidatorFn {
|
||||||
|
return (control: AbstractControl): ValidationErrors | null => {
|
||||||
|
return !!targetControl &&
|
||||||
|
(targetControl.value as string) === (control.value as string)
|
||||||
|
? { ucapNotSameWith: true }
|
||||||
|
: null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ucapSameWith(targetControl: AbstractControl): ValidatorFn {
|
||||||
|
return (control: AbstractControl): ValidationErrors | null => {
|
||||||
|
return !!targetControl && targetControl.value !== control.value
|
||||||
|
? { ucapSameWith: true }
|
||||||
|
: null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ucapNotSame(value: string | number): ValidatorFn {
|
||||||
|
return (control: AbstractControl): ValidationErrors | null => {
|
||||||
|
return !!value && value === control.value ? { ucapNotSame: true } : null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ucapSame(value: string | number): ValidatorFn {
|
||||||
|
return (control: AbstractControl): ValidationErrors | null => {
|
||||||
|
return !!value && value !== control.value ? { ucapSame: true } : null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ucap-account-change-password',
|
selector: 'ucap-account-change-password',
|
||||||
|
@ -20,48 +76,81 @@ import { LoginInfo, KEY_LOGIN_INFO } from '@app/types';
|
||||||
})
|
})
|
||||||
export class ChangePasswordComponent implements OnInit {
|
export class ChangePasswordComponent implements OnInit {
|
||||||
@Input()
|
@Input()
|
||||||
changePasswordBtnEnable: boolean;
|
loginId: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
phoneNumber: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
encryptedLoginPw: string;
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
changePassword = new EventEmitter<{
|
changePassword = new EventEmitter<{
|
||||||
currentLoginPw: string;
|
currentLoginPw: string;
|
||||||
newLoginPw: string;
|
newLoginPw: string;
|
||||||
newConfirmLoginPw: string;
|
|
||||||
notValid: () => void;
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
@ViewChild('currentLoginPw', { static: true })
|
|
||||||
currentLoginPwElementRef: ElementRef;
|
|
||||||
@ViewChild('newLoginPw', { static: true })
|
|
||||||
newLoginPwElementRef: ElementRef;
|
|
||||||
|
|
||||||
changePasswordForm: FormGroup;
|
changePasswordForm: FormGroup;
|
||||||
|
currentLoginPwFormControl = new FormControl('');
|
||||||
|
newLoginPwFormControl = new FormControl('');
|
||||||
|
newConfirmLoginPwFormControl = new FormControl('');
|
||||||
|
|
||||||
|
PasswordValidationResult = PasswordValidationResult;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private changeDetectorRef: ChangeDetectorRef,
|
private changeDetectorRef: ChangeDetectorRef
|
||||||
private localStorageService: LocalStorageService
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
const loginInfo: LoginInfo = this.localStorageService.get<LoginInfo>(
|
const currentLoginPwValidators: ValidatorFn[] = [Validators.required];
|
||||||
KEY_LOGIN_INFO
|
if (!!this.encryptedLoginPw) {
|
||||||
|
currentLoginPwValidators.push(ucapPasswordSame(this.encryptedLoginPw));
|
||||||
|
}
|
||||||
|
this.currentLoginPwFormControl.setValidators(currentLoginPwValidators);
|
||||||
|
|
||||||
|
let validateOption: PasswordValidationOption = {};
|
||||||
|
if (!!this.loginId) {
|
||||||
|
validateOption = {
|
||||||
|
...validateOption,
|
||||||
|
userId: this.loginId
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!!this.phoneNumber) {
|
||||||
|
validateOption = {
|
||||||
|
...validateOption,
|
||||||
|
phoneNumber: this.phoneNumber
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const newLoginPwValidators: ValidatorFn[] = [
|
||||||
|
Validators.required,
|
||||||
|
ucapNotSameWith(this.currentLoginPwFormControl),
|
||||||
|
ucapPassword(validateOption)
|
||||||
|
];
|
||||||
|
this.newLoginPwFormControl.setValidators(newLoginPwValidators);
|
||||||
|
|
||||||
|
const newConfirmLoginPwValidators: ValidatorFn[] = [
|
||||||
|
Validators.required,
|
||||||
|
ucapSameWith(this.newLoginPwFormControl)
|
||||||
|
];
|
||||||
|
this.newConfirmLoginPwFormControl.setValidators(
|
||||||
|
newConfirmLoginPwValidators
|
||||||
);
|
);
|
||||||
|
|
||||||
this.changePasswordForm = this.formBuilder.group({
|
this.changePasswordForm = this.formBuilder.group({
|
||||||
currentLoginPw: ['', Validators.required],
|
currentLoginPwFormControl: this.currentLoginPwFormControl,
|
||||||
newLoginPw: ['', Validators.required],
|
newLoginPwFormControl: this.newLoginPwFormControl,
|
||||||
newConfirmLoginPw: ['', Validators.required]
|
newConfirmLoginPwFormControl: this.newConfirmLoginPwFormControl
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onClickChangePassword() {
|
onClickChangePassword() {
|
||||||
this.changePassword.emit({
|
this.changePassword.emit({
|
||||||
currentLoginPw: this.changePasswordForm.get('currentLoginPw').value,
|
currentLoginPw: PasswordUtil.encrypt(
|
||||||
newLoginPw: this.changePasswordForm.get('newLoginPw').value,
|
this.currentLoginPwFormControl.value
|
||||||
newConfirmLoginPw: this.changePasswordForm.get('newConfirmLoginPw').value,
|
),
|
||||||
notValid: () => {
|
newLoginPw: PasswordUtil.encrypt(this.newLoginPwFormControl.value)
|
||||||
this.currentLoginPwElementRef.nativeElement.focus();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,39 @@
|
||||||
[path]="userInfo.profileImageFile"
|
[path]="userInfo.profileImageFile"
|
||||||
[default]="'assets/images/img_nophoto_50.png'"
|
[default]="'assets/images/img_nophoto_50.png'"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<mat-spinner
|
||||||
|
*ngIf="
|
||||||
|
profileImageFileUploadItem &&
|
||||||
|
profileImageFileUploadItem.uploadingProgress$
|
||||||
|
"
|
||||||
|
mode="determinate"
|
||||||
|
strokeWidth="5"
|
||||||
|
diameter="84"
|
||||||
|
[value]="profileImageFileUploadItem.uploadingProgress$ | async"
|
||||||
|
class="upload-profile-image-spinner"
|
||||||
|
></mat-spinner>
|
||||||
|
|
||||||
|
<button
|
||||||
|
mat-mini-fab
|
||||||
|
class="mat-elevation-z6 upload-profile-image-btn"
|
||||||
|
*ngIf="isMe"
|
||||||
|
matTooltip="프로필 이미지 변경"
|
||||||
|
matTooltipPosition="above"
|
||||||
|
[disabled]="
|
||||||
|
profileImageFileUploadItem &&
|
||||||
|
profileImageFileUploadItem.uploadingProgress$
|
||||||
|
"
|
||||||
|
(click)="profileImageFileInput.click()"
|
||||||
|
>
|
||||||
|
<span class="mdi mdi-upload mdi-24px"></span>
|
||||||
|
</button>
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
#profileImageFileInput
|
||||||
|
style="display: none"
|
||||||
|
(change)="onChangeFileInput()"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="!isMe" class="profile-option">
|
<div *ngIf="!isMe" class="profile-option">
|
||||||
|
|
|
@ -58,6 +58,18 @@
|
||||||
background-color: #efefef;
|
background-color: #efefef;
|
||||||
border: 2px solid #ffffff;
|
border: 2px solid #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.upload-profile-image-spinner {
|
||||||
|
position: absolute;
|
||||||
|
top: 90px;
|
||||||
|
left: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-profile-image-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 140px;
|
||||||
|
left: 210px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.profile-option {
|
.profile-option {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
import { Component, OnInit, Input, EventEmitter, Output } from '@angular/core';
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
Input,
|
||||||
|
EventEmitter,
|
||||||
|
Output,
|
||||||
|
ViewChild,
|
||||||
|
ElementRef
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
import { UserInfo } from '@ucap-webmessenger/protocol-sync';
|
import { UserInfo } from '@ucap-webmessenger/protocol-sync';
|
||||||
import {
|
import { UserInfoF } from '@ucap-webmessenger/protocol-query';
|
||||||
UserInfoSS,
|
import { FileUploadItem } from '@ucap-webmessenger/api-common';
|
||||||
UserInfoF,
|
|
||||||
UserInfoDN
|
|
||||||
} from '@ucap-webmessenger/protocol-query';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ucap-profile-profile',
|
selector: 'ucap-profile-profile',
|
||||||
|
@ -37,6 +42,14 @@ export class ProfileComponent implements OnInit {
|
||||||
isBuddy: boolean;
|
isBuddy: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
uploadProfileImage = new EventEmitter<FileUploadItem>();
|
||||||
|
|
||||||
|
@ViewChild('profileImageFileInput', { static: false })
|
||||||
|
profileImageFileInput: ElementRef<HTMLInputElement>;
|
||||||
|
|
||||||
|
profileImageFileUploadItem: FileUploadItem;
|
||||||
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
ngOnInit() {}
|
ngOnInit() {}
|
||||||
|
@ -73,4 +86,14 @@ export class ProfileComponent implements OnInit {
|
||||||
isBuddy: false
|
isBuddy: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onChangeFileInput() {
|
||||||
|
this.profileImageFileUploadItem = FileUploadItem.fromFiles(
|
||||||
|
this.profileImageFileInput.nativeElement.files
|
||||||
|
)[0];
|
||||||
|
|
||||||
|
this.uploadProfileImage.emit(this.profileImageFileUploadItem);
|
||||||
|
|
||||||
|
this.profileImageFileInput.nativeElement.value = '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { FlexLayoutModule } from '@angular/flex-layout';
|
||||||
import { MatRippleModule, MatCheckboxModule } from '@angular/material';
|
import { MatRippleModule, MatCheckboxModule } from '@angular/material';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||||
|
|
||||||
import { UCapUiModule } from '@ucap-webmessenger/ui';
|
import { UCapUiModule } from '@ucap-webmessenger/ui';
|
||||||
|
|
||||||
|
@ -35,6 +36,7 @@ const SERVICES = [];
|
||||||
MatCheckboxModule,
|
MatCheckboxModule,
|
||||||
MatCardModule,
|
MatCardModule,
|
||||||
MatTooltipModule,
|
MatTooltipModule,
|
||||||
|
MatProgressSpinnerModule,
|
||||||
|
|
||||||
UCapUiModule
|
UCapUiModule
|
||||||
],
|
],
|
||||||
|
|
|
@ -5,15 +5,19 @@ import {
|
||||||
Output,
|
Output,
|
||||||
Input,
|
Input,
|
||||||
AfterViewInit,
|
AfterViewInit,
|
||||||
OnInit
|
OnInit,
|
||||||
|
OnChanges,
|
||||||
|
SimpleChanges
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import { NGXLogger } from 'ngx-logger';
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
|
||||||
|
const PATH = 'path';
|
||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: 'img[ucapImage]'
|
selector: 'img[ucapImage]'
|
||||||
})
|
})
|
||||||
export class ImageDirective implements OnInit, AfterViewInit {
|
export class ImageDirective implements OnInit, AfterViewInit, OnChanges {
|
||||||
@Input()
|
@Input()
|
||||||
base: string;
|
base: string;
|
||||||
|
|
||||||
|
@ -50,6 +54,18 @@ export class ImageDirective implements OnInit, AfterViewInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
|
this.loadImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
const pathChanges = changes[PATH];
|
||||||
|
|
||||||
|
if (!!pathChanges && !pathChanges.firstChange) {
|
||||||
|
this.loadImage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadImage(): void {
|
||||||
if (this.imageSrc === this.default) {
|
if (this.imageSrc === this.default) {
|
||||||
this.elementRef.nativeElement.src = this.default;
|
this.elementRef.nativeElement.src = this.default;
|
||||||
this.loaded.emit(this.elementRef.nativeElement);
|
this.loaded.emit(this.elementRef.nativeElement);
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { BottomSheetService } from './bottom-sheet.service';
|
import { SnackBarService } from './snack-bar.service';
|
||||||
|
|
||||||
describe('ui::BottomSheetService', () => {
|
describe('ui::SnackBarService', () => {
|
||||||
beforeEach(() => TestBed.configureTestingModule({}));
|
beforeEach(() => TestBed.configureTestingModule({}));
|
||||||
|
|
||||||
it('should be created', () => {
|
it('should be created', () => {
|
||||||
const service: BottomSheetService = TestBed.get(BottomSheetService);
|
const service: SnackBarService = TestBed.get(SnackBarService);
|
||||||
expect(service).toBeTruthy();
|
expect(service).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue
Block a user