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:
parent
e0ed5fc5d8
commit
e77d25996d
2 changed files with 42 additions and 6 deletions
|
@ -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"] }
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in a new issue