A decentralized music tracking and discovery platform built on AT Protocol 🎵

Add Top Albums embed UI and date filter

Introduce Header to TopAlbums and TopTracks embeds. Render a table of
albums with artwork, artist links, and numeric ranks; use uuid v4 for
row keys. Send startDate/endDate from getLastDays(7) to the
getActorAlbums endpoint. Add small CSS utilities: .rounded-[5px] and
.opacity-60.

+60
+6
apps/embed/public/styles.css
··· 1909 1909 .\!rounded-full { 1910 1910 border-radius: calc(infinity * 1px) !important; 1911 1911 } 1912 + .rounded-\[5px\] { 1913 + border-radius: 5px; 1914 + } 1912 1915 .rounded-box { 1913 1916 border-radius: var(--radius-box); 1914 1917 } ··· 2443 2446 } 2444 2447 .opacity-0 { 2445 2448 opacity: 0%; 2449 + } 2450 + .opacity-60 { 2451 + opacity: 60%; 2446 2452 } 2447 2453 .opacity-100 { 2448 2454 opacity: 100%;
+48
apps/embed/src/embeds/TopAlbumsEmbedPage.tsx
··· 1 + import { v4 } from "uuid"; 2 + import Header from "../components/Header"; 1 3 import type { Album } from "../types/album"; 2 4 import type { Profile } from "../types/profile"; 3 5 ··· 9 11 export function TopAlbumsEmbedPage(props: TopAlbumEmbedPageProps) { 10 12 return ( 11 13 <div className="p-[15px]"> 14 + <Header profile={props.profile} /> 12 15 <h2 className="m-[0px]">Top Albums</h2> 16 + 17 + <div className="w-full overflow-x-auto"> 18 + <table className="table-borderless table"> 19 + <tbody> 20 + {props.albums.map((album, index) => ( 21 + <tr key={v4()}> 22 + <td> 23 + <div className="flex flex-row items-center"> 24 + <div className="mr-[20px] min-w-[30px]">{index + 1}</div> 25 + <a 26 + href={`https://rocksky.app/${album.uri?.split("at://")[1]?.replace("app.rocksky.", "")}`} 27 + target="_blank" 28 + className="flex flex-row items-center no-underline text-inherit" 29 + > 30 + {album.albumArt && ( 31 + <img 32 + className="max-w-[60px] max-h-[60px] mr-[20px] rounded-[5px]" 33 + src={album.albumArt!} 34 + /> 35 + )} 36 + {!album.albumArt && ( 37 + <div className="w-[60px] h-[60px] bg-[var(--color-avatar-background)] flex items-center justify-center mr-[20px]"> 38 + <div className="h-[30px] w-[30px]"></div> 39 + </div> 40 + )} 41 + <div> 42 + <div>{album.title}</div> 43 + <a 44 + href={`https://rocksky.app/${album.artistUri.split("at://")[1]?.replace("app.rocksky.", "")}`} 45 + target="_blank" 46 + className="no-underline text-inherit" 47 + > 48 + <div className="font-rockford-light opacity-60"> 49 + {album.artist} 50 + </div> 51 + </a> 52 + </div> 53 + </a> 54 + </div> 55 + </td> 56 + </tr> 57 + ))} 58 + </tbody> 59 + </table> 60 + </div> 13 61 </div> 14 62 ); 15 63 }
+2
apps/embed/src/embeds/TopTracksEmbedPage.tsx
··· 1 + import Header from "../components/Header"; 1 2 import type { Profile } from "../types/profile"; 2 3 import type { Track } from "../types/track"; 3 4 ··· 9 10 export function TopTracksEmbedPage(props: TopTracksEmbedPageProps) { 10 11 return ( 11 12 <div className="p-[15px]"> 13 + <Header profile={props.profile} /> 12 14 <h2 className="m-[0px]">Top Tracks</h2> 13 15 </div> 14 16 );
+4
apps/embed/src/xrpc/getTopAlbums.ts
··· 1 1 import { ROCKSKY_API_URL } from "../consts"; 2 + import getLastDays from "../lib/getLastDays"; 2 3 import type { Album } from "../types/album"; 3 4 4 5 export default async function getTopAlbums(handle: string) { 6 + const [start, end] = getLastDays(7); 5 7 const url = new URL( 6 8 `${ROCKSKY_API_URL}/xrpc/app.rocksky.actor.getActorAlbums`, 7 9 ); 8 10 url.searchParams.append("did", handle); 11 + url.searchParams.append("startDate", start.toISOString()); 12 + url.searchParams.append("endDate", end.toISOString()); 9 13 url.searchParams.append("limit", "20"); 10 14 const res = await fetch(url); 11 15