animation of chat search is added
This commit is contained in:
parent
dd23462374
commit
d23efa7b8e
11
package-lock.json
generated
11
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "ucap-webmessenger",
|
"name": "ucap-webmessenger",
|
||||||
"version": "0.0.9",
|
"version": "0.0.10",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -3870,6 +3870,15 @@
|
||||||
"integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
|
"integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"angular-animations": {
|
||||||
|
"version": "0.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/angular-animations/-/angular-animations-0.0.10.tgz",
|
||||||
|
"integrity": "sha512-UKKWRZDDXl3m+bcS1PfW5xZ2WoyM9ixfdLS7OG9lDrWm5KkIYEIrZvC+r5nfU3C5ovltHId5e2BlwYqL18kxOA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^1.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"angular-split": {
|
"angular-split": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/angular-split/-/angular-split-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/angular-split/-/angular-split-3.0.2.tgz",
|
||||||
|
|
|
@ -99,6 +99,7 @@
|
||||||
"@types/webpack": "^4.41.2",
|
"@types/webpack": "^4.41.2",
|
||||||
"@types/webpack-merge": "^4.1.5",
|
"@types/webpack-merge": "^4.1.5",
|
||||||
"@types/webpack-node-externals": "^1.7.0",
|
"@types/webpack-node-externals": "^1.7.0",
|
||||||
|
"angular-animations": "^0.0.10",
|
||||||
"angular-split": "^3.0.2",
|
"angular-split": "^3.0.2",
|
||||||
"autolinker": "^3.11.1",
|
"autolinker": "^3.11.1",
|
||||||
"awesome-node-loader": "^1.1.1",
|
"awesome-node-loader": "^1.1.1",
|
||||||
|
|
|
@ -22,7 +22,13 @@
|
||||||
>
|
>
|
||||||
</ucap-chat-message-box-date-splitter>
|
</ucap-chat-message-box-date-splitter>
|
||||||
|
|
||||||
<div class="chat-row">
|
<div
|
||||||
|
class="chat-row"
|
||||||
|
[@shake]="{
|
||||||
|
value: shakeIt,
|
||||||
|
params: { duration: 1000, delay: 0, translate: '5px' }
|
||||||
|
}"
|
||||||
|
>
|
||||||
<div *ngIf="isInformation(message); then information; else contents"></div>
|
<div *ngIf="isInformation(message); then information; else contents"></div>
|
||||||
<ng-template #information>
|
<ng-template #information>
|
||||||
<ng-container
|
<ng-container
|
||||||
|
|
|
@ -6,9 +6,12 @@ import {
|
||||||
Output,
|
Output,
|
||||||
AfterViewInit,
|
AfterViewInit,
|
||||||
ElementRef,
|
ElementRef,
|
||||||
ViewChild
|
ViewChild,
|
||||||
|
ChangeDetectorRef
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { shakeAnimation } from 'angular-animations';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Info,
|
Info,
|
||||||
EventType,
|
EventType,
|
||||||
|
@ -18,14 +21,14 @@ import {
|
||||||
MassTranslationEventJson
|
MassTranslationEventJson
|
||||||
} from '@ucap-webmessenger/protocol-event';
|
} from '@ucap-webmessenger/protocol-event';
|
||||||
import { NGXLogger } from 'ngx-logger';
|
import { NGXLogger } from 'ngx-logger';
|
||||||
import { DatePipe } from '@angular/common';
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { FileDownloadItem } from '@ucap-webmessenger/api';
|
import { FileDownloadItem } from '@ucap-webmessenger/api';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ucap-chat-message-box',
|
selector: 'ucap-chat-message-box',
|
||||||
templateUrl: './message-box.component.html',
|
templateUrl: './message-box.component.html',
|
||||||
styleUrls: ['./message-box.component.scss']
|
styleUrls: ['./message-box.component.scss'],
|
||||||
|
animations: [shakeAnimation()]
|
||||||
})
|
})
|
||||||
export class MessageBoxComponent implements OnInit, AfterViewInit {
|
export class MessageBoxComponent implements OnInit, AfterViewInit {
|
||||||
@Input()
|
@Input()
|
||||||
|
@ -99,11 +102,11 @@ export class MessageBoxComponent implements OnInit, AfterViewInit {
|
||||||
|
|
||||||
firstEventSeq = 0;
|
firstEventSeq = 0;
|
||||||
existReadHere = false;
|
existReadHere = false;
|
||||||
|
shakeIt = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private elementRef: ElementRef<HTMLElement>,
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
private logger: NGXLogger,
|
private logger: NGXLogger
|
||||||
private datePipe: DatePipe
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
@ -186,4 +189,13 @@ export class MessageBoxComponent implements OnInit, AfterViewInit {
|
||||||
this.contextMenu.emit({ event, message });
|
this.contextMenu.emit({ event, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shake() {
|
||||||
|
this.shakeIt = false;
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
setTimeout(() => {
|
||||||
|
this.shakeIt = true;
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
}, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
fxFlexFill
|
fxFlexFill
|
||||||
#psChatContent
|
#psChatContent
|
||||||
[bufferAmount]="5"
|
[bufferAmount]="5"
|
||||||
[compareItems]=""
|
[compareItems]="compareItemsFunc"
|
||||||
(psScrollUp)="onScrollup($event)"
|
(psScrollUp)="onScrollup($event)"
|
||||||
(psYReachStart)="onYReachStart($event)"
|
(psYReachStart)="onYReachStart($event)"
|
||||||
(psYReachEnd)="onYReachEnd($event)"
|
(psYReachEnd)="onYReachEnd($event)"
|
||||||
|
@ -53,6 +53,7 @@
|
||||||
<!-- MESSAGE -->
|
<!-- MESSAGE -->
|
||||||
<div #container>
|
<div #container>
|
||||||
<ucap-chat-message-box
|
<ucap-chat-message-box
|
||||||
|
#chatMessageBox
|
||||||
*ngFor="let message of scroll.viewPortItems; trackBy: trackByEvent"
|
*ngFor="let message of scroll.viewPortItems; trackBy: trackByEvent"
|
||||||
[id]="message.seq"
|
[id]="message.seq"
|
||||||
[message]="message"
|
[message]="message"
|
||||||
|
|
|
@ -8,7 +8,9 @@ import {
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
ElementRef,
|
ElementRef,
|
||||||
ChangeDetectorRef
|
ChangeDetectorRef,
|
||||||
|
ViewChildren,
|
||||||
|
QueryList
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -23,12 +25,12 @@ import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
|
||||||
import { UserInfo, RoomInfo, RoomType } from '@ucap-webmessenger/protocol-room';
|
import { UserInfo, RoomInfo, RoomType } from '@ucap-webmessenger/protocol-room';
|
||||||
import { NGXLogger } from 'ngx-logger';
|
import { NGXLogger } from 'ngx-logger';
|
||||||
import { VersionInfo2Response } from '@ucap-webmessenger/api-public';
|
import { VersionInfo2Response } from '@ucap-webmessenger/api-public';
|
||||||
import { DatePipe } from '@angular/common';
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { FileDownloadItem } from '@ucap-webmessenger/api';
|
import { FileDownloadItem } from '@ucap-webmessenger/api';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { Observable, Subscription } from 'rxjs';
|
import { Observable, Subscription } from 'rxjs';
|
||||||
import { VirtualScrollerComponent, IPageInfo } from 'ngx-virtual-scroller';
|
import { VirtualScrollerComponent, IPageInfo } from 'ngx-virtual-scroller';
|
||||||
|
import { MessageBoxComponent } from './message-box.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ucap-chat-messages',
|
selector: 'ucap-chat-messages',
|
||||||
|
@ -113,6 +115,9 @@ export class MessagesComponent implements OnInit, OnDestroy {
|
||||||
@ViewChild(VirtualScrollerComponent, { static: false })
|
@ViewChild(VirtualScrollerComponent, { static: false })
|
||||||
private virtualScroller: VirtualScrollerComponent;
|
private virtualScroller: VirtualScrollerComponent;
|
||||||
|
|
||||||
|
@ViewChildren('chatMessageBox')
|
||||||
|
chatMessageBoxList: QueryList<MessageBoxComponent>;
|
||||||
|
|
||||||
storedScrollItem: Info<EventJson>; // 이전대화를 불러올 경우 현재 스크롤 포지션 유지를 위한 값. 0 이면 초기로딩.
|
storedScrollItem: Info<EventJson>; // 이전대화를 불러올 경우 현재 스크롤 포지션 유지를 위한 값. 0 이면 초기로딩.
|
||||||
scrollUpInitalized = false; // ps 에서 초기 로딩시 scroll reach start 이벤트 발생 버그를 우회하기 위한 init 값으로 scrollUp 에 의해 true 로 된다.
|
scrollUpInitalized = false; // ps 에서 초기 로딩시 scroll reach start 이벤트 발생 버그를 우회하기 위한 init 값으로 scrollUp 에 의해 true 로 된다.
|
||||||
firstCheckReadHere = true;
|
firstCheckReadHere = true;
|
||||||
|
@ -142,12 +147,10 @@ export class MessagesComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
readToHereEvent: Info<EventJson>;
|
readToHereEvent: Info<EventJson>;
|
||||||
existReadToHereEvent = true;
|
existReadToHereEvent = true;
|
||||||
swapped = false;
|
|
||||||
hidden = false;
|
hidden = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private logger: NGXLogger,
|
private logger: NGXLogger,
|
||||||
private datePipe: DatePipe,
|
|
||||||
private changeDetectorRef: ChangeDetectorRef,
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
private translateService: TranslateService
|
private translateService: TranslateService
|
||||||
) {}
|
) {}
|
||||||
|
@ -390,14 +393,13 @@ export class MessagesComponent implements OnInit, OnDestroy {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
swapScroll(
|
scrollTo(
|
||||||
to: Info<EventJson>,
|
to: Info<EventJson>,
|
||||||
preCallback: () => void,
|
preCallback: () => void,
|
||||||
postCallback: () => void,
|
postCallback: () => void,
|
||||||
useHide: boolean,
|
useHide: boolean
|
||||||
useSwap: boolean
|
|
||||||
) {
|
) {
|
||||||
this.preSwapScroll(useHide, useSwap);
|
this.preSwapScroll(useHide);
|
||||||
if (!!preCallback) {
|
if (!!preCallback) {
|
||||||
preCallback();
|
preCallback();
|
||||||
}
|
}
|
||||||
|
@ -406,33 +408,19 @@ export class MessagesComponent implements OnInit, OnDestroy {
|
||||||
if (!!postCallback) {
|
if (!!postCallback) {
|
||||||
postCallback();
|
postCallback();
|
||||||
}
|
}
|
||||||
this.postSwapScroll(useHide, useSwap);
|
this.postSwapScroll(useHide);
|
||||||
}, 100);
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
preSwapScroll(useHide: boolean, useSwap: boolean) {
|
preSwapScroll(useHide: boolean) {
|
||||||
// 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;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (useHide && !this.hidden) {
|
if (useHide && !this.hidden) {
|
||||||
this.chatMessagesContainer.nativeElement.classList.add('hide');
|
this.chatMessagesContainer.nativeElement.classList.add('hide');
|
||||||
this.hidden = true;
|
this.hidden = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
postSwapScroll(useHide: boolean, useSwap: boolean) {
|
postSwapScroll(useHide: boolean) {
|
||||||
// if (useSwap && this.swapped) {
|
|
||||||
// this.chatMessagesBuffer.nativeElement.innerHTML = '';
|
|
||||||
// this.chatMessagesBuffer.nativeElement.scrollTop = 0;
|
|
||||||
// this.chatMessagesBuffer.nativeElement.classList.add('disappear');
|
|
||||||
// this.swapped = false;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (useHide && this.hidden) {
|
if (useHide && this.hidden) {
|
||||||
this.chatMessagesContainer.nativeElement.classList.remove('hide');
|
this.chatMessagesContainer.nativeElement.classList.remove('hide');
|
||||||
this.hidden = false;
|
this.hidden = false;
|
||||||
|
@ -443,27 +431,29 @@ export class MessagesComponent implements OnInit, OnDestroy {
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollToBottom(speed?: number): void {
|
scrollToBottom(): void {
|
||||||
if (!!this.storedScrollItem) {
|
if (!!this.storedScrollItem) {
|
||||||
if (!!this.readToHereEvent && this.firstCheckReadHere) {
|
if (!!this.readToHereEvent && this.firstCheckReadHere) {
|
||||||
this.swapScroll(
|
this.scrollTo(
|
||||||
this.readToHereEvent,
|
this.readToHereEvent,
|
||||||
() => {},
|
() => {},
|
||||||
() => {
|
() => {
|
||||||
this.firstCheckReadHere = false;
|
this.firstCheckReadHere = false;
|
||||||
},
|
},
|
||||||
true,
|
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.swapScroll(
|
const index = this.eventList.findIndex(
|
||||||
this.storedScrollItem,
|
i => i.seq === this.storedScrollItem.seq
|
||||||
|
);
|
||||||
|
|
||||||
|
this.scrollTo(
|
||||||
|
1 <= index ? this.eventList[index - 1] : this.eventList[0],
|
||||||
() => {},
|
() => {},
|
||||||
() => {
|
() => {
|
||||||
this.storedScrollItem = undefined;
|
this.storedScrollItem = undefined;
|
||||||
},
|
},
|
||||||
true,
|
false
|
||||||
true
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (this.scrollUpInitalized) {
|
} else if (this.scrollUpInitalized) {
|
||||||
|
@ -473,16 +463,13 @@ export class MessagesComponent implements OnInit, OnDestroy {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
speed = speed || 0;
|
|
||||||
|
|
||||||
if (!!this.readToHereEvent && this.firstCheckReadHere) {
|
if (!!this.readToHereEvent && this.firstCheckReadHere) {
|
||||||
this.swapScroll(
|
this.scrollTo(
|
||||||
this.readToHereEvent,
|
this.readToHereEvent,
|
||||||
() => {},
|
() => {},
|
||||||
() => {
|
() => {
|
||||||
this.firstCheckReadHere = false;
|
this.firstCheckReadHere = false;
|
||||||
},
|
},
|
||||||
true,
|
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -490,20 +477,18 @@ export class MessagesComponent implements OnInit, OnDestroy {
|
||||||
this.virtualScroller.viewPortInfo.endIndex ===
|
this.virtualScroller.viewPortInfo.endIndex ===
|
||||||
this.eventList.length - 2
|
this.eventList.length - 2
|
||||||
) {
|
) {
|
||||||
this.swapScroll(
|
this.scrollTo(
|
||||||
this.eventList[this.eventList.length - 1],
|
this.eventList[this.eventList.length - 1],
|
||||||
() => {},
|
() => {},
|
||||||
() => {},
|
() => {},
|
||||||
false,
|
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.swapScroll(
|
this.scrollTo(
|
||||||
this.eventList[this.eventList.length - 1],
|
this.eventList[this.eventList.length - 1],
|
||||||
() => {},
|
() => {},
|
||||||
() => {},
|
() => {},
|
||||||
true,
|
true
|
||||||
false
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -520,8 +505,21 @@ export class MessagesComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
gotoPosition(eventSeq: number) {
|
gotoPosition(eventSeq: number) {
|
||||||
if (!!this.virtualScroller) {
|
if (!!this.virtualScroller) {
|
||||||
const e = this.eventList.find(v => v.seq === eventSeq);
|
const index = this.eventList.findIndex(v => v.seq === eventSeq);
|
||||||
this.virtualScroller.scrollInto(e, true, 0, 0, () => {});
|
this.virtualScroller.scrollInto(
|
||||||
|
1 <= index ? this.eventList[index - 1] : this.eventList[0],
|
||||||
|
true,
|
||||||
|
0,
|
||||||
|
100,
|
||||||
|
() => {
|
||||||
|
const chatMessageBox = this.chatMessageBoxList.find(
|
||||||
|
el => el.message.seq === eventSeq
|
||||||
|
);
|
||||||
|
if (!!chatMessageBox) {
|
||||||
|
chatMessageBox.shake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -536,7 +534,7 @@ export class MessagesComponent implements OnInit, OnDestroy {
|
||||||
if (this.scrollUpInitalized && this.eventRemained) {
|
if (this.scrollUpInitalized && this.eventRemained) {
|
||||||
this.storeScrollPosition();
|
this.storeScrollPosition();
|
||||||
|
|
||||||
this.preSwapScroll(false, false);
|
this.preSwapScroll(false);
|
||||||
|
|
||||||
this.moreEvent.emit(this.eventList[0].seq);
|
this.moreEvent.emit(this.eventList[0].seq);
|
||||||
|
|
||||||
|
@ -612,5 +610,5 @@ export class MessagesComponent implements OnInit, OnDestroy {
|
||||||
item1: Info<EventJson>,
|
item1: Info<EventJson>,
|
||||||
item2: Info<EventJson>
|
item2: Info<EventJson>
|
||||||
// tslint:disable-next-line: semicolon
|
// tslint:disable-next-line: semicolon
|
||||||
): boolean => item1.seq === item2.seq;
|
): boolean => !!item1 && !!item2 && item1.seq === item2.seq;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* Public API Surface of ucap-webmessenger-ui
|
* Public API Surface of ucap-webmessenger-ui
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from './lib/animations';
|
export * from './lib/animations/index';
|
||||||
|
|
||||||
export * from './lib/components/file-viewer/document-viewer.component';
|
export * from './lib/components/file-viewer/document-viewer.component';
|
||||||
export * from './lib/components/file-viewer/image-viewer.component';
|
export * from './lib/components/file-viewer/image-viewer.component';
|
||||||
|
|
Loading…
Reference in New Issue
Block a user