diff --git a/src/components/CraftProfileForm.tsx b/src/components/CraftProfileForm.tsx index 4ccaade..4e0edd8 100644 --- a/src/components/CraftProfileForm.tsx +++ b/src/components/CraftProfileForm.tsx @@ -28,6 +28,7 @@ import { LabDraftBanner } from '@/components/LabDraftBanner'; import { LabProfileProgress } from '@/components/LabProfileProgress'; import { FormWizard, WizardStep } from '@/components/FormWizard'; import { isValidNoffer, type EntityType } from '@/lib/types'; +import { normalizeClinkCode } from '@/lib/clink'; import type { LabOption } from '@/lib/labExtras'; import { labProfilePathFromKind } from '@/lib/labRoutes'; import { normalizeUrlInput, sanitizeUrl, isValidEmail } from '@/lib/sanitizeUrl'; @@ -327,7 +328,7 @@ export function CraftProfileForm({ config }: { config: CraftFormConfig }) { if (website.trim()) tags.push(['website', normalizeUrlInput(website)]); if (portfolio.trim()) tags.push(['portfolio', normalizeUrlInput(portfolio)]); if (social.trim()) tags.push(['social', normalizeUrlInput(social)]); - if (isValidNoffer(noffer.trim())) tags.push(['noffer', noffer.trim().toLowerCase()]); + if (isValidNoffer(noffer.trim())) tags.push(['noffer', normalizeClinkCode(noffer)]); // Craft-specific extras for (const f of extraFields) { diff --git a/src/components/CrewForm.tsx b/src/components/CrewForm.tsx index b6d5992..3087302 100644 --- a/src/components/CrewForm.tsx +++ b/src/components/CrewForm.tsx @@ -23,6 +23,7 @@ import { isCrewVideoUrl, parseCrewVideo } from '@/lib/videoEmbed'; import { CrewSponsorPicker } from '@/components/crew/CrewSponsorPicker'; import { NofferField } from '@/components/NofferField'; import { SKATE_CREW_KIND, SKATE_SPOT_KIND, SKATE_SHOP_KIND, parseAddrCoord, ZAP_GOAL_KIND, CREW_VIBE_LABELS, CREW_FOCUS_LABELS, CREW_FOCUS_DESCRIPTIONS, CREW_FOCUS_TAG, CREW_LOOKING_FOR_OPTIONS, CREW_ROLE_META, CREW_ROLE_ORDER, SHOW_CREW_TREASURY, isValidNoffer, type SkateCrew, type CrewFocus, type CrewRole } from '@/lib/types'; +import { normalizeClinkCode } from '@/lib/clink'; import { CREW_VIBE_KEYS, CREW_FOCUS_KEYS, CREW_FOCUS_DESC_KEYS, CREW_LOOKING_FOR_KEYS, CREW_ROLE_KEYS } from '@/lib/labelKeys'; import { useCrewGoal } from '@/hooks/useCrewGoal'; import { useCoordinator, useCoordinatorHealth } from '@/hooks/useCoordinator'; @@ -571,7 +572,7 @@ export function CrewForm({ existing }: CrewFormProps) { if (s.trim()) tags.push(['social', s.trim()]); } for (const handle of nip05s) tags.push(['nip05', handle]); - if (isValidNoffer(noffer.trim())) tags.push(['noffer', noffer.trim().toLowerCase()]); + if (isValidNoffer(noffer.trim())) tags.push(['noffer', normalizeClinkCode(noffer)]); // Members: admins get an `admin` role marker on their `p` tag. for (const pk of members) { diff --git a/src/components/EditProfileForm.tsx b/src/components/EditProfileForm.tsx index 3b54f61..6d06c9e 100644 --- a/src/components/EditProfileForm.tsx +++ b/src/components/EditProfileForm.tsx @@ -30,6 +30,7 @@ import { Nip05HandlesEditor } from '@/components/Nip05HandlesEditor'; import { SkateNip05Input } from '@/components/SkateNip05Input'; import { SKATE_NIP05_DOMAIN, parseSkateNip05 } from '@/lib/skateNip05'; import { isValidNoffer, isValidSilentPayment, isValidLightningAddress } from '@/lib/types'; +import { normalizeClinkCode } from '@/lib/clink'; import { useTranslation } from '@/contexts/language'; export const EditProfileForm: React.FC = () => { @@ -52,7 +53,7 @@ export const EditProfileForm: React.FC = () => { // Lightning address (LUD-16). Part of NostrMetadata but not in the default // form fields, so we manage it here and merge it on save. const [lud16, setLud16] = useState(''); - const lud16Trimmed = lud16.trim(); + const lud16Trimmed = lud16.trim().replace(/^lightning:\/\//i, '').replace(/^lightning:/i, ''); const lud16Invalid = lud16Trimmed.length > 0 && !isValidLightningAddress(lud16Trimmed); // Bitcoin Silent Payment address (BIP-352). Custom kind-0 field. @@ -224,7 +225,7 @@ export const EditProfileForm: React.FC = () => { // Merge the custom CLINK offer code (or remove it when cleared). if (nofferValid) { - data.noffer = nofferTrimmed.toLowerCase(); + data.noffer = normalizeClinkCode(nofferTrimmed); } else { delete data.noffer; } @@ -493,6 +494,7 @@ export const EditProfileForm: React.FC = () => { value={lud16} onChange={setLud16} placeholder="you@yourdomain.com" + inputMode="email" status={ lud16Invalid ? { kind: 'error', text: t('settingsUi.editProfile.lud16Invalid') } @@ -713,7 +715,8 @@ const FieldRow: React.FC<{ onChange: (v: string) => void; placeholder: string; status: { kind: 'hint' | 'ok' | 'error'; text: string }; -}> = ({ id, icon, iconClass, label, value, onChange, placeholder, status }) => ( + inputMode?: React.HTMLAttributes['inputMode']; +}> = ({ id, icon, iconClass, label, value, onChange, placeholder, status, inputMode }) => (
+ {lud16Copied ? ( + + ) : ( + + )} + )} {/* Bitcoin Silent Payment address — copyable */} @@ -1052,7 +1071,7 @@ function ProfilePage({ pubkey }: { pubkey: string }) { )} {/* Tip Card */} - {!isOwnProfile && (profileEvent || nofferCode) && ( + {!isOwnProfile && (hasLightning || hasClink) && ( @@ -1061,13 +1080,13 @@ function ProfilePage({ pubkey }: { pubkey: string }) { {t('walletUi.profile.supportSkaterDesc')}

- {profileEvent && ( + {profileEvent && hasLightning && ( )} - {nofferCode && ( + {hasClink && nofferCode && (