diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/messages.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/messages.component.ts index f5b78937..3672db1a 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/messages.component.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/messages.component.ts @@ -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( diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box.component.ts b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box.component.ts index 29e0694a..789b7d32 100644 --- a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box.component.ts +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box.component.ts @@ -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'); } diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/messages.component.html b/projects/ucap-webmessenger-ui-chat/src/lib/components/messages.component.html index 7df4f3e0..57ea8b25 100644 --- a/projects/ucap-webmessenger-ui-chat/src/lib/components/messages.component.html +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/messages.component.html @@ -9,8 +9,14 @@ (psYReachStart)="onYReachStart($event)" (psYReachEnd)="onYReachEnd($event)" [enableUnequalChildrenSizes]="true" + [modifyOverflowStyleOfParentScroll]="false" + (vsChange)="onVsChange($event)" > -
+
>(); + @ViewChild('chatMessagesContainer', { static: false }) + chatMessagesContainer: ElementRef; + @ViewChild(PerfectScrollbarDirective, { static: false }) psChatContent: PerfectScrollbarDirective; @ViewChild(VirtualScrollerComponent, { static: false }) private virtualScroller: VirtualScrollerComponent; - storedScrollHeight = 0; // 이전대화를 불러올 경우 현재 스크롤 포지션 유지를 위한 값. 0 이면 초기로딩. + storedScrollItem: Info; // 이전대화를 불러올 경우 현재 스크롤 포지션 유지를 위한 값. 0 이면 초기로딩. scrollUpInitalized = false; // ps 에서 초기 로딩시 scroll reach start 이벤트 발생 버그를 우회하기 위한 init 값으로 scrollUp 에 의해 true 로 된다. firstCheckReadHere = true; initRoomLastEventSeq: number; @@ -142,6 +147,7 @@ export class MessagesComponent implements OnInit, OnDestroy { constructor( private logger: NGXLogger, private datePipe: DatePipe, + private changeDetectorRef: ChangeDetectorRef, private translateService: TranslateService ) {} @@ -153,12 +159,12 @@ export class MessagesComponent implements OnInit, OnDestroy { this.loginRes = loginRes; }); this.roomInfoSubscription = this.roomInfo$.subscribe(roomInfo => { - this.eventList = undefined; - this.newEventList = undefined; - this.searchedList = undefined; - this.eventInfoStatus = undefined; - this.eventRemained = undefined; - this.userInfos = undefined; + // this.eventList = undefined; + // this.newEventList = undefined; + // this.searchedList = undefined; + // this.eventInfoStatus = undefined; + // this.eventRemained = undefined; + // this.userInfos = undefined; this.initEventMore(); this.roomInfo = roomInfo; @@ -175,6 +181,9 @@ export class MessagesComponent implements OnInit, OnDestroy { this.firstCheckReadHere = false; } + this.eventList = eventList; + this.changeDetectorRef.detectChanges(); + if (this.searchingMode) { const baseseq = this.baseEventSeq; // setTimeout(() => { @@ -187,8 +196,6 @@ export class MessagesComponent implements OnInit, OnDestroy { this.ready(); } } - - this.eventList = eventList; }); this.newEventListSubscription = this.newEventList$.subscribe( newEventList => { @@ -280,6 +287,9 @@ export class MessagesComponent implements OnInit, OnDestroy { } getUnreadCount(message: Info): string | number { + // if (!this.userInfos || 0 === this.userInfos.length) { + // return ''; + // } const unreadCnt = this.userInfos.filter(user => { if (message.senderSeq === user.seq) { // 본인 글은 unreadCount 에 포함하지 않는다. @@ -310,7 +320,9 @@ export class MessagesComponent implements OnInit, OnDestroy { } /** Date Splitter show check */ - getDateSplitter(curIndex: number): boolean { + getDateSplitter(message: Info): boolean { + const curIndex = this.eventList.findIndex(v => v.seq === message.seq); + if (curIndex === 0) { return true; } @@ -318,21 +330,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): boolean { if ( !!this.roomInfo && !!this.roomInfo.lastReadEventSeq && @@ -344,6 +350,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 +375,48 @@ 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(); }); } 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 +436,15 @@ export class MessagesComponent implements OnInit, OnDestroy { this.firstCheckReadHere = false; } else { - setTimeout(() => { - this.psChatContent.scrollToBottom(0, speed); - }); + this.virtualScroller.scrollToIndex( + this.eventList.length - 1, + true, + 0, + 0, + () => { + this.chatMessagesContainer.nativeElement.classList.remove('hide'); + } + ); } } } @@ -429,7 +453,7 @@ export class MessagesComponent implements OnInit, OnDestroy { initEventMore() { // 방정보가 바뀌면 이전대화 보기 관련 값들을 초기화 한다. this.scrollUpInitalized = false; - this.storedScrollHeight = 0; + this.storedScrollItem = undefined; } clear() {} @@ -464,9 +488,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 +533,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 +546,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): number { return info.seq; }