diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index 6117286c59..97b8963919 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -54,7 +54,14 @@ import "./ha-browse-media-tts"; declare global { interface HASSDomEvents { "media-picked": MediaPickedEvent; - "media-browsed": { ids: MediaPlayerItemId[]; current?: MediaPlayerItem }; + "media-browsed": { + // Items of the new browse stack + ids: MediaPlayerItemId[]; + // Current fetched item for this browse stack + current?: MediaPlayerItem; + // If the new stack should replace the old stack + replace?: boolean; + }; } } @@ -433,8 +440,8 @@ export class HaMediaPlayerBrowse extends LitElement { if (changedProps.has("entityId")) { this._setError(undefined); - } - if (!changedProps.has("navigateIds")) { + } else if (!changedProps.has("navigateIds")) { + // Neither entity ID or navigateIDs changed, nothing to fetch return; } @@ -443,6 +450,7 @@ export class HaMediaPlayerBrowse extends LitElement { const oldNavigateIds = changedProps.get("navigateIds") as | this["navigateIds"] | undefined; + const navigateIds = this.navigateIds; // We're navigating. Reset the shizzle. this._content?.scrollTo(0, 0); @@ -451,11 +459,9 @@ export class HaMediaPlayerBrowse extends LitElement { const oldParentItem = this._parentItem; this._currentItem = undefined; this._parentItem = undefined; - const currentId = this.navigateIds[this.navigateIds.length - 1]; + const currentId = navigateIds[navigateIds.length - 1]; const parentId = - this.navigateIds.length > 1 - ? this.navigateIds[this.navigateIds.length - 2] - : undefined; + navigateIds.length > 1 ? navigateIds[navigateIds.length - 2] : undefined; let currentProm: Promise | undefined; let parentProm: Promise | undefined; @@ -464,9 +470,9 @@ export class HaMediaPlayerBrowse extends LitElement { if ( // Check if we navigated to a child oldNavigateIds && - this.navigateIds.length > oldNavigateIds.length && + navigateIds.length === oldNavigateIds.length + 1 && oldNavigateIds.every((oldVal, idx) => { - const curVal = this.navigateIds[idx]; + const curVal = navigateIds[idx]; return ( curVal.media_content_id === oldVal.media_content_id && curVal.media_content_type === oldVal.media_content_type @@ -477,8 +483,8 @@ export class HaMediaPlayerBrowse extends LitElement { } else if ( // Check if we navigated to a parent oldNavigateIds && - this.navigateIds.length < oldNavigateIds.length && - this.navigateIds.every((curVal, idx) => { + navigateIds.length === oldNavigateIds.length - 1 && + navigateIds.every((curVal, idx) => { const oldVal = oldNavigateIds[idx]; return ( curVal.media_content_id === oldVal.media_content_id && @@ -501,11 +507,33 @@ export class HaMediaPlayerBrowse extends LitElement { (item) => { this._currentItem = item; fireEvent(this, "media-browsed", { - ids: this.navigateIds, + ids: navigateIds, current: item, }); }, - (err) => this._setError(err) + (err) => { + // When we change entity ID, we will first try to see if the new entity is + // able to resolve the new path. If that results in an error, browse the root. + const isNewEntityWithSamePath = + oldNavigateIds && + changedProps.has("entityId") && + navigateIds.length === oldNavigateIds.length && + oldNavigateIds.every( + (oldItem, idx) => + navigateIds[idx].media_content_id === oldItem.media_content_id && + navigateIds[idx].media_content_type === oldItem.media_content_type + ); + if (isNewEntityWithSamePath) { + fireEvent(this, "media-browsed", { + ids: [ + { media_content_id: undefined, media_content_type: undefined }, + ], + replace: true, + }); + } else { + this._setError(err); + } + } ); // Fetch parent if (!parentProm && parentId !== undefined) { diff --git a/src/panels/media-browser/ha-bar-media-player.ts b/src/panels/media-browser/ha-bar-media-player.ts index 65a02e1fc6..1dee8318f1 100644 --- a/src/panels/media-browser/ha-bar-media-player.ts +++ b/src/panels/media-browser/ha-bar-media-player.ts @@ -25,7 +25,6 @@ import { computeStateDomain } from "../../common/entity/compute_state_domain"; import { computeStateName } from "../../common/entity/compute_state_name"; import { domainIcon } from "../../common/entity/domain_icon"; import { supportsFeature } from "../../common/entity/supports-feature"; -import { navigate } from "../../common/navigate"; import "../../components/ha-button-menu"; import "../../components/ha-icon-button"; import { UNAVAILABLE_STATES } from "../../data/entity"; @@ -47,6 +46,12 @@ import type { HomeAssistant } from "../../types"; import "../lovelace/components/hui-marquee"; import { BrowserMediaPlayer } from "./browser-media-player"; +declare global { + interface HASSDomEvents { + "player-picked": { entityId: string }; + } +} + @customElement("ha-bar-media-player") class BarMediaPlayer extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -399,7 +404,7 @@ class BarMediaPlayer extends LitElement { private _selectPlayer(ev: CustomEvent): void { const entityId = (ev.currentTarget as any).player; - navigate(`/media-browser/${entityId}`, { replace: true }); + fireEvent(this, "player-picked", { entityId }); } static get styles(): CSSResultGroup { diff --git a/src/panels/media-browser/ha-panel-media-browser.ts b/src/panels/media-browser/ha-panel-media-browser.ts index 0ab9f870c4..7a2590b903 100644 --- a/src/panels/media-browser/ha-panel-media-browser.ts +++ b/src/panels/media-browser/ha-panel-media-browser.ts @@ -43,6 +43,16 @@ import { isCameraMediaSource, } from "../../data/camera"; +const createMediaPanelUrl = (entityId: string, items: MediaPlayerItemId[]) => { + let path = `/media-browser/${entityId}`; + for (const item of items.slice(1)) { + path += + "/" + + encodeURIComponent(`${item.media_content_type},${item.media_content_id}`); + } + return path; +}; + @customElement("ha-panel-media-browser") class PanelMediaBrowser extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -120,6 +130,7 @@ class PanelMediaBrowser extends LitElement { .hass=${this.hass} .entityId=${this._entityId} .narrow=${this.narrow} + @player-picked=${this._playerPicked} > `; } @@ -179,7 +190,9 @@ class PanelMediaBrowser extends LitElement { } private _goBack() { - history.back(); + navigate( + createMediaPanelUrl(this._entityId, this._navigateIds.slice(0, -1)) + ); } private _mediaBrowsed(ev: { detail: HASSDomEvents["media-browsed"] }) { @@ -188,15 +201,9 @@ class PanelMediaBrowser extends LitElement { 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}`); + navigate(createMediaPanelUrl(this._entityId, ev.detail.ids), { + replace: ev.detail.replace, + }); } private async _mediaPicked( @@ -239,6 +246,14 @@ class PanelMediaBrowser extends LitElement { }); } + private _playerPicked(ev) { + const entityId: string = ev.detail.entityId; + if (entityId === this._entityId) { + return; + } + navigate(createMediaPanelUrl(entityId, this._navigateIds)); + } + private async _startUpload() { const input = document.createElement("input"); input.type = "file";