animation of chat search is added

This commit is contained in:
richard-loafle 2020-01-30 14:12:51 +09:00
parent dd23462374
commit d23efa7b8e
7 changed files with 83 additions and 56 deletions

11
package-lock.json generated
View File

@ -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",

View File

@ -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",

View File

@ -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

View File

@ -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);
}
} }

View File

@ -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"

View File

@ -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;
} }

View File

@ -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';