diff --git a/src/addon/messages/pages/discussion/discussion.ts b/src/addon/messages/pages/discussion/discussion.ts index d87bc12ba..fb0f2299a 100644 --- a/src/addon/messages/pages/discussion/discussion.ts +++ b/src/addon/messages/pages/discussion/discussion.ts @@ -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} Resolved when done. */ loadPrevious(infiniteComplete?: any): Promise { + 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. */ diff --git a/src/components/infinite-loading/core-infinite-loading.html b/src/components/infinite-loading/core-infinite-loading.html index 35a8285a8..8775a5b60 100644 --- a/src/components/infinite-loading/core-infinite-loading.html +++ b/src/components/infinite-loading/core-infinite-loading.html @@ -1,5 +1,5 @@ -
+
@@ -9,12 +9,12 @@
- + -
+
@@ -24,6 +24,6 @@
-
+
\ No newline at end of file diff --git a/src/components/infinite-loading/infinite-loading.ts b/src/components/infinite-loading/infinite-loading.ts index 101e303d4..977eba6b7 100644 --- a/src/components/infinite-loading/infinite-loading.ts +++ b/src/components/infinite-loading/infinite-loading.ts @@ -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; + } + }