]> git.seodisparate.com - EN605.607.81.SP22_ASDM_Project/commitdiff
Impl sending/receiving emotes
authorStephen Seo <seo.disparate@gmail.com>
Fri, 29 Apr 2022 09:30:41 +0000 (18:30 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Fri, 29 Apr 2022 09:30:41 +0000 (18:30 +0900)
back_end/src/db_handler.rs
back_end/src/json_handlers.rs
front_end/index.html
front_end/src/state.rs
front_end/src/yew_components.rs

index 9b5aa24ae1d05e2b2d67665b13d23eb45765b7cf..f08bcf546038a462c46c8c87b9e2fbe0018f92d1 100644 (file)
@@ -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<u32>, Option<u32>), RusqliteError> =
             prepared_stmt.query_row([sender_id], |row| Ok((row.get(0).ok(), row.get(1).ok())));
index 973d60f6fbc7b6dfec9da4875b0c835750cde4ae..81e376b691ca7c34903ec2bcbe201f9affbc43e6 100644 (file)
@@ -8,7 +8,8 @@
 //You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
 use crate::{
     constants::BACKEND_PHRASE_MAX_LENGTH,
-    db_handler::{CheckPairingType, DBHandlerRequest, GetIDSenderType}, state::EmoteEnum,
+    db_handler::{CheckPairingType, DBHandlerRequest, GetIDSenderType},
+    state::EmoteEnum,
 };
 
 use std::{
index bf0034c883843b6079e1d02badbf40899065b6a4..7dee15636be77772e297d6feb6fee394571836a3 100644 (file)
 
         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;
index 7824c2d2c5076106d5694554ee2ec16d4cc77995..85663bb75b3e7cfad15983226ca6271a1e1b25a4 100644 (file)
@@ -98,20 +98,6 @@ impl GameState {
         }
     }
 
-    pub fn get_network_current_side(&self) -> Option<Turn> {
-        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<EmoteEnum> 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::*;
index b272934d37c9ab411f45394ac80c035bff85a91f..e50b2bb924489719dad30219b24ed9bd2e97fb4b 100644 (file)
@@ -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 {
+        Self {}
+    }
+
+    fn view(&self, ctx: &Context<Self>) -> Html {
+        let onclick = ctx.link().callback(|_| EmoteButtonMsg::Pressed);
+        let emote_id = format!("emote_{}", ctx.props().emote);
+        html! {
+            <button class={"emote"} id={emote_id} onclick={onclick}>
+                {ctx.props().emote.get_unicode()}
+            </button>
+        }
+    }
+
+    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
+        ctx.link()
+            .get_parent()
+            .expect("Wrapper should be parent of EmoteButton")
+            .clone()
+            .downcast::<Wrapper>()
+            .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<Turn>),
     GotPairing(Option<Turn>),
-    GotStatus(NetworkedGameState, Option<String>),
+    /// Second opt string is board_str, third opt string is received emote
+    GotStatus(NetworkedGameState, Option<String>, Option<String>),
     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 {
             <div class="wrapper">
                 <MainMenu />
                 <ResetButton />
+                <div class="emote_wrapper">
+                    <EmoteButton emote={EmoteEnum::Smile} />
+                    <EmoteButton emote={EmoteEnum::Neutral} />
+                    <EmoteButton emote={EmoteEnum::Frown} />
+                    <EmoteButton emote={EmoteEnum::Think} />
+                </div>
                 <Slot idx=0 state={shared.board[0].clone()} placed={shared.placed[0].clone()} />
                 <Slot idx=1 state={shared.board[1].clone()} placed={shared.placed[1].clone()} />
                 <Slot idx=2 state={shared.board[2].clone()} placed={shared.placed[2].clone()} />
@@ -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!(
+                                        "<b class=\"{}\">{} sent <b class=\"emote\">{}</p></b>",
+                                        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!(
+                                        "<b class=\"{}\">{} sent invalid emote</b>",
+                                        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!(
                                             "<b class=\"cyan\">It is CyanPlayer's ({}) Turn</b>",
                                             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!(
                                     "<b class=\"magenta\">It is MagentaPlayer's ({}) Turn</b>",
-                                    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<SendEmoteRequestResponse, _> =
+                        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!(
+                            "<b class=\"{}\">{} sent <b class=\"emote\">{}</p></b>",
+                            current_side.get_color(),
+                            current_side,
+                            emote.get_unicode()
+                        ),
+                        INFO_TEXT_MAX_ITEMS,
+                    )
+                    .ok();
+                }
+            }
         } // match (msg)
 
         true