your personal website on atproto - mirror blento.app
at fix-cached-posts 104 lines 3.1 kB view raw
1<script lang="ts"> 2 import { onMount } from 'svelte'; 3 import { getAdditionalUserData } from '$lib/website/context'; 4 import type { ContentComponentProps } from '../../../types'; 5 import LastFMAlbumArt from '../LastFMAlbumArt.svelte'; 6 import { RelativeTime } from '@foxui/time'; 7 import { fetchLastFM } from '../api.remote'; 8 9 interface Track { 10 name: string; 11 artist: { '#text': string }; 12 album: { '#text': string }; 13 image: { '#text': string; size: string }[]; 14 url: string; 15 date?: { uts: string }; 16 '@attr'?: { nowplaying: string }; 17 } 18 19 let { item }: ContentComponentProps = $props(); 20 21 const data = getAdditionalUserData(); 22 const cacheKey = $derived(`lastfmRecentTracks:${item.cardData.lastfmUsername}`); 23 24 // svelte-ignore state_referenced_locally 25 let tracks = $state(data[cacheKey] as Track[] | undefined); 26 let error = $state(false); 27 28 onMount(async () => { 29 if (tracks) return; 30 if (!item.cardData.lastfmUsername) return; 31 32 try { 33 const result = await fetchLastFM({ 34 method: 'user.getRecentTracks', 35 user: item.cardData.lastfmUsername 36 }); 37 if (result) { 38 tracks = result?.recenttracks?.track ?? []; 39 data[cacheKey] = tracks; 40 } else { 41 error = true; 42 } 43 } catch { 44 error = true; 45 } 46 }); 47</script> 48 49<div class="z-10 flex h-full w-full flex-col gap-3 overflow-y-scroll p-4"> 50 {#if tracks && tracks.length > 0} 51 {#each tracks as track, i (track.url + i)} 52 <a 53 href={track.url} 54 target="_blank" 55 rel="noopener noreferrer" 56 class="flex w-full items-center gap-3" 57 > 58 <div class="size-10 shrink-0"> 59 <LastFMAlbumArt images={track.image} alt={track.album?.['#text']} /> 60 </div> 61 <div class="min-w-0 flex-1"> 62 <div class="inline-flex w-full max-w-full justify-between gap-2"> 63 <div 64 class="text-accent-500 accent:text-accent-950 min-w-0 flex-1 shrink truncate font-semibold" 65 > 66 {track.name} 67 </div> 68 {#if track['@attr']?.nowplaying === 'true'} 69 <div class="flex shrink-0 items-center gap-1 text-xs text-green-500"> 70 <span class="inline-block size-2 animate-pulse rounded-full bg-green-500"></span> 71 Now 72 </div> 73 {:else if track.date?.uts} 74 <div class="shrink-0 text-xs"> 75 <RelativeTime date={new Date(parseInt(track.date.uts) * 1000)} locale="en-US" /> ago 76 </div> 77 {/if} 78 </div> 79 <div class="my-1 min-w-0 truncate text-xs whitespace-nowrap"> 80 {track.artist?.['#text']} 81 </div> 82 </div> 83 </a> 84 {/each} 85 {:else if error} 86 <div 87 class="text-base-500 dark:text-base-400 accent:text-white/60 flex h-full items-center justify-center text-center text-sm" 88 > 89 Failed to load tracks. 90 </div> 91 {:else if tracks} 92 <div 93 class="text-base-500 dark:text-base-400 accent:text-white/60 flex h-full items-center justify-center text-center text-sm" 94 > 95 No recent tracks found. 96 </div> 97 {:else} 98 <div 99 class="text-base-500 dark:text-base-400 accent:text-white/60 flex h-full items-center justify-center text-center text-sm" 100 > 101 Loading tracks... 102 </div> 103 {/if} 104</div>