From 09a27a6791ec784739b40fb54092cf89d88c3433 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 17 Jan 2022 07:40:51 -0800 Subject: [PATCH] Reflect media browser panel state in URL (#11317) --- .../dialog-media-player-browse.ts | 36 ++- .../media-player/ha-media-player-browse.ts | 277 ++++++++++-------- src/layouts/home-assistant.ts | 6 +- .../media-browser/ha-panel-media-browser.ts | 53 +++- 4 files changed, 222 insertions(+), 150 deletions(-) diff --git a/src/components/media-player/dialog-media-player-browse.ts b/src/components/media-player/dialog-media-player-browse.ts index 6e5a54d973..791aee67d5 100644 --- a/src/components/media-player/dialog-media-player-browse.ts +++ b/src/components/media-player/dialog-media-player-browse.ts @@ -9,32 +9,30 @@ import { haStyleDialog } from "../../resources/styles"; import type { HomeAssistant } from "../../types"; import "../ha-dialog"; import "./ha-media-player-browse"; +import type { MediaPlayerItemId } from "./ha-media-player-browse"; import { MediaPlayerBrowseDialogParams } from "./show-media-browser-dialog"; @customElement("dialog-media-player-browse") class DialogMediaPlayerBrowse extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @state() private _entityId!: string; - - @state() private _mediaContentId?: string; - - @state() private _mediaContentType?: string; - - @state() private _action?: MediaPlayerBrowseAction; + @state() private _navigateIds?: MediaPlayerItemId[]; @state() private _params?: MediaPlayerBrowseDialogParams; public showDialog(params: MediaPlayerBrowseDialogParams): void { this._params = params; - this._entityId = this._params.entityId; - this._mediaContentId = this._params.mediaContentId; - this._mediaContentType = this._params.mediaContentType; - this._action = this._params.action || "play"; + this._navigateIds = [ + { + media_content_id: this._params.mediaContentId, + media_content_type: this._params.mediaContentType, + }, + ]; } public closeDialog() { this._params = undefined; + this._navigateIds = undefined; fireEvent(this, "dialog-closed", { dialog: this.localName }); } @@ -55,17 +53,21 @@ class DialogMediaPlayerBrowse extends LitElement { `; } + private _mediaBrowsed(ev) { + this._navigateIds = ev.detail.ids; + } + private _mediaPicked(ev: HASSDomEvent): void { this._params!.mediaPickedCallback(ev.detail); if (this._action !== "play") { @@ -73,6 +75,10 @@ class DialogMediaPlayerBrowse extends LitElement { } } + private get _action(): MediaPlayerBrowseAction { + return this._params!.action || "play"; + } + static get styles(): CSSResultGroup { return [ haStyleDialog, diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index 0d5ec6adb8..7c08bad54c 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -53,19 +53,21 @@ import "../ha-svg-icon"; declare global { interface HASSDomEvents { "media-picked": MediaPickedEvent; + "media-browsed": { ids: MediaPlayerItemId[]; back?: boolean }; } } +export interface MediaPlayerItemId { + media_content_id: string | undefined; + media_content_type: string | undefined; +} + @customElement("ha-media-player-browse") export class HaMediaPlayerBrowse extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @property() public entityId!: string; - @property() public mediaContentId?: string; - - @property() public mediaContentType?: string; - @property() public action: MediaPlayerBrowseAction = "play"; @property({ type: Boolean }) public dialog = false; @@ -76,11 +78,13 @@ export class HaMediaPlayerBrowse extends LitElement { @property({ type: Boolean, attribute: "scroll", reflect: true }) private _scrolled = false; - @state() private _loading = false; + @property() public navigateIds!: MediaPlayerItemId[]; @state() private _error?: { message: string; code: string }; - @state() private _mediaPlayerItems: MediaPlayerItem[] = []; + @state() private _parentItem?: MediaPlayerItem; + + @state() private _currentItem?: MediaPlayerItem; @query(".header") private _header?: HTMLDivElement; @@ -109,47 +113,18 @@ export class HaMediaPlayerBrowse extends LitElement { } } - public navigateBack() { - this._mediaPlayerItems!.pop(); - const item = this._mediaPlayerItems!.pop(); - if (!item) { - return; - } - this._navigate(item); - } - protected render(): TemplateResult { - if (this._loading) { + if (this._error) { + return html` +
${this._renderError(this._error)}
+ `; + } + + if (!this._currentItem) { return html``; } - if (this._error && !this._mediaPlayerItems.length) { - if (this.dialog) { - this._closeDialogAction(); - showAlertDialog(this, { - title: this.hass.localize( - "ui.components.media-browser.media_browsing_error" - ), - text: this._renderError(this._error), - }); - } else { - return html` -
${this._renderError(this._error)}
- `; - } - } - - if (!this._mediaPlayerItems.length) { - return html``; - } - - const currentItem = - this._mediaPlayerItems[this._mediaPlayerItems.length - 1]; - - const previousItem: MediaPlayerItem | undefined = - this._mediaPlayerItems.length > 1 - ? this._mediaPlayerItems[this._mediaPlayerItems.length - 2] - : undefined; + const currentItem = this._currentItem; const subtitle = this.hass.localize( `ui.components.media-browser.class.${currentItem.media_class}` @@ -202,11 +177,11 @@ export class HaMediaPlayerBrowse extends LitElement { : html``}
`; } - public updated(changedProps: PropertyValues): void { - super.updated(changedProps); + public willUpdate(changedProps: PropertyValues): void { + super.willUpdate(changedProps); if (!changedProps.has("route")) { return; @@ -96,10 +105,28 @@ class PanelMediaBrowser extends LitElement { return; } - const routePlayer = this.route.path.substring(1).split("/")[0]; + const [routePlayer, ...navigateIdsEncoded] = this.route.path + .substring(1) + .split("/"); + if (routePlayer !== this._entityId) { this._entityId = routePlayer; } + + this._navigateIds = [ + { + media_content_type: undefined, + media_content_id: undefined, + }, + ...navigateIdsEncoded.map((navigateId) => { + const [media_content_type, media_content_id] = + decodeURIComponent(navigateId).split(","); + return { + media_content_type, + media_content_id, + }; + }), + ]; } private _showSelectMediaPlayerDialog(): void { @@ -111,6 +138,22 @@ class PanelMediaBrowser extends LitElement { }); } + private _mediaBrowsed(ev) { + if (ev.detail.back) { + history.back(); + return; + } + let path = ""; + for (const item of ev.detail.ids.slice(1)) { + path += + "/" + + encodeURIComponent( + `${item.media_content_type},${item.media_content_id}` + ); + } + navigate(`/media-browser/${this._entityId}${path}`); + } + private async _mediaPicked( ev: HASSDomEvent ): Promise {