From 00c2332785df750ae8184bb2781d01de17bc083f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 13 Mar 2025 15:33:03 +0100 Subject: [PATCH 1/2] MOBILE-4584 H5P: Autoplay embedded H5P when downloaded or outdated --- .../services/handlers/displayh5p.ts | 21 ++++---- src/core/features/h5p/classes/player.ts | 10 ++-- .../h5p/components/h5p-iframe/h5p-iframe.ts | 2 +- .../h5p-player/core-h5p-player.html | 25 +++++---- .../h5p/components/h5p-player/h5p-player.ts | 52 +++++++++++++------ src/theme/components/ion-button.scss | 8 +++ 6 files changed, 75 insertions(+), 43 deletions(-) diff --git a/src/addons/filter/displayh5p/services/handlers/displayh5p.ts b/src/addons/filter/displayh5p/services/handlers/displayh5p.ts index f3a88c56c87..9154b1e075c 100644 --- a/src/addons/filter/displayh5p/services/handlers/displayh5p.ts +++ b/src/addons/filter/displayh5p/services/handlers/displayh5p.ts @@ -37,7 +37,7 @@ export class AddonFilterDisplayH5PHandlerService extends CoreFilterDefaultHandle */ filter( text: string, - ): string | Promise { + ): string { return CoreText.processHTML(text, (element) => { const h5pIframes = Array.from(element.querySelectorAll('iframe.h5p-iframe')); @@ -83,25 +83,28 @@ export class AddonFilterDisplayH5PHandlerService extends CoreFilterDefaultHandle viewContainerRef: ViewContainerRef, component?: string, componentId?: string | number, - ): void | Promise { + ): void { const placeholders = Array.from(container.querySelectorAll('div.core-h5p-tmp-placeholder')); placeholders.forEach((placeholder) => { - const url = placeholder.getAttribute('data-player-src') || ''; + if (!placeholder.parentElement) { + return; + } // Create the component to display the player. - const componentRef = viewContainerRef.createComponent(CoreH5PPlayerComponent); + const h5pInstance = viewContainerRef.createComponent(CoreH5PPlayerComponent).instance; - componentRef.instance.src = url; - componentRef.instance.component = component; - componentRef.instance.componentId = componentId; + const url = placeholder.getAttribute('data-player-src') || ''; + h5pInstance.src = url; + h5pInstance.component = component; + h5pInstance.componentId = componentId; // Check if auto-play was enabled when inserting the iframe using the TinyMCE editor. - componentRef.instance.autoPlay = CoreUtils.isTrueOrOne(placeholder.parentElement?.dataset.mobileappAutoplay); + h5pInstance.autoPlay = CoreUtils.isTrueOrOne(placeholder.parentElement.dataset.mobileappAutoplay); // Move the component to its right position. - placeholder.parentElement?.replaceChild(componentRef.instance.elementRef.nativeElement, placeholder); + placeholder.parentElement.replaceChild(h5pInstance.getElement(), placeholder); }); } diff --git a/src/core/features/h5p/classes/player.ts b/src/core/features/h5p/classes/player.ts index f3e36ea7bd0..8bc17d03492 100644 --- a/src/core/features/h5p/classes/player.ts +++ b/src/core/features/h5p/classes/player.ts @@ -139,10 +139,10 @@ export class CoreH5PPlayer { html += ''; }); - html += '
' + - '' + - '
'; + html += `
+
`; const fileEntry = await CoreFile.writeFile(indexPath, html); @@ -370,7 +370,7 @@ export class CoreH5PPlayer { } /** - * Get the encoded URL for embeding an H5P content. + * Get the encoded URL for embedding an H5P content. * * @param siteUrl The site URL. * @param h5pUrl The URL of the .h5p file. diff --git a/src/core/features/h5p/components/h5p-iframe/h5p-iframe.ts b/src/core/features/h5p/components/h5p-iframe/h5p-iframe.ts index 94b241c997c..2ced9bf4bd4 100644 --- a/src/core/features/h5p/components/h5p-iframe/h5p-iframe.ts +++ b/src/core/features/h5p/components/h5p-iframe/h5p-iframe.ts @@ -93,7 +93,7 @@ export class CoreH5PIframeComponent implements OnChanges, OnDestroy { } /** - * Detect changes on input properties. + * @inheritdoc */ ngOnChanges(changes: {[name: string]: SimpleChange}): void { // If it's already playing don't change it. diff --git a/src/core/features/h5p/components/h5p-player/core-h5p-player.html b/src/core/features/h5p/components/h5p-player/core-h5p-player.html index e30f0435e12..99373e140ec 100644 --- a/src/core/features/h5p/components/h5p-player/core-h5p-player.html +++ b/src/core/features/h5p/components/h5p-player/core-h5p-player.html @@ -1,13 +1,16 @@ -
- - +@if (showPackage) { + +} @else if (urlParams) { +
+ + -
- +
+ +
-
- - +} diff --git a/src/core/features/h5p/components/h5p-player/h5p-player.ts b/src/core/features/h5p/components/h5p-player/h5p-player.ts index b094f46a3c5..c1ab9dae5d6 100644 --- a/src/core/features/h5p/components/h5p-player/h5p-player.ts +++ b/src/core/features/h5p/components/h5p-player/h5p-player.ts @@ -65,10 +65,12 @@ export class CoreH5PPlayerComponent implements OnInit, OnChanges, OnDestroy { protected siteCanDownload: boolean; protected observer?: CoreEventObserver; protected logger: CoreLogger; + protected nativeElement: HTMLElement; constructor( - public elementRef: ElementRef, + elementRef: ElementRef, ) { + this.nativeElement = elementRef.nativeElement; this.logger = CoreLogger.getInstance('CoreH5PPlayerComponent'); this.site = CoreSites.getRequiredCurrentSite(); @@ -80,23 +82,34 @@ export class CoreH5PPlayerComponent implements OnInit, OnChanges, OnDestroy { * @inheritdoc */ ngOnInit(): void { - this.checkCanDownload(); - if (this.autoPlay) { - this.play(); - } + this.playIfRequested(); } /** - * Detect changes on input properties. + * @inheritdoc */ ngOnChanges(changes: {[name: string]: SimpleChange}): void { // If it's already playing there's no need to check if it can be downloaded or auto-played. if (changes.src && !this.showPackage) { - this.checkCanDownload(); - if (this.autoPlay) { + this.playIfRequested(); + } + } + + protected async playIfRequested(): Promise { + + if (!this.autoPlay) { + await this.checkCanDownload(); + + if (this.canDownload$.getValue() && + (this.state === DownloadStatus.OUTDATED || this.state === DownloadStatus.DOWNLOADED)) { + // It will be played if it's downloaded. this.play(); } + + return; } + + this.play(); } /** @@ -166,8 +179,6 @@ export class CoreH5PPlayerComponent implements OnInit, OnChanges, OnDestroy { /** * Download the H5P in background if the size is low. - * - * @returns Promise resolved when done. */ protected async attemptDownloadInBg(): Promise { if (!this.urlParams || !this.src || !this.siteCanDownload || !CoreH5P.canGetTrustedH5PFileInSite() || @@ -192,15 +203,13 @@ export class CoreH5PPlayerComponent implements OnInit, OnChanges, OnDestroy { /** * Check if the package can be downloaded. - * - * @returns Promise resolved when done. */ protected async checkCanDownload(): Promise { - this.observer && this.observer.off(); + this.observer?.off(); this.urlParams = CoreUrl.extractUrlParams(this.src || ''); if (this.src && this.siteCanDownload && CoreH5P.canGetTrustedH5PFileInSite() && this.site.containsUrl(this.src)) { - this.calculateState(); + await this.calculateState(); // Listen for changes in the state. try { @@ -209,7 +218,7 @@ export class CoreH5PPlayerComponent implements OnInit, OnChanges, OnDestroy { this.observer = CoreEvents.on(eventName, () => { this.calculateState(); }); - } catch (error) { + } catch { // An error probably means the file cannot be downloaded or we cannot check it (offline). } @@ -234,7 +243,7 @@ export class CoreH5PPlayerComponent implements OnInit, OnChanges, OnDestroy { this.canDownload$.next(true); this.state = state; - } catch (error) { + } catch { this.canDownload$.next(false); } finally { this.calculating$.next(false); @@ -242,7 +251,16 @@ export class CoreH5PPlayerComponent implements OnInit, OnChanges, OnDestroy { } /** - * Component destroyed. + * Get the native element. + * + * @returns The native element. + */ + getElement(): HTMLElement { + return this.nativeElement; + } + + /** + * @inheritdoc */ ngOnDestroy(): void { this.observer?.off(); diff --git a/src/theme/components/ion-button.scss b/src/theme/components/ion-button.scss index 4cb9af093c0..41d0bb81656 100644 --- a/src/theme/components/ion-button.scss +++ b/src/theme/components/ion-button.scss @@ -114,6 +114,14 @@ ion-button { outline: var(--a11y-shadow-focus-outline); z-index: 1; } + + &.enlarge-on-hover::part(native) { + transition: transform 0.2s ease-in-out; + will-change: transform; + &:hover { + transform: scale(1.20); + } + } } ion-button, From 020d73d6260ba7c9b573aa6c8b457743e90d4c46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 13 Mar 2025 15:33:26 +0100 Subject: [PATCH 2/2] MOBILE-4786 core: Lazy load iframes --- src/core/components/iframe/core-iframe.html | 4 ++-- src/core/features/h5p/classes/player.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/components/iframe/core-iframe.html b/src/core/components/iframe/core-iframe.html index f111bc588ab..e84471852be 100644 --- a/src/core/components/iframe/core-iframe.html +++ b/src/core/components/iframe/core-iframe.html @@ -18,11 +18,11 @@ @if (allowFullscreen) { } @else { } diff --git a/src/core/features/h5p/classes/player.ts b/src/core/features/h5p/classes/player.ts index 8bc17d03492..50c34a50a27 100644 --- a/src/core/features/h5p/classes/player.ts +++ b/src/core/features/h5p/classes/player.ts @@ -141,7 +141,7 @@ export class CoreH5PPlayer { html += `
`; const fileEntry = await CoreFile.writeFile(indexPath, html); @@ -366,7 +366,7 @@ export class CoreH5PPlayer { return ''; } - return ``; + return ``; } /**