virtual scroll of chat room
This commit is contained in:
parent
98a067e889
commit
9bc416cce3
34
package-lock.json
generated
34
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "ucap-webmessenger",
|
||||
"version": "0.0.4",
|
||||
"version": "0.0.5",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -349,6 +349,15 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"@angular/cdk-experimental": {
|
||||
"version": "8.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@angular/cdk-experimental/-/cdk-experimental-8.2.3.tgz",
|
||||
"integrity": "sha512-pGWLh+njlSK2HavyES1g9xZQXmnqj9HJalL+EJii1gLEyvEatdJfsBxidxvdxrXaIYrY0ZfkiG2qW/4wOOKRHA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^1.7.1"
|
||||
}
|
||||
},
|
||||
"@angular/cli": {
|
||||
"version": "8.3.22",
|
||||
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-8.3.22.tgz",
|
||||
|
@ -3272,6 +3281,12 @@
|
|||
"defer-to-connect": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"@tweenjs/tween.js": {
|
||||
"version": "17.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-17.4.0.tgz",
|
||||
"integrity": "sha512-J3fzl1F6wvh8KXVVcIuHN12xi1ZDcPA/0Vix+ZcJYwZWVHUwfIqfvzYXXEw7ybeev6477KCTt9fKydU+ajUqcg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/anymatch": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz",
|
||||
|
@ -3479,6 +3494,12 @@
|
|||
"integrity": "sha512-78AdXtlhpCHT0K3EytMpn4JNxaf5tbqbLcbIRoQIHzpTIyjpxLQKRoxU55ujBXAtg3Nl2h/XWvfDa9dsMOd0pQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/tween.js": {
|
||||
"version": "17.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/tween.js/-/tween.js-17.2.0.tgz",
|
||||
"integrity": "sha512-mOsqurEtFEzwgkVc/jDVE2XrjZBYTbrmDUyCr9GXmnfc6q5otokxFtKvSY/B21zgz9LVRIvRTawKczjKi57wrA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/uglify-js": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.0.4.tgz",
|
||||
|
@ -11369,6 +11390,17 @@
|
|||
"resize-observer-polyfill": "^1.5.0"
|
||||
}
|
||||
},
|
||||
"ngx-virtual-scroller": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/ngx-virtual-scroller/-/ngx-virtual-scroller-3.0.3.tgz",
|
||||
"integrity": "sha512-00au5zpcPMzbci9VmM9QlOT6ZQ+NqgpWaXsM2Y7dg6SSlCDcFMW8nMxoMsogP2b2mrBLpIIEB91nVtwLnwWs4A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@tweenjs/tween.js": "17.4.0",
|
||||
"@types/tween.js": "17.2.0",
|
||||
"tslib": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"nice-try": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
|
||||
|
|
|
@ -59,6 +59,7 @@
|
|||
"@angular-devkit/build-ng-packagr": "^0.803.22",
|
||||
"@angular/animations": "^8.2.14",
|
||||
"@angular/cdk": "^8.2.3",
|
||||
"@angular/cdk-experimental": "^8.2.3",
|
||||
"@angular/cli": "^8.3.22",
|
||||
"@angular/common": "^8.2.14",
|
||||
"@angular/compiler": "^8.2.14",
|
||||
|
@ -134,6 +135,7 @@
|
|||
"ngrx-store-freeze": "^0.2.4",
|
||||
"ngx-logger": "^4.0.8",
|
||||
"ngx-perfect-scrollbar": "^8.0.0",
|
||||
"ngx-virtual-scroller": "^3.0.3",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"parallel-webpack": "^2.4.0",
|
||||
"protractor": "~5.4.0",
|
||||
|
|
|
@ -42,18 +42,22 @@
|
|||
mat-icon-button
|
||||
aria-label="chats button"
|
||||
class="responsive-chats-button chat-timer"
|
||||
*ngIf="!!roomInfo && roomInfo.isTimeRoom"
|
||||
*ngIf="!!roomInfoSubject.value && roomInfoSubject.value.isTimeRoom"
|
||||
>
|
||||
<mat-icon>timer</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="room-info">
|
||||
<h3 class="room-name">
|
||||
<ng-container *ngIf="!roomInfo || !userInfoList">
|
||||
<ng-container
|
||||
*ngIf="!roomInfoSubject.value || !userInfoListSubject.value"
|
||||
>
|
||||
{{ 'chat.getRoomNameInProgress' | translate }}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!!roomInfo && !!userInfoList">
|
||||
<ng-container [ngSwitch]="roomInfo.roomType">
|
||||
<ng-container
|
||||
*ngIf="!!roomInfoSubject.value && !!userInfoListSubject.value"
|
||||
>
|
||||
<ng-container [ngSwitch]="roomInfoSubject.value.roomType">
|
||||
<ng-container *ngSwitchCase="RoomType.Mytalk">
|
||||
MyTalk
|
||||
</ng-container>
|
||||
|
@ -70,11 +74,12 @@
|
|||
<ng-container *ngSwitchDefault>
|
||||
<ng-template
|
||||
[ngIf]="
|
||||
!!roomInfo.roomName && '' !== roomInfo.roomName.trim()
|
||||
!!roomInfoSubject.value.roomName &&
|
||||
'' !== roomInfoSubject.value.roomName.trim()
|
||||
"
|
||||
[ngIfElse]="roomNameNotExist"
|
||||
>
|
||||
{{ roomInfo.roomName }}
|
||||
{{ roomInfoSubject.value.roomName }}
|
||||
</ng-template>
|
||||
<ng-template #roomNameNotExist>
|
||||
{{ _roomUserInfos | ucapTranslate: 'name':',' }}
|
||||
|
@ -85,31 +90,33 @@
|
|||
</h3>
|
||||
<!-- Timer Room Info -->
|
||||
<div
|
||||
*ngIf="roomInfo && roomInfo.isTimeRoom"
|
||||
*ngIf="roomInfoSubject.value && roomInfoSubject.value.isTimeRoom"
|
||||
class="room-type text-accent-color "
|
||||
>
|
||||
<span class="bg-accent-darkest"
|
||||
>{{ getConvertTimer(roomInfo.timeRoomInterval) }} </span
|
||||
>{{
|
||||
getConvertTimer(roomInfoSubject.value.timeRoomInterval)
|
||||
}} </span
|
||||
>{{ 'chat.isRoomTypeSecret' | translate }}
|
||||
</div>
|
||||
<!-- Timer Room Info -->
|
||||
</div>
|
||||
<div class="room-option">
|
||||
<button
|
||||
*ngIf="!!roomInfo"
|
||||
*ngIf="!!roomInfoSubject.value"
|
||||
mat-icon-button
|
||||
(click)="onClickReceiveAlarm()"
|
||||
aria-label="Toggle Receive Alarm"
|
||||
>
|
||||
<mat-icon
|
||||
class="amber-fg"
|
||||
*ngIf="roomInfo.receiveAlarm"
|
||||
*ngIf="roomInfoSubject.value.receiveAlarm"
|
||||
matTooltip="{{ 'chat.notificationIsOn' | translate }}"
|
||||
>notifications_active</mat-icon
|
||||
>
|
||||
<mat-icon
|
||||
class="secondary-text"
|
||||
*ngIf="!roomInfo.receiveAlarm"
|
||||
*ngIf="!roomInfoSubject.value.receiveAlarm"
|
||||
matTooltip="{{ 'chat.notificationIsOff' | translate }}"
|
||||
>notifications_off</mat-icon
|
||||
>
|
||||
|
@ -218,39 +225,39 @@
|
|||
(fileDragLeave)="onFileDragLeave()"
|
||||
>
|
||||
<!-- CHAT MESSAGES -->
|
||||
<perfect-scrollbar
|
||||
fxFlex="1 1 auto"
|
||||
#psChatContent
|
||||
(psScrollUp)="onScrollup($event)"
|
||||
(psYReachStart)="onScrollReachStart($event)"
|
||||
(psYReachEnd)="onScrollReachEnd($event)"
|
||||
|
||||
<ucap-chat-messages
|
||||
#chatMessages
|
||||
[eventList$]="eventListSubject.asObservable()"
|
||||
[newEventList$]="eventListNewSubject.asObservable()"
|
||||
[searchedList$]="searchedListSubject.asObservable()"
|
||||
[roomInfo$]="roomInfoSubject.asObservable()"
|
||||
[eventInfoStatus$]="eventInfoStatusSubject.asObservable()"
|
||||
[eventRemained$]="eventRemainedSubject.asObservable()"
|
||||
[userInfos$]="userInfoListSubject.asObservable()"
|
||||
[loginRes$]="loginResSubject.asObservable()"
|
||||
[sessionVerInfo]="sessionVerInfo"
|
||||
[isShowUnreadCount]="getShowUnreadCount()"
|
||||
[clearReadHere]="clearReadHere"
|
||||
[minShowReadHere]="
|
||||
environment.productConfig.CommonSetting.readHereShowMinimumEventCount
|
||||
"
|
||||
[translationSimpleview]="translationSimpleview"
|
||||
[searchingMode]="moreSearchProcessing"
|
||||
(moreEvent)="onMoreEvent($event)"
|
||||
(massDetail)="onMassDetail($event)"
|
||||
(massTranslationDetail)="onMassTranslationDetail($event)"
|
||||
(save)="onSave($event)"
|
||||
(fileViewer)="onFileViewer($event)"
|
||||
(contextMenu)="onContextMenuMessage($event)"
|
||||
(openProfile)="onClickOpenProfile($event)"
|
||||
(scrollUp)="onScrollupMessages($event)"
|
||||
(yReachStart)="onYReachStartMessages($event)"
|
||||
(yReachEnd)="onYReachEndMessages($event)"
|
||||
(existNewMessage)="onExistNewMessage($event)"
|
||||
>
|
||||
<ucap-chat-messages
|
||||
[eventList]="eventList"
|
||||
[searchedList]="searchedList"
|
||||
[roomInfo]="roomInfo"
|
||||
[eventInfoStatus]="eventInfoStatus"
|
||||
[eventRemain]="eventRemain$ | async"
|
||||
[userInfos]="userInfoList"
|
||||
[loginRes]="loginRes"
|
||||
[sessionVerInfo]="sessionVerInfo"
|
||||
[isShowUnreadCount]="getShowUnreadCount()"
|
||||
[clearReadHere]="clearReadHere"
|
||||
[minShowReadHere]="
|
||||
environment.productConfig.CommonSetting.readHereShowMinimumEventCount
|
||||
"
|
||||
[initRoomLastEventSeq]="initRoomLastEventSeq"
|
||||
[translationSimpleview]="translationSimpleview"
|
||||
(moreEvent)="onMoreEvent($event)"
|
||||
(massDetail)="onMassDetail($event)"
|
||||
(massTranslationDetail)="onMassTranslationDetail($event)"
|
||||
(save)="onSave($event)"
|
||||
(fileViewer)="onFileViewer($event)"
|
||||
(contextMenu)="onContextMenuMessage($event)"
|
||||
(openProfile)="onClickOpenProfile($event)"
|
||||
>
|
||||
</ucap-chat-messages>
|
||||
</perfect-scrollbar>
|
||||
</ucap-chat-messages>
|
||||
|
||||
<!-- CHAT MESSAGES -->
|
||||
<div class="file-drop-zone-container">
|
||||
<ucap-file-upload-queue
|
||||
|
@ -319,9 +326,7 @@
|
|||
#messageContextMenuTrigger="matMenuTrigger"
|
||||
[matMenuTriggerFor]="messageContextMenu"
|
||||
></div>
|
||||
<mat-menu
|
||||
#messageContextMenu="matMenu"
|
||||
>
|
||||
<mat-menu #messageContextMenu="matMenu">
|
||||
<ng-template matMenuContent let-message="message" let-clicktype="clicktype">
|
||||
<ng-container *ngIf="!isRecalledMessage(message.type)">
|
||||
<button
|
||||
|
@ -333,14 +338,14 @@
|
|||
</button>
|
||||
<button
|
||||
mat-menu-item
|
||||
*ngIf="isForwardableMessage(message, eventInfoStatus)"
|
||||
*ngIf="isForwardableMessage(message, eventInfoStatusSubject.value)"
|
||||
(click)="onClickMessageContextMenu('REPLAY', message)"
|
||||
>
|
||||
{{ 'chat.forwardEventTo' | translate }}
|
||||
</button>
|
||||
<button
|
||||
mat-menu-item
|
||||
*ngIf="isForwardableMessage(message, eventInfoStatus)"
|
||||
*ngIf="isForwardableMessage(message, eventInfoStatusSubject.value)"
|
||||
(click)="onClickMessageContextMenu('REPLAY_TO_ME', message)"
|
||||
>
|
||||
{{ 'chat.forwardEventToMe' | translate }}
|
||||
|
@ -353,7 +358,7 @@
|
|||
</button>
|
||||
<button
|
||||
mat-menu-item
|
||||
*ngIf="isRecallableMessage(message, loginRes.userSeq)"
|
||||
*ngIf="isRecallableMessage(message, loginResSubject.value.userSeq)"
|
||||
(click)="onClickMessageContextMenu('RECALL', message)"
|
||||
>
|
||||
{{ 'chat.recallEvent' | translate }}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -5,7 +5,7 @@
|
|||
[ngClass]="{
|
||||
me: mine,
|
||||
contact: !mine,
|
||||
searched: searched
|
||||
highlight: highlight
|
||||
}"
|
||||
>
|
||||
<ucap-chat-message-box-read-here
|
||||
|
|
|
@ -87,7 +87,7 @@ $meBox-bg: #ffffff;
|
|||
text-align: end;
|
||||
}
|
||||
}
|
||||
&.searched {
|
||||
&.highlight {
|
||||
.bubble {
|
||||
color: red;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ export class MessageBoxComponent implements OnInit, AfterViewInit {
|
|||
mine = false;
|
||||
|
||||
@Input()
|
||||
searched = false;
|
||||
highlight = false;
|
||||
|
||||
@Input()
|
||||
existReadToHere = false;
|
||||
|
@ -113,10 +113,12 @@ export class MessageBoxComponent implements OnInit, AfterViewInit {
|
|||
ngAfterViewInit(): void {
|
||||
this.logger.debug(
|
||||
'offsetHeight' + this.message.seq,
|
||||
this.elementRef.nativeElement.offsetHeight
|
||||
this.mbContainer.nativeElement.offsetHeight
|
||||
);
|
||||
this.elementRef.nativeElement.style.height = `${this.elementRef.nativeElement.offsetHeight}px`;
|
||||
this.elementRef.nativeElement.style.maxHeight = `${this.elementRef.nativeElement.offsetHeight}px`;
|
||||
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');
|
||||
}
|
||||
|
||||
|
|
|
@ -1,62 +1,80 @@
|
|||
<div class="chat-messages" #scrollMe>
|
||||
<div
|
||||
*ngIf="eventRemain && messages.length > 0"
|
||||
class="message-row view-previous"
|
||||
>
|
||||
<button mat-button (click)="onClickMore($event)" class="bg-accent-dark">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<circle class="st0" cx="10" cy="10" r="7.3" />
|
||||
<polyline class="st0" points="12.9,10 10,7.1 7.1,10 " />
|
||||
<line class="st0" x1="10" y1="12.9" x2="10" y2="7.1" />
|
||||
</svg>
|
||||
<span>{{ 'chat.event.showPreviousEvents' | translate }}</span>
|
||||
</button>
|
||||
<virtual-scroller
|
||||
#scroll
|
||||
[items]="eventList"
|
||||
perfectScrollbar
|
||||
fxFlexFill
|
||||
#psChatContent
|
||||
(psScrollUp)="onScrollup($event)"
|
||||
(psYReachStart)="onYReachStart($event)"
|
||||
(psYReachEnd)="onYReachEnd($event)"
|
||||
[enableUnequalChildrenSizes]="true"
|
||||
>
|
||||
<div class="chat-messages" #scrollMe>
|
||||
<div
|
||||
class="unRead-count"
|
||||
*ngIf="
|
||||
!!roomInfo &&
|
||||
!!firstEventSeq &&
|
||||
roomInfo.lastReadEventSeq < firstEventSeq
|
||||
"
|
||||
*ngIf="eventRemained && eventList.length > 0"
|
||||
class="message-row view-previous"
|
||||
>
|
||||
<span class="line"></span>
|
||||
<span class="count" [innerHTML]="getStringReadHereMore()"> </span>
|
||||
<button mat-button (click)="onClickMore($event)" class="bg-accent-dark">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<circle class="st0" cx="10" cy="10" r="7.3" />
|
||||
<polyline class="st0" points="12.9,10 10,7.1 7.1,10 " />
|
||||
<line class="st0" x1="10" y1="12.9" x2="10" y2="7.1" />
|
||||
</svg>
|
||||
<span>{{ 'chat.event.showPreviousEvents' | translate }}</span>
|
||||
</button>
|
||||
<div
|
||||
class="unRead-count"
|
||||
*ngIf="
|
||||
!!roomInfo &&
|
||||
!!baseEventSeq &&
|
||||
roomInfo.lastReadEventSeq < baseEventSeq
|
||||
"
|
||||
>
|
||||
<span class="line"></span>
|
||||
<span class="count" [innerHTML]="getStringReadHereMore()"> </span>
|
||||
<span class="line"></span>
|
||||
</div>
|
||||
<span class="line"></span>
|
||||
</div>
|
||||
<span class="line"></span>
|
||||
</div>
|
||||
|
||||
<!-- MESSAGE -->
|
||||
<ucap-chat-message-box
|
||||
*ngFor="let message of messages; let i = index"
|
||||
[id]="message.seq"
|
||||
[message]="message"
|
||||
[mine]="message.senderSeq === loginRes.userSeq"
|
||||
[searched]="getEventSearched(message.seq)"
|
||||
[existReadToHere]="getReadHere(i) && existReadHere && !clearReadHere"
|
||||
[dateChanged]="getDateSplitter(i)"
|
||||
[senderName]="getUserName(message.senderSeq)"
|
||||
[profileImageRoot]="profileImageRoot"
|
||||
[profileImage]="getUserProfile(message.senderSeq)"
|
||||
[eventInfoStatus]="eventInfoStatus"
|
||||
[translationSimpleview]="translationSimpleview"
|
||||
[unreadCount]="isShowUnreadCount ? getUnreadCount(message) : undefined"
|
||||
(openProfile)="onClickOpenProfile($event)"
|
||||
(massDetail)="onMassDetail($event)"
|
||||
(massTranslationDetail)="onMassTranslationDetail($event)"
|
||||
(fileViewer)="onFileViewer($event)"
|
||||
(save)="onSave($event)"
|
||||
(contextMenu)="onContextMenu($event)"
|
||||
>
|
||||
</ucap-chat-message-box>
|
||||
</div>
|
||||
<!-- MESSAGE -->
|
||||
<div #container>
|
||||
<ucap-chat-message-box
|
||||
*ngFor="
|
||||
let message of scroll.viewPortItems;
|
||||
trackBy: trackByEvent;
|
||||
let i = index
|
||||
"
|
||||
[id]="message.seq"
|
||||
[message]="message"
|
||||
[mine]="message.senderSeq === loginRes.userSeq"
|
||||
[highlight]="isHighlightedEvent(message.seq)"
|
||||
[existReadToHere]="getReadHere(i) && existReadHere && !clearReadHere"
|
||||
[dateChanged]="getDateSplitter(i)"
|
||||
[senderName]="getUserName(message.senderSeq)"
|
||||
[profileImageRoot]="profileImageRoot"
|
||||
[profileImage]="getUserProfile(message.senderSeq)"
|
||||
[eventInfoStatus]="eventInfoStatus"
|
||||
[translationSimpleview]="translationSimpleview"
|
||||
[unreadCount]="isShowUnreadCount ? getUnreadCount(message) : undefined"
|
||||
(openProfile)="onClickOpenProfile($event)"
|
||||
(massDetail)="onMassDetail($event)"
|
||||
(massTranslationDetail)="onMassTranslationDetail($event)"
|
||||
(fileViewer)="onFileViewer($event)"
|
||||
(save)="onSave($event)"
|
||||
(contextMenu)="onContextMenu($event)"
|
||||
>
|
||||
</ucap-chat-message-box>
|
||||
</div>
|
||||
</div>
|
||||
</virtual-scroller>
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
import { Component, OnInit, Input, EventEmitter, Output } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
Input,
|
||||
EventEmitter,
|
||||
Output,
|
||||
ViewChild,
|
||||
OnDestroy
|
||||
} from '@angular/core';
|
||||
|
||||
import {
|
||||
Info,
|
||||
|
@ -12,40 +20,39 @@ import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
|
|||
import { UserInfo, RoomInfo, RoomType } from '@ucap-webmessenger/protocol-room';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { VersionInfo2Response } from '@ucap-webmessenger/api-public';
|
||||
import { FileInfo } from '../models/file-info.json';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import moment from 'moment';
|
||||
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';
|
||||
|
||||
@Component({
|
||||
selector: 'ucap-chat-messages',
|
||||
templateUrl: './messages.component.html',
|
||||
styleUrls: ['./messages.component.scss']
|
||||
})
|
||||
export class MessagesComponent implements OnInit {
|
||||
export class MessagesComponent implements OnInit, OnDestroy {
|
||||
@Input()
|
||||
loginRes: LoginResponse;
|
||||
loginRes$: Observable<LoginResponse>;
|
||||
@Input()
|
||||
roomInfo: RoomInfo;
|
||||
roomInfo$: Observable<RoomInfo>;
|
||||
@Input()
|
||||
set eventList(elist: Info<EventJson>[]) {
|
||||
if (!!elist && elist.length > 0) {
|
||||
this.firstEventSeq = elist[0].seq;
|
||||
}
|
||||
|
||||
this.messages = elist;
|
||||
}
|
||||
eventList$: Observable<Info<EventJson>[]>;
|
||||
@Input()
|
||||
searchedList: Info<EventJson>[];
|
||||
newEventList$: Observable<Info<EventJson>[]>;
|
||||
@Input()
|
||||
eventInfoStatus?: InfoResponse;
|
||||
searchedList$: Observable<Info<EventJson>[]>;
|
||||
@Input()
|
||||
eventRemain: boolean;
|
||||
eventInfoStatus$: Observable<InfoResponse>;
|
||||
@Input()
|
||||
userInfos?: UserInfo[];
|
||||
eventRemained$: Observable<boolean>;
|
||||
@Input()
|
||||
sessionVerInfo: VersionInfo2Response;
|
||||
@Input()
|
||||
userInfos$: Observable<UserInfo[]>;
|
||||
|
||||
@Input()
|
||||
isShowUnreadCount = true;
|
||||
@Input()
|
||||
|
@ -53,9 +60,9 @@ export class MessagesComponent implements OnInit {
|
|||
@Input()
|
||||
minShowReadHere = 10;
|
||||
@Input()
|
||||
initRoomLastEventSeq: number;
|
||||
@Input()
|
||||
translationSimpleview = false;
|
||||
@Input()
|
||||
searchingMode = false;
|
||||
|
||||
@Output()
|
||||
openProfile = new EventEmitter<number>();
|
||||
|
@ -83,13 +90,50 @@ export class MessagesComponent implements OnInit {
|
|||
type?: string;
|
||||
}>();
|
||||
|
||||
messages: Info<EventJson>[];
|
||||
@Output()
|
||||
scrollUp = new EventEmitter<any>();
|
||||
|
||||
@Output()
|
||||
yReachEnd = new EventEmitter<any>();
|
||||
@Output()
|
||||
yReachStart = new EventEmitter<any>();
|
||||
|
||||
@Output()
|
||||
existNewMessage = new EventEmitter<Info<EventJson>>();
|
||||
|
||||
@ViewChild(PerfectScrollbarDirective, { static: false })
|
||||
psChatContent?: PerfectScrollbarDirective;
|
||||
|
||||
@ViewChild(VirtualScrollerComponent, { static: false })
|
||||
private virtualScroller: VirtualScrollerComponent;
|
||||
|
||||
storedScrollHeight = 0; // 이전대화를 불러올 경우 현재 스크롤 포지션 유지를 위한 값. 0 이면 초기로딩.
|
||||
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;
|
||||
|
||||
EventType = EventType;
|
||||
profileImageRoot: string;
|
||||
moment = moment;
|
||||
|
||||
firstEventSeq = 0;
|
||||
existReadHere = false;
|
||||
|
||||
constructor(
|
||||
|
@ -101,6 +145,91 @@ export class MessagesComponent implements OnInit {
|
|||
ngOnInit() {
|
||||
this.profileImageRoot =
|
||||
this.profileImageRoot || this.sessionVerInfo.profileRoot;
|
||||
|
||||
this.loginResSubscription = this.loginRes$.subscribe(loginRes => {
|
||||
this.loginRes = loginRes;
|
||||
});
|
||||
this.roomInfoSubscription = this.roomInfo$.subscribe(roomInfo => {
|
||||
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;
|
||||
}
|
||||
|
||||
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.eventList = eventList;
|
||||
});
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -132,7 +261,8 @@ export class MessagesComponent implements OnInit {
|
|||
}
|
||||
return '';
|
||||
}
|
||||
getEventSearched(seq: number): boolean {
|
||||
|
||||
isHighlightedEvent(seq: number): boolean {
|
||||
return (
|
||||
!!this.searchedList &&
|
||||
this.searchedList.filter(event => event.seq === seq).length > 0
|
||||
|
@ -177,11 +307,11 @@ export class MessagesComponent implements OnInit {
|
|||
if (curIndex > 0) {
|
||||
return (
|
||||
this.datePipe.transform(
|
||||
moment(this.messages[curIndex].sendDate).toDate(),
|
||||
moment(this.eventList[curIndex].sendDate).toDate(),
|
||||
'yyyyMMdd'
|
||||
) !==
|
||||
this.datePipe.transform(
|
||||
moment(this.messages[curIndex - 1].sendDate).toDate(),
|
||||
moment(this.eventList[curIndex - 1].sendDate).toDate(),
|
||||
'yyyyMMdd'
|
||||
)
|
||||
);
|
||||
|
@ -202,7 +332,7 @@ export class MessagesComponent implements OnInit {
|
|||
) {
|
||||
if (!this.roomInfo.isTimeRoom) {
|
||||
if (
|
||||
this.messages[messageIndex].seq ===
|
||||
this.eventList[messageIndex].seq ===
|
||||
this.roomInfo.lastReadEventSeq + 1
|
||||
) {
|
||||
this.existReadHere = true;
|
||||
|
@ -217,11 +347,99 @@ export class MessagesComponent implements OnInit {
|
|||
getStringReadHereMore(): string {
|
||||
let rtnStr = '';
|
||||
rtnStr = this.translateService.instant('chat.event.moreUnreadEventsWith', {
|
||||
countOfUnread: this.firstEventSeq - (this.roomInfo.lastReadEventSeq + 1)
|
||||
countOfUnread: this.baseEventSeq - (this.roomInfo.lastReadEventSeq + 1)
|
||||
});
|
||||
return rtnStr;
|
||||
}
|
||||
|
||||
storeScrollHeight() {
|
||||
this.storedScrollHeight = this.psChatContent.elementRef.nativeElement.scrollHeight;
|
||||
}
|
||||
|
||||
ready(): void {
|
||||
setTimeout(() => {
|
||||
this.scrollToBottom();
|
||||
});
|
||||
}
|
||||
|
||||
scrollToBottom(speed?: number): void {
|
||||
if (this.storedScrollHeight > 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 {
|
||||
setTimeout(() => {
|
||||
this.psChatContent.scrollToTop(
|
||||
this.psChatContent.elementRef.nativeElement.scrollHeight -
|
||||
this.storedScrollHeight,
|
||||
speed
|
||||
);
|
||||
|
||||
this.storedScrollHeight = 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
} 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 {
|
||||
setTimeout(() => {
|
||||
this.psChatContent.scrollToBottom(0, speed);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initEventMore() {
|
||||
// 방정보가 바뀌면 이전대화 보기 관련 값들을 초기화 한다.
|
||||
this.scrollUpInitalized = false;
|
||||
this.storedScrollHeight = 0;
|
||||
}
|
||||
|
||||
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);
|
||||
this.virtualScroller.scrollDebounceTime = 0;
|
||||
this.virtualScroller.scrollInto(e);
|
||||
}
|
||||
}
|
||||
|
||||
onClickOpenProfile(userSeq: number) {
|
||||
this.openProfile.emit(userSeq);
|
||||
}
|
||||
|
@ -230,7 +448,11 @@ export class MessagesComponent implements OnInit {
|
|||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
this.moreEvent.emit(this.messages[0].seq);
|
||||
if (this.scrollUpInitalized && this.eventRemained) {
|
||||
this.storedScrollHeight = this.psChatContent.elementRef.nativeElement.scrollHeight;
|
||||
|
||||
this.moreEvent.emit(this.eventList[0].seq);
|
||||
}
|
||||
}
|
||||
|
||||
/** [Event] MassTalk Detail View */
|
||||
|
@ -267,4 +489,19 @@ export class MessagesComponent implements OnInit {
|
|||
}) {
|
||||
this.contextMenu.emit(event);
|
||||
}
|
||||
|
||||
onScrollup(event: any) {
|
||||
this.scrollUpInitalized = true;
|
||||
this.scrollUp.emit(event);
|
||||
}
|
||||
onYReachStart(event: any) {
|
||||
this.yReachStart.emit(event);
|
||||
}
|
||||
onYReachEnd(event: any) {
|
||||
this.yReachEnd.emit(event);
|
||||
}
|
||||
|
||||
trackByEvent(index: number, info: Info<EventJson>): number {
|
||||
return info.seq;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,12 +4,19 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|||
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
|
||||
import { ScrollingModule } from '@angular/cdk/scrolling';
|
||||
import { ScrollingModule as ExperimentalScrollingModule } from '@angular/cdk-experimental/scrolling';
|
||||
|
||||
import { MatTooltipModule } from '@angular/material';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
|
||||
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
|
||||
import { VirtualScrollerModule } from 'ngx-virtual-scroller';
|
||||
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { UCapUiModule } from '@ucap-webmessenger/ui';
|
||||
|
@ -36,7 +43,6 @@ import { TranslationComponent as MBTranslationComponent } from './components/mes
|
|||
import { VideoComponent as MBVideoComponent } from './components/message-box/video.component';
|
||||
import { VideoConferenceComponent as MBVideoConferenceComponent } from './components/message-box/video-conference.component';
|
||||
import { AllimComponent as MBAllimComponent } from './components/message-box/allim.component';
|
||||
import { MatTooltipModule } from '@angular/material';
|
||||
|
||||
const COMPONENTS = [
|
||||
FormComponent,
|
||||
|
@ -71,6 +77,10 @@ const PROVIDERS = [DatePipe];
|
|||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
FlexLayoutModule,
|
||||
|
||||
ScrollingModule,
|
||||
ExperimentalScrollingModule,
|
||||
|
||||
MatFormFieldModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
|
@ -78,6 +88,9 @@ const PROVIDERS = [DatePipe];
|
|||
MatMenuModule,
|
||||
MatTooltipModule,
|
||||
|
||||
PerfectScrollbarModule,
|
||||
VirtualScrollerModule,
|
||||
|
||||
TranslateModule,
|
||||
|
||||
UCapUiModule
|
||||
|
|
|
@ -19,7 +19,9 @@ export * from './lib/components/message-box/video-conference.component';
|
|||
export * from './lib/components/message-box/video.component';
|
||||
|
||||
export * from './lib/components/form.component';
|
||||
export * from './lib/components/message-box.component';
|
||||
export * from './lib/components/messages.component';
|
||||
export * from './lib/components/search.component';
|
||||
|
||||
export * from './lib/models/file-info.json';
|
||||
export * from './lib/models/mass-talk-info.json';
|
||||
|
|
Loading…
Reference in New Issue
Block a user