From 7d28926575cf6111c3d8a2bf63ef7dc3a12bb281 Mon Sep 17 00:00:00 2001 From: PenguinWithATie <166940857+PenguinWithATie@users.noreply.github.com> Date: Mon, 21 Oct 2024 08:07:19 -0600 Subject: [PATCH] timer_refactor (#345) --- apis/src/components/molecules/live_timer.rs | 196 ++++++++---------- .../src/components/organisms/display_timer.rs | 6 +- apis/src/providers/timer.rs | 62 ++---- 3 files changed, 111 insertions(+), 153 deletions(-) diff --git a/apis/src/components/molecules/live_timer.rs b/apis/src/components/molecules/live_timer.rs index 09040cb6..2ea4fb8a 100644 --- a/apis/src/components/molecules/live_timer.rs +++ b/apis/src/components/molecules/live_timer.rs @@ -6,7 +6,10 @@ use hive_lib::{Color, GameStatus}; use lazy_static::lazy_static; use leptos::*; use leptos_router::RouterContext; -use leptos_use::{use_interval_fn_with_options, utils::Pausable, UseIntervalFnOptions}; +use leptos_use::{ + use_interval_fn_with_options, utils::Pausable, watch_with_options, whenever_with_options, + UseIntervalFnOptions, WatchOptions, +}; use regex::Regex; use shared_types::GameId; use std::time::Duration; @@ -18,7 +21,6 @@ lazy_static! { #[component] pub fn LiveTimer(side: Signal) -> impl IntoView { - let timer_signals = expect_context::(); let game_state = expect_context::(); let sounds = expect_context::(); let auth_context = expect_context::(); @@ -26,111 +28,81 @@ pub fn LiveTimer(side: Signal) -> impl IntoView { Some(Ok(Some(user))) => Some(user.id), _ => None, }); - let user_color = game_state.user_color_as_signal(user_id.into()); let in_progress = create_read_slice(game_state.signal, |gs| { gs.game_response .as_ref() .map_or(false, |gr| gr.game_status == GameStatus::InProgress) }); - - let time_is_zero = Memo::new(move |_| { - let timer = timer_signals.signal.get(); - let time_left = match side() { - Color::White => timer.white_time_left.unwrap_or_default(), - Color::Black => timer.black_time_left.unwrap_or_default(), - }; - time_left == Duration::from_secs(0) - }); - let user_needs_warning: Memo = Memo::new(move |_| { - if let Some(color) = user_color() { - let trigger_at = timer_signals.low_time.trigger_at.get(); - if color != side() || trigger_at.is_none() { - return false; - } - - let trigger_at = trigger_at.unwrap_or_default(); - - let timer = timer_signals.signal.get(); - let time_left = match color { - Color::White => timer.white_time_left.unwrap_or_default(), - Color::Black => timer.black_time_left.unwrap_or_default(), - }; - - return !timer.finished - && !timer_signals.low_time.issued.get() - && time_left < trigger_at; - } - false - }); - - let should_refresh_warning: Memo = Memo::new(move |_| { - if let Some(color) = user_color() { - let refresh_at = timer_signals.low_time.refresh_at.get(); - if color != side() || refresh_at.is_none() { - return false; - } - - let refresh_at = refresh_at.unwrap_or(Duration::MAX); - let timer = timer_signals.signal.get(); - let time_left = match color { - Color::White => timer.white_time_left.unwrap_or_default(), - Color::Black => timer.black_time_left.unwrap_or_default(), - }; - - return !timer.finished - && timer_signals.low_time.issued.get() - && time_left > refresh_at; - } - false - }); - let time_is_red = Memo::new(move |_| { - if time_is_zero() { - String::from("bg-ladybug-red") - } else { - String::new() - } - }); + let timer = expect_context::().signal; let tick_rate = Duration::from_millis(100); let Pausable { pause, resume, .. } = use_interval_fn_with_options( move || { - let timer = timer_signals.signal.get(); - if !timer.finished { - batch(move || { - timer_signals.signal.update(|timer| { - if timer.turn % 2 == 0 { - timer.white_time_left = timer.white_time_left.map(|t| { - t.checked_sub(tick_rate).unwrap_or(Duration::from_secs(0)) - }); - } else { - timer.black_time_left = timer.black_time_left.map(|t| { - t.checked_sub(tick_rate).unwrap_or(Duration::from_secs(0)) - }); - } - }); - }); - } + timer.update(|t| { + if t.turn % 2 == 0 { + t.white_time_left = t + .white_time_left + .map(|t| t.checked_sub(tick_rate).unwrap_or_default()); + } else { + t.black_time_left = t + .black_time_left + .map(|t| t.checked_sub(tick_rate).unwrap_or_default()); + }; + }) }, 100, UseIntervalFnOptions::default().immediate(false), ); - - create_effect(move |_| { - let timer = timer_signals.signal.get(); - if in_progress() { - if (side() == Color::White) == (timer.turn % 2 == 0) && !timer.finished { + let should_resume = Signal::derive(move || { + timer + .with(|t| in_progress() && (side() == Color::White) == (t.turn % 2 == 0) && !t.finished) + }); + let time_is_zero = Signal::derive(move || timer().time_left(side()).is_zero()); + let user_needs_warning = Signal::derive(move || { + user_color().map_or(false, |color| { + timer.with(|t| { + t.warning_trigger().map_or(false, |trigger_at| { + if color == side() && !t.finished { + t.time_left(color) < trigger_at + } else { + false + } + }) + }) + }) + }); + let should_refresh_warning = Signal::derive(move || { + user_color().map_or(false, |color| { + timer.with(|t| { + t.warning_refresh().map_or(false, |refresh_at| { + if color == side() && !t.finished { + t.time_left(color) > refresh_at + } else { + false + } + }) + }) + }) + }); + #[allow(unused)] + watch_with_options( + should_resume, + move |v, _, _| { + if *v { resume(); } else { pause(); } - } else { - pause(); - } - }); + }, + //Has immediate = true, hence not unused + WatchOptions::default().immediate(true), + ); - create_effect(move |_| { - // When time runs out declare winner and style timer that ran out - if time_is_zero() && !timer_signals.signal.get().finished { + #[allow(unused)] + whenever_with_options( + move || time_is_zero() && !timer().finished, + move |v, _, _| { + // When time runs out declare winner and style timer that ran out let api = ApiRequests::new(); let router = expect_context::(); if let Some(caps) = NANOID.captures(&router.pathname().get_untracked()) { @@ -139,34 +111,40 @@ pub fn LiveTimer(side: Signal) -> impl IntoView { api.game_check_time(&game_id); } } - } - }); - - create_effect(move |_| { - if user_needs_warning() { - sounds.play_sound(SoundType::LowTime); - timer_signals.low_time.issued.set(true); - } - }); - create_effect(move |_| { - if should_refresh_warning() { - timer_signals.low_time.issued.set(false); - } - }); + }, + //Has immediate = true, hence not unused + WatchOptions::default().immediate(true), + ); + #[allow(unused)] + whenever_with_options( + user_needs_warning, + move |_, _, issued| { + let issued = issued.unwrap_or_default(); + if issued { + !should_refresh_warning() + } else { + sounds.play_sound(SoundType::LowTime); + true + } + }, + //Has immediate = true, hence not unused + WatchOptions::default().immediate(true), + ); view! {
{move || { - let timer = timer_signals.signal.get(); - let time_left = match side() { - Color::White => timer.white_time_left.unwrap_or_default(), - Color::Black => timer.black_time_left.unwrap_or_default(), - }; + let timer = timer(); + let time_left = timer.time_left(side()); timer.time_mode.time_remaining(time_left) }} diff --git a/apis/src/components/organisms/display_timer.rs b/apis/src/components/organisms/display_timer.rs index f62df3f8..08289421 100644 --- a/apis/src/components/organisms/display_timer.rs +++ b/apis/src/components/organisms/display_timer.rs @@ -51,9 +51,9 @@ pub fn DisplayTimer(placement: Placement, vertical: bool) -> impl IntoView { "h-full flex justify-center md:leading-4 row-span-2 md:row-span-1 short:row-span-2 short:text-xs items-center flex-col border-y-2 border-r-2 border-black dark:border-white select-none"), true => ("flex grow justify-end items-center", "w-14 h-14 grow-0 duration-300",""), }; - let timer = expect_context::(); + let timer = expect_context::().signal; let active_side = create_memo(move |_| { - let timer = timer.signal.get(); + let timer = timer(); match timer.finished { true => "bg-stone-200 dark:bg-reserve-twilight", false => { @@ -109,7 +109,7 @@ pub fn DisplayTimer(placement: Placement, vertical: bool) -> impl IntoView { , - pub low_time: LowTimeWarning, } impl Default for TimerSignal { @@ -21,10 +21,7 @@ impl Default for TimerSignal { impl TimerSignal { pub fn new() -> Self { let signal = RwSignal::new(Timer::new()); - Self { - signal, - low_time: LowTimeWarning::new(signal), - } + Self { signal } } pub fn update_from_hb(&self, hb: HeartbeatResponse) { @@ -80,6 +77,25 @@ impl Timer { last_interaction: None, } } + pub fn time_left(&self, color: Color) -> Duration { + match color { + Color::White => self.white_time_left, + Color::Black => self.black_time_left, + } + .unwrap_or_default() + } + + pub fn warning_trigger(&self) -> Option { + let increment = self.time_increment.unwrap_or_default(); + match self.time_mode { + TimeMode::RealTime => self.time_base.map(|b| b / 10 + increment * 2), + _ => None, + } + } + pub fn warning_refresh(&self) -> Option { + self.warning_trigger() + .and_then(|trigger| self.time_increment.map(|increment| trigger + increment * 2)) + } } impl Default for Timer { @@ -88,42 +104,6 @@ impl Default for Timer { } } -#[derive(Clone, Debug, Copy)] -pub struct LowTimeWarning { - pub issued: RwSignal, - pub refresh_at: Signal>, - pub trigger_at: Signal>, -} - -impl LowTimeWarning { - pub fn new(timer: RwSignal) -> Self { - let trigger_at = Signal::derive(move || { - let timer = timer.get(); - if timer.time_mode == TimeMode::RealTime { - timer.time_base.map(|base| { - base / 10 + timer.time_increment.unwrap_or(Duration::from_secs(0)) * 2 - }) - } else { - None - } - }); - - let refresh_at = Signal::derive(move || { - let timer = timer.get(); - if let (Some(trigger_at), Some(time_increment)) = (trigger_at(), timer.time_increment) { - Some(trigger_at + time_increment * 2) - } else { - None - } - }); - Self { - issued: RwSignal::new(false), - refresh_at, - trigger_at, - } - } -} - pub fn provide_timer() { provide_context(TimerSignal::new()) }