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 {
|
||||
APIRequest,
|
||||
APIResponse,
|
||||
APIEncoder,
|
||||
APIDecoder,
|
||||
ParameterUtil
|
||||
ParameterUtil,
|
||||
APIFormDataEncoder,
|
||||
JsonAnalization,
|
||||
StatusCode
|
||||
} from '@ucap-webmessenger/api';
|
||||
import { FileUploadItem } from '../models/file-upload-item';
|
||||
|
||||
export interface FileProfileSaveRequest extends APIRequest {
|
||||
userSeq: number;
|
||||
deviceType: DeviceType;
|
||||
token: string;
|
||||
file?: File;
|
||||
fileUploadItem: FileUploadItem;
|
||||
intro?: string;
|
||||
initProfileImage?: boolean;
|
||||
}
|
||||
|
||||
export interface FileProfileSaveResponse extends APIResponse {
|
||||
ProfileURL?: string;
|
||||
ProfileSubDir?: string;
|
||||
profileURL?: 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
|
||||
) => {
|
||||
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> = (
|
||||
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,
|
||||
fileProfileSaveUrl?: string
|
||||
): Observable<FileProfileSaveResponse> {
|
||||
return this.httpClient
|
||||
.post<any>(
|
||||
!!fileProfileSaveUrl ? fileProfileSaveUrl : this.urls.fileProfileSave,
|
||||
{},
|
||||
{
|
||||
params: encodeFileProfileSave(req)
|
||||
const httpReq = new HttpRequest(
|
||||
'POST',
|
||||
!!fileProfileSaveUrl ? fileProfileSaveUrl : this.urls.fileProfileSave,
|
||||
encodeFileProfileSave(req),
|
||||
{ reportProgress: true, responseType: 'text' as 'json' }
|
||||
);
|
||||
|
||||
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));
|
||||
}
|
||||
)
|
||||
.pipe(map(res => decodeFileProfileSave(res)));
|
||||
return false;
|
||||
}),
|
||||
map((event: HttpResponse<any>) => {
|
||||
req.fileUploadItem.uploadComplete();
|
||||
return decodeFileProfileSave(event.body);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public urlForFileTalkDownload(
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
CreateChatDialogData,
|
||||
CreateChatDialogResult
|
||||
} 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 * as AppStore from '@app/store';
|
||||
|
@ -28,8 +28,7 @@ import {
|
|||
UserInfoF,
|
||||
UserInfoDN
|
||||
} from '@ucap-webmessenger/protocol-query';
|
||||
import { MatTabChangeEvent, MatTabGroup } from '@angular/material';
|
||||
import { RightDrawer } from '@app/types';
|
||||
import { MatTabChangeEvent } from '@angular/material';
|
||||
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
|
||||
import { SessionStorageService } from '@ucap-webmessenger/web-storage';
|
||||
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 { DeviceType } from '@ucap-webmessenger/core';
|
||||
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 {
|
||||
EditMessageDialogComponent,
|
||||
|
@ -87,6 +86,7 @@ export class LeftSideComponent implements OnInit, OnDestroy {
|
|||
|
||||
sessionVerinfo: VersionInfo2Response;
|
||||
loginRes: LoginResponse;
|
||||
loginResSubscription: Subscription;
|
||||
|
||||
constructor(
|
||||
private store: Store<any>,
|
||||
|
@ -95,9 +95,6 @@ export class LeftSideComponent implements OnInit, OnDestroy {
|
|||
private messageApiService: MessageApiService,
|
||||
private logger: NGXLogger
|
||||
) {
|
||||
this.loginRes = this.sessionStorageService.get<LoginResponse>(
|
||||
KEY_LOGIN_RES_INFO
|
||||
);
|
||||
this.sessionVerinfo = this.sessionStorageService.get<VersionInfo2Response>(
|
||||
KEY_VER_INFO
|
||||
);
|
||||
|
@ -112,6 +109,15 @@ export class LeftSideComponent implements OnInit, OnDestroy {
|
|||
this.badgeChatUnReadCount = count;
|
||||
});
|
||||
|
||||
this.loginResSubscription = this.store
|
||||
.pipe(
|
||||
select(AppStore.AccountSelector.AuthenticationSelector.loginRes),
|
||||
tap(loginRes => {
|
||||
this.loginRes = loginRes;
|
||||
})
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
this.getMessageUnreadCount();
|
||||
this.badgeMessageInterval = setInterval(
|
||||
() => this.getMessageUnreadCount(),
|
||||
|
@ -129,6 +135,9 @@ export class LeftSideComponent implements OnInit, OnDestroy {
|
|||
if (!!this.badgeMessageUnReadCountSubscription) {
|
||||
this.badgeMessageUnReadCountSubscription.unsubscribe();
|
||||
}
|
||||
if (!!this.loginResSubscription) {
|
||||
this.loginResSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
if (!!this.badgeMessageInterval) {
|
||||
clearInterval(this.badgeMessageInterval);
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
<mat-card-content>
|
||||
<div fxFlex class="setting-tab">
|
||||
<ucap-account-change-password
|
||||
[loginId]="data.loginId"
|
||||
[phoneNumber]="data.phoneNumber"
|
||||
[encryptedLoginPw]="data.encryptedLoginPw"
|
||||
(changePassword)="onChangePassword($event)"
|
||||
></ucap-account-change-password>
|
||||
</div>
|
||||
|
|
|
@ -1,24 +1,18 @@
|
|||
import { Component, OnInit, Inject, OnDestroy } from '@angular/core';
|
||||
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 { 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';
|
||||
import CryptoJS from 'crypto-js';
|
||||
|
||||
export interface ChangePasswordDialogData {}
|
||||
export interface ChangePasswordDialogData {
|
||||
loginId: string;
|
||||
phoneNumber?: string;
|
||||
encryptedLoginPw?: string;
|
||||
}
|
||||
|
||||
export interface ChangePasswordDialogResult {
|
||||
choice: boolean;
|
||||
currentLoginPw?: string;
|
||||
newLoginPw?: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
|
@ -27,170 +21,25 @@ export interface ChangePasswordDialogResult {
|
|||
styleUrls: ['./change-password.dialog.component.scss']
|
||||
})
|
||||
export class ChangePasswordDialogComponent implements OnInit, OnDestroy {
|
||||
loginRes: LoginResponse;
|
||||
sessionVerinfo: VersionInfo2Response;
|
||||
isMe: boolean;
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<
|
||||
ChangePasswordDialogData,
|
||||
ChangePasswordDialogResult
|
||||
>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: ChangePasswordDialogData,
|
||||
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
|
||||
);
|
||||
}
|
||||
private dialogService: DialogService
|
||||
) {}
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
ngOnDestroy(): void {}
|
||||
|
||||
onChangePassword(info: {
|
||||
currentLoginPw: string;
|
||||
newLoginPw: string;
|
||||
newConfirmLoginPw: string;
|
||||
notValid: () => void;
|
||||
}): 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 });
|
||||
onChangePassword(info: { currentLoginPw: string; newLoginPw: string }): void {
|
||||
this.dialogRef.close({
|
||||
choice: true,
|
||||
currentLoginPw: info.currentLoginPw,
|
||||
newLoginPw: info.newLoginPw
|
||||
});
|
||||
}
|
||||
|
||||
onClickChoice(choice: boolean): void {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<ucap-profile-profile
|
||||
[userInfo]="data.userInfo"
|
||||
[userInfo]="userInfo"
|
||||
[profileImageRoot]="sessionVerinfo.profileRoot"
|
||||
[isMe]="isMe"
|
||||
[isBuddy]="isBuddy"
|
||||
|
@ -7,5 +7,6 @@
|
|||
(openChat)="onClickChat($event)"
|
||||
(toggleFavorit)="onClickToggleFavorit($event)"
|
||||
(toggleBuddy)="onClickToggleBuddy($event)"
|
||||
(uploadProfileImage)="onUploadProfileImage($event)"
|
||||
>
|
||||
</ucap-profile-profile>
|
||||
|
|
|
@ -8,6 +8,7 @@ import { Store, select } from '@ngrx/store';
|
|||
import * as AppStore from '@app/store';
|
||||
import * as ChatStore from '@app/store/messenger/chat';
|
||||
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 {
|
||||
|
@ -15,12 +16,28 @@ import {
|
|||
UserInfoF,
|
||||
UserInfoDN
|
||||
} 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 { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { map, take, finalize } from 'rxjs/operators';
|
||||
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 {
|
||||
userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN;
|
||||
|
@ -34,8 +51,11 @@ export interface ProfileDialogResult {}
|
|||
styleUrls: ['./profile.dialog.component.scss']
|
||||
})
|
||||
export class ProfileDialogComponent implements OnInit, OnDestroy {
|
||||
userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN;
|
||||
loginRes: LoginResponse;
|
||||
sessionVerinfo: VersionInfo2Response;
|
||||
environmentsInfo: EnvironmentsInfo;
|
||||
|
||||
isMe: boolean;
|
||||
isBuddy: boolean;
|
||||
isFavorit: boolean;
|
||||
|
@ -47,6 +67,8 @@ export class ProfileDialogComponent implements OnInit, OnDestroy {
|
|||
@Inject(MAT_DIALOG_DATA) public data: ProfileDialogData,
|
||||
private dialogService: DialogService,
|
||||
private sessionStorageService: SessionStorageService,
|
||||
private commonApiService: CommonApiService,
|
||||
private snackBarService: SnackBarService,
|
||||
private store: Store<any>
|
||||
) {
|
||||
this.sessionVerinfo = this.sessionStorageService.get<VersionInfo2Response>(
|
||||
|
@ -55,6 +77,11 @@ export class ProfileDialogComponent implements OnInit, OnDestroy {
|
|||
this.loginRes = this.sessionStorageService.get<LoginResponse>(
|
||||
KEY_LOGIN_RES_INFO
|
||||
);
|
||||
this.environmentsInfo = this.sessionStorageService.get<EnvironmentsInfo>(
|
||||
KEY_ENVIRONMENTS_INFO
|
||||
);
|
||||
|
||||
this.userInfo = data.userInfo;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
@ -156,8 +183,68 @@ export class ProfileDialogComponent implements OnInit, OnDestroy {
|
|||
this.store.dispatch(
|
||||
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, {
|
||||
width: '360px',
|
||||
data: {
|
||||
title: 'Alert',
|
||||
title: '로그인',
|
||||
html: `아이디 또는 패스워드가<br/>일치하지 않습니다.`
|
||||
}
|
||||
});
|
||||
|
@ -83,7 +83,7 @@ export class LoginPageComponent implements OnInit, OnDestroy {
|
|||
>(AlertDialogComponent, {
|
||||
width: '360px',
|
||||
data: {
|
||||
title: 'Alert',
|
||||
title: '로그인',
|
||||
html: `비밀번호 오류 횟수 초과입니다.<br/>비밀번호를 확인하신 후<br/>잠시 후 다시 시작해 주세요.`
|
||||
}
|
||||
});
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
|
||||
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 {
|
||||
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 { environment } from '../../environments/environment';
|
||||
import { SnackBarService } from '@ucap-webmessenger/ui';
|
||||
|
||||
@Injectable()
|
||||
export class AppMessengerResolver implements Resolve<void> {
|
||||
|
@ -63,6 +64,7 @@ export class AppMessengerResolver implements Resolve<void> {
|
|||
private optionProtocolService: OptionProtocolService,
|
||||
private authenticationProtocolService: AuthenticationProtocolService,
|
||||
private innerProtocolService: InnerProtocolService,
|
||||
private snackBarService: SnackBarService,
|
||||
private logger: NGXLogger
|
||||
) {}
|
||||
|
||||
|
@ -101,25 +103,38 @@ export class AppMessengerResolver implements Resolve<void> {
|
|||
}),
|
||||
switchMap(() => this.innerProtocolService.conn({})),
|
||||
switchMap(res => {
|
||||
return this.authenticationProtocolService.login({
|
||||
loginId: loginInfo.loginId,
|
||||
loginPw: loginInfo.loginPw,
|
||||
deviceType: environmentsInfo.deviceType,
|
||||
deviceId: ' ',
|
||||
token: '',
|
||||
localeCode: loginInfo.localeCode,
|
||||
pushId: ' ',
|
||||
companyCode: loginInfo.companyCode,
|
||||
passwordEncodingType: 1,
|
||||
clientVersion: '',
|
||||
reconnect: false,
|
||||
ip: 'localhost',
|
||||
hostName: '',
|
||||
ssoMode: SSOMode.AUTH,
|
||||
userSpecificInformation: 'PRO_000482',
|
||||
productId: environment.productConfig.productId,
|
||||
productName: environment.productConfig.productName
|
||||
});
|
||||
return this.authenticationProtocolService
|
||||
.login({
|
||||
loginId: loginInfo.loginId,
|
||||
loginPw: loginInfo.loginPw,
|
||||
deviceType: environmentsInfo.deviceType,
|
||||
deviceId: ' ',
|
||||
token: '',
|
||||
localeCode: loginInfo.localeCode,
|
||||
pushId: ' ',
|
||||
companyCode: loginInfo.companyCode,
|
||||
passwordEncodingType: 1,
|
||||
clientVersion: '',
|
||||
reconnect: false,
|
||||
ip: 'localhost',
|
||||
hostName: '',
|
||||
ssoMode: SSOMode.AUTH,
|
||||
userSpecificInformation: 'PRO_000482',
|
||||
productId: environment.productConfig.productId,
|
||||
productName: environment.productConfig.productName
|
||||
})
|
||||
.pipe(
|
||||
catchError(err => {
|
||||
switch (err as ServerErrorCode) {
|
||||
case ServerErrorCode.ERRCD_IDPW:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return throwError(err);
|
||||
})
|
||||
);
|
||||
}),
|
||||
switchMap(res => {
|
||||
loginRes = res;
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
|
||||
import CryptoJS from 'crypto-js';
|
||||
|
||||
import {
|
||||
SessionStorageService,
|
||||
LocalStorageService
|
||||
|
@ -10,6 +8,7 @@ import { LocaleCode } from '@ucap-webmessenger/core';
|
|||
import { LoginInfo, KEY_LOGIN_INFO } from '../types';
|
||||
import { KEY_VER_INFO } from '@app/types/ver-info.type';
|
||||
import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type';
|
||||
import { PasswordUtil } from '@ucap-webmessenger/pi';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
|
@ -33,7 +32,7 @@ export class AppAuthenticationService {
|
|||
this.sessionStorageService.set<LoginInfo>(KEY_LOGIN_INFO, {
|
||||
...loginInfo,
|
||||
initPw: loginInfo.loginId === loginInfo.loginPw,
|
||||
loginPw: CryptoJS.enc.Hex.stringify(CryptoJS.SHA256(loginInfo.loginPw))
|
||||
loginPw: PasswordUtil.encrypt(loginInfo.loginPw)
|
||||
});
|
||||
|
||||
if (rememberMe) {
|
||||
|
@ -51,11 +50,4 @@ export class AppAuthenticationService {
|
|||
this.sessionStorageService.remove(KEY_VER_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(
|
||||
'[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,
|
||||
ConfirmDialogComponent,
|
||||
ConfirmDialogData,
|
||||
ConfirmDialogResult
|
||||
ConfirmDialogResult,
|
||||
SnackBarService,
|
||||
AlertDialogComponent,
|
||||
AlertDialogData,
|
||||
AlertDialogResult
|
||||
} from '@ucap-webmessenger/ui';
|
||||
|
||||
import {
|
||||
|
@ -34,7 +38,10 @@ import {
|
|||
privacyAgreeSuccess,
|
||||
increaseLoginFailCount,
|
||||
initialLoginFailCount,
|
||||
logoutInitialize
|
||||
logoutInitialize,
|
||||
userPasswordSet,
|
||||
userPasswordSetSuccess,
|
||||
userPasswordSetFailure
|
||||
} from './actions';
|
||||
import {
|
||||
LoginInfo,
|
||||
|
@ -60,6 +67,10 @@ import {
|
|||
ChangePasswordDialogData,
|
||||
ChangePasswordDialogResult
|
||||
} from '@app/layouts/messenger/dialogs/account/change-password.dialog.component';
|
||||
import {
|
||||
ServiceProtocolService,
|
||||
UserPasswordSetResponse
|
||||
} from '@ucap-webmessenger/protocol-service';
|
||||
|
||||
@Injectable()
|
||||
export class Effects {
|
||||
|
@ -273,10 +284,24 @@ export class Effects {
|
|||
width: '500px',
|
||||
height: '500px',
|
||||
disableClose: false,
|
||||
data: {}
|
||||
data: {
|
||||
loginId: loginInfo.loginId,
|
||||
encryptedLoginPw: loginInfo.loginPw,
|
||||
phoneNumber: loginRes.userInfo.hpNumber
|
||||
}
|
||||
});
|
||||
|
||||
if (!!result && result.choice) {
|
||||
this.store.dispatch(
|
||||
userPasswordSet({
|
||||
req: {
|
||||
companyCode: loginInfo.companyCode,
|
||||
loginId: loginInfo.loginId,
|
||||
oldLoginPw: result.currentLoginPw,
|
||||
newLoginPw: result.newLoginPw
|
||||
}
|
||||
})
|
||||
);
|
||||
} else {
|
||||
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(
|
||||
private actions$: Actions,
|
||||
private ngZone: NgZone,
|
||||
|
@ -334,8 +420,9 @@ export class Effects {
|
|||
private appAuthenticationService: AppAuthenticationService,
|
||||
private protocolService: ProtocolService,
|
||||
private authenticationProtocolService: AuthenticationProtocolService,
|
||||
|
||||
private serviceProtocolService: ServiceProtocolService,
|
||||
private dialogService: DialogService,
|
||||
private snackBarService: SnackBarService,
|
||||
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
|
||||
private logger: NGXLogger
|
||||
) {}
|
||||
|
|
|
@ -5,7 +5,8 @@ import {
|
|||
increaseLoginFailCount,
|
||||
initialLoginFailCount,
|
||||
logout,
|
||||
logoutInitialize
|
||||
logoutInitialize,
|
||||
updateLoginRes
|
||||
} from './actions';
|
||||
|
||||
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) => {
|
||||
return {
|
||||
...state,
|
||||
|
|
|
@ -6,7 +6,6 @@ import * as EventStore from './event';
|
|||
import * as OptionStore from './option';
|
||||
import * as QueryStore from './query';
|
||||
import * as RoomStore from './room';
|
||||
import * as ServiceStore from './service';
|
||||
import * as StatusStore from './status';
|
||||
import * as SyncStore from './sync';
|
||||
import * as SettingsStore from './settings';
|
||||
|
@ -17,7 +16,6 @@ export interface State {
|
|||
option: OptionStore.State;
|
||||
query: QueryStore.State;
|
||||
room: RoomStore.State;
|
||||
service: ServiceStore.State;
|
||||
status: StatusStore.State;
|
||||
sync: SyncStore.State;
|
||||
settings: SettingsStore.State;
|
||||
|
@ -29,7 +27,6 @@ export const effects: Type<any>[] = [
|
|||
OptionStore.Effects,
|
||||
QueryStore.Effects,
|
||||
RoomStore.Effects,
|
||||
ServiceStore.Effects,
|
||||
StatusStore.Effects,
|
||||
SyncStore.Effects,
|
||||
SettingsStore.Effects
|
||||
|
@ -42,7 +39,6 @@ export function reducers(state: State | undefined, action: Action) {
|
|||
option: OptionStore.reducer,
|
||||
query: QueryStore.reducer,
|
||||
room: RoomStore.reducer,
|
||||
service: ServiceStore.reducer,
|
||||
status: StatusStore.reducer,
|
||||
sync: SyncStore.reducer,
|
||||
settings: SettingsStore.reducer
|
||||
|
@ -66,9 +62,6 @@ export function selectors<S>(selector: Selector<any, State>) {
|
|||
QuerySelector: QueryStore.selectors(
|
||||
createSelector(selector, (state: State) => state.query)
|
||||
),
|
||||
ServiceSelector: ServiceStore.selectors(
|
||||
createSelector(selector, (state: State) => state.service)
|
||||
),
|
||||
StatusSelector: StatusStore.selectors(
|
||||
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/utils/password.util';
|
||||
|
||||
export * from './lib/ucap-pi.module';
|
||||
|
||||
export * from './lib/config/urls';
|
||||
|
|
|
@ -10,16 +10,17 @@ import { NGXLogger } from 'ngx-logger';
|
|||
import {
|
||||
makeWebSocketObservable,
|
||||
GetWebSocketResponses,
|
||||
NormalClosureMessage,
|
||||
NormalClosureMessage
|
||||
} from '@ucap-webmessenger/web-socket';
|
||||
|
||||
import { PacketBody } from '../protocols/packet';
|
||||
import {
|
||||
PacketBodyValueDivider,
|
||||
PacketBodyDivider,
|
||||
PacketBodyDivider
|
||||
} from '../types/packet-body-divider';
|
||||
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 { _MODULE_CONFIG } from '../config/token';
|
||||
|
@ -37,7 +38,7 @@ interface RequestState {
|
|||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ProtocolService {
|
||||
readonly urls: Urls;
|
||||
|
@ -186,7 +187,7 @@ export class ProtocolService {
|
|||
|
||||
packet = this.encodePacket(serviceType, subServiceType, [
|
||||
...bodyList,
|
||||
{ type: PacketBodyValue.RequestId, value: requestId },
|
||||
{ type: PacketBodyValue.RequestId, value: requestId }
|
||||
]);
|
||||
|
||||
responseSubject = new Subject<ProtocolMessage>().pipe(
|
||||
|
@ -209,7 +210,7 @@ export class ProtocolService {
|
|||
|
||||
this.pendingRequests.set(requestId, {
|
||||
subject: responseSubject,
|
||||
request: { serviceType, subServiceType, bodyList },
|
||||
request: { serviceType, subServiceType, bodyList }
|
||||
});
|
||||
} else {
|
||||
packet = this.encodePacket(serviceType, subServiceType, bodyList);
|
||||
|
@ -302,8 +303,8 @@ export class ProtocolService {
|
|||
serviceType,
|
||||
subServiceType,
|
||||
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 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-value.type';
|
||||
export * from './lib/types/service';
|
||||
export * from './lib/types/error-code';
|
||||
|
||||
export * from './lib/ucap-protocol.module';
|
||||
|
||||
|
|
|
@ -5,43 +5,134 @@
|
|||
<input
|
||||
matInput
|
||||
type="password"
|
||||
formControlName="currentLoginPw"
|
||||
#currentLoginPw
|
||||
[formControl]="currentLoginPwFormControl"
|
||||
/>
|
||||
<mat-error>
|
||||
현재 패스워드를 입력해 주세요
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<mat-label>신규 패스워드</mat-label>
|
||||
<input
|
||||
matInput
|
||||
type="password"
|
||||
formControlName="newLoginPw"
|
||||
#newLoginPw
|
||||
/>
|
||||
<mat-error>
|
||||
신규 패스워드를 입력해 주세요
|
||||
</mat-error>
|
||||
<input matInput type="password" [formControl]="newLoginPwFormControl" />
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<mat-label>신규 패스워드 확인</mat-label>
|
||||
<input
|
||||
matInput
|
||||
type="password"
|
||||
formControlName="newConfirmLoginPw"
|
||||
#newConfirmLoginPw
|
||||
[formControl]="newConfirmLoginPwFormControl"
|
||||
/>
|
||||
<mat-error>
|
||||
신규 패스워드 확인을 입력해 주세요
|
||||
</mat-error>
|
||||
</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
|
||||
mat-raised-button
|
||||
class="submit-button bg-accent-dark"
|
||||
aria-label="패스워드 변경"
|
||||
[disabled]="changePasswordForm.invalid || !changePasswordBtnEnable"
|
||||
[disabled]="changePasswordForm.invalid"
|
||||
(click)="onClickChangePassword()"
|
||||
>
|
||||
패스워드 변경
|
||||
|
|
|
@ -17,6 +17,11 @@
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.error-container {
|
||||
height: 80px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.submit-button {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
|
|
|
@ -8,10 +8,66 @@ import {
|
|||
ElementRef,
|
||||
ChangeDetectorRef
|
||||
} from '@angular/core';
|
||||
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
||||
import { Company } from '@ucap-webmessenger/api-external';
|
||||
import { LocalStorageService } from '@ucap-webmessenger/web-storage';
|
||||
import { LoginInfo, KEY_LOGIN_INFO } from '@app/types';
|
||||
import {
|
||||
FormControl,
|
||||
FormGroup,
|
||||
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({
|
||||
selector: 'ucap-account-change-password',
|
||||
|
@ -20,48 +76,81 @@ import { LoginInfo, KEY_LOGIN_INFO } from '@app/types';
|
|||
})
|
||||
export class ChangePasswordComponent implements OnInit {
|
||||
@Input()
|
||||
changePasswordBtnEnable: boolean;
|
||||
loginId: string;
|
||||
|
||||
@Input()
|
||||
phoneNumber: string;
|
||||
|
||||
@Input()
|
||||
encryptedLoginPw: string;
|
||||
|
||||
@Output()
|
||||
changePassword = new EventEmitter<{
|
||||
currentLoginPw: string;
|
||||
newLoginPw: string;
|
||||
newConfirmLoginPw: string;
|
||||
notValid: () => void;
|
||||
}>();
|
||||
|
||||
@ViewChild('currentLoginPw', { static: true })
|
||||
currentLoginPwElementRef: ElementRef;
|
||||
@ViewChild('newLoginPw', { static: true })
|
||||
newLoginPwElementRef: ElementRef;
|
||||
|
||||
changePasswordForm: FormGroup;
|
||||
currentLoginPwFormControl = new FormControl('');
|
||||
newLoginPwFormControl = new FormControl('');
|
||||
newConfirmLoginPwFormControl = new FormControl('');
|
||||
|
||||
PasswordValidationResult = PasswordValidationResult;
|
||||
|
||||
constructor(
|
||||
private formBuilder: FormBuilder,
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private localStorageService: LocalStorageService
|
||||
private changeDetectorRef: ChangeDetectorRef
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
const loginInfo: LoginInfo = this.localStorageService.get<LoginInfo>(
|
||||
KEY_LOGIN_INFO
|
||||
const currentLoginPwValidators: ValidatorFn[] = [Validators.required];
|
||||
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({
|
||||
currentLoginPw: ['', Validators.required],
|
||||
newLoginPw: ['', Validators.required],
|
||||
newConfirmLoginPw: ['', Validators.required]
|
||||
currentLoginPwFormControl: this.currentLoginPwFormControl,
|
||||
newLoginPwFormControl: this.newLoginPwFormControl,
|
||||
newConfirmLoginPwFormControl: this.newConfirmLoginPwFormControl
|
||||
});
|
||||
}
|
||||
|
||||
onClickChangePassword() {
|
||||
this.changePassword.emit({
|
||||
currentLoginPw: this.changePasswordForm.get('currentLoginPw').value,
|
||||
newLoginPw: this.changePasswordForm.get('newLoginPw').value,
|
||||
newConfirmLoginPw: this.changePasswordForm.get('newConfirmLoginPw').value,
|
||||
notValid: () => {
|
||||
this.currentLoginPwElementRef.nativeElement.focus();
|
||||
}
|
||||
currentLoginPw: PasswordUtil.encrypt(
|
||||
this.currentLoginPwFormControl.value
|
||||
),
|
||||
newLoginPw: PasswordUtil.encrypt(this.newLoginPwFormControl.value)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,39 @@
|
|||
[path]="userInfo.profileImageFile"
|
||||
[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 *ngIf="!isMe" class="profile-option">
|
||||
|
|
|
@ -12,12 +12,12 @@
|
|||
word-wrap: break-word;
|
||||
}
|
||||
}
|
||||
::ng-deep .mat-card-header-text{
|
||||
width:100%;
|
||||
.mat-card-subtitle{
|
||||
::ng-deep .mat-card-header-text {
|
||||
width: 100%;
|
||||
.mat-card-subtitle {
|
||||
color: rgb(256, 256, 256, 0.7) !important;
|
||||
text-align:center;
|
||||
margin-top:10px !important;
|
||||
text-align: center;
|
||||
margin-top: 10px !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,78 +25,90 @@
|
|||
width: 400px;
|
||||
padding: 0 0 20px;
|
||||
position: relative;
|
||||
.mat-card-header{
|
||||
.mat-card-header {
|
||||
justify-content: center;
|
||||
padding-bottom: 40px;
|
||||
background: #76d9c5;
|
||||
/*background: linear-gradient(to right, #345385, #ef4c73);*/
|
||||
color: #ffffff;
|
||||
padding-top: 20px;
|
||||
width:100%;
|
||||
.mat-card-title{
|
||||
width: 100%;
|
||||
.mat-card-title {
|
||||
margin-bottom: 12px;
|
||||
max-width: 100%;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
margin:0 20px;
|
||||
span{
|
||||
margin: 0 20px;
|
||||
span {
|
||||
@include ellipsis(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
.mat-card-content{
|
||||
margin-top:-40px;
|
||||
.profile-img{
|
||||
display:flex;
|
||||
height:80px;
|
||||
.mat-card-content {
|
||||
margin-top: -40px;
|
||||
.profile-img {
|
||||
display: flex;
|
||||
height: 80px;
|
||||
justify-content: center;
|
||||
margin-bottom:20px;
|
||||
img{
|
||||
margin-bottom: 20px;
|
||||
img {
|
||||
widows: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
background-color:#efefef;
|
||||
border:2px solid #ffffff;
|
||||
background-color: #efefef;
|
||||
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{
|
||||
display:flex;
|
||||
padding:0 20px;
|
||||
color:#ffffff;
|
||||
.profile-option {
|
||||
display: flex;
|
||||
padding: 0 20px;
|
||||
color: #ffffff;
|
||||
margin-top: -100px;
|
||||
height: 120px;
|
||||
.btn-favorite{
|
||||
.btn-favorite {
|
||||
cursor: pointer;
|
||||
.on{
|
||||
fill:yellow;
|
||||
.on {
|
||||
fill: yellow;
|
||||
}
|
||||
}
|
||||
.btn-groupadd{
|
||||
margin-left:auto;
|
||||
.btn-groupadd {
|
||||
margin-left: auto;
|
||||
cursor: pointer;
|
||||
svg{
|
||||
display:none;
|
||||
&.on{
|
||||
display:block;
|
||||
svg {
|
||||
display: none;
|
||||
&.on {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ul{
|
||||
padding:0 20px;
|
||||
display:flex;
|
||||
ul {
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
margin-top:-20px;
|
||||
li{
|
||||
display:inline-flex;
|
||||
height:30px;
|
||||
margin-top: -20px;
|
||||
li {
|
||||
display: inline-flex;
|
||||
height: 30px;
|
||||
align-items: center;
|
||||
flex-flow:row;
|
||||
margin-bottom:20px;
|
||||
flex-flow: row;
|
||||
margin-bottom: 20px;
|
||||
|
||||
svg{
|
||||
margin-right:10px;
|
||||
color:#777777;
|
||||
svg {
|
||||
margin-right: 10px;
|
||||
color: #777777;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
UserInfoSS,
|
||||
UserInfoF,
|
||||
UserInfoDN
|
||||
} from '@ucap-webmessenger/protocol-query';
|
||||
import { UserInfoF } from '@ucap-webmessenger/protocol-query';
|
||||
import { FileUploadItem } from '@ucap-webmessenger/api-common';
|
||||
|
||||
@Component({
|
||||
selector: 'ucap-profile-profile',
|
||||
|
@ -37,6 +42,14 @@ export class ProfileComponent implements OnInit {
|
|||
isBuddy: boolean;
|
||||
}>();
|
||||
|
||||
@Output()
|
||||
uploadProfileImage = new EventEmitter<FileUploadItem>();
|
||||
|
||||
@ViewChild('profileImageFileInput', { static: false })
|
||||
profileImageFileInput: ElementRef<HTMLInputElement>;
|
||||
|
||||
profileImageFileUploadItem: FileUploadItem;
|
||||
|
||||
constructor() {}
|
||||
|
||||
ngOnInit() {}
|
||||
|
@ -73,4 +86,14 @@ export class ProfileComponent implements OnInit {
|
|||
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 { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
|
||||
import { UCapUiModule } from '@ucap-webmessenger/ui';
|
||||
|
||||
|
@ -35,6 +36,7 @@ const SERVICES = [];
|
|||
MatCheckboxModule,
|
||||
MatCardModule,
|
||||
MatTooltipModule,
|
||||
MatProgressSpinnerModule,
|
||||
|
||||
UCapUiModule
|
||||
],
|
||||
|
|
|
@ -5,15 +5,19 @@ import {
|
|||
Output,
|
||||
Input,
|
||||
AfterViewInit,
|
||||
OnInit
|
||||
OnInit,
|
||||
OnChanges,
|
||||
SimpleChanges
|
||||
} from '@angular/core';
|
||||
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
|
||||
const PATH = 'path';
|
||||
|
||||
@Directive({
|
||||
selector: 'img[ucapImage]'
|
||||
})
|
||||
export class ImageDirective implements OnInit, AfterViewInit {
|
||||
export class ImageDirective implements OnInit, AfterViewInit, OnChanges {
|
||||
@Input()
|
||||
base: string;
|
||||
|
||||
|
@ -50,6 +54,18 @@ export class ImageDirective implements OnInit, AfterViewInit {
|
|||
}
|
||||
|
||||
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) {
|
||||
this.elementRef.nativeElement.src = this.default;
|
||||
this.loaded.emit(this.elementRef.nativeElement);
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
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({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: BottomSheetService = TestBed.get(BottomSheetService);
|
||||
const service: SnackBarService = TestBed.get(SnackBarService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
Block a user