unread count 관련 처리.

This commit is contained in:
leejh 2019-10-30 16:22:49 +09:00
parent d099bcb035
commit 33e6566eff
11 changed files with 181 additions and 55 deletions

View File

@ -137,7 +137,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewChecked {
this.userInfoListSubscription = this.store
.pipe(
select(AppStore.MessengerSelector.RoomSelector.userInfoList),
select(AppStore.MessengerSelector.RoomSelector.selectUserinfolist),
tap(userInfoList => {
this.userInfoList = userInfoList;
})

View File

@ -154,11 +154,7 @@ export class AppNotificationService {
'Notification::eventProtocolService::ReadNotification',
noti
);
this.store.dispatch(
EventStore.readNotification({
noti
})
);
this.store.dispatch(EventStore.readNotification(noti));
}
break;
case SSVC_TYPE_EVENT_CANCEL_NOTI:

View File

@ -133,7 +133,7 @@ export const readFailure = createAction(
export const readNotification = createAction(
'[Messenger::Event] Read Notification',
props<{ noti: ReadNotification }>()
props<ReadNotification>()
);
/** 대화 회수 */

View File

@ -32,10 +32,14 @@ import {
ReadResponse,
DelResponse,
CancelResponse,
EventType
EventType,
ReadNotification,
SSVC_TYPE_EVENT_SEND_RES,
SSVC_TYPE_EVENT_SEND_NOTI
} 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 {
@ -149,7 +153,7 @@ export class Effects {
exhaustMap(req =>
this.eventProtocolService.read(req).pipe(
map((res: ReadResponse) => {
return readNotification({ noti: res });
return readNotification(res as ReadNotification);
}),
catchError(error => of(readFailure({ error })))
)
@ -161,14 +165,33 @@ export class Effects {
() => {
return this.actions$.pipe(
ofType(readNotification),
map(action => action.noti),
tap(noti => {
this.store.dispatch(
SyncStore.updateUnreadCount({
roomSeq: noti.roomSeq,
noReadCnt: 0
})
);
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.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
})
);
}
})
);
},
@ -376,12 +399,24 @@ export class Effects {
// opened room :: event add
if (!!roomInfo && roomInfo.roomSeq === action.roomSeq) {
this.store.dispatch(appendInfoList({ info: action.info }));
if (
action.SVC_TYPE === SVC_TYPE_EVENT &&
action.SSVC_TYPE === SSVC_TYPE_EVENT_SEND_NOTI
) {
this.store.dispatch(
read({
roomSeq: action.roomSeq,
lastReadSeq: action.info.seq
})
);
}
}
// not opened room :: unread count increased
if (
action.SVC_TYPE === SVC_TYPE_EVENT &&
action.SSVC_TYPE === SSVC_TYPE_EVENT_INFO_RES
action.SSVC_TYPE === SSVC_TYPE_EVENT_SEND_RES
) {
/**
* RES noti .

View File

@ -15,6 +15,7 @@ import {
ExitRequest,
ExitResponse
} from '@ucap-webmessenger/protocol-room';
import { ReadNotification } from '@ucap-webmessenger/protocol-event';
export const info = createAction(
'[Messenger::Room] Info',
@ -60,6 +61,11 @@ export const updateOnlyAlarm = createAction(
props<{ roomInfo: RoomInfo }>()
);
export const updateRoomUserLastReadSeq = createAction(
'[Messenger::Room] Update Room User Only LastReadSeq',
props<ReadNotification>()
);
export const update = createAction(
'[Messenger::Room] Update',
props<{ req: UpdateRequest }>()

View File

@ -1,8 +1,13 @@
import { createReducer, on } from '@ngrx/store';
import { initialState } from './state';
import { infoSuccess, updateSuccess } from './actions';
import { initialState, adapterUserInfo, adapterUserInfoShort } from './state';
import {
infoSuccess,
updateSuccess,
updateRoomUserLastReadSeq
} from './actions';
import * as AuthenticationStore from '@app/store/account/authentication';
import { UserInfo } from '@ucap-webmessenger/protocol-room';
export const reducer = createReducer(
initialState,
@ -10,8 +15,12 @@ export const reducer = createReducer(
return {
...state,
roomInfo: action.roomInfo,
userInfoList: action.userInfoList,
userInfoShortList: action.userInfoShortList
userInfoList: adapterUserInfo.addAll(action.userInfoList, {
...state.userInfoList
}),
userInfoShortList: adapterUserInfoShort.addAll(action.userInfoShortList, {
...state.userInfoShortList
})
};
}),
@ -26,6 +35,23 @@ export const reducer = createReducer(
};
}),
on(updateRoomUserLastReadSeq, (state, action) => {
const userSeq = action.SENDER_SEQ;
const userInfo: UserInfo = {
...state.userInfoList.entities[userSeq],
lastReadEventSeq: action.lastReadSeq
};
return {
...state,
userInfoList: adapterUserInfo.updateOne(
{ id: userSeq, changes: userInfo },
{ ...state.userInfoList }
)
};
}),
on(AuthenticationStore.logout, (state, action) => {
return {
...initialState

View File

@ -1,3 +1,4 @@
import { EntityState, createEntityAdapter } from '@ngrx/entity';
import { Selector, createSelector } from '@ngrx/store';
import {
InfoRequest,
@ -6,31 +7,71 @@ import {
UserInfo
} from '@ucap-webmessenger/protocol-room';
export interface UserInfoShortListState extends EntityState<UserInfoShort> {}
export interface UserInfoListState extends EntityState<UserInfo> {}
export interface State {
roomInfo: RoomInfo | null;
userInfoShortList: UserInfoShort[] | null;
userInfoList: UserInfo[] | null;
userInfoShortList: UserInfoShortListState;
userInfoList: UserInfoListState;
}
export const adapterUserInfoShort = createEntityAdapter<UserInfoShort>({
selectId: userInfo => userInfo.seq
});
export const adapterUserInfo = createEntityAdapter<UserInfo>({
selectId: userInfo => userInfo.seq
});
const userInfoShortListInitialState: UserInfoShortListState = adapterUserInfoShort.getInitialState(
{}
);
const userInfoListInitialState: UserInfoListState = adapterUserInfo.getInitialState(
{}
);
export const initialState: State = {
roomInfo: null,
userInfoShortList: null,
userInfoList: null
userInfoShortList: userInfoShortListInitialState,
userInfoList: userInfoListInitialState
};
const {
selectAll: ngeSelectAllUserInfoShort,
selectEntities: ngeSelectEntitiesUserInfoShort,
selectIds: ngeSelectIdsUserInfoShort,
selectTotal: ngeSelectTotalUserInfoShort
} = adapterUserInfoShort.getSelectors();
const {
selectAll: ngeSelectAllUserInfo,
selectEntities: ngeSelectEntitiesUserInfo,
selectIds: ngeSelectIdsUserInfo,
selectTotal: ngeSelectTotalUserInfo
} = adapterUserInfo.getSelectors();
export function selectors<S>(selector: Selector<any, State>) {
const selectUserInfoShortList = createSelector(
selector,
(state: State) => state.userInfoShortList
);
const selectUserInfoList = createSelector(
selector,
(state: State) => state.userInfoList
);
return {
roomInfo: createSelector(
selector,
(state: State) => state.roomInfo
),
userInfoShortList: createSelector(
selector,
(state: State) => state.userInfoShortList
selectUserInfoShortList: createSelector(
selectUserInfoShortList,
ngeSelectAllUserInfoShort
),
userInfoList: createSelector(
selector,
(state: State) => state.userInfoList
selectUserinfolist: createSelector(
selectUserInfoList,
ngeSelectAllUserInfo
)
};
}

View File

@ -8,7 +8,6 @@ import {
} from '@ucap-webmessenger/protocol-sync';
import {
RoomInfo,
UserInfoShort,
UserInfo as RoomUserInfo
} from '@ucap-webmessenger/protocol-room';

View File

@ -146,7 +146,12 @@
</div>
<div class="time secondary-text">
{{ message.sendDate | date: 'a hh:mm' }}
<ul>
<li>{{ getUnreadCount(message) }}</li>
<li>
{{ message.sendDate | date: 'a hh:mm' }}
</li>
</ul>
</div>
</ng-template>
</div>

View File

@ -140,23 +140,23 @@ $meBox-bg: #ffffff;
display: flex;
flex-direction: column;
}
.information-msg{
width:100%;
height:100%;
text-align:center;
background-color:#cccccc;
padding:10px;
margin:10px 0;
.information-msg {
width: 100%;
height: 100%;
text-align: center;
background-color: #cccccc;
padding: 10px;
margin: 10px 0;
}
.message-row {
margin-bottom: 30px;
.date-splitter{
display:block;
width:100%;
margin-bottom:10px;
.date-splitter {
display: block;
width: 100%;
margin-bottom: 10px;
}
.chat-row{
.chat-row {
position: relative;
display: flex;
flex-direction: row;
@ -165,14 +165,14 @@ $meBox-bg: #ffffff;
}
}
&.me {
.chat-row{
.chat-row {
flex-direction: row-reverse;
.profile-info {
flex-direction: row-reverse;
display: flex;
justify-content: flex-end;
}
}
}
}
.message-main {
@ -219,10 +219,15 @@ $meBox-bg: #ffffff;
.secondary-text {
align-self: flex-end;
font-size: 11px;
color:#666666;
color: #666666;
word-wrap: break-word;
white-space: nowrap;
}
&.me {
.secondary-text {
text-align: end;
}
}
}
.message-row.me > .bubble {
@ -267,4 +272,4 @@ $meBox-bg: #ffffff;
top: 5px;
}
}
}
}

View File

@ -12,10 +12,8 @@ import {
EventType,
InfoResponse
} from '@ucap-webmessenger/protocol-event';
import {
LoginResponse,
UserInfo
} from '@ucap-webmessenger/protocol-authentication';
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
import { UserInfo } from '@ucap-webmessenger/protocol-room';
import { NGXLogger } from 'ngx-logger';
import { VersionInfo2Response } from '@ucap-webmessenger/api-public';
import { FileInfo } from '../models/file-info.json';
@ -61,6 +59,9 @@ export class MessagesComponent implements OnInit {
this.profileImageRoot || this.sessionVerInfo.profileRoot;
}
/**
* UserInfo getter
*/
getUserName(seq: number): string {
if (!this.userInfos) {
return '';
@ -88,6 +89,17 @@ export class MessagesComponent implements OnInit {
return '';
}
getUnreadCount(message: Info): string | number {
const unreadCnt = this.userInfos.filter(user => {
if (message.senderSeq === user.seq) {
// 본인 글은 unreadCount 에 포함하지 않는다.
return false;
}
return user.lastReadEventSeq < message.seq;
}).length;
return unreadCnt === 0 ? '' : unreadCnt;
}
/**
* Event .
* @description event , .
@ -106,7 +118,7 @@ export class MessagesComponent implements OnInit {
return false;
}
/** Date Splitter check */
/** Date Splitter show check */
getDateSplitter(curIndex: number): boolean {
if (curIndex === 0) {
return true;
@ -141,6 +153,7 @@ export class MessagesComponent implements OnInit {
this.save.emit(value);
}
/** [Event] Context Menu */
onContextMenuMessage(event: MouseEvent, message: Info) {
this.contextMenu.emit({ event, message });
}