From e77d25996d239101bb97d12f51c10f2c636c8160 Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Fri, 29 Apr 2022 11:08:54 +0900 Subject: [PATCH] front-end: fix repeated disconnects on close When the front-end connects to the back-end, it creates a callback that sends a disconnect message with the received ID on "pagehide" and "beforeunload" events. The previous implementation did not "undo" these callbacks when the game was reset and a new ID was received. This fix prevents the front-end from resending disconnect messages with previously received IDs on browser window/tab close. --- front_end/Cargo.toml | 2 +- front_end/src/yew_components.rs | 46 +++++++++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/front_end/Cargo.toml b/front_end/Cargo.toml index a4094ca..61251bf 100644 --- a/front_end/Cargo.toml +++ b/front_end/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" yew = "0.19" log = "0.4.6" wasm-logger = "0.2.0" -web-sys = { version = "0.3.56", features = ["Window", "Document", "Element", "Request", "RequestInit", "Headers", "RequestMode", "Response", "ReadableStream", "AddEventListenerOptions", "EventTarget"] } +web-sys = { version = "0.3.56", features = ["Window", "Document", "Element", "Request", "RequestInit", "Headers", "RequestMode", "Response", "ReadableStream", "AddEventListenerOptions", "EventListenerOptions", "EventTarget"] } js-sys = "0.3.56" oorandom = "11.1.3" wasm-bindgen = { version = "0.2.79", features = ["serde-serialize"] } diff --git a/front_end/src/yew_components.rs b/front_end/src/yew_components.rs index 1f5f46c..3ea0c7c 100644 --- a/front_end/src/yew_components.rs +++ b/front_end/src/yew_components.rs @@ -23,12 +23,12 @@ use crate::state::{ PlacedEnum, SharedState, Turn, }; -use std::cell::Cell; +use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::rc::Rc; use js_sys::{Function, Promise}; -use web_sys::{AddEventListenerOptions, Document, HtmlInputElement}; +use web_sys::{AddEventListenerOptions, Document, EventListenerOptions, HtmlInputElement}; use wasm_bindgen_futures::JsFuture; @@ -321,6 +321,7 @@ pub struct Wrapper { player_id: Option, place_request: Option, do_backend_tick: bool, + cleanup_id_callback: Rc>>, } impl Wrapper { @@ -602,6 +603,33 @@ impl Wrapper { shared.board[idx].set(slot.get()); } } + + fn cleanup_disconnect_callbacks(&mut self) { + let window = web_sys::window().expect("Should be able to get window"); + // if previously set disconnect callback is set, unset it + if let Some(callback) = self.cleanup_id_callback.borrow_mut().take() { + let mut options = EventListenerOptions::new(); + options.capture(true); + if window + .remove_event_listener_with_callback_and_event_listener_options( + "pagehide", &callback, &options, + ) + .is_err() + { + log::warn!("Failed to remove event listener for disconnect ID on pagehide"); + } + if window + .remove_event_listener_with_callback_and_event_listener_options( + "beforeunload", + &callback, + &options, + ) + .is_err() + { + log::warn!("Failed to remove event listener for disconnect ID on beforeunload"); + } + } + } } #[derive(Clone, Debug, PartialEq, Eq)] @@ -642,6 +670,7 @@ impl Component for Wrapper { player_id: None, place_request: None, do_backend_tick: true, + cleanup_id_callback: Rc::new(RefCell::new(None)), } } @@ -1307,11 +1336,16 @@ impl Component for Wrapper { log::warn!("{}", string); } BREnum::GotID(id, turn_opt) => { + self.cleanup_disconnect_callbacks(); + // set reset and disconnect on page "unload" let player_id = id; + let listener_function: Rc>> = + self.cleanup_id_callback.clone(); ctx.link().send_future(async move { + let listener_function = listener_function; let promise = - Promise::new(&mut |resolve: js_sys::Function, _reject| { + Promise::new(&mut move |resolve: js_sys::Function, _reject| { let window = web_sys::window().expect("Should be able to get window"); let outer_function = Function::new_with_args( @@ -1328,17 +1362,18 @@ impl Component for Wrapper { ); let binded_func = outer_function.bind1(&outer_function, &resolve); + listener_function.replace(Some(binded_func)); window .add_event_listener_with_callback_and_add_event_listener_options( "pagehide", - &binded_func, + listener_function.borrow().as_ref().unwrap(), AddEventListenerOptions::new().capture(true).once(true) ) .expect("Should be able to set \"pagehide\" callback"); window .add_event_listener_with_callback_and_add_event_listener_options( "beforeunload", - &binded_func, + listener_function.borrow().as_ref().unwrap(), AddEventListenerOptions::new().capture(true).once(true) ) .expect("Should be able to set \"beforeunload\" callback"); @@ -1708,6 +1743,7 @@ impl Component for Wrapper { ) .ok(); self.do_backend_tick = false; + self.cleanup_disconnect_callbacks(); } } // match (msg)