From 96afa88236719ca43f07aa0f7a8e2c41e4d83fa4 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Fri, 8 Nov 2024 10:04:10 +0100 Subject: [PATCH] fix(youtube-player): ready event not emitting Fixes that the `youtube-player`'s `ready` event wasn't emitting. The issue is that we create the outputs lazily based on a stream of newly-created players, however that stream emits after the `ready` event. Relates to #29874. --- src/youtube-player/youtube-player.spec.ts | 5 ----- src/youtube-player/youtube-player.ts | 17 +++++++++++++---- .../youtube-player/youtube-player.md | 5 ----- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/youtube-player/youtube-player.spec.ts b/src/youtube-player/youtube-player.spec.ts index 19a4792e8d94..9e37727eecb9 100644 --- a/src/youtube-player/youtube-player.spec.ts +++ b/src/youtube-player/youtube-player.spec.ts @@ -725,17 +725,12 @@ describe('YoutubePlayer', () => { const player = noEventsApp.componentInstance.player; const subscriptions: Subscription[] = []; - const readySpy = jasmine.createSpy('ready spy'); const stateChangeSpy = jasmine.createSpy('stateChange spy'); const playbackQualityChangeSpy = jasmine.createSpy('playbackQualityChange spy'); const playbackRateChangeSpy = jasmine.createSpy('playbackRateChange spy'); const errorSpy = jasmine.createSpy('error spy'); const apiChangeSpy = jasmine.createSpy('apiChange spy'); - subscriptions.push(player.ready.subscribe(readySpy)); - events.onReady({target: playerSpy}); - expect(readySpy).toHaveBeenCalledWith({target: playerSpy}); - subscriptions.push(player.stateChange.subscribe(stateChangeSpy)); events.onStateChange({target: playerSpy, data: 5}); expect(stateChangeSpy).toHaveBeenCalledWith({target: playerSpy, data: 5}); diff --git a/src/youtube-player/youtube-player.ts b/src/youtube-player/youtube-player.ts index 344a84f11051..0d3bd9febf93 100644 --- a/src/youtube-player/youtube-player.ts +++ b/src/youtube-player/youtube-player.ts @@ -29,6 +29,7 @@ import { CSP_NONCE, ChangeDetectorRef, AfterViewInit, + EventEmitter, } from '@angular/core'; import {isPlatformBrowser} from '@angular/common'; import {Observable, of as observableOf, Subject, BehaviorSubject, fromEventPattern} from 'rxjs'; @@ -218,22 +219,29 @@ export class YouTubePlayer implements AfterViewInit, OnChanges, OnDestroy { */ @Input() placeholderImageQuality: PlaceholderImageQuality; - /** Outputs are direct proxies from the player itself. */ - @Output() readonly ready: Observable = - this._getLazyEmitter('onReady'); + // Note: ready event can't go through the lazy emitter, because it + // happens before the `_playerChanges` stream emits the new player. + /** Emits when the player is initialized. */ + @Output() readonly ready: Observable = new EventEmitter(); + + /** Emits when the state of the player has changed. */ @Output() readonly stateChange: Observable = this._getLazyEmitter('onStateChange'); + /** Emits when there's an error while initializing the player. */ @Output() readonly error: Observable = this._getLazyEmitter('onError'); + /** Emits when the underlying API of the player has changed. */ @Output() readonly apiChange: Observable = this._getLazyEmitter('onApiChange'); + /** Emits when the playback quality has changed. */ @Output() readonly playbackQualityChange: Observable = this._getLazyEmitter('onPlaybackQualityChange'); + /** Emits when the playback rate has changed. */ @Output() readonly playbackRateChange: Observable = this._getLazyEmitter('onPlaybackRateChange'); @@ -575,7 +583,7 @@ export class YouTubePlayer implements AfterViewInit, OnChanges, OnDestroy { }), ); - const whenReady = () => { + const whenReady = (event: YT.PlayerEvent) => { // Only assign the player once it's ready, otherwise YouTube doesn't expose some APIs. this._ngZone.run(() => { this._isLoading = false; @@ -584,6 +592,7 @@ export class YouTubePlayer implements AfterViewInit, OnChanges, OnDestroy { this._pendingPlayer = undefined; player.removeEventListener('onReady', whenReady); this._playerChanges.next(player); + (this.ready as EventEmitter).emit(event); this._setSize(); this._setQuality(); diff --git a/tools/public_api_guard/youtube-player/youtube-player.md b/tools/public_api_guard/youtube-player/youtube-player.md index fb8690f358f4..236abe369746 100644 --- a/tools/public_api_guard/youtube-player/youtube-player.md +++ b/tools/public_api_guard/youtube-player/youtube-player.md @@ -24,12 +24,10 @@ export const YOUTUBE_PLAYER_CONFIG: InjectionToken; // @public export class YouTubePlayer implements AfterViewInit, OnChanges, OnDestroy { constructor(...args: unknown[]); - // (undocumented) readonly apiChange: Observable; disableCookies: boolean; disablePlaceholder: boolean; endSeconds: number | undefined; - // (undocumented) readonly error: Observable; getAvailablePlaybackRates(): number[]; getAvailableQualityLevels(): YT.SuggestedVideoQuality[]; @@ -77,9 +75,7 @@ export class YouTubePlayer implements AfterViewInit, OnChanges, OnDestroy { pauseVideo(): void; placeholderButtonLabel: string; placeholderImageQuality: PlaceholderImageQuality; - // (undocumented) readonly playbackQualityChange: Observable; - // (undocumented) readonly playbackRateChange: Observable; playerVars: YT.PlayerVars | undefined; playVideo(): void; @@ -90,7 +86,6 @@ export class YouTubePlayer implements AfterViewInit, OnChanges, OnDestroy { protected _shouldShowPlaceholder(): boolean; showBeforeIframeApiLoads: boolean; startSeconds: number | undefined; - // (undocumented) readonly stateChange: Observable; stopVideo(): void; suggestedQuality: YT.SuggestedVideoQuality | undefined;