617 lines
16 KiB
TypeScript
Raw Normal View History

2020-01-17 10:41:22 +09:00
import {
Component,
OnInit,
Input,
EventEmitter,
Output,
ViewChild,
2020-01-17 12:29:04 +09:00
OnDestroy,
ChangeDetectionStrategy,
2020-01-20 17:26:38 +09:00
ElementRef,
ChangeDetectorRef
2020-01-17 10:41:22 +09:00
} from '@angular/core';
import {
Info,
EventType,
2019-11-06 13:48:06 +09:00
InfoResponse,
EventJson,
FileEventJson,
MassTranslationEventJson
} from '@ucap-webmessenger/protocol-event';
2019-10-30 16:22:49 +09:00
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
2019-12-19 14:23:11 +09:00
import { UserInfo, RoomInfo, RoomType } from '@ucap-webmessenger/protocol-room';
2019-10-08 15:13:01 +09:00
import { NGXLogger } from 'ngx-logger';
import { VersionInfo2Response } from '@ucap-webmessenger/api-public';
2019-10-14 17:19:13 +09:00
import { DatePipe } from '@angular/common';
2019-11-13 16:30:15 +09:00
import moment from 'moment';
import { FileDownloadItem } from '@ucap-webmessenger/api';
import { TranslateService } from '@ngx-translate/core';
2020-01-17 10:41:22 +09:00
import { Observable, Subscription } from 'rxjs';
import { VirtualScrollerComponent, IPageInfo } from 'ngx-virtual-scroller';
2019-10-08 13:31:33 +09:00
@Component({
selector: 'ucap-chat-messages',
templateUrl: './messages.component.html',
styleUrls: ['./messages.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
2020-01-17 10:41:22 +09:00
export class MessagesComponent implements OnInit, OnDestroy {
@Input()
2020-01-17 10:41:22 +09:00
loginRes$: Observable<LoginResponse>;
2019-10-08 13:31:33 +09:00
@Input()
2020-01-17 10:41:22 +09:00
roomInfo$: Observable<RoomInfo>;
2019-12-19 14:23:11 +09:00
@Input()
2020-01-17 10:41:22 +09:00
eventList$: Observable<Info<EventJson>[]>;
@Input()
2020-01-17 10:41:22 +09:00
newEventList$: Observable<Info<EventJson>[]>;
2019-12-23 15:23:27 +09:00
@Input()
2020-01-17 10:41:22 +09:00
searchedList$: Observable<Info<EventJson>[]>;
@Input()
2020-01-17 10:41:22 +09:00
eventInfoStatus$: Observable<InfoResponse>;
@Input()
2020-01-17 10:41:22 +09:00
eventRemained$: Observable<boolean>;
@Input()
sessionVerInfo: VersionInfo2Response;
2020-01-17 10:41:22 +09:00
@Input()
userInfos$: Observable<UserInfo[]>;
@Input()
isShowUnreadCount = true;
2019-12-19 14:23:11 +09:00
@Input()
clearReadHere: boolean;
@Input()
minShowReadHere = 10;
2019-12-31 14:36:32 +09:00
@Input()
translationSimpleview = false;
2020-01-17 10:41:22 +09:00
@Input()
searchingMode = false;
2019-11-08 13:35:39 +09:00
@Output()
openProfile = new EventEmitter<number>();
@Output()
moreEvent = new EventEmitter<number>();
2019-10-11 18:03:01 +09:00
@Output()
massDetail = new EventEmitter<number>();
@Output()
2020-01-02 08:44:16 +09:00
massTranslationDetail = new EventEmitter<{
message: Info<MassTranslationEventJson>;
2020-01-02 08:44:16 +09:00
contentsType: string;
}>();
@Output()
2019-11-07 15:46:02 +09:00
fileViewer = new EventEmitter<FileEventJson>();
@Output()
save = new EventEmitter<{
fileInfo: FileEventJson;
fileDownloadItem: FileDownloadItem;
type: string;
}>();
2019-10-16 16:33:19 +09:00
@Output()
contextMenu = new EventEmitter<{
event: MouseEvent;
2019-11-06 13:48:06 +09:00
message: Info<EventJson>;
type?: string;
2019-10-16 16:33:19 +09:00
}>();
2020-01-17 10:41:22 +09:00
@Output()
scrollUp = new EventEmitter<any>();
@Output()
yReachEnd = new EventEmitter<any>();
@Output()
yReachStart = new EventEmitter<any>();
@Output()
existNewMessage = new EventEmitter<Info<EventJson>>();
@ViewChild('chatMessagesContainer', { static: false })
chatMessagesContainer: ElementRef<HTMLElement>;
2020-01-20 18:01:31 +09:00
@ViewChild('chatMessagesBuffer', { static: false })
chatMessagesBuffer: ElementRef<HTMLElement>;
2020-01-17 10:41:22 +09:00
@ViewChild(VirtualScrollerComponent, { static: false })
private virtualScroller: VirtualScrollerComponent;
storedScrollItem: Info<EventJson>; // 이전대화를 불러올 경우 현재 스크롤 포지션 유지를 위한 값. 0 이면 초기로딩.
2020-01-17 10:41:22 +09:00
scrollUpInitalized = false; // ps 에서 초기 로딩시 scroll reach start 이벤트 발생 버그를 우회하기 위한 init 값으로 scrollUp 에 의해 true 로 된다.
firstCheckReadHere = true;
initRoomLastEventSeq: number;
baseEventSeq = 0;
loginRes: LoginResponse;
loginResSubscription: Subscription;
roomInfo: RoomInfo;
roomInfoSubscription: Subscription;
eventList: Info<EventJson>[];
eventListSubscription: Subscription;
newEventList: Info<EventJson>[];
newEventListSubscription: Subscription;
searchedList: Info<EventJson>[];
searchedListSubscription: Subscription;
eventInfoStatus: InfoResponse;
eventInfoStatusSubscription: Subscription;
eventRemained: boolean;
eventRemainedSubscription: Subscription;
userInfos: UserInfo[];
userInfosSubscription: Subscription;
2019-12-19 14:23:11 +09:00
2019-10-08 16:41:23 +09:00
EventType = EventType;
profileImageRoot: string;
2019-11-13 16:30:15 +09:00
moment = moment;
2019-10-08 16:41:23 +09:00
readToHereEvent: Info<EventJson>;
existReadToHereEvent = true;
swapped = false;
2020-01-23 11:55:51 +09:00
hidden = false;
2019-12-19 14:23:11 +09:00
constructor(
private logger: NGXLogger,
private datePipe: DatePipe,
2020-01-20 17:26:38 +09:00
private changeDetectorRef: ChangeDetectorRef,
private translateService: TranslateService
) {}
2019-10-08 15:13:01 +09:00
ngOnInit() {
this.profileImageRoot =
this.profileImageRoot || this.sessionVerInfo.profileRoot;
2020-01-17 10:41:22 +09:00
this.loginResSubscription = this.loginRes$.subscribe(loginRes => {
this.loginRes = loginRes;
});
this.roomInfoSubscription = this.roomInfo$.subscribe(roomInfo => {
2020-01-20 17:26:38 +09:00
// this.eventList = undefined;
// this.newEventList = undefined;
// this.searchedList = undefined;
// this.eventInfoStatus = undefined;
// this.eventRemained = undefined;
// this.userInfos = undefined;
2020-01-17 12:29:04 +09:00
2020-01-17 10:41:22 +09:00
this.initEventMore();
this.roomInfo = roomInfo;
2020-01-21 16:36:48 +09:00
/** [S] initializing by changed room */
// reset :: roomLastEventSeq
if (!!roomInfo && !!roomInfo.finalEventSeq) {
this.initRoomLastEventSeq = roomInfo.finalEventSeq;
2020-01-17 10:41:22 +09:00
}
// clear :: readToHearEvent object
this.readToHereEvent = undefined;
this.existReadToHereEvent = true;
/** [E] initializing by changed room */
});
this.eventListSubscription = this.eventList$.subscribe(eventList => {
this.eventList = eventList;
2020-01-21 16:36:48 +09:00
if (!!eventList && eventList.length > 0) {
if (!this.readToHereEvent && this.existReadToHereEvent) {
this.readToHereEvent = this.getReadHere();
}
if (
this.baseEventSeq > 0 &&
!!this.roomInfo &&
!!this.roomInfo.lastReadEventSeq &&
this.baseEventSeq <= this.roomInfo.lastReadEventSeq
) {
// 기존 대화 내용이 있는 상태에서 추가로 조회된 내용중에 read here 가 있을 경우.
this.firstCheckReadHere = false;
}
} else {
this.readToHereEvent = undefined;
}
2020-01-21 16:36:48 +09:00
2020-01-20 17:26:38 +09:00
this.changeDetectorRef.detectChanges();
2020-01-17 10:41:22 +09:00
if (this.searchingMode) {
const baseseq = this.baseEventSeq;
// setTimeout(() => {
// this.doSearchTextInEvent(this.searchText, baseseq);
// }, 800);
this.baseEventSeq = eventList[0].seq;
} else {
if (!!eventList && eventList.length > 0) {
this.baseEventSeq = eventList[0].seq;
this.ready();
}
}
});
this.newEventListSubscription = this.newEventList$.subscribe(
newEventList => {
this.newEventList = newEventList;
}
);
this.searchedListSubscription = this.searchedList$.subscribe(
searchedList => {
this.searchedList = searchedList;
}
);
this.eventInfoStatusSubscription = this.eventInfoStatus$.subscribe(
eventInfoStatus => {
this.eventInfoStatus = eventInfoStatus;
}
);
this.eventRemainedSubscription = this.eventRemained$.subscribe(
eventRemained => {
this.eventRemained = eventRemained;
}
);
this.userInfosSubscription = this.userInfos$.subscribe(userInfos => {
this.userInfos = userInfos;
});
}
ngOnDestroy(): void {
if (!!this.loginResSubscription) {
this.loginResSubscription.unsubscribe();
}
if (!!this.roomInfoSubscription) {
this.roomInfoSubscription.unsubscribe();
}
if (!!this.eventListSubscription) {
this.eventListSubscription.unsubscribe();
}
if (!!this.newEventListSubscription) {
this.newEventListSubscription.unsubscribe();
}
if (!!this.searchedListSubscription) {
this.searchedListSubscription.unsubscribe();
}
if (!!this.eventInfoStatusSubscription) {
this.eventInfoStatusSubscription.unsubscribe();
}
if (!!this.eventRemainedSubscription) {
this.eventRemainedSubscription.unsubscribe();
}
if (!!this.userInfosSubscription) {
this.userInfosSubscription.unsubscribe();
}
}
2019-10-30 16:22:49 +09:00
/**
* UserInfo getter
*/
getUserName(seq: number): string {
2019-10-11 15:55:27 +09:00
if (!this.userInfos) {
return '';
}
const userInfo: UserInfo[] = this.userInfos.filter(
user => user.seq === seq
);
if (!!userInfo && userInfo.length > 0) {
return userInfo[0].name;
}
return '(알수없는 사용자)';
}
getUserProfile(seq: number): string {
2019-10-11 15:55:27 +09:00
if (!this.userInfos) {
return '';
}
const userInfo: UserInfo[] = this.userInfos.filter(
user => user.seq === seq
);
if (!!userInfo && userInfo.length > 0) {
2019-10-17 16:57:37 +09:00
return userInfo[0].profileImageFile;
}
return '';
2019-10-08 15:13:01 +09:00
}
2020-01-17 10:41:22 +09:00
isHighlightedEvent(seq: number): boolean {
2019-12-23 15:23:27 +09:00
return (
!!this.searchedList &&
this.searchedList.filter(event => event.seq === seq).length > 0
);
}
2019-10-11 18:03:01 +09:00
2019-11-06 13:48:06 +09:00
getUnreadCount(message: Info<EventJson>): string | number {
2020-01-20 17:26:38 +09:00
// if (!this.userInfos || 0 === this.userInfos.length) {
// return '';
// }
const unreadCnt = this.userInfos
.filter(user => user.isJoinRoom && user.seq !== message.senderSeq)
.filter(user => user.lastReadEventSeq < message.seq).length;
2019-10-30 16:22:49 +09:00
return unreadCnt === 0 ? '' : unreadCnt;
}
/**
* Event .
* @description event , .
* Edit with reducers.ts / sync / updateRoomForNewEventMessage
*/
2019-11-06 13:48:06 +09:00
getIsInformation(info: Info<EventJson>) {
if (
info.type === EventType.Join ||
info.type === EventType.Exit ||
info.type === EventType.ForcedExit ||
info.type === EventType.RenameRoom ||
info.type === EventType.NotificationForTimerRoom ||
info.type === EventType.GuideForRoomTimerChanged
) {
return true;
}
return false;
}
2019-10-30 16:22:49 +09:00
/** Date Splitter show check */
getDateSplitter(message: Info<EventJson>): boolean {
const curIndex = this.eventList.findIndex(v => v.seq === message.seq);
2019-10-14 17:19:13 +09:00
if (curIndex === 0) {
return true;
}
if (curIndex > 0) {
2020-01-17 12:29:04 +09:00
if (!this.eventList[curIndex]) {
return false;
}
return !moment(this.eventList[curIndex].sendDate).isSame(
moment(this.eventList[curIndex - 1].sendDate),
'day'
2019-10-14 17:19:13 +09:00
);
}
return false;
}
2020-01-21 16:36:48 +09:00
getReadHere(): Info<EventJson> | undefined {
2019-12-19 14:23:11 +09:00
if (
!!this.roomInfo &&
!!this.roomInfo.lastReadEventSeq &&
this.initRoomLastEventSeq - this.roomInfo.lastReadEventSeq >
this.minShowReadHere
2019-12-19 14:23:11 +09:00
) {
if (
this.roomInfo.roomType === RoomType.Single ||
this.roomInfo.roomType === RoomType.Multi
) {
if (!this.roomInfo.isTimeRoom) {
2020-01-21 16:36:48 +09:00
return this.eventList.find(
v => v.seq === this.roomInfo.lastReadEventSeq + 1
);
2019-12-19 14:23:11 +09:00
}
}
} else {
this.existReadToHereEvent = false;
2019-12-19 14:23:11 +09:00
}
2020-01-21 16:36:48 +09:00
return undefined;
2019-12-19 14:23:11 +09:00
}
getStringReadHereMore(): string {
let rtnStr = '';
rtnStr = this.translateService.instant('chat.event.moreUnreadEventsWith', {
2020-01-17 10:41:22 +09:00
countOfUnread: this.baseEventSeq - (this.roomInfo.lastReadEventSeq + 1)
});
return rtnStr;
}
storeScrollPosition() {
this.storedScrollItem = this.eventList[
this.virtualScroller.viewPortInfo.startIndex
];
}
swapScroll(
to: Info<EventJson>,
preCallback: () => void,
postCallback: () => void,
2020-01-23 11:55:51 +09:00
useHide: boolean,
useSwap: boolean
) {
2020-01-23 11:55:51 +09:00
this.preSwapScroll(useHide, useSwap);
if (!!preCallback) {
preCallback();
}
this.virtualScroller.scrollInto(to, true, 0, 0, () => {
setTimeout(() => {
if (!!postCallback) {
postCallback();
}
2020-01-23 11:55:51 +09:00
this.postSwapScroll(useHide, useSwap);
}, 100);
});
}
2020-01-23 11:55:51 +09:00
preSwapScroll(useHide: boolean, useSwap: boolean) {
2020-01-29 18:02:57 +09:00
// if (useSwap && !this.swapped) {
// this.chatMessagesBuffer.nativeElement.innerHTML = this.chatMessagesContainer.nativeElement.innerHTML;
// this.chatMessagesBuffer.nativeElement.scrollTop = this.chatMessagesContainer.nativeElement.scrollTop;
// this.chatMessagesBuffer.nativeElement.classList.remove('disappear');
// this.swapped = true;
// }
2020-01-23 11:55:51 +09:00
if (useHide && !this.hidden) {
this.chatMessagesContainer.nativeElement.classList.add('hide');
this.hidden = true;
}
}
2020-01-23 11:55:51 +09:00
postSwapScroll(useHide: boolean, useSwap: boolean) {
2020-01-29 18:02:57 +09:00
// if (useSwap && this.swapped) {
// this.chatMessagesBuffer.nativeElement.innerHTML = '';
// this.chatMessagesBuffer.nativeElement.scrollTop = 0;
// this.chatMessagesBuffer.nativeElement.classList.add('disappear');
// this.swapped = false;
// }
2020-01-23 11:55:51 +09:00
if (useHide && this.hidden) {
this.chatMessagesContainer.nativeElement.classList.remove('hide');
this.hidden = false;
}
2020-01-17 10:41:22 +09:00
}
ready(): void {
this.scrollToBottom();
2020-01-17 10:41:22 +09:00
}
scrollToBottom(speed?: number): void {
if (!!this.storedScrollItem) {
if (!!this.readToHereEvent && this.firstCheckReadHere) {
this.swapScroll(
this.readToHereEvent,
() => {},
() => {
this.firstCheckReadHere = false;
2020-01-23 11:55:51 +09:00
},
true,
true
);
} else {
this.swapScroll(
this.storedScrollItem,
() => {},
() => {
this.storedScrollItem = undefined;
2020-01-23 11:55:51 +09:00
},
true,
true
);
2020-01-17 10:41:22 +09:00
}
} else if (this.scrollUpInitalized) {
if (!!this.newEventList && this.newEventList.length > 0) {
this.existNewMessage.emit(
this.newEventList[this.newEventList.length - 1]
);
}
} else {
speed = speed || 0;
if (!!this.readToHereEvent && this.firstCheckReadHere) {
this.swapScroll(
this.readToHereEvent,
() => {},
() => {
this.firstCheckReadHere = false;
2020-01-23 11:55:51 +09:00
},
true,
true
);
} else {
2020-01-23 11:55:51 +09:00
if (
this.virtualScroller.viewPortInfo.endIndex ===
this.eventList.length - 2
) {
this.swapScroll(
this.eventList[this.eventList.length - 1],
() => {},
() => {},
false,
false
);
} else {
this.swapScroll(
this.eventList[this.eventList.length - 1],
() => {},
() => {},
true,
false
);
}
2020-01-17 10:41:22 +09:00
}
}
}
initEventMore() {
// 방정보가 바뀌면 이전대화 보기 관련 값들을 초기화 한다.
this.scrollUpInitalized = false;
this.storedScrollItem = undefined;
2020-01-17 10:41:22 +09:00
}
clear() {}
gotoPosition(eventSeq: number) {
if (!!this.virtualScroller) {
const e = this.eventList.find(v => v.seq === eventSeq);
2020-01-21 17:48:37 +09:00
this.virtualScroller.scrollInto(e, true, 0, 0, () => {});
2020-01-17 10:41:22 +09:00
}
}
2020-01-15 13:53:17 +09:00
onClickOpenProfile(userSeq: number) {
this.openProfile.emit(userSeq);
2019-11-08 13:35:39 +09:00
}
onClickMore(event: any) {
event.preventDefault();
event.stopPropagation();
2020-01-17 10:41:22 +09:00
if (this.scrollUpInitalized && this.eventRemained) {
this.storeScrollPosition();
2020-01-17 10:41:22 +09:00
2020-01-29 18:02:57 +09:00
this.preSwapScroll(false, false);
2020-01-20 18:01:31 +09:00
2020-01-17 10:41:22 +09:00
this.moreEvent.emit(this.eventList[0].seq);
this.virtualScroller.invalidateCachedMeasurementAtIndex(0);
2020-01-17 10:41:22 +09:00
}
}
2019-10-14 17:19:13 +09:00
/** [Event] MassTalk Detail View */
2019-10-11 18:03:01 +09:00
onMassDetail(value: number) {
this.massDetail.emit(value);
}
onMassTranslationDetail(params: {
message: Info<MassTranslationEventJson>;
2020-01-02 08:44:16 +09:00
contentsType: string;
}) {
this.massTranslationDetail.emit(params);
}
2019-10-14 17:19:13 +09:00
/** [Event] Image Viewer */
2019-11-07 15:46:02 +09:00
onFileViewer(fileInfo: FileEventJson) {
this.fileViewer.emit(fileInfo);
}
2019-10-14 17:19:13 +09:00
/** [Event] Attach File Save & Save As */
onSave(value: {
fileInfo: FileEventJson;
fileDownloadItem: FileDownloadItem;
type: string;
}) {
this.save.emit(value);
}
2019-10-30 16:22:49 +09:00
/** [Event] Context Menu */
2020-01-15 13:53:17 +09:00
onContextMenu(event: {
event: MouseEvent;
message: Info<EventJson>;
type?: string;
}) {
this.contextMenu.emit(event);
}
2020-01-17 10:41:22 +09:00
onScrollup(event: any) {
if (!this.eventList || 0 === this.eventList.length) {
return;
}
2020-01-17 10:41:22 +09:00
this.scrollUpInitalized = true;
this.scrollUp.emit(event);
}
onYReachStart(event: any) {
this.yReachStart.emit(event);
}
onYReachEnd(event: any) {
this.yReachEnd.emit(event);
}
onVsChange(event: IPageInfo) {
if (
-1 === event.startIndex ||
-1 === event.endIndex ||
(0 === event.startIndex && 0 === event.endIndex)
) {
return;
}
// this.logger.debug('onVsChange', event);
}
2020-01-17 10:41:22 +09:00
trackByEvent(index: number, info: Info<EventJson>): number {
return info.seq;
}
2020-01-29 17:45:38 +09:00
compareItemsFunc = (
item1: Info<EventJson>,
item2: Info<EventJson>
// tslint:disable-next-line: semicolon
): boolean => item1.seq === item2.seq;
}