import { CommonApiService, MassTalkSaveRequest, FileTalkShareRequest, FileTalkShareMultiRequest, FileTalkShareMultiResponse } from '@ucap-webmessenger/api-common'; import { KEY_ENVIRONMENTS_INFO } from './../../../types/environment.type'; import { Injectable, Inject } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Store, select } from '@ngrx/store'; import { NGXLogger } from 'ngx-logger'; import { of } from 'rxjs'; import { tap, switchMap, map, catchError, exhaustMap, withLatestFrom, concatMap, take } from 'rxjs/operators'; import moment from 'moment'; import { InfoData, Info, InfoResponse, EventProtocolService, SVC_TYPE_EVENT, SSVC_TYPE_EVENT_INFO_DATA, SSVC_TYPE_EVENT_INFO_RES, SendResponse, ReadResponse, DelResponse, CancelResponse, EventType, ReadNotification, SSVC_TYPE_EVENT_SEND_RES, SSVC_TYPE_EVENT_SEND_NOTI, EventJson, StickerEventJson, FileEventJson, BundleImageEventJson, MassTextEventJson, TranslationEventJson, MassTranslationEventJson, decodeFileEventJson, decodeBundleImageEventJson } from '@ucap-webmessenger/protocol-event'; import * as ChatStore from '@app/store/messenger/chat'; import * as RoomStore from '@app/store/messenger/room'; import * as SyncStore from '@app/store/messenger/sync'; import { NativeService, UCAP_NATIVE_SERVICE, WindowState } from '@ucap-webmessenger/native'; import { ElectronBrowserWindowChannel } from '@ucap-webmessenger/electron-core'; import { info, infoSuccess, infoFailure, send, sendSuccess, sendFailure, appendInfoList, newInfo, sendNotification, readNotification, cancelNotification, delNotification, recallInfoList, read, readFailure, del, delFailure, delInfoList, cancel, cancelFailure, forward, forwardAfterRoomOpen, sendMass, sendMassFailure, infoMoreSuccess, infoIntervalClear, fileInfo, fileInfoSuccess, fileInfoFailure, roomOpenAfterForward, infoForSearch, infoForSearchEnd, infoAll, forwardFailure } from './actions'; import { SessionStorageService } from '@ucap-webmessenger/web-storage'; import { RoomInfo, RoomProtocolService, OpenResponse } from '@ucap-webmessenger/protocol-room'; import { LoginInfo, KEY_LOGIN_INFO, EnvironmentsInfo } from '@app/types'; import { Dictionary } from '@ngrx/entity'; import { openSuccess, openFailure } from '../room'; import { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type'; import { StatusCode } from '@ucap-webmessenger/api'; import { FileProtocolService, SSVC_TYPE_FILE_INFO_DATA, SSVC_TYPE_FILE_INFO_CHECK_DATA, SSVC_TYPE_FILE_INFO_RES, FileInfo, FileDownloadInfo, InfoData as FileInfoData, InfoCheckData as FileInfoCheckData, InfoResponse as FileInfoResponse, FileType } from '@ucap-webmessenger/protocol-file'; import { environment } from '../../../../environments/environment'; import { AlertDialogComponent, AlertDialogResult, AlertDialogData, DialogService } from '@ucap-webmessenger/ui'; import { TranslateService } from '@ngx-translate/core'; @Injectable() export class Effects { // selectedRoomForInfo$ = createEffect(() => // this.actions$.pipe( // ofType(ChatStore.selectedRoom), // map(action => { // return info({ // roomSeq: action.roomSeq, // baseSeq: 0, // requestCount: environment.productConfig.CommonSetting.eventRequestDefaultCount // }); // }) // ) // ); selectedRoomForInfo$ = createEffect(() => this.actions$.pipe( ofType(RoomStore.infoSuccess), map(action => { const roomInfo = action.roomInfo; let requestCount = environment.productConfig.CommonSetting.eventRequestInitCount; // 여기까지 읽음 처리.. if (!!roomInfo.finalEventSeq && !!roomInfo.lastReadEventSeq) { requestCount = roomInfo.finalEventSeq - roomInfo.lastReadEventSeq; // 기존 요청개수보다 요청할 갯수가 적을 경우 기본값. if ( environment.productConfig.CommonSetting.eventRequestInitCount >= requestCount ) { requestCount = environment.productConfig.CommonSetting.eventRequestInitCount; } else { // 여기까지 읽음 처리를 위한 최대 요청 개수 제한. if ( environment.productConfig.CommonSetting .readHereShowMaximumEventCount < requestCount ) { requestCount = environment.productConfig.CommonSetting .readHereEventRequestCount; } else { requestCount = requestCount + 5; } } } return info({ roomSeq: roomInfo.roomSeq, baseSeq: 0, requestCount }); }) ) ); info$ = createEffect( () => { let infoList: Info[]; return this.actions$.pipe( ofType(info), tap(() => { infoList = []; }), switchMap(req => { return this.eventProtocolService.info(req).pipe( map(res => { switch (res.SSVC_TYPE) { case SSVC_TYPE_EVENT_INFO_DATA: infoList.push(...(res as InfoData).infoList); break; case SSVC_TYPE_EVENT_INFO_RES: { if (req.baseSeq === 0) { this.store.dispatch( infoSuccess({ infoList, res: res as InfoResponse, remainInfo: infoList.length === req.requestCount ? true : false }) ); } else { this.store.dispatch( infoMoreSuccess({ infoList, res: res as InfoResponse, remainInfo: infoList.length === req.requestCount ? true : false }) ); } if (req.baseSeq === 0) { // 최초 이벤트 목록 조회 // SSVC_TYPE_EVENT_READ_REQ 수행. const maxSeq = Math.max.apply( Math, infoList.map(v => v.seq) ); this.store.dispatch( read({ roomSeq: req.roomSeq, lastReadSeq: Number(maxSeq) }) ); // File 정보 수집. this.store.dispatch( fileInfo({ req: { roomSeq: req.roomSeq, // { 파일타입 } cf) I : 이미지 V: 동영상 F: 파일 "" 빈값이면 모든 타입을 내려줌 type: FileType.All } }) ); } } break; } }), catchError(error => of(infoFailure({ error }))) ); }) ); }, { dispatch: false } ); infoForSearch$ = createEffect( () => { let infoList: Info[]; return this.actions$.pipe( ofType(infoForSearch), tap(() => { infoList = []; }), withLatestFrom( this.store.pipe( select( (state: any) => state.messenger.event.infoSearchListProcessing as boolean ) ) ), switchMap(([action, processing]) => { return this.eventProtocolService.info(action.req).pipe( map(async res => { const req = action.req; switch (res.SSVC_TYPE) { case SSVC_TYPE_EVENT_INFO_DATA: infoList.push(...(res as InfoData).infoList); break; case SSVC_TYPE_EVENT_INFO_RES: { if (req.baseSeq === 0) { this.store.dispatch( infoSuccess({ infoList, res: res as InfoResponse, remainInfo: infoList.length === req.requestCount ? true : false }) ); } else { this.store.dispatch( infoMoreSuccess({ infoList, res: res as InfoResponse, remainInfo: infoList.length === req.requestCount ? true : false }) ); } // 검색어가 있을경우 조회된 이벤트 리스트 중 검색어를 찾고, 없으면 재귀한다. if (!!action.searchText && infoList.length > 0) { const searchList = infoList.filter(event => { let contents = ''; if (event.type === EventType.Character) { contents = event.sentMessage; } else if ( event.type === EventType.Sticker && !!event.sentMessageJson ) { contents = (event.sentMessageJson as StickerEventJson) .chat; } else if ( event.type === EventType.File && !!event.sentMessageJson ) { contents = (event.sentMessageJson as FileEventJson) .fileName; } else if ( event.type === EventType.MassText && !!event.sentMessageJson ) { contents = (event.sentMessageJson as MassTextEventJson) .content; } else if ( event.type === EventType.Translation && !!event.sentMessageJson ) { contents = (event.sentMessageJson as TranslationEventJson) .original; } else if ( event.type === EventType.MassTranslation && !!event.sentMessageJson ) { contents = (event.sentMessageJson as MassTranslationEventJson) .original; } return contents.indexOf(action.searchText) > -1; }); if ( searchList.length === 0 && infoList.length === action.req.requestCount && processing ) { // 재귀 this.store.dispatch( infoForSearch({ req: { roomSeq: req.roomSeq, baseSeq: infoList[0].seq, requestCount: req.requestCount }, searchText: action.searchText }) ); // // 재귀하지 않는다. // this.store.dispatch(infoForSearchEnd({})); } else { if (infoList.length < action.req.requestCount) { this.store.dispatch(infoForSearchEnd({})); await this.dialogService.open< AlertDialogComponent, AlertDialogData, AlertDialogResult >(AlertDialogComponent, { width: '360px', disableClose: true, data: { title: '', message: this.translateService.instant( 'chat.noMoreEvents' ) } }); } } } } break; } }), catchError(error => of(infoFailure({ error }))) ); }) ); }, { dispatch: false } ); infoAll$ = createEffect( () => { let infoList: Info[]; return this.actions$.pipe( ofType(infoAll), tap(() => { infoList = []; }), withLatestFrom( this.store.pipe( select( (state: any) => state.messenger.event.infoSearchListProcessing as boolean ) ) ), switchMap(([params, processing]) => { const req = params.req; const mergedInfoList = params.infoList; return this.eventProtocolService.info(req).pipe( map(async res => { switch (res.SSVC_TYPE) { case SSVC_TYPE_EVENT_INFO_DATA: infoList.push(...(res as InfoData).infoList); break; case SSVC_TYPE_EVENT_INFO_RES: { if (!!infoList && 0 < infoList.length) { infoList = infoList.sort((a, b) => a.seq - b.seq); } if ( infoList.length > 0 && infoList.length >= req.requestCount && processing ) { // 재귀 this.store.dispatch( infoAll({ req: { roomSeq: req.roomSeq, baseSeq: infoList[0].seq, requestCount: req.requestCount }, infoList: !!mergedInfoList ? [...infoList, ...mergedInfoList] : infoList }) ); } else { this.store.dispatch( infoMoreSuccess({ infoList: !!mergedInfoList ? [...infoList, ...mergedInfoList] : infoList, res: res as InfoResponse, remainInfo: infoList.length === req.requestCount ? true : false }) ); this.store.dispatch(infoForSearchEnd({})); } } break; } }), catchError(error => of(infoFailure({ error }))) ); }) ); }, { dispatch: false } ); fileInfo$ = createEffect( () => { let fileInfoList: FileInfo[]; let fileInfoCheckList: FileDownloadInfo[]; return this.actions$.pipe( ofType(fileInfo), tap(() => { fileInfoList = []; fileInfoCheckList = []; }), switchMap(action => { return this.fileProtocolService.info(action.req).pipe( map(res => { switch (res.SSVC_TYPE) { case SSVC_TYPE_FILE_INFO_DATA: fileInfoList.push(...(res as FileInfoData).fileInfos); break; case SSVC_TYPE_FILE_INFO_CHECK_DATA: fileInfoCheckList.push( ...(res as FileInfoCheckData).fileDownloadInfos ); break; case SSVC_TYPE_FILE_INFO_RES: { this.store.dispatch( fileInfoSuccess({ fileInfoList, fileInfoCheckList, res: res as FileInfoResponse }) ); } break; } }), catchError(error => of(fileInfoFailure({ error }))) ); }) ); }, { dispatch: false } ); infoIntervalClear$ = createEffect( () => { return this.actions$.pipe( ofType(infoIntervalClear), withLatestFrom( this.store.pipe( select((state: any) => state.messenger.room.roomInfo as RoomInfo) ), this.store.pipe( select( (state: any) => state.messenger.event.infoList.entities as Dictionary< Info > ) ) ), map(([action, roomInfo, eventList]) => { if (roomInfo.isTimeRoom && roomInfo.timeRoomInterval > 0) { const delEventSeq: number[] = []; // tslint:disable-next-line: forin for (const key in eventList) { const event: Info = eventList[key]; if ( new Date().getTime() - moment(event.sendDate) .toDate() .getTime() >= roomInfo.timeRoomInterval * 1000 ) { delEventSeq.push(event.seq); } } if (delEventSeq.length > 0) { this.store.dispatch(delInfoList({ eventSeqs: delEventSeq })); } } }) ); }, { dispatch: false } ); read$ = createEffect(() => this.actions$.pipe( ofType(read), exhaustMap(req => this.eventProtocolService.read(req).pipe( map((res: ReadResponse) => { return readNotification(res as ReadNotification); }), catchError(error => of(readFailure({ error }))) ) ) ) ); readNotification$ = createEffect( () => { return this.actions$.pipe( ofType(readNotification), withLatestFrom( this.store.pipe( select((state: any) => state.messenger.room.roomInfo as RoomInfo) ), this.store.pipe( select( (state: any) => state.account.authentication.loginRes as LoginResponse ) ) ), // map(([action, roomInfo]) => ([action.noti, roomInfo])), tap(([action, roomInfo, loginRes]) => { // 현재 오픈된 방에 대한 read noti/res 가 유입될 경우 roomUserList 의 lastReadSeq 를 갱신한다. if ( !!roomInfo && !!roomInfo.roomSeq && roomInfo.roomSeq === action.roomSeq ) { this.store.dispatch(RoomStore.updateRoomUserLastReadSeq(action)); } // noti/res 를 일으킨 주체가 본인이라면 현재 오픈된 방 여부에 상관없이 해당방에 대한 noReadCnt 를 초기화 한다. if (action.SENDER_SEQ === loginRes.userSeq) { this.store.dispatch( SyncStore.updateUnreadCount({ roomSeq: action.roomSeq, noReadCnt: 0 }) ); } }) ); }, { dispatch: false } ); send$ = createEffect(() => this.actions$.pipe( ofType(send), concatMap(action => this.eventProtocolService.send(action.req).pipe( map((res: SendResponse) => { return sendSuccess({ senderSeq: action.senderSeq, res }); }), catchError(error => of(sendFailure({ error }))) ) ) ) ); sendSuccess$ = createEffect( () => { return this.actions$.pipe( ofType(sendSuccess), tap(action => { const res = action.res; this.store.dispatch( newInfo({ roomSeq: res.roomSeq, info: res.info, SVC_TYPE: res.SVC_TYPE, SSVC_TYPE: res.SSVC_TYPE }) ); }) ); }, { dispatch: false } ); sendNotification$ = createEffect( () => { return this.actions$.pipe( ofType(sendNotification), map(action => action.noti), tap(noti => { this.store.dispatch( newInfo({ roomSeq: noti.roomSeq, info: noti.info, SVC_TYPE: noti.SVC_TYPE, SSVC_TYPE: noti.SSVC_TYPE }) ); }) ); }, { dispatch: false } ); forward$ = createEffect( () => { return this.actions$.pipe( ofType(forward), tap(action => { const loginResInfo: LoginResponse = this.sessionStorageService.get< LoginResponse >(KEY_LOGIN_RES_INFO); const environmentsInfo = this.sessionStorageService.get< EnvironmentsInfo >(KEY_ENVIRONMENTS_INFO); if (!!action.trgtRoomSeq) { // 대화전달 후 방오픈. Exist roomSeq. if (action.req.eventType === EventType.File) { const fileEventJson: FileEventJson = decodeFileEventJson( action.req.sentMessage ); const req: FileTalkShareRequest = { userSeq: loginResInfo.userSeq, deviceType: environmentsInfo.deviceType, token: loginResInfo.tokenString, attachmentsSeq: fileEventJson.attachmentSeq.toString(), roomSeq: action.trgtRoomSeq, synapKey: '' }; this.commonApiService .fileTalkShare(req) .pipe( take(1), map(res => { if (res.statusCode === StatusCode.Success) { action = { ...action, req: { ...action.req, sentMessage: res.returnJson } }; this.store.dispatch(roomOpenAfterForward(action)); } else { this.store.dispatch(forwardFailure({ error: res })); } }), catchError(error => of(forwardFailure({ error }))) ) .subscribe(); } else if (action.req.eventType === EventType.BundleImage) { const fileEventJson: BundleImageEventJson = decodeBundleImageEventJson( action.req.sentMessage ); const req: FileTalkShareMultiRequest = { userSeq: loginResInfo.userSeq, deviceType: environmentsInfo.deviceType, token: loginResInfo.tokenString, attachmentsSeq: fileEventJson.attachmentSeq.toString(), roomSeq: action.trgtRoomSeq }; this.commonApiService .fileTalkShareMulti(req) .pipe( take(1), map(res => { if (res.statusCode === StatusCode.Success) { action = { ...action, req: { ...action.req, sentMessage: res.returnJson } }; this.store.dispatch(roomOpenAfterForward(action)); } else { this.store.dispatch(forwardFailure({ error: res })); } }), catchError(error => of(forwardFailure({ error }))) ) .subscribe(); } else { this.store.dispatch(roomOpenAfterForward(action)); } } else if (!!action.trgtUserSeqs && action.trgtUserSeqs.length > 0) { // 방오픈 후 대화전달. this.store.dispatch(forwardAfterRoomOpen(action)); } }) ); }, { dispatch: false } ); forwardAfterRoomOpen$ = createEffect(() => this.actions$.pipe( ofType(forwardAfterRoomOpen), exhaustMap(action => { return this.roomProtocolService .open({ divCd: 'forwardOpen', userSeqs: action.trgtUserSeqs }) .pipe( map((res: OpenResponse) => { return openSuccess({ res }); }), map(res => { const loginResInfo: LoginResponse = this.sessionStorageService.get< LoginResponse >(KEY_LOGIN_RES_INFO); const environmentsInfo = this.sessionStorageService.get< EnvironmentsInfo >(KEY_ENVIRONMENTS_INFO); if (action.req.eventType === EventType.File) { const fileEventJson: FileEventJson = decodeFileEventJson( action.req.sentMessage ); const req: FileTalkShareRequest = { userSeq: loginResInfo.userSeq, deviceType: environmentsInfo.deviceType, token: loginResInfo.tokenString, attachmentsSeq: fileEventJson.attachmentSeq.toString(), roomSeq: action.trgtRoomSeq, synapKey: '' }; this.commonApiService .fileTalkShare(req) .pipe( take(1), map(resFileShare => { if (resFileShare.statusCode === StatusCode.Success) { action = { ...action, req: { ...action.req, sentMessage: resFileShare.returnJson } }; this.store.dispatch( send({ senderSeq: action.senderSeq, req: { roomSeq: res.res.roomSeq, eventType: action.req.eventType, sentMessage: action.req.sentMessage } }) ); } else { this.store.dispatch(forwardFailure({ error: res })); } }), catchError(error => of(forwardFailure({ error }))) ) .subscribe(); } else if (action.req.eventType === EventType.BundleImage) { const fileEventJson: BundleImageEventJson = decodeBundleImageEventJson( action.req.sentMessage ); const req: FileTalkShareMultiRequest = { userSeq: loginResInfo.userSeq, deviceType: environmentsInfo.deviceType, token: loginResInfo.tokenString, attachmentsSeq: fileEventJson.attachmentSeq.toString(), roomSeq: action.trgtRoomSeq }; this.commonApiService .fileTalkShareMulti(req) .pipe( take(1), map(resMultiShare => { if (resMultiShare.statusCode === StatusCode.Success) { action = { ...action, req: { ...action.req, sentMessage: resMultiShare.returnJson } }; this.store.dispatch( send({ senderSeq: action.senderSeq, req: { roomSeq: res.res.roomSeq, eventType: action.req.eventType, sentMessage: action.req.sentMessage } }) ); } else { this.store.dispatch( forwardFailure({ error: resMultiShare }) ); } }), catchError(error => of(forwardFailure({ error }))) ) .subscribe(); } else { this.store.dispatch( send({ senderSeq: action.senderSeq, req: { roomSeq: res.res.roomSeq, eventType: action.req.eventType, sentMessage: action.req.sentMessage } }) ); } return res; }), catchError(error => of(openFailure({ error }))) ); }) ) ); roomOpenAfterForward$ = createEffect(() => this.actions$.pipe( ofType(roomOpenAfterForward), concatMap(action => { return this.eventProtocolService .send({ roomSeq: action.trgtRoomSeq, eventType: action.req.eventType, sentMessage: action.req.sentMessage }) .pipe( map((res: SendResponse) => { this.store.dispatch( newInfo({ roomSeq: res.roomSeq, info: res.info, SVC_TYPE: res.SVC_TYPE, SSVC_TYPE: res.SSVC_TYPE }) ); return ChatStore.selectedRoom({ roomSeq: action.trgtRoomSeq }); }), catchError(error => of(sendFailure({ error }))) ); }) ) ); sendMass$ = createEffect( () => { return this.actions$.pipe( ofType(sendMass), exhaustMap(action => { const loginResInfo: LoginResponse = this.sessionStorageService.get< LoginResponse >(KEY_LOGIN_RES_INFO); const environmentsInfo = this.sessionStorageService.get< EnvironmentsInfo >(KEY_ENVIRONMENTS_INFO); const req: MassTalkSaveRequest = { userSeq: loginResInfo.userSeq, deviceType: environmentsInfo.deviceType, token: loginResInfo.tokenString, content: action.req.sentMessage.replace(/"/g, '\\"'), roomSeq: action.req.roomSeq }; return this.commonApiService.massTalkSave(req).pipe( map(res => { if (res.statusCode === StatusCode.Success) { this.store.dispatch( send({ senderSeq: action.senderSeq, req: { roomSeq: res.roomSeq, eventType: EventType.MassText, sentMessage: res.returnJson } }) ); } else { this.store.dispatch(sendMassFailure({ error: res })); } }), catchError(error => of(sendMassFailure({ error }))) ); }) ); }, { dispatch: false } ); newInfo$ = createEffect( () => { return this.actions$.pipe( ofType(newInfo), withLatestFrom( this.store.pipe( select((state: any) => state.messenger.room.roomInfo as RoomInfo) ), this.store.pipe( select( (state: any) => state.messenger.sync.room.entities as Dictionary ) ) ), tap(([action, roomInfo, trgtRoomInfos]) => { // opened room :: event add if (!!roomInfo && roomInfo.roomSeq === action.roomSeq) { if ( action.SVC_TYPE === SVC_TYPE_EVENT && action.SSVC_TYPE === SSVC_TYPE_EVENT_SEND_NOTI ) { this.store.dispatch(appendInfoList({ info: action.info })); const windowState = this.nativeService.getWindowState(); if ( !!windowState && windowState.windowState !== WindowState.Minimized && windowState.windowState !== WindowState.Hidden && windowState.windowFocusState === ElectronBrowserWindowChannel.Focus ) { this.store.dispatch( read({ roomSeq: action.roomSeq, lastReadSeq: action.info.seq }) ); } else { // Blur 상태 일 때 } } if ( action.info.type === EventType.File || action.info.type === EventType.BundleImage ) { // File 정보 수집. this.store.dispatch( fileInfo({ req: { roomSeq: action.roomSeq, // { 파일타입 } cf) I : 이미지 V: 동영상 F: 파일 "" 빈값이면 모든 타입을 내려줌 type: FileType.All } }) ); } } else { // not opened room :: unread count increased if ( action.SVC_TYPE === SVC_TYPE_EVENT && action.SSVC_TYPE === SSVC_TYPE_EVENT_SEND_RES ) { /** * 다른 디바이스에서 대화를 송신 할경우 RES 가 noti 로 유입될 수 있다. * 이때 unread count 를 중가하지 않는다. */ } else { if ( !!trgtRoomInfos && !!trgtRoomInfos[action.roomSeq] && action.info.type !== EventType.Join && action.info.type !== EventType.Exit && action.info.type !== EventType.ForcedExit ) { const noReadCnt = trgtRoomInfos[action.roomSeq].noReadCnt; this.store.dispatch( SyncStore.updateUnreadCount({ roomSeq: action.roomSeq, noReadCnt: noReadCnt + 1 }) ); } } } // 대화 > 리스트 :: finalEventMessage refresh this.store.dispatch(ChatStore.newEventMessage(action)); }) ); }, { dispatch: false } ); cancel$ = createEffect(() => this.actions$.pipe( ofType(cancel), exhaustMap(req => this.eventProtocolService.cancel(req).pipe( map((res: CancelResponse) => { return cancelNotification({ noti: res }); }), catchError(error => of(cancelFailure({ error }))) ) ) ) ); cancelNotification$ = createEffect( () => { return this.actions$.pipe( ofType(cancelNotification), 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( recallInfoList({ eventSeq: action.noti.eventSeq }) ); } // 대화 > 리스트의 항목 갱신 const loginInfo = this.sessionStorageService.get( KEY_LOGIN_INFO ); this.store.dispatch( SyncStore.refreshRoom({ roomSeq: action.noti.roomSeq, isDetail: false, localeCode: loginInfo.localeCode }) ); }) ); }, { dispatch: false } ); del$ = createEffect(() => this.actions$.pipe( ofType(del), exhaustMap(req => this.eventProtocolService.del(req).pipe( map((res: DelResponse) => { return delNotification({ noti: res }); }), catchError(error => of(delFailure({ error }))) ) ) ) ); delNotification$ = createEffect( () => { return this.actions$.pipe( ofType(delNotification), map(action => action.noti), withLatestFrom( this.store.pipe( select((state: any) => state.messenger.room.roomInfo as RoomInfo) ) ), tap(([noti, roomInfo]) => { // 현재 방이 오픈되어 있으면 방내용 갱신 if (!!roomInfo && roomInfo.roomSeq === noti.roomSeq) { this.store.dispatch(delInfoList({ eventSeqs: [noti.eventSeq] })); } // 대화 > 리스트의 항목 갱신 const loginInfo = this.sessionStorageService.get( KEY_LOGIN_INFO ); this.store.dispatch( SyncStore.refreshRoom({ roomSeq: noti.roomSeq, isDetail: false, localeCode: loginInfo.localeCode }) ); }) ); }, { dispatch: false } ); constructor( private actions$: Actions, private store: Store, @Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService, private commonApiService: CommonApiService, private eventProtocolService: EventProtocolService, private fileProtocolService: FileProtocolService, private roomProtocolService: RoomProtocolService, private sessionStorageService: SessionStorageService, private translateService: TranslateService, private dialogService: DialogService, private logger: NGXLogger ) {} }