diff --git a/public/app/features/playlist/playlist_srv.ts b/public/app/features/playlist/playlist_srv.ts index cb2763129ef..97ebfff7320 100644 --- a/public/app/features/playlist/playlist_srv.ts +++ b/public/app/features/playlist/playlist_srv.ts @@ -7,6 +7,7 @@ import coreModule from '../../core/core_module'; import appEvents from 'app/core/app_events'; import locationUtil from 'app/core/utils/location_util'; import kbn from 'app/core/utils/kbn'; +import { store } from 'app/store/store'; export class PlaylistSrv { private cancelPromise: any; @@ -15,6 +16,8 @@ export class PlaylistSrv { private interval: number; private startUrl: string; private numberOfLoops = 0; + private storeUnsub: () => void; + private validPlaylistUrl: string; isPlaying: boolean; /** @ngInject */ @@ -39,15 +42,16 @@ export class PlaylistSrv { const dash = this.dashboards[this.index]; const queryParams = this.$location.search(); const filteredParams = _.pickBy(queryParams, value => value !== null); + const nextDashboardUrl = locationUtil.stripBaseFromUrl(dash.url); // this is done inside timeout to make sure digest happens after // as this can be called from react this.$timeout(() => { - const stripedUrl = locationUtil.stripBaseFromUrl(dash.url); - this.$location.url(stripedUrl + '?' + toUrlParams(filteredParams)); + this.$location.url(nextDashboardUrl + '?' + toUrlParams(filteredParams)); }); this.index++; + this.validPlaylistUrl = nextDashboardUrl; this.cancelPromise = this.$timeout(() => this.next(), this.interval); } @@ -56,6 +60,15 @@ export class PlaylistSrv { this.next(); } + // Detect url changes not caused by playlist srv and stop playlist + storeUpdated() { + const state = store.getState(); + + if (state.location.path !== this.validPlaylistUrl) { + this.stop(); + } + } + start(playlistId) { this.stop(); @@ -63,6 +76,10 @@ export class PlaylistSrv { this.index = 0; this.isPlaying = true; + // setup location tracking + this.storeUnsub = store.subscribe(() => this.storeUpdated()); + this.validPlaylistUrl = this.$location.path(); + appEvents.emit('playlist-started'); return this.backendSrv.get(`/api/playlists/${playlistId}`).then(playlist => { @@ -85,6 +102,10 @@ export class PlaylistSrv { this.index = 0; this.isPlaying = false; + if (this.storeUnsub) { + this.storeUnsub(); + } + if (this.cancelPromise) { this.$timeout.cancel(this.cancelPromise); } diff --git a/public/app/features/playlist/specs/playlist_srv.test.ts b/public/app/features/playlist/specs/playlist_srv.test.ts index bfb1732d9c6..628818fd716 100644 --- a/public/app/features/playlist/specs/playlist_srv.test.ts +++ b/public/app/features/playlist/specs/playlist_srv.test.ts @@ -1,4 +1,14 @@ +import configureMockStore from 'redux-mock-store'; import { PlaylistSrv } from '../playlist_srv'; +import { setStore } from 'app/store/store'; + +const mockStore = configureMockStore(); + +setStore( + mockStore({ + location: {}, + }) +); const dashboards = [{ url: 'dash1' }, { url: 'dash2' }]; @@ -19,6 +29,7 @@ const createPlaylistSrv = (): [PlaylistSrv, { url: jest.MockInstance } const mockLocation = { url: jest.fn(), search: () => ({}), + path: () => '/playlists/1', }; const mockTimeout = jest.fn(); @@ -96,4 +107,32 @@ describe('PlaylistSrv', () => { expect(hrefMock).toHaveBeenCalledTimes(3); expect(hrefMock).toHaveBeenLastCalledWith(initialUrl); }); + + it('storeUpdated should stop playlist when navigating away', async () => { + await srv.start(1); + + srv.storeUpdated(); + + expect(srv.isPlaying).toBe(false); + }); + + it('storeUpdated should not stop playlist when navigating to next dashboard', async () => { + await srv.start(1); + + srv.next(); + + setStore( + mockStore({ + location: { + path: 'dash2', + }, + }) + ); + + expect((srv as any).validPlaylistUrl).toBe('dash2'); + + srv.storeUpdated(); + + expect(srv.isPlaying).toBe(true); + }); });