From 05a148d30af96bb2d58bcb2ec22e497c21041fc9 Mon Sep 17 00:00:00 2001 From: leejinho Date: Fri, 6 Mar 2020 16:42:14 +0900 Subject: [PATCH] =?UTF-8?q?=ED=86=B5=ED=95=A9=EA=B2=80=EC=83=89=EC=97=90?= =?UTF-8?q?=20UX=20=EC=A0=81=EC=9A=A9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../organization-tree.component.ts | 2 +- .../main-contents/organization.component.html | 4 +- .../main-contents/organization.component.ts | 1 - .../integrated-search.dialog.component.html | 137 ++++++++- .../integrated-search.dialog.component.ts | 291 +++++++++++------- .../src/app/store/messenger/query/actions.ts | 28 +- .../src/app/store/messenger/query/effects.ts | 60 +++- .../src/app/store/messenger/query/reducers.ts | 39 ++- .../src/app/store/messenger/query/state.ts | 17 +- 9 files changed, 448 insertions(+), 131 deletions(-) diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/organization-tree.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/organization-tree.component.ts index eb947a7d..0712019c 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/organization-tree.component.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/organization-tree.component.ts @@ -152,7 +152,7 @@ export class OrganizationTreeComponent implements OnInit, OnDestroy { } /** 검색 취소 */ onClickCancel() { - this.store.dispatch(QueryStore.cancelSearchDeptUser({})); + this.store.dispatch(QueryStore.clearSearchDeptUser({})); } /** 조직도 부서 선택 */ diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/main-contents/organization.component.html b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/main-contents/organization.component.html index 42541ded..be74ead4 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/main-contents/organization.component.html +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/main-contents/organization.component.html @@ -8,7 +8,7 @@ >

- + + {{ 'common.searchResult' | translate }}({{ departmentUserInfoList.length }} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/main-contents/organization.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/main-contents/organization.component.ts index 86213048..96f90306 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/main-contents/organization.component.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/main-contents/organization.component.ts @@ -259,7 +259,6 @@ export class OrganizationComponent implements OnInit, OnDestroy { } /** Handling chipset for selectedUserList */ - /** 선택된 사용자 취소 */ onClickDeleteUser(userInfo: UserInfoSS) { this.selectedUserList = this.selectedUserList.filter( item => item.seq !== userInfo.seq diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/search/integrated-search.dialog.component.html b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/search/integrated-search.dialog.component.html index 7b9ae48a..6b55bd32 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/search/integrated-search.dialog.component.html +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/search/integrated-search.dialog.component.html @@ -12,20 +12,127 @@ - - +
+
+ + +
+
+
+ +
+
+
+
+ +
+ +
+ +
diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/search/integrated-search.dialog.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/search/integrated-search.dialog.component.ts index 9e30a661..6dd8c0c4 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/search/integrated-search.dialog.component.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/search/integrated-search.dialog.component.ts @@ -1,23 +1,25 @@ -import { Component, OnInit, Inject, OnDestroy, ViewChild } from '@angular/core'; +import { + Component, + OnInit, + Inject, + OnDestroy, + ChangeDetectorRef +} from '@angular/core'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; -import { KEY_VER_INFO } from '@app/types'; +import { KEY_VER_INFO, MainMenu, KEY_AUTH_INFO } from '@app/types'; import { SessionStorageService } from '@ucap-webmessenger/web-storage'; import { Store, select } from '@ngrx/store'; import * as AppStore from '@app/store'; -import * as StatusStore from '@app/store/messenger/status'; -import { - UserInfoSS, - QueryProtocolService, - DeptSearchType, - SSVC_TYPE_QUERY_DEPT_USER_DATA, - DeptUserData, - SSVC_TYPE_QUERY_DEPT_USER_RES, - DeptUserResponse -} from '@ucap-webmessenger/protocol-query'; +import * as QueryStore from '@app/store/messenger/query'; +import * as ChatStore from '@app/store/messenger/chat'; +import * as SettingsStore from '@app/store/messenger/settings'; +import * as SyncStore from '@app/store/messenger/sync'; + +import { UserInfoSS, AuthResponse } from '@ucap-webmessenger/protocol-query'; import { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; -import { map, catchError, take, tap } from 'rxjs/operators'; -import { Subscription, of, Observable } from 'rxjs'; +import { map, take, tap } from 'rxjs/operators'; +import { Subscription, Observable, BehaviorSubject } from 'rxjs'; import { EnvironmentsInfo, KEY_ENVIRONMENTS_INFO } from '@app/types'; import { NGXLogger } from 'ngx-logger'; import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native'; @@ -25,16 +27,20 @@ import { environment } from '../../../../../environments/environment'; import { StatusBulkInfo } from '@ucap-webmessenger/protocol-status'; import { VersionInfo2Response } from '@ucap-webmessenger/api-public'; import { DaesangProtocolService } from '@ucap-webmessenger/daesang'; -import { - DialogService, - IntegratedSearchComponent -} from '@ucap-webmessenger/ui'; +import { DialogService } from '@ucap-webmessenger/ui'; import { ProfileDialogComponent, ProfileDialogData, ProfileDialogResult } from '../profile/profile.dialog.component'; -import { PageEvent } from '@angular/material/paginator'; +import { + SelectGroupDialogComponent, + SelectGroupDialogData, + SelectGroupDialogResult +} from '../group/select-group.dialog.component'; +import { TranslateService } from '@ngx-translate/core'; +import { GroupDetailData } from '@ucap-webmessenger/protocol-sync'; +import { ConferenceService } from '@ucap-webmessenger/api-prompt'; export interface IntegratedSearchDialogData { keyword: string; @@ -52,19 +58,21 @@ export class IntegratedSearchDialogComponent implements OnInit, OnDestroy { loginResSubscription: Subscription; sessionVerinfo: VersionInfo2Response; environmentsInfo: EnvironmentsInfo; + authInfo: AuthResponse; - presence$: Observable; - searchSubscription: Subscription; - searchUserInfos: UserInfoSS[] = []; - searchingProcessing = false; + searchDepartmentUserInfoListSubscription: Subscription; + searchingProcessing$: Observable; + + departmentUserInfoList: UserInfoSS[] = []; + originDepartmentUserInfoList: UserInfoSS[] = []; + + selectedUserList: UserInfoSS[] = []; // selected user in departmentUserList detail + + profileImageRoot: string; + presenceSubscription: Subscription; + presenceSubject = new BehaviorSubject(undefined); currentSearchWord: string; - totalCount = 0; - pageCurrent = 1; - pageListCount = 20; - - @ViewChild('integratedSearch', { static: false }) - integratedSearchComponent: IntegratedSearchComponent; constructor( public dialogRef: MatDialogRef< @@ -73,22 +81,30 @@ export class IntegratedSearchDialogComponent implements OnInit, OnDestroy { >, @Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService, @Inject(MAT_DIALOG_DATA) public data: IntegratedSearchDialogData, - private queryProtocolService: QueryProtocolService, private sessionStorageService: SessionStorageService, private daesangProtocolService: DaesangProtocolService, private dialogService: DialogService, + private translateService: TranslateService, + private conferenceService: ConferenceService, + private store: Store, + private changeDetectorRef: ChangeDetectorRef, private logger: NGXLogger ) { this.environmentsInfo = this.sessionStorageService.get( KEY_ENVIRONMENTS_INFO ); + + this.authInfo = this.sessionStorageService.get(KEY_AUTH_INFO); + this.sessionVerinfo = this.sessionStorageService.get( KEY_VER_INFO ); } ngOnInit() { + this.profileImageRoot = this.sessionVerinfo.profileRoot; + this.loginResSubscription = this.store .pipe( select(AppStore.AccountSelector.AuthenticationSelector.loginRes), @@ -98,26 +114,48 @@ export class IntegratedSearchDialogComponent implements OnInit, OnDestroy { ) .subscribe(); - this.onSearch(this.data.keyword); + this.searchDepartmentUserInfoListSubscription = this.store + .pipe( + select( + AppStore.MessengerSelector.QuerySelector + .integrateSearchDepartmentUserInfoList + ) + ) + .subscribe(list => { + this.departmentUserInfoList = list; + this.originDepartmentUserInfoList = list; + }); - this.presence$ = this.store.pipe( - select(AppStore.MessengerSelector.StatusSelector.selectAllStatusBulkInfo) + this.searchingProcessing$ = this.store.pipe( + select( + AppStore.MessengerSelector.QuerySelector + .integrateSearchDepartmentProcessing + ) ); + + this.presenceSubscription = this.store + .pipe( + select( + AppStore.MessengerSelector.StatusSelector.selectAllStatusBulkInfo + ) + ) + .subscribe(presence => { + this.presenceSubject.next(presence); + }); + + this.onSearch(this.data.keyword); } ngOnDestroy(): void { - if (!!this.searchSubscription) { - this.searchSubscription.unsubscribe(); - } if (!!this.loginResSubscription) { this.loginResSubscription.unsubscribe(); } - } - - onReSearch(searchWord: string) { - this.pageCurrent = 1; - this.integratedSearchComponent.paginator.pageIndex = 0; - this.onSearch(searchWord); + if (!!this.searchDepartmentUserInfoListSubscription) { + this.searchDepartmentUserInfoListSubscription.unsubscribe(); + } + if (!!this.presenceSubscription) { + this.presenceSubscription.unsubscribe(); + } } onSearch(searchWord: string) { @@ -126,78 +164,122 @@ export class IntegratedSearchDialogComponent implements OnInit, OnDestroy { } if (searchWord.trim().length > 0) { - this.searchingProcessing = true; - const searchUserInfos: UserInfoSS[] = []; - - this.searchSubscription = this.queryProtocolService - .deptUser({ - divCd: 'INT_SRCH', + this.store.dispatch( + QueryStore.integrateSearchDeptUser({ companyCode: this.loginRes.companyCode, - searchRange: DeptSearchType.All, - search: searchWord.trim(), - senderCompanyCode: this.loginRes.companyCode, - senderEmployeeType: this.loginRes.userInfo.employeeType, - pageCurrent: this.pageCurrent, - pageListCount: this.pageListCount + search: searchWord.trim() }) - .pipe( - map(res => { - switch (res.SSVC_TYPE) { - case SSVC_TYPE_QUERY_DEPT_USER_DATA: - const userInfos = (res as DeptUserData).userInfos; - searchUserInfos.push(...userInfos); - break; - case SSVC_TYPE_QUERY_DEPT_USER_RES: - { - const response = res as DeptUserResponse; - - // 검색 결과 처리. - this.searchUserInfos = searchUserInfos.sort((a, b) => - a.name < b.name ? -1 : a.name > b.name ? 1 : 0 - ); - - this.totalCount = response.pageTotalCount; - this.pageCurrent = response.pageCurrent; - this.pageListCount = response.pageListCount; - this.searchingProcessing = false; - - // 검색 결과에 따른 프레즌스 조회. - if (searchUserInfos.length > 0) { - this.store.dispatch( - StatusStore.bulkInfo({ - divCd: 'INT_SRCH', - userSeqs: this.searchUserInfos.map(user => user.seq) - }) - ); - } - } - break; - } - }), - catchError(error => { - this.searchingProcessing = false; - return of(this.logger.error(error)); - }) - ) - .subscribe(); + ); } else { // clear list. - this.searchingProcessing = false; - this.searchUserInfos = []; + this.store.dispatch(QueryStore.integrateClearSearchDeptUser({})); } } - onCancel(): void { - this.dialogRef.close({}); + /** Selected User Handling */ + onToggleAllUser(params: { isChecked: boolean; userInfos: UserInfoSS[] }) { + params.userInfos.forEach(userInfo => { + if (params.isChecked) { + if ( + this.selectedUserList.filter(user => user.seq === userInfo.seq) + .length === 0 + ) { + this.selectedUserList = [...this.selectedUserList, userInfo]; + } + } else { + this.selectedUserList = this.selectedUserList.filter( + user => user.seq !== userInfo.seq + ); + } + }); + } + onToggleUser(userInfo: UserInfoSS) { + if (userInfo.seq === this.loginRes.userSeq) { + return; + } + + if ( + this.selectedUserList.filter(user => user.seq === userInfo.seq).length === + 0 + ) { + this.selectedUserList = [...this.selectedUserList, userInfo]; + } else { + this.selectedUserList = this.selectedUserList.filter( + item => item.seq !== userInfo.seq + ); + } + this.changeDetectorRef.detectChanges(); } - onChangePage(event: PageEvent) { - this.pageCurrent = event.pageIndex + 1; - this.pageListCount = event.pageSize; - - this.onSearch(this.currentSearchWord); + /** Handling chipset for selectedUserList */ + onClickDeleteUser(userInfo: UserInfoSS) { + this.selectedUserList = this.selectedUserList.filter( + item => item.seq !== userInfo.seq + ); + this.changeDetectorRef.detectChanges(); } + /** Handling Button */ + async onClickAddGroup() { + this.logger.debug('onClickAddGroup', this.selectedUserList); + + const result = await this.dialogService.open< + SelectGroupDialogComponent, + SelectGroupDialogData, + SelectGroupDialogResult + >(SelectGroupDialogComponent, { + width: '600px', + data: { + title: this.translateService.instant('group.selectTargetGroup') + } + }); + + if (!!result && !!result.choice && result.choice) { + if (!!result.group) { + const oldGroup: GroupDetailData = result.group; + const trgtUserSeq: number[] = []; + result.group.userSeqs.map(seq => trgtUserSeq.push(seq)); + this.selectedUserList + .filter(v => result.group.userSeqs.indexOf(v.seq) < 0) + .forEach(user => { + trgtUserSeq.push(user.seq); + }); + + this.store.dispatch( + SyncStore.updateGroupMember({ + oldGroup, + trgtUserSeq + }) + ); + } + } + } + onClickChatOpen() { + if (!!this.selectedUserList && this.selectedUserList.length > 0) { + // Open Room. + const seq: number[] = []; + this.selectedUserList.map(user => seq.push(user.seq)); + this.store.dispatch(ChatStore.openRoom({ userSeqList: seq })); + + // GNB Change to Chat + this.store.dispatch( + SettingsStore.selectedGnbMenuIndex({ + menuIndex: MainMenu.Chat + }) + ); + } + } + onClickConference() { + const targetUserSeqs = this.selectedUserList.map(userInfo => userInfo.seq); + if (!!targetUserSeqs && targetUserSeqs.length > 0) { + this.conferenceService.conferenceCreate({ + userSeq: this.loginRes.userSeq, + deviceType: this.environmentsInfo.deviceType, + tokenKey: this.loginRes.tokenString, + targetUserSeqs + }); + } + } onClickOpenProfile(userSeq: number) { if (!userSeq || userSeq < 0) { return; @@ -234,4 +316,7 @@ export class IntegratedSearchDialogComponent implements OnInit, OnDestroy { ) .subscribe(); } + onCancel(): void { + this.dialogRef.close({}); + } } diff --git a/projects/ucap-webmessenger-app/src/app/store/messenger/query/actions.ts b/projects/ucap-webmessenger-app/src/app/store/messenger/query/actions.ts index e9e52ab3..b90d6757 100644 --- a/projects/ucap-webmessenger-app/src/app/store/messenger/query/actions.ts +++ b/projects/ucap-webmessenger-app/src/app/store/messenger/query/actions.ts @@ -100,10 +100,34 @@ export const searchDeptUserSuccess = createAction( export const searchDeptUserFailure = createAction( '[Messenger::Query] Search Dept User Failure', + props<{ error: any }>() +); + +export const clearSearchDeptUser = createAction( + '[Messenger::Query] Clear Search Dept User Success', props() ); -export const cancelSearchDeptUser = createAction( - '[Messenger::Query] Cancel Search Dept User Success', +/** 통합검색 > 조회 */ +export const integrateSearchDeptUser = createAction( + '[Messenger::Query] Integration Search Dept User', + props<{ + companyCode: string; + search: string; + }>() +); + +export const integrateSearchDeptUserSuccess = createAction( + '[Messenger::Query] Integration Search Dept User Success.', + props<{ userInfos: UserInfoSS[] }>() +); + +export const integrateSearchDeptUserFailure = createAction( + '[Messenger::Query] Integration Search Dept User Failure.', + props<{ error: any }>() +); + +export const integrateClearSearchDeptUser = createAction( + '[Messenger::Query] Integration Clear Search Dept User Success', props() ); diff --git a/projects/ucap-webmessenger-app/src/app/store/messenger/query/effects.ts b/projects/ucap-webmessenger-app/src/app/store/messenger/query/effects.ts index 70a42762..a4ce8abd 100644 --- a/projects/ucap-webmessenger-app/src/app/store/messenger/query/effects.ts +++ b/projects/ucap-webmessenger-app/src/app/store/messenger/query/effects.ts @@ -28,7 +28,11 @@ import { selectedDeptSuccess, searchDeptUser, searchDeptUserSuccess, - cancelSearchDeptUser + clearSearchDeptUser, + integrateSearchDeptUser, + integrateSearchDeptUserSuccess, + integrateSearchDeptUserFailure, + searchDeptUserFailure } from './actions'; import { @@ -146,7 +150,58 @@ export class Effects { this.store.dispatch(selectedDeptSuccess({})); }), - catchError(error => of(deptUserFailure({ error }))) + catchError(error => of(searchDeptUserFailure({ error }))) + ); + }) + ); + }, + { dispatch: false } + ); + + integrateSearchDeptUser$ = createEffect( + () => { + return this.actions$.pipe( + ofType(integrateSearchDeptUser), + withLatestFrom( + this.store.pipe( + select( + (state: any) => + state.account.authentication.loginRes as LoginResponse + ) + ) + ), + switchMap(([req, loginResInfo]) => { + return this.organizationService + .getDeptUser({ + divCd: 'INT_SRCH', + companyCode: req.companyCode, + search: req.search, + searchRange: DeptSearchType.All, + senderCompanyCode: loginResInfo.companyCode, + senderEmployeeType: loginResInfo.userInfo.employeeType + }) + .pipe( + map(datas => { + const userInfos: UserInfoSS[] = datas.userInfos; + this.store.dispatch( + integrateSearchDeptUserSuccess({ + userInfos + }) + ); + + // 검색 결과에 따른 프레즌스 조회. + const userSeqList: number[] = []; + userInfos.map(user => userSeqList.push(user.seq)); + if (userSeqList.length > 0) { + this.store.dispatch( + StatusStore.bulkInfo({ + divCd: 'inttrSrch', + userSeqs: userSeqList + }) + ); + } + }), + catchError(error => of(integrateSearchDeptUserFailure({ error }))) ); }) ); @@ -179,7 +234,6 @@ export class Effects { .pipe( map(datas => { const userInfos: UserInfoSS[] = datas.userInfos; - this.store.dispatch( searchDeptUserSuccess({ userInfos diff --git a/projects/ucap-webmessenger-app/src/app/store/messenger/query/reducers.ts b/projects/ucap-webmessenger-app/src/app/store/messenger/query/reducers.ts index 98795556..2e345db0 100644 --- a/projects/ucap-webmessenger-app/src/app/store/messenger/query/reducers.ts +++ b/projects/ucap-webmessenger-app/src/app/store/messenger/query/reducers.ts @@ -11,8 +11,12 @@ import { searchDeptUserSuccess, searchDeptUserFailure, deptUserFailure, - cancelSearchDeptUser, - myDeptUserFailure + clearSearchDeptUser, + myDeptUserFailure, + integrateSearchDeptUser, + integrateSearchDeptUserSuccess, + integrateSearchDeptUserFailure, + integrateClearSearchDeptUser } from './actions'; import * as AuthenticationStore from '@app/store/account/authentication'; @@ -96,7 +100,7 @@ export const reducer = createReducer( }; }), - on(cancelSearchDeptUser, (state, action) => { + on(clearSearchDeptUser, (state, action) => { return { ...state, isSearch: false, @@ -104,6 +108,35 @@ export const reducer = createReducer( }; }), + on(integrateSearchDeptUser, (state, action) => { + return { + ...state, + integrateSearchDepartmentProcessing: true + }; + }), + + on(integrateSearchDeptUserSuccess, (state, action) => { + return { + ...state, + integrateSearchDepartmentUserInfoList: action.userInfos, + integrateSearchDepartmentProcessing: false + }; + }), + + on(integrateSearchDeptUserFailure, (state, action) => { + return { + ...state, + integrateSearchDepartmentProcessing: false + }; + }), + + on(integrateClearSearchDeptUser, (state, action) => { + return { + ...state, + integrateSearchDepartmentUserInfoList: null + }; + }), + on(AuthenticationStore.logoutInitialize, (state, action) => { return { ...initialState diff --git a/projects/ucap-webmessenger-app/src/app/store/messenger/query/state.ts b/projects/ucap-webmessenger-app/src/app/store/messenger/query/state.ts index 1b330972..96aa9912 100644 --- a/projects/ucap-webmessenger-app/src/app/store/messenger/query/state.ts +++ b/projects/ucap-webmessenger-app/src/app/store/messenger/query/state.ts @@ -18,6 +18,9 @@ export interface State { searchDepartmentUserInfoList: UserInfoSS[] | null; departmentUserInfoList: UserInfoSS[] | null; myDepartmentUserInfoList: UserInfoSS[] | null; + + integrateSearchDepartmentProcessing: boolean; + integrateSearchDepartmentUserInfoList: UserInfoSS[] | null; } export const initialState: State = { @@ -30,7 +33,10 @@ export const initialState: State = { searchDepartmentUserInfoList: null, departmentUserInfoList: null, - myDepartmentUserInfoList: null + myDepartmentUserInfoList: null, + + integrateSearchDepartmentProcessing: false, + integrateSearchDepartmentUserInfoList: null }; export function selectors(selector: Selector) { @@ -62,6 +68,15 @@ export function selectors(selector: Selector) { myDepartmentUserInfoList: createSelector( selector, (state: State) => state.myDepartmentUserInfoList + ), + + integrateSearchDepartmentProcessing: createSelector( + selector, + (state: State) => state.integrateSearchDepartmentProcessing + ), + integrateSearchDepartmentUserInfoList: createSelector( + selector, + (state: State) => state.integrateSearchDepartmentUserInfoList ) }; }