Signup: require a display name (changeable later), remove skip

Per request, a display name is now mandatory when signing up:
- Removed the "Skip for now" button from the profile step — the only forward
  path is Save, which is disabled until a non-empty name is entered.
- Marked the field required (*), added helper copy ("Required — how you'll show
  up across SkateSpots. Change it anytime."), and a hint under the button.
- finishSignup guards against an empty/whitespace name (defense-in-depth for
  keyboard/Enter), trims the name before publishing kind 0.
- Reworded the step copy: name required, bio + avatar still optional.

Only affects the primary nsec signup dialog (also the endpoint the mnemonic
flow feeds into); the mnemonic dialog has no name step.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
shakespeare.diy 2026-09-03 18:32:08 +07:00
parent 39afc00b6e
commit 3833625bde

View file

@ -152,14 +152,26 @@ const SignupDialog: React.FC<SignupDialogProps> = ({ isOpen, onClose, onComplete
}; };
const finishSignup = async (skipProfile = false) => { const finishSignup = async (skipProfile = false) => {
// A display name is mandatory (changeable later). Guard here too so the step
// can't be completed without one, even via keyboard/Enter.
const trimmedName = profileData.name.trim();
if (!skipProfile && !trimmedName) {
toast({
title: 'Display name required',
description: 'Pick a display name so other skaters know who you are. You can change it anytime.',
variant: 'destructive',
});
return;
}
// Mark signup completion time for fallback welcome modal // Mark signup completion time for fallback welcome modal
localStorage.setItem('signup_completed', Date.now().toString()); localStorage.setItem('signup_completed', Date.now().toString());
try { try {
// Publish profile if user provided information // Publish profile if user provided information
if (!skipProfile && (profileData.name || profileData.about || profileData.picture)) { if (!skipProfile && (trimmedName || profileData.about || profileData.picture)) {
const metadata: Record<string, string> = {}; const metadata: Record<string, string> = {};
if (profileData.name) metadata.name = profileData.name; if (trimmedName) metadata.name = trimmedName;
if (profileData.about) metadata.about = profileData.about; if (profileData.about) metadata.about = profileData.about;
if (profileData.picture) metadata.picture = profileData.picture; if (profileData.picture) metadata.picture = profileData.picture;
@ -365,11 +377,11 @@ const SignupDialog: React.FC<SignupDialogProps> = ({ isOpen, onClose, onComplete
<div className='relative z-10 space-y-2'> <div className='relative z-10 space-y-2'>
<p className='text-base font-semibold'> <p className='text-base font-semibold'>
You're in! 🛹 Add a name so skaters know who you are You're in! 🛹 Pick a display name so skaters know who you are
</p> </p>
<p className='text-sm text-muted-foreground'> <p className='text-sm text-muted-foreground'>
This is optional — you can skip it and fill it in later. You can change it anytime later. Bio and avatar are optional.
</p> </p>
</div> </div>
</div> </div>
@ -390,16 +402,22 @@ const SignupDialog: React.FC<SignupDialogProps> = ({ isOpen, onClose, onComplete
<div className={`space-y-4 text-left ${isPublishing ? 'opacity-50 pointer-events-none' : ''}`}> <div className={`space-y-4 text-left ${isPublishing ? 'opacity-50 pointer-events-none' : ''}`}>
<div className='space-y-2'> <div className='space-y-2'>
<label htmlFor='profile-name' className='text-sm font-medium'> <label htmlFor='profile-name' className='text-sm font-medium'>
Display Name Display Name <span className='text-red-500'>*</span>
</label> </label>
<Input <Input
id='profile-name' id='profile-name'
value={profileData.name} value={profileData.name}
onChange={(e) => setProfileData(prev => ({ ...prev, name: e.target.value }))} onChange={(e) => setProfileData(prev => ({ ...prev, name: e.target.value }))}
placeholder='Your name' placeholder='e.g. Tony, gnarlyGrind, or your crew name'
className='rounded-lg' className='rounded-lg'
maxLength={64}
required
aria-required='true'
disabled={isPublishing} disabled={isPublishing}
/> />
<p className='text-xs text-muted-foreground'>
Required — how you'll show up across SkateSpots. Change it anytime.
</p>
</div> </div>
<div className='space-y-2'> <div className='space-y-2'>
@ -456,12 +474,12 @@ const SignupDialog: React.FC<SignupDialogProps> = ({ isOpen, onClose, onComplete
</div> </div>
</div> </div>
{/* Action buttons */} {/* Action button — a display name is required to continue. */}
<div className='space-y-3'> <div className='space-y-3'>
<Button <Button
className='w-full rounded-full py-4 text-base font-semibold bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 transform transition-all duration-200 hover:scale-105 shadow-lg disabled:opacity-50 disabled:cursor-not-allowed disabled:transform-none' className='w-full rounded-full py-4 text-base font-semibold bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 transform transition-all duration-200 hover:scale-105 shadow-lg disabled:opacity-50 disabled:cursor-not-allowed disabled:transform-none disabled:hover:scale-100'
onClick={() => finishSignup(false)} onClick={() => finishSignup(false)}
disabled={isPublishing || isUploading} disabled={isPublishing || isUploading || !profileData.name.trim()}
> >
{isPublishing ? ( {isPublishing ? (
<> <>
@ -476,21 +494,11 @@ const SignupDialog: React.FC<SignupDialogProps> = ({ isOpen, onClose, onComplete
)} )}
</Button> </Button>
<Button {!profileData.name.trim() && !isPublishing && (
variant='outline' <p className='text-center text-xs text-muted-foreground'>
className='w-full rounded-full py-3 disabled:opacity-50 disabled:cursor-not-allowed' Add a display name to continue.
onClick={() => finishSignup(true)} </p>
disabled={isPublishing || isUploading} )}
>
{isPublishing ? (
<>
<div className='w-4 h-4 mr-2 border-2 border-current border-t-transparent rounded-full animate-spin' />
Setting up account...
</>
) : (
'Skip for now'
)}
</Button>
</div> </div>
</div> </div>
)} )}