diff --git a/src/components/feed/Feed.tsx b/src/components/feed/Feed.tsx index 270e4c5..502c70a 100644 --- a/src/components/feed/Feed.tsx +++ b/src/components/feed/Feed.tsx @@ -42,7 +42,11 @@ export function Feed() { const filteredNotes = activeNotes.filter((event) => { const c = event.content.trim(); - return c.length > 0 && !c.startsWith("{") && !c.startsWith("["); + if (!c || c.startsWith("{") || c.startsWith("[")) return false; + // Filter out notes that look like base64 blobs or relay protocol messages + if (c.length > 500 && /^[A-Za-z0-9+/=]{50,}$/.test(c.slice(0, 100))) return false; + if (c.startsWith("nlogpost:") || c.startsWith("T1772")) return false; + return true; }); return ( diff --git a/src/components/profile/ProfileView.tsx b/src/components/profile/ProfileView.tsx index a510a31..b044f66 100644 --- a/src/components/profile/ProfileView.tsx +++ b/src/components/profile/ProfileView.tsx @@ -1,24 +1,118 @@ import { useEffect, useState } from "react"; import { NDKEvent } from "@nostr-dev-kit/ndk"; import { useUIStore } from "../../stores/ui"; +import { useUserStore } from "../../stores/user"; import { useProfile } from "../../hooks/useProfile"; -import { fetchUserNotes } from "../../lib/nostr"; +import { fetchUserNotes, publishProfile } from "../../lib/nostr"; import { shortenPubkey } from "../../lib/utils"; import { NoteCard } from "../feed/NoteCard"; +function EditProfileForm({ pubkey, onSaved }: { pubkey: string; onSaved: () => void }) { + const { profile, fetchOwnProfile } = useUserStore(); + const [name, setName] = useState(profile?.name || ""); + const [displayName, setDisplayName] = useState(profile?.displayName || ""); + const [about, setAbout] = useState(profile?.about || ""); + const [picture, setPicture] = useState(profile?.picture || ""); + const [banner, setBanner] = useState(profile?.banner || ""); + const [website, setWebsite] = useState(profile?.website || ""); + const [nip05, setNip05] = useState(profile?.nip05 || ""); + const [lud16, setLud16] = useState(profile?.lud16 || ""); + const [saving, setSaving] = useState(false); + const [error, setError] = useState(null); + const [saved, setSaved] = useState(false); + + const handleSave = async () => { + setSaving(true); + setError(null); + try { + await publishProfile({ + name: name.trim() || undefined, + display_name: displayName.trim() || undefined, + about: about.trim() || undefined, + picture: picture.trim() || undefined, + banner: banner.trim() || undefined, + website: website.trim() || undefined, + nip05: nip05.trim() || undefined, + lud16: lud16.trim() || undefined, + }); + await fetchOwnProfile(); + setSaved(true); + setTimeout(onSaved, 1000); + } catch (err) { + setError(`Failed to save: ${err}`); + } finally { + setSaving(false); + } + }; + + const field = (label: string, value: string, onChange: (v: string) => void, placeholder = "") => ( +
+ + onChange(e.target.value)} + placeholder={placeholder} + className="w-full bg-bg border border-border px-3 py-1.5 text-text text-[12px] focus:outline-none focus:border-accent/50" + style={{ WebkitUserSelect: "text", userSelect: "text" } as React.CSSProperties} + /> +
+ ); + + return ( +
+
+ {field("Display name", displayName, setDisplayName, "Square that Circle")} + {field("Username", name, setName, "squarethecircle")} + {field("NIP-05 (verified name)", nip05, setNip05, "you@domain.com")} + {field("Lightning address (lud16)", lud16, setLud16, "you@walletofsatoshi.com")} + {field("Website", website, setWebsite, "https://…")} + {field("Profile picture URL", picture, setPicture, "https://…")} +
+
+ +