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.
This commit is contained in:
Stephen Seo 2022-04-29 11:08:54 +09:00
parent e0ed5fc5d8
commit e77d25996d
2 changed files with 42 additions and 6 deletions

View file

@ -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"] }

View file

@ -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<u32>,
place_request: Option<u8>,
do_backend_tick: bool,
cleanup_id_callback: Rc<RefCell<Option<Function>>>,
}
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<RefCell<Option<Function>>> =
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)