Use generated username fallback in other parts of the UI

This commit is contained in:
Alex Gleason 2025-06-01 11:11:13 -05:00
parent 9cb2795496
commit 130300f1c1
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7
3 changed files with 15 additions and 40 deletions

View File

@ -122,14 +122,15 @@ The data may be transformed into a more appropriate format if needed, and multip
To display profile data for a user by their Nostr pubkey (such as an event author), use the `useAuthor` hook. To display profile data for a user by their Nostr pubkey (such as an event author), use the `useAuthor` hook.
```tsx ```tsx
import { NostrEvent, NostrMetadata } from '@nostrify/nostrify'; import type { NostrEvent, NostrMetadata } from '@nostrify/nostrify';
import { useAuthor } from '@/hooks/useAuthor'; import { useAuthor } from '@/hooks/useAuthor';
import { genUserName } from '@/lib/genUserName';
function Post({ event }: { event: NostrEvent }) { function Post({ event }: { event: NostrEvent }) {
const author = useAuthor(event.pubkey); const author = useAuthor(event.pubkey);
const metadata: NostrMetadata | undefined = author.data?.metadata; const metadata: NostrMetadata | undefined = author.data?.metadata;
const displayName = metadata?.name || event.pubkey.slice(0, 8); const displayName = metadata?.name ?? genUserName(event.pubkey);
const profileImage = metadata?.picture; const profileImage = metadata?.picture;
// ...render elements with this data // ...render elements with this data

View File

@ -3,6 +3,7 @@ import { type NostrEvent } from '@nostrify/nostrify';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { nip19 } from 'nostr-tools'; import { nip19 } from 'nostr-tools';
import { useAuthor } from '@/hooks/useAuthor'; import { useAuthor } from '@/hooks/useAuthor';
import { genUserName } from '@/lib/genUserName';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
interface NoteContentProps { interface NoteContentProps {
@ -118,7 +119,7 @@ function NostrMention({ pubkey }: { pubkey: string }) {
const author = useAuthor(pubkey); const author = useAuthor(pubkey);
const npub = nip19.npubEncode(pubkey); const npub = nip19.npubEncode(pubkey);
const hasRealName = !!author.data?.metadata?.name; const hasRealName = !!author.data?.metadata?.name;
const displayName = author.data?.metadata?.name ?? generateDeterministicName(pubkey); const displayName = author.data?.metadata?.name ?? genUserName(pubkey);
return ( return (
<Link <Link
@ -133,34 +134,4 @@ function NostrMention({ pubkey }: { pubkey: string }) {
@{displayName} @{displayName}
</Link> </Link>
); );
}
// Generate a deterministic name based on pubkey
function generateDeterministicName(pubkey: string): string {
// Use a simple hash of the pubkey to generate consistent adjective + noun combinations
const adjectives = [
'Swift', 'Bright', 'Calm', 'Bold', 'Wise', 'Kind', 'Quick', 'Brave',
'Cool', 'Sharp', 'Clear', 'Strong', 'Smart', 'Fast', 'Keen', 'Pure',
'Noble', 'Gentle', 'Fierce', 'Steady', 'Clever', 'Proud', 'Silent', 'Wild'
];
const nouns = [
'Fox', 'Eagle', 'Wolf', 'Bear', 'Lion', 'Tiger', 'Hawk', 'Owl',
'Deer', 'Raven', 'Falcon', 'Lynx', 'Otter', 'Whale', 'Shark', 'Dolphin',
'Phoenix', 'Dragon', 'Panther', 'Jaguar', 'Cheetah', 'Leopard', 'Puma', 'Cobra'
];
// Create a simple hash from the pubkey
let hash = 0;
for (let i = 0; i < pubkey.length; i++) {
const char = pubkey.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash; // Convert to 32-bit integer
}
// Use absolute value to ensure positive index
const adjIndex = Math.abs(hash) % adjectives.length;
const nounIndex = Math.abs(hash >> 8) % nouns.length;
return `${adjectives[adjIndex]}${nouns[nounIndex]}`;
} }

View File

@ -10,7 +10,8 @@ import {
DropdownMenuTrigger, DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu.tsx'; } from '@/components/ui/dropdown-menu.tsx';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar.tsx'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar.tsx';
import { useLoggedInAccounts } from '@/hooks/useLoggedInAccounts'; import { useLoggedInAccounts, type Account } from '@/hooks/useLoggedInAccounts';
import { genUserName } from '@/lib/genUserName';
interface AccountSwitcherProps { interface AccountSwitcherProps {
onAddAccountClick: () => void; onAddAccountClick: () => void;
@ -21,16 +22,18 @@ export function AccountSwitcher({ onAddAccountClick }: AccountSwitcherProps) {
if (!currentUser) return null; if (!currentUser) return null;
const getDisplayName = (account: Account): string => account.metadata.name ?? genUserName(account.pubkey);
return ( return (
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<button className='flex items-center gap-3 p-3 rounded-full hover:bg-accent transition-all w-full text-foreground'> <button className='flex items-center gap-3 p-3 rounded-full hover:bg-accent transition-all w-full text-foreground'>
<Avatar className='w-10 h-10'> <Avatar className='w-10 h-10'>
<AvatarImage src={currentUser.metadata.picture} alt={currentUser.metadata.name} /> <AvatarImage src={currentUser.metadata.picture} alt={getDisplayName(currentUser)} />
<AvatarFallback>{currentUser.metadata.name?.charAt(0) || <UserIcon />}</AvatarFallback> <AvatarFallback>{getDisplayName(currentUser).charAt(0)}</AvatarFallback>
</Avatar> </Avatar>
<div className='flex-1 text-left hidden md:block truncate'> <div className='flex-1 text-left hidden md:block truncate'>
<p className='font-medium text-sm truncate'>{currentUser.metadata.name || currentUser.pubkey}</p> <p className='font-medium text-sm truncate'>{getDisplayName(currentUser)}</p>
</div> </div>
<ChevronDown className='w-4 h-4 text-muted-foreground' /> <ChevronDown className='w-4 h-4 text-muted-foreground' />
</button> </button>
@ -44,11 +47,11 @@ export function AccountSwitcher({ onAddAccountClick }: AccountSwitcherProps) {
className='flex items-center gap-2 cursor-pointer p-2 rounded-md' className='flex items-center gap-2 cursor-pointer p-2 rounded-md'
> >
<Avatar className='w-8 h-8'> <Avatar className='w-8 h-8'>
<AvatarImage src={user.metadata.picture} alt={user.metadata.name} /> <AvatarImage src={user.metadata.picture} alt={getDisplayName(user)} />
<AvatarFallback>{user.metadata.name?.charAt(0) || <UserIcon />}</AvatarFallback> <AvatarFallback>{getDisplayName(user)?.charAt(0) || <UserIcon />}</AvatarFallback>
</Avatar> </Avatar>
<div className='flex-1 truncate'> <div className='flex-1 truncate'>
<p className='text-sm font-medium'>{user.metadata.name || user.pubkey}</p> <p className='text-sm font-medium'>{getDisplayName(user)}</p>
</div> </div>
{user.id === currentUser.id && <div className='w-2 h-2 rounded-full bg-primary'></div>} {user.id === currentUser.id && <div className='w-2 h-2 rounded-full bg-primary'></div>}
</DropdownMenuItem> </DropdownMenuItem>