'use client'; import React, { useState, useEffect } from 'react'; import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { nip19 } from "nostr-tools" import { Label } from "./ui/label" import { Textarea } from "@/components/ui/textarea" import { verifyEvent } from 'nostr-tools/pure' import { hexToBytes } from '@noble/hashes/utils' import { useNostr, useProfile } from 'nostr-react'; import { signEvent } from '@/utils/utils'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Separator } from "@/components/ui/separator"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { AlertCircle, User, AtSign, FileImage, Info, Loader2 } from 'lucide-react'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { Alert, AlertDescription } from "@/components/ui/alert"; export function CreateProfileForm() { const { publish } = useNostr(); // Local state for form inputs const [username, setUsername] = useState(""); const [displayName, setDisplayName] = useState(""); const [bio, setBio] = useState(""); const [picture, setPicture] = useState(""); const [website, setWebsite] = useState(""); const [isSubmitting, setIsSubmitting] = useState(false); const [validationErrors, setValidationErrors] = useState<{[key: string]: string}>({}); // Get user info from local storage const [npub, setNpub] = useState(''); const [pubkey, setPubkey] = useState(''); const [loginType, setLoginType] = useState(''); useEffect(() => { if (typeof window !== 'undefined') { const storedPubkey = window.localStorage.getItem("pubkey") ?? ''; const storedLoginType = window.localStorage.getItem("loginType") ?? ''; setPubkey(storedPubkey); setLoginType(storedLoginType); if (storedPubkey && storedPubkey.length > 0) { setNpub(nip19.npubEncode(storedPubkey)); } } }, []); // Try to load existing profile data if available const { data: userData, isLoading: profileLoading } = useProfile({ pubkey, }); useEffect(() => { if (userData) { setUsername(userData.name || ""); setDisplayName(userData.display_name || ""); setBio(userData.about || ""); setPicture(userData.picture || ""); setWebsite(userData.website || ""); } }, [userData]); // Input handlers const handleUsernameChange = (event: React.ChangeEvent) => { const value = event.target.value; setUsername(value); // Clear validation error if fixed if (validationErrors.username && value.trim()) { const newErrors = {...validationErrors}; delete newErrors.username; setValidationErrors(newErrors); } }; const handleDisplayNameChange = (event: React.ChangeEvent) => { setDisplayName(event.target.value); }; const handleBioChange = (event: React.ChangeEvent) => { setBio(event.target.value); }; const handlePictureChange = (event: React.ChangeEvent) => { setPicture(event.target.value); }; const handleWebsiteChange = (event: React.ChangeEvent) => { setWebsite(event.target.value); }; // Form validation const validateForm = () => { const errors: {[key: string]: string} = {}; if (!username.trim()) { errors.username = "Username is required"; } if (picture && !isValidUrl(picture)) { errors.picture = "Please enter a valid URL for your profile picture"; } if (website && !isValidUrl(website)) { errors.website = "Please enter a valid URL for your website"; } setValidationErrors(errors); return Object.keys(errors).length === 0; }; const isValidUrl = (url: string) => { try { new URL(url); return true; } catch { return false; } }; // Form submission async function handleProfileUpdate() { if (!validateForm()) { return; } setIsSubmitting(true); try { // Create the profile content object const profileContent: Record = { name: username, display_name: displayName || username, // Fallback to username if display name is empty }; if (bio) profileContent.about = bio; if (picture) profileContent.picture = picture; if (website) profileContent.website = website; // Create the event const event = { kind: 0, created_at: Math.floor(Date.now() / 1000), tags: [], content: JSON.stringify(profileContent), pubkey: pubkey, id: "", sig: "", }; // Sign the event const signedEvent = await signEvent(loginType, event); if (signedEvent === null) { throw new Error('Failed to sign the event'); } // Verify the event const isGood = verifyEvent(signedEvent); if (isGood) { // Publish to relays publish(signedEvent); // Redirect to profile page window.location.href = `/profile/${npub}`; } else { throw new Error('Event verification failed'); } } catch (error) { setValidationErrors({ submit: error instanceof Error ? error.message : 'Failed to create profile. Please try again.' }); setIsSubmitting(false); } } // Extract initials for avatar fallback const getInitials = () => { if (displayName) { return displayName.charAt(0).toUpperCase(); } else if (username) { return username.charAt(0).toUpperCase(); } return 'U'; }; return (
{/* Profile preview card */} Profile Preview How your profile could appear to others
{getInitials()}

{displayName || username || 'Display Name'}

{username && (

@{username}

)} {bio && (

{bio.length > 100 ? `${bio.substring(0, 100)}...` : bio}

)}
{/* Profile form */}

This is your unique username on Nostr

{validationErrors.username && (

{validationErrors.username}

)}

This is the name that will be displayed to others

Enter a direct link to your profile image (JPEG or PNG)

{validationErrors.picture && (

{validationErrors.picture}

)}
(Optional)
{validationErrors.website && (

{validationErrors.website}

)}