onboarding: honest TLS badge, localized hero + nick hint, channel picker with suggestions

This commit is contained in:
Jean Chevronnet 2026-07-05 20:13:07 +00:00
parent 829ebf2e85
commit 0d89bc701a
14 changed files with 92 additions and 23 deletions

View file

@ -4,15 +4,21 @@
"guestIdent": "Invité"
},
"startup": {
"channels": ["#accueil"]
"channels": [
"#accueil"
],
"suggestions": [
"#accueil",
"#taverne",
"#musique",
"#devs",
"#orbit"
]
},
"branding": {
"name": "Tchatou",
"icon": "https://tchatou.fr/static/img/favicon.svg",
"url": "https://tchatou.fr",
"tagline": "Le tchat français,",
"taglineEm": "en direct.",
"subtitle": "Salons publics, messages privés, modération. Choisis un pseudo, ou crée un compte — et rejoins la conversation, avec toute la France.",
"projectUrl": "https://orbit.tchatou.fr",
"links": [],
"accent": ""
@ -40,6 +46,11 @@
"linkPreviews": true,
"multiNetwork": true
},
"plugins": ["/app/plugins/orbit-clock.js", "/app/plugins/orbit-copy.js", "/app/plugins/orbit-games.js", "/app/plugins/orbit-invite.js"],
"plugins": [
"/app/plugins/orbit-clock.js",
"/app/plugins/orbit-copy.js",
"/app/plugins/orbit-games.js",
"/app/plugins/orbit-invite.js"
],
"builtins": []
}

View file

@ -143,6 +143,7 @@ export function ConnectScreen() {
raw.split(',').map((c) => c.trim()).filter(Boolean)
.map((c) => (c.startsWith('#') || c.startsWith('&') ? c : `#${c}`));
const chan = parseChannels(chanField)[0] || cfg.startup.channels[0];
const suggestions = cfg.startup.suggestions?.length ? cfg.startup.suggestions : cfg.startup.channels;
const connecting = status === 'connecting';
const ready = nick.trim().length >= 2;
@ -169,10 +170,10 @@ export function ConnectScreen() {
</div>
<h1 className="cjoin__title">
{cfg.branding.tagline}<br />
<em>{cfg.branding.taglineEm}</em>
{cfg.branding.tagline || t('connect.tagline')}<br />
<em>{cfg.branding.taglineEm || t('connect.taglineEm')}</em>
</h1>
<p className="cjoin__sub">{cfg.branding.subtitle}</p>
<p className="cjoin__sub">{cfg.branding.subtitle || t('connect.subtitle')}</p>
<form onSubmit={(e) => { e.preventDefault(); go(); }}>
<div className="cjoin__composer">
@ -192,11 +193,15 @@ export function ConnectScreen() {
{connecting ? <span className="cjoin__sendspin" /> : <span className="arr"></span>}
</button>
</div>
<p className="cjoin__hint">{t('connect.nickHint')}</p>
<div className="cjoin__row">
<label className="cjoin__chan">{t('connect.joinHint')}
<input className="cjoin__chan-in" value={chanField} spellCheck={false} autoComplete="off"
aria-label={t('connect.joinHint')} onChange={(e) => setChanField(e.target.value)} />
list="cjoin-chans" aria-label={t('connect.channelAria')} onChange={(e) => setChanField(e.target.value)} />
<datalist id="cjoin-chans">
{suggestions.map((c) => <option key={c} value={c} />)}
</datalist>
</label>
<button type="button" className="cjoin__pw-t" onClick={() => setShowPw((v) => !v)}>
{showPw ? t('connect.hidePassword') : t('connect.registered')}

View file

@ -17,6 +17,8 @@ export interface AppConfig {
startup: {
/** Channels auto-joined on connect (the first is the primary/active one). */
channels: string[];
/** Suggested channels offered in the connect-screen channel picker (datalist). */
suggestions?: string[];
};
branding: {
name: string; // app/network name shown in the UI
@ -88,14 +90,14 @@ export type PluginEntry =
const DEFAULT_CONFIG: AppConfig = {
server: { url: 'wss://www.swaygo.fr/irc/', guestIdent: 'Invité' },
startup: { channels: ['#accueil'] },
startup: { channels: ['#accueil'], suggestions: ['#accueil', '#taverne', '#musique', '#devs', '#orbit'] },
branding: {
name: 'Tchatou',
icon: 'https://tchatou.fr/static/img/favicon.svg',
url: 'https://tchatou.fr',
tagline: 'Le tchat français,',
taglineEm: 'en direct.',
subtitle: 'Salons publics, messages privés, modération — et zéro inscription. Choisis un pseudo et rejoins la conversation, avec toute la France.',
tagline: '', // empty → connect screen falls back to the localised connect.tagline
taglineEm: '',
subtitle: '',
projectUrl: 'https://orbit.tchatou.fr',
},
turnstile: { enabled: true, sitekey: '0x4AAAAAADlXGeFQ-Aj3Kitp' },

View file

@ -59,7 +59,12 @@
"passwordPlaceholder": "Passwort (falls dein Nick registriert ist)",
"passwordLabel": "Passwort",
"noData": "Keine Registrierung erforderlich",
"encrypted": "Ende-zu-Ende-verschlüsselt",
"encrypted": "Verschlüsselt (TLS)",
"tagline": "Der französische Chat,",
"taglineEm": "live.",
"subtitle": "Öffentliche Räume, private Nachrichten, Moderation. Wähle einen Spitznamen oder erstelle ein Konto und tritt dem Gespräch bei, mit ganz Frankreich.",
"channelAria": "Kanal zum Beitreten",
"nickHint": "2 bis 30 Zeichen, keine Leerzeichen",
"ircv3": "IRCv3",
"helpButton": "Hilfe & FAQ",
"forgotButton": "Passwort vergessen?",

View file

@ -59,7 +59,12 @@
"passwordPlaceholder": "Password (if your nick is registered)",
"passwordLabel": "Password",
"noData": "No sign-up required",
"encrypted": "End-to-end encrypted",
"encrypted": "Encrypted (TLS)",
"tagline": "The French chat,",
"taglineEm": "live.",
"subtitle": "Public rooms, private messages, moderation. Pick a nickname or create an account, and join the conversation with all of France.",
"channelAria": "Channel to join",
"nickHint": "2 to 30 characters, no spaces",
"ircv3": "IRCv3",
"helpButton": "Help & FAQ",
"forgotButton": "Forgot password?",

View file

@ -59,7 +59,12 @@
"passwordPlaceholder": "Contraseña (si tu nick está registrado)",
"passwordLabel": "Contraseña",
"noData": "Sin registro necesario",
"encrypted": "Cifrado de extremo a extremo",
"encrypted": "Cifrado (TLS)",
"tagline": "El chat francés,",
"taglineEm": "en directo.",
"subtitle": "Salas públicas, mensajes privados, moderación. Elige un apodo o crea una cuenta y únete a la conversación, con toda Francia.",
"channelAria": "Canal al que unirse",
"nickHint": "2 a 30 caracteres, sin espacios",
"ircv3": "IRCv3",
"helpButton": "Ayuda & FAQ",
"forgotButton": "¿Olvidaste tu contraseña?",

View file

@ -59,7 +59,12 @@
"passwordPlaceholder": "Mot de passe (si ton pseudo est enregistré)",
"passwordLabel": "Mot de passe",
"noData": "Aucune inscription",
"encrypted": "Chiffré de bout en bout",
"encrypted": "Chiffré (TLS)",
"tagline": "Le tchat français,",
"taglineEm": "en direct.",
"subtitle": "Salons publics, messages privés, modération. Choisis un pseudo, ou crée un compte, et rejoins la conversation, avec toute la France.",
"channelAria": "Salon à rejoindre",
"nickHint": "2 à 30 caractères, sans espaces",
"ircv3": "IRCv3",
"helpButton": "Aide & FAQ",
"forgotButton": "Mot de passe oublié ?",

View file

@ -59,7 +59,12 @@
"passwordPlaceholder": "Password (se il tuo nick è registrato)",
"passwordLabel": "Password",
"noData": "Nessuna registrazione richiesta",
"encrypted": "Crittografia end-to-end",
"encrypted": "Crittografato (TLS)",
"tagline": "La chat francese,",
"taglineEm": "in diretta.",
"subtitle": "Stanze pubbliche, messaggi privati, moderazione. Scegli un nickname o crea un account e unisciti alla conversazione, con tutta la Francia.",
"channelAria": "Canale a cui unirsi",
"nickHint": "2 a 30 caratteri, senza spazi",
"ircv3": "IRCv3",
"helpButton": "Aiuto & FAQ",
"forgotButton": "Password dimenticata?",

View file

@ -59,7 +59,12 @@
"passwordPlaceholder": "पासवर्ड (यदि तपाईंको उपनाम दर्ता छ)",
"passwordLabel": "पासवर्ड",
"noData": "दर्ता आवश्यक छैन",
"encrypted": "एन्ड-टु-एन्ड एन्क्रिप्टेड",
"encrypted": "इन्क्रिप्टेड (TLS)",
"tagline": "फ्रान्सेली च्याट,",
"taglineEm": "प्रत्यक्ष।",
"subtitle": "सार्वजनिक कोठाहरू, निजी सन्देशहरू, मध्यस्थता। उपनाम छान्नुहोस् वा खाता बनाउनुहोस् र सम्पूर्ण फ्रान्ससँग कुराकानीमा सामेल हुनुहोस्।",
"channelAria": "सामेल हुने च्यानल",
"nickHint": "२ देखि ३० अक्षर, खाली ठाउँ बिना",
"ircv3": "IRCv3",
"helpButton": "मद्दत र FAQ",
"forgotButton": "पासवर्ड बिर्सनुभयो?",

View file

@ -59,7 +59,12 @@
"passwordPlaceholder": "Senha (se seu apelido estiver registrado)",
"passwordLabel": "Senha",
"noData": "Sem cadastro necessário",
"encrypted": "Criptografado de ponta a ponta",
"encrypted": "Criptografado (TLS)",
"tagline": "O bate-papo francês,",
"taglineEm": "ao vivo.",
"subtitle": "Salas públicas, mensagens privadas, moderação. Escolha um apelido ou crie uma conta e entre na conversa, com toda a França.",
"channelAria": "Canal para entrar",
"nickHint": "2 a 30 caracteres, sem espaços",
"ircv3": "IRCv3",
"helpButton": "Ajuda & FAQ",
"forgotButton": "Esqueceu a senha?",

View file

@ -59,7 +59,12 @@
"passwordPlaceholder": "Palavra-passe (se a tua alcunha estiver registada)",
"passwordLabel": "Palavra-passe",
"noData": "Sem registo necessário",
"encrypted": "Encriptado de ponta a ponta",
"encrypted": "Encriptado (TLS)",
"tagline": "O chat francês,",
"taglineEm": "em direto.",
"subtitle": "Salas públicas, mensagens privadas, moderação. Escolhe um pseudónimo ou cria uma conta e junta-te à conversa, com toda a França.",
"channelAria": "Canal a entrar",
"nickHint": "2 a 30 caracteres, sem espaços",
"ircv3": "IRCv3",
"helpButton": "Ajuda & FAQ",
"forgotButton": "Esqueceste-te da palavra-passe?",

View file

@ -59,7 +59,12 @@
"passwordPlaceholder": "Пароль (если псевдоним зарегистрирован)",
"passwordLabel": "Пароль",
"noData": "Регистрация не нужна",
"encrypted": "Сквозное шифрование",
"encrypted": "Шифрование (TLS)",
"tagline": "Французский чат,",
"taglineEm": "в прямом эфире.",
"subtitle": "Публичные комнаты, личные сообщения, модерация. Выберите никнейм или создайте аккаунт и присоединяйтесь к беседе, со всей Францией.",
"channelAria": "Канал для входа",
"nickHint": "От 2 до 30 символов, без пробелов",
"ircv3": "IRCv3",
"helpButton": "Помощь и FAQ",
"forgotButton": "Забыли пароль?",

View file

@ -59,7 +59,12 @@
"passwordPlaceholder": "Şifre (takma adınız kayıtlıysa)",
"passwordLabel": "Şifre",
"noData": "Kayıt gerekmez",
"encrypted": "Uçtan uca şifreli",
"encrypted": "Şifreli (TLS)",
"tagline": "Fransız sohbeti,",
"taglineEm": "canlı.",
"subtitle": "Herkese açık odalar, özel mesajlar, moderasyon. Bir takma ad seç ya da hesap oluştur ve tüm Fransa ile sohbete katıl.",
"channelAria": "Katılınacak kanal",
"nickHint": "2 ila 30 karakter, boşluksuz",
"ircv3": "IRCv3",
"helpButton": "Yardım & SSS",
"forgotButton": "Şifremi unuttum?",

View file

@ -110,6 +110,7 @@ input, textarea { font-family: inherit; }
.cjoin__sendspin { width: 16px; height: 16px; border-radius: 50%; border: 2px solid rgba(255,255,255,.4); border-top-color: #fff; animation: c-spin .7s linear infinite; }
@keyframes c-spin { to { transform: rotate(360deg); } }
.cjoin__hint { margin: .55rem 0 0 .2rem; font-size: .76rem; color: var(--faint); }
.cjoin__row { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; margin-top: .9rem; }
.cjoin__chip { font-size: .84rem; color: var(--muted); }
.cjoin__chip b { color: var(--ink); font-weight: 700; }