1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
use crate::{
    dom::{window, Application,util, Task, Program, dom_node::intern},
    vdom::Attribute,
};
use wasm_bindgen::{prelude::*, JsCast};
use js_sys::Promise;
use wasm_bindgen_futures::JsFuture;

impl<APP, MSG> Program<APP, MSG>
where
    MSG: 'static,
    APP: Application<MSG> + 'static,
{

    /// attach event listeners to the window
    pub fn add_window_event_listeners(&self, event_listeners: Vec<Attribute<MSG>>) {
        self.add_event_listeners(&window(), event_listeners).expect("must add to event listener");
    }


    /// Creates a Cmd in which the MSG will be emitted
    /// whenever the browser is resized
    pub fn on_resize<F>(&self, mut cb: F)
    where
        F: FnMut(i32, i32) -> MSG + Clone + 'static,
    {
        let program = self.clone();
        let closure: Closure<dyn FnMut(web_sys::Event)> =
            Closure::new(move|_| {
                let (window_width, window_height) = util::get_window_size();
                let msg = cb(window_width, window_height);
                program.dispatch(msg);
            });
        window().add_event_listener_with_callback(intern("resize"), closure.as_ref().unchecked_ref()).expect("resize callback");
        self.event_closures.borrow_mut().push(closure);
    }

    /// TODO: only executed once, since the Task Future is droped once done
    /// TODO: this should be a stream, instead of just one-time future
    /// a variant of resize task, but instead of returning Cmd, it is returning Task
    pub fn on_resize_task<F>(mut cb: F) -> Task<MSG>
    where
        F: FnMut(i32, i32) -> MSG + Clone + 'static,
    {
        Task::new(async move{
            let promise = Promise::new(&mut |resolve, _reject|{
                let resize_callback: Closure<dyn FnMut(web_sys::Event)> =
                    Closure::new(move|_| {
                        resolve.call0(&JsValue::NULL).expect("must resolve");
                    });
                window().add_event_listener_with_callback(intern("resize"), resize_callback.as_ref().unchecked_ref()).expect("add event callback");
                resize_callback.forget();
            });
            JsFuture::from(promise).await.expect("must await");
            let (window_width, window_height) = util::get_window_size();
            cb(window_width, window_height)
        })
    }

    /// attached a callback and will be triggered when the hash portion of the window location
    /// url is changed
    pub fn on_hashchange<F>(&self, mut cb: F)
    where
        F: FnMut(String) -> MSG + 'static,
    {
        let program = self.clone();
        let closure: Closure<dyn FnMut(web_sys::Event)> =
            Closure::new(move |_| {
                let hash = util::get_location_hash();
                let msg = cb(hash);
                program.dispatch(msg);
            });
        window().set_onhashchange(Some(closure.as_ref().unchecked_ref()));
        self.event_closures.borrow_mut().push(closure);
    }


}