Skip to main content

euv_ui/hook/use_online_status/
impl.rs

1use super::*;
2
3/// Inherent implementation of [`UseOnlineStatus`].
4impl UseOnlineStatus {
5    /// Creates a handle whose answer starts at `true`.
6    ///
7    /// # Returns
8    ///
9    /// - `UseOnlineStatus` - The fresh handle.
10    pub(crate) fn new() -> Self {
11        Self {
12            online: Signal::create(true),
13            teardown: Rc::new(RefCell::new(None)),
14        }
15    }
16
17    /// Returns the current answer as a snapshot.
18    ///
19    /// # Returns
20    ///
21    /// - `bool` - `true` when the browser believes it is online.
22    pub fn is_online(&self) -> bool {
23        self.get_online().get()
24    }
25
26    /// Seeds the answer from `navigator.onLine` and subscribes to the
27    /// window's `online` and `offline` events.
28    ///
29    /// On a host without a browser the answer stays `true`: a native
30    /// build has no connection to lose.
31    pub(crate) fn attach(&self) {
32        #[cfg(target_arch = "wasm32")]
33        {
34            let Some(window) = browser_window() else {
35                return;
36            };
37            let online: Signal<bool> = self.get_online();
38            let navigator: Navigator = window.navigator();
39            online.set(navigator.on_line());
40            let set_online: Closure<dyn FnMut(Event)> =
41                Closure::wrap(Box::new(move |_event: Event| {
42                    online.set(true);
43                }));
44            let set_offline: Closure<dyn FnMut(Event)> =
45                Closure::wrap(Box::new(move |_event: Event| {
46                    online.set(false);
47                }));
48            if window
49                .add_event_listener_with_callback(EVENT_ONLINE, set_online.as_ref().unchecked_ref())
50                .is_err()
51            {
52                return;
53            }
54            if window
55                .add_event_listener_with_callback(
56                    EVENT_OFFLINE,
57                    set_offline.as_ref().unchecked_ref(),
58                )
59                .is_err()
60            {
61                return;
62            }
63            *self.get_teardown().borrow_mut() = Some(Box::new(move || {
64                let _: Result<(), JsValue> = window.remove_event_listener_with_callback(
65                    EVENT_ONLINE,
66                    set_online.as_ref().unchecked_ref(),
67                );
68                let _: Result<(), JsValue> = window.remove_event_listener_with_callback(
69                    EVENT_OFFLINE,
70                    set_offline.as_ref().unchecked_ref(),
71                );
72            }));
73        }
74    }
75
76    /// Removes the window listeners.
77    pub(crate) fn detach(&self) {
78        let taken: Option<Box<dyn Fn()>> = self.get_teardown().borrow_mut().take();
79        if let Some(teardown) = taken {
80            teardown();
81        }
82    }
83}