Skip to content

Commit

Permalink
Merge pull request #3 from kamilkisiela/fix/to-infinite-scroll
Browse files Browse the repository at this point in the history
Fix/to infinite scroll
  • Loading branch information
eitanfr authored Aug 27, 2017
2 parents ea0b023 + 2b6b100 commit b3f69b9
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 78 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
"@angular/platform-browser-dynamic": "^4.1.0",
"@angular/router": "^4.0.0",
"@angular/service-worker": "^1.0.0-beta.14",
"angular2-virtual-scroll": "eitanfr/angular2-virtual-scroll#077f2afd622d9fb6aaa81774fe7f96e8f3750685",
"apollo-angular": "^0.13.0",
"apollo-client": "^1.3.0",
"core-js": "^2.4.1",
Expand Down
30 changes: 10 additions & 20 deletions src/app/chat/chat-view/chat-view.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,13 @@
</ion-toolbar>
</ion-header>
<ion-content>
<div class="message-scroller">
<div class="loading-spinner-container" *ngIf="loadingMessages && isFirstLoad">
<ion-spinner class="loading-spinner"></ion-spinner>
<div #chatContent class="message-scroller">
<div class="loading-spinner" *ngIf="chatService.isLoadingMoreMessages() || initialLoading">
<ion-spinner name="bubbles" ></ion-spinner>
</div>

<virtual-scroll
[items]="messages"
[childHeight]="66"
[keepIndexOnChange]="keepIndexOnItemsChange"
(update)="scrollItems = $event"
(change)="scrollValueChanged($event)">

<div class="loading-more-spinner" *ngIf="isLoadingMore">
<ion-spinner name="bubbles"></ion-spinner>
</div>

<ion-item *ngFor="let message of scrollItems" no-lines>
<chat-message [message]="message"></chat-message>
</ion-item>

</virtual-scroll>
<ion-item *ngFor="let message of messages" no-lines>
<chat-message [message]="message"></chat-message>
</ion-item>
</div>
</ion-content>
<ion-footer>
Expand All @@ -39,3 +25,7 @@
</button>
</ion-row>
</ion-footer>




16 changes: 4 additions & 12 deletions src/app/chat/chat-view/chat-view.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
}

.message-scroller {
overflow: scroll;
overflow: auto;
position: absolute;
height: 100%;
width: 100%;
Expand All @@ -41,17 +41,9 @@
}

.loading-spinner {
width: 100px;
height: 100px;
}

virtual-scroll,
[virtualScroll] {
overflow: hidden;
overflow-y: auto;
width: 100%;
height: 100%;
display: block;
display: flex;
justify-content: center;
margin-top: 15px;
}

.message-input {
Expand Down
107 changes: 64 additions & 43 deletions src/app/chat/chat-view/chat-view.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import {
import { ActivatedRoute, Router } from '@angular/router';
import { ChatService } from '../services/chat/chat.service';
import { MessagesQuery } from '../../graphql/types/types';
import { ChangeEvent, VirtualScrollComponent } from 'angular2-virtual-scroll';
import { ChannelsService } from '../services/channels/channels.service';
import { Subscription } from 'rxjs/Subscription';
import { Observable } from 'rxjs/Observable';


@Component({
Expand All @@ -25,11 +25,12 @@ import { Subscription } from 'rxjs/Subscription';
export class ChatViewComponent implements OnInit, OnDestroy {

@ViewChild('chatContent') chatContent: any;
@ViewChild(VirtualScrollComponent) virtualScroll: VirtualScrollComponent;
@ViewChild('messageInput') messageInput: any;

private readonly PAGE_MESSAGE_COUNT = 100;
private readonly LOAD_ITEMS_NUM_TRIGGER = 40;
private readonly PAGE_MESSAGE_COUNT = 80;
private readonly PAGE_PERCENT_LOAD_MORE_TRIGGER = 0.3;
private readonly MAX_PAGE_LOAD_MORE_PIXEL_LEN = 3500;


public channel: MessagesQuery.Channel;
private routeParamsSub;
Expand All @@ -39,11 +40,8 @@ export class ChatViewComponent implements OnInit, OnDestroy {
private chatContentScrollSubscription;
public isFirstLoad = true;
public messages;
private scrollValue: ChangeEvent;
public isLoadingMore;
public keepIndexOnItemsChange = false;
public scrollItems: any;
public loadingMessages = false;
public initialLoading = false;

constructor(private router: Router,
private route: ActivatedRoute,
Expand Down Expand Up @@ -80,27 +78,27 @@ export class ChatViewComponent implements OnInit, OnDestroy {
this.channelSub = channelObservable.subscribe((result) => {
const channelData = result.data;
const channelLoading = result.loading;
this.loadingMessages = channelLoading && !channelData;
if (this.loadingMessages) {
this.initialLoading = channelLoading && !channelData;
if (this.initialLoading) {
this.cd.markForCheck();
return;
}
this.channel = isDirect ? channelData.directChannel : channelData.channelByName;
this.cd.markForCheck();

const messagesQueryObservable = this.chatService.getMessages({
channelId: this.channel.id,
channelDetails: { name: this.channel.name, direct: isDirect },
count: this.PAGE_MESSAGE_COUNT,
cursor: null,
searchRegex: null,
channelId: this.channel.id ,
channelDetails: {name: this.channel.name , direct: isDirect} ,
count: this.PAGE_MESSAGE_COUNT ,
cursor: null ,
searchRegex: null ,
excludeServer: true
}
);

this.messagesSub = messagesQueryObservable.subscribe(({ data, loading }) => {
this.loadingMessages = loading && !data;
if (this.loadingMessages) {
this.messagesSub = messagesQueryObservable.subscribe(({data , loading}) => {
this.initialLoading = loading && !data;
if (this.initialLoading) {
this.cd.markForCheck();
return;
}
Expand All @@ -110,35 +108,75 @@ export class ChatViewComponent implements OnInit, OnDestroy {
return;
}

const oldScrollHeight = this.chatContent.nativeElement.scrollHeight;
this.messages = data.messages.messagesArray;

if (this.isFirstLoad) {
this.isFirstLoad = false;
this.channel = data.messages.channel;
this.chatService.subscribeToMessageAdded(this.channel.id);

this.scrollToBottom();
setTimeout(() => {
this.addScrollListener();
this.scrollToBottom();
}, 0);
}

if (!this.chatService.isLoadingMoreMessages() && this.isScrolledToBottom()) {
setTimeout(() => {
this.scrollToBottom();
}, 0);
}

if (this.isScrolledToTop()) {
setTimeout(() => {
this.chatContent.nativeElement.scrollTop = this.chatContent.nativeElement.scrollHeight - oldScrollHeight;
}, 0);
}

if (!this.isFirstLoad && this.messages && this.isScrolledToBottom()) {
this.scrollToBottom();
}

this.cd.markForCheck();
});

this.scrollToBottom();
this.cd.markForCheck();
});
this.cd.markForCheck();
});
}

isScrolledToBottom(): boolean {
if (this.scrollValue) {
return this.scrollValue.end === this.messages.length - 1;
addScrollListener() {
const currentPageHeight = this.chatContent.nativeElement.scrollHeight;
const pagePixelLenForLoadMore = Math.min(currentPageHeight * this.PAGE_PERCENT_LOAD_MORE_TRIGGER , this.MAX_PAGE_LOAD_MORE_PIXEL_LEN);

if (!this.chatContentScrollSubscription) {
this.chatContentScrollSubscription = Observable.fromEvent(this.chatContent.nativeElement, 'scroll').subscribe(() => {
this.onScrollChange(pagePixelLenForLoadMore);
});
}
return false;
}

onScrollChange(pagePixelLenForLoadMore) {
if (this.chatContent.nativeElement.scrollTop < pagePixelLenForLoadMore) {
if (!this.chatService.isLoadingMoreMessages()) {
this.loadMoreMessages();
this.cd.markForCheck();
}
}
}

isScrolledToBottom(): boolean {
const chatElement = this.chatContent.nativeElement;
return chatElement.scrollTop + chatElement.clientHeight >= chatElement.scrollHeight;
}

isScrolledToTop(): boolean {
return this.chatContent.nativeElement.scrollTop === 0;
}

scrollToBottom() {
setTimeout(() => this.virtualScroll.scrollInto(this.messages[this.messages.length - 1]), 1);
this.chatContent.nativeElement.scrollTop = this.chatContent.nativeElement.scrollHeight;
}

sendMessage() {
Expand All @@ -150,27 +188,10 @@ export class ChatViewComponent implements OnInit, OnDestroy {
}
}

isLoadMoreNeeded() {
const scrollValue = this.scrollValue;
return !this.isLoadingMore && scrollValue && scrollValue.start < this.LOAD_ITEMS_NUM_TRIGGER;
}

loadMoreMessages() {
return this.chatService.loadMoreMessages(this.channel.id, this.PAGE_MESSAGE_COUNT);
}

scrollValueChanged(scrollValue) {
this.scrollValue = scrollValue;
if (this.messages && !this.isFirstLoad && this.isLoadMoreNeeded()) {
this.isLoadingMore = true;
this.keepIndexOnItemsChange = true;
this.loadMoreMessages().then(() => {
this.isLoadingMore = false;
});
}
}


unsubscribeChannel() {
this.chatService.unsubscribeMessagesSubscription();
if (this.messagesSub) {
Expand Down
2 changes: 0 additions & 2 deletions src/app/chat/chat.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@ import { MainSidenavComponent } from './main-sidenav/main-sidenav.component';
import { ChannelsService } from './services/channels/channels.service';
import { ChatService } from './services/chat/chat.service';
import { ChannelNotFoundComponent } from './channel-not-found/channel-not-found.component';
import { VirtualScrollModule } from 'angular2-virtual-scroll';

@NgModule({
imports: [
CommonModule,
SharedModule,
ChatRoutingModule,
VirtualScrollModule,
],
declarations: [
MainPageComponent,
Expand Down

0 comments on commit b3f69b9

Please sign in to comment.