aboutsummaryrefslogtreecommitdiffstats
path: root/apps/web/components/ui/user-avatar.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/components/ui/user-avatar.tsx')
-rw-r--r--apps/web/components/ui/user-avatar.tsx52
1 files changed, 52 insertions, 0 deletions
diff --git a/apps/web/components/ui/user-avatar.tsx b/apps/web/components/ui/user-avatar.tsx
new file mode 100644
index 00000000..4ebb6ec3
--- /dev/null
+++ b/apps/web/components/ui/user-avatar.tsx
@@ -0,0 +1,52 @@
+"use client";
+
+import { useMemo } from "react";
+import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
+import { cn } from "@/lib/utils";
+
+import { getAssetUrl } from "@karakeep/shared/utils/assetUtils";
+
+interface UserAvatarProps {
+ image?: string | null;
+ name?: string | null;
+ className?: string;
+ imgClassName?: string;
+ fallbackClassName?: string;
+ fallback?: React.ReactNode;
+}
+
+const isExternalUrl = (value: string) =>
+ value.startsWith("http://") || value.startsWith("https://");
+
+export function UserAvatar({
+ image,
+ name,
+ className,
+ imgClassName,
+ fallbackClassName,
+ fallback,
+}: UserAvatarProps) {
+ const avatarUrl = useMemo(() => {
+ if (!image) {
+ return null;
+ }
+ return isExternalUrl(image) ? image : getAssetUrl(image);
+ }, [image]);
+
+ const fallbackContent = fallback ?? name?.charAt(0) ?? "U";
+
+ return (
+ <Avatar className={className}>
+ {avatarUrl && (
+ <AvatarImage
+ src={avatarUrl}
+ alt={name ?? "User"}
+ className={cn("object-cover", imgClassName)}
+ />
+ )}
+ <AvatarFallback className={cn("text-sm font-medium", fallbackClassName)}>
+ {fallbackContent}
+ </AvatarFallback>
+ </Avatar>
+ );
+}