From e262aed938474d85fec8dc41c2e326cedbd7b3ae Mon Sep 17 00:00:00 2001
From: SkateSpots
Date: Mon, 12 Oct 2026 17:04:34 +0700
Subject: [PATCH] feat(cordn): 'Open in Staircase' link on the crew coordinator
card
CrewCoordinatorCard now offers a StaircaseLink in both branches: our coordinator (SKATESPOTS_COORDINATOR add-link) when a crew has none, and the crew's own coordinator add-link when it has a pubkey (falls back to the Cordn home otherwise). Lets crews jump to real MLS chat in the native Cordn client.
---
AGENTS.md | 5 +++-
src/components/crew/CrewCoordinatorCard.tsx | 29 ++++++++++++++++++++-
src/i18n/en.ts | 1 +
src/i18n/es.ts | 1 +
4 files changed, 34 insertions(+), 2 deletions(-)
diff --git a/AGENTS.md b/AGENTS.md
index f5dd8f5..cebef66 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -2134,7 +2134,10 @@ 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).
+ 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.
- **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.
diff --git a/src/components/crew/CrewCoordinatorCard.tsx b/src/components/crew/CrewCoordinatorCard.tsx
index 8568a9e..935b580 100644
--- a/src/components/crew/CrewCoordinatorCard.tsx
+++ b/src/components/crew/CrewCoordinatorCard.tsx
@@ -7,7 +7,7 @@ import { useTranslation } from '@/contexts/language';
import { genUserName } from '@/lib/genUserName';
import { sanitizeUrl, urlDomain } from '@/lib/sanitizeUrl';
import { resolveCordnEndpoint } from '@/lib/cordn/endpoint';
-import { CORDN_RELAY_URL } from '@/lib/coordinator';
+import { CORDN_RELAY_URL, coordinatorAddUrl, CORDN_HOME, SKATESPOTS_COORDINATOR } from '@/lib/coordinator';
import type { CrewCoordinator, SkateCrew } from '@/lib/types';
import { nip19 } from 'nostr-tools';
@@ -61,6 +61,7 @@ export function CrewCoordinatorCard({
{t('socialUi.crewCoordinator.chatWord')} {t('socialUi.crewCoordinator.defaultB')}{' '}
{t('socialUi.crewCoordinator.e2ee')} {t('socialUi.crewCoordinator.defaultC')}
+
{isFounder && (
{t('socialUi.crewCoordinator.advancedA')}{' '}
@@ -101,6 +102,13 @@ export function CrewCoordinatorCard({
{coordinator.operator && }
+
+
{endpoint && (
{t('socialUi.crewCoordinator.deliveredOver')}{' '}
@@ -135,6 +143,25 @@ 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. */
+function StaircaseLink({ coordinator }: { coordinator: { pubkey?: string; relays?: string[] } }) {
+ const { t } = useTranslation();
+ const href = coordinator.pubkey
+ ? coordinatorAddUrl({ pubkey: coordinator.pubkey, relays: coordinator.relays ?? [] })
+ : CORDN_HOME;
+ return (
+
+ {t('socialUi.crewCoordinator.openInStaircase')}
+
+ );
+}
+
function CoordinatorEndpoint({ coordinator }: { coordinator: CrewCoordinator }) {
const { t } = useTranslation();
const safe = sanitizeUrl(coordinator.url);
diff --git a/src/i18n/en.ts b/src/i18n/en.ts
index 64d62b2..66e403c 100644
--- a/src/i18n/en.ts
+++ b/src/i18n/en.ts
@@ -4045,6 +4045,7 @@ socialUi: {
ownRelaySafe: "Nothing crew-specific leaves your crew's own relay.",
manage: 'Manage coordinator',
open: 'Open {{domain}}',
+ openInStaircase: 'Open in Staircase',
runBy: 'Coordinator run by',
},
dms: {
diff --git a/src/i18n/es.ts b/src/i18n/es.ts
index f923af1..8d8ea9b 100644
--- a/src/i18n/es.ts
+++ b/src/i18n/es.ts
@@ -4045,6 +4045,7 @@ 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',
runBy: 'Coordinador gestionado por',
},
dms: {