diff --git a/public/icons.svg b/public/icons.svg index e1c4d3d..7e8b218 100644 --- a/public/icons.svg +++ b/public/icons.svg @@ -92,5 +92,8 @@ + + + diff --git a/src/hooks/status.ts b/src/hooks/status.ts new file mode 100644 index 0000000..2ebfc0f --- /dev/null +++ b/src/hooks/status.ts @@ -0,0 +1,22 @@ +import { useMemo } from "react"; + +import { EventKind, ReplaceableNoteStore, RequestBuilder } from "@snort/system"; +import { useRequestBuilder } from "@snort/system-react"; + +type StatusTag = "general" | "music"; + +export function useStatus(tag: StatusTag, author?: string, leaveOpen = true) { + const sub = useMemo(() => { + if (!author) return null; + const b = new RequestBuilder(`status:${tag}:${author.slice(0, 8)}`); + b.withOptions({ leaveOpen }); + b.withFilter() + .kinds([30315 as EventKind]) + .tag("d", [tag]) + .authors([author]); + return b; + }, [author]); + + const { data } = useRequestBuilder(ReplaceableNoteStore, sub); + return data; +} diff --git a/src/pages/alerts.css b/src/pages/alerts.css index d283147..e89f532 100644 --- a/src/pages/alerts.css +++ b/src/pages/alerts.css @@ -110,3 +110,21 @@ width: 24px; height: 24px; } + +.music { + display: flex; + flex-direction: column; + align-items: center; + padding: 16px; + gap: 4px; +} + +.music .cover { + object-fit: cover; + width: 120px; + height: 120px; +} + +.music .track { + margin: 0; +} diff --git a/src/pages/alerts.tsx b/src/pages/alerts.tsx index 567943f..5205d7b 100644 --- a/src/pages/alerts.tsx +++ b/src/pages/alerts.tsx @@ -5,6 +5,7 @@ import { useParams } from "react-router-dom"; import { ZapAlerts } from "./widgets/zaps"; import { Views } from "./widgets/views"; import { TopZappersWidget } from "./widgets/top-zappers"; +import { Music } from "./widgets/music"; export function AlertsPage() { const params = useParams(); @@ -24,6 +25,9 @@ export function AlertsPage() { case "top-zappers": { return ; } + case "music": { + return ; + } } return null; } diff --git a/src/pages/layout.tsx b/src/pages/layout.tsx index 962f0b0..4001b9a 100644 --- a/src/pages/layout.tsx +++ b/src/pages/layout.tsx @@ -78,7 +78,11 @@ export function LayoutPage() { navigate("/settings")}> - Settings + + + navigate("/widgets")}> + + Login.logout()}> diff --git a/src/pages/widgets.tsx b/src/pages/widgets.tsx index 2c4dd32..dfe6baa 100644 --- a/src/pages/widgets.tsx +++ b/src/pages/widgets.tsx @@ -12,6 +12,7 @@ import { eventToLink, hexToBech32 } from "utils"; import { ZapAlertItem } from "./widgets/zaps"; import { TopZappersWidget } from "./widgets/top-zappers"; import { Views } from "./widgets/views"; +import { Music } from "./widgets/music"; import groupBy from "lodash/groupBy"; interface ZapAlertConfigurationProps { @@ -195,6 +196,13 @@ export function WidgetsPage() { {currentLink && } +
+

+ +

+ + {currentLink && } +
); } diff --git a/src/pages/widgets/music.tsx b/src/pages/widgets/music.tsx new file mode 100644 index 0000000..11f6ca1 --- /dev/null +++ b/src/pages/widgets/music.tsx @@ -0,0 +1,22 @@ +import { NostrLink } from "@snort/system"; +import { unixNow } from "@snort/shared"; +import { useCurrentStreamFeed } from "hooks/current-stream-feed"; +import { useStatus } from "hooks/status"; +import { getHost, findTag } from "utils"; + +export function Music({ link }: { link: NostrLink }) { + const currentEvent = useCurrentStreamFeed(link, true); + const host = getHost(currentEvent); + const nowPlaying = useStatus("music", host, true); + const cover = nowPlaying && findTag(nowPlaying, "cover"); + const expiry = nowPlaying && findTag(nowPlaying, "expiration"); + const isExpired = expiry && Number(expiry) > unixNow(); + return ( + !isExpired && ( +
+ {cover && {nowPlaying.content}} + {nowPlaying &&

🎵 {nowPlaying.content}

} +
+ ) + ); +}