diff --git a/documents/업무/3월/1째주/actions.ts b/documents/업무/3월/1째주/actions.ts new file mode 100644 index 0000000..a895582 --- /dev/null +++ b/documents/업무/3월/1째주/actions.ts @@ -0,0 +1,344 @@ +import { createAction, props } from '@ngrx/store'; +import { + BuddyRequest, + GroupRequest, + GroupDetailData, + GroupResponse, + UserInfo, + RoomRequest, + PhoneBookInfo, + PhoneBookReadyRequest, + PhoneBookReadyResponse, + PhoneBookReadyNotification, + PhoneBookReadyOkRequest, + PhoneBookReadyOkResponse, + PhoneBookSndNotification, + PhoneBookRcvRequest, + PhoneBookRcvResponse +} from '@ucap-webmessenger/protocol-sync'; +import { + RoomInfo, + UserInfoShort, + UserInfo as RoomUserInfo, + InfoRequest +} from '@ucap-webmessenger/protocol-room'; +import { Info, EventJson } from '@ucap-webmessenger/protocol-event'; +import { + AddResponse as GroupAddResponse, + UpdateRequest as GroupUpdateRequest, + UpdateResponse as GroupUpdateResponse, + DelRequest as GroupDelRequest, + DelResponse as GroupDelResponse +} from '@ucap-webmessenger/protocol-group'; +import { + AddRequest as BuddyAddRequest, + AddResponse as BuddyAddResponse, + DelRequest as BuddyDelRequest, + DelResponse as BuddyDelResponse, + UpdateRequest as BuddyUpdateRequest, + UpdateResponse as BuddyUpdateResponse +} from '@ucap-webmessenger/protocol-buddy'; + +export const buddy2 = createAction( + '[Messenger::Sync] Buddy2', + props() +); + +export const buddy2Success = createAction( + '[Messenger::Sync] Buddy2 Success', + props<{ buddyList: UserInfo[]; syncDate: string }>() +); + +export const buddy2Failure = createAction( + '[Messenger::Sync] Buddy2 Failure', + props<{ error: any }>() +); + +export const group2 = createAction( + '[Messenger::Sync] Group2', + props() +); + +export const group2Success = createAction( + '[Messenger::Sync] Group2 Success', + props<{ groupList: GroupDetailData[]; syncDate: string }>() +); + +export const group2Failure = createAction( + '[Messenger::Sync] Group2 Failure', + props<{ error: any }>() +); + +export const room = createAction( + '[Messenger::Sync] Room', + props() +); + +export const roomSuccess = createAction( + '[Messenger::Sync] Room Success', + props<{ + roomList: RoomInfo[]; + roomUserInfoMap: { + [param: string]: { + userInfoShortList: UserInfoShort[]; + userInfoList: RoomUserInfo[]; + }; + }; + syncDate: string; + }>() +); + +export const roomFailure = createAction( + '[Messenger::Sync] Room Failure', + props<{ error: any }>() +); + +export const updateRoomForNewEventMessage = createAction( + '[Messenger::Sync] updateRoomForNewEventMessage', + props<{ + roomSeq: string; + info: Info; + }>() +); + +export const refreshRoom = createAction( + '[Messenger::Sync] refresh room in sync', + props() +); +export const refreshRoomSuccess = createAction( + '[Messenger::Sync] refresh room in sync Success', + props<{ + roomInfo: RoomInfo; + userInfoShortList: UserInfoShort[]; + userInfoList: RoomUserInfo[]; + }>() +); +export const refreshRoomFailure = createAction( + '[Messenger::Sync] refresh room in sync Failure', + props<{ error: any }>() +); + +export const updateUnreadCount = createAction( + '[Messenger::Sync] Update unread count', + props<{ + roomSeq: string; + noReadCnt?: number; + }>() +); + +/** 새그룹 추가 & 그룹원 추가 */ +export const createGroupAndBuddy = createAction( + '[Messenger::Sync] Group & Buddy Create', + props<{ + groupName: string; + trgtUserSeq: number[]; + }>() +); +export const createGroupAndBuddySuccess = createAction( + '[Messenger::Sync] Group & Buddy Create Success', + props() +); +export const createGroupAndBuddyFailure = createAction( + '[Messenger::Sync] Group & Buddy Create Failure', + props<{ error: any }>() +); +/** 그룹원 수정 */ +export const updateGroupMember = createAction( + '[Messenger::Sync] Update Group Member', + props<{ + oldGroup: GroupDetailData; + trgtUserSeq: number[]; + }>() +); +export const updateGroupMemberSuccess = createAction( + '[Messenger::Sync] Update Group Member Success', + props() +); +export const updateGroupMemberFailure = createAction( + '[Messenger::Sync] Update Group Member Failure', + props<{ error: any }>() +); +/** 그룹원 이동 */ +export const moveGroupMember = createAction( + '[Messenger::Sync] Move Group Member', + props<{ + fromGroup: GroupDetailData; + toGroup: GroupDetailData; + trgtUserSeq: number[]; + }>() +); +export const moveGroupToFailure = createAction( + '[Messenger::Sync] Move Group Member / To', + props<{ error: any }>() +); +export const moveGroupFromFailure = createAction( + '[Messenger::Sync] Move Group Member / From', + props<{ error: any }>() +); + +/** 동료 추가 */ +export const addBuddy = createAction( + '[Messenger::Sync] Buddy Add', + props() +); +export const addBuddySuccess = createAction( + '[Messenger::Sync] Buddy Add Success', + props() +); +export const addBuddyFailure = createAction( + '[Messenger::Sync] Buddy Add Failure', + props<{ error: any }>() +); +/** 동료 삭제 */ +export const delBuddy = createAction( + '[Messenger::Sync] Buddy Del', + props() +); +export const delBuddySuccess = createAction( + '[Messenger::Sync] Buddy Del Success', + props() +); +export const delBuddyFailure = createAction( + '[Messenger::Sync] Buddy Del Failure', + props<{ error: any }>() +); +/** 동료 삭제 및 그룹 클리어.(in profile) */ +export const delBuddyAndClear = createAction( + '[Messenger::Sync] Buddy Del and Group Clear', + props<{ seq: number }>() +); +export const delBuddyAndClearSuccess = createAction( + '[Messenger::Sync] Buddy Del and Group Clear Success', + props() +); +export const delBuddyAndClearFailure = createAction( + '[Messenger::Sync] Buddy Del and Group Clear Failure', + props<{ error: any }>() +); +/** 동료 변경(즐겨찾기) */ +export const updateBuddy = createAction( + '[Messenger::Sync] Buddy Update', + props() +); +export const updateBuddySuccess = createAction( + '[Messenger::Sync] Buddy Update Success', + props() +); +export const updateBuddyFailure = createAction( + '[Messenger::Sync] Buddy Update Failure', + props<{ error: any }>() +); + +/** 새그룹 추가 */ +export const createGroup = createAction( + '[Messenger::Sync] Group Create', + props<{ + groupName: string; + }>() +); +export const createGroupSuccess = createAction( + '[Messenger::Sync] Group Create Success', + props() +); +export const createGroupFailure = createAction( + '[Messenger::Sync] Group Create Failure', + props<{ error: any }>() +); +/** 그룹 업데이트 */ +export const updateGroup = createAction( + '[Messenger::Sync] Group Update', + props() +); +export const updateGroupSuccess = createAction( + '[Messenger::Sync] Group Update Success', + props() +); +export const updateGroupFailure = createAction( + '[Messenger::Sync] Group Update Failure', + props<{ error: any }>() +); +/** 그룹 삭제 */ +export const delGroup = createAction( + '[Messenger::Sync] Group Del', + props<{ + group: GroupDetailData; + }>() +); +export const delGroupSuccess = createAction( + '[Messenger::Sync] Group Del Success', + props() +); +export const delGroupFailure = createAction( + '[Messenger::Sync] Group Del Failure', + props<{ error: any }>() +); + +/** Sync 되어 있는 방의 방인원의 isJoinRoom flag 를 false 로 변경. */ +export const clearRoomUsers = createAction( + '[Messenger::Sync] Clear room users.', + props<{ roomSeq: string; userSeqs: number[] }>() +); + +/** + * PhoneBook Req Sync Date + */ +export const phoneBookReqSyncDate = createAction( + '[Messenger::Sync] PhoneBook Ready Req Sync Date', + props<{ syncStartDate: string }>() +); + +/* PhoneBook Ready*/ +export const phoneBookReady = createAction( + '[Messenger::Sync] PhoneBook Ready Sync.', + props<{ req: PhoneBookReadyRequest }>() +); + +export const phoneBookReadySuccess = createAction( + '[Messenger::Sync] PhoneBook Ready Success', + props<{ res: PhoneBookReadyResponse }>() +); + +export const phoneBookReadyFailure = createAction( + '[Messenger::Sync] PhoneBook Ready Failure', + props<{ error: any }>() +); +/* PhoneBook Ready*/ + +/* PhoneBook Ready Ok*/ +export const phoneBookReadyOk = createAction( + '[Messenger::Sync] PhoneBook Ready OK.', + props<{ req: PhoneBookReadyOkRequest }>() +); + +export const phoneBookReadyOkSuccess = createAction( + '[Messenger::Sync] PhoneBook Ready OK Success.', + props<{ res: PhoneBookReadyOkResponse }>() +); + +export const phoneBookReadyOkFailure = createAction( + '[Messenger::Sync] PhoneBook Ready OK Failure', + props<{ error: any }>() +); +/* PhoneBook Ready Ok*/ + +/* PhoneBook Send Noti */ +export const phoneBookSndNotification = createAction( + '[Messenger::Sync] PhoneBook Send Notification', + props<{ noti: PhoneBookSndNotification }>() +); + +/* PhoneBook Send Noti */ +/* PhoneBook Recv */ +export const phoneBookRcv = createAction( + '[Messenger::Sync] PhoneBook Rcv request', + props<{ req: PhoneBookRcvRequest }>() +); +export const phoneBookRcvSuccess = createAction( + '[Messenger::Sync] PhoneBook Rcv Success', + props() +); +export const phoneBookRcvFailure = createAction( + '[Messenger::Sync] PhoneBook Rcv Failure', + props() +); +/* PhoneBook Send Noti */ diff --git a/documents/업무/3월/1째주/effects.ts b/documents/업무/3월/1째주/effects.ts new file mode 100644 index 0000000..4526a17 --- /dev/null +++ b/documents/업무/3월/1째주/effects.ts @@ -0,0 +1,1343 @@ +import { Injectable } from '@angular/core'; + +import { Actions, ofType, createEffect } from '@ngrx/effects'; + +import { of, Observable, TimeoutError } from 'rxjs'; +import { + catchError, + exhaustMap, + map, + withLatestFrom, + switchMap, + tap, + concatMap, + timeoutWith, + take, + timeout, + delay +} from 'rxjs/operators'; + +import { Store, select } from '@ngrx/store'; +import { Dictionary } from '@ngrx/entity'; +import { NGXLogger } from 'ngx-logger'; +import { environment } from '../../../../environments/environment'; + +import { + buddy2, + buddy2Success, + buddy2Failure, + group2, + group2Success, + group2Failure, + room, + roomFailure, + roomSuccess, + updateRoomForNewEventMessage, + refreshRoom, + refreshRoomFailure, + refreshRoomSuccess, + createGroupAndBuddy, + addBuddy, + addBuddyFailure, + delBuddy, + delBuddyFailure, + delBuddySuccess, + updateBuddy, + updateBuddySuccess, + updateBuddyFailure, + createGroup, + createGroupSuccess, + createGroupFailure, + updateGroup, + updateGroupFailure, + updateGroupMember, + delGroup, + delGroupFailure, + delGroupSuccess, + delBuddyAndClear, + moveGroupMember, + moveGroupFromFailure, + moveGroupToFailure, + clearRoomUsers, + phoneBookReqSyncDate, + phoneBookReady, + phoneBookReadySuccess, + phoneBookReadyFailure, + phoneBookReadyOk, + phoneBookReadyOkSuccess, + phoneBookReadyOkFailure, + phoneBookSndNotification, + phoneBookRcvSuccess, + phoneBookRcvFailure, + phoneBookRcv +} from './actions'; +import { SessionStorageService } from '@ucap-webmessenger/web-storage'; +import { LoginInfo, KEY_LOGIN_INFO } from '@app/types'; +import { + SyncProtocolService, + SSVC_TYPE_SYNC_BUDDY2_DATA, + BuddyResponse, + BuddyDetailData, + SSVC_TYPE_SYNC_GROUP_DATA2, + GroupDetailData, + GroupResponse, + UserInfo, + SSVC_TYPE_SYNC_BUDDY2_RES, + SSVC_TYPE_SYNC_GROUP_RES2, + SSVC_TYPE_SYNC_ROOM_DATA, + SSVC_TYPE_SYNC_ROOM_USER, + SSVC_TYPE_SYNC_ROOM_USER2, + SSVC_TYPE_SYNC_ROOM_RES, + RoomData, + RoomUserData, + RoomUserDetailData, + RoomResponse, + PhoneBookReadyResponse, + PhoneBookReadyOkResponse, + PhoneBookRcvResponse, + PhoneBookSndNotification, + PhoneBookSyncType, + KEY_PHONE_BOOK, + SSVC_TYPE_SYNC_PHONEBOOK_SND_NOTI, + PhoneBookInfo +} from '@ucap-webmessenger/protocol-sync'; +import { + RoomInfo, + UserInfoShort, + UserInfo as RoomUserInfo, + RoomProtocolService, + SSVC_TYPE_ROOM_INFO_ROOM, + SSVC_TYPE_ROOM_INFO_USER, + SSVC_TYPE_ROOM_INFO_USER2, + InfoData, + UserShortData, + UserData, + SSVC_TYPE_ROOM_INFO_RES, + RoomType +} from '@ucap-webmessenger/protocol-room'; +import { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; +import { + GroupProtocolService, + AddResponse as GroupAddResponse, + UpdateResponse as GroupUpdateResponse, + DelResponse as GroupDelResponse +} from '@ucap-webmessenger/protocol-group'; +import { + BuddyProtocolService, + AddResponse as BuddyAddResponse, + DelResponse as BuddyDelResponse, + UpdateResponse as BuddyUpdateResponse +} from '@ucap-webmessenger/protocol-buddy'; + +import * as ChatStore from '@app/store/messenger/chat'; +import * as RoomStore from '@app/store/messenger/room'; +import { + AlertDialogComponent, + AlertDialogResult, + DialogService, + AlertDialogData +} from '@ucap-webmessenger/ui'; +import { UserInfoSS } from '@ucap-webmessenger/protocol-query'; +import { TranslateService } from '@ngx-translate/core'; +import { LocalStorageService } from '@ucap-webmessenger/web-storage'; +import { ProtocolService } from '@ucap-webmessenger/protocol'; + +@Injectable() +export class Effects { + buddy2$ = createEffect( + () => { + let buddyList: UserInfo[]; + + return this.actions$.pipe( + ofType(buddy2), + tap(() => { + buddyList = []; + }), + switchMap(req => { + return this.syncProtocolService.buddy2(req).pipe( + map(res => { + switch (res.SSVC_TYPE) { + case SSVC_TYPE_SYNC_BUDDY2_DATA: + buddyList.push( + ...(res as BuddyDetailData).buddyInfos.filter( + v => v.isBuddy && v.isActive + ) + ); + break; + case SSVC_TYPE_SYNC_BUDDY2_RES: + this.store.dispatch( + buddy2Success({ + buddyList, + syncDate: (res as BuddyResponse).syncDate + }) + ); + break; + } + }), + catchError(error => of(buddy2Failure({ error }))) + ); + }) + ); + }, + { dispatch: false } + ); + + group2$ = createEffect( + () => { + let groupList: GroupDetailData[]; + + return this.actions$.pipe( + ofType(group2), + tap(() => { + groupList = []; + }), + concatMap(req => { + return this.syncProtocolService.group2(req).pipe( + map(res => { + switch (res.SSVC_TYPE) { + case SSVC_TYPE_SYNC_GROUP_DATA2: + if ((res as GroupDetailData).isActive) { + groupList.push(res as GroupDetailData); + } + break; + case SSVC_TYPE_SYNC_GROUP_RES2: + this.store.dispatch( + group2Success({ + groupList: [...groupList], + syncDate: (res as GroupResponse).syncDate + }) + ); + break; + } + }), + catchError(error => of(group2Failure({ error }))) + ); + }) + ); + }, + { dispatch: false } + ); + + room$ = createEffect( + () => { + let roomList: RoomInfo[]; + let roomUserInfoMap: { + [param: string]: { + userInfoShortList: UserInfoShort[]; + userInfoList: RoomUserInfo[]; + }; + }; + + return this.actions$.pipe( + ofType(room), + tap(() => { + roomList = []; + roomUserInfoMap = {}; + }), + switchMap(req => { + return this.syncProtocolService.room(req).pipe( + map(res => { + switch (res.SSVC_TYPE) { + case SSVC_TYPE_SYNC_ROOM_DATA: + roomList.push( + ...(res as RoomData).roomInfos.filter( + v => v.isJoinRoom && v.roomType !== RoomType.Bot + ) + ); + break; + case SSVC_TYPE_SYNC_ROOM_USER: + { + const roomUserData = res as RoomUserData; + if (!roomUserInfoMap[roomUserData.roomSeq]) { + roomUserInfoMap[roomUserData.roomSeq] = { + userInfoList: [], + userInfoShortList: [] + }; + } + roomUserInfoMap[ + roomUserData.roomSeq + ].userInfoShortList.push(...roomUserData.userInfos); + } + break; + case SSVC_TYPE_SYNC_ROOM_USER2: + { + const roomUserDetailData = res as RoomUserDetailData; + if (!roomUserInfoMap[roomUserDetailData.roomSeq]) { + roomUserInfoMap[roomUserDetailData.roomSeq] = { + userInfoList: [], + userInfoShortList: [] + }; + } + roomUserInfoMap[ + roomUserDetailData.roomSeq + ].userInfoList.push(...roomUserDetailData.userInfos); + } + break; + case SSVC_TYPE_SYNC_ROOM_RES: + { + const tmpRoomUserInfoMap: { + [param: string]: { + userInfoShortList: UserInfoShort[]; + userInfoList: RoomUserInfo[]; + }; + } = {}; + + roomList.forEach(roomInfo => { + for (const key in roomUserInfoMap) { + if ( + key === roomInfo.roomSeq && + roomUserInfoMap.hasOwnProperty(key) + ) { + tmpRoomUserInfoMap[key] = roomUserInfoMap[key]; + } + } + }); + + this.store.dispatch( + roomSuccess({ + roomList, + roomUserInfoMap: tmpRoomUserInfoMap, + syncDate: (res as RoomResponse).syncDate + }) + ); + } + break; + } + }), + catchError(error => of(roomFailure({ error }))) + ); + }) + ); + }, + { dispatch: false } + ); + + newEventMessageForRoomInfoList$ = createEffect( + () => + this.actions$.pipe( + ofType(ChatStore.newEventMessage), + withLatestFrom( + this.store.pipe( + select((state: any) => state.messenger.sync.room.ids as string[]) + ), + this.store.pipe( + select((state: any) => state.messenger.sync.room.syncDate as string) + ) + ), + tap(([action, roomSeqList, roomSyncDate]) => { + const index = roomSeqList.findIndex( + (roomSeq, i) => roomSeq === action.roomSeq + ); + + if (-1 === index) { + const loginInfo = this.sessionStorageService.get( + KEY_LOGIN_INFO + ); + + this.store.dispatch( + room({ + syncDate: roomSyncDate, + localeCode: loginInfo.localeCode + }) + ); + return; + } + + this.store.dispatch(updateRoomForNewEventMessage(action)); + }) + ), + { dispatch: false } + ); + + openRoom$ = createEffect( + () => + this.actions$.pipe( + ofType(ChatStore.openRoom), + withLatestFrom( + this.store.pipe( + select( + (state: any) => + state.account.authentication.loginRes as LoginResponse + ) + ), + this.store.pipe( + select( + (state: any) => + state.messenger.sync.room.entities as Dictionary + ) + ), + this.store.pipe( + select( + (state: any) => + state.messenger.sync.roomUser.entities as Dictionary< + RoomUserDetailData + > + ) + ), + this.store.pipe( + select( + (state: any) => + state.messenger.sync.roomUserShort.entities as Dictionary< + RoomUserData + > + ) + ) + ), + tap( + async ([action, loginRes, roomInfos, roomUsers, roomUserShorts]) => { + const userSeqList = [...action.userSeqList]; + let roomSeq = null; + + for (const key in roomUsers) { + if (key === undefined) { + continue; + } + + if (roomUsers.hasOwnProperty(key)) { + const element = roomUsers[key]; + if (userSeqList.length === element.userInfos.length) { + roomSeq = key; + if (!!action.isTimeRoom && action.isTimeRoom) { + // 타이머 방 + if (!roomInfos[key].isTimeRoom) { + roomSeq = null; + continue; + } + } else { + // 일반 방 + if (roomInfos[key].isTimeRoom) { + roomSeq = null; + continue; + } + } + // user check + for (const roomUserInfo of element.userInfos) { + if (-1 === userSeqList.indexOf(roomUserInfo.seq)) { + roomSeq = null; + break; + } + } + } + } + } + + for (const key in roomUserShorts) { + if (key === undefined) { + continue; + } + + if (roomUserShorts.hasOwnProperty(key)) { + const element = roomUserShorts[key]; + if (userSeqList.length === element.userInfos.length) { + roomSeq = key; + if (!!action.isTimeRoom && action.isTimeRoom) { + // 타이머 방 + if (!roomInfos[key].isTimeRoom) { + roomSeq = null; + continue; + } + } else { + // 일반 방 + if (roomInfos[key].isTimeRoom) { + roomSeq = null; + continue; + } + } + // user check + for (const roomUserDetailData of element.userInfos) { + if (-1 === userSeqList.indexOf(roomUserDetailData.seq)) { + roomSeq = null; + break; + } + } + } + } + } + + // 내 정보를 추가하여 방생성. + userSeqList.push(loginRes.userSeq); + + this.logger.debug( + 'openRoom', + 'userSeqList', + userSeqList, + 'roomSeq', + roomSeq + ); + if (!!roomSeq) { + this.store.dispatch(ChatStore.selectedRoom({ roomSeq })); + return; + } + + if ( + environment.productConfig.CommonSetting.maxChatRoomUser < + userSeqList.length + ) { + await this.dialogService.open< + AlertDialogComponent, + AlertDialogData, + AlertDialogResult + >(AlertDialogComponent, { + data: { + title: this.translateService.instant('chat.errors.label'), + html: this.translateService.instant( + 'chat.errors.maxCountOfRoomMemberWith', + { + maxCount: + environment.productConfig.CommonSetting.maxChatRoomUser + } + ) + } + }); + return; + } + + if (!!action.isTimeRoom && action.isTimeRoom) { + // 타이머 방 + this.store.dispatch( + RoomStore.openTimer({ + req: { + divCd: 'DivCodeT', + roomName: '', + isTimerRoom: true, + timerRoomInterval: + environment.productConfig.CommonSetting + .timerRoomDefaultInterval, // 24h default + userSeqs: userSeqList + } + }) + ); + } else { + // 일반 방 + this.store.dispatch( + RoomStore.open({ + req: { divCd: 'DivCode', userSeqs: userSeqList } + }) + ); + } + } + ) + ), + { dispatch: false } + ); + + refreshRoom$ = createEffect( + () => { + let roomInfo: RoomInfo; + let userInfoShortList: UserInfoShort[]; + let userInfoList: RoomUserInfo[]; + + return this.actions$.pipe( + ofType(refreshRoom), + tap(() => { + roomInfo = null; + userInfoShortList = []; + userInfoList = []; + }), + switchMap(req => { + return this.roomProtocolService.info(req).pipe( + map(res => { + switch (res.SSVC_TYPE) { + case SSVC_TYPE_ROOM_INFO_ROOM: + roomInfo = (res as InfoData).roomInfo; + break; + case SSVC_TYPE_ROOM_INFO_USER: + userInfoShortList.push(...(res as UserShortData).userInfos); + break; + case SSVC_TYPE_ROOM_INFO_USER2: + userInfoList.push(...(res as UserData).userInfos); + break; + case SSVC_TYPE_ROOM_INFO_RES: + this.store.dispatch( + refreshRoomSuccess({ + roomInfo, + userInfoShortList, + userInfoList + }) + ); + break; + } + }), + catchError(error => of(refreshRoomFailure({ error }))) + ); + }) + ); + }, + { dispatch: false } + ); + + // 대화상대 초대 성공 후 처리. + inviteSuccess$ = createEffect(() => + this.actions$.pipe( + ofType(RoomStore.inviteSuccess), + withLatestFrom( + this.store.pipe( + select((state: any) => state.messenger.room.roomInfo as RoomInfo) + ), + this.store.pipe( + select((state: any) => state.messenger.sync.room.syncDate as string) + ) + ), + map(([action, roomInfo, roomSyncDate]) => { + const loginInfo = this.sessionStorageService.get( + KEY_LOGIN_INFO + ); + + this.store.dispatch( + room({ + syncDate: roomSyncDate, + localeCode: loginInfo.localeCode + }) + ); + + if (!!roomInfo && roomInfo.roomSeq === action.roomSeq) { + return ChatStore.selectedRoom({ roomSeq: action.roomSeq }); + } + }) + ) + ); + + // 대화상대 강제퇴장 수행 후 처리. + // exitForcingNotificationRes$ = createEffect( + // () => { + // return this.actions$.pipe( + // ofType(RoomStore.exitForcingNotificationRes), + // withLatestFrom( + // this.store.pipe( + // select((state: any) => state.messenger.room.roomInfo as RoomInfo) + // ), + // this.store.pipe( + // select((state: any) => state.messenger.sync.room.syncDate as string) + // ) + // ), + // tap(([action, roomInfo, roomSyncDate]) => { + // if (!!roomInfo && roomInfo.roomSeq === action.noti.roomSeq) { + // this.store.dispatch( + // ChatStore.selectedRoom({ roomSeq: action.noti.roomSeq }) + // ); + // } + + // const loginInfo = this.sessionStorageService.get( + // KEY_LOGIN_INFO + // ); + + // this.store.dispatch( + // room({ + // syncDate: roomSyncDate, + // localeCode: loginInfo.localeCode + // }) + // ); + // }) + // ); + // }, + // { dispatch: false } + // ); + exitForcingSuccess$ = createEffect( + () => { + return this.actions$.pipe( + ofType(RoomStore.exitForcingSuccess), + withLatestFrom( + this.store.pipe( + select((state: any) => state.messenger.room.roomInfo as RoomInfo) + ), + this.store.pipe( + select((state: any) => state.messenger.sync.room.syncDate as string) + ) + ), + tap(([action, roomInfo, roomSyncDate]) => { + // ROOM::열려 있는 대화방에서 강퇴 인원의 isJoinRoom = false 로 변경. + this.store.dispatch( + RoomStore.exitNotificationOthers({ + roomSeq: action.res.roomSeq, + trgtUser: action.res.userSeqs + }) + ); + + // SYNC::대화리스트의 대화자정보에서 강퇴 인원의 isJoinRoom = false 로 변경. + this.store.dispatch( + clearRoomUsers({ + roomSeq: action.res.roomSeq, + userSeqs: action.res.userSeqs + }) + ); + // if (!!roomInfo && roomInfo.roomSeq === action.res.roomSeq) { + // this.store.dispatch( + // ChatStore.selectedRoom({ roomSeq: action.res.roomSeq }) + // ); + // } + + // const loginInfo = this.sessionStorageService.get( + // KEY_LOGIN_INFO + // ); + + // this.store.dispatch( + // room({ + // syncDate: roomSyncDate, + // localeCode: loginInfo.localeCode + // }) + // ); + }) + ); + }, + { dispatch: false } + ); + // 대화방에서 강제퇴장 받은 후 처리. + exitForcingNotification$ = createEffect( + () => { + return this.actions$.pipe( + ofType(RoomStore.exitForcingNotification), + withLatestFrom( + this.store.pipe( + select((state: any) => state.messenger.room.roomInfo as RoomInfo) + ) + ), + tap(([action, roomInfo]) => { + if (!!roomInfo && roomInfo.roomSeq === action.noti.roomSeq) { + // 열려 있는 대화방을 닫음. + this.store.dispatch(ChatStore.clearSelectedRoom()); + } + + // sync.roomList 에서 방 삭제. + this.store.dispatch( + RoomStore.exitSuccess({ + res: { + roomSeq: action.noti.roomSeq + } + }) + ); + }) + ); + }, + { dispatch: false } + ); + + createGroup$ = createEffect(() => + this.actions$.pipe( + ofType(createGroup), + map(action => action.groupName), + exhaustMap(req => { + return this.groupProtocolService + .add({ + groupName: req + }) + .pipe( + map((res: GroupAddResponse) => { + return createGroupSuccess(res); + }), + catchError(error => of(createGroupFailure({ error }))) + ); + }) + ) + ); + + createGroupAndBuddy$ = createEffect(() => + this.actions$.pipe( + ofType(createGroupAndBuddy), + withLatestFrom( + this.store.pipe( + select( + (state: any) => + state.messenger.sync.buddy2.entities as Dictionary + ) + ) + ), + exhaustMap(([action, buddyList]) => { + return this.groupProtocolService + .add({ + groupName: action.groupName + }) + .pipe( + map((res: GroupAddResponse) => { + return createGroupSuccess(res); + }), + tap(res => { + if (!!action.trgtUserSeq && action.trgtUserSeq.length > 0) { + // 그룹원으로 추가할 대상이 유입. + + // STEP 1 : 그룹원으로 등록될 대상중 Buddy 등록해야 하는 인원 수집. + const addBuddyList: number[] = []; + action.trgtUserSeq.forEach(item => { + if (!buddyList[item]) { + addBuddyList.push(item); + } + }); + + if (addBuddyList.length > 0) { + this.store.dispatch(addBuddy({ userSeqs: addBuddyList })); + } + + this.store.dispatch( + updateGroup({ + // 0: 동료그룹SEQ(n) + groupSeq: res.groupSeq, + groupName: res.groupName, + userSeqs: action.trgtUserSeq + }) + ); + } + }), + catchError(error => of(createGroupFailure({ error }))) + ); + }) + ) + ); + + updateGroupMember$ = createEffect( + () => { + return this.actions$.pipe( + ofType(updateGroupMember), + withLatestFrom( + this.store.pipe( + select( + (state: any) => + state.messenger.sync.buddy2.entities as Dictionary + ) + ), + this.store.pipe( + select( + (state: any) => + state.messenger.sync.group2.entities as Dictionary< + GroupDetailData + > + ) + ), + this.store.pipe( + select( + (state: any) => + state.messenger.query.myDepartmentUserInfoList as UserInfoSS[] + ) + ) // 내 부서원 비교. + ), + tap(([action, buddyList, groupList, myDeptUserList]) => { + // Add Buddy + const addBuddyList: number[] = []; + action.trgtUserSeq.forEach(item => { + if (!buddyList[item]) { + addBuddyList.push(item); + } + }); + + // Del Buddy + let delBuddyInGroup: number[] = action.oldGroup.userSeqs.filter( + v => action.trgtUserSeq.indexOf(v) < 0 + ); + + // 소속부서(내부서) 고정그룹 사용시 소속부서원을 삭제하지 않는다. + if (environment.productConfig.CommonSetting.useMyDeptGroup) { + delBuddyInGroup = delBuddyInGroup.filter( + delbuddy => + myDeptUserList.filter(deptUser => deptUser.seq === delbuddy) + .length === 0 + ); + } + delBuddyInGroup = delBuddyInGroup.filter(delbuddy => { + let exist = false; + // tslint:disable-next-line: forin + for (const key in groupList) { + const group: GroupDetailData = groupList[key]; + if ( + group.seq !== action.oldGroup.seq && + group.userSeqs.filter(v => v === delbuddy).length > 0 + ) { + exist = true; + break; + } + } + return !exist; + }); + + if (addBuddyList.length > 0) { + this.store.dispatch(addBuddy({ userSeqs: addBuddyList })); + } + + if (delBuddyInGroup.length > 0) { + // 즐겨찾기 해제. + delBuddyInGroup.forEach(buddySeq => { + this.buddyProtocolService + .update({ + seq: buddySeq, + isFavorit: false + }) + .pipe(catchError(error => of(delBuddyFailure({ error })))); + }); + + // 동료 삭제 + this.store.dispatch(delBuddy({ userSeqs: delBuddyInGroup })); + } + + this.logger.debug('group member update', action.trgtUserSeq); + this.store.dispatch( + updateGroup({ + groupSeq: action.oldGroup.seq, + groupName: action.oldGroup.name, + userSeqs: action.trgtUserSeq + }) + ); + }) + ); + }, + { dispatch: false } + ); + + moveGroupMember$ = createEffect(() => + this.actions$.pipe( + ofType(moveGroupMember), + withLatestFrom( + this.store.pipe( + select( + (state: any) => + state.messenger.sync.group2.entities as Dictionary< + GroupDetailData + > + ) + ), + this.store.pipe( + select((state: any) => state.messenger.sync.group2.syncDate as string) + ) + ), + exhaustMap(([action, groupList, syncDate]) => { + // copy to + let toTrgtUserSeqs = groupList[action.toGroup.seq].userSeqs; + action.trgtUserSeq.forEach(trgtSeq => { + if (toTrgtUserSeqs.indexOf(trgtSeq) > -1) { + // ignore + } else { + toTrgtUserSeqs = toTrgtUserSeqs.concat(trgtSeq); + } + }); + + return this.groupProtocolService + .update2({ + groupSeq: action.toGroup.seq, + groupName: action.toGroup.name, + userSeqs: toTrgtUserSeqs + }) + .pipe( + exhaustMap((resTo: GroupUpdateResponse) => { + // del from + const fromTrgtUserSeqs = groupList[action.fromGroup.seq].userSeqs; + + return this.groupProtocolService + .update2({ + groupSeq: action.fromGroup.seq, + groupName: action.fromGroup.name, + userSeqs: fromTrgtUserSeqs.filter( + trgtSeq => action.trgtUserSeq.indexOf(trgtSeq) < 0 + ) + }) + .pipe( + map((resFrom: GroupUpdateResponse) => { + return group2({ + syncDate + }); + }), + catchError(error => of(moveGroupFromFailure({ error }))) + ); + }), + catchError(error => of(moveGroupToFailure({ error }))) + ); + }) + ) + ); + + delGroup$ = createEffect(() => + this.actions$.pipe( + ofType(delGroup), + withLatestFrom( + this.store.pipe( + select( + (state: any) => + state.messenger.sync.group2.entities as Dictionary< + GroupDetailData + > + ) + ), + this.store.pipe( + select( + (state: any) => + state.messenger.query.myDepartmentUserInfoList as UserInfoSS[] + ) + ) // 내 부서원 비교. + ), + exhaustMap(([action, groupList, myDeptUserList]) => { + // Del Buddy + const trgtBuddys = action.group.userSeqs; + + let delBuddyList: number[] = []; + // 소속부서(내부서) 고정그룹 사용시 소속부서원을 삭제하지 않는다. + if (environment.productConfig.CommonSetting.useMyDeptGroup) { + delBuddyList = trgtBuddys.filter( + delbuddy => + myDeptUserList.filter(deptUser => deptUser.seq === delbuddy) + .length === 0 + ); + } + + delBuddyList = trgtBuddys.filter(delbuddy => { + let exist = false; + // tslint:disable-next-line: forin + for (const key in groupList) { + const group: GroupDetailData = groupList[key]; + if ( + group.seq !== action.group.seq && + group.userSeqs.filter(v => v === delbuddy).length > 0 + ) { + exist = true; + break; + } + } + return !exist; + }); + + if (delBuddyList.length > 0) { + this.logger.debug('Del Buddy', delBuddyList); + // 즐겨찾기 해제. + delBuddyList.forEach(buddySeq => { + this.buddyProtocolService + .update({ + seq: buddySeq, + isFavorit: false + }) + .pipe(catchError(error => of(delBuddyFailure({ error })))); + }); + + // 동료 삭제 + this.store.dispatch(delBuddy({ userSeqs: delBuddyList })); + } + + return this.groupProtocolService + .del({ + groupSeq: action.group.seq + }) + .pipe( + map((res: GroupDelResponse) => { + // this.store.dispatch(delGroupSuccess(res)); + return delGroupSuccess(res); + }), + catchError(error => of(delGroupFailure({ error }))) + ); + }) + ) + ); + + addBuddy$ = createEffect(() => + this.actions$.pipe( + ofType(addBuddy), + withLatestFrom( + this.store.pipe( + select((state: any) => state.messenger.sync.buddy2.syncDate as string) + ) + ), + exhaustMap(([req, syncDate]) => + this.buddyProtocolService.add(req).pipe( + map((res: BuddyAddResponse) => { + return buddy2({ + syncDate + }); + }), + catchError(error => of(addBuddyFailure({ error }))) + ) + ) + ) + ); + + delBuddy$ = createEffect(() => + this.actions$.pipe( + ofType(delBuddy), + withLatestFrom( + this.store.pipe( + select((state: any) => state.messenger.sync.buddy2.syncDate as string) + ) + ), + exhaustMap(([req, syncDate]) => + this.buddyProtocolService.del(req).pipe( + map((res: BuddyDelResponse) => { + return delBuddySuccess(res); + }), + catchError(error => of(delBuddyFailure({ error }))) + ) + ) + ) + ); + + delBuddyAndClear$ = createEffect( + () => { + return this.actions$.pipe( + ofType(delBuddyAndClear), + withLatestFrom( + this.store.pipe( + select( + (state: any) => + state.messenger.sync.group2.entities as Dictionary< + GroupDetailData + > + ) + ), + this.store.pipe( + select( + (state: any) => + state.messenger.query.myDepartmentUserInfoList as UserInfoSS[] + ) + ) // 내 부서원 비교. + ), + tap(([req, groupList, myDeptUserList]) => { + // tslint:disable-next-line: forin + for (const key in groupList) { + const group: GroupDetailData = groupList[key]; + if (group.userSeqs.indexOf(req.seq) > -1) { + // 소속부서(내부서) 고정그룹 사용시 소속부서원을 삭제하지 않는다. + if ( + environment.productConfig.CommonSetting.useMyDeptGroup && + environment.productConfig.CommonSetting.fixedGroupSeqs.filter( + fixedGroupSeq => fixedGroupSeq === group.seq + ).length > 0 + ) { + // skip;; + } else { + // Group Clear.. + this.store.dispatch( + updateGroup({ + groupSeq: group.seq, + groupName: group.name, + userSeqs: group.userSeqs.filter( + userSeq => userSeq !== req.seq + ) + }) + ); + } + } + } + + // 소속부서(내부서) 고정그룹 사용시 소속부서원을 삭제하지 않는다. + if ( + environment.productConfig.CommonSetting.useMyDeptGroup && + myDeptUserList.filter(deptUser => deptUser.seq === req.seq).length > + 0 + ) { + // skip;; + } else { + // Favorit Clear.. + this.store.dispatch( + updateBuddy({ + seq: req.seq, + isFavorit: false + }) + ); + + // Buddy Clear.. + this.store.dispatch(delBuddy({ userSeqs: [req.seq] })); + } + }) + ); + }, + { dispatch: false } + ); + + updateBuddy$ = createEffect(() => + this.actions$.pipe( + ofType(updateBuddy), + exhaustMap(req => + this.buddyProtocolService.update(req).pipe( + map((res: BuddyUpdateResponse) => { + return updateBuddySuccess(res); + }), + catchError(error => of(updateBuddyFailure({ error }))) + ) + ) + ) + ); + + updateGroup$ = createEffect(() => + this.actions$.pipe( + ofType(updateGroup), + withLatestFrom( + this.store.pipe( + select((state: any) => state.messenger.sync.group2.syncDate as string) + ) + ), + concatMap(([req, syncDate]) => + this.groupProtocolService.update2(req).pipe( + map((res: GroupUpdateResponse) => { + return group2({ + syncDate + }); + }), + catchError(error => of(updateGroupFailure({ error }))) + ) + ) + ) + ); + + phoneBookReady$ = createEffect(() => + this.actions$.pipe( + ofType(phoneBookReady), + concatMap(action => + this.syncProtocolService.phoneBookReady(action.req).pipe( + map((res: PhoneBookReadyResponse) => { + return phoneBookReadySuccess({ res }); + // return phoneBookReqSyncDate({ + // syncStartDate: new Date().toString() + // }); + }), + catchError(error => of(phoneBookReadyFailure({ error }))) + ) + ) + ) + ); + + phoneBookReadySuccess$ = createEffect( + () => + this.actions$.pipe( + ofType(phoneBookReadySuccess), + tap(() => { + this.syncProtocolService.notification$ + .pipe( + take(1), + timeout(10000), + tap(noti => { + switch (noti.SSVC_TYPE) { + case SSVC_TYPE_SYNC_PHONEBOOK_SND_NOTI: + { + console.log( + 'SSVC_TYPE_SYNC_PHONEBOOK_SND_NOTI', + noti as PhoneBookSndNotification + ); + } + break; + default: + break; + } + }) + ) + .subscribe( + x => this.logger.debug(x), + err => { + if (err instanceof TimeoutError) { + this.logger.debug('TimeoutError'); + return; + } + this.logger.debug(err); + } + ); + }) + ), + { dispatch: false } + ); + + // phoneBookReadySuccess$ = createEffect(() => + // this.actions$.pipe( + // ofType(phoneBookReadySuccess), + // tap(() => { + // this.syncProtocolService.notification$ + // .pipe( + // take(1), + // timeout(30 * 1000), + // tap(noti => { + // switch (noti.SSVC_TYPE) { + // case SSVC_TYPE_SYNC_PHONEBOOK_SND_NOTI: + // { + // console.log( + // 'SSVC_TYPE_SYNC_PHONEBOOK_SND_NOTI', + // noti as PhoneBookSndNotification + // ); + // } + // break; + // default: + // break; + // } + // }) + // ) + // .subscribe(); + // }) + // ) + // ); + + phoneBookReadyOk$ = createEffect(() => + this.actions$.pipe( + ofType(phoneBookReadyOk), + map(action => action.req), + exhaustMap(req => { + return this.syncProtocolService.phoneBookReadyOk(req).pipe( + map((res: PhoneBookReadyOkResponse) => { + return phoneBookReadyOkSuccess({ res }); + }), + catchError(error => of(phoneBookReadyOkFailure({ error }))) + ); + }) + ) + ); + + phoneBookSndNotification$ = createEffect( + () => { + return this.actions$.pipe( + ofType(phoneBookSndNotification), + // map(action => action.noti), + map(action => { + const noti = action.noti as PhoneBookSndNotification; + const prePhoneBook = this.localStorageService.encGet< + PhoneBookSndNotification + >(KEY_PHONE_BOOK, environment.customConfig.appKey); + let data: PhoneBookSndNotification; + + if (!prePhoneBook) { + data = noti; + } else if (noti.pageTotalCount > prePhoneBook.phoneBookInfos.length) { + prePhoneBook.phoneBookInfos.push( + ...(noti as PhoneBookSndNotification).phoneBookInfos + ); + + data = prePhoneBook; + } + + this.localStorageService.encSet( + KEY_PHONE_BOOK, + data, + environment.customConfig.appKey + ); + + this.store.dispatch( + phoneBookRcv({ + req: { + syncSuccessType: PhoneBookSyncType.OK_Y, + destination: PhoneBookSyncType.TO_MOBILE, + syncType: PhoneBookSyncType.SYNC_TYPE_WHOLE, + pageTotalCount: noti.pageTotalCount, + pageCurrent: noti.pageCurrent, + pageListCount: noti.pageListCount + } + }) + ); + }) + ); + }, + { dispatch: false } + ); + + phoneBookRcv$ = createEffect(() => + this.actions$.pipe( + ofType(phoneBookRcv), + concatMap(action => + this.syncProtocolService.phoneBookRcv(action.req).pipe( + map((res: PhoneBookRcvResponse) => { + return phoneBookRcvSuccess(res); + }), + catchError(error => of(phoneBookRcvFailure(error))) + ) + ) + ) + ); + + constructor( + private actions$: Actions, + private store: Store, + private syncProtocolService: SyncProtocolService, + private roomProtocolService: RoomProtocolService, + private groupProtocolService: GroupProtocolService, + private buddyProtocolService: BuddyProtocolService, + private sessionStorageService: SessionStorageService, + private translateService: TranslateService, + private dialogService: DialogService, + private localStorageService: LocalStorageService, + private logger: NGXLogger + ) {} +}