From 105cd880f2723448aca66550543f830fa154dbea Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Fri, 29 Apr 2022 18:30:41 +0900 Subject: [PATCH] Impl sending/receiving emotes --- back_end/src/db_handler.rs | 6 +- back_end/src/json_handlers.rs | 3 +- front_end/index.html | 27 ++++++ front_end/src/state.rs | 31 ++++--- front_end/src/yew_components.rs | 150 ++++++++++++++++++++++++++++++-- 5 files changed, 192 insertions(+), 25 deletions(-) diff --git a/back_end/src/db_handler.rs b/back_end/src/db_handler.rs index 9b5aa24..f08bcf5 100644 --- a/back_end/src/db_handler.rs +++ b/back_end/src/db_handler.rs @@ -11,7 +11,9 @@ use crate::constants::{ BACKEND_CLEANUP_INTERVAL_SECONDS, COLS, GAME_CLEANUP_TIMEOUT, PLAYER_CLEANUP_TIMEOUT, PLAYER_COUNT_LIMIT, ROWS, TURN_SECONDS, }; -use crate::state::{board_from_string, new_string_board, string_from_board, BoardState, Turn, EmoteEnum}; +use crate::state::{ + board_from_string, new_string_board, string_from_board, BoardState, EmoteEnum, Turn, +}; use std::collections::HashMap; use std::sync::mpsc::{Receiver, RecvTimeoutError, SyncSender}; @@ -1159,7 +1161,7 @@ impl DBHandler { _conn_result.as_ref().unwrap() }; - let mut prepared_stmt = conn.prepare("SELECT games.cyan_player, games.magenta_player FROM games JOIN players WHERE players.id = ?, games.id = players.game_id;") + let mut prepared_stmt = conn.prepare("SELECT games.cyan_player, games.magenta_player FROM games JOIN players WHERE players.id = ? AND games.id = players.game_id;") .map_err(|_| String::from("Failed to prepare db query for getting opponent id for sending emote"))?; let row_result: Result<(Option, Option), RusqliteError> = prepared_stmt.query_row([sender_id], |row| Ok((row.get(0).ok(), row.get(1).ok()))); diff --git a/back_end/src/json_handlers.rs b/back_end/src/json_handlers.rs index 973d60f..81e376b 100644 --- a/back_end/src/json_handlers.rs +++ b/back_end/src/json_handlers.rs @@ -8,7 +8,8 @@ //You should have received a copy of the GNU General Public License along with this program. If not, see . use crate::{ constants::BACKEND_PHRASE_MAX_LENGTH, - db_handler::{CheckPairingType, DBHandlerRequest, GetIDSenderType}, state::EmoteEnum, + db_handler::{CheckPairingType, DBHandlerRequest, GetIDSenderType}, + state::EmoteEnum, }; use std::{ diff --git a/front_end/index.html b/front_end/index.html index bf0034c..7dee156 100644 --- a/front_end/index.html +++ b/front_end/index.html @@ -48,6 +48,33 @@ display: grid; } + div.emote_wrapper { + grid-row: 3; + grid-column: 8; + display: grid; + } + button.emote { + font-size: 2em; + } + b.emote { + font-size: 1.5em; + } + button#emote_smile { + grid-row: 1; + grid-column: 1; + } + button#emote_neutral { + grid-row: 1; + grid-column: 2; + } + button#emote_frown { + grid-row: 1; + grid-column: 3; + } + button#emote_think { + grid-row: 1; + grid-column: 4; + } button.networkedMultiplayer { grid-row: 1; grid-column: 1; diff --git a/front_end/src/state.rs b/front_end/src/state.rs index 7824c2d..85663bb 100644 --- a/front_end/src/state.rs +++ b/front_end/src/state.rs @@ -98,20 +98,6 @@ impl GameState { } } - pub fn get_network_current_side(&self) -> Option { - if let GameState::NetworkedMultiplayer { - paired: _, - current_side, - current_turn: _, - phrase: _, - } = *self - { - current_side - } else { - None - } - } - pub fn set_networked_current_turn(&mut self, turn: Turn) { if let GameState::NetworkedMultiplayer { paired: _, @@ -573,6 +559,12 @@ pub struct PlaceTokenResponse { pub board: String, } +#[derive(Debug, Serialize, Deserialize)] +pub struct SendEmoteRequestResponse { + pub r#type: String, + pub status: String, +} + #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum NetworkedGameState { CyanTurn, @@ -638,6 +630,17 @@ impl From for String { } } +impl EmoteEnum { + pub fn get_unicode(&self) -> char { + match *self { + EmoteEnum::Smile => '🙂', + EmoteEnum::Neutral => '😐', + EmoteEnum::Frown => '🙁', + EmoteEnum::Think => '🤔', + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/front_end/src/yew_components.rs b/front_end/src/yew_components.rs index b272934..e50b2bb 100644 --- a/front_end/src/yew_components.rs +++ b/front_end/src/yew_components.rs @@ -18,9 +18,9 @@ use crate::html_helper::{ }; use crate::random_helper::get_seeded_random; use crate::state::{ - board_from_string, BoardState, GameState, GameStateResponse, MainMenuMessage, + board_from_string, BoardState, EmoteEnum, GameState, GameStateResponse, MainMenuMessage, NetworkedGameState, PairingRequestResponse, PairingStatusResponse, PlaceTokenResponse, - PlacedEnum, SharedState, Turn, + PlacedEnum, SendEmoteRequestResponse, SharedState, Turn, }; use std::cell::{Cell, RefCell}; @@ -227,6 +227,46 @@ impl Component for ResetButton { } } +struct EmoteButton {} + +enum EmoteButtonMsg { + Pressed, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Properties)] +struct EmoteButtonProperties { + emote: EmoteEnum, +} + +impl Component for EmoteButton { + type Message = EmoteButtonMsg; + type Properties = EmoteButtonProperties; + + fn create(_ctx: &Context) -> Self { + Self {} + } + + fn view(&self, ctx: &Context) -> Html { + let onclick = ctx.link().callback(|_| EmoteButtonMsg::Pressed); + let emote_id = format!("emote_{}", ctx.props().emote); + html! { + + } + } + + fn update(&mut self, ctx: &Context, msg: Self::Message) -> bool { + ctx.link() + .get_parent() + .expect("Wrapper should be parent of EmoteButton") + .clone() + .downcast::() + .send_message(WrapperMsg::SendEmote(ctx.props().emote)); + true + } +} + pub struct Slot {} pub enum SlotMessage { @@ -485,7 +525,11 @@ impl Wrapper { _ => NetworkedGameState::InternalError, }; - WrapperMsg::BackendResponse(BREnum::GotStatus(networked_game_state, response.board)) + WrapperMsg::BackendResponse(BREnum::GotStatus( + networked_game_state, + response.board, + response.peer_emote, + )) }); } @@ -651,7 +695,8 @@ pub enum BREnum { Error(String), GotID(u32, Option), GotPairing(Option), - GotStatus(NetworkedGameState, Option), + /// Second opt string is board_str, third opt string is received emote + GotStatus(NetworkedGameState, Option, Option), GotPlaced(PlacedEnum, String), } @@ -667,6 +712,8 @@ pub enum WrapperMsg { BackendRequest { place: u8 }, BackendResponse(BREnum), Reset, + SendEmote(EmoteEnum), + SentEmote(EmoteEnum), } impl WrapperMsg { @@ -697,6 +744,12 @@ impl Component for Wrapper {
+
+ + + + +
@@ -1286,7 +1339,6 @@ impl Component for Wrapper { let is_networked_multiplayer = shared.game_state.borrow().is_networked_multiplayer(); if !self.do_backend_tick || !is_networked_multiplayer { - // disconnect id if backend tick is to be stopped self.send_disconnect(); self.cleanup_disconnect_callbacks(); return false; @@ -1444,7 +1496,41 @@ impl Component for Wrapper { .ok(); } } - BREnum::GotStatus(networked_game_state, board_opt) => { + BREnum::GotStatus(networked_game_state, board_opt, emote_opt) => { + let current_side = shared + .game_state + .borrow() + .get_networked_current_side() + .expect("Should be Networked mode"); + if let Some(emote_string) = emote_opt { + if let Ok(emote_enum) = EmoteEnum::try_from(emote_string.as_str()) { + append_to_info_text( + &document, + "info_text0", + &format!( + "{} sent {}

", + current_side.get_opposite().get_color(), + current_side.get_opposite(), + emote_enum.get_unicode() + ), + INFO_TEXT_MAX_ITEMS, + ) + .ok(); + } else { + append_to_info_text( + &document, + "info_text0", + &format!( + "{} sent invalid emote", + current_side.get_color(), + current_side.get_opposite() + ), + INFO_TEXT_MAX_ITEMS, + ) + .ok(); + } + } + if let Some(board_string) = board_opt { self.update_board_from_string(&shared, &document, board_string); } @@ -1461,7 +1547,7 @@ impl Component for Wrapper { &format!( "It is CyanPlayer's ({}) Turn", if current_game_state - .get_network_current_side() + .get_networked_current_side() .unwrap_or(Turn::CyanPlayer) == Turn::CyanPlayer { @@ -1485,7 +1571,7 @@ impl Component for Wrapper { "info_text1", &format!( "It is MagentaPlayer's ({}) Turn", - if current_game_state.get_network_current_side().unwrap_or(Turn::CyanPlayer) == Turn::MagentaPlayer + if current_game_state.get_networked_current_side().unwrap_or(Turn::CyanPlayer) == Turn::MagentaPlayer { "your" } else { @@ -1730,6 +1816,54 @@ impl Component for Wrapper { self.do_backend_tick = false; self.cleanup_disconnect_callbacks(); } + WrapperMsg::SendEmote(emote) => { + if self.player_id.is_none() { + return false; + } + let emote = emote; + let player_id = self.player_id.unwrap(); + ctx.link().send_future(async move { + let mut json_entries = HashMap::new(); + json_entries.insert("id".into(), format!("{}", player_id)); + json_entries.insert("type".into(), "send_emote".into()); + json_entries.insert("emote".into(), emote.to_string()); + + let send_to_backend_result = send_to_backend(json_entries).await; + if let Err(e) = send_to_backend_result { + return WrapperMsg::BackendResponse(BREnum::Error(format!("{:?}", e))); + } + + let request_result: Result = + serde_json::from_str(&send_to_backend_result.unwrap()); + if let Err(e) = request_result { + return WrapperMsg::BackendResponse(BREnum::Error(format!("{:?}", e))); + } + let response = request_result.unwrap(); + + if response.status.as_str() == "ok" { + WrapperMsg::SentEmote(emote) + } else { + WrapperMsg::BackendResponse(BREnum::Error(format!("{:?}", response.status))) + } + }); + } + WrapperMsg::SentEmote(emote) => { + let current_side_opt = shared.game_state.borrow().get_networked_current_side(); + if let Some(current_side) = current_side_opt { + append_to_info_text( + &document, + "info_text0", + &format!( + "{} sent {}

", + current_side.get_color(), + current_side, + emote.get_unicode() + ), + INFO_TEXT_MAX_ITEMS, + ) + .ok(); + } + } } // match (msg) true