From fe3d9bae2293a7f6ead966d479f67b28ac840bab Mon Sep 17 00:00:00 2001 From: SkateSpots Date: Mon, 26 Oct 2026 12:12:36 +0700 Subject: [PATCH] ui(auth): compact, scroll-safe sign-in dialog - Fix the real bug: the dialog clipped its bottom links (flex column + max-h calc(100dvh-2rem), content min-h-0 flex-1 overflow-y-auto). - One dominant action per state: 'New here' is a compact row, not a second hero. - Plain card labels (Browser extension / Signer app / Sign in with your phone) with tiny NIP-07/46/55 hints; protocol jargon out of the main labels. - Bunker: input + Connect with instructions collapsed under 'Where do I get a bunker?'; no duplicate heading/divider. - Secret key moved to a quiet bottom link; subtitle says 'or keep browsing'. - 8px spacing grid (px-5 gutters, space-y-4 sections, gap-2 cards, p-2.5 cards, 448px max width). New strings backfilled into all 20 locales. Verified with headless 1280x900 + 390x844 screenshots. --- AGENTS.md | 16 +++- src/components/auth/LoginDialog.tsx | 142 ++++++++++++++-------------- src/i18n/cs/drift.ts | 7 ++ src/i18n/de/drift.ts | 7 ++ src/i18n/el/drift.ts | 7 ++ src/i18n/en.ts | 5 + src/i18n/es.ts | 5 + src/i18n/fr/drift.ts | 7 ++ src/i18n/id/drift.ts | 7 ++ src/i18n/it/drift.ts | 7 ++ src/i18n/ja/drift.ts | 7 ++ src/i18n/ko/drift.ts | 7 ++ src/i18n/nl/drift.ts | 7 ++ src/i18n/pl/drift.ts | 7 ++ src/i18n/pt/drift.ts | 7 ++ src/i18n/ro/drift.ts | 7 ++ src/i18n/ru/drift.ts | 7 ++ src/i18n/sv/drift.ts | 7 ++ src/i18n/th/drift.ts | 7 ++ src/i18n/tr/drift.ts | 7 ++ src/i18n/uk/drift.ts | 7 ++ src/i18n/vi/drift.ts | 7 ++ src/i18n/zh-CN/drift.ts | 7 ++ src/i18n/zh-TW/drift.ts | 7 ++ 24 files changed, 237 insertions(+), 71 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 529469d..6e5d7a4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1533,8 +1533,20 @@ session with **no other memory of this project**. So: **Sign in with your phone** (NostrConnect) — plus a quiet **Secret Key** link; the cards are a **controlled tab chooser** (`loginTab` state) over the existing panels, and the NostrConnect QR moved from the bunker tab into its own - `connect` panel. Verified with a headless 1280×900 screenshot of the - auto-opened welcome modal. ⚠️ `SignupDialog` is a **default-only** export, so + `connect` panel. **Compact pass (2026-10-26):** the dialog is now + scroll-safe (`flex flex-col` + `max-h-[calc(100dvh-2rem)]`, content + `min-h-0 flex-1 overflow-y-auto` — it used to clip the bottom links), the + New-here block is a compact row (one dominant action per state), bunker + instructions are a collapsed `
` ("Where do I get a bunker?"), and the + secret-key option is a quiet bottom link. Labels are plain ("Browser + extension · Signer app · Sign in with your phone") with **NIP-07/46/55** as + tiny hints (`settingsUi.loginDialog.{extensionCard,signerCard,secretKeyLink, + whereBunker,welcomeSubtitleBrowse}`; backfilled to all 20 locales). Spacing is + an **8px grid**: `px-5` (20px) gutters, `space-y-4` (16px) between sections, + `gap-2` (8px) between cards, `p-2.5` (10px) card padding, `pt-5/pb-3` header — + a 448px max width keeps 3 cards ≥~120px each while staying a single focused + column. Verified with headless 1280×900 and 390×844 screenshots. + ⚠️ `SignupDialog` is a **default-only** export, so lazy-load it as `lazy(() => import('…/SignupDialog'))`. - **Pass the app on — Android only (2026-10-26):** the About page renders diff --git a/src/components/auth/LoginDialog.tsx b/src/components/auth/LoginDialog.tsx index c9e6919..622ddeb 100644 --- a/src/components/auth/LoginDialog.tsx +++ b/src/components/auth/LoginDialog.tsx @@ -321,47 +321,51 @@ export const LoginDialog: React.FC = ({ isOpen, onClose, onLog return ( - - - + + + {t('settingsUi.loginDialog.welcomeTitle')} - - {t('settingsUi.loginDialog.welcomeSubtitle')} + + {t('settingsUi.loginDialog.welcomeSubtitleBrowse')} -
- {/* Sign Up */} -
-
-
-
- - {t('settingsUi.loginDialog.newHere')} -
-

{t('settingsUi.loginDialog.createFree')}

- +
+ {/* Sign up — a compact row, not a second hero: the dialog should have + ONE dominant action per state, and logging in is the main path. */} +
+
+
+
+

{t('settingsUi.loginDialog.newHere')}

+

{t('settingsUi.loginDialog.createFree')}

+
+
- {t('settingsUi.loginDialog.orLogIn')} + {t('settingsUi.loginDialog.orLogIn')}
setLoginTab(v as typeof loginTab)} className="w-full"> - {/* Method cards — a clear three-way choice (the old 4-tab strip was - jargon-heavy). The secret-key option stays as a quiet link. */} + {/* Method cards — plain-language labels; protocol names move to a + tiny hint so newcomers aren't met with "NIP-46" as the main word. */}
{([ - { id: 'extension', Icon: Shield, label: t('settingsUi.loginDialog.browserExtensionTitle') }, - { id: isAndroid ? 'amber' : 'bunker', Icon: isAndroid ? Smartphone : Cloud, label: isAndroid ? t('settingsUi.loginDialog.loginWithAmber') : t('settingsUi.loginDialog.bunkerTitle') }, - { id: 'connect', Icon: QrCode, label: t('settingsUi.loginDialog.signInWithPhone') }, - ] as const).map(({ id, Icon, label }) => { + { id: 'extension', Icon: Shield, label: t('settingsUi.loginDialog.extensionCard'), hint: 'NIP-07' }, + { id: isAndroid ? 'amber' : 'bunker', Icon: isAndroid ? Smartphone : Cloud, label: t('settingsUi.loginDialog.signerCard'), hint: isAndroid ? 'NIP-55' : 'NIP-46' }, + { id: 'connect', Icon: QrCode, label: t('settingsUi.loginDialog.signInWithPhone'), hint: 'NIP-46' }, + ] as const).map(({ id, Icon, label, hint }) => { const active = loginTab === id; return ( ); })}
-
- -
{/* ── AMBER TAB ── */} @@ -576,35 +569,32 @@ export const LoginDialog: React.FC = ({ isOpen, onClose, onLog
- {/* ── BUNKER TAB ── */} + {/* ── SIGNER (BUNKER) TAB ── */} -
-
- {t('settingsUi.loginDialog.orPasteBunker')} -
-
- -
-
- -

{t('settingsUi.loginDialog.bunkerTitle')}

-

{t('settingsUi.loginDialog.bunkerPasteBefore')} bunker:// {t('settingsUi.loginDialog.bunkerPasteAfter')}

+
+
+ +

{t('settingsUi.loginDialog.signerCard')}

-
-

Amber: {t('settingsUi.loginDialog.amberInstructions')}

-

nsec.app: {t('settingsUi.loginDialog.nsecAppInstructions')}

-
-
- { setBunkerUri(e.target.value); setBunkerError(''); }} className='bg-black/60 border-blue-500/30 text-white placeholder:text-gray-600 text-xs font-mono' autoComplete='off' autoCorrect='off' spellCheck={false} /> - {bunkerError &&

{bunkerError}

} -
- - + {/* Instructions collapse: the default state is just input + Connect. */} +
+ + {t('settingsUi.loginDialog.whereBunker')} + +
+

Amber: {t('settingsUi.loginDialog.amberInstructions')}

+

nsec.app: {t('settingsUi.loginDialog.nsecAppInstructions')}

+ +
+
@@ -691,18 +681,32 @@ export const LoginDialog: React.FC = ({ isOpen, onClose, onLog {/* ── EXTENSION TAB ── */} -
- -
-

{t('settingsUi.loginDialog.browserExtensionTitle')}

-

{t('settingsUi.loginDialog.browserExtensionDesc')}

+
+
+ +

{t('settingsUi.loginDialog.browserExtensionTitle')}

-
+ + {/* Quiet fallback for advanced users; not a third hero action. */} +
+ +
diff --git a/src/i18n/cs/drift.ts b/src/i18n/cs/drift.ts index 3ed450b..f2db57e 100644 --- a/src/i18n/cs/drift.ts +++ b/src/i18n/cs/drift.ts @@ -93,6 +93,13 @@ const drift = { } }, "settingsUi": { + "loginDialog": { + "welcomeSubtitleBrowse": "Zaregistruj se nebo se přihlas — nebo si dál prohlížej.", + "extensionCard": "Rozšíření prohlížeče", + "signerCard": "Podepisovací aplikace", + "secretKeyLink": "Použít místo toho tajný klíč", + "whereBunker": "Kde získám bunker?", + }, "settingsPage": { "trust": "Důvěra", "trustSub": "Zdroje a zobrazení" diff --git a/src/i18n/de/drift.ts b/src/i18n/de/drift.ts index 8b0f18c..203cef0 100644 --- a/src/i18n/de/drift.ts +++ b/src/i18n/de/drift.ts @@ -95,6 +95,13 @@ const drift = { }, }, settingsUi: { + loginDialog: { + welcomeSubtitleBrowse: "Registriere dich oder melde dich an – oder stöbere weiter.", + extensionCard: "Browser-Erweiterung", + signerCard: "Signer-App", + secretKeyLink: "Stattdessen einen geheimen Schlüssel verwenden", + whereBunker: "Wo bekomme ich einen Bunker?", + }, settingsPage: { trust: "Vertrauen", trustSub: "Quellen und Anzeige", diff --git a/src/i18n/el/drift.ts b/src/i18n/el/drift.ts index 8d17f97..2d9d92b 100644 --- a/src/i18n/el/drift.ts +++ b/src/i18n/el/drift.ts @@ -97,6 +97,13 @@ const drift = { }, }, settingsUi: { + loginDialog: { + welcomeSubtitleBrowse: "Εγγραφείτε ή συνδεθείτε — ή συνεχίστε την περιήγηση.", + extensionCard: "Επέκταση προγράμματος περιήγησης", + signerCard: "Εφαρμογή υπογραφής", + secretKeyLink: "Χρήση μυστικού κλειδιού", + whereBunker: "Πού βρίσκω bunker;", + }, settingsPage: { trust: "Εμπιστοσύνη", trustSub: "Πηγές και εμφάνιση", diff --git a/src/i18n/en.ts b/src/i18n/en.ts index a98dccd..df12dea 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -4552,6 +4552,11 @@ settingsUi: { loginWithAmber: 'Login with Amber (Android)', welcomeTitle: 'Welcome to SkateSpots', welcomeSubtitle: 'Sign up or log in to continue', + welcomeSubtitleBrowse: 'Sign up or log in — or keep browsing.', + extensionCard: 'Browser extension', + signerCard: 'Signer app', + secretKeyLink: 'Use a secret key instead', + whereBunker: 'Where do I get a bunker?', newHere: 'New here?', createFree: 'Create a free account. No email required.', createAccount: 'Create Account', diff --git a/src/i18n/es.ts b/src/i18n/es.ts index 58acfc7..15d29cb 100644 --- a/src/i18n/es.ts +++ b/src/i18n/es.ts @@ -4552,6 +4552,11 @@ settingsUi: { loginWithAmber: 'Iniciar sesión con Amber (Android)', welcomeTitle: 'Bienvenido a SkateSpots', welcomeSubtitle: 'Regístrate o inicia sesión para continuar', + welcomeSubtitleBrowse: 'Regístrate o inicia sesión, o sigue explorando.', + extensionCard: 'Extensión del navegador', + signerCard: 'App firmante', + secretKeyLink: 'Usar una clave secreta', + whereBunker: '¿Dónde consigo un bunker?', newHere: '¿Nuevo por aquí?', createFree: 'Crea una cuenta gratis. Sin correo electrónico.', createAccount: 'Crear cuenta', diff --git a/src/i18n/fr/drift.ts b/src/i18n/fr/drift.ts index b8d0346..669be6c 100644 --- a/src/i18n/fr/drift.ts +++ b/src/i18n/fr/drift.ts @@ -95,6 +95,13 @@ const drift = { }, }, settingsUi: { + loginDialog: { + welcomeSubtitleBrowse: "Inscris-toi ou connecte-toi — ou continue à explorer.", + extensionCard: "Extension navigateur", + signerCard: "App signataire", + secretKeyLink: "Utiliser une clé secrète", + whereBunker: "Où trouver un bunker ?", + }, settingsPage: { trust: "Confiance", trustSub: "Sources et affichage", diff --git a/src/i18n/id/drift.ts b/src/i18n/id/drift.ts index 882a951..a2695ec 100644 --- a/src/i18n/id/drift.ts +++ b/src/i18n/id/drift.ts @@ -89,6 +89,13 @@ const driftStrings = { }, }, settingsUi: { + loginDialog: { + welcomeSubtitleBrowse: "Daftar atau masuk — atau tetap jelajahi.", + extensionCard: "Ekstensi peramban", + signerCard: "Aplikasi penandatangan", + secretKeyLink: "Gunakan kunci rahasia saja", + whereBunker: "Di mana saya mendapatkan bunker?", + }, settingsPage: { trust: 'Kepercayaan', trustSub: 'Sumber dan tampilan', diff --git a/src/i18n/it/drift.ts b/src/i18n/it/drift.ts index ac01c28..2c8db0d 100644 --- a/src/i18n/it/drift.ts +++ b/src/i18n/it/drift.ts @@ -95,6 +95,13 @@ const drift = { }, }, settingsUi: { + loginDialog: { + welcomeSubtitleBrowse: "Registrati o accedi — oppure continua a esplorare.", + extensionCard: "Estensione del browser", + signerCard: "App firmataria", + secretKeyLink: "Usa invece una chiave segreta", + whereBunker: "Dove trovo un bunker?", + }, settingsPage: { trust: "Fiducia", trustSub: "Fonti e visualizzazione", diff --git a/src/i18n/ja/drift.ts b/src/i18n/ja/drift.ts index af93c8d..97ee0be 100644 --- a/src/i18n/ja/drift.ts +++ b/src/i18n/ja/drift.ts @@ -105,6 +105,13 @@ const drift: Dict = { }, }, settingsUi: { + loginDialog: { + welcomeSubtitleBrowse: "登録またはログイン — または閲覧を続ける。", + extensionCard: "ブラウザ拡張", + signerCard: "署名アプリ", + secretKeyLink: "代わりに秘密鍵を使う", + whereBunker: "バンカーはどこで入手?", + }, settingsPage: { trust: '信頼', trustSub: 'ソースと表示', diff --git a/src/i18n/ko/drift.ts b/src/i18n/ko/drift.ts index 9cd5dd5..a9afb00 100644 --- a/src/i18n/ko/drift.ts +++ b/src/i18n/ko/drift.ts @@ -105,6 +105,13 @@ const drift: Dict = { }, }, settingsUi: { + loginDialog: { + welcomeSubtitleBrowse: "가입하거나 로그인하세요 — 또는 계속 둘러보세요.", + extensionCard: "브라우저 확장", + signerCard: "서명 앱", + secretKeyLink: "대신 비밀 키 사용", + whereBunker: "벙커는 어디서 구하나요?", + }, settingsPage: { trust: '신뢰', trustSub: '출처와 표시', diff --git a/src/i18n/nl/drift.ts b/src/i18n/nl/drift.ts index 16b7e2b..46b51de 100644 --- a/src/i18n/nl/drift.ts +++ b/src/i18n/nl/drift.ts @@ -93,6 +93,13 @@ const drift = { } }, "settingsUi": { + "loginDialog": { + "welcomeSubtitleBrowse": "Meld je aan of log in — of blijf rondkijken.", + "extensionCard": "Browserextensie", + "signerCard": "Signeer-app", + "secretKeyLink": "Gebruik in plaats daarvan een geheime sleutel", + "whereBunker": "Waar vind ik een bunker?", + }, "settingsPage": { "trust": "Vertrouwen", "trustSub": "Bronnen en weergave" diff --git a/src/i18n/pl/drift.ts b/src/i18n/pl/drift.ts index 3af6297..c0c6c09 100644 --- a/src/i18n/pl/drift.ts +++ b/src/i18n/pl/drift.ts @@ -93,6 +93,13 @@ const drift = { } }, "settingsUi": { + "loginDialog": { + "welcomeSubtitleBrowse": "Zarejestruj się lub zaloguj — albo przeglądaj dalej.", + "extensionCard": "Rozszerzenie przeglądarki", + "signerCard": "Aplikacja podpisująca", + "secretKeyLink": "Użyj zamiast tego klucza tajnego", + "whereBunker": "Gdzie znajdę bunkier?", + }, "settingsPage": { "trust": "Zaufanie", "trustSub": "Źródła i wyświetlanie" diff --git a/src/i18n/pt/drift.ts b/src/i18n/pt/drift.ts index 15ed3a7..03e8653 100644 --- a/src/i18n/pt/drift.ts +++ b/src/i18n/pt/drift.ts @@ -95,6 +95,13 @@ const drift = { }, }, settingsUi: { + loginDialog: { + welcomeSubtitleBrowse: "Cadastre-se ou entre — ou continue explorando.", + extensionCard: "Extensão do navegador", + signerCard: "App assinador", + secretKeyLink: "Usar uma chave secreta", + whereBunker: "Onde consigo um bunker?", + }, settingsPage: { trust: "Confiança", trustSub: "Fontes e exibição", diff --git a/src/i18n/ro/drift.ts b/src/i18n/ro/drift.ts index 763ae2e..f4a526c 100644 --- a/src/i18n/ro/drift.ts +++ b/src/i18n/ro/drift.ts @@ -93,6 +93,13 @@ const drift = { } }, "settingsUi": { + "loginDialog": { + "welcomeSubtitleBrowse": "Înregistrează-te sau autentifică-te — sau continuă să explorezi.", + "extensionCard": "Extensie de browser", + "signerCard": "Aplicație de semnare", + "secretKeyLink": "Folosește în schimb o cheie secretă", + "whereBunker": "De unde obțin un bunker?", + }, "settingsPage": { "trust": "Încredere", "trustSub": "Surse și afișare" diff --git a/src/i18n/ru/drift.ts b/src/i18n/ru/drift.ts index 86867ea..5d9b6a6 100644 --- a/src/i18n/ru/drift.ts +++ b/src/i18n/ru/drift.ts @@ -97,6 +97,13 @@ const drift = { }, }, settingsUi: { + loginDialog: { + welcomeSubtitleBrowse: "Зарегистрируйтесь или войдите — или продолжайте просмотр.", + extensionCard: "Расширение браузера", + signerCard: "Приложение-подписант", + secretKeyLink: "Использовать секретный ключ", + whereBunker: "Где взять бункер?", + }, settingsPage: { trust: "Доверие", trustSub: "Источники и отображение", diff --git a/src/i18n/sv/drift.ts b/src/i18n/sv/drift.ts index 553b516..f8b08ef 100644 --- a/src/i18n/sv/drift.ts +++ b/src/i18n/sv/drift.ts @@ -97,6 +97,13 @@ const drift = { }, }, settingsUi: { + loginDialog: { + welcomeSubtitleBrowse: "Registrera dig eller logga in — eller fortsätt bläddra.", + extensionCard: "Webbläsartillägg", + signerCard: "Signeringsapp", + secretKeyLink: "Använd en hemlig nyckel i stället", + whereBunker: "Var hittar jag en bunker?", + }, settingsPage: { trust: "Förtroende", trustSub: "Källor och visning", diff --git a/src/i18n/th/drift.ts b/src/i18n/th/drift.ts index 3ad00b5..ca0d6d3 100644 --- a/src/i18n/th/drift.ts +++ b/src/i18n/th/drift.ts @@ -89,6 +89,13 @@ const driftStrings = { }, }, settingsUi: { + loginDialog: { + welcomeSubtitleBrowse: "สมัครหรือเข้าสู่ระบบ — หรือสำรวจต่อ", + extensionCard: "ส่วนขยายเบราว์เซอร์", + signerCard: "แอปเซ็นชื่อ", + secretKeyLink: "ใช้คีย์ลับแทน", + whereBunker: "หา bunker ได้ที่ไหน?", + }, settingsPage: { trust: 'ความน่าเชื่อถือ', trustSub: 'แหล่งที่มาและการแสดงผล', diff --git a/src/i18n/tr/drift.ts b/src/i18n/tr/drift.ts index b78001a..e25987d 100644 --- a/src/i18n/tr/drift.ts +++ b/src/i18n/tr/drift.ts @@ -89,6 +89,13 @@ const driftStrings = { }, }, settingsUi: { + loginDialog: { + welcomeSubtitleBrowse: "Kaydol veya giriş yap — ya da göz atmaya devam et.", + extensionCard: "Tarayıcı uzantısı", + signerCard: "İmzalayıcı uygulama", + secretKeyLink: "Bunun yerine gizli anahtar kullan", + whereBunker: "Bunker'ı nereden bulurum?", + }, settingsPage: { trust: 'Güven', trustSub: 'Kaynaklar ve görünüm', diff --git a/src/i18n/uk/drift.ts b/src/i18n/uk/drift.ts index ee57bb0..cbd8282 100644 --- a/src/i18n/uk/drift.ts +++ b/src/i18n/uk/drift.ts @@ -97,6 +97,13 @@ const drift = { }, }, settingsUi: { + loginDialog: { + welcomeSubtitleBrowse: "Зареєструйтеся або увійдіть — або продовжуйте перегляд.", + extensionCard: "Розширення браузера", + signerCard: "Застосунок-підписувач", + secretKeyLink: "Використати секретний ключ", + whereBunker: "Де взяти бункер?", + }, settingsPage: { trust: "Довіра", trustSub: "Джерела та відображення", diff --git a/src/i18n/vi/drift.ts b/src/i18n/vi/drift.ts index 78a15f0..089e5e3 100644 --- a/src/i18n/vi/drift.ts +++ b/src/i18n/vi/drift.ts @@ -89,6 +89,13 @@ const driftStrings = { }, }, settingsUi: { + loginDialog: { + welcomeSubtitleBrowse: "Đăng ký hoặc đăng nhập — hoặc tiếp tục khám phá.", + extensionCard: "Tiện ích trình duyệt", + signerCard: "Ứng dụng ký", + secretKeyLink: "Dùng khóa bí mật thay thế", + whereBunker: "Tìm bunker ở đâu?", + }, settingsPage: { trust: 'Tin cậy', trustSub: 'Nguồn và hiển thị', diff --git a/src/i18n/zh-CN/drift.ts b/src/i18n/zh-CN/drift.ts index 7c00a06..2c93e93 100644 --- a/src/i18n/zh-CN/drift.ts +++ b/src/i18n/zh-CN/drift.ts @@ -105,6 +105,13 @@ const drift: Dict = { }, }, settingsUi: { + loginDialog: { + welcomeSubtitleBrowse: "注册或登录 — 或继续浏览。", + extensionCard: "浏览器扩展", + signerCard: "签名应用", + secretKeyLink: "改用私钥", + whereBunker: "在哪里获取 bunker?", + }, settingsPage: { trust: '信任', trustSub: '来源与显示', diff --git a/src/i18n/zh-TW/drift.ts b/src/i18n/zh-TW/drift.ts index 07497ca..90c7d5b 100644 --- a/src/i18n/zh-TW/drift.ts +++ b/src/i18n/zh-TW/drift.ts @@ -105,6 +105,13 @@ const drift: Dict = { }, }, settingsUi: { + loginDialog: { + welcomeSubtitleBrowse: "註冊或登入 — 或繼續瀏覽。", + extensionCard: "瀏覽器擴充功能", + signerCard: "簽章應用程式", + secretKeyLink: "改用私密金鑰", + whereBunker: "在哪裡取得 bunker?", + }, settingsPage: { trust: '信任', trustSub: '來源與顯示',