A React component library for rendering common AT Protocol records for applications such as Bluesky and Leaflet.
1import React from "react"; 2import { AtProtoRecord } from "../core/AtProtoRecord"; 3import { CurrentlyPlayingRenderer } from "../renderers/CurrentlyPlayingRenderer"; 4import { useDidResolution } from "../hooks/useDidResolution"; 5import type { TealActorStatusRecord } from "../types/teal"; 6 7/** 8 * Props for rendering teal.fm currently playing status. 9 */ 10export interface CurrentlyPlayingProps { 11 /** DID of the user whose currently playing status to display. */ 12 did: string; 13 /** Record key within the `fm.teal.alpha.actor.status` collection (usually "self"). */ 14 rkey?: string; 15 /** Prefetched teal.fm status record. When provided, skips fetching from the network. */ 16 record?: TealActorStatusRecord; 17 /** Optional renderer override for custom presentation. */ 18 renderer?: React.ComponentType<CurrentlyPlayingRendererInjectedProps>; 19 /** Fallback node displayed before loading begins. */ 20 fallback?: React.ReactNode; 21 /** Indicator node shown while data is loading. */ 22 loadingIndicator?: React.ReactNode; 23 /** Preferred color scheme for theming. */ 24 colorScheme?: "light" | "dark" | "system"; 25 /** Auto-refresh music data and album art every 15 seconds. Defaults to true. */ 26 autoRefresh?: boolean; 27} 28 29/** 30 * Values injected into custom currently playing renderer implementations. 31 */ 32export type CurrentlyPlayingRendererInjectedProps = { 33 /** Loaded teal.fm status record value. */ 34 record: TealActorStatusRecord; 35 /** Indicates whether the record is currently loading. */ 36 loading: boolean; 37 /** Fetch error, if any. */ 38 error?: Error; 39 /** Preferred color scheme for downstream components. */ 40 colorScheme?: "light" | "dark" | "system"; 41 /** DID associated with the record. */ 42 did: string; 43 /** Record key for the status. */ 44 rkey: string; 45 /** Auto-refresh music data and album art every 15 seconds. */ 46 autoRefresh?: boolean; 47 /** Label to display. */ 48 label?: string; 49 /** Refresh interval in milliseconds. */ 50 refreshInterval?: number; 51 /** Handle to display in not listening state */ 52 handle?: string; 53}; 54 55/** NSID for teal.fm actor status records. */ 56export const CURRENTLY_PLAYING_COLLECTION = "fm.teal.alpha.actor.status"; 57 58/** 59 * Displays the currently playing track from teal.fm with auto-refresh. 60 * 61 * @param did - DID whose currently playing status should be fetched. 62 * @param rkey - Record key within the teal.fm status collection (defaults to "self"). 63 * @param renderer - Optional component override that will receive injected props. 64 * @param fallback - Node rendered before the first load begins. 65 * @param loadingIndicator - Node rendered while the status is loading. 66 * @param colorScheme - Preferred color scheme for theming the renderer. 67 * @param autoRefresh - When true (default), refreshes album art and streaming platform links every 15 seconds. 68 * @returns A JSX subtree representing the currently playing track with loading states handled. 69 */ 70export const CurrentlyPlaying: React.FC<CurrentlyPlayingProps> = React.memo(({ 71 did, 72 rkey = "self", 73 record, 74 renderer, 75 fallback, 76 loadingIndicator, 77 colorScheme, 78 autoRefresh = true, 79}) => { 80 // Resolve handle from DID 81 const { handle } = useDidResolution(did); 82 83 const Comp: React.ComponentType<CurrentlyPlayingRendererInjectedProps> = 84 renderer ?? ((props) => <CurrentlyPlayingRenderer {...props} />); 85 const Wrapped: React.FC<{ 86 record: TealActorStatusRecord; 87 loading: boolean; 88 error?: Error; 89 }> = (props) => ( 90 <Comp 91 {...props} 92 colorScheme={colorScheme} 93 did={did} 94 rkey={rkey} 95 autoRefresh={autoRefresh} 96 label="CURRENTLY PLAYING" 97 refreshInterval={15000} 98 handle={handle} 99 /> 100 ); 101 102 if (record !== undefined) { 103 return ( 104 <AtProtoRecord<TealActorStatusRecord> 105 record={record} 106 renderer={Wrapped} 107 fallback={fallback} 108 loadingIndicator={loadingIndicator} 109 /> 110 ); 111 } 112 113 return ( 114 <AtProtoRecord<TealActorStatusRecord> 115 did={did} 116 collection={CURRENTLY_PLAYING_COLLECTION} 117 rkey={rkey} 118 renderer={Wrapped} 119 fallback={fallback} 120 loadingIndicator={loadingIndicator} 121 /> 122 ); 123}); 124 125export default CurrentlyPlaying;