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:
parent
90bfdec58a
commit
e9f03d52fd
@ -15,6 +15,20 @@
|
|||||||
"when": 1731290863632,
|
"when": 1731290863632,
|
||||||
"tag": "0001_opposite_newton_destine",
|
"tag": "0001_opposite_newton_destine",
|
||||||
"breakpoints": true
|
"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
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -169,6 +169,7 @@ export default {
|
|||||||
name: sb.name,
|
name: sb.name,
|
||||||
type: sb.type,
|
type: sb.type,
|
||||||
author: sb.author.name,
|
author: sb.author.name,
|
||||||
|
authorAvatarUrl: sb.author.avatarUrl,
|
||||||
sharedOn: r.sharedOn,
|
sharedOn: r.sharedOn,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -51,7 +51,7 @@ const getSharedUsers = async (usersToSandboxes: UsersToSandboxes[]) => {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
const userData: User = await userRes.json()
|
const userData: User = await userRes.json()
|
||||||
return { id: userData.id, name: userData.name }
|
return { id: userData.id, name: userData.name, avatarUrl: userData.avatarUrl }
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ export default async function DashboardPage() {
|
|||||||
type: "react" | "node"
|
type: "react" | "node"
|
||||||
author: string
|
author: string
|
||||||
sharedOn: Date
|
sharedOn: Date
|
||||||
|
authorAvatarUrl: string
|
||||||
}[]
|
}[]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -20,6 +20,7 @@ export default function DashboardSharedWithMe({
|
|||||||
name: string
|
name: string
|
||||||
type: "react" | "node"
|
type: "react" | "node"
|
||||||
author: string
|
author: string
|
||||||
|
authorAvatarUrl: string
|
||||||
sharedOn: Date
|
sharedOn: Date
|
||||||
}[]
|
}[]
|
||||||
}) {
|
}) {
|
||||||
@ -58,7 +59,11 @@ export default function DashboardSharedWithMe({
|
|||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Avatar name={sandbox.author} className="mr-2" />
|
<Avatar
|
||||||
|
name={sandbox.author}
|
||||||
|
avatarUrl={sandbox.authorAvatarUrl}
|
||||||
|
className="mr-2"
|
||||||
|
/>
|
||||||
{sandbox.author}
|
{sandbox.author}
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
@ -23,7 +23,7 @@ export default function Navbar({
|
|||||||
}: {
|
}: {
|
||||||
userData: User
|
userData: User
|
||||||
sandboxData: Sandbox
|
sandboxData: Sandbox
|
||||||
shared: { id: string; name: string }[]
|
shared: { id: string; name: string; avatarUrl: string }[]
|
||||||
}) {
|
}) {
|
||||||
const [isEditOpen, setIsEditOpen] = useState(false)
|
const [isEditOpen, setIsEditOpen] = useState(false)
|
||||||
const [isShareOpen, setIsShareOpen] = useState(false)
|
const [isShareOpen, setIsShareOpen] = useState(false)
|
||||||
|
@ -43,6 +43,7 @@ export default function ShareSandboxModal({
|
|||||||
shared: {
|
shared: {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
|
avatarUrl: string
|
||||||
}[]
|
}[]
|
||||||
}) {
|
}) {
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
@ -142,7 +143,11 @@ export default function ShareSandboxModal({
|
|||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{shared.map((user) => (
|
{shared.map((user) => (
|
||||||
<SharedUser key={user.id} user={user} sandboxId={data.id} />
|
<SharedUser
|
||||||
|
key={user.id}
|
||||||
|
user={user}
|
||||||
|
sandboxId={data.id}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,7 +10,7 @@ export default function SharedUser({
|
|||||||
user,
|
user,
|
||||||
sandboxId,
|
sandboxId,
|
||||||
}: {
|
}: {
|
||||||
user: { id: string; name: string }
|
user: { id: string; name: string; avatarUrl: string }
|
||||||
sandboxId: string
|
sandboxId: string
|
||||||
}) {
|
}) {
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
@ -24,7 +24,7 @@ export default function SharedUser({
|
|||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Avatar name={user.name} className="mr-2" />
|
<Avatar name={user.name} avatarUrl={user.avatarUrl} className="mr-2" />
|
||||||
{user.name}
|
{user.name}
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
|
@ -1,23 +1,42 @@
|
|||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
import Image from "next/image"
|
||||||
|
|
||||||
export default function Avatar({
|
export default function Avatar({
|
||||||
name,
|
name,
|
||||||
|
avatarUrl,
|
||||||
className,
|
className,
|
||||||
}: {
|
}: {
|
||||||
name: string
|
name: string
|
||||||
|
avatarUrl?: string | null
|
||||||
className?: string
|
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 (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
className,
|
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
|
{avatarUrl ? (
|
||||||
.split(" ")
|
<Image
|
||||||
.slice(0, 2)
|
src={avatarUrl}
|
||||||
.map((letter) => letter[0].toUpperCase())}
|
alt={name || "User"}
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
className="w-full h-full object-cover"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
initials
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import { User } from "@/lib/types"
|
|||||||
import { useClerk } from "@clerk/nextjs"
|
import { useClerk } from "@clerk/nextjs"
|
||||||
import { LogOut, Sparkles } from "lucide-react"
|
import { LogOut, Sparkles } from "lucide-react"
|
||||||
import { useRouter } from "next/navigation"
|
import { useRouter } from "next/navigation"
|
||||||
|
import Avatar from "./avatar"
|
||||||
|
|
||||||
export default function UserButton({ userData }: { userData: User }) {
|
export default function UserButton({ userData }: { userData: User }) {
|
||||||
if (!userData) return null
|
if (!userData) return null
|
||||||
@ -21,13 +22,7 @@ export default function UserButton({ userData }: { userData: User }) {
|
|||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger>
|
<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">
|
<Avatar name={userData.name} avatarUrl={userData.avatarUrl} />
|
||||||
{userData.name &&
|
|
||||||
userData.name
|
|
||||||
.split(" ")
|
|
||||||
.slice(0, 2)
|
|
||||||
.map((name) => name[0].toUpperCase())}
|
|
||||||
</div>
|
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent className="w-48" align="end">
|
<DropdownMenuContent className="w-48" align="end">
|
||||||
<div className="py-1.5 px-2 w-full">
|
<div className="py-1.5 px-2 w-full">
|
||||||
|
@ -5,6 +5,12 @@ const nextConfig = {
|
|||||||
{
|
{
|
||||||
hostname: "cdn.simpleicons.org",
|
hostname: "cdn.simpleicons.org",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
hostname: "img.clerk.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hostname: "images.clerk.dev",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user