MOBILE-2962 messages: Keep scroll position when loading previous
parent
546667d11a
commit
bbfe831846
|
@ -27,6 +27,7 @@ import { CoreLoggerProvider } from '@providers/logger';
|
|||
import { CoreAppProvider } from '@providers/app';
|
||||
import { coreSlideInOut } from '@classes/animations';
|
||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||
import { CoreInfiniteLoadingComponent } from '@components/infinite-loading/infinite-loading';
|
||||
import { Md5 } from 'ts-md5/dist/md5';
|
||||
import * as moment from 'moment';
|
||||
|
||||
|
@ -41,6 +42,7 @@ import * as moment from 'moment';
|
|||
})
|
||||
export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||
@ViewChild(Content) content: Content;
|
||||
@ViewChild(CoreInfiniteLoadingComponent) infinite: CoreInfiniteLoadingComponent;
|
||||
|
||||
siteId: string;
|
||||
protected fetching: boolean;
|
||||
|
@ -381,6 +383,9 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
message.showUserData = this.showUserData(message, this.messages[index - 1]);
|
||||
});
|
||||
|
||||
// Call resize to recalculate the dimensions.
|
||||
this.content && this.content.resize();
|
||||
|
||||
// Notify that there can be a new message.
|
||||
this.notifyNewMessage();
|
||||
|
||||
|
@ -825,11 +830,28 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
* @return {Promise<any>} Resolved when done.
|
||||
*/
|
||||
loadPrevious(infiniteComplete?: any): Promise<any> {
|
||||
let infiniteHeight = this.infinite ? this.infinite.getHeight() : 0;
|
||||
const scrollHeight = this.domUtils.getScrollHeight(this.content);
|
||||
|
||||
// If there is an ongoing fetch, wait for it to finish.
|
||||
return this.waitForFetch().finally(() => {
|
||||
this.pagesLoaded++;
|
||||
|
||||
this.fetchMessages().catch((error) => {
|
||||
this.fetchMessages().then(() => {
|
||||
|
||||
// Try to keep the scroll position.
|
||||
const scrollBottom = scrollHeight - this.domUtils.getScrollTop(this.content);
|
||||
|
||||
if (this.canLoadMore && infiniteHeight && this.infinite) {
|
||||
// The height of the infinite is different while spinner is shown. Add that difference.
|
||||
infiniteHeight = infiniteHeight - this.infinite.getHeight();
|
||||
} else if (!this.canLoadMore) {
|
||||
// Can't load more, take into account the full height of the infinite loading since it will disappear now.
|
||||
infiniteHeight = infiniteHeight || (this.infinite ? this.infinite.getHeight() : 0);
|
||||
}
|
||||
|
||||
this.keepScroll(scrollHeight, scrollBottom, infiniteHeight);
|
||||
}).catch((error) => {
|
||||
this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading.
|
||||
this.pagesLoaded--;
|
||||
this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingmessages', true);
|
||||
|
@ -839,6 +861,31 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep scroll position after loading previous messages.
|
||||
* We don't use resizeContent because the approach used is different and it isn't easy to calculate these positions.
|
||||
*/
|
||||
protected keepScroll(oldScrollHeight: number, oldScrollBottom: number, infiniteHeight: number, retries?: number): void {
|
||||
retries = retries || 0;
|
||||
|
||||
setTimeout(() => {
|
||||
const newScrollHeight = this.domUtils.getScrollHeight(this.content);
|
||||
|
||||
if (newScrollHeight == oldScrollHeight) {
|
||||
// Height hasn't changed yet. Retry if max retries haven't been reached.
|
||||
if (retries <= 10) {
|
||||
this.keepScroll(oldScrollHeight, oldScrollBottom, infiniteHeight, retries + 1);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const scrollTo = newScrollHeight - oldScrollBottom + infiniteHeight;
|
||||
|
||||
this.domUtils.scrollTo(this.content, 0, scrollTo, 0);
|
||||
}, 30);
|
||||
}
|
||||
|
||||
/**
|
||||
* Content or scroll has been resized. For content, only call it if it's been added on top.
|
||||
*/
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<ng-container *ngIf="!loadingMore && position != 'top'">
|
||||
<div *ngIf="enabled || error" padding-horizontal>
|
||||
<div *ngIf="enabled || error" padding-horizontal #bottombutton>
|
||||
<button *ngIf="!error" ion-button block (click)="loadMore()" color="light">
|
||||
{{ 'core.loadmore' | translate }}
|
||||
</button>
|
||||
|
@ -9,12 +9,12 @@
|
|||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ion-infinite-scroll [enabled]="enabled && !error && !loadingMore" (ionInfinite)="loadMore($event)" [position]="position">
|
||||
<ion-infinite-scroll [enabled]="enabled && !error && !loadingMore" (ionInfinite)="loadMore($event)" [position]="position" #infinitescroll>
|
||||
<ion-infinite-scroll-content></ion-infinite-scroll-content>
|
||||
</ion-infinite-scroll>
|
||||
|
||||
<ng-container *ngIf="!loadingMore && position == 'top'">
|
||||
<div *ngIf="enabled || error" padding-horizontal>
|
||||
<div *ngIf="enabled || error" padding-horizontal #topbutton>
|
||||
<button *ngIf="!error" ion-button block (click)="loadMore()" color="light">
|
||||
{{ 'core.loadmore' | translate }}
|
||||
</button>
|
||||
|
@ -24,6 +24,6 @@
|
|||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div *ngIf="loadingMore" padding text-center>
|
||||
<div *ngIf="loadingMore" padding text-center #spinnercontainer>
|
||||
<ion-spinner></ion-spinner>
|
||||
</div>
|
|
@ -12,8 +12,9 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, Input, Output, EventEmitter, OnChanges, SimpleChange, Optional } from '@angular/core';
|
||||
import { Component, Input, Output, EventEmitter, OnChanges, SimpleChange, Optional, ViewChild, ElementRef } from '@angular/core';
|
||||
import { InfiniteScroll, Content } from 'ionic-angular';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
|
||||
/**
|
||||
* Component to show a infinite loading trigger and spinner while more data is being loaded.
|
||||
|
@ -31,11 +32,16 @@ export class CoreInfiniteLoadingComponent implements OnChanges {
|
|||
@Input() position = 'bottom';
|
||||
@Output() action: EventEmitter<() => void>; // Will emit an event when triggered.
|
||||
|
||||
@ViewChild('topbutton') topButton: ElementRef;
|
||||
@ViewChild('infinitescroll') infiniteEl: ElementRef;
|
||||
@ViewChild('bottombutton') bottomButton: ElementRef;
|
||||
@ViewChild('spinnercontainer') spinnerContainer: ElementRef;
|
||||
|
||||
loadingMore = false; // Hide button and avoid loading more.
|
||||
|
||||
protected infiniteScroll: InfiniteScroll;
|
||||
|
||||
constructor(@Optional() private content: Content) {
|
||||
constructor(@Optional() private content: Content, private domUtils: CoreDomUtilsProvider) {
|
||||
this.action = new EventEmitter();
|
||||
}
|
||||
|
||||
|
@ -77,6 +83,18 @@ export class CoreInfiniteLoadingComponent implements OnChanges {
|
|||
* Complete loading.
|
||||
*/
|
||||
complete(): void {
|
||||
if (this.position == 'top') {
|
||||
// Wait a bit before allowing loading more, otherwise it could be re-triggered automatically when it shouldn't.
|
||||
setTimeout(this.completeLoadMore.bind(this), 400);
|
||||
} else {
|
||||
this.completeLoadMore();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete loading.
|
||||
*/
|
||||
protected completeLoadMore(): void {
|
||||
this.loadingMore = false;
|
||||
this.infiniteScroll && this.infiniteScroll.complete();
|
||||
this.infiniteScroll = undefined;
|
||||
|
@ -89,4 +107,28 @@ export class CoreInfiniteLoadingComponent implements OnChanges {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the height of the element.
|
||||
*
|
||||
* @return {number} Height.
|
||||
*/
|
||||
getHeight(): number {
|
||||
return this.getElementHeight(this.topButton) + this.getElementHeight(this.infiniteEl) +
|
||||
this.getElementHeight(this.bottomButton) + this.getElementHeight(this.spinnerContainer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the height of an element.
|
||||
*
|
||||
* @param {ElementRef} element Element ref.
|
||||
* @return {number} Height.
|
||||
*/
|
||||
protected getElementHeight(element: ElementRef): number {
|
||||
if (element && element.nativeElement) {
|
||||
return this.domUtils.getElementHeight(element.nativeElement, true, true, true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue