import { MatMenuTrigger } from '@angular/material'; import { Component, OnInit, ViewChild, OnDestroy, EventEmitter, Output } from '@angular/core'; import { Observable, combineLatest, Subscription, of } from 'rxjs'; import { map, tap, catchError, exhaustMap } from 'rxjs/operators'; 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 StatusStore from '@app/store/messenger/status'; import { NGXLogger } from 'ngx-logger'; import { Company } from '@ucap-webmessenger/api-external'; import { SessionStorageService } from '@ucap-webmessenger/web-storage'; import { VersionInfo2Response } from '@ucap-webmessenger/api-public'; import { LoginInfo, KEY_LOGIN_INFO, UserSelectDialogType } from '@app/types'; import { KEY_VER_INFO } from '@app/types/ver-info.type'; import { ExpansionPanelComponent as GroupExpansionPanelComponent } from '@ucap-webmessenger/ui-group'; import { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; import { UserInfo, GroupDetailData } from '@ucap-webmessenger/protocol-sync'; import { StatusProtocolService } from '@ucap-webmessenger/protocol-status'; import { DeptSearchType, UserInfoSS, UserInfoF, UserInfoDN, QueryProtocolService, SSVC_TYPE_QUERY_DEPT_USER_DATA, SSVC_TYPE_QUERY_DEPT_USER_RES, DeptUserData } from '@ucap-webmessenger/protocol-query'; import { ucapAnimations, DialogService, ConfirmDialogData, ConfirmDialogComponent, ConfirmDialogResult } from '@ucap-webmessenger/ui'; import { CreateChatDialogComponent, CreateChatDialogData, CreateChatDialogResult } from './../../dialogs/chat/create-chat.dialog.component'; import { EditGroupDialogComponent, EditGroupDialogData, EditGroupDialogResult } from '@app/layouts/messenger/dialogs/group/edit-group.dialog.component'; @Component({ selector: 'app-layout-chat-left-sidenav-group', templateUrl: './group.component.html', styleUrls: ['./group.component.scss'], animations: ucapAnimations }) export class GroupComponent implements OnInit, OnDestroy { @Output() newGroupAndMember = new EventEmitter(); @Output() openProfile = new EventEmitter< UserInfo | UserInfoSS | UserInfoF | UserInfoDN >(); @ViewChild('groupExpansionPanel', { static: true }) groupExpansionPanel: GroupExpansionPanelComponent; @ViewChild('profileContextMenuTrigger', { static: true }) profileContextMenuTrigger: MatMenuTrigger; profileContextMenuPosition = { x: '0px', y: '0px' }; @ViewChild('groupContextMenuTrigger', { static: true }) groupContextMenuTrigger: MatMenuTrigger; groupContextMenuPosition = { x: '0px', y: '0px' }; groupBuddyList$: Observable< { group: GroupDetailData; buddyList: UserInfo[] }[] >; favoritBuddyList$: Observable; companyList$: Observable; companyCode: string; loginRes: LoginResponse; loginResSubscription: Subscription; sessionVerinfo: VersionInfo2Response; isShowSearch = false; searchProcessing = false; searchUserInfos: UserInfoSS[] = []; constructor( private store: Store, private sessionStorageService: SessionStorageService, private dialogService: DialogService, private queryProtocolService: QueryProtocolService, private statusProtocolService: StatusProtocolService, private logger: NGXLogger ) {} ngOnInit() { this.sessionVerinfo = this.sessionStorageService.get( KEY_VER_INFO ); const loginInfo = this.sessionStorageService.get(KEY_LOGIN_INFO); this.companyCode = loginInfo.companyCode; this.loginResSubscription = this.store .pipe( select(AppStore.AccountSelector.AuthenticationSelector.loginRes), tap(loginRes => { this.loginRes = loginRes; }) ) .subscribe(); this.companyList$ = this.store.pipe( select(AppStore.SettingSelector.CompanySelector.companyList) ); this.groupBuddyList$ = combineLatest([ this.store.pipe( select(AppStore.MessengerSelector.SyncSelector.selectAllBuddy2) ), this.store.pipe( select(AppStore.MessengerSelector.SyncSelector.selectAllGroup2) ) ]).pipe( map(([buddyList, groupList]) => { const groupBuddyList: { group: GroupDetailData; buddyList: UserInfo[]; }[] = []; for (const group of groupList) { groupBuddyList.push({ group, buddyList: buddyList.filter(buddy => { return group.userSeqs.indexOf(buddy.seq) > -1; }) }); } return groupBuddyList; }) ); this.favoritBuddyList$ = this.store .pipe(select(AppStore.MessengerSelector.SyncSelector.selectAllBuddy2)) .pipe( map(buddyInfoList => { return buddyInfoList .filter(buddy => buddy.isFavorit) .sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0)); }) ); } ngOnDestroy(): void { this.logger.debug('ngOnDestroy'); if (!!this.loginResSubscription) { this.loginResSubscription.unsubscribe(); } } async onClickGroupMenu(menuType: string) { this.logger.debug('menuType', menuType); switch (menuType) { case 'GROUP_NEW': { this.newGroupAndMember.emit(); } break; case 'GROUP_EXPAND_MORE': { this.groupExpansionPanel.expandMore(); } break; case 'GROUP_EXPAND_LESS': { this.groupExpansionPanel.expandLess(); } break; case 'GROUP_SAVE': break; case 'GROUP_RESTORE': break; default: break; } } onSelectBuddy(buddy: UserInfo) { this.logger.debug('onSelectBuddy', buddy); if (buddy.seq === this.loginRes.userSeq) { this.store.dispatch( ChatStore.openRoom({ userSeqList: [this.loginRes.talkWithMeBotSeq] }) ); } else { this.store.dispatch(ChatStore.openRoom({ userSeqList: [buddy.seq] })); } } getStatusBulkInfo(buddy: UserInfo) { return this.store.pipe( select( AppStore.MessengerSelector.StatusSelector.selectEntitiesStatusBulkInfo ), map(statusBulkInfo => !!statusBulkInfo ? statusBulkInfo[buddy.seq] : undefined ) ); } /** 유저검색 */ onKeyDownEnterOrganizationTenantSearch(params: { companyCode: string; searchWord: string; }) { if (params.searchWord.trim().length > 1) { this.isShowSearch = true; this.searchProcessing = true; const searchUserInfos: UserInfoSS[] = []; this.queryProtocolService .deptUser({ divCd: 'GRP', companyCode: params.companyCode, searchRange: DeptSearchType.All, search: params.searchWord, senderCompanyCode: params.companyCode, senderEmployeeType: this.loginRes.userInfo.employeeType }) .pipe( map(res => { switch (res.SSVC_TYPE) { case SSVC_TYPE_QUERY_DEPT_USER_DATA: searchUserInfos.push(...(res as DeptUserData).userInfos); break; case SSVC_TYPE_QUERY_DEPT_USER_RES: { // 검색 결과 처리. this.searchUserInfos = searchUserInfos; this.searchProcessing = false; // 검색 결과에 따른 프레즌스 조회. const userSeqList: number[] = []; this.searchUserInfos.map(user => userSeqList.push(user.seq)); if (userSeqList.length > 0) { this.store.dispatch( StatusStore.bulkInfo({ divCd: 'groupSrch', userSeqs: userSeqList }) ); } } break; } }), catchError(error => { this.searchProcessing = false; return of(this.logger.error(error)); }) ) .subscribe(); } } /** 검색 취소 */ onClickCancel() { this.isShowSearch = false; this.searchUserInfos = []; } getShowContextMenu(userInfo: UserInfo | UserInfoF) { if (userInfo.seq === this.loginRes.userSeq) { return false; } else { return true; } } onClickProfileContextMenu(menuType: string, userInfo: UserInfo | UserInfoF) { this.logger.debug( 'onClickProfileContextMenu', 'menuType', menuType, 'userInfo', userInfo ); switch (menuType) { case 'REGISTER_FAVORITE': this.store.dispatch( SyncStore.updateBuddy({ seq: userInfo.seq, isFavorit: !userInfo.isFavorit }) ); break; } } onClickOpenProfile(userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN) { this.openProfile.emit(userInfo); } onContextMenuProfile( event: MouseEvent, userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN ) { event.preventDefault(); event.stopPropagation(); this.profileContextMenuPosition.x = event.clientX + 'px'; this.profileContextMenuPosition.y = event.clientY + 'px'; this.profileContextMenuTrigger.menu.focusFirstItem('mouse'); this.profileContextMenuTrigger.menuData = { userInfo }; this.profileContextMenuTrigger.openMenu(); } async onClickGroupContextMenu(menuType: string, group: GroupDetailData) { this.logger.debug( 'onClickGroupContextMenu', 'menuType', menuType, 'group', group ); switch (menuType) { case 'CHAT': this.store.dispatch( ChatStore.openRoom({ userSeqList: group.userSeqs }) ); break; case 'SEND_NOTE': break; case 'RENAME': { const result = await this.dialogService.open< EditGroupDialogComponent, EditGroupDialogData, EditGroupDialogResult >(EditGroupDialogComponent, { data: { title: 'Group Name Edit', group } }); if (!!result && !!result.choice && result.choice) { if (!!result.groupName && result.groupName.trim().length > 0) { this.store.dispatch( SyncStore.updateGroup({ groupSeq: result.group.seq, groupName: result.groupName, userSeqs: result.group.userSeqs }) ); } } } break; case 'EDIT_MEMBER': { const result = await this.dialogService.open< CreateChatDialogComponent, CreateChatDialogData, CreateChatDialogResult >(CreateChatDialogComponent, { width: '600px', data: { type: UserSelectDialogType.EditMember, title: 'Group Member Edit', group } }); if (!!result && !!result.choice && result.choice) { if (!!result.oldGroup) { const userSeqs: number[] = []; result.selectedUserList.map(user => userSeqs.push(user.seq)); this.store.dispatch( SyncStore.updateGroupMember({ oldGroup: group, trgtUserSeq: userSeqs }) ); } } } break; case 'DELETE': { const result = await this.dialogService.open< ConfirmDialogComponent, ConfirmDialogData, ConfirmDialogResult >(ConfirmDialogComponent, { width: '360px', data: { title: 'Delete group', html: `그룹(${group.name})을 삭제하시겠습니까?
그룹 멤버는 해당 그룹에서만 삭제됩니다.` } }); if (!!result && !!result.choice && result.choice) { this.store.dispatch(SyncStore.delGroup({ group })); } } break; default: break; } } onMoreGroup(params: { event: MouseEvent; group: GroupDetailData }) { params.event.preventDefault(); params.event.stopPropagation(); this.groupContextMenuPosition.x = params.event.clientX + 'px'; this.groupContextMenuPosition.y = params.event.clientY + 'px'; this.groupContextMenuTrigger.menu.focusFirstItem('mouse'); this.groupContextMenuTrigger.menuData = { group: params.group }; this.groupContextMenuTrigger.openMenu(); } }