Personal Site

Add functionality for handling song updates. `updateMetadata` and `nothingPlaying` should only be called when DOM updates are required, not every time a playing message is recived

- nothingPlaying simply prevents the popup appearing.

- updateMetadata naively updates title, album, and art, since all of those can be updated in place.
- The complexity in the artists update is because if the innerHTML is replaced and the user is tabbed onto one of the artists, the page will delete the focus, confusing the user and interrupting them. To avoid this, the array of artists is updated in place, with extra artists added and removed as needed. If an artist is removed, focus is sent to the last artist which could receive focus, to reduce disruption

vielle.dev 9d4f7e4d 87923615

verified
+69
+69
src/components/playing/NowPlaying.astro
··· 464 464 artists: querySelector(this, "[slot=artists]", HTMLSpanElement), 465 465 art: querySelector(this, "[slot=art]", HTMLImageElement), 466 466 }; 467 + 468 + updateMetadata(playing: Exclude<nowPlaying, null>) { 469 + // title can be updated without distrupting focus 470 + this.elements.title.innerText = playing.name; 471 + this.elements.title.href = playing.external_urls.spotify; 472 + 473 + // same for album 474 + this.elements.album.innerText = playing.album.name; 475 + 476 + // same for art 477 + this.elements.art.src = playing.album.images[0].url; 478 + 479 + const artistLen = this.elements.artists.children.length; 480 + 481 + // artists is more complex, as focus needs to be maintained. 482 + const replaceArtists = playing.artists.slice(0, artistLen); 483 + // slice uses array.length if end is >= array.length 484 + // so we need to padd it out 485 + replaceArtists.push( 486 + ...new Array(artistLen - replaceArtists.length).fill(undefined), 487 + ); 488 + const addArtists = playing.artists.slice(artistLen); 489 + 490 + console.log(replaceArtists, addArtists); 491 + 492 + let lastValidArtist = elIs( 493 + this.elements.artists.children[0], 494 + HTMLElement, 495 + ); 496 + replaceArtists.forEach((artist, i) => { 497 + // if this index exists in both arrays, update in place 498 + // this respects focus and shouldnt cause issues 499 + const el = elIs(this.elements.artists.children[i], HTMLAnchorElement); 500 + if (artist) { 501 + el.innerHTML = artist.name; 502 + el.href = artist.external_urls.spotify; 503 + // update last valid for moving focus too if needed 504 + lastValidArtist = el; 505 + } 506 + 507 + // if index exists in old array but not new array 508 + if (!artist) { 509 + if (document.activeElement === el) lastValidArtist.focus(); 510 + // essentially destroy it 511 + el.remove(); 512 + } 513 + }); 514 + 515 + // this is safe to stick direct in DOM 516 + // this is when the new artist count > old artist count 517 + this.elements.artists.append( 518 + ...addArtists.map((artist) => { 519 + const a = document.createElement("a"); 520 + 521 + a.innerText = artist.name; 522 + a.href = artist.external_urls.spotify; 523 + 524 + return a; 525 + }), 526 + ); 527 + 528 + // remove the inline display value 529 + this.style.removeProperty("display"); 530 + } 531 + 532 + nothingPlaying() { 533 + // dont let it show up if nothing is playing 534 + this.style.setProperty("display", "none"); 535 + } 467 536 } 468 537 customElements.define("now-playing", HTMLNowPlayingElement); 469 538