572 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 { PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
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(PerfectScrollbarDirective, { static: false })
2020-01-17 12:29:04 +09:00
psChatContent: PerfectScrollbarDirective;
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
2019-12-19 14:23:11 +09:00
existReadHere = false;
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;
});
this.eventListSubscription = this.eventList$.subscribe(eventList => {
if (
!!eventList &&
eventList.length > 0 &&
!!this.roomInfo &&
!!this.roomInfo.lastReadEventSeq &&
this.baseEventSeq <= this.roomInfo.lastReadEventSeq
) {
// 조회된 내용중에 read here 가 있을 경우.
this.firstCheckReadHere = false;
}
this.eventList = eventList;
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 '';
// }
2019-10-30 16:22:49 +09:00
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 , .
* 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;
}
getReadHere(message: Info<EventJson>): boolean {
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) {
const messageIndex = this.eventList.findIndex(
v => v.seq === message.seq
);
2019-12-19 14:23:11 +09:00
if (
2020-01-17 10:41:22 +09:00
this.eventList[messageIndex].seq ===
2019-12-19 14:23:11 +09:00
this.roomInfo.lastReadEventSeq + 1
) {
this.existReadHere = true;
return true;
}
}
}
}
return false;
}
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
];
}
scrollToStoredItem() {
2020-01-20 18:01:31 +09:00
this.virtualScroller.scrollInto(this.storedScrollItem, true, 0, 0, () => {
this.chatMessagesBuffer.nativeElement.innerHTML = '';
this.chatMessagesBuffer.nativeElement.scrollTop = 0;
this.chatMessagesBuffer.nativeElement.classList.add('disappear');
this.chatMessagesContainer.nativeElement.classList.remove('hide');
this.storedScrollItem = undefined;
});
2020-01-17 10:41:22 +09:00
}
ready(): void {
if (!this.scrollUpInitalized) {
this.chatMessagesContainer.nativeElement.classList.add('hide');
}
2020-01-17 10:41:22 +09:00
setTimeout(() => {
this.scrollToBottom();
});
}
scrollToBottom(speed?: number): void {
if (!!this.storedScrollItem) {
// if (this.psChatContent) {
// this.psChatContent.update();
2020-01-17 10:41:22 +09:00
const element = document.getElementById('message-box-readhere');
if (!!element && this.firstCheckReadHere) {
setTimeout(() => {
this.psChatContent.scrollToTop(element.offsetTop - 200, speed);
});
2020-01-17 10:41:22 +09:00
this.firstCheckReadHere = false;
} else {
this.scrollToStoredItem();
2020-01-17 10:41:22 +09:00
}
// }
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.psChatContent) {
this.psChatContent.update();
const element = document.getElementById('message-box-readhere');
if (!!element && this.firstCheckReadHere) {
setTimeout(() => {
this.psChatContent.scrollToTop(element.offsetTop - 200, speed);
});
this.firstCheckReadHere = false;
} else {
2020-01-20 17:26:38 +09:00
this.virtualScroller.scrollToIndex(
this.eventList.length - 1,
true,
0,
0,
() => {
2020-01-20 17:26:38 +09:00
this.chatMessagesContainer.nativeElement.classList.remove('hide');
}
);
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.psChatContent) {
// this.psChatContent.update();
// const element = document.getElementById(eventSeq.toString());
// if (!!element) {
// setTimeout(() => {
// this.psChatContent.scrollToTop(element.offsetTop - 200);
// });
// }
// }
if (!!this.virtualScroller) {
const e = this.eventList.find(v => v.seq === eventSeq);
2020-01-20 18:01:31 +09:00
this.virtualScroller.scrollInto(e, false, undefined, 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.storedScrollItem = this.psChatContent.elementRef.nativeElement.scrollHeight;
this.storeScrollPosition();
2020-01-17 10:41:22 +09:00
2020-01-20 18:01:31 +09:00
this.chatMessagesBuffer.nativeElement.innerHTML = this.chatMessagesContainer.nativeElement.innerHTML;
this.chatMessagesBuffer.nativeElement.scrollTop = this.chatMessagesContainer.nativeElement.scrollTop;
this.chatMessagesBuffer.nativeElement.classList.remove('disappear');
this.chatMessagesContainer.nativeElement.classList.add('hide');
this.changeDetectorRef.detectChanges();
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;
}
}