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
80
use std::cell::Ref;

use dyn_clone::DynClone;
use js_sys::Function;
use observable_rs::{ListenerHandle, Observable};
// use serde::{de::DeserializeOwned, Serialize};
use wasm_bindgen::JsValue;

use crate::collections::List;

// Traits for javascript-specific functionality around Observable<T>

/// This trait is necessary to support generic observables
/// which cannot themselves be exportable via wasm_bindgen
pub trait JsObserve: DynClone {
    // type Target: Clone + Into<JsValue>;

    fn get_js(&self) -> JsValue;

    /// The default implementation of map is to call the closure once
    /// with the output of .get - other types may call the closure multiple
    /// times for different sub-values
    fn map_js(&self, cb: Function) -> JsValue {
        let ar = js_sys::Array::new();
        let ret = cb.call1(&JsValue::UNDEFINED, &self.get_js()).unwrap();
        ar.push(&ret);
        ar.into()
    }

    fn unsubscribe(&self, handle: ListenerHandle) -> bool;

    fn subscribe(&self, cb: Box<dyn Fn(JsValue)>) -> ListenerHandle;
    fn once(&self, cb: Box<dyn Fn(JsValue)>) -> ListenerHandle;
}

impl<T> JsObserve for Observable<T>
where
    T: Into<JsValue> + Clone,
{
    // we need to be able provide a JS value (JS only has one value type)
    fn get_js(&self) -> JsValue {
        let a: Ref<T> = self.get();
        (*a).clone().into()
    }

    fn subscribe(&self, cb: Box<dyn Fn(JsValue)>) -> ListenerHandle {
        Observable::subscribe(self, Box::new(move |v: &T| cb(v.clone().into())))
    }

    fn once(&self, cb: Box<dyn Fn(JsValue)>) -> ListenerHandle {
        Observable::once(self, Box::new(move |v: &T| cb(v.clone().into())))
    }

    fn unsubscribe(&self, handle: ListenerHandle) -> bool {
        Observable::unsubscribe(self, handle)
    }
}

impl<T> JsObserve for Observable<List<T>>
where
    T: Into<JsValue> + Clone,
{
    // we need to be able provide a JS value (JS only has one value type)
    fn get_js(&self) -> JsValue {
        let a: Ref<List<T>> = self.get();
        (&*a).into()
    }

    fn subscribe(&self, cb: Box<dyn Fn(JsValue)>) -> ListenerHandle {
        Observable::subscribe(self, Box::new(move |v: &List<T>| cb(v.into())))
    }

    fn once(&self, cb: Box<dyn Fn(JsValue)>) -> ListenerHandle {
        Observable::once(self, Box::new(move |v: &List<T>| cb(v.into())))
    }

    fn unsubscribe(&self, handle: ListenerHandle) -> bool {
        Observable::unsubscribe(self, handle)
    }
}