diff --git a/board-frontend/src/app.rs b/board-frontend/src/app.rs
index b7af009..30cc4d6 100644
--- a/board-frontend/src/app.rs
+++ b/board-frontend/src/app.rs
@@ -1,28 +1,29 @@
use crate::hand_view::HandView;
+use crate::remote_view::RemoteGameView;
use crate::tile_view::PlacedTileView;
+use crate::types::SelectedTile;
use board_shared::board::Board;
use board_shared::deck::RngDeck;
use board_shared::expr::is_valid_guess;
use board_shared::game::Game;
use board_shared::position::Grid2d;
use board_shared::tile::Tile;
+use futures::StreamExt;
use gloo_dialogs::alert;
+use gloo_net::websocket::futures::WebSocket;
+use std::cell::RefCell;
+use std::rc::Rc;
+use web_sys::HtmlInputElement;
use yew::prelude::*;
-enum SelectedTile {
- InHand(usize),
- Equals,
- None,
-}
-
#[derive(Properties, PartialEq)]
-struct BoardViewProps {
- board: Board,
- on_click: Callback<(usize, usize)>,
+pub struct BoardViewProps {
+ pub board: Board,
+ pub on_click: Callback<(usize, usize)>,
}
#[function_component(BoardView)]
-fn board_view(BoardViewProps { board, on_click }: &BoardViewProps) -> Html {
+pub fn board_view(BoardViewProps { board, on_click }: &BoardViewProps) -> Html {
html! {
{ (0..board.height()).map(|y| html! {
@@ -36,8 +37,8 @@ fn board_view(BoardViewProps { board, on_click }: &BoardViewProps) -> Html {
}
}
-#[function_component(App)]
-pub fn app() -> Html {
+#[function_component(GameView)]
+pub fn game_view() -> Html {
let game = use_state(Game::default);
let current_game = use_state(Game::default);
@@ -128,3 +129,86 @@ pub fn app() -> Html {
}
}
+
+#[function_component(App)]
+pub fn app() -> Html {
+ let remote = use_state(|| "ws://localhost:8081/ws".to_string());
+ let player_name = use_state(|| None);
+ let room_name = use_state(|| None);
+
+ let player_name_ref = use_node_ref();
+ let room_name_ref = use_node_ref();
+ let on_create_click = {
+ let player_name = player_name.clone();
+ let player_name_ref = player_name_ref.clone();
+ Callback::from(move |_| {
+ player_name.set(Some(
+ player_name_ref
+ .cast::()
+ .expect("player_name_ref is not attached to a input element")
+ .value(),
+ ));
+ })
+ };
+ let on_join_click = {
+ let player_name = player_name.clone();
+ let player_name_ref = player_name_ref.clone();
+ let room_name = room_name.clone();
+ let room_name_ref = room_name_ref.clone();
+ Callback::from(move |_| {
+ player_name.set(Some(
+ player_name_ref
+ .cast::()
+ .expect("player_name_ref is not attached to a input element")
+ .value(),
+ ));
+ room_name.set(Some(
+ room_name_ref
+ .cast::()
+ .expect("room_name_ref is not attached to a input element")
+ .value(),
+ ));
+ })
+ };
+
+ if let Some(player_name) = (*player_name).as_ref() {
+ let ws = WebSocket::open(remote.as_ref()).unwrap();
+ let (write, read) = ws.split();
+
+ return html! {
+
+
+
+ };
+ }
+
+ html! {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+}
diff --git a/board-frontend/src/main.rs b/board-frontend/src/main.rs
index 8a3496e..6414221 100644
--- a/board-frontend/src/main.rs
+++ b/board-frontend/src/main.rs
@@ -1,6 +1,8 @@
mod app;
mod hand_view;
+mod remote_view;
mod tile_view;
+mod types;
use app::App;
diff --git a/board-frontend/src/remote_view.rs b/board-frontend/src/remote_view.rs
new file mode 100644
index 0000000..dd9c297
--- /dev/null
+++ b/board-frontend/src/remote_view.rs
@@ -0,0 +1,149 @@
+use crate::app::BoardView;
+use crate::types::SelectedTile;
+use board_network::protocol::{ClientMessage, ServerMessage};
+use board_shared::game::Game;
+use futures::stream::{SplitSink, SplitStream};
+use futures::{SinkExt, StreamExt};
+use gloo_dialogs::alert;
+use gloo_net::websocket::futures::WebSocket;
+use gloo_net::websocket::Message;
+use std::cell::RefCell;
+use std::rc::Rc;
+use yew::platform::spawn_local;
+use yew::prelude::*;
+
+#[derive(Properties)]
+pub struct RemoteGameViewProps {
+ pub write: Rc>>,
+ pub read: Rc>>,
+ pub player_name: String,
+ pub room_name: Option,
+}
+
+impl PartialEq for RemoteGameViewProps {
+ fn eq(&self, other: &Self) -> bool {
+ Rc::ptr_eq(&self.write, &other.write)
+ && Rc::ptr_eq(&self.read, &other.read)
+ && self.player_name == other.player_name
+ && self.room_name == other.room_name
+ }
+}
+
+#[function_component(RemoteGameView)]
+pub fn remote_game_view(
+ RemoteGameViewProps {
+ write,
+ read,
+ player_name,
+ room_name,
+ }: &RemoteGameViewProps,
+) -> Html {
+ macro_rules! send_client_message {
+ ($write:expr, $message:expr) => {{
+ let write = $write.clone();
+ spawn_local(async move {
+ write
+ .borrow_mut()
+ .send(Message::Text(
+ serde_json::to_string(&$message).expect("Cannot serialize"),
+ ))
+ .await
+ .unwrap();
+ });
+ }};
+ }
+
+ let selected_tile = use_state(|| SelectedTile::None);
+ let is_started = use_state(|| false);
+ let current_player_turn = use_state(|| 0);
+ let game = use_state(Game::default);
+ {
+ let player_name = player_name.clone();
+ let room_name = room_name.clone();
+ let write = write.clone();
+ use_effect_with_deps(
+ move |_| {
+ send_client_message!(
+ write,
+ if let Some(room_name) = room_name {
+ ClientMessage::JoinRoom(room_name, player_name)
+ } else {
+ ClientMessage::CreateRoom(player_name)
+ }
+ );
+ },
+ (),
+ );
+
+ let is_started = is_started.clone();
+ let current_player_turn = current_player_turn.clone();
+ let read = read.clone();
+ use_effect_with_deps(
+ move |_| {
+ spawn_local(async move {
+ while let Some(event) = read.borrow_mut().next().await {
+ if let Message::Text(msg) = event.unwrap() {
+ match serde_json::from_str::(&msg) {
+ Ok(ServerMessage::JoinedRoom {
+ room_name,
+ has_started,
+ ..
+ }) => {
+ alert(&format!("Joined room {}", room_name));
+ is_started.set(has_started);
+ }
+ Ok(ServerMessage::PlayerTurn(player_id)) => {
+ current_player_turn.set(player_id);
+ is_started.set(true);
+ }
+ r => {
+ alert(&format!("{:?}", r));
+ }
+ };
+ }
+ }
+ });
+ || {}
+ },
+ (),
+ );
+ }
+
+ let on_validate_click = {
+ let write = write.clone();
+ Callback::from(move |_| {
+ send_client_message!(write, ClientMessage::Validate);
+ })
+ };
+ let on_start_game_click = {
+ let write = write.clone();
+ Callback::from(move |_| {
+ send_client_message!(write, ClientMessage::StartGame);
+ })
+ };
+ let on_equals_select = {
+ Callback::from(move |_| {
+ selected_tile.set(SelectedTile::Equals);
+ })
+ };
+
+ html! {
+
+ {"Remote Game"}
+
+
+
+ if *is_started {
+
+ } else {
+
+ }
+
+ if *is_started {
+
+
{format!("Player {}'s turn", *current_player_turn)}
+
+ }
+
+ }
+}
diff --git a/board-frontend/src/types.rs b/board-frontend/src/types.rs
new file mode 100644
index 0000000..010adb1
--- /dev/null
+++ b/board-frontend/src/types.rs
@@ -0,0 +1,5 @@
+pub enum SelectedTile {
+ InHand(usize),
+ Equals,
+ None,
+}
diff --git a/board-network/src/protocol.rs b/board-network/src/protocol.rs
index 5ce1e62..7ebc34f 100644
--- a/board-network/src/protocol.rs
+++ b/board-network/src/protocol.rs
@@ -35,6 +35,7 @@ pub enum ServerMessage {
room_name: String,
players: Vec<(String, u32, bool)>,
active_player: usize,
+ has_started: bool,
},
/// Notify that the room cannot be joined.
JoinFailed(String),
diff --git a/board-server/src/room.rs b/board-server/src/room.rs
index ffbe549..569947d 100644
--- a/board-server/src/room.rs
+++ b/board-server/src/room.rs
@@ -72,6 +72,7 @@ impl Room {
.map(|p| (p.name.clone(), p.score, p.ws.is_some()))
.collect(),
active_player: self.active_player,
+ has_started: self.has_started,
})?;
Ok(())