forked from CIT/Vmeda.Online
		
	MOBILE-3320 messages: Improve scroll position after loading previous
This commit is contained in:
		
							parent
							
								
									8681b91a0e
								
							
						
					
					
						commit
						a81d1c7161
					
				@ -174,6 +174,6 @@
 | 
				
			|||||||
            </p>
 | 
					            </p>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <core-send-message-form *ngIf="footerType == 'message'" (onSubmit)="sendMessage($event)" [showKeyboard]="showKeyboard"
 | 
					        <core-send-message-form *ngIf="footerType == 'message'" (onSubmit)="sendMessage($event)" [showKeyboard]="showKeyboard"
 | 
				
			||||||
            [placeholder]="'addon.messages.newmessage' | translate" (onResize)="resizeContent()"></core-send-message-form>
 | 
					            [placeholder]="'addon.messages.newmessage' | translate"></core-send-message-form>
 | 
				
			||||||
    </ion-toolbar>
 | 
					    </ion-toolbar>
 | 
				
			||||||
</ion-footer>
 | 
					</ion-footer>
 | 
				
			||||||
 | 
				
			|||||||
@ -12,7 +12,7 @@
 | 
				
			|||||||
// See the License for the specific language governing permissions and
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
// limitations under the License.
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { AfterViewInit, Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
 | 
					import { AfterViewInit, Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core';
 | 
				
			||||||
import { IonContent } from '@ionic/angular';
 | 
					import { IonContent } from '@ionic/angular';
 | 
				
			||||||
import { AlertOptions } from '@ionic/core';
 | 
					import { AlertOptions } from '@ionic/core';
 | 
				
			||||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
					import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
				
			||||||
@ -76,6 +76,7 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
 | 
				
			|||||||
    protected viewDestroyed = false;
 | 
					    protected viewDestroyed = false;
 | 
				
			||||||
    protected memberInfoObserver: CoreEventObserver;
 | 
					    protected memberInfoObserver: CoreEventObserver;
 | 
				
			||||||
    protected showLoadingModal = false; // Whether to show a loading modal while fetching data.
 | 
					    protected showLoadingModal = false; // Whether to show a loading modal while fetching data.
 | 
				
			||||||
 | 
					    protected hostElement: HTMLElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    conversationId?: number; // Conversation ID. Undefined if it's a new individual conversation.
 | 
					    conversationId?: number; // Conversation ID. Undefined if it's a new individual conversation.
 | 
				
			||||||
    conversation?: AddonMessagesConversationFormatted; // The conversation object (if it exists).
 | 
					    conversation?: AddonMessagesConversationFormatted; // The conversation object (if it exists).
 | 
				
			||||||
@ -113,7 +114,9 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    constructor(
 | 
					    constructor(
 | 
				
			||||||
        protected route: ActivatedRoute,
 | 
					        protected route: ActivatedRoute,
 | 
				
			||||||
 | 
					        protected elementRef: ElementRef<HTMLElement>,
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
 | 
					        this.hostElement = elementRef.nativeElement;
 | 
				
			||||||
        this.siteId = CoreSites.getCurrentSiteId();
 | 
					        this.siteId = CoreSites.getCurrentSiteId();
 | 
				
			||||||
        this.currentUserId = CoreSites.getCurrentSiteUserId();
 | 
					        this.currentUserId = CoreSites.getCurrentSiteUserId();
 | 
				
			||||||
        this.groupMessagingEnabled = AddonMessages.isGroupMessagingEnabled();
 | 
					        this.groupMessagingEnabled = AddonMessages.isGroupMessagingEnabled();
 | 
				
			||||||
@ -357,7 +360,6 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
 | 
				
			|||||||
            CoreDomUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingmessages', true);
 | 
					            CoreDomUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingmessages', true);
 | 
				
			||||||
        } finally {
 | 
					        } finally {
 | 
				
			||||||
            this.checkCanDelete();
 | 
					            this.checkCanDelete();
 | 
				
			||||||
            this.resizeContent();
 | 
					 | 
				
			||||||
            this.loaded = true;
 | 
					            this.loaded = true;
 | 
				
			||||||
            this.setPolling(); // Make sure we're polling messages.
 | 
					            this.setPolling(); // Make sure we're polling messages.
 | 
				
			||||||
            this.setContactRequestInfo();
 | 
					            this.setContactRequestInfo();
 | 
				
			||||||
@ -537,7 +539,9 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const messages = Array.from(document.querySelectorAll('.addon-message-not-mine')).slice(-this.newMessages).reverse();
 | 
					            const messages = Array.from(this.hostElement.querySelectorAll('.addon-message-not-mine'))
 | 
				
			||||||
 | 
					                .slice(-this.newMessages)
 | 
				
			||||||
 | 
					                .reverse();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const newMessagesUnread = messages.findIndex((message) => {
 | 
					            const newMessagesUnread = messages.findIndex((message) => {
 | 
				
			||||||
                const elementRect = message.getBoundingClientRect();
 | 
					                const elementRect = message.getBoundingClientRect();
 | 
				
			||||||
@ -1045,7 +1049,7 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
 | 
				
			|||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let infiniteHeight = this.infinite?.infiniteEl?.nativeElement.getBoundingClientRect().height || 0;
 | 
					        let infiniteHeight = this.infinite?.hostElement.getBoundingClientRect().height || 0;
 | 
				
			||||||
        const scrollHeight = (this.scrollElement?.scrollHeight || 0);
 | 
					        const scrollHeight = (this.scrollElement?.scrollHeight || 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // If there is an ongoing fetch, wait for it to finish.
 | 
					        // If there is an ongoing fetch, wait for it to finish.
 | 
				
			||||||
@ -1060,7 +1064,7 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
 | 
				
			|||||||
                // Try to keep the scroll position.
 | 
					                // Try to keep the scroll position.
 | 
				
			||||||
                const scrollBottom = scrollHeight - (this.scrollElement?.scrollTop || 0);
 | 
					                const scrollBottom = scrollHeight - (this.scrollElement?.scrollTop || 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                const height = this.infinite?.infiniteEl?.nativeElement.getBoundingClientRect().height || 0;
 | 
					                const height = this.infinite?.hostElement.getBoundingClientRect().height || 0;
 | 
				
			||||||
                if (this.canLoadMore && infiniteHeight && this.infinite) {
 | 
					                if (this.canLoadMore && infiniteHeight && this.infinite) {
 | 
				
			||||||
                    // The height of the infinite is different while spinner is shown. Add that difference.
 | 
					                    // The height of the infinite is different while spinner is shown. Add that difference.
 | 
				
			||||||
                    infiniteHeight = infiniteHeight - height;
 | 
					                    infiniteHeight = infiniteHeight - height;
 | 
				
			||||||
@ -1082,10 +1086,8 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Keep scroll position after loading previous messages.
 | 
					     * 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 = 0): void {
 | 
					    protected keepScroll(oldScrollHeight: number, oldScrollBottom: number, infiniteHeight: number, retries = 0): void {
 | 
				
			||||||
 | 
					 | 
				
			||||||
        setTimeout(() => {
 | 
					        setTimeout(() => {
 | 
				
			||||||
            const newScrollHeight = (this.scrollElement?.scrollHeight || 0);
 | 
					            const newScrollHeight = (this.scrollElement?.scrollHeight || 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1098,37 +1100,16 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const scrollTo = newScrollHeight - oldScrollBottom + infiniteHeight;
 | 
					            // Scroll has changed, but maybe it hasn't reached the full height yet.
 | 
				
			||||||
 | 
					            setTimeout(() => {
 | 
				
			||||||
 | 
					                const newScrollHeight = (this.scrollElement?.scrollHeight || 0);
 | 
				
			||||||
 | 
					                const scrollTo = newScrollHeight - oldScrollBottom + infiniteHeight;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.content!.scrollToPoint(0, scrollTo, 0);
 | 
					                this.content!.scrollToPoint(0, scrollTo, 0);
 | 
				
			||||||
 | 
					            }, 30);
 | 
				
			||||||
        }, 30);
 | 
					        }, 30);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Content or scroll has been resized. For content, only call it if it's been added on top.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    resizeContent(): void {
 | 
					 | 
				
			||||||
        /* @todo probably not needed.
 | 
					 | 
				
			||||||
        let top = this.content!.getContentDimensions().scrollTop;
 | 
					 | 
				
			||||||
        // @todo this.content.resize();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Wait for new content height to be calculated.
 | 
					 | 
				
			||||||
        setTimeout(() => {
 | 
					 | 
				
			||||||
            // Visible content size changed, maintain the bottom position.
 | 
					 | 
				
			||||||
            if (!this.viewDestroyed && (this.scrollElement?.clientHeight || 0) != this.oldContentHeight) {
 | 
					 | 
				
			||||||
                if (!top) {
 | 
					 | 
				
			||||||
                    top = this.content!.getContentDimensions().scrollTop;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                top += this.oldContentHeight - (this.scrollElement?.clientHeight || 0);
 | 
					 | 
				
			||||||
                this.oldContentHeight = (this.scrollElement?.clientHeight || 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                this.content!.scrollToPoint(0, top, 0);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
        */
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Scroll bottom when render has finished.
 | 
					     * Scroll bottom when render has finished.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
@ -1160,7 +1141,7 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    scrollToFirstUnreadMessage(): void {
 | 
					    scrollToFirstUnreadMessage(): void {
 | 
				
			||||||
        if (this.newMessages > 0) {
 | 
					        if (this.newMessages > 0) {
 | 
				
			||||||
            const messages = Array.from(document.querySelectorAll('.addon-message-not-mine'));
 | 
					            const messages = Array.from(this.hostElement.querySelectorAll('.addon-message-not-mine'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            CoreDomUtils.scrollToElement(this.content!, <HTMLElement> messages[messages.length - this.newMessages]);
 | 
					            CoreDomUtils.scrollToElement(this.content!, <HTMLElement> messages[messages.length - this.newMessages]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -13,7 +13,7 @@
 | 
				
			|||||||
// limitations under the License.
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Component, Input, Output, EventEmitter, OnChanges, SimpleChange, ViewChild, ElementRef } from '@angular/core';
 | 
					import { Component, Input, Output, EventEmitter, OnChanges, SimpleChange, ViewChild, ElementRef } from '@angular/core';
 | 
				
			||||||
import { IonContent, IonInfiniteScroll } from '@ionic/angular';
 | 
					import { IonInfiniteScroll } from '@ionic/angular';
 | 
				
			||||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
					import { CoreDomUtils } from '@services/utils/dom';
 | 
				
			||||||
import { CoreUtils } from '@services/utils/utils';
 | 
					import { CoreUtils } from '@services/utils/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -37,15 +37,16 @@ export class CoreInfiniteLoadingComponent implements OnChanges {
 | 
				
			|||||||
    @Output() action: EventEmitter<() => void>; // Will emit an event when triggered.
 | 
					    @Output() action: EventEmitter<() => void>; // Will emit an event when triggered.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @ViewChild('topbutton') topButton?: ElementRef;
 | 
					    @ViewChild('topbutton') topButton?: ElementRef;
 | 
				
			||||||
    @ViewChild('infinitescroll') infiniteEl?: ElementRef;
 | 
					 | 
				
			||||||
    @ViewChild('bottombutton') bottomButton?: ElementRef;
 | 
					    @ViewChild('bottombutton') bottomButton?: ElementRef;
 | 
				
			||||||
    @ViewChild('spinnercontainer') spinnerContainer?: ElementRef;
 | 
					    @ViewChild('spinnercontainer') spinnerContainer?: ElementRef;
 | 
				
			||||||
    @ViewChild(IonInfiniteScroll) infiniteScroll?: IonInfiniteScroll;
 | 
					    @ViewChild(IonInfiniteScroll) infiniteScroll?: IonInfiniteScroll;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    loadingMore = false; // Hide button and avoid loading more.
 | 
					    loadingMore = false; // Hide button and avoid loading more.
 | 
				
			||||||
 | 
					    hostElement: HTMLElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(protected element: ElementRef) {
 | 
					    constructor(protected element: ElementRef<HTMLElement>) {
 | 
				
			||||||
        this.action = new EventEmitter();
 | 
					        this.action = new EventEmitter();
 | 
				
			||||||
 | 
					        this.hostElement = element.nativeElement;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@ -76,14 +77,14 @@ export class CoreInfiniteLoadingComponent implements OnChanges {
 | 
				
			|||||||
        await CoreUtils.nextTick();
 | 
					        await CoreUtils.nextTick();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Calculate distance from edge.
 | 
					        // Calculate distance from edge.
 | 
				
			||||||
        const content = this.element.nativeElement.closest('ion-content') as IonContent;
 | 
					        const content = this.hostElement.closest('ion-content');
 | 
				
			||||||
        if (!content) {
 | 
					        if (!content) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const scrollElement = await content.getScrollElement();
 | 
					        const scrollElement = await content.getScrollElement();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const infiniteHeight = this.element.nativeElement.getBoundingClientRect().height;
 | 
					        const infiniteHeight = this.hostElement.getBoundingClientRect().height;
 | 
				
			||||||
        const scrollTop = scrollElement.scrollTop;
 | 
					        const scrollTop = scrollElement.scrollTop;
 | 
				
			||||||
        const height = scrollElement.offsetHeight;
 | 
					        const height = scrollElement.offsetHeight;
 | 
				
			||||||
        const threshold = height * THRESHOLD;
 | 
					        const threshold = height * THRESHOLD;
 | 
				
			||||||
@ -141,11 +142,20 @@ export class CoreInfiniteLoadingComponent implements OnChanges {
 | 
				
			|||||||
     * @deprecated since 3.9.5
 | 
					     * @deprecated since 3.9.5
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    getHeight(): number {
 | 
					    getHeight(): number {
 | 
				
			||||||
        // return this.element.nativeElement.getBoundingClientRect().height;
 | 
					        return (this.position == 'top' ?
 | 
				
			||||||
 | 
					            this.getElementHeight(this.topButton?.nativeElement) :
 | 
				
			||||||
 | 
					            this.getElementHeight(this.bottomButton?.nativeElement)) +
 | 
				
			||||||
 | 
					            this.getElementHeight(this.infiniteScrollElement) +
 | 
				
			||||||
 | 
					            this.getElementHeight(this.spinnerContainer?.nativeElement);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return (this.position == 'top' ? this.getElementHeight(this.topButton): this.getElementHeight(this.bottomButton)) +
 | 
					    /**
 | 
				
			||||||
            this.getElementHeight(this.infiniteEl) +
 | 
					     * Get the infinite scroll element.
 | 
				
			||||||
            this.getElementHeight(this.spinnerContainer);
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Element or null.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    get infiniteScrollElement(): HTMLIonInfiniteScrollElement | null {
 | 
				
			||||||
 | 
					        return this.hostElement.querySelector('ion-infinite-scroll');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@ -154,9 +164,9 @@ export class CoreInfiniteLoadingComponent implements OnChanges {
 | 
				
			|||||||
     * @param element Element ref.
 | 
					     * @param element Element ref.
 | 
				
			||||||
     * @return Height.
 | 
					     * @return Height.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected getElementHeight(element?: ElementRef): number {
 | 
					    protected getElementHeight(element?: HTMLElement | null): number {
 | 
				
			||||||
        if (element && element.nativeElement) {
 | 
					        if (element) {
 | 
				
			||||||
            return CoreDomUtils.getElementHeight(element.nativeElement, true, true, true);
 | 
					            return CoreDomUtils.getElementHeight(element, true, true, true);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return 0;
 | 
					        return 0;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user