feat: user avatar images

- added user avatars for each user
- it will fetch user images from github or google and if there is no image then it will show initials
This commit is contained in:
Akhileshrangani4 2024-11-10 23:40:10 -05:00
parent 90bfdec58a
commit e9f03d52fd
11 changed files with 64 additions and 18 deletions

View File

@ -15,6 +15,20 @@
"when": 1731290863632,
"tag": "0001_opposite_newton_destine",
"breakpoints": true
},
{
"idx": 2,
"version": "5",
"when": 1731296235880,
"tag": "0002_rainy_fantastic_four",
"breakpoints": true
},
{
"idx": 3,
"version": "5",
"when": 1731297339306,
"tag": "0003_lying_snowbird",
"breakpoints": true
}
]
}

View File

@ -169,6 +169,7 @@ export default {
name: sb.name,
type: sb.type,
author: sb.author.name,
authorAvatarUrl: sb.author.avatarUrl,
sharedOn: r.sharedOn,
}
})

View File

@ -51,7 +51,7 @@ const getSharedUsers = async (usersToSandboxes: UsersToSandboxes[]) => {
}
)
const userData: User = await userRes.json()
return { id: userData.id, name: userData.name }
return { id: userData.id, name: userData.name, avatarUrl: userData.avatarUrl }
})
)

View File

@ -35,6 +35,7 @@ export default async function DashboardPage() {
type: "react" | "node"
author: string
sharedOn: Date
authorAvatarUrl: string
}[]
return (

View File

@ -20,6 +20,7 @@ export default function DashboardSharedWithMe({
name: string
type: "react" | "node"
author: string
authorAvatarUrl: string
sharedOn: Date
}[]
}) {
@ -58,7 +59,11 @@ export default function DashboardSharedWithMe({
</TableCell>
<TableCell>
<div className="flex items-center">
<Avatar name={sandbox.author} className="mr-2" />
<Avatar
name={sandbox.author}
avatarUrl={sandbox.authorAvatarUrl}
className="mr-2"
/>
{sandbox.author}
</div>
</TableCell>

View File

@ -23,7 +23,7 @@ export default function Navbar({
}: {
userData: User
sandboxData: Sandbox
shared: { id: string; name: string }[]
shared: { id: string; name: string; avatarUrl: string }[]
}) {
const [isEditOpen, setIsEditOpen] = useState(false)
const [isShareOpen, setIsShareOpen] = useState(false)

View File

@ -43,6 +43,7 @@ export default function ShareSandboxModal({
shared: {
id: string
name: string
avatarUrl: string
}[]
}) {
const [loading, setLoading] = useState(false)
@ -142,7 +143,11 @@ export default function ShareSandboxModal({
</DialogHeader>
<div className="space-y-2">
{shared.map((user) => (
<SharedUser key={user.id} user={user} sandboxId={data.id} />
<SharedUser
key={user.id}
user={user}
sandboxId={data.id}
/>
))}
</div>
</div>

View File

@ -10,7 +10,7 @@ export default function SharedUser({
user,
sandboxId,
}: {
user: { id: string; name: string }
user: { id: string; name: string; avatarUrl: string }
sandboxId: string
}) {
const [loading, setLoading] = useState(false)
@ -24,7 +24,7 @@ export default function SharedUser({
return (
<div className="flex items-center justify-between">
<div className="flex items-center">
<Avatar name={user.name} className="mr-2" />
<Avatar name={user.name} avatarUrl={user.avatarUrl} className="mr-2" />
{user.name}
</div>
<Button

View File

@ -1,23 +1,42 @@
import { cn } from "@/lib/utils"
import Image from "next/image"
export default function Avatar({
name,
avatarUrl,
className,
}: {
name: string
avatarUrl?: string | null
className?: string
}) {
// Generate initials from name if no avatarUrl is provided
const initials = name
? name
.split(" ")
.slice(0, 2)
.map((letter) => letter[0].toUpperCase())
.join("")
: "?"
return (
<div
className={cn(
className,
"w-5 h-5 font-mono rounded-full overflow-hidden bg-gradient-to-t from-neutral-800 to-neutral-600 flex items-center justify-center text-[0.5rem] font-medium"
"w-9 h-9 font-mono rounded-full overflow-hidden bg-gradient-to-t from-neutral-800 to-neutral-600 flex items-center justify-center text-sm font-medium"
)}
>
{name
.split(" ")
.slice(0, 2)
.map((letter) => letter[0].toUpperCase())}
{avatarUrl ? (
<Image
src={avatarUrl}
alt={name || "User"}
width={20}
height={20}
className="w-full h-full object-cover"
/>
) : (
initials
)}
</div>
)
}

View File

@ -11,6 +11,7 @@ import { User } from "@/lib/types"
import { useClerk } from "@clerk/nextjs"
import { LogOut, Sparkles } from "lucide-react"
import { useRouter } from "next/navigation"
import Avatar from "./avatar"
export default function UserButton({ userData }: { userData: User }) {
if (!userData) return null
@ -21,13 +22,7 @@ export default function UserButton({ userData }: { userData: User }) {
return (
<DropdownMenu>
<DropdownMenuTrigger>
<div className="w-9 h-9 font-mono rounded-full overflow-hidden bg-gradient-to-t from-neutral-800 to-neutral-600 flex items-center justify-center text-sm font-medium">
{userData.name &&
userData.name
.split(" ")
.slice(0, 2)
.map((name) => name[0].toUpperCase())}
</div>
<Avatar name={userData.name} avatarUrl={userData.avatarUrl} />
</DropdownMenuTrigger>
<DropdownMenuContent className="w-48" align="end">
<div className="py-1.5 px-2 w-full">

View File

@ -5,6 +5,12 @@ const nextConfig = {
{
hostname: "cdn.simpleicons.org",
},
{
hostname: "img.clerk.com",
},
{
hostname: "images.clerk.dev",
},
],
},
}