MOBILE-3947 swipe: Fix Swipe slides update component

main
Pau Ferrer Ocaña 2024-01-09 08:54:59 +01:00
parent 210b3a75a3
commit 522d1e2c79
2 changed files with 73 additions and 38 deletions

View File

@ -142,7 +142,7 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro
} }
/** /**
* Component loaded. * @inheritdoc
*/ */
ngOnInit(): void { ngOnInit(): void {
this.canNavigate = typeof this.canNavigate == 'undefined' ? true : CoreUtils.isTrueOrOne(this.canNavigate); this.canNavigate = typeof this.canNavigate == 'undefined' ? true : CoreUtils.isTrueOrOne(this.canNavigate);
@ -164,7 +164,7 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro
} }
/** /**
* Detect and act upon changes that Angular cant or wont detect on its own (objects and arrays). * @inheritdoc
*/ */
ngDoCheck(): void { ngDoCheck(): void {
const items = this.manager?.getSource().getItems(); const items = this.manager?.getSource().getItems();
@ -368,7 +368,7 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro
} }
/** /**
* Component destroyed. * @inheritdoc
*/ */
ngOnDestroy(): void { ngOnDestroy(): void {
this.undeleteEventObserver?.off(); this.undeleteEventObserver?.off();

View File

@ -15,7 +15,9 @@
import { import {
Component, ContentChild, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, Output, SimpleChange, TemplateRef, ViewChild, Component, ContentChild, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, Output, SimpleChange, TemplateRef, ViewChild,
} from '@angular/core'; } from '@angular/core';
import { AsyncDirective } from '@classes/async-directive';
import { CoreSwipeSlidesItemsManager } from '@classes/items-management/swipe-slides-items-manager'; import { CoreSwipeSlidesItemsManager } from '@classes/items-management/swipe-slides-items-manager';
import { CorePromisedValue } from '@classes/promised-value';
import { IonContent } from '@ionic/angular'; import { IonContent } from '@ionic/angular';
import { CoreDomUtils, VerticalPoint } from '@services/utils/dom'; import { CoreDomUtils, VerticalPoint } from '@services/utils/dom';
import { CoreUtils } from '@services/utils/utils'; import { CoreUtils } from '@services/utils/utils';
@ -32,7 +34,7 @@ import { SwiperOptions } from 'swiper/types';
templateUrl: 'swipe-slides.html', templateUrl: 'swipe-slides.html',
styleUrls: ['swipe-slides.scss'], styleUrls: ['swipe-slides.scss'],
}) })
export class CoreSwipeSlidesComponent<Item = unknown> implements OnChanges, OnDestroy { export class CoreSwipeSlidesComponent<Item = unknown> implements OnChanges, OnDestroy, AsyncDirective {
@Input() manager?: CoreSwipeSlidesItemsManager<Item>; @Input() manager?: CoreSwipeSlidesItemsManager<Item>;
@Input() options: CoreSwipeSlidesOptions = {}; @Input() options: CoreSwipeSlidesOptions = {};
@ -46,22 +48,21 @@ export class CoreSwipeSlidesComponent<Item = unknown> implements OnChanges, OnDe
* This setTimeout waits for Ionic's async initialization to complete. * This setTimeout waits for Ionic's async initialization to complete.
* Otherwise, an outdated swiper reference will be used. * Otherwise, an outdated swiper reference will be used.
*/ */
setTimeout(() => { setTimeout(async () => {
if (swiperRef?.nativeElement?.swiper) { if (swiperRef?.nativeElement?.swiper) {
this.swiper = swiperRef.nativeElement.swiper as Swiper; this.swiper = swiperRef.nativeElement.swiper as Swiper;
await this.initialize();
if (this.options.initialSlide) { if (this.options.initialSlide) {
this.swiper.slideTo(this.options.initialSlide, 0, this.options.runCallbacksOnInit); this.swiper.slideTo(this.options.initialSlide, 0, this.options.runCallbacksOnInit);
} }
this.updateOptions();
this.swiper.on('slideChangeTransitionStart', () => this.slideWillChange()); this.swiper.on('slideChangeTransitionStart', () => this.slideWillChange());
this.swiper.on('slideChangeTransitionEnd', () => this.slideDidChange()); this.swiper.on('slideChangeTransitionEnd', () => this.slideDidChange());
Object.keys(this.options).forEach((key) => {
if (this.swiper) {
this.swiper.params[key] = this.options[key];
}
});
} }
}, 0); }, 0);
} }
@ -72,6 +73,7 @@ export class CoreSwipeSlidesComponent<Item = unknown> implements OnChanges, OnDe
protected unsubscribe?: () => void; protected unsubscribe?: () => void;
protected resizeListener: CoreEventObserver; protected resizeListener: CoreEventObserver;
protected activeSlideIndexes: number[] = []; protected activeSlideIndexes: number[] = [];
protected onReadyPromise = new CorePromisedValue<void>();
constructor( constructor(
elementRef: ElementRef<HTMLElement>, elementRef: ElementRef<HTMLElement>,
@ -87,17 +89,11 @@ export class CoreSwipeSlidesComponent<Item = unknown> implements OnChanges, OnDe
/** /**
* @inheritdoc * @inheritdoc
*/ */
ngOnChanges(changes: { [name: string]: SimpleChange }): void { async ngOnChanges(changes: { [name: string]: SimpleChange }): Promise<void> {
if (!this.unsubscribe && this.manager) { await this.initialize();
this.initialize(this.manager);
}
if (changes.options) { if (changes.options) {
Object.keys(this.options).forEach((key) => { this.updateOptions();
if (this.swiper) {
this.swiper.params[key] = this.options[key];
}
});
} }
} }
@ -122,8 +118,12 @@ export class CoreSwipeSlidesComponent<Item = unknown> implements OnChanges, OnDe
/** /**
* Initialize some properties based on the manager. * Initialize some properties based on the manager.
*/ */
protected async initialize(manager: CoreSwipeSlidesItemsManager<Item>): Promise<void> { protected async initialize(): Promise<void> {
this.unsubscribe = manager.getSource().addListener({ if (this.unsubscribe || !this.swiper || !this.manager) {
return;
}
this.unsubscribe = this.manager.getSource().addListener({
onItemsUpdated: () => this.onItemsUpdated(), onItemsUpdated: () => this.onItemsUpdated(),
}); });
@ -131,16 +131,16 @@ export class CoreSwipeSlidesComponent<Item = unknown> implements OnChanges, OnDe
// This is because default callbacks aren't triggered for index 0, and to prevent auto scroll on init. // This is because default callbacks aren't triggered for index 0, and to prevent auto scroll on init.
this.options.runCallbacksOnInit = false; this.options.runCallbacksOnInit = false;
await manager.getSource().waitForLoaded(); await this.manager.getSource().waitForLoaded();
if (this.options.initialSlide === undefined) { if (this.options.initialSlide === undefined) {
// Calculate the initial slide. // Calculate the initial slide.
const index = manager.getSource().getInitialItemIndex(); const index = this.manager.getSource().getInitialItemIndex();
this.options.initialSlide = Math.max(index, 0); this.options.initialSlide = Math.max(index, 0);
} }
// Emit change events with the initial item. // Emit change events with the initial item.
const items = manager.getSource().getItems(); const items = this.manager.getSource().getItems();
if (!items || !items.length) { if (!items || !items.length) {
return; return;
} }
@ -155,9 +155,11 @@ export class CoreSwipeSlidesComponent<Item = unknown> implements OnChanges, OnDe
this.activeSlideIndexes = [initialIndex]; this.activeSlideIndexes = [initialIndex];
manager.setSelectedItem(items[initialIndex]); this.manager.setSelectedItem(items[initialIndex]);
this.onWillChange.emit(initialItemData); this.onWillChange.emit(initialItemData);
this.onDidChange.emit(initialItemData); this.onDidChange.emit(initialItemData);
this.onReadyPromise.resolve();
} }
/** /**
@ -167,19 +169,20 @@ export class CoreSwipeSlidesComponent<Item = unknown> implements OnChanges, OnDe
* @param speed Animation speed. * @param speed Animation speed.
* @param runCallbacks Whether to run callbacks. * @param runCallbacks Whether to run callbacks.
*/ */
slideToIndex(index: number, speed?: number, runCallbacks?: boolean): void { async slideToIndex(index: number, speed?: number, runCallbacks?: boolean): Promise<void> {
// If slides are being updated, wait for the update to finish. // If slides are being updated, wait for the update to finish.
if (!this.swiper) { await this.ready();
return;
}
// Verify that the number of slides matches the number of items. // Verify that the number of slides matches the number of items.
const slidesLength = this.swiper.slides.length; const slidesLength = this.swiper?.slides?.length || 0;
if (slidesLength !== this.items.length) { if (slidesLength !== this.items.length) {
// Number doesn't match, do a new update to try to match them. // Number doesn't match, do a new update to try to match them.
this.updateSlidesComponent(); await this.updateSlidesComponent();
} }
if (!this.swiper?.slides) {
return;
}
this.swiper?.slideTo(index, speed, runCallbacks); this.swiper?.slideTo(index, speed, runCallbacks);
} }
@ -190,10 +193,10 @@ export class CoreSwipeSlidesComponent<Item = unknown> implements OnChanges, OnDe
* @param speed Animation speed. * @param speed Animation speed.
* @param runCallbacks Whether to run callbacks. * @param runCallbacks Whether to run callbacks.
*/ */
slideToItem(item: Item, speed?: number, runCallbacks?: boolean): void { async slideToItem(item: Item, speed?: number, runCallbacks?: boolean): Promise<void> {
const index = this.manager?.getSource().getItemIndex(item) ?? -1; const index = this.manager?.getSource().getItemIndex(item) ?? -1;
if (index != -1) { if (index != -1) {
this.slideToIndex(index, speed, runCallbacks); await this.slideToIndex(index, speed, runCallbacks);
} }
} }
@ -225,7 +228,7 @@ export class CoreSwipeSlidesComponent<Item = unknown> implements OnChanges, OnDe
await CoreUtils.nextTick(); await CoreUtils.nextTick();
// Update the slides component so the slides list reflects the new items. // Update the slides component so the slides list reflects the new items.
this.updateSlidesComponent(); await this.updateSlidesComponent();
const currentItem = this.manager?.getSelectedItem(); const currentItem = this.manager?.getSelectedItem();
@ -234,10 +237,26 @@ export class CoreSwipeSlidesComponent<Item = unknown> implements OnChanges, OnDe
} }
// Keep the same slide in case the list has changed. // Keep the same slide in case the list has changed.
const newIndex = this.manager.getSource().getItemIndex(currentItem) ?? -1; this.slideToItem(currentItem, 0, false);
if (newIndex != -1) {
this.swiper?.slideTo(newIndex, 0, false);
} }
/**
* Update Swiper params from options.
*/
protected updateOptions(): void {
if (!this.swiper) {
return;
}
if (this.swiper.params === undefined) {
this.swiper.params = {};
}
Object.keys(this.options).forEach((key) => {
if (this.swiper) {
this.swiper.params[key] = this.options[key];
}
});
} }
/** /**
@ -322,8 +341,24 @@ export class CoreSwipeSlidesComponent<Item = unknown> implements OnChanges, OnDe
/** /**
* Update slides component. * Update slides component.
*/ */
updateSlidesComponent(): void { async updateSlidesComponent(): Promise<void> {
await this.ready();
if (!this.swiper) {
return;
}
this.swiper?.update(); this.swiper?.update();
// We need to ensure the slides are updated before continuing.
await CoreUtils.nextTicks(2);
}
/**
* @inheritdoc
*/
async ready(): Promise<void> {
return this.onReadyPromise;
} }
/** /**