Clean up avatar shape code: remove magic strings, type casts, and inconsistencies
- avatar.tsx: consolidate to single isEmojiShape boolean, fix AvatarFallback using same logic as Avatar - avatarShape.ts: accept NostrMetadata directly in getAvatarShape (no cast needed) - Remove 'as Record<string, unknown>' casts from all ~50 call sites - Replace 'circle' magic string with empty string in form defaults and parseShape - Use isValidAvatarShape instead of string comparison in save logic - ProfileCard: extract IIFE overlay style into useMemo, use isEmojiShape throughout
This commit is contained in:
@@ -156,7 +156,7 @@ export function AddMembersDialog({ open, onOpenChange, listId, listPubkeys }: Ad
|
||||
onClick={() => handleAdd(profile)}
|
||||
onMouseEnter={() => setSelectedIdx(idx)}
|
||||
>
|
||||
<Avatar shape={getAvatarShape(profile.metadata as Record<string, unknown>)} className="size-9 shrink-0">
|
||||
<Avatar shape={getAvatarShape(profile.metadata)} className="size-9 shrink-0">
|
||||
<AvatarImage src={profile.metadata.picture} alt={name} />
|
||||
<AvatarFallback className="bg-primary/20 text-primary text-xs">
|
||||
{name[0]?.toUpperCase()}
|
||||
|
||||
@@ -32,7 +32,7 @@ export function BadgeDetailContent({ event }: { event: NostrEvent }) {
|
||||
|
||||
const author = useAuthor(event.pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = metadata?.name || genUserName(event.pubkey);
|
||||
const npub = useMemo(() => nip19.npubEncode(event.pubkey), [event.pubkey]);
|
||||
|
||||
@@ -205,7 +205,7 @@ export function BadgeDetailContent({ event }: { event: NostrEvent }) {
|
||||
function AwardeeCard({ pubkey, metadata }: { pubkey: string; metadata?: NostrMetadata }) {
|
||||
const displayName = metadata?.name || metadata?.display_name || genUserName(pubkey);
|
||||
const about = metadata?.about;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const profileUrl = useProfileUrl(pubkey, metadata);
|
||||
|
||||
return (
|
||||
|
||||
@@ -59,7 +59,7 @@ export function BookFeedItem({ event, className }: BookFeedItemProps) {
|
||||
const { user } = useCurrentUser();
|
||||
const author = useAuthor(event.pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = getDisplayName(metadata, event.pubkey);
|
||||
const profileUrl = useProfileUrl(event.pubkey, metadata);
|
||||
const { data: stats } = useEventStats(event.id);
|
||||
|
||||
@@ -121,7 +121,7 @@ function roleSort(a: string, b: string): number {
|
||||
function PersonRow({ pubkey, label, size = 'md' }: { pubkey: string; label?: string; size?: 'sm' | 'md' }) {
|
||||
const { data } = useAuthor(pubkey);
|
||||
const metadata: NostrMetadata | undefined = data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const name = metadata?.display_name || metadata?.name || genUserName(pubkey);
|
||||
const profileUrl = useProfileUrl(pubkey, metadata);
|
||||
const avatarCls = size === 'sm' ? 'size-8' : 'size-11';
|
||||
|
||||
@@ -64,7 +64,7 @@ export function useEventComments(event: NostrEvent | undefined) {
|
||||
function CommentRow({ event }: { event: NostrEvent }) {
|
||||
const author = useAuthor(event.pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = getDisplayName(metadata, event.pubkey);
|
||||
const profileUrl = useProfileUrl(event.pubkey, metadata);
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ function parseCommunityEvent(event: NostrEvent) {
|
||||
function ModeratorRow({ pubkey }: { pubkey: string }) {
|
||||
const { data } = useAuthor(pubkey);
|
||||
const metadata: NostrMetadata | undefined = data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const name = metadata?.display_name || metadata?.name || genUserName(pubkey);
|
||||
const profileUrl = useProfileUrl(pubkey, metadata);
|
||||
|
||||
@@ -85,7 +85,7 @@ export function CommunityContent({ event }: { event: NostrEvent }) {
|
||||
// Owner
|
||||
const ownerAuthor = useAuthor(event.pubkey);
|
||||
const ownerMetadata = ownerAuthor.data?.metadata;
|
||||
const ownerAvatarShape = getAvatarShape(ownerMetadata as Record<string, unknown>);
|
||||
const ownerAvatarShape = getAvatarShape(ownerMetadata);
|
||||
const ownerName = ownerMetadata?.display_name || ownerMetadata?.name || genUserName(event.pubkey);
|
||||
const ownerProfileUrl = useProfileUrl(event.pubkey, ownerMetadata);
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@ export function ComposeBox({
|
||||
initialContent = '',
|
||||
}: ComposeBoxProps) {
|
||||
const { user, metadata, isLoading: isProfileLoading } = useCurrentUser();
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const userProfileUrl = useProfileUrl(user?.pubkey ?? '', metadata);
|
||||
const { mutateAsync: createEvent, isPending } = useNostrPublish();
|
||||
const { mutateAsync: postComment, isPending: isCommentPending } = usePostComment();
|
||||
|
||||
@@ -936,7 +936,7 @@ export function MuteSettingsInternals() {
|
||||
function MutedUserProfile({ pubkey }: { pubkey: string }) {
|
||||
const author = useAuthor(pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = metadata?.name ?? genUserName(pubkey);
|
||||
|
||||
if (author.isLoading) {
|
||||
|
||||
@@ -85,12 +85,12 @@ export const EditProfileForm: React.FC<EditProfileFormProps> = ({ onValuesChange
|
||||
|
||||
// Parse existing shape from raw event content
|
||||
const parseShape = (): string => {
|
||||
if (!event) return 'circle';
|
||||
if (!event) return '';
|
||||
try {
|
||||
const parsed = JSON.parse(event.content);
|
||||
if (isValidAvatarShape(parsed.shape)) return parsed.shape;
|
||||
} catch { /* ignore */ }
|
||||
return 'circle';
|
||||
return '';
|
||||
};
|
||||
|
||||
// Initialize the form with default values
|
||||
@@ -106,7 +106,7 @@ export const EditProfileForm: React.FC<EditProfileFormProps> = ({ onValuesChange
|
||||
lud16: '',
|
||||
bot: false,
|
||||
fields: [],
|
||||
shape: 'circle',
|
||||
shape: '',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -219,8 +219,8 @@ export const EditProfileForm: React.FC<EditProfileFormProps> = ({ onValuesChange
|
||||
// Combine existing metadata with new values
|
||||
const data: Record<string, unknown> = { ...metadata, ...standardMetadata };
|
||||
|
||||
// Add shape only if non-default
|
||||
if (shape && shape !== 'circle') {
|
||||
// Add shape only if set (an emoji string)
|
||||
if (shape && isValidAvatarShape(shape)) {
|
||||
data.shape = shape;
|
||||
} else {
|
||||
delete data.shape;
|
||||
|
||||
@@ -86,7 +86,7 @@ function EmbeddedNaddrCard({ event, className }: { event: NostrEvent; className?
|
||||
const navigate = useNavigate();
|
||||
const author = useAuthor(event.pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = metadata?.name || genUserName(event.pubkey);
|
||||
const npub = useMemo(() => nip19.npubEncode(event.pubkey), [event.pubkey]);
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ function EmbeddedNoteCard({
|
||||
const author = useAuthor(event.pubkey);
|
||||
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = metadata?.name || genUserName(event.pubkey);
|
||||
const profileUrl = useProfileUrl(event.pubkey, metadata);
|
||||
const neventId = useMemo(
|
||||
|
||||
@@ -525,7 +525,7 @@ export function CommunityPreview({ addr }: { addr: { kind: number; pubkey: strin
|
||||
export function ProfilePreview({ pubkey }: { pubkey: string }) {
|
||||
const author = useAuthor(pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = metadata?.name ?? genUserName(pubkey);
|
||||
const profileUrl = useProfileUrl(pubkey, metadata);
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ export function FollowPackContent({ event }: { event: NostrEvent }) {
|
||||
{previewPubkeys.map((pk) => {
|
||||
const member = membersMap?.get(pk);
|
||||
const name = member?.metadata?.name || genUserName(pk);
|
||||
const shape = getAvatarShape(member?.metadata as Record<string, unknown>);
|
||||
const shape = getAvatarShape(member?.metadata);
|
||||
return (
|
||||
<Avatar key={pk} shape={shape} className="size-7">
|
||||
<AvatarImage src={member?.metadata?.picture} alt={name} />
|
||||
|
||||
@@ -43,7 +43,7 @@ export function FollowPackDetailContent({ event }: { event: NostrEvent }) {
|
||||
|
||||
const author = useAuthor(event.pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = metadata?.name || genUserName(event.pubkey);
|
||||
const npub = useMemo(() => nip19.npubEncode(event.pubkey), [event.pubkey]);
|
||||
|
||||
@@ -270,7 +270,7 @@ function MemberCard({
|
||||
const npub = useMemo(() => nip19.npubEncode(pubkey), [pubkey]);
|
||||
const displayName = metadata?.name || metadata?.display_name || genUserName(pubkey);
|
||||
const about = metadata?.about;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const { follow, unfollow, isPending } = useFollowActions();
|
||||
|
||||
const handleFollowToggle = useCallback(
|
||||
|
||||
@@ -240,7 +240,7 @@ function ZapsTab({ zaps }: { zaps: ZapEntry[] }) {
|
||||
function ReactionRow({ entry }: { entry: ReactionEntry }) {
|
||||
const author = useAuthor(entry.pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = metadata?.name || genUserName(entry.pubkey);
|
||||
const nevent = useMemo(() => nip19.neventEncode({ id: entry.eventId, author: entry.pubkey }), [entry.eventId, entry.pubkey]);
|
||||
|
||||
@@ -278,7 +278,7 @@ function ReactionRow({ entry }: { entry: ReactionEntry }) {
|
||||
function UserRow({ pubkey, subtitle }: { pubkey: string; subtitle?: string }) {
|
||||
const author = useAuthor(pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = metadata?.name || genUserName(pubkey);
|
||||
const npub = useMemo(() => nip19.npubEncode(pubkey), [pubkey]);
|
||||
|
||||
@@ -316,7 +316,7 @@ function UserRow({ pubkey, subtitle }: { pubkey: string; subtitle?: string }) {
|
||||
function ZapRow({ zap }: { zap: ZapEntry }) {
|
||||
const author = useAuthor(zap.senderPubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = metadata?.name || genUserName(zap.senderPubkey);
|
||||
const npub = useMemo(() => nip19.npubEncode(zap.senderPubkey), [zap.senderPubkey]);
|
||||
|
||||
@@ -360,7 +360,7 @@ function ZapRow({ zap }: { zap: ZapEntry }) {
|
||||
function QuoteRow({ quote }: { quote: QuoteEntry }) {
|
||||
const author = useAuthor(quote.pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = metadata?.name || genUserName(quote.pubkey);
|
||||
const nevent = useMemo(() => nip19.neventEncode({ id: quote.eventId, author: quote.pubkey }), [quote.eventId, quote.pubkey]);
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ export function LeftSidebar() {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const { user, metadata, event: currentUserEvent, isLoading: isProfileLoading } = useCurrentUser();
|
||||
const currentUserAvatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const currentUserAvatarShape = getAvatarShape(metadata);
|
||||
const { currentUser, otherUsers, setLogin } = useLoggedInAccounts();
|
||||
const { logout } = useLoginActions();
|
||||
const { theme, setTheme, applyCustomTheme, customTheme } = useTheme();
|
||||
@@ -311,7 +311,7 @@ export function LeftSidebar() {
|
||||
<div className="border-b border-border">
|
||||
{otherUsers.map((account) => (
|
||||
<button key={account.id} onClick={() => { setLogin(account.id); setAccountPopoverOpen(false); }} className="flex items-center gap-3 w-full px-4 py-3 hover:bg-secondary/60 transition-colors">
|
||||
<Avatar shape={getAvatarShape(account.metadata as Record<string, unknown>)} className="size-9 shrink-0">
|
||||
<Avatar shape={getAvatarShape(account.metadata)} className="size-9 shrink-0">
|
||||
<AvatarImage src={account.metadata.picture} alt={getDisplayName(account)} />
|
||||
<AvatarFallback className="bg-primary/20 text-primary text-xs">{getDisplayName(account).charAt(0).toUpperCase()}</AvatarFallback>
|
||||
</Avatar>
|
||||
|
||||
@@ -195,7 +195,7 @@ export function LiveStreamChat({ aTag, className }: LiveStreamChatProps) {
|
||||
function ChatMessage({ event }: { event: NostrEvent }) {
|
||||
const author = useAuthor(event.pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = getDisplayName(metadata, event.pubkey);
|
||||
const profileUrl = useProfileUrl(event.pubkey, metadata);
|
||||
|
||||
|
||||
@@ -284,7 +284,7 @@ export function LiveStreamPage({ event }: LiveStreamPageProps) {
|
||||
function StreamAuthorRow({ event, participants }: { event: NostrEvent; participants: Participant[] }) {
|
||||
const author = useAuthor(event.pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = getDisplayName(metadata, event.pubkey);
|
||||
const profileUrl = useProfileUrl(event.pubkey, metadata);
|
||||
|
||||
@@ -367,7 +367,7 @@ function ZapButton({ event }: { event: NostrEvent }) {
|
||||
function ParticipantRow({ pubkey, role }: { pubkey: string; role?: string }) {
|
||||
const author = useAuthor(pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = getDisplayName(metadata, pubkey);
|
||||
const profileUrl = useProfileUrl(pubkey, metadata);
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@ interface FlatEntry {
|
||||
function AudioThumb({ pubkey }: { pubkey: string }) {
|
||||
const author = useAuthor(pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const name = metadata?.name ?? genUserName(pubkey);
|
||||
|
||||
return (
|
||||
|
||||
@@ -304,7 +304,7 @@ function MentionItem({
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
>
|
||||
<div className="relative shrink-0">
|
||||
<Avatar shape={getAvatarShape(metadata as Record<string, unknown>)} className="size-8">
|
||||
<Avatar shape={getAvatarShape(metadata)} className="size-8">
|
||||
<AvatarImage src={metadata.picture} alt={displayName} />
|
||||
<AvatarFallback className="bg-primary/20 text-primary text-xs">
|
||||
{displayName[0]?.toUpperCase() || '?'}
|
||||
|
||||
@@ -37,7 +37,7 @@ export function MobileDrawer({ open, onOpenChange }: MobileDrawerProps) {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const { user, metadata, event: currentUserEvent } = useCurrentUser();
|
||||
const currentUserAvatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const currentUserAvatarShape = getAvatarShape(metadata);
|
||||
const userProfileUrl = useProfileUrl(user?.pubkey ?? '', metadata);
|
||||
const { logout } = useLoginActions();
|
||||
const { otherUsers, setLogin } = useLoggedInAccounts();
|
||||
@@ -139,7 +139,7 @@ export function MobileDrawer({ open, onOpenChange }: MobileDrawerProps) {
|
||||
onClick={() => { setLogin(account.id); handleClose(); }}
|
||||
className="flex items-center gap-3 w-full px-3 py-2 hover:bg-secondary/60 transition-colors"
|
||||
>
|
||||
<Avatar shape={getAvatarShape(account.metadata as Record<string, unknown>)} className="size-7 shrink-0">
|
||||
<Avatar shape={getAvatarShape(account.metadata)} className="size-7 shrink-0">
|
||||
<AvatarImage src={account.metadata.picture} alt={getDisplayName(account)} />
|
||||
<AvatarFallback className="bg-primary/20 text-primary text-xs">
|
||||
{getDisplayName(account).charAt(0).toUpperCase()}
|
||||
|
||||
@@ -268,7 +268,7 @@ function SearchProfileItem({
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
>
|
||||
<div className="relative shrink-0">
|
||||
<Avatar shape={getAvatarShape(metadata as Record<string, unknown>)} className="size-9">
|
||||
<Avatar shape={getAvatarShape(metadata)} className="size-9">
|
||||
<AvatarImage src={metadata.picture} alt={displayName} />
|
||||
<AvatarFallback className="bg-primary/20 text-primary text-sm">
|
||||
{displayName[0]?.toUpperCase() || '?'}
|
||||
|
||||
@@ -62,7 +62,7 @@ function TrackDetail({ event }: { event: NostrEvent }) {
|
||||
|
||||
const author = useAuthor(event.pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = getDisplayName(metadata, event.pubkey);
|
||||
const profileUrl = useProfileUrl(event.pubkey, metadata);
|
||||
const { user } = useCurrentUser();
|
||||
@@ -290,7 +290,7 @@ function PlaylistDetail({ event }: { event: NostrEvent }) {
|
||||
const parsed = useMemo(() => parseMusicPlaylist(event), [event]);
|
||||
const author = useAuthor(event.pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = getDisplayName(metadata, event.pubkey);
|
||||
const profileUrl = useProfileUrl(event.pubkey, metadata);
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ export function NoteCard({
|
||||
const author = useAuthor(event.pubkey);
|
||||
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = getDisplayName(metadata, event.pubkey);
|
||||
const nip05 = metadata?.nip05;
|
||||
const { data: nip05Verified, isPending: nip05Pending } = useNip05Verify(
|
||||
|
||||
@@ -236,7 +236,7 @@ function NoteMoreMenuContent({ event, open, onOpenChange, onReport, onMention, o
|
||||
const isOwnPost = user?.pubkey === event.pubkey;
|
||||
const author = useAuthor(event.pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = metadata?.name || genUserName(event.pubkey);
|
||||
const { addMute, removeMute, isMuted } = useMuteList();
|
||||
const userMuted = isMuted('pubkey', event.pubkey);
|
||||
|
||||
@@ -39,7 +39,7 @@ export function PhotoBottomBar({ event }: PhotoBottomBarProps) {
|
||||
const { user } = useCurrentUser();
|
||||
const author = useAuthor(event.pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = getDisplayName(metadata, event.pubkey) ?? genUserName(event.pubkey);
|
||||
const profileUrl = useProfileUrl(event.pubkey, metadata);
|
||||
const { data: stats } = useEventStats(event.id);
|
||||
|
||||
@@ -61,7 +61,7 @@ function EpisodeDetail({ event }: { event: NostrEvent }) {
|
||||
|
||||
const author = useAuthor(event.pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = getDisplayName(metadata, event.pubkey);
|
||||
const profileUrl = useProfileUrl(event.pubkey, metadata);
|
||||
const { user } = useCurrentUser();
|
||||
@@ -291,7 +291,7 @@ function TrailerDetail({ event }: { event: NostrEvent }) {
|
||||
|
||||
const author = useAuthor(event.pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = getDisplayName(metadata, event.pubkey);
|
||||
const profileUrl = useProfileUrl(event.pubkey, metadata);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import type { NostrMetadata } from '@nostrify/nostrify';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||
import { type AvatarShape, isValidAvatarShape, isEmoji, getEmojiMaskUrl } from '@/lib/avatarShape';
|
||||
@@ -120,8 +120,26 @@ export function ProfileCard({
|
||||
const patch = (key: keyof NostrMetadata) => (v: string) => onChange?.({ [key]: v });
|
||||
|
||||
// Read shape from metadata (it's a custom property passed through the loose schema)
|
||||
const rawShape = (metadata as Record<string, unknown>).shape;
|
||||
const rawShape = metadata.shape;
|
||||
const shape: AvatarShape | undefined = isValidAvatarShape(rawShape) ? rawShape : undefined;
|
||||
const isEmojiShape = !!shape && isEmoji(shape);
|
||||
|
||||
// Memoized mask style for the hover overlay on emoji-shaped avatars
|
||||
const overlayMaskStyle = useMemo<React.CSSProperties | undefined>(() => {
|
||||
if (!isEmojiShape) return undefined;
|
||||
const maskUrl = getEmojiMaskUrl(shape);
|
||||
if (!maskUrl) return undefined;
|
||||
return {
|
||||
WebkitMaskImage: `url(${maskUrl})`,
|
||||
maskImage: `url(${maskUrl})`,
|
||||
WebkitMaskSize: 'contain',
|
||||
maskSize: 'contain' as string,
|
||||
WebkitMaskRepeat: 'no-repeat',
|
||||
maskRepeat: 'no-repeat' as string,
|
||||
WebkitMaskPosition: 'center',
|
||||
maskPosition: 'center' as string,
|
||||
};
|
||||
}, [isEmojiShape, shape]);
|
||||
|
||||
const nip05 = metadata.nip05;
|
||||
const nip05Domain = nip05 ? getNip05Domain(nip05) : undefined;
|
||||
@@ -174,7 +192,7 @@ export function ProfileCard({
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button type="button" className="relative shrink-0 cursor-pointer group outline-none">
|
||||
<Avatar shape={shape} className={cn("size-24 shadow-sm", shape && isEmoji(shape) ? "ring-4 ring-background" : "border-4 border-background")}>
|
||||
<Avatar shape={shape} className={cn("size-24 shadow-sm", isEmojiShape ? "ring-4 ring-background" : "border-4 border-background")}>
|
||||
<AvatarImage src={metadata.picture} alt={displayName} className="object-cover" />
|
||||
<AvatarFallback className="bg-primary/20 text-primary text-2xl font-bold">
|
||||
{metadata.picture ? initial : <Plus className="size-8 text-muted-foreground" strokeWidth={4} />}
|
||||
@@ -183,24 +201,9 @@ export function ProfileCard({
|
||||
<div
|
||||
className={cn(
|
||||
'absolute inset-0 bg-black/0 group-hover:bg-black/45 transition-colors flex items-center justify-center',
|
||||
(!shape || shape === 'circle') && 'rounded-full',
|
||||
!isEmojiShape && 'rounded-full',
|
||||
)}
|
||||
style={(() => {
|
||||
if (shape && isEmoji(shape)) {
|
||||
const maskUrl = getEmojiMaskUrl(shape);
|
||||
if (maskUrl) return {
|
||||
WebkitMaskImage: `url(${maskUrl})`,
|
||||
maskImage: `url(${maskUrl})`,
|
||||
WebkitMaskSize: 'contain',
|
||||
maskSize: 'contain' as string,
|
||||
WebkitMaskRepeat: 'no-repeat',
|
||||
maskRepeat: 'no-repeat' as string,
|
||||
WebkitMaskPosition: 'center',
|
||||
maskPosition: 'center' as string,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
})()}
|
||||
style={overlayMaskStyle}
|
||||
>
|
||||
<Camera className="size-6 text-white opacity-0 group-hover:opacity-100 transition-opacity drop-shadow" />
|
||||
</div>
|
||||
@@ -239,14 +242,14 @@ export function ProfileCard({
|
||||
setEmojiPickerOpen(false);
|
||||
}
|
||||
}} />
|
||||
{shape && isEmoji(shape) && (
|
||||
{isEmojiShape && (
|
||||
<div className="px-4 pb-4 pt-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="w-full text-destructive hover:text-destructive"
|
||||
onClick={() => { onAvatarShape?.('circle'); setEmojiPickerOpen(false); }}
|
||||
onClick={() => { onAvatarShape?.(''); setEmojiPickerOpen(false); }}
|
||||
>
|
||||
<XIcon className="size-3.5 mr-1.5" />
|
||||
Remove avatar shape
|
||||
@@ -258,7 +261,7 @@ export function ProfileCard({
|
||||
</>
|
||||
) : (
|
||||
<div className="relative shrink-0">
|
||||
<Avatar shape={shape} className={cn("size-24 shadow-sm", shape && isEmoji(shape) ? "ring-4 ring-background" : "border-4 border-background")}>
|
||||
<Avatar shape={shape} className={cn("size-24 shadow-sm", isEmojiShape ? "ring-4 ring-background" : "border-4 border-background")}>
|
||||
<AvatarImage src={metadata.picture} alt={displayName} className="object-cover" />
|
||||
<AvatarFallback className="bg-primary/20 text-primary text-2xl font-bold">
|
||||
{initial}
|
||||
|
||||
@@ -30,7 +30,7 @@ function ProfileHoverCardBody({ pubkey }: { pubkey: string }) {
|
||||
const queryClient = useQueryClient();
|
||||
const author = useAuthor(pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = metadata?.name ?? genUserName(pubkey);
|
||||
const profileUrl = useProfileUrl(pubkey, metadata);
|
||||
const nip05 = metadata?.nip05;
|
||||
|
||||
@@ -387,7 +387,7 @@ function ProfileItem({
|
||||
onMouseDown={(e) => e.preventDefault()} // Prevent input blur
|
||||
>
|
||||
<div className="relative shrink-0">
|
||||
<Avatar shape={getAvatarShape(metadata as Record<string, unknown>)} className="size-10">
|
||||
<Avatar shape={getAvatarShape(metadata)} className="size-10">
|
||||
<AvatarImage src={metadata.picture} alt={displayName} />
|
||||
<AvatarFallback className="bg-primary/20 text-primary text-sm">
|
||||
{displayName[0]?.toUpperCase() || '?'}
|
||||
|
||||
@@ -23,7 +23,7 @@ const spacingClasses: Record<AvatarSize, string> = {
|
||||
function RSVPAvatar({ pubkey, size = 'sm' }: { pubkey: string; size?: AvatarSize }) {
|
||||
const { data } = useAuthor(pubkey);
|
||||
const metadata: NostrMetadata | undefined = data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
|
||||
const displayName = metadata?.display_name || metadata?.name || genUserName(pubkey);
|
||||
const initial = displayName.charAt(0).toUpperCase();
|
||||
|
||||
@@ -162,7 +162,7 @@ function EmbeddedPost({ event }: { event: NostrEvent }) {
|
||||
function EmbeddedNote({ event }: { event: NostrEvent }) {
|
||||
const author = useAuthor(event.pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = metadata?.name || genUserName(event.pubkey);
|
||||
const nip05 = metadata?.nip05;
|
||||
const npub = useMemo(() => nip19.npubEncode(event.pubkey), [event.pubkey]);
|
||||
|
||||
@@ -244,7 +244,7 @@ export function RightSidebar() {
|
||||
function HotPostCard({ event }: { event: NostrEvent }) {
|
||||
const author = useAuthor(event.pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = metadata?.name || genUserName(event.pubkey);
|
||||
const encodedId = useMemo(() => nip19.neventEncode({ id: event.id, author: event.pubkey }), [event]);
|
||||
const { onClick: openPost, onAuxClick } = useOpenPost(`/${encodedId}`);
|
||||
@@ -291,7 +291,7 @@ function LatestAccountCard({ event, onDismiss }: { event: NostrEvent; onDismiss:
|
||||
}
|
||||
|
||||
const displayName = metadata.name || genUserName(event.pubkey);
|
||||
const latestAvatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const latestAvatarShape = getAvatarShape(metadata);
|
||||
const npub = useMemo(() => nip19.npubEncode(event.pubkey), [event.pubkey]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -159,7 +159,7 @@ export function TeamSoapboxCard({ className }: { className?: string }) {
|
||||
{previewPubkeys.map((pk) => {
|
||||
const member = membersMap?.get(pk);
|
||||
const name = member?.metadata?.name || genUserName(pk);
|
||||
const shape = getAvatarShape(member?.metadata as Record<string, unknown>);
|
||||
const shape = getAvatarShape(member?.metadata);
|
||||
return (
|
||||
<Avatar key={pk} shape={shape} className="size-8 ring-2 ring-background">
|
||||
<AvatarImage src={member?.metadata?.picture} alt={name} />
|
||||
|
||||
@@ -29,7 +29,7 @@ export function ThemeUpdateCard({ event }: ThemeUpdateCardProps) {
|
||||
const isOwn = user?.pubkey === event.pubkey;
|
||||
const author = useAuthor(event.pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const authorEvent = author.data?.event;
|
||||
const displayName = metadata?.name ?? genUserName(event.pubkey);
|
||||
const profileUrl = useProfileUrl(event.pubkey, metadata);
|
||||
|
||||
@@ -46,7 +46,7 @@ export function AccountSwitcher({ onAddAccountClick }: AccountSwitcherProps) {
|
||||
{isLoading ? (
|
||||
<Skeleton className='w-10 h-10 rounded-full shrink-0' />
|
||||
) : (
|
||||
<Avatar shape={getAvatarShape(currentUser.metadata as Record<string, unknown>)} className='w-10 h-10'>
|
||||
<Avatar shape={getAvatarShape(currentUser.metadata)} className='w-10 h-10'>
|
||||
<AvatarImage src={currentUser.metadata.picture} alt={getDisplayName(currentUser)} />
|
||||
<AvatarFallback>{getDisplayName(currentUser).charAt(0)}</AvatarFallback>
|
||||
</Avatar>
|
||||
@@ -69,7 +69,7 @@ export function AccountSwitcher({ onAddAccountClick }: AccountSwitcherProps) {
|
||||
onClick={() => setLogin(user.id)}
|
||||
className='flex items-center gap-2 cursor-pointer p-2 rounded-md'
|
||||
>
|
||||
<Avatar shape={getAvatarShape(user.metadata as Record<string, unknown>)} className='w-8 h-8'>
|
||||
<Avatar shape={getAvatarShape(user.metadata)} className='w-8 h-8'>
|
||||
<AvatarImage src={user.metadata.picture} alt={getDisplayName(user)} />
|
||||
<AvatarFallback>{getDisplayName(user)?.charAt(0) || <UserIcon />}</AvatarFallback>
|
||||
</Avatar>
|
||||
|
||||
@@ -162,7 +162,7 @@ MessageBubble.displayName = 'MessageBubble';
|
||||
const ChatHeader = ({ pubkey, onBack }: { pubkey: string; onBack?: () => void }) => {
|
||||
const author = useAuthor(pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
|
||||
const displayName = metadata?.name || genUserName(pubkey);
|
||||
const avatarUrl = metadata?.picture;
|
||||
|
||||
@@ -40,7 +40,7 @@ const ConversationItemComponent = ({
|
||||
}: ConversationItemProps) => {
|
||||
const author = useAuthor(pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
|
||||
const displayName = metadata?.name || genUserName(pubkey);
|
||||
const avatarUrl = metadata?.picture;
|
||||
|
||||
@@ -24,12 +24,11 @@ const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
|
||||
// Reset per render so stale values don't persist
|
||||
hasSrcRef.current = false
|
||||
|
||||
const isCircle = !shape || !isEmoji(shape)
|
||||
const isEmojiShape = shape ? isEmoji(shape) : false
|
||||
const isEmojiShape = !!shape && isEmoji(shape)
|
||||
|
||||
// Build inline style: mask-image for emoji shapes
|
||||
const mergedStyle = React.useMemo<React.CSSProperties>(() => {
|
||||
if (isEmojiShape && shape) {
|
||||
if (isEmojiShape) {
|
||||
const maskUrl = getEmojiMaskUrl(shape)
|
||||
if (maskUrl) {
|
||||
return {
|
||||
@@ -55,7 +54,7 @@ const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex h-10 w-10 shrink-0 overflow-hidden bg-muted",
|
||||
isCircle && "rounded-full",
|
||||
!isEmojiShape && "rounded-full",
|
||||
className
|
||||
)}
|
||||
style={mergedStyle}
|
||||
@@ -126,7 +125,7 @@ const AvatarFallback = React.forwardRef<
|
||||
const hasSrcRef = React.useContext(AvatarHasSrcContext)
|
||||
const shape = React.useContext(AvatarShapeContext)
|
||||
|
||||
const isCircle = !shape || shape === 'circle'
|
||||
const isEmojiShape = !!shape && isEmoji(shape)
|
||||
|
||||
// AvatarImage renders before AvatarFallback (DOM order), so hasSrcRef
|
||||
// is already set by the time we read it here in the same render frame.
|
||||
@@ -137,7 +136,7 @@ const AvatarFallback = React.forwardRef<
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex h-full w-full items-center justify-center",
|
||||
isCircle && "rounded-full",
|
||||
!isEmojiShape && "rounded-full",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -32,10 +32,11 @@ export function isValidAvatarShape(value: unknown): value is AvatarShape {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a valid AvatarShape from a NostrMetadata object (or any object with a `shape` property).
|
||||
* Extracts a valid AvatarShape from a metadata object (or any object with a `shape` property).
|
||||
* Accepts `NostrMetadata` directly — no type cast needed at call sites.
|
||||
* Returns `undefined` if the shape is missing or invalid (which means "circle" / default).
|
||||
*/
|
||||
export function getAvatarShape(metadata: Record<string, unknown> | undefined): AvatarShape | undefined {
|
||||
export function getAvatarShape(metadata: { [key: string]: unknown } | undefined): AvatarShape | undefined {
|
||||
const raw = metadata?.shape;
|
||||
return isValidAvatarShape(raw) ? raw : undefined;
|
||||
}
|
||||
|
||||
@@ -500,7 +500,7 @@ function BookContentTabs({ isbn, commentRoot, orderedReplies, commentsLoading }:
|
||||
function BookReviewCard({ event, review }: { event: NostrEvent; review: BookReview }) {
|
||||
const author = useAuthor(event.pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = getDisplayName(metadata, event.pubkey);
|
||||
const profileUrl = useProfileUrl(event.pubkey, metadata);
|
||||
const [showSpoiler, setShowSpoiler] = useState(false);
|
||||
|
||||
@@ -58,7 +58,7 @@ function MemberCard({ pubkey, isOwner, listId, onRemoved }: {
|
||||
}) {
|
||||
const author = useAuthor(pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = metadata?.display_name || metadata?.name || genUserName(pubkey);
|
||||
const profileUrl = useProfileUrl(pubkey, metadata);
|
||||
const { data: followData } = useFollowList();
|
||||
|
||||
@@ -421,7 +421,7 @@ function CopyableHex({ value }: { value: string }) {
|
||||
function AuthorHintRow({ pubkey }: { pubkey: string }) {
|
||||
const author = useAuthor(pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = getDisplayName(metadata, pubkey);
|
||||
const profileUrl = useProfileUrl(pubkey, metadata);
|
||||
|
||||
@@ -764,7 +764,7 @@ function PostDetailContent({ event }: { event: NostrEvent }) {
|
||||
const queryClient = useQueryClient();
|
||||
const author = useAuthor(event.pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = getDisplayName(metadata, event.pubkey);
|
||||
|
||||
// Refetch the author's profile whenever we navigate to a post by this author.
|
||||
|
||||
@@ -289,7 +289,7 @@ function MenuRow({ icon, label, onClick, destructive }: { icon: React.ReactNode;
|
||||
function FollowingUserRow({ pubkey }: { pubkey: string }) {
|
||||
const author = useAuthor(pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = metadata?.name || genUserName(pubkey);
|
||||
const npubEncoded = useMemo(() => nip19.npubEncode(pubkey), [pubkey]);
|
||||
|
||||
@@ -997,7 +997,7 @@ export function ProfilePage() {
|
||||
// Kind 0 — resolved from the author cache (seeded by the feed query above).
|
||||
const author = useAuthor(pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const profileStatus = useUserStatus(pubkey);
|
||||
|
||||
// Refetch the author's profile whenever we navigate to this profile page.
|
||||
|
||||
@@ -258,12 +258,12 @@ export function ProfileSettings() {
|
||||
};
|
||||
|
||||
const parseShape = (): string => {
|
||||
if (!event) return 'circle';
|
||||
if (!event) return '';
|
||||
try {
|
||||
const parsed = JSON.parse(event.content);
|
||||
if (isValidAvatarShape(parsed.shape)) return parsed.shape;
|
||||
} catch { /* ignore */ }
|
||||
return 'circle';
|
||||
return '';
|
||||
};
|
||||
|
||||
const form = useForm<FormValues>({
|
||||
@@ -271,7 +271,7 @@ export function ProfileSettings() {
|
||||
defaultValues: {
|
||||
name: '', about: '', picture: '', banner: '',
|
||||
website: '', nip05: '', lud16: '', bot: false, fields: [],
|
||||
shape: 'circle',
|
||||
shape: '',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -401,8 +401,8 @@ export function ProfileSettings() {
|
||||
const { fields: customFields, shape, ...standardMetadata } = values;
|
||||
const data: Record<string, unknown> = { ...metadata, ...standardMetadata };
|
||||
|
||||
// Add shape only if non-default
|
||||
if (shape && shape !== 'circle') {
|
||||
// Add shape only if set (an emoji string)
|
||||
if (shape && isValidAvatarShape(shape)) {
|
||||
data.shape = shape;
|
||||
} else {
|
||||
delete data.shape;
|
||||
|
||||
@@ -825,7 +825,7 @@ function AccountItem({ profile, isFollowed }: { profile: { pubkey: string; metad
|
||||
const npub = useMemo(() => nip19.npubEncode(profile.pubkey), [profile.pubkey]);
|
||||
const metadata = profile.metadata as { name?: string; nip05?: string; picture?: string; about?: string; bot?: boolean };
|
||||
const displayName = metadata?.name || genUserName(profile.pubkey);
|
||||
const profileAvatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const profileAvatarShape = getAvatarShape(metadata);
|
||||
const tags = profile.event?.tags ?? [];
|
||||
|
||||
return (
|
||||
@@ -889,7 +889,7 @@ function FollowsList() {
|
||||
function FollowItem({ pubkey }: { pubkey: string }) {
|
||||
const author = useAuthor(pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const npub = useMemo(() => nip19.npubEncode(pubkey), [pubkey]);
|
||||
const displayName = metadata?.name || genUserName(pubkey);
|
||||
const tags = author.data?.event?.tags ?? [];
|
||||
|
||||
@@ -200,7 +200,7 @@ function StreamCard({ event }: { event: NostrEvent }) {
|
||||
function StreamCardAuthor({ pubkey }: { pubkey: string }) {
|
||||
const author = useAuthor(pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = getDisplayName(metadata, pubkey);
|
||||
const profileUrl = useProfileUrl(pubkey, metadata);
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ import type { UserList } from '@/hooks/useUserLists';
|
||||
function MiniAvatar({ pubkey }: { pubkey: string }) {
|
||||
const author = useAuthor(pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = metadata?.name ?? genUserName(pubkey);
|
||||
return (
|
||||
<Avatar shape={avatarShape} className="size-7 border-2 border-background shrink-0">
|
||||
|
||||
@@ -224,7 +224,7 @@ function VideoGridCard({ event }: { event: NostrEvent }) {
|
||||
|
||||
const author = useAuthor(event.pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = getDisplayName(metadata, event.pubkey);
|
||||
const profileUrl = useProfileUrl(event.pubkey, metadata);
|
||||
|
||||
|
||||
@@ -279,7 +279,7 @@ export function VineCard({ event, isActive, isNearActive, onCommentClick }: Vine
|
||||
const { user } = useCurrentUser();
|
||||
const author = useAuthor(event.pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const avatarShape = getAvatarShape(metadata as Record<string, unknown>);
|
||||
const avatarShape = getAvatarShape(metadata);
|
||||
const displayName = getDisplayName(metadata, event.pubkey);
|
||||
const profileUrl = useProfileUrl(event.pubkey, metadata);
|
||||
const { data: stats } = useEventStats(event.id);
|
||||
|
||||
Reference in New Issue
Block a user