From 0674a4ef66fa6b5995d0766966ed932c3ffa8db7 Mon Sep 17 00:00:00 2001 From: Josep Boix Requesens Date: Thu, 17 Oct 2024 14:18:34 +0200 Subject: [PATCH] feat: bidirectional looping when repeat all is enabled Resolves #32 by adding logic to loop to the last element when navigating backwards from the first item in the playlist while Repeat All is enabled. --- docs/README.md | 3 +- .../src/pillarbox-playlist.js | 21 ++++-- .../test/pillarbox-playlist.spec.js | 68 +++++++++++++++++++ 3 files changed, 84 insertions(+), 8 deletions(-) diff --git a/docs/README.md b/docs/README.md index 6489735..55618c0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -71,8 +71,7 @@ and testing the element. ## Contributing Contributions are welcome! If you'd like to contribute, please follow the project's code style and -linting rules. Here -are some commands to help you get started: +linting rules. Here are some commands to help you get started: Check your JavaScript code: diff --git a/packages/pillarbox-playlist/src/pillarbox-playlist.js b/packages/pillarbox-playlist/src/pillarbox-playlist.js index b1eb63d..f49e522 100644 --- a/packages/pillarbox-playlist/src/pillarbox-playlist.js +++ b/packages/pillarbox-playlist/src/pillarbox-playlist.js @@ -291,7 +291,6 @@ export class PillarboxPlaylist extends Plugin { this.updateState_(); } - /** * Get the currently playing index. * @@ -350,9 +349,9 @@ export class PillarboxPlaylist extends Plugin { } /** - * Advances to the next item in the playlist. If repeat mode is enabled, then - * once the last item of the playlist is reached this function will play - * the first one. + * Advances to the next item in the playlist. If {@link RepeatMode#REPEAT_ALL} + * mode is enabled, then once the last item of the playlist is reached this + * function will play the first one. */ next() { if (this.hasNext()) { @@ -382,6 +381,8 @@ export class PillarboxPlaylist extends Plugin { * - If the media is live, navigates to the previous item regardless of the threshold. * - If playback is beyond the threshold, restarts the current media. * - If playback is within the threshold, navigates to the previous item. + * - If {@link RepeatMode#REPEAT_ALL} mode is enabled, then once the first + * item of the playlist is reached this function will play the last one. * * @see previousNavigationThreshold */ @@ -393,7 +394,15 @@ export class PillarboxPlaylist extends Plugin { return; } - this.select(this.currentIndex_ - 1); + if (this.hasPrevious()) { + this.select(this.currentIndex_ - 1); + + return; + } + + if (this.repeat === RepeatMode.REPEAT_ALL) { + this.select(this.items_.length - 1); + } } isLive() { @@ -463,7 +472,7 @@ export class PillarboxPlaylist extends Plugin { updateState_() { this.setState({ // Converts the items array to a JSON string before setting it in the state. - // Otherwise the change is not detected. + // Otherwise, the change is not detected. items: JSON.stringify(this.items_), currentIndex: this.currentIndex_ }); diff --git a/packages/pillarbox-playlist/test/pillarbox-playlist.spec.js b/packages/pillarbox-playlist/test/pillarbox-playlist.spec.js index 2c382fc..487a2e1 100644 --- a/packages/pillarbox-playlist/test/pillarbox-playlist.spec.js +++ b/packages/pillarbox-playlist/test/pillarbox-playlist.spec.js @@ -204,6 +204,29 @@ describe('PillarboxPlaylist', () => { expect(srcSpy).toHaveBeenLastCalledWith(playlist[3].sources); expect(posterSpy).toHaveBeenLastCalledWith(playlist[3].poster); }); + + it('should play first element if the current index is the last of the playlist and repeat mode all is enabled', () => { + // Given + const srcSpy = vi.spyOn(player, 'src').mockImplementation(() => { + }); + const posterSpy = vi.spyOn(player, 'poster').mockImplementation(() => { + }); + + // When + pillarboxPlaylist.toggleRepeat(RepeatMode.REPEAT_ALL); + pillarboxPlaylist.load(playlist); + pillarboxPlaylist.select(3); + pillarboxPlaylist.next(); + + // Then + expect(pillarboxPlaylist.hasPrevious()).toBeFalsy(); + expect(pillarboxPlaylist.hasNext()).toBeTruthy(); + expect(pillarboxPlaylist.items.length).toBe(4); + expect(pillarboxPlaylist.currentIndex).toBe(0); + expect(pillarboxPlaylist.currentItem).toBe(playlist[0]); + expect(srcSpy).toHaveBeenLastCalledWith(playlist[0].sources); + expect(posterSpy).toHaveBeenLastCalledWith(playlist[0].poster); + }); }); describe('previous', () => { @@ -246,6 +269,51 @@ describe('PillarboxPlaylist', () => { expect(pillarboxPlaylist.currentItem).toBe(playlist[2]); expect(currentTime).toHaveBeenLastCalledWith(0); }); + + it('should not play previous if the current index is the last of the playlist', () => { + // Given + const srcSpy = vi.spyOn(player, 'src').mockImplementation(() => { + }); + const posterSpy = vi.spyOn(player, 'poster').mockImplementation(() => { + }); + + // When + pillarboxPlaylist.load(playlist); + pillarboxPlaylist.select(0); + pillarboxPlaylist.previous(); + + // Then + expect(pillarboxPlaylist.hasPrevious()).toBeFalsy(); + expect(pillarboxPlaylist.hasNext()).toBeTruthy(); + expect(pillarboxPlaylist.items.length).toBe(4); + expect(pillarboxPlaylist.currentIndex).toBe(0); + expect(pillarboxPlaylist.currentItem).toBe(playlist[0]); + expect(srcSpy).toHaveBeenLastCalledWith(playlist[0].sources); + expect(posterSpy).toHaveBeenLastCalledWith(playlist[0].poster); + }); + + it('should play last element if the current index is the first of the playlist and repeat mode all is enabled', () => { + // Given + const srcSpy = vi.spyOn(player, 'src').mockImplementation(() => { + }); + const posterSpy = vi.spyOn(player, 'poster').mockImplementation(() => { + }); + + // When + pillarboxPlaylist.toggleRepeat(RepeatMode.REPEAT_ALL); + pillarboxPlaylist.load(playlist); + pillarboxPlaylist.select(0); + pillarboxPlaylist.previous(); + + // Then + expect(pillarboxPlaylist.hasPrevious()).toBeTruthy(); + expect(pillarboxPlaylist.hasNext()).toBeFalsy(); + expect(pillarboxPlaylist.items.length).toBe(4); + expect(pillarboxPlaylist.currentIndex).toBe(3); + expect(pillarboxPlaylist.currentItem).toBe(playlist[3]); + expect(srcSpy).toHaveBeenLastCalledWith(playlist[3].sources); + expect(posterSpy).toHaveBeenLastCalledWith(playlist[3].poster); + }); }); describe('autoadvance', () => {