Impl sending/receiving emotes

This commit is contained in:
Stephen Seo 2022-04-29 18:30:41 +09:00
parent 36dd43bb70
commit 105cd880f2
5 changed files with 192 additions and 25 deletions

View file

@ -11,7 +11,9 @@ use crate::constants::{
BACKEND_CLEANUP_INTERVAL_SECONDS, COLS, GAME_CLEANUP_TIMEOUT, PLAYER_CLEANUP_TIMEOUT, BACKEND_CLEANUP_INTERVAL_SECONDS, COLS, GAME_CLEANUP_TIMEOUT, PLAYER_CLEANUP_TIMEOUT,
PLAYER_COUNT_LIMIT, ROWS, TURN_SECONDS, 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::collections::HashMap;
use std::sync::mpsc::{Receiver, RecvTimeoutError, SyncSender}; use std::sync::mpsc::{Receiver, RecvTimeoutError, SyncSender};
@ -1159,7 +1161,7 @@ impl DBHandler {
_conn_result.as_ref().unwrap() _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"))?; .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> = 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()))); prepared_stmt.query_row([sender_id], |row| Ok((row.get(0).ok(), row.get(1).ok())));

View 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/>. //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::{ use crate::{
constants::BACKEND_PHRASE_MAX_LENGTH, constants::BACKEND_PHRASE_MAX_LENGTH,
db_handler::{CheckPairingType, DBHandlerRequest, GetIDSenderType}, state::EmoteEnum, db_handler::{CheckPairingType, DBHandlerRequest, GetIDSenderType},
state::EmoteEnum,
}; };
use std::{ use std::{

View file

@ -48,6 +48,33 @@
display: grid; 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 { button.networkedMultiplayer {
grid-row: 1; grid-row: 1;
grid-column: 1; grid-column: 1;

View 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) { pub fn set_networked_current_turn(&mut self, turn: Turn) {
if let GameState::NetworkedMultiplayer { if let GameState::NetworkedMultiplayer {
paired: _, paired: _,
@ -573,6 +559,12 @@ pub struct PlaceTokenResponse {
pub board: String, pub board: String,
} }
#[derive(Debug, Serialize, Deserialize)]
pub struct SendEmoteRequestResponse {
pub r#type: String,
pub status: String,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum NetworkedGameState { pub enum NetworkedGameState {
CyanTurn, 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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View file

@ -18,9 +18,9 @@ use crate::html_helper::{
}; };
use crate::random_helper::get_seeded_random; use crate::random_helper::get_seeded_random;
use crate::state::{ use crate::state::{
board_from_string, BoardState, GameState, GameStateResponse, MainMenuMessage, board_from_string, BoardState, EmoteEnum, GameState, GameStateResponse, MainMenuMessage,
NetworkedGameState, PairingRequestResponse, PairingStatusResponse, PlaceTokenResponse, NetworkedGameState, PairingRequestResponse, PairingStatusResponse, PlaceTokenResponse,
PlacedEnum, SharedState, Turn, PlacedEnum, SendEmoteRequestResponse, SharedState, Turn,
}; };
use std::cell::{Cell, RefCell}; 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 struct Slot {}
pub enum SlotMessage { pub enum SlotMessage {
@ -485,7 +525,11 @@ impl Wrapper {
_ => NetworkedGameState::InternalError, _ => 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), Error(String),
GotID(u32, Option<Turn>), GotID(u32, Option<Turn>),
GotPairing(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), GotPlaced(PlacedEnum, String),
} }
@ -667,6 +712,8 @@ pub enum WrapperMsg {
BackendRequest { place: u8 }, BackendRequest { place: u8 },
BackendResponse(BREnum), BackendResponse(BREnum),
Reset, Reset,
SendEmote(EmoteEnum),
SentEmote(EmoteEnum),
} }
impl WrapperMsg { impl WrapperMsg {
@ -697,6 +744,12 @@ impl Component for Wrapper {
<div class="wrapper"> <div class="wrapper">
<MainMenu /> <MainMenu />
<ResetButton /> <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=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=1 state={shared.board[1].clone()} placed={shared.placed[1].clone()} />
<Slot idx=2 state={shared.board[2].clone()} placed={shared.placed[2].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 = let is_networked_multiplayer =
shared.game_state.borrow().is_networked_multiplayer(); shared.game_state.borrow().is_networked_multiplayer();
if !self.do_backend_tick || !is_networked_multiplayer { if !self.do_backend_tick || !is_networked_multiplayer {
// disconnect id if backend tick is to be stopped
self.send_disconnect(); self.send_disconnect();
self.cleanup_disconnect_callbacks(); self.cleanup_disconnect_callbacks();
return false; return false;
@ -1444,7 +1496,41 @@ impl Component for Wrapper {
.ok(); .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 { if let Some(board_string) = board_opt {
self.update_board_from_string(&shared, &document, board_string); self.update_board_from_string(&shared, &document, board_string);
} }
@ -1461,7 +1547,7 @@ impl Component for Wrapper {
&format!( &format!(
"<b class=\"cyan\">It is CyanPlayer's ({}) Turn</b>", "<b class=\"cyan\">It is CyanPlayer's ({}) Turn</b>",
if current_game_state if current_game_state
.get_network_current_side() .get_networked_current_side()
.unwrap_or(Turn::CyanPlayer) .unwrap_or(Turn::CyanPlayer)
== Turn::CyanPlayer == Turn::CyanPlayer
{ {
@ -1485,7 +1571,7 @@ impl Component for Wrapper {
"info_text1", "info_text1",
&format!( &format!(
"<b class=\"magenta\">It is MagentaPlayer's ({}) Turn</b>", "<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" "your"
} else { } else {
@ -1730,6 +1816,54 @@ impl Component for Wrapper {
self.do_backend_tick = false; self.do_backend_tick = false;
self.cleanup_disconnect_callbacks(); 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) } // match (msg)
true true