import { Injectable } 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 } from 'rxjs/operators'; import { InfoData, Info, InfoResponse, EventProtocolService, SSVC_TYPE_EVENT_INFO_DATA, SSVC_TYPE_EVENT_INFO_RES, SendResponse, ReadResponse, DelResponse, CancelResponse } from '@ucap-webmessenger/protocol-event'; import * as ChatStore from '@app/store/messenger/chat'; import * as EventStore from '@app/store/messenger/event'; import * as SyncStore from '@app/store/messenger/sync'; import { info, infoSuccess, infoFailure, send, sendSuccess, sendFailure, appendInfoList, newInfo, sendNotification, readNotification, cancelNotification, delNotification, recallInfoList, read, readFailure, del, delFailure, delInfoList, cancel, cancelFailure, forward, forwardFailure, forwardAfterRoomOpen } from './actions'; import { SessionStorageService } from '@ucap-webmessenger/web-storage'; import { RoomInfo, RoomProtocolService, OpenResponse } from '@ucap-webmessenger/protocol-room'; import { LoginInfo, KEY_LOGIN_INFO } from '@app/types'; import { Dictionary } from '@ngrx/entity'; import { openSuccess, openFailure } from '../room'; @Injectable() export class Effects { selectedRoomForInfo$ = createEffect(() => this.actions$.pipe( ofType(ChatStore.selectedRoom), map(action => { return info({ roomSeq: action.roomSeq, baseSeq: 0, requestCount: 50 }); }) ) ); 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: { this.store.dispatch( infoSuccess({ infoList, res: res as InfoResponse }) ); 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) }) ); } } break; } }), catchError(error => of(infoFailure({ error }))) ); }) ); }, { dispatch: false } ); read$ = createEffect(() => this.actions$.pipe( ofType(read), exhaustMap(req => this.eventProtocolService.read(req).pipe( map((res: ReadResponse) => { return readNotification({ noti: res }); }), catchError(error => of(readFailure({ error }))) ) ) ) ); readNotification$ = createEffect( () => { return this.actions$.pipe( ofType(readNotification), map(action => action.noti), tap(noti => { this.store.dispatch( SyncStore.updateUnreadCount({ roomSeq: noti.roomSeq, noReadCnt: 0 }) ); }) ); }, { dispatch: false } ); send$ = createEffect(() => this.actions$.pipe( ofType(send), exhaustMap(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; const appendInfo: Info = { seq: res.seq, type: res.eventType, senderSeq: action.senderSeq, sendDate: res.sendDate, sentMessage: res.message, receiverCount: res.receiverCount }; this.store.dispatch( newInfo({ roomSeq: res.roomSeq, info: appendInfo }) ); }) ); }, { dispatch: false } ); sendNotification$ = createEffect( () => { return this.actions$.pipe( ofType(sendNotification), map(action => action.noti), tap(noti => { const appendInfo: Info = { seq: noti.seq, type: noti.eventType, senderSeq: noti.SENDER_SEQ, sendDate: noti.sendDate, sentMessage: noti.message, receiverCount: noti.receiverCount }; this.store.dispatch( newInfo({ roomSeq: noti.roomSeq, info: appendInfo }) ); }) ); }, { dispatch: false } ); forward$ = createEffect( () => { return this.actions$.pipe( ofType(forward), map(action => { if (!!action.trgtRoomSeq) { this.store.dispatch( ChatStore.selectedRoom({ roomSeq: action.trgtRoomSeq }) ); this.store.dispatch( EventStore.send({ senderSeq: action.senderSeq, req: { roomSeq: action.trgtRoomSeq, eventType: action.req.eventType, sentMessage: action.req.sentMessage } }) ); } 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 => { this.store.dispatch( EventStore.send({ senderSeq: action.senderSeq, req: { roomSeq: res.res.roomSeq, eventType: action.req.eventType, sentMessage: action.req.sentMessage } }) ); return res; }), catchError(error => of(openFailure({ error }))) ); }) ) ); 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) { this.store.dispatch(appendInfoList({ info: action.info })); } // not opened room :: unread count increased if (!roomInfo || roomInfo.roomSeq !== action.roomSeq) { if (!!trgtRoomInfos && !!trgtRoomInfos[action.roomSeq]) { 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: true, 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({ eventSeq: noti.eventSeq })); } // 대화 > 리스트의 항목 갱신 const loginInfo = this.sessionStorageService.get( KEY_LOGIN_INFO ); this.store.dispatch( SyncStore.refreshRoom({ roomSeq: noti.roomSeq, isDetail: true, localeCode: loginInfo.localeCode }) ); }) ); }, { dispatch: false } ); constructor( private actions$: Actions, private store: Store, private eventProtocolService: EventProtocolService, private roomProtocolService: RoomProtocolService, private sessionStorageService: SessionStorageService, private logger: NGXLogger ) {} }