diff --git a/src/components/EditProfileForm.tsx b/src/components/EditProfileForm.tsx
index fe532b9..3b54f61 100644
--- a/src/components/EditProfileForm.tsx
+++ b/src/components/EditProfileForm.tsx
@@ -20,7 +20,7 @@ import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { Switch } from '@/components/ui/switch';
import { Label } from '@/components/ui/label';
-import { Loader2, Upload, Zap, ShieldCheck, AlertCircle, Bitcoin, Award, MessageSquareLock, UserRound, X, Plus, Bot, BadgeCheck, HelpCircle, Server, AtSign } from 'lucide-react';
+import { Loader2, Upload, Zap, ShieldCheck, AlertCircle, Bitcoin, Award, MessageSquareLock, UserRound, Handshake, X, Plus, Bot, BadgeCheck, HelpCircle, Server, AtSign } from 'lucide-react';
import { NSchema as n, type NostrMetadata } from '@nostrify/nostrify';
import { useQueryClient } from '@tanstack/react-query';
import { useUploadFile } from '@/hooks/useUploadFile';
@@ -72,6 +72,8 @@ export const EditProfileForm: React.FC = () => {
// pointer formats as Settings → Coordinator (nprofile / cordn.net link /
// npub / hex) as well as an http(s) URL — this is the discovery field.
const [cordn, setCordn] = useState('');
+ const [openToSponsorships, setOpenToSponsorships] = useState(false);
+ const [sponsorNote, setSponsorNote] = useState('');
// Extra personal NIP-05 handles — stored in a separate kind-30078 record so
// another client re-saving the profile can't drop them.
@@ -131,6 +133,8 @@ export const EditProfileForm: React.FC = () => {
);
}
if (typeof raw.cordn === 'string') setCordn(raw.cordn);
+ setOpenToSponsorships(raw.sponsorships_open === true);
+ if (typeof raw.sponsorships_note === 'string') setSponsorNote(raw.sponsorships_note);
}
}, [metadata, form]);
@@ -252,6 +256,17 @@ export const EditProfileForm: React.FC = () => {
// Retire the legacy field now that badges supersede it.
delete data.sponsors;
+ // Sponsorship opt-in (custom kind-0 fields). The profile only claims
+ // this when the skater explicitly turned it on.
+ if (openToSponsorships) {
+ data.sponsorships_open = true;
+ if (sponsorNote.trim()) data.sponsorships_note = sponsorNote.trim();
+ else delete data.sponsorships_note;
+ } else {
+ delete data.sponsorships_open;
+ delete data.sponsorships_note;
+ }
+
// Cordn contact link (custom field).
if (cordnTrimmed) {
// Normalise a bare pointer (nprofile/npub/hex) to Cordn's canonical
@@ -402,6 +417,41 @@ export const EditProfileForm: React.FC = () => {
)}
/>
+ {/* Sponsorship opt-in — explicit consent, never inferred from activity. */}
+
+
+
+
+
+ {t('settingsUi.editProfile.openToSponsorshipsHint')}
+
+
+
+
+ {openToSponsorships && (
+
+
+
+ )}
+
+
{/* NIP-05 — the verified-handle field. Given real prominence + live
verification feedback so people know if it's actually working. */}
void;
+ hint?: string;
}) {
const interactive = !!onClick && value > 0;
const empty = !loading && value === 0;
@@ -94,6 +96,9 @@ function StatPill({
)}
{label}
+ {hint && (
+
+ )}
>
@@ -106,6 +111,7 @@ function StatPill({