import { Injectable, Inject } from '@angular/core'; import { tap, withLatestFrom } from 'rxjs/operators'; import { Store, select } from '@ngrx/store'; import { SSVC_TYPE_LOGOUT_RES, SSVC_TYPE_LOGOUT_REMOTE_NOTI, AuthenticationProtocolService, LogoutResponse, LogoutRemoteNotification, LogoutNotification, LoginResponse } from '@ucap-webmessenger/protocol-authentication'; import { NGXLogger } from 'ngx-logger'; import { EventProtocolService, SSVC_TYPE_EVENT_SEND_NOTI, SendNotification, SSVC_TYPE_EVENT_READ_NOTI, SSVC_TYPE_EVENT_CANCEL_NOTI, SSVC_TYPE_EVENT_DEL_RES, SSVC_TYPE_EVENT_SEND_RES, SSVC_TYPE_EVENT_READ_RES, EventType } from '@ucap-webmessenger/protocol-event'; import { InfoProtocolService, SSVC_TYPE_INFO_USER_NOTI, UserNotification } from '@ucap-webmessenger/protocol-info'; import { RoomProtocolService, SSVC_TYPE_ROOM_INVITE_NOTI, SSVC_TYPE_ROOM_EXIT_NOTI, SSVC_TYPE_ROOM_EXIT_FORCING_NOTI, SSVC_TYPE_ROOM_FONT_UPD_NOTI, InviteNotification, UpdateNotification as RoomUpdateNotification, SSVC_TYPE_ROOM_UPD_RES, SSVC_TYPE_ROOM_EXIT_FORCING_RES, SSVC_TYPE_ROOM_EXIT_RES, SSVC_TYPE_ROOM_INVITE_RES, ExitForcingResponse, RoomInfo } from '@ucap-webmessenger/protocol-room'; import { StatusProtocolService, SSVC_TYPE_STATUS_NOTI, StatusNotification } from '@ucap-webmessenger/protocol-status'; import { ReadNotification, CancelNotification, DelNotification } from '@ucap-webmessenger/protocol-event'; import { ExitNotification, ExitForcingNotification, UpdateFontNotification } from '@ucap-webmessenger/protocol-room'; import { GroupProtocolService, SSVC_TYPE_GROUP_UPD_RES2, UpdateNotification as GroupUpdateNotification, SSVC_TYPE_GROUP_ADD_RES, AddNotification as GroupAddNotification, SSVC_TYPE_GROUP_DEL_RES, DelNotification as GroupDelNotification } from '@ucap-webmessenger/protocol-group'; import { BuddyProtocolService, SSVC_TYPE_BUDDY_UPD_RES, UpdateNotification as BuddyUpdateNotification, SSVC_TYPE_BUDDY_ADD_RES, AddNotification as BuddyAddNotification, SSVC_TYPE_BUDDY_DEL_RES, DelNotification as BuddyDelNotification } from '@ucap-webmessenger/protocol-buddy'; import * as AuthenticationStore from '@app/store/account/authentication'; import * as InfoStore from '@app/store/account/info'; import * as EventStore from '@app/store/messenger/event'; import * as MessageStore from '@app/store/messenger/message'; import * as SyncStore from '@app/store/messenger/sync'; import * as RoomStore from '@app/store/messenger/room'; import * as StatusStore from '@app/store/messenger/status'; import { NotificationRequest, NativeService, UCAP_NATIVE_SERVICE, NotificationType, WindowState } from '@ucap-webmessenger/native'; import { StringUtil, DialogService, TranslateService as UcapTranslateService } from '@ucap-webmessenger/ui'; import { UmgProtocolService, SSVC_TYPE_UMG_NOTI, UmgNotiNotification, SSVC_TYPE_UMG_DELETE_NOTI, UmgDeleteNotiNotification } from '@ucap-webmessenger/protocol-umg'; import { LocalStorageService, SessionStorageService } from '@ucap-webmessenger/web-storage'; import { AppUserInfo, KEY_APP_USER_INFO } from '@app/types/app-user-info.type'; import { environment } from '../../environments/environment'; import { NotificationMethod, LocaleCode } from '@ucap-webmessenger/core'; import { Dictionary } from '@ngrx/entity'; import { MessageType } from '@ucap-webmessenger/api-message'; import { LogoutInfo, KEY_LOGOUT_INFO, KEY_VER_INFO } from '@app/types'; import { TranslateService } from '@ngx-translate/core'; import { deleteMessageSuccess } from '@app/store/messenger/message'; import { ServerErrorCode } from '@ucap-webmessenger/protocol'; import { OptionProtocolService, SSVC_TYPE_OPTION_REG_UPD_RES, RegUpdateResponse, RegUpdateNotification } from '@ucap-webmessenger/protocol-option'; import { GeneralSetting, Settings, NotificationSetting, ChatSetting } from '@ucap-webmessenger/ui-settings'; import clone from 'clone'; import { ElectronBrowserWindowChannel } from '@ucap-webmessenger/electron-core'; import { UserInfo, RoomUserData } from '@ucap-webmessenger/protocol-sync'; import { VersionInfo2Response } from '@ucap-webmessenger/api-public'; import { QueryProtocolService } from '@ucap-webmessenger/protocol-query'; import { UserInfoListState } from '@app/store/messenger/room'; @Injectable() export class AppNotificationService { constructor( private authenticationProtocolService: AuthenticationProtocolService, private eventProtocolService: EventProtocolService, private infoProtocolService: InfoProtocolService, private roomProtocolService: RoomProtocolService, private groupProtocolService: GroupProtocolService, private buddyProtocolService: BuddyProtocolService, private queryProtocolService: QueryProtocolService, private statusProtocolService: StatusProtocolService, private translateService: TranslateService, private ucapTranslateService: UcapTranslateService, private optionProtocolService: OptionProtocolService, private umgProtocolService: UmgProtocolService, private localStorageService: LocalStorageService, private sessionStorageService: SessionStorageService, @Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService, private dialogService: DialogService, private store: Store, private logger: NGXLogger ) {} public subscribe(): void { this.authenticationProtocolService.logoutNotification$ .pipe( tap(notiOrRes => { switch (notiOrRes.SSVC_TYPE) { case SSVC_TYPE_LOGOUT_RES: { const res = notiOrRes as LogoutNotification; this.logger.debug( 'Notification::authenticationProtocolService::LogoutResponse', res ); this.sessionStorageService.set(KEY_LOGOUT_INFO, { personLogout: true, reasonCode: res.reasonCode, ip: res.ip, mac: res.mac } as LogoutInfo); } break; case SSVC_TYPE_LOGOUT_REMOTE_NOTI: { const noti = notiOrRes as LogoutRemoteNotification; this.logger.debug( 'Notification::authenticationProtocolService::LogoutRemoteNotification', noti ); this.sessionStorageService.set(KEY_LOGOUT_INFO, { personLogout: true, reasonCode: ServerErrorCode.ERRCD_FORCE_INIT, forceType: noti.requestDeviceType } as LogoutInfo); } break; default: break; } this.dialogService.closeAll(); this.store.dispatch(AuthenticationStore.loginRedirect()); }) ) .subscribe(); this.eventProtocolService.notification$ .pipe( withLatestFrom( this.store.pipe( select((state: any) => state.messenger.room.roomInfo as RoomInfo) ), this.store.pipe( select( (state: any) => state.messenger.room.userInfoList.entities as Dictionary< UserInfoListState > ) ), this.store.pipe( select( (state: any) => state.messenger.sync.room.entities as Dictionary ) ), this.store.pipe( select( (state: any) => state.messenger.sync.buddy2.entities as Dictionary ) ), this.store.pipe( select( (state: any) => state.messenger.sync.roomUserShort.entities as Dictionary< RoomUserData > ) ) ), tap( ([ notiOrRes, curRoomInfo, curRoomUserInfo, roomList, buddyList, roomUserShorts ]) => { switch (notiOrRes.SSVC_TYPE) { case SSVC_TYPE_EVENT_SEND_RES: case SSVC_TYPE_EVENT_SEND_NOTI: { const noti = notiOrRes as SendNotification; this.logger.debug( 'Notification::eventProtocolService::SendNotification', noti ); this.store.dispatch( EventStore.sendNotification({ noti }) ); // notification.. if (notiOrRes.SSVC_TYPE === SSVC_TYPE_EVENT_SEND_NOTI) { let doNoti = true; const windowState = this.nativeService.getWindowState(); // 현재 열려 있는 방일경우 노티 안함. if ( !!curRoomInfo && !!curRoomInfo.roomSeq && curRoomInfo.roomSeq === noti.roomSeq && !!windowState && windowState.windowState !== WindowState.Minimized && windowState.windowState !== WindowState.Hidden ) { doNoti = false; } // // 포커스 아웃일때 무조건 노티. // // Case 1 : 단순 포커스 아웃. // // Case 2 : hidden 시 포커스 인 상태이지만 위에서 필터링 됨. // console.log(windowState); // if ( // windowState.windowFocusState !== // ElectronBrowserWindowChannel.Focus // ) { // doNoti = true; // } // 방별 알림이 꺼져 있으면 노티 안함. > 우선순위 최상위. if ( !!roomList[noti.roomSeq] && !roomList[noti.roomSeq].receiveAlarm ) { doNoti = false; } if (doNoti) { const appUserInfo = this.localStorageService.encGet< AppUserInfo >(KEY_APP_USER_INFO, environment.customConfig.appKey); if (appUserInfo.settings.notification.use) { if ( appUserInfo.settings.notification.method === NotificationMethod.Sound ) { const audio = new Audio( 'assets/sounds/messageAlarm.mp3' ); audio.play(); } else { const contents = StringUtil.convertFinalEventMessage( noti.eventType, noti.info.sentMessageJson ); if (!!contents) { const notiReq: NotificationRequest = { type: NotificationType.Event, seq: noti.roomSeq, title: this.translateService.instant( 'notification.titleChatEventArrived' ), contents, image: '', useSound: [ NotificationMethod.Sound, NotificationMethod.SoundAndAlert ].some( n => n === appUserInfo.settings.notification.method ) ? true : false, displayTime: appUserInfo.settings.notification .alertExposureTime * 1000 }; // Sender Info setting // STEP 1 >> In buddy group. let senderInfo: any = buddyList[noti.SENDER_SEQ]; // STEP 2 >> In Current Room Users. if (!senderInfo) { senderInfo = curRoomUserInfo[noti.SENDER_SEQ]; } // STEP 3 >> In All Room Users. if (!senderInfo) { for (const key in roomUserShorts) { if (key === undefined) { continue; } if (roomUserShorts.hasOwnProperty(key)) { const element = roomUserShorts[key]; const filteredUserInfos = element.userInfos.filter( info => info.seq === noti.SENDER_SEQ ); if ( !!filteredUserInfos && filteredUserInfos.length > 0 ) { senderInfo = filteredUserInfos[0]; break; } } } } // Sender Info setting. if (!!senderInfo) { // name set let name = senderInfo.name; let grade = senderInfo.grade; switch ( this.ucapTranslateService.currentLang.toUpperCase() ) { case LocaleCode.English: name = senderInfo.nameEn; grade = senderInfo.gradeEn; break; case LocaleCode.Chinese: name = senderInfo.nameCn; grade = senderInfo.gradeCn; break; } notiReq.title = this.translateService.instant( 'notification.titleChatEventArrivedByUser', { userInfo: !!grade ? `${name} ${grade}` : name } ); // Image set. if (!!senderInfo.profileImageFile) { const sessionVerinfo = this.sessionStorageService.get< VersionInfo2Response >(KEY_VER_INFO); notiReq.image = `${sessionVerinfo.profileRoot}${senderInfo.profileImageFile}`; } } // express noti popup this.nativeService.notify(notiReq); } } } } } } break; case SSVC_TYPE_EVENT_READ_RES: case SSVC_TYPE_EVENT_READ_NOTI: { // 대화방 unread count 처리. const noti = notiOrRes as ReadNotification; this.logger.debug( 'Notification::eventProtocolService::ReadNotification', noti ); this.store.dispatch(EventStore.readNotification(noti)); } break; case SSVC_TYPE_EVENT_CANCEL_NOTI: { const noti = notiOrRes as CancelNotification; this.logger.debug( 'Notification::eventProtocolService::CancelNotification', noti ); this.store.dispatch( EventStore.cancelNotification({ noti }) ); } break; case SSVC_TYPE_EVENT_DEL_RES: { const noti = notiOrRes as DelNotification; this.logger.debug( 'Notification::eventProtocolService::DelNotification', noti ); this.store.dispatch( EventStore.delNotification({ noti }) ); } break; default: break; } } ) ) .subscribe(); this.infoProtocolService.notification$ .pipe( tap(notiOrRes => { switch (notiOrRes.SSVC_TYPE) { case SSVC_TYPE_INFO_USER_NOTI: { const noti = notiOrRes as UserNotification; this.logger.debug( 'Notification::infoProtocolService::UserNotification', noti ); this.store.dispatch( InfoStore.userNotification({ noti }) ); } break; default: break; } }) ) .subscribe(); this.groupProtocolService.notification$ .pipe( withLatestFrom( this.store.pipe( select( (state: any) => state.messenger.sync.group2.syncDate as string ) ) ), tap(([notiOrRes, syncDate]) => { switch (notiOrRes.SSVC_TYPE) { case SSVC_TYPE_GROUP_UPD_RES2: { const noti = notiOrRes as GroupUpdateNotification; this.logger.debug( 'Notification::groupProtocolService::GroupUpdateNotification', noti ); this.store.dispatch( SyncStore.group2({ syncDate }) ); } break; case SSVC_TYPE_GROUP_ADD_RES: { const noti = notiOrRes as GroupAddNotification; this.logger.debug( 'Notification::groupProtocolService::GroupAddNotification', noti ); this.store.dispatch(SyncStore.createGroupSuccess(noti)); } break; case SSVC_TYPE_GROUP_DEL_RES: { const noti = notiOrRes as GroupDelNotification; this.logger.debug( 'Notification::groupProtocolService::GroupDelNotification', noti ); this.store.dispatch(SyncStore.delGroupSuccess(noti)); } break; default: break; } }) ) .subscribe(); this.buddyProtocolService.notification$ .pipe( withLatestFrom( this.store.pipe( select( (state: any) => state.messenger.sync.buddy2.syncDate as string ) ) ), tap(([notiOrRes, syncDate]) => { switch (notiOrRes.SSVC_TYPE) { case SSVC_TYPE_BUDDY_UPD_RES: { const noti = notiOrRes as BuddyUpdateNotification; this.logger.debug( 'Notification::groupProtocolService::BuddyUpdateNotification', noti ); this.store.dispatch(SyncStore.updateBuddySuccess(noti)); } break; case SSVC_TYPE_BUDDY_ADD_RES: { const noti = notiOrRes as BuddyAddNotification; this.logger.debug( 'Notification::groupProtocolService::BuddyAddNotification', noti ); this.store.dispatch(SyncStore.buddy2({ syncDate })); } break; case SSVC_TYPE_BUDDY_DEL_RES: { const noti = notiOrRes as BuddyDelNotification; this.logger.debug( 'Notification::groupProtocolService::BuddyDelNotification', noti ); this.store.dispatch(SyncStore.delBuddySuccess(noti)); } break; default: break; } }) ) .subscribe(); this.roomProtocolService.notification$ .pipe( withLatestFrom( this.store.pipe( select( (state: any) => state.account.authentication.loginRes as LoginResponse ) ) ), tap(([notiOrRes, loginResInfo]) => { switch (notiOrRes.SSVC_TYPE) { case SSVC_TYPE_ROOM_INVITE_RES: case SSVC_TYPE_ROOM_INVITE_NOTI: { const noti = notiOrRes as InviteNotification; this.logger.debug( 'Notification::roomProtocolService::InviteNotification', noti ); this.store.dispatch( RoomStore.inviteNotification({ noti }) ); } break; case SSVC_TYPE_ROOM_UPD_RES: { const noti = notiOrRes as RoomUpdateNotification; this.logger.debug( 'Notification::roomProtocolService::RoomUpdateNotification', noti ); this.store.dispatch( RoomStore.updateSuccess({ res: { roomSeq: noti.roomSeq, roomName: noti.roomName.trim().length === 0 ? ' ' : noti.roomName.trim(), receiveAlarm: noti.receiveAlarm, syncAll: false } }) ); } break; case SSVC_TYPE_ROOM_EXIT_RES: case SSVC_TYPE_ROOM_EXIT_NOTI: { const noti = notiOrRes as ExitNotification; this.logger.debug( 'Notification::roomProtocolService::ExitNotification', noti ); if (noti.SENDER_SEQ === loginResInfo.userSeq) { this.store.dispatch( RoomStore.exitNotification({ noti }) ); } else { this.store.dispatch( RoomStore.exitNotificationOthers({ roomSeq: noti.roomSeq, trgtUser: [noti.SENDER_SEQ] }) ); if (!!noti && !!noti.SENDER_SEQ) { this.store.dispatch( SyncStore.clearRoomUsers({ roomSeq: noti.roomSeq, userSeqs: [noti.SENDER_SEQ] }) ); } } } break; case SSVC_TYPE_ROOM_EXIT_FORCING_RES: { // 내가 강퇴 진행. const res = notiOrRes as ExitForcingResponse; this.logger.debug( 'Notification::roomProtocolService::ExitForcingNotification RES', res ); this.store.dispatch( RoomStore.exitForcingSuccess({ res }) ); } break; case SSVC_TYPE_ROOM_EXIT_FORCING_NOTI: { const noti = notiOrRes as ExitForcingNotification; this.logger.debug( 'Notification::roomProtocolService::ExitForcingNotification NOTI', noti ); if (noti.userSeqs.indexOf(loginResInfo.userSeq) >= 0) { // 강퇴 대상이 본인이면 열려 있는 대화방을 닫고, 대화리스트에서 삭제. this.store.dispatch( RoomStore.exitForcingNotification({ noti }) ); } else { // ROOM::열려 있는 대화방에서 강퇴 인원의 isJoinRoom = false 로 변경. this.store.dispatch( RoomStore.exitNotificationOthers({ roomSeq: noti.roomSeq, trgtUser: noti.userSeqs }) ); // SYNC::대화리스트의 대화자정보에서 강퇴 인원의 isJoinRoom = false 로 변경. this.store.dispatch( SyncStore.clearRoomUsers({ roomSeq: noti.roomSeq, userSeqs: noti.userSeqs }) ); } } break; case SSVC_TYPE_ROOM_FONT_UPD_NOTI: { const noti = notiOrRes as UpdateFontNotification; this.logger.debug( 'Notification::roomProtocolService::UpdateFontNotification', noti ); this.store.dispatch( RoomStore.updateFontNotification({ noti }) ); } break; default: break; } }) ) .subscribe(); this.statusProtocolService.notification$ .pipe( tap(notiOrRes => { switch (notiOrRes.SSVC_TYPE) { case SSVC_TYPE_STATUS_NOTI: { const noti = notiOrRes as StatusNotification; this.logger.debug( 'Notification::statusProtocolService::StatusNotification', noti ); this.store.dispatch( StatusStore.statusNotification({ noti }) ); } break; default: break; } }) ) .subscribe(); this.optionProtocolService.notification$ .pipe( tap(notiOrRes => { switch (notiOrRes.SSVC_TYPE) { case SSVC_TYPE_OPTION_REG_UPD_RES: { const noti = notiOrRes as RegUpdateNotification; this.logger.debug( 'Notification::optionProtocolService::RegUpdateNotification', noti ); const appUserInfo: AppUserInfo = this.localStorageService.encGet< AppUserInfo >(KEY_APP_USER_INFO, environment.customConfig.appKey); const modifiedSettings: Settings = clone(appUserInfo.settings); // 모바일에서 해당 값만 수정함. modifiedSettings.notification.receiveForMobile = noti.mobileNotification; appUserInfo.settings = modifiedSettings; this.localStorageService.encSet( KEY_APP_USER_INFO, appUserInfo, environment.customConfig.appKey ); } break; default: break; } }) ) .subscribe(); this.umgProtocolService.notification$ .pipe( tap(notiOrRes => { switch (notiOrRes.SSVC_TYPE) { case SSVC_TYPE_UMG_NOTI: { const noti = notiOrRes as UmgNotiNotification; this.logger.debug( 'Notification::umgProtocolService::UmgNotiNotification', noti ); // unreadCount refresh.. this.store.dispatch(MessageStore.retrieveUnreadCount({})); // Receive Message List refresh.. this.store.dispatch( MessageStore.retrieveMessage({ messageType: MessageType.Receive }) ); // notification.. const appUserInfo = this.localStorageService.encGet< AppUserInfo >(KEY_APP_USER_INFO, environment.customConfig.appKey); if (appUserInfo.settings.notification.use) { if ( appUserInfo.settings.notification.method === NotificationMethod.Sound ) { const audio = new Audio('assets/sounds/messageAlarm.mp3'); audio.play(); } else { const notiReq: NotificationRequest = { type: NotificationType.Message, seq: noti.keyId, title: this.translateService.instant( 'notification.titleMessageArrived' ), contents: noti.text, image: noti.senderInfo.profileImageFile, useSound: [ NotificationMethod.Sound, NotificationMethod.SoundAndAlert ].some( n => n === appUserInfo.settings.notification.method ) ? true : false, displayTime: appUserInfo.settings.notification.alertExposureTime * 1000 }; this.nativeService.notify(notiReq); } } // direct open detail if (appUserInfo.settings.notification.receiveForMessage) { this.store.dispatch( MessageStore.detailMessage({ messageType: MessageType.Receive, msgId: Number(noti.keyId) }) ); } } break; case SSVC_TYPE_UMG_DELETE_NOTI: { const noti = notiOrRes as UmgDeleteNotiNotification; this.logger.debug( 'Notification::umgProtocolService::UmgDeleteNotiNotification', noti ); // Remove one Receive Message if (!!noti && !!noti.keyId) { // clear badge in left navi this.store.dispatch(MessageStore.retrieveUnreadCount({})); // delete message in receive message list this.store.dispatch( deleteMessageSuccess({ messageType: MessageType.Receive, msgList: [ { msgId: Number(noti.keyId) } ] }) ); } } break; default: break; } }) ) .subscribe(); } }