feat(crew): clearer, consistent crew-creation form
- Coordinator block: shorter copy, a Remove (✕) button, and an honest "pointer only — real Cordn MLS isn't wired yet" note (the old paragraph was a wall of jargon). - Save now blocks on a non-empty but unparseable coordinator (was silently dropped) instead of publishing a crew with no coordinator. - One consistent block order for create (wizard "Extras") and edit, grouped: core -> squad/places -> identity (NIP-05, links, band) -> agent -> showcase (video, sponsor) -> treasury -> integrations (Lab link, coordinator).
This commit is contained in:
parent
127819e3da
commit
3cc16d18ca
3 changed files with 50 additions and 23 deletions
|
|
@ -428,6 +428,12 @@ export function CrewForm({ existing }: CrewFormProps) {
|
|||
return;
|
||||
}
|
||||
|
||||
// An invalid coordinator would be silently dropped on save — tell the user.
|
||||
if (coordinatorInput.trim() && !parsedCoordinator) {
|
||||
toast({ title: t('socialUi.coordinator.invalid'), variant: 'destructive' });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const identifier = existing?.identifier ?? `crew-${Date.now().toString(36)}`;
|
||||
|
||||
|
|
@ -1091,8 +1097,7 @@ export function CrewForm({ existing }: CrewFormProps) {
|
|||
<Server className="h-4 w-4" /> {t('socialUi.coordinator.customTitle')} <span className="font-normal text-gray-500">{t('socialUi.coordinator.customBadge')}</span>
|
||||
</Label>
|
||||
<p className="text-xs text-gray-500">
|
||||
<span className="font-semibold text-gray-300">{t('socialUi.coordinator.customDescA')}</span> {t('socialUi.coordinator.customDescB')}{' '}
|
||||
<span className="font-semibold text-gray-300">{t('socialUi.coordinator.customRunOwn')}</span>{' '}
|
||||
{t('socialUi.coordinator.customDescShort')}{' '}
|
||||
<a
|
||||
href="https://github.com/Cordn-msg/cordn"
|
||||
target="_blank"
|
||||
|
|
@ -1100,17 +1105,32 @@ export function CrewForm({ existing }: CrewFormProps) {
|
|||
className="text-teal-400 underline underline-offset-2 hover:text-teal-300"
|
||||
>
|
||||
{t('socialUi.coordinator.cordnCoordinator')}
|
||||
</a>{' '}
|
||||
{t('socialUi.coordinator.customDescC')}
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<Input
|
||||
id="crew-coordinator"
|
||||
placeholder="nprofile1… · cordn.net/chat/coordinators?c=… · https://…"
|
||||
value={coordinatorInput}
|
||||
onChange={(e) => setCoordinatorInput(e.target.value)}
|
||||
className="bg-gray-800 border-gray-700 font-mono text-xs"
|
||||
spellCheck={false}
|
||||
/>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
id="crew-coordinator"
|
||||
placeholder="nprofile1… · cordn.net/chat/coordinators?c=… · https://…"
|
||||
value={coordinatorInput}
|
||||
onChange={(e) => setCoordinatorInput(e.target.value)}
|
||||
className="bg-gray-800 border-gray-700 font-mono text-xs"
|
||||
spellCheck={false}
|
||||
/>
|
||||
{coordinatorInput.trim() !== '' && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="shrink-0 border-gray-700 text-gray-400 hover:text-red-400"
|
||||
onClick={() => setCoordinatorInput('')}
|
||||
title={t('socialUi.coordinator.remove')}
|
||||
aria-label={t('socialUi.coordinator.remove')}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
{coordinatorInput.trim() !== '' &&
|
||||
(parsedCoordinator ? (
|
||||
<p className="break-all text-[11px] text-teal-400">
|
||||
|
|
@ -1134,6 +1154,7 @@ export function CrewForm({ existing }: CrewFormProps) {
|
|||
{t('socialUi.coordinator.useMine')}
|
||||
</Button>
|
||||
)}
|
||||
<p className="text-[10px] leading-snug text-gray-600">{t('socialUi.coordinator.pointerNote')}</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
|
@ -1281,19 +1302,19 @@ export function CrewForm({ existing }: CrewFormProps) {
|
|||
{bioBlock}
|
||||
{vibeBlock}
|
||||
{crewTypeBlock}
|
||||
{agentBlock}
|
||||
{openCallBlock}
|
||||
{treasuryBlock}
|
||||
{membersBlock}
|
||||
{spotsBlock}
|
||||
{labLinkBlock}
|
||||
{videoBlock}
|
||||
{sponsorBlock}
|
||||
{coordinatorBlock}
|
||||
{locationBlock}
|
||||
{nip05Block}
|
||||
{linksBlock}
|
||||
{bandBlock}
|
||||
{agentBlock}
|
||||
{videoBlock}
|
||||
{sponsorBlock}
|
||||
{treasuryBlock}
|
||||
{labLinkBlock}
|
||||
{coordinatorBlock}
|
||||
<Button type="submit" className={cn('w-full uppercase font-black', TEAL)} disabled={busy}>
|
||||
{isPublishing ? (
|
||||
<><Loader2 className="h-4 w-4 mr-2 animate-spin" />{t('socialUi.form.publishing')}</>
|
||||
|
|
@ -1370,14 +1391,14 @@ export function CrewForm({ existing }: CrewFormProps) {
|
|||
filled: hasExtras,
|
||||
content: (
|
||||
<div className="space-y-6">
|
||||
{treasuryBlock}
|
||||
{videoBlock}
|
||||
{sponsorBlock}
|
||||
{labLinkBlock}
|
||||
{nip05Block}
|
||||
{linksBlock}
|
||||
{bandBlock}
|
||||
{nip05Block}
|
||||
{agentBlock}
|
||||
{videoBlock}
|
||||
{sponsorBlock}
|
||||
{treasuryBlock}
|
||||
{labLinkBlock}
|
||||
{coordinatorBlock}
|
||||
</div>
|
||||
),
|
||||
|
|
|
|||
|
|
@ -3600,6 +3600,9 @@ socialUi: {
|
|||
privacyNote: '🔒 A coordinator sees ciphertext and metadata only — never message contents. Run your own to keep even the metadata yours.',
|
||||
customTitle: 'Custom Chat Coordinator',
|
||||
customBadge: '· experimental · your own relay',
|
||||
customDescShort: "Optional — your crew already has end-to-end encrypted chat built in, so you don't need this. Only set it if you run your own",
|
||||
remove: 'Remove',
|
||||
pointerNote: "Pointer only for now: a wss:// endpoint is used as the crew's chat relay; an https:// one via the shared ContextVM relay. Real Cordn MLS isn't wired yet.",
|
||||
customDescA: "You don't need this.",
|
||||
customDescB: 'Your crew already has private, end-to-end encrypted group chat built in — no setup. This field is only for crews who',
|
||||
customRunOwn: 'run their own',
|
||||
|
|
|
|||
|
|
@ -3600,6 +3600,9 @@ socialUi: {
|
|||
privacyNote: '🔒 Un coordinador solo ve texto cifrado y metadatos — nunca el contenido de los mensajes. Monta el tuyo para que hasta los metadatos sean tuyos.',
|
||||
customTitle: 'Coordinador de chat propio',
|
||||
customBadge: '· experimental · tu propio relé',
|
||||
customDescShort: 'Opcional: tu cuadrilla ya tiene chat cifrado de extremo a extremo integrado, así que no lo necesitas. Configúralo solo si usas tu propio',
|
||||
remove: 'Quitar',
|
||||
pointerNote: 'Por ahora es solo un puntero: un endpoint wss:// se usa como relé del chat de la cuadrilla y uno https:// a través del relé ContextVM compartido. El MLS real de Cordn aún no está conectado.',
|
||||
customDescA: 'No lo necesitas.',
|
||||
customDescB: 'Tu cuadrilla ya tiene chat de grupo privado y cifrado de extremo a extremo integrado — sin configurar nada. Este campo es solo para cuadrillas que',
|
||||
customRunOwn: 'montan el suyo',
|
||||
|
|
|
|||
Loading…
Reference in a new issue