Add Avatar component and integrate it into AudioBubble; update package dependencies

This commit is contained in:
2025-05-28 23:50:23 +02:00
parent 8873f72b9f
commit 9f6c9ec02f
5 changed files with 131 additions and 10 deletions

View File

@@ -98,6 +98,7 @@ export default function Home() {
<AudioBubble
key={event.id}
eventId={event.id}
event={event}
url={url}
isPlaying={event.id === playingEventId}
onClick={handleBubbleClick}

View File

@@ -1,16 +1,23 @@
"use client"
import { useEffect, useRef, useState } from "react"
import { Music } from "lucide-react"
// import { Music } from "lucide-react"
import { useProfile } from "nostr-react"
import { Event as NostrEvent } from "nostr-tools"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
interface AudioBubbleProps {
eventId: string
event: NostrEvent
url: string
isPlaying: boolean
onClick: (url: string, eventId: string) => void
}
export default function AudioBubble({ eventId, url, isPlaying, onClick }: AudioBubbleProps) {
export default function AudioBubble({ eventId, event, url, isPlaying, onClick }: AudioBubbleProps) {
const { data } = useProfile({ pubkey: event.pubkey })
const bubbleRef = useRef<HTMLDivElement>(null)
const [position, setPosition] = useState({
x: Math.random() * 80 + 10, // 10-90% of screen width
@@ -67,11 +74,10 @@ export default function AudioBubble({ eventId, url, isPlaying, onClick }: AudioB
return (
<div
ref={bubbleRef}
className={`absolute rounded-full flex items-center justify-center cursor-pointer transition-all duration-300 ${
isPlaying
? "bg-white/90 shadow-lg shadow-white/30"
className={`absolute rounded-full flex items-center justify-center cursor-pointer transition-all duration-300 ${isPlaying
? "bg-white/90 shadow-lg shadow-white/30"
: "bg-white/70 hover:bg-white/80"
}`}
}`}
style={{
left: `${position.x}%`,
top: `${position.y}%`,
@@ -80,14 +86,19 @@ export default function AudioBubble({ eventId, url, isPlaying, onClick }: AudioB
transform: "translate(-50%, -50%)",
zIndex: isPlaying ? 10 : 1,
backdropFilter: "blur(2px)",
boxShadow: isPlaying
? "0 0 15px 5px rgba(255, 255, 255, 0.3)"
boxShadow: isPlaying
? "0 0 15px 5px rgba(255, 255, 255, 0.3)"
: "0 0 10px 2px rgba(255, 255, 255, 0.15)"
}}
onClick={handleClick}
>
<Music className={`${isPlaying ? "animate-pulse text-black/90" : "text-black/70"}`} size={size * 0.35} />
{/* <Music className={`${isPlaying ? "animate-pulse text-black/90" : "text-black/70"}`} size={size * 0.35} /> */}
<Avatar>
<AvatarImage src={data?.picture} />
<AvatarFallback>{data?.name}</AvatarFallback>
</Avatar>
{isPlaying && (
<div className="absolute -bottom-5 left-1/2 transform -translate-x-1/2 text-white/50 text-[10px] whitespace-nowrap animate-pulse">
Info

53
components/ui/avatar.tsx Normal file
View File

@@ -0,0 +1,53 @@
"use client"
import * as React from "react"
import * as AvatarPrimitive from "@radix-ui/react-avatar"
import { cn } from "@/lib/utils"
function Avatar({
className,
...props
}: React.ComponentProps<typeof AvatarPrimitive.Root>) {
return (
<AvatarPrimitive.Root
data-slot="avatar"
className={cn(
"relative flex size-8 shrink-0 overflow-hidden rounded-full",
className
)}
{...props}
/>
)
}
function AvatarImage({
className,
...props
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
return (
<AvatarPrimitive.Image
data-slot="avatar-image"
className={cn("aspect-square size-full", className)}
{...props}
/>
)
}
function AvatarFallback({
className,
...props
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
return (
<AvatarPrimitive.Fallback
data-slot="avatar-fallback"
className={cn(
"bg-muted flex size-full items-center justify-center rounded-full",
className
)}
{...props}
/>
)
}
export { Avatar, AvatarImage, AvatarFallback }

55
package-lock.json generated
View File

@@ -8,6 +8,7 @@
"name": "nostr-voice",
"version": "0.1.0",
"dependencies": {
"@radix-ui/react-avatar": "^1.1.10",
"@radix-ui/react-dialog": "^1.1.14",
"@radix-ui/react-slot": "^1.2.3",
"class-variance-authority": "^0.7.1",
@@ -1008,6 +1009,33 @@
"integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==",
"license": "MIT"
},
"node_modules/@radix-ui/react-avatar": {
"version": "1.1.10",
"resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.10.tgz",
"integrity": "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-callback-ref": "1.1.1",
"@radix-ui/react-use-is-hydrated": "0.1.0",
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
@@ -1318,6 +1346,24 @@
}
}
},
"node_modules/@radix-ui/react-use-is-hydrated": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.0.tgz",
"integrity": "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==",
"license": "MIT",
"dependencies": {
"use-sync-external-store": "^1.5.0"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-layout-effect": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
@@ -6678,6 +6724,15 @@
}
}
},
"node_modules/use-sync-external-store": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
"integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
"license": "MIT",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/vaul": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vaul/-/vaul-1.1.2.tgz",

View File

@@ -9,6 +9,7 @@
"lint": "next lint"
},
"dependencies": {
"@radix-ui/react-avatar": "^1.1.10",
"@radix-ui/react-dialog": "^1.1.14",
"@radix-ui/react-slot": "^1.2.3",
"class-variance-authority": "^0.7.1",