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, EnvironmentsInfo, KEY_ENVIRONMENTS_INFO, KEY_VER_INFO } from '@app/types'; 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'; import { SelectGroupDialogComponent, SelectGroupDialogResult, SelectGroupDialogData } from '../../dialogs/group/select-group.dialog.component'; import { MessageWriteDialogComponent, MessageWriteDialogResult, MessageWriteDialogData } from '../../dialogs/message/message-write.dialog.component'; import { environment } from '../../../../../environments/environment'; @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: false }) 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: { group: GroupDetailData; buddyList: UserInfo[] }[]; groupBuddyListSubscription: Subscription; favoritBuddyList$: Observable; companyList$: Observable; companyCode: string; loginRes: LoginResponse; loginResSubscription: Subscription; environmentsInfo: EnvironmentsInfo; 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 ) { this.environmentsInfo = this.sessionStorageService.get( KEY_ENVIRONMENTS_INFO ); } 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.groupBuddyListSubscription = 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; }) }); } this.groupBuddyList = groupBuddyList; return groupBuddyList; }) ) .subscribe(); 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 { if (!!this.loginResSubscription) { this.loginResSubscription.unsubscribe(); } if (!!this.groupBuddyListSubscription) { this.groupBuddyListSubscription.unsubscribe(); } this.logger.debug('-----------------------GroupComponent ngOnDestroy'); } 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 = []; } /** 그룹 header > more 버튼 > visible of context menu */ getShowGroupContextMenu(menuType: string, group: GroupDetailData) { // 즐겨찾기 그룹 숨김메뉴 if ( menuType === 'DIV1' || menuType === 'RENAME' || menuType === 'EDIT_MEMBER' || menuType === 'DELETE' ) { if (!group || group === undefined) { return false; } /** 수정불가 그룹 핸들링. */ if ( !!group && !!environment.customConfig && !!environment.customConfig.fixedGroupSeqs ) { const fixedGroupSeqs: number[] = environment.customConfig.fixedGroupSeqs; if (!!fixedGroupSeqs && fixedGroupSeqs.length > 0) { if (fixedGroupSeqs.indexOf(group.seq) > -1) { return false; } } } } // 기본 그룹 숨김메뉴 if (menuType === 'RENAME' || menuType === 'DELETE') { if (!!group && group.seq === 0) { return false; } } // 그룹원 0명인 그룹 메뉴 정리 if (menuType === 'CHAT') { if (!!group && !!group.userSeqs && group.userSeqs.length > 0) { return true; } else { return false; } } return true; } /** 그룹 > 그룹원 > visible of context menu */ getShowProfileContextMenu( menuType: string, userInfo: UserInfo | UserInfoF, group?: GroupDetailData ) { if (userInfo.seq === this.loginRes.userSeq) { return false; } if (!group || group === undefined) { if ( menuType === 'REGISTER_FAVORITE' || menuType === 'SEND_MESSAGE' || menuType === 'REGISTER_NICKNAME' ) { // continue; } else { return false; } } return true; } async onClickProfileContextMenu( menuType: string, userInfo: UserInfo | UserInfoF, group?: GroupDetailData ) { this.logger.debug( 'onClickProfileContextMenu', 'menuType', menuType, 'userInfo', userInfo, 'group', group ); switch (menuType) { case 'VIEW_PROFILE': this.openProfile.emit(userInfo); break; case 'CHAT': this.onSelectBuddy(userInfo); break; case 'REMOVE_FROM_GROUP': { const result = await this.dialogService.open< ConfirmDialogComponent, ConfirmDialogData, ConfirmDialogResult >(ConfirmDialogComponent, { width: '360px', data: { title: 'Delete Member in Group', html: `[${userInfo.name} ${userInfo.grade}]를 [${group.name}]그룹에서 삭제하시겠습니까?` } }); if (!!result && !!result.choice && result.choice) { const trgtUserSeq = group.userSeqs.filter( user => user !== userInfo.seq ); this.store.dispatch( SyncStore.updateGroupMember({ oldGroup: group, trgtUserSeq }) ); } } break; case 'COPY_BUDDY': { const result = await this.dialogService.open< SelectGroupDialogComponent, SelectGroupDialogData, SelectGroupDialogResult >(SelectGroupDialogComponent, { width: '600px', data: { title: 'Group Select' } }); if (!!result && !!result.choice && result.choice) { if (!!result.group) { const oldGroup: GroupDetailData = result.group; const trgtUserSeq: number[] = []; let exist = false; result.group.userSeqs.map(seq => { trgtUserSeq.push(seq); if (seq === userInfo.seq) { exist = true; } }); if (!exist) { trgtUserSeq.push(userInfo.seq); } this.store.dispatch( SyncStore.updateGroupMember({ oldGroup, trgtUserSeq }) ); } } } break; case 'MOVE_BUDDY': { const result = await this.dialogService.open< SelectGroupDialogComponent, SelectGroupDialogData, SelectGroupDialogResult >(SelectGroupDialogComponent, { width: '600px', data: { title: 'Group Select' } }); if (!!result && !!result.choice && result.choice) { if (!!result.group) { this.store.dispatch( SyncStore.moveGroupMember({ fromGroup: group, toGroup: result.group, trgtUserSeq: [userInfo.seq] }) ); } } } break; case 'REGISTER_FAVORITE': this.store.dispatch( SyncStore.updateBuddy({ seq: userInfo.seq, isFavorit: !userInfo.isFavorit }) ); break; case 'SEND_MESSAGE': { this.dialogService.open< MessageWriteDialogComponent, MessageWriteDialogData, MessageWriteDialogResult >(MessageWriteDialogComponent, { width: '600px', height: '600px', disableClose: true, hasBackdrop: false, data: { loginRes: this.loginRes, environmentsInfo: this.environmentsInfo, receiverList: [userInfo] } }); } break; } } onClickOpenProfile(userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN) { this.openProfile.emit(userInfo); } onContextMenuProfile( event: MouseEvent, userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN, group: GroupDetailData ) { 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, group }; 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_MESSAGE': { const curGroupBuddyList = this.groupBuddyList.filter( groupInfo => groupInfo.group.seq === group.seq ); if (!!curGroupBuddyList && curGroupBuddyList.length > 0) { this.dialogService.open< MessageWriteDialogComponent, MessageWriteDialogData, MessageWriteDialogResult >(MessageWriteDialogComponent, { width: '600px', height: '600px', disableClose: true, hasBackdrop: false, data: { loginRes: this.loginRes, environmentsInfo: this.environmentsInfo, receiverList: curGroupBuddyList[0].buddyList } }); } } 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(); } }