feat(cordn): web + Staircase MLS entry points on the crew coordinator card
Relabel the cordn.net add-link accurately as 'Cordn (web)' (real MLS in the browser) and add a 'Copy for Staircase' button that copies the coordinator nprofile for the native app. Our coordinator by default; a BYO coordinator's add-link when it has a pubkey.
This commit is contained in:
parent
e262aed938
commit
3d9234d65c
4 changed files with 51 additions and 18 deletions
|
|
@ -2134,10 +2134,11 @@ The community relay moved off the hosted relay.tools instance
|
|||
default; **Settings → Coordinator** shows a "Use SkateSpots coordinator" block
|
||||
and **CrewForm** prefills it for new crews. Disclosure copy lives in
|
||||
`socialUi.coordinator.ourDefault*` + the existing `privacyNote` (coordinators see
|
||||
ciphertext + metadata, never contents). `CrewCoordinatorCard` shows an **"Open in
|
||||
Staircase"** link (`coordinatorAddUrl`) in both branches (the native Cordn client
|
||||
for real MLS) — our coordinator by default, a BYO coordinator's add-link when it
|
||||
has a pubkey.
|
||||
ciphertext + metadata, never contents). `CrewCoordinatorCard` offers two real-MLS
|
||||
entry points in both branches — **"Cordn (web)"** (the cordn.net add-link → real
|
||||
MLS in the browser) and **"Copy for Staircase"** (copies the nprofile to add in
|
||||
the native app) — our coordinator by default, a BYO coordinator's when it has a
|
||||
pubkey.
|
||||
- **Cutover (2026-10-12):** `SKATE_RELAY` (`src/lib/types.ts`) → the new relay; the
|
||||
default pools, the DM/CVM/zap relay arrays, the `cvm-server` + `push-service`
|
||||
defaults, and the archive default all point at the new relay.
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { Link } from 'react-router-dom';
|
||||
import { Lock, ShieldCheck, Server, ExternalLink, Settings } from 'lucide-react';
|
||||
import { Lock, ShieldCheck, Server, ExternalLink, Settings, Copy } from 'lucide-react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||
import { useAuthor } from '@/hooks/useAuthor';
|
||||
import { useTranslation } from '@/contexts/language';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { genUserName } from '@/lib/genUserName';
|
||||
import { sanitizeUrl, urlDomain } from '@/lib/sanitizeUrl';
|
||||
import { resolveCordnEndpoint } from '@/lib/cordn/endpoint';
|
||||
|
|
@ -143,22 +144,51 @@ export function CrewCoordinatorCard({
|
|||
);
|
||||
}
|
||||
|
||||
/** Link to open a coordinator in Staircase (the native Cordn client) for real
|
||||
* MLS chat. Falls back to the Cordn home when the coordinator has no pubkey. */
|
||||
/** Real-MLS entry points for a coordinator: open the Cordn **web** client
|
||||
* (cordn.net add-link), or copy the nprofile to add it in the **Staircase**
|
||||
* native app. Falls back to the Cordn home when there's no pubkey. */
|
||||
function StaircaseLink({ coordinator }: { coordinator: { pubkey?: string; relays?: string[] } }) {
|
||||
const { t } = useTranslation();
|
||||
const href = coordinator.pubkey
|
||||
const { toast } = useToast();
|
||||
const nprofile = coordinator.pubkey
|
||||
? nip19.nprofileEncode({ pubkey: coordinator.pubkey, relays: coordinator.relays ?? [] })
|
||||
: null;
|
||||
const webUrl = coordinator.pubkey
|
||||
? coordinatorAddUrl({ pubkey: coordinator.pubkey, relays: coordinator.relays ?? [] })
|
||||
: CORDN_HOME;
|
||||
|
||||
const copyNprofile = async () => {
|
||||
if (!nprofile) return;
|
||||
try {
|
||||
await navigator.clipboard.writeText(nprofile);
|
||||
toast({ title: t('common.copied') });
|
||||
} catch {
|
||||
toast({ title: t('common.tryAgain'), variant: 'destructive' });
|
||||
}
|
||||
};
|
||||
|
||||
const btn =
|
||||
'inline-flex items-center gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs font-bold transition-colors';
|
||||
return (
|
||||
<a
|
||||
href={href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-1.5 rounded-lg border border-teal-500/30 bg-teal-500/10 px-2.5 py-1.5 text-xs font-bold text-teal-300 transition-colors hover:bg-teal-500/20"
|
||||
>
|
||||
<ExternalLink className="h-3.5 w-3.5" /> {t('socialUi.crewCoordinator.openInStaircase')}
|
||||
</a>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<a
|
||||
href={webUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={`${btn} border-teal-500/30 bg-teal-500/10 text-teal-300 hover:bg-teal-500/20`}
|
||||
>
|
||||
<ExternalLink className="h-3.5 w-3.5" /> {t('socialUi.crewCoordinator.openInCordnWeb')}
|
||||
</a>
|
||||
{nprofile && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void copyNprofile()}
|
||||
className={`${btn} border-white/15 bg-white/5 text-gray-300 hover:bg-white/10`}
|
||||
>
|
||||
<Copy className="h-3.5 w-3.5" /> {t('socialUi.crewCoordinator.copyForStaircase')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4045,7 +4045,8 @@ socialUi: {
|
|||
ownRelaySafe: "Nothing crew-specific leaves your crew's own relay.",
|
||||
manage: 'Manage coordinator',
|
||||
open: 'Open {{domain}}',
|
||||
openInStaircase: 'Open in Staircase',
|
||||
openInCordnWeb: 'Cordn (web)',
|
||||
copyForStaircase: 'Copy for Staircase',
|
||||
runBy: 'Coordinator run by',
|
||||
},
|
||||
dms: {
|
||||
|
|
|
|||
|
|
@ -4045,7 +4045,8 @@ socialUi: {
|
|||
ownRelaySafe: 'Nada específico de la peña sale del relé propio de tu peña.',
|
||||
manage: 'Gestionar coordinador',
|
||||
open: 'Abrir {{domain}}',
|
||||
openInStaircase: 'Abrir en Staircase',
|
||||
openInCordnWeb: 'Cordn (web)',
|
||||
copyForStaircase: 'Copiar para Staircase',
|
||||
runBy: 'Coordinador gestionado por',
|
||||
},
|
||||
dms: {
|
||||
|
|
|
|||
Loading…
Reference in a new issue