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 } from '@ucap-webmessenger/protocol-event'; import * as ChatStore from '@app/store/messenger/chat'; import * as SyncStore from '@app/store/messenger/sync'; import { info, infoSuccess, infoFailure, send, sendSuccess, sendFailure, appendInfoList, newInfo, sendNotification, readNotification, cancelNotification, delNotification, recallInfoList, read, readFailure } from './actions'; import { SessionStorageService } from '@ucap-webmessenger/web-storage'; import { RoomInfo } from '@ucap-webmessenger/protocol-room'; import { refreshRoom } from '../sync'; import { LoginInfo, KEY_LOGIN_INFO } from '@app/types'; import { Dictionary } from '@ngrx/entity'; @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( () => { return this.actions$.pipe( ofType(read), switchMap(req => { return this.eventProtocolService.read(req).pipe( map((res: ReadResponse) => { this.store.dispatch( SyncStore.updateUnreadCount({ roomSeq: res.roomSeq, noReadCnt: 0 }) ); }), catchError(error => of(readFailure({ error }))) ); }) ); }, { 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 } ); 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 } ); readNotification$ = createEffect( () => { return this.actions$.pipe( ofType(readNotification), map(action => action.noti), tap(noti => {}) ); }, { dispatch: false } ); 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.logger.debug('cancelNotification$', action, roomInfo); 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 } ); delNotification$ = createEffect( () => { return this.actions$.pipe( ofType(delNotification), map(action => action.noti), tap(noti => {}) ); }, { dispatch: false } ); constructor( private actions$: Actions, private store: Store, private eventProtocolService: EventProtocolService, private sessionStorageService: SessionStorageService, private logger: NGXLogger ) {} }