diff --git a/src/app/[handle]/page.tsx b/src/app/[handle]/page.tsx index 47c3970..7099bda 100644 --- a/src/app/[handle]/page.tsx +++ b/src/app/[handle]/page.tsx @@ -8,6 +8,7 @@ import { PostCard } from '@/components/PostCard'; import { User, Post } from '@/lib/types'; import AutoTextarea from '@/components/AutoTextarea'; import { Rocket } from 'lucide-react'; +import { formatFullHandle } from '@/lib/utils/handle'; interface UserSummary { id: string; @@ -29,7 +30,7 @@ function UserRow({ user }: { user: UserSummary }) {
{user.displayName || user.handle}
-
@{user.handle}
+
{formatFullHandle(user.handle)}
{user.bio && (
{user.bio}
)} @@ -340,7 +341,7 @@ export default function ProfilePage() { {/* User Info */}

{user.displayName || user.handle}

-

@{user.handle}

+

{formatFullHandle(user.handle)}

{user.bio && (

{user.bio}

diff --git a/src/app/api/posts/route.ts b/src/app/api/posts/route.ts index bac0560..710d535 100644 --- a/src/app/api/posts/route.ts +++ b/src/app/api/posts/route.ts @@ -25,7 +25,7 @@ const createPostSchema = z.object({ title: z.string().optional(), description: z.string().optional(), image: z.string().url().optional().nullable(), - }).optional(), + }).optional().nullable(), }); // Create a new post diff --git a/src/app/explore/page.tsx b/src/app/explore/page.tsx index 00725a0..84dc8eb 100644 --- a/src/app/explore/page.tsx +++ b/src/app/explore/page.tsx @@ -5,6 +5,7 @@ import Link from 'next/link'; import { SearchIcon, TrendingIcon, UsersIcon } from '@/components/Icons'; import { PostCard } from '@/components/PostCard'; import { Post } from '@/lib/types'; +import { formatFullHandle } from '@/lib/utils/handle'; interface User { id: string; @@ -28,7 +29,7 @@ function UserCard({ user }: { user: User }) {
{user.displayName || user.handle}
-
@{user.handle}
+
{formatFullHandle(user.handle)}
{user.bio &&
{user.bio}
}
diff --git a/src/app/search/page.tsx b/src/app/search/page.tsx index ccef5b8..803dc89 100644 --- a/src/app/search/page.tsx +++ b/src/app/search/page.tsx @@ -3,6 +3,7 @@ import { useState, useEffect, useCallback } from 'react'; import Link from 'next/link'; import { useSearchParams, useRouter } from 'next/navigation'; +import { formatFullHandle } from '@/lib/utils/handle'; interface User { id: string; @@ -97,7 +98,7 @@ function UserCard({ user }: { user: User }) {
{user.displayName || user.handle}
-
@{user.handle}
+
{formatFullHandle(user.handle)}
{user.bio && (
{post.author?.displayName || post.author?.handle} - @{post.author?.handle} · {formatTime(post.createdAt)} + {formatFullHandle(post.author?.handle || '')} · {formatTime(post.createdAt)}
{post.content}
diff --git a/src/components/Compose.tsx b/src/components/Compose.tsx index af71c64..2bd734e 100644 --- a/src/components/Compose.tsx +++ b/src/components/Compose.tsx @@ -5,6 +5,7 @@ import AutoTextarea from '@/components/AutoTextarea'; import { Post, Attachment } from '@/lib/types'; import { ImageIcon } from 'lucide-react'; import { VideoEmbed } from '@/components/VideoEmbed'; +import { formatFullHandle } from '@/lib/utils/handle'; interface ComposeProps { onPost: (content: string, mediaIds: string[], linkPreview?: any, replyToId?: string) => void; @@ -121,7 +122,7 @@ export function Compose({ onPost, replyingTo, onCancelReply, placeholder = "What {replyingTo && !isReply && (
- Replying to @{replyingTo.author.handle} + Replying to {formatFullHandle(replyingTo.author.handle)}
{post.replyTo && (
- Replied to e.stopPropagation()}>@{post.replyTo.author.handle} + Replied to e.stopPropagation()}>{formatFullHandle(post.replyTo.author.handle)}
)} diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 750b396..e33b83f 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -4,6 +4,7 @@ import Link from 'next/link'; import { usePathname } from 'next/navigation'; import { useAuth } from '@/lib/contexts/AuthContext'; import { HomeIcon, SearchIcon, BellIcon, UserIcon, ShieldIcon, SynapsisLogo, BookOpenIcon, SettingsIcon } from './Icons'; +import { formatFullHandle } from '@/lib/utils/handle'; export function Sidebar() { const { user, isAdmin } = useAuth(); @@ -71,7 +72,7 @@ export function Sidebar() {
{user.displayName}
-
@{user.handle}
+
{formatFullHandle(user.handle)}
diff --git a/src/lib/utils/handle.ts b/src/lib/utils/handle.ts new file mode 100644 index 0000000..0a09715 --- /dev/null +++ b/src/lib/utils/handle.ts @@ -0,0 +1,21 @@ +export const NODE_DOMAIN = process.env.NEXT_PUBLIC_NODE_DOMAIN || 'localhost:3000'; + +/** + * Formats a handle into its full federated form: @user@domain + * If the handle already contains a domain (e.g. user@other.com), it returns it as @user@other.com + * If it's a local handle (e.g. user), it appends the local node domain: @user@localnode.com + */ +export function formatFullHandle(handle: string): string { + if (!handle) return ''; + + // Remove leading @ if present for processing + const cleanHandle = handle.startsWith('@') ? handle.slice(1) : handle; + + // Check if it already has a domain (contains @) + if (cleanHandle.includes('@')) { + return `@${cleanHandle}`; + } + + // Append local node domain + return `@${cleanHandle}@${NODE_DOMAIN}`; +}