Skip to content

Commit

Permalink
Uses slices and batchs signal updates when using the gamestate signal
Browse files Browse the repository at this point in the history
  • Loading branch information
IongIer committed Jun 11, 2024
1 parent edc0957 commit 6aed657
Show file tree
Hide file tree
Showing 29 changed files with 416 additions and 298 deletions.
4 changes: 4 additions & 0 deletions apis/src/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ mod game_action;
mod game_reaction;
mod hex;
mod hex_stack;
mod move_info;
mod piece_type;
mod rating_change_info;
mod server_message;
mod server_result;
mod svg_pos;
Expand All @@ -14,7 +16,9 @@ pub use client_message::ClientRequest;
pub use game_action::GameAction;
pub use game_reaction::GameReaction;
pub use hex_stack::HexStack;
pub use move_info::MoveInfo;
pub use piece_type::PieceType;
pub use rating_change_info::RatingChangeInfo;
pub use svg_pos::SvgPos;

pub use challenge_action::ChallengeAction;
Expand Down
41 changes: 41 additions & 0 deletions apis/src/common/move_info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use hive_lib::{Piece, Position};

#[derive(Debug, Clone, PartialEq)]
pub struct MoveInfo {
// the piece (either from reserve or board) that has been clicked last
pub active: Option<Piece>,
// the position of the board piece that has been clicked last
pub current_position: Option<Position>,
// possible destinations of selected piece
pub target_positions: Vec<Position>,
// the position of the target that got clicked last
pub target_position: Option<Position>,
// the position of the reserve piece that got clicked last
pub reserve_position: Option<Position>,
}

impl Default for MoveInfo {
fn default() -> Self {
Self::new()
}
}

impl MoveInfo {
pub fn new() -> Self {
Self {
active: None,
current_position: None,
target_positions: vec![],
target_position: None,
reserve_position: None,
}
}

pub fn reset(&mut self) {
self.target_positions.clear();
self.active = None;
self.target_position = None;
self.current_position = None;
self.reserve_position = None;
}
}
20 changes: 20 additions & 0 deletions apis/src/common/rating_change_info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use crate::responses::GameResponse;

#[derive(Clone, PartialEq)]
pub struct RatingChangeInfo {
pub white_rating_change: i64,
pub white_rating: u64,
pub black_rating_change: i64,
pub black_rating: u64,
}

impl RatingChangeInfo {
pub fn from_game_response(gr: &GameResponse) -> Self {
RatingChangeInfo {
white_rating_change: gr.white_rating_change.unwrap_or_default() as i64,
white_rating: gr.white_rating.unwrap_or_default() as u64,
black_rating_change: gr.black_rating_change.unwrap_or_default() as i64,
black_rating: gr.black_rating.unwrap_or_default() as u64,
}
}
}
36 changes: 20 additions & 16 deletions apis/src/components/atoms/gc_button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,27 +78,31 @@ pub fn ConfirmButton(
(interval().pause)();
}
});

let pending_slice =
create_read_slice(game_state().signal, |gs| gs.game_control_pending.clone());

let cancel = move |_| is_clicked.update(|v| *v = false);
let pending =
move |game_control: GameControl| match (game_state().signal)().game_control_pending {
Some(GameControl::DrawOffer(gc_color)) => {
if color == gc_color && matches!(game_control, GameControl::DrawOffer(_)) {
return true;
}
false
let pending = move |game_control: GameControl| match pending_slice() {
Some(GameControl::DrawOffer(gc_color)) => {
if color == gc_color && matches!(game_control, GameControl::DrawOffer(_)) {
return true;
}
Some(GameControl::TakebackRequest(gc_color)) => {
if color == gc_color && matches!(game_control, GameControl::TakebackRequest(_)) {
return true;
}
false
false
}
Some(GameControl::TakebackRequest(gc_color)) => {
if color == gc_color && matches!(game_control, GameControl::TakebackRequest(_)) {
return true;
}
_ => false,
};
false
}
_ => false,
};

let turn = create_read_slice(game_state().signal, |gs| gs.state.turn as i32);

let disabled = move || {
let turn = (game_state().signal)().state.turn as i32;
if game_control().allowed_on_turn(turn) {
if game_control().allowed_on_turn(turn()) {
!matches!(game_control(), GameControl::Resign(_))
&& (pending(GameControl::DrawOffer(color))
|| pending(GameControl::TakebackRequest(color)))
Expand Down
9 changes: 8 additions & 1 deletion apis/src/components/atoms/hex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,14 @@ pub fn Hex(hex: Hex) -> impl IntoView {

match hex.kind {
HexType::Active(_) => {
if game_state.signal.get_untracked().target_position.is_none() || hex.level == 0 {
if game_state
.signal
.get_untracked()
.move_info
.target_position
.is_none()
|| hex.level == 0
{
view! { <Active position=hex.position level=expanded_level/> }
} else {
view! { <Active position=hex.position level=expanded_sublevel/> }
Expand Down
19 changes: 9 additions & 10 deletions apis/src/components/atoms/history_button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ pub fn HistoryButton(
#[prop(optional)] post_action: Option<Callback<()>>,
#[prop(optional)] node_ref: Option<NodeRef<html::Button>>,
) -> impl IntoView {
let game_state_signal = expect_context::<GameStateSignal>();
let is_last_turn = game_state_signal.is_last_turn_as_signal();
let is_first_turn = game_state_signal.is_first_turn_as_signal();
let cloned_action = action.clone();
let nav_buttons_style = "flex place-items-center justify-center hover:bg-pillbug-teal transform transition-transform duration-300 active:scale-95 m-1 h-7 rounded-md border-cyan-500 dark:border-button-twilight border-2 drop-shadow-lg disabled:opacity-25 disabled:cursor-not-allowed disabled:hover:bg-transparent";
let icon = match action {
Expand All @@ -25,17 +28,13 @@ pub fn HistoryButton(
HistoryNavigation::Next => icondata::AiStepForwardFilled,
HistoryNavigation::Previous => icondata::AiStepBackwardFilled,
};
let is_disabled = move || {
let game_state_signal = expect_context::<GameStateSignal>();
let game_state = game_state_signal.signal.get();
match cloned_action {
HistoryNavigation::Last | HistoryNavigation::MobileLast | HistoryNavigation::Next => {
game_state.is_last_turn()
}
HistoryNavigation::Previous | HistoryNavigation::First => {
game_state.history_turn.is_none() || game_state.history_turn == Some(0)
}

let is_disabled = move || match cloned_action {
HistoryNavigation::Last | HistoryNavigation::MobileLast | HistoryNavigation::Next => {
is_last_turn()
}

HistoryNavigation::Previous | HistoryNavigation::First => is_first_turn(),
};
let debounced_action = debounce(std::time::Duration::from_millis(10), move |_| {
send_action(&action);
Expand Down
3 changes: 2 additions & 1 deletion apis/src/components/atoms/piece.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,9 @@ pub fn Piece(
.top_piece(position)
.unwrap_or(piece);

let active_piece = create_read_slice(game_state_signal.signal, |gs| gs.move_info.active);
let show_ds = move || {
if let Some(active) = game_state_signal.signal.get().active {
if let Some(active) = active_piece() {
if active == piece {
return "#no_ds";
}
Expand Down
2 changes: 1 addition & 1 deletion apis/src/components/atoms/status_indicator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub fn StatusIndicator(username: String) -> impl IntoView {

let icon_color = move || {
if user_is_player() {
if user_has_ws() {
if batch(user_has_ws) {
"fill-grasshopper-green"
} else {
"fill-ladybug-red"
Expand Down
18 changes: 10 additions & 8 deletions apis/src/components/atoms/title.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ pub fn Title() -> impl IntoView {
let games = expect_context::<GamesSignal>();
let focused = expect_context::<RefocusSignal>();
let title_text = move || {
let len = games.own.get().next_untimed.len()
+ games.own.get().next_realtime.len()
+ games.own.get().next_correspondence.len();
if !focused.signal.get().focused && len > 0 {
format!("({}) HiveGame.com", len)
} else {
String::from("HiveGame.com")
}
batch(move || {
let len = games.own.get().next_untimed.len()
+ games.own.get().next_realtime.len()
+ games.own.get().next_correspondence.len();
if !focused.signal.get().focused && len > 0 {
format!("({}) HiveGame.com", len)
} else {
String::from("HiveGame.com")
}
})
};

view! { <T text=title_text/> }
Expand Down
58 changes: 32 additions & 26 deletions apis/src/components/layouts/base_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,6 @@ pub fn BaseLayout(children: ChildrenFn) -> impl IntoView {
};
});

let api = ApiRequests::new();

let focused = use_window_focus();
let _ = watch(
focused,
Expand All @@ -161,35 +159,43 @@ pub fn BaseLayout(children: ChildrenFn) -> impl IntoView {

let counter = RwSignal::new(0_u64);
let retry_at = RwSignal::new(2_u64);

let Pausable { .. } = use_interval_fn(
move || {
api.ping();
counter.update(|c| *c += 1);
match ws_ready() {
ConnectionReadyState::Closed => {
if retry_at.get() == counter.get() {
//log!("Reconnecting due to ReadyState");
batch({
let ws = ws.clone();
let api = ApiRequests::new();
move || {
api.ping();
counter.update(|c| *c += 1);
match ws_ready() {
ConnectionReadyState::Closed => {
if retry_at.get() == counter.get() {
//log!("Reconnecting due to ReadyState");
ws.open();
counter.update(|c| *c = 0);
retry_at.update(|r| *r *= 2);
}
}
ConnectionReadyState::Open => {
counter.update(|c| *c = 0);
retry_at.update(|r| *r = 2);
}
_ => {}
}
if Utc::now()
.signed_duration_since(ping.signal.get_untracked().last_update)
.num_seconds()
>= 5
&& retry_at.get() == counter.get()
{
//log!("Reconnecting due to ping duration");
ws.open();
counter.update(|c| *c = 0);
counter.update(|r| *r *= 2);
}
retry_at.update(|r| *r *= 2);
};
}
ConnectionReadyState::Open => {
counter.update(|c| *c = 0);
}
_ => {}
}
if Utc::now()
.signed_duration_since(ping.signal.get_untracked().last_update)
.num_seconds()
>= 5
&& retry_at.get() == counter.get()
{
//log!("Reconnecting due to ping duration");
ws.open();
counter.update(|c| *c = 0);
counter.update(|r| *r *= 2);
};
})
},
1000,
);
Expand Down
15 changes: 11 additions & 4 deletions apis/src/components/molecules/board_pieces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,24 @@ pub fn BoardPieces() -> impl IntoView {
let board = move || {
let mut board = Vec::new();
let game_state = (game_state_signal.signal)();
let targets = game_state.target_positions;
let targets = game_state.move_info.target_positions;
let last_move = game_state.state.board.last_move;
let active_piece = (game_state.active, game_state.target_position);
let from_to_position = (game_state.current_position, game_state.target_position);
let active_piece = (
game_state.move_info.active,
game_state.move_info.target_position,
);
let from_to_position = (
game_state.move_info.current_position,
game_state.move_info.target_position,
);
// TODO: Find a better solution instead of the nested loop here
for r in 0..32 {
for q in 0..32 {
let position = Position::new(q, r);
// start this empty and only add
let bug_stack = game_state.state.board.board.get(position).clone();
let mut hs = HexStack::new(&bug_stack, position);
if game_state.active.is_none() {
if game_state.move_info.active.is_none() {
if let (_, Some(to)) = last_move {
if to == position {
hs.add_last_move(Direction::To);
Expand Down
5 changes: 3 additions & 2 deletions apis/src/components/molecules/control_buttons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,15 @@ pub fn ControlButtons() -> impl IntoView {
unreachable!()
}
};
let pending = create_read_slice(game_state.signal, |gs| gs.game_control_pending.clone());

let pending_draw = move || match (game_state.signal)().game_control_pending {
let pending_draw = move || match pending() {
Some(GameControl::DrawOffer(gc_color)) => gc_color.opposite_color() == color(),

_ => false,
};

let pending_takeback = move || match (game_state.signal)().game_control_pending {
let pending_takeback = move || match pending() {
Some(GameControl::TakebackRequest(gc_color)) => gc_color.opposite_color() == color(),

_ => false,
Expand Down
29 changes: 23 additions & 6 deletions apis/src/components/molecules/game_info.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,37 @@
use leptos::*;
use shared_types::TimeMode;

use crate::{components::molecules::time_row::TimeRow, providers::game_state::GameStateSignal};

#[derive(Clone, PartialEq)]
struct TimeInfo {
time_mode: TimeMode,
time_base: Option<i32>,
time_increment: Option<i32>,
rated: bool,
}

#[component]
pub fn GameInfo(#[prop(optional)] extend_tw_classes: &'static str) -> impl IntoView {
let game_state = expect_context::<GameStateSignal>();
let game_info = create_read_slice(game_state.signal, |gs| {
gs.game_response.as_ref().map(|gr| TimeInfo {
time_mode: gr.time_mode.clone(),
time_base: gr.time_base,
time_increment: gr.time_increment,
rated: gr.rated,
})
});
move || {
if let Some(gr) = game_state.signal.get().game_response {
let rated = format!("• {}", if gr.rated { "Rated" } else { "Casual" });
if let Some(gi) = game_info() {
let rated = format!("• {}", if gi.rated { "Rated" } else { "Casual" });
view! {
<div class=extend_tw_classes>
<div class="flex items-center gap-1">
<div class="flex gap-1 items-center">
<TimeRow
time_mode=gr.time_mode
time_base=gr.time_base
increment=gr.time_increment
time_mode=gi.time_mode
time_base=gi.time_base
increment=gi.time_increment
extend_tw_classes="whitespace-nowrap"
/>
{rated}
Expand Down
Loading

0 comments on commit 6aed657

Please sign in to comment.