대화방 > 이전 대화 보기 기능 구현. infinity scroll

This commit is contained in:
leejh 2019-10-31 18:12:38 +09:00
parent 4b4f8a0067
commit 749237b1bc
13 changed files with 131 additions and 27 deletions

View File

@ -19,7 +19,8 @@
{{ getRoomName() }}
</div>
<div *ngIf="!!roomInfo && roomInfo.isTimeRoom">
<mat-icon>timer</mat-icon> {{ getConvertTimer(roomInfo.timeRoomInterval) }}
<mat-icon>timer</mat-icon>
{{ getConvertTimer(roomInfo.timeRoomInterval) }}
</div>
<div class="room-option">
<button
@ -73,13 +74,19 @@
<!-- Timer Room Info -->
<!-- CHAT MESSAGES -->
<perfect-scrollbar fxFlex="1 1 auto" #psChatContent>
<perfect-scrollbar
fxFlex="1 1 auto"
#psChatContent
(psYReachStart)="onScrollup($event)"
>
<ucap-chat-messages
[messages]="eventList$ | async"
[eventInfoStatus]="eventInfoStatus$ | async"
[eventRemain]="eventRemain$ | async"
[userInfos]="userInfoList"
[loginRes]="loginRes"
[sessionVerInfo]="sessionVerInfo"
(moreEvent)="onMoreEvent($event)"
(massDetail)="onMassDetail($event)"
(save)="onSave($event)"
(imageViewer)="onImageViewer($event)"

View File

@ -58,7 +58,7 @@ import {
ImageViewerDialogData,
ImageViewerDialogResult
} from '@app/layouts/common/dialogs/image-viewer.dialog.component';
import { Maximum_Range } from '@ucap-webmessenger/core';
import { CONST } from '@ucap-webmessenger/core';
import { PerfectScrollbarComponent } from 'ngx-perfect-scrollbar';
@Component({
@ -83,12 +83,15 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewChecked {
loginRes: LoginResponse;
loginResSubscription: Subscription;
eventList$: Observable<Info[]>;
baseEventSeq = 0;
roomInfo: RoomInfo;
roomInfoSubscription: Subscription;
userInfoList: UserInfo[];
userInfoListSubscription: Subscription;
eventListProcessing$: Observable<boolean>;
eventInfoStatus$: Observable<InfoResponse>;
eventRemain$: Observable<boolean>;
eventRemain = false;
sessionVerInfo: VersionInfo2Response;
isRecalledMessage = isRecalled;
@ -148,8 +151,20 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewChecked {
select(AppStore.MessengerSelector.EventSelector.infoListProcessing)
);
this.eventRemain$ = this.store.pipe(
select(AppStore.MessengerSelector.EventSelector.remainInfo),
tap(remainInfo => {
this.eventRemain = remainInfo;
})
);
this.eventList$ = this.store.pipe(
select(AppStore.MessengerSelector.EventSelector.selectAllInfoList)
select(AppStore.MessengerSelector.EventSelector.selectAllInfoList),
tap(infoList => {
if (!!infoList && infoList.length > 0) {
this.baseEventSeq = infoList[0].seq;
}
})
);
this.eventInfoStatus$ = this.store.pipe(
@ -229,7 +244,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewChecked {
return;
}
if (message.trim().length > Maximum_Range.MassText) {
if (message.trim().length > CONST.MASSTEXT_LEN) {
// MASS TEXT
this.store.dispatch(
EventStore.sendMass({
@ -260,6 +275,22 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewChecked {
this.store.dispatch(RoomStore.updateOnlyAlarm({ roomInfo: this.roomInfo }));
}
onScrollup(event: any) {
this.onMoreEvent(this.baseEventSeq);
}
/** More Event */
onMoreEvent(seq: number) {
if (this.eventRemain) {
this.store.dispatch(
EventStore.info({
roomSeq: this.roomInfo.roomSeq,
baseSeq: seq,
requestCount: CONST.EVENT_INFO_READ_COUNT
})
);
}
}
/** MassText Detail View */
onMassDetail(value: number) {
this.store.dispatch(

View File

@ -29,6 +29,14 @@ export const infoSuccess = createAction(
}>()
);
export const infoMoreSuccess = createAction(
'[Messenger::Event] Info More Success',
props<{
infoList: Info[];
res: InfoResponse;
}>()
);
export const infoFailure = createAction(
'[Messenger::Event] Info Failure',
props<{ error: any }>()

View File

@ -67,7 +67,8 @@ import {
forwardFailure,
forwardAfterRoomOpen,
sendMass,
sendMassFailure
sendMassFailure,
infoMoreSuccess
} from './actions';
import { SessionStorageService } from '@ucap-webmessenger/web-storage';
import {
@ -81,6 +82,7 @@ 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 { CONST } from '@ucap-webmessenger/core';
@Injectable()
export class Effects {
@ -91,7 +93,7 @@ export class Effects {
return info({
roomSeq: action.roomSeq,
baseSeq: 0,
requestCount: 50
requestCount: CONST.EVENT_INFO_READ_COUNT
});
})
)
@ -115,12 +117,21 @@ export class Effects {
break;
case SSVC_TYPE_EVENT_INFO_RES:
{
this.store.dispatch(
infoSuccess({
infoList,
res: res as InfoResponse
})
);
if (req.baseSeq === 0) {
this.store.dispatch(
infoSuccess({
infoList,
res: res as InfoResponse
})
);
} else {
this.store.dispatch(
infoMoreSuccess({
infoList,
res: res as InfoResponse
})
);
}
if (req.baseSeq === 0) {
// 최초 이벤트 목록 조회시 SSVC_TYPE_EVENT_READ_REQ 수행.

View File

@ -6,10 +6,12 @@ import {
info,
infoFailure,
recallInfoList,
delInfoList
delInfoList,
infoMoreSuccess
} from './actions';
import * as AuthenticationStore from '@app/store/account/authentication';
import { Info, EventType } from '@ucap-webmessenger/protocol-event';
import { CONST } from '@ucap-webmessenger/core';
export const reducer = createReducer(
initialState,
@ -27,7 +29,27 @@ export const reducer = createReducer(
...state.infoList
}),
infoStatus: action.res,
infoListProcessing: false
infoListProcessing: false,
remainInfo:
!!action.infoList &&
action.infoList.length === CONST.EVENT_INFO_READ_COUNT
? true
: false
};
}),
on(infoMoreSuccess, (state, action) => {
return {
...state,
infoList: adapterInfoList.upsertMany(action.infoList, {
...state.infoList
}),
infoStatus: action.res,
infoListProcessing: false,
remainInfo:
!!action.infoList &&
action.infoList.length === CONST.EVENT_INFO_READ_COUNT
? true
: false
};
}),

View File

@ -8,6 +8,7 @@ export interface State {
infoListProcessing: boolean;
infoList: InfoListState;
infoStatus: InfoResponse | null;
remainInfo: boolean;
}
export const adapterInfoList = createEntityAdapter<Info>({
@ -22,7 +23,8 @@ const infoListInitialState: InfoListState = adapterInfoList.getInitialState({});
export const initialState: State = {
infoListProcessing: false,
infoList: infoListInitialState,
infoStatus: null
infoStatus: null,
remainInfo: false
};
const {
@ -43,6 +45,10 @@ export function selectors<S>(selector: Selector<any, State>) {
selector,
(state: State) => state.infoListProcessing
),
remainInfo: createSelector(
selector,
(state: State) => state.remainInfo
),
infoList: createSelector(
selector,
(state: State) => state.infoList

View File

@ -101,6 +101,7 @@ import {
import * as ChatStore from '@app/store/messenger/chat';
import * as RoomStore from '@app/store/messenger/room';
import { CONST } from '@ucap-webmessenger/core';
@Injectable()
export class Effects {
@ -405,7 +406,7 @@ export class Effects {
divCd: 'DivCodeT',
roomName: '',
isTimerRoom: true,
timerRoomInterval: 24 * 60 * 60, // 24h default
timerRoomInterval: CONST.DEFAULT_TIMER_ROOM_INTERVAL, // 24h default
userSeqs: userSeqList
}
})

View File

@ -0,0 +1,10 @@
export enum CONST {
/** 대용량 텍스트로 보내는 문자열의 길이 기준 */
MASSTEXT_LEN = 800,
/** 대화방의 이벤트를 조회하는 개수 */
EVENT_INFO_READ_COUNT = 50,
/** Timer Room 최초 오픈시 timer interval */
DEFAULT_TIMER_ROOM_INTERVAL = 24 * 60 * 60,
/** 한번에 채팅을 할 수 있는 인원수 제한 */
CHATROOM_USER = 300
}

View File

@ -1,4 +0,0 @@
export enum Maximum_Range {
MassText = 800,
ChatRoom = 300
}

View File

@ -7,12 +7,12 @@ export * from './lib/type/call-alarm.type';
export * from './lib/type/call-forward.type';
export * from './lib/type/call-mode.type';
export * from './lib/type/caller-type.type';
export * from './lib/type/const.type';
export * from './lib/type/default-screen.type';
export * from './lib/type/device-devision.type';
export * from './lib/type/device-type.type';
export * from './lib/type/file-transfer-permissions.type';
export * from './lib/type/locale-code.type';
export * from './lib/type/maximum-range.type';
export * from './lib/type/notification-method.type';
export * from './lib/type/organization-chart-permissions.type';
export * from './lib/type/push-type.type';

View File

@ -35,8 +35,6 @@ export interface InfoResponse extends ProtocolResponse {
baseSeq: number;
// 유효한파일기준이벤트SEQ(n)
validFileBaseSeq: number;
// 이벤트정보 개수(n)
count: number;
}
export const encodeInfo: ProtocolEncoder<InfoRequest> = (req: InfoRequest) => {
@ -82,7 +80,6 @@ export const decodeInfo: ProtocolDecoder<InfoResponse> = (
return decodeProtocolMessage(message, {
roomSeq: message.bodyList[0],
baseSeq: message.bodyList[1],
validFileBaseSeq: message.bodyList[2],
count: message.bodyList[3]
validFileBaseSeq: message.bodyList[2]
} as InfoResponse);
};

View File

@ -1,4 +1,7 @@
<div class="chat-messages">
<!-- <div class="message-row" *ngIf="eventRemain">
<button mat-button (click)="onClickMore($event)">이전 대화 보기</button>
</div> -->
<!-- MESSAGE -->
<div
*ngFor="let message of messages; let i = index"

View File

@ -1,3 +1,4 @@
import { CONST } from '@ucap-webmessenger/core';
import {
Component,
OnInit,
@ -32,17 +33,20 @@ export class MessagesComponent implements OnInit {
@Input()
eventInfoStatus?: InfoResponse;
@Input()
eventRemain: boolean;
@Input()
userInfos?: UserInfo[];
@Input()
sessionVerInfo: VersionInfo2Response;
@Output()
moreEvent = new EventEmitter<number>();
@Output()
massDetail = new EventEmitter<number>();
@Output()
imageViewer = new EventEmitter<FileInfo>();
@Output()
save = new EventEmitter<{ fileInfo: FileInfo; type: string }>();
@Output()
contextMenu = new EventEmitter<{
event: MouseEvent;
@ -50,6 +54,7 @@ export class MessagesComponent implements OnInit {
}>();
EventType = EventType;
CONST = CONST;
profileImageRoot: string;
constructor(private logger: NGXLogger, private datePipe: DatePipe) {}
@ -138,6 +143,13 @@ export class MessagesComponent implements OnInit {
return false;
}
onClickMore(event: any) {
event.preventDefault();
event.stopPropagation();
this.moreEvent.emit(this.messages[0].seq);
}
/** [Event] MassTalk Detail View */
onMassDetail(value: number) {
this.massDetail.emit(value);