]> git.seodisparate.com - EN605.607.81.SP22_ASDM_Project/commitdiff
front-end: fix repeated disconnects on close
authorStephen Seo <seo.disparate@gmail.com>
Fri, 29 Apr 2022 02:08:54 +0000 (11:08 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Fri, 29 Apr 2022 02:08:54 +0000 (11:08 +0900)
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
front_end/src/yew_components.rs

index a4094ca9e499d6f678978cc76936edf164653cf0..61251bfde4660e3ddce05f439f12aea2aa4d8653 100644 (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"] }
index 1f5f46cfa3678a0531c9e25bc98a9a39bae34644..3ea0c7c910a8d1923efd9a4db22f6d695ef2e44c 100644 (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)