2026-03-25 16:18:17 +01:00
|
|
|
use futures::channel::mpsc::UnboundedSender;
|
|
|
|
|
use leptos::prelude::*;
|
|
|
|
|
|
|
|
|
|
use crate::app::NetCommand;
|
2026-03-29 17:15:22 +02:00
|
|
|
use crate::i18n::*;
|
2026-03-25 16:18:17 +01:00
|
|
|
|
|
|
|
|
#[component]
|
|
|
|
|
pub fn LoginScreen(error: Option<String>) -> impl IntoView {
|
2026-03-29 17:15:22 +02:00
|
|
|
let i18n = use_i18n();
|
2026-03-25 16:18:17 +01:00
|
|
|
let (room_name, set_room_name) = signal(String::new());
|
|
|
|
|
|
|
|
|
|
let cmd_tx = use_context::<UnboundedSender<NetCommand>>()
|
|
|
|
|
.expect("UnboundedSender<NetCommand> not found in context");
|
|
|
|
|
|
|
|
|
|
let cmd_tx_create = cmd_tx.clone();
|
2026-03-29 19:19:33 +02:00
|
|
|
let cmd_tx_join = cmd_tx.clone();
|
|
|
|
|
let cmd_tx_bot = cmd_tx;
|
2026-03-25 16:18:17 +01:00
|
|
|
|
|
|
|
|
view! {
|
2026-04-09 15:33:47 +02:00
|
|
|
<div class="login-card">
|
|
|
|
|
// ── Decorative board header ─────────────────────────────────────
|
|
|
|
|
<div class="login-card-header">
|
|
|
|
|
<div class="login-board-stripe"></div>
|
2026-03-29 17:15:22 +02:00
|
|
|
</div>
|
|
|
|
|
|
2026-04-09 15:33:47 +02:00
|
|
|
// ── Card body ──────────────────────────────────────────────────
|
|
|
|
|
<div class="login-card-body">
|
|
|
|
|
<div class="login-lang-switcher">
|
|
|
|
|
<div class="lang-switcher">
|
|
|
|
|
<button
|
|
|
|
|
class:lang-active=move || i18n.get_locale() == Locale::en
|
|
|
|
|
on:click=move |_| i18n.set_locale(Locale::en)
|
|
|
|
|
>"EN"</button>
|
|
|
|
|
<button
|
|
|
|
|
class:lang-active=move || i18n.get_locale() == Locale::fr
|
|
|
|
|
on:click=move |_| i18n.set_locale(Locale::fr)
|
|
|
|
|
>"FR"</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-03-25 16:18:17 +01:00
|
|
|
|
2026-04-09 15:33:47 +02:00
|
|
|
<h1 class="login-title">"Trictrac"</h1>
|
|
|
|
|
<p class="login-subtitle">
|
|
|
|
|
<em>"Jeu de trictrac"</em>
|
|
|
|
|
" — "
|
|
|
|
|
<em>"XVIII" <sup>"e"</sup> " siècle"</em>
|
|
|
|
|
</p>
|
2026-03-25 16:18:17 +01:00
|
|
|
|
2026-04-09 15:33:47 +02:00
|
|
|
<div class="login-ornament">"✦"</div>
|
2026-03-25 16:18:17 +01:00
|
|
|
|
2026-04-09 15:33:47 +02:00
|
|
|
{error.map(|err| view! { <p class="error-msg">{err}</p> })}
|
2026-03-25 16:18:17 +01:00
|
|
|
|
2026-04-09 15:33:47 +02:00
|
|
|
<input
|
|
|
|
|
class="login-input"
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder=move || t_string!(i18n, room_name_placeholder)
|
|
|
|
|
prop:value=move || room_name.get()
|
|
|
|
|
on:input=move |ev| set_room_name.set(event_target_value(&ev))
|
|
|
|
|
/>
|
2026-03-29 19:19:33 +02:00
|
|
|
|
2026-04-09 15:33:47 +02:00
|
|
|
<div class="login-actions">
|
|
|
|
|
<button
|
|
|
|
|
class="login-btn login-btn-primary"
|
|
|
|
|
disabled=move || room_name.get().is_empty()
|
|
|
|
|
on:click=move |_| {
|
|
|
|
|
cmd_tx_create
|
|
|
|
|
.unbounded_send(NetCommand::CreateRoom { room: room_name.get() })
|
|
|
|
|
.ok();
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
{t!(i18n, create_room)}
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
class="login-btn login-btn-secondary"
|
|
|
|
|
disabled=move || room_name.get().is_empty()
|
|
|
|
|
on:click=move |_| {
|
|
|
|
|
cmd_tx_join
|
|
|
|
|
.unbounded_send(NetCommand::JoinRoom { room: room_name.get() })
|
|
|
|
|
.ok();
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
{t!(i18n, join_room)}
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
class="login-btn login-btn-bot"
|
|
|
|
|
on:click=move |_| {
|
|
|
|
|
cmd_tx_bot.unbounded_send(NetCommand::PlayVsBot).ok();
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
{t!(i18n, play_vs_bot)}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-03-25 16:18:17 +01:00
|
|
|
</div>
|
|
|
|
|
}
|
|
|
|
|
}
|