virtual scroll of chat room is implemented
This commit is contained in:
parent
86a9cd3d84
commit
f41c5ae5f0
|
@ -7,7 +7,8 @@ import {
|
|||
Output,
|
||||
EventEmitter,
|
||||
Inject,
|
||||
ChangeDetectorRef
|
||||
ChangeDetectorRef,
|
||||
ChangeDetectionStrategy
|
||||
} from '@angular/core';
|
||||
import {
|
||||
ucapAnimations,
|
||||
|
@ -133,7 +134,8 @@ import { FileProtocolService } from '@ucap-webmessenger/protocol-file';
|
|||
selector: 'app-layout-messenger-messages',
|
||||
templateUrl: './messages.component.html',
|
||||
styleUrls: ['./messages.component.scss'],
|
||||
animations: ucapAnimations
|
||||
animations: ucapAnimations,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
@Output()
|
||||
|
@ -391,14 +393,14 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
}
|
||||
});
|
||||
|
||||
this.interval = setInterval(() => {
|
||||
if (
|
||||
!!this.roomInfoSubject.value &&
|
||||
!!this.roomInfoSubject.value.isTimeRoom
|
||||
) {
|
||||
if (
|
||||
!!this.roomInfoSubject.value &&
|
||||
!!this.roomInfoSubject.value.isTimeRoom
|
||||
) {
|
||||
this.interval = setInterval(() => {
|
||||
this.store.dispatch(EventStore.infoIntervalClear({}));
|
||||
}
|
||||
}, 1000);
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
|
@ -424,7 +426,9 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
this.searchEventListProcessingSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
clearInterval(this.interval);
|
||||
if (!!this.interval) {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
|
@ -1847,7 +1851,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
this.eventRemainedSubject.value
|
||||
) {
|
||||
this.moreSearchProcessing = true;
|
||||
this.chatMessages.storeScrollHeight();
|
||||
this.chatMessages.storeScrollPosition();
|
||||
|
||||
// Case :: retrieve event infos step by step until include searchtext in event..
|
||||
this.store.dispatch(
|
||||
|
|
|
@ -111,14 +111,14 @@ export class MessageBoxComponent implements OnInit, AfterViewInit {
|
|||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.logger.debug(
|
||||
'offsetHeight' + this.message.seq,
|
||||
this.mbContainer.nativeElement.offsetHeight
|
||||
);
|
||||
this.elementRef.nativeElement.style.height = `${this.mbContainer
|
||||
.nativeElement.offsetHeight + 20}px`;
|
||||
this.elementRef.nativeElement.style.maxHeight = `${this.mbContainer
|
||||
.nativeElement.offsetHeight + 20}px`;
|
||||
// this.logger.debug(
|
||||
// 'offsetHeight' + this.message.seq,
|
||||
// this.mbContainer.nativeElement.offsetHeight
|
||||
// );
|
||||
// this.elementRef.nativeElement.style.height = `${this.mbContainer
|
||||
// .nativeElement.offsetHeight + 20}px`;
|
||||
// this.elementRef.nativeElement.style.maxHeight = `${this.mbContainer
|
||||
// .nativeElement.offsetHeight + 20}px`;
|
||||
this.mbContainer.nativeElement.classList.remove('hide');
|
||||
}
|
||||
|
||||
|
|
|
@ -9,8 +9,14 @@
|
|||
(psYReachStart)="onYReachStart($event)"
|
||||
(psYReachEnd)="onYReachEnd($event)"
|
||||
[enableUnequalChildrenSizes]="true"
|
||||
[modifyOverflowStyleOfParentScroll]="false"
|
||||
(vsChange)="onVsChange($event)"
|
||||
>
|
||||
<div class="chat-messages" [style.opacity]="fixScreen ? 0 : 1">
|
||||
<div
|
||||
#chatMessagesContainer
|
||||
class="chat-messages hide"
|
||||
[style.opacity]="fixScreen ? 0 : 1"
|
||||
>
|
||||
<div
|
||||
*ngIf="eventRemained && eventList.length > 0"
|
||||
class="message-row view-previous"
|
||||
|
@ -51,17 +57,15 @@
|
|||
<!-- MESSAGE -->
|
||||
<div #container>
|
||||
<ucap-chat-message-box
|
||||
*ngFor="
|
||||
let message of scroll.viewPortItems;
|
||||
trackBy: trackByEvent;
|
||||
let i = index
|
||||
"
|
||||
*ngFor="let message of scroll.viewPortItems; trackBy: trackByEvent"
|
||||
[id]="message.seq"
|
||||
[message]="message"
|
||||
[mine]="message.senderSeq === loginRes.userSeq"
|
||||
[highlight]="isHighlightedEvent(message.seq)"
|
||||
[existReadToHere]="getReadHere(i) && existReadHere && !clearReadHere"
|
||||
[dateChanged]="getDateSplitter(i)"
|
||||
[existReadToHere]="
|
||||
getReadHere(message) && existReadHere && !clearReadHere
|
||||
"
|
||||
[dateChanged]="getDateSplitter(message)"
|
||||
[senderName]="getUserName(message.senderSeq)"
|
||||
[profileImageRoot]="profileImageRoot"
|
||||
[profileImage]="getUserProfile(message.senderSeq)"
|
||||
|
|
|
@ -185,3 +185,7 @@ $meBox-bg: #ffffff;
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hide {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ import {
|
|||
Output,
|
||||
ViewChild,
|
||||
OnDestroy,
|
||||
ElementRef,
|
||||
Self
|
||||
ChangeDetectionStrategy,
|
||||
ElementRef
|
||||
} from '@angular/core';
|
||||
|
||||
import {
|
||||
|
@ -28,12 +28,13 @@ import { FileDownloadItem } from '@ucap-webmessenger/api';
|
|||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { VirtualScrollerComponent } from 'ngx-virtual-scroller';
|
||||
import { VirtualScrollerComponent, IPageInfo } from 'ngx-virtual-scroller';
|
||||
|
||||
@Component({
|
||||
selector: 'ucap-chat-messages',
|
||||
templateUrl: './messages.component.html',
|
||||
styleUrls: ['./messages.component.scss']
|
||||
styleUrls: ['./messages.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class MessagesComponent implements OnInit, OnDestroy {
|
||||
@Input()
|
||||
|
@ -103,13 +104,16 @@ export class MessagesComponent implements OnInit, OnDestroy {
|
|||
@Output()
|
||||
existNewMessage = new EventEmitter<Info<EventJson>>();
|
||||
|
||||
@ViewChild('chatMessagesContainer', { static: false })
|
||||
chatMessagesContainer: ElementRef<HTMLElement>;
|
||||
|
||||
@ViewChild(PerfectScrollbarDirective, { static: false })
|
||||
psChatContent: PerfectScrollbarDirective;
|
||||
|
||||
@ViewChild(VirtualScrollerComponent, { static: false })
|
||||
private virtualScroller: VirtualScrollerComponent;
|
||||
|
||||
storedScrollHeight = 0; // 이전대화를 불러올 경우 현재 스크롤 포지션 유지를 위한 값. 0 이면 초기로딩.
|
||||
storedScrollItem: Info<EventJson>; // 이전대화를 불러올 경우 현재 스크롤 포지션 유지를 위한 값. 0 이면 초기로딩.
|
||||
scrollUpInitalized = false; // ps 에서 초기 로딩시 scroll reach start 이벤트 발생 버그를 우회하기 위한 init 값으로 scrollUp 에 의해 true 로 된다.
|
||||
firstCheckReadHere = true;
|
||||
initRoomLastEventSeq: number;
|
||||
|
@ -175,6 +179,8 @@ export class MessagesComponent implements OnInit, OnDestroy {
|
|||
this.firstCheckReadHere = false;
|
||||
}
|
||||
|
||||
this.eventList = eventList;
|
||||
|
||||
if (this.searchingMode) {
|
||||
const baseseq = this.baseEventSeq;
|
||||
// setTimeout(() => {
|
||||
|
@ -187,8 +193,6 @@ export class MessagesComponent implements OnInit, OnDestroy {
|
|||
this.ready();
|
||||
}
|
||||
}
|
||||
|
||||
this.eventList = eventList;
|
||||
});
|
||||
this.newEventListSubscription = this.newEventList$.subscribe(
|
||||
newEventList => {
|
||||
|
@ -310,7 +314,9 @@ export class MessagesComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
/** Date Splitter show check */
|
||||
getDateSplitter(curIndex: number): boolean {
|
||||
getDateSplitter(message: Info<EventJson>): boolean {
|
||||
const curIndex = this.eventList.findIndex(v => v.seq === message.seq);
|
||||
|
||||
if (curIndex === 0) {
|
||||
return true;
|
||||
}
|
||||
|
@ -318,21 +324,15 @@ export class MessagesComponent implements OnInit, OnDestroy {
|
|||
if (!this.eventList[curIndex]) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
this.datePipe.transform(
|
||||
moment(this.eventList[curIndex].sendDate).toDate(),
|
||||
'yyyyMMdd'
|
||||
) !==
|
||||
this.datePipe.transform(
|
||||
moment(this.eventList[curIndex - 1].sendDate).toDate(),
|
||||
'yyyyMMdd'
|
||||
)
|
||||
return !moment(this.eventList[curIndex].sendDate).isSame(
|
||||
moment(this.eventList[curIndex - 1].sendDate),
|
||||
'day'
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
getReadHere(messageIndex: number): boolean {
|
||||
getReadHere(message: Info<EventJson>): boolean {
|
||||
if (
|
||||
!!this.roomInfo &&
|
||||
!!this.roomInfo.lastReadEventSeq &&
|
||||
|
@ -344,6 +344,10 @@ export class MessagesComponent implements OnInit, OnDestroy {
|
|||
this.roomInfo.roomType === RoomType.Multi
|
||||
) {
|
||||
if (!this.roomInfo.isTimeRoom) {
|
||||
const messageIndex = this.eventList.findIndex(
|
||||
v => v.seq === message.seq
|
||||
);
|
||||
|
||||
if (
|
||||
this.eventList[messageIndex].seq ===
|
||||
this.roomInfo.lastReadEventSeq + 1
|
||||
|
@ -365,40 +369,51 @@ export class MessagesComponent implements OnInit, OnDestroy {
|
|||
return rtnStr;
|
||||
}
|
||||
|
||||
storeScrollHeight() {
|
||||
this.storedScrollHeight = this.psChatContent.elementRef.nativeElement.scrollHeight;
|
||||
storeScrollPosition() {
|
||||
this.storedScrollItem = this.eventList[
|
||||
this.virtualScroller.viewPortInfo.startIndex
|
||||
];
|
||||
}
|
||||
|
||||
scrollToStoredItem() {
|
||||
if (!this.scrollUpInitalized) {
|
||||
this.chatMessagesContainer.nativeElement.classList.remove('hide');
|
||||
}
|
||||
|
||||
this.virtualScroller.scrollInto(this.storedScrollItem, true, 0, 0, () => {
|
||||
this.storedScrollItem = undefined;
|
||||
});
|
||||
}
|
||||
|
||||
ready(): void {
|
||||
if (!this.scrollUpInitalized) {
|
||||
this.chatMessagesContainer.nativeElement.classList.add('hide');
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this.scrollToBottom();
|
||||
// setTimeout(() => {
|
||||
// this.chatMessagesContainer.nativeElement.classList.remove('hide');
|
||||
// }, 100);
|
||||
});
|
||||
}
|
||||
|
||||
scrollToBottom(speed?: number): void {
|
||||
if (this.storedScrollHeight > 0) {
|
||||
if (this.psChatContent) {
|
||||
this.psChatContent.update();
|
||||
if (!!this.storedScrollItem) {
|
||||
// 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);
|
||||
});
|
||||
const element = document.getElementById('message-box-readhere');
|
||||
if (!!element && this.firstCheckReadHere) {
|
||||
setTimeout(() => {
|
||||
this.psChatContent.scrollToTop(element.offsetTop - 200, speed);
|
||||
});
|
||||
|
||||
this.firstCheckReadHere = false;
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this.psChatContent.scrollToTop(
|
||||
this.psChatContent.elementRef.nativeElement.scrollHeight -
|
||||
this.storedScrollHeight,
|
||||
speed
|
||||
);
|
||||
|
||||
this.storedScrollHeight = 0;
|
||||
});
|
||||
}
|
||||
this.firstCheckReadHere = false;
|
||||
} else {
|
||||
this.scrollToStoredItem();
|
||||
}
|
||||
// }
|
||||
} else if (this.scrollUpInitalized) {
|
||||
if (!!this.newEventList && this.newEventList.length > 0) {
|
||||
this.existNewMessage.emit(
|
||||
|
@ -418,9 +433,23 @@ export class MessagesComponent implements OnInit, OnDestroy {
|
|||
|
||||
this.firstCheckReadHere = false;
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this.psChatContent.scrollToBottom(0, speed);
|
||||
});
|
||||
this.virtualScroller.scrollInto(
|
||||
this.eventList[this.eventList.length - 1],
|
||||
true,
|
||||
0,
|
||||
0,
|
||||
() => {
|
||||
if (!this.scrollUpInitalized) {
|
||||
this.chatMessagesContainer.nativeElement.classList.remove(
|
||||
'hide'
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// setTimeout(() => {
|
||||
// this.psChatContent.scrollToBottom(0, speed);
|
||||
// });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -429,7 +458,7 @@ export class MessagesComponent implements OnInit, OnDestroy {
|
|||
initEventMore() {
|
||||
// 방정보가 바뀌면 이전대화 보기 관련 값들을 초기화 한다.
|
||||
this.scrollUpInitalized = false;
|
||||
this.storedScrollHeight = 0;
|
||||
this.storedScrollItem = undefined;
|
||||
}
|
||||
|
||||
clear() {}
|
||||
|
@ -464,9 +493,12 @@ export class MessagesComponent implements OnInit, OnDestroy {
|
|||
event.stopPropagation();
|
||||
|
||||
if (this.scrollUpInitalized && this.eventRemained) {
|
||||
this.storedScrollHeight = this.psChatContent.elementRef.nativeElement.scrollHeight;
|
||||
// this.storedScrollItem = this.psChatContent.elementRef.nativeElement.scrollHeight;
|
||||
this.storeScrollPosition();
|
||||
|
||||
this.moreEvent.emit(this.eventList[0].seq);
|
||||
|
||||
this.virtualScroller.invalidateCachedMeasurementAtIndex(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -506,6 +538,9 @@ export class MessagesComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
onScrollup(event: any) {
|
||||
if (!this.eventList || 0 === this.eventList.length) {
|
||||
return;
|
||||
}
|
||||
this.scrollUpInitalized = true;
|
||||
this.scrollUp.emit(event);
|
||||
}
|
||||
|
@ -516,6 +551,17 @@ export class MessagesComponent implements OnInit, OnDestroy {
|
|||
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);
|
||||
}
|
||||
|
||||
trackByEvent(index: number, info: Info<EventJson>): number {
|
||||
return info.seq;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user