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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use std::cell::RefCell;
use std::rc::Rc;

use yew::prelude::*;

use crate::*;

/// State handle for [`use_bridge`] hook
pub struct UseBridgeHandle<T>
where
    T: Bridged,
{
    inner: Rc<RefCell<Box<dyn Bridge<T>>>>,
}

impl<T> UseBridgeHandle<T>
where
    T: Bridged,
{
    /// Send a message to an worker.
    pub fn send(&self, msg: T::Input) {
        let mut bridge = self.inner.borrow_mut();
        bridge.send(msg);
    }
}

/// A hook to bridge to an [`Worker`].
///
/// This hooks will only bridge the worker once over the entire component lifecycle.
///
/// Takes a callback as the only argument. The callback will be updated on every render to make
/// sure captured values (if any) are up to date.
///
/// # Examples
///
/// ```
/// # mod example {
/// use serde::{Deserialize, Serialize};
/// use yew::prelude::*;
/// use yew_agent::{use_bridge, UseBridgeHandle};
///
/// // This would usually live in the same file as your worker
/// #[derive(Serialize, Deserialize)]
/// pub enum WorkerResponseType {
///     IncrementCounter,
/// }
/// # mod my_worker_mod {
/// #   use yew_agent::{HandlerId, Public, WorkerLink};
/// #   use super::WorkerResponseType;
/// #   pub struct MyWorker {
/// #       pub link: WorkerLink<Self>,
/// #   }
///
/// #   impl yew_agent::Worker for MyWorker {
/// #       type Input = ();
/// #       type Output = WorkerResponseType;
/// #       type Reach = Public<Self>;
/// #       type Message = ();
/// #
/// #       fn create(link: WorkerLink<Self>) -> Self {
/// #           MyWorker { link }
/// #       }
/// #
/// #       fn update(&mut self, _msg: Self::Message) {
/// #           // do nothing
/// #       }
/// #
/// #       fn handle_input(&mut self, _msg: Self::Input, id: HandlerId) {
/// #           self.link.respond(id, WorkerResponseType::IncrementCounter);
/// #       }
/// #   }
/// # }
/// use my_worker_mod::MyWorker; // note that <MyWorker as yew_agent::Worker>::Output == WorkerResponseType
/// #[function_component(UseBridge)]
/// fn bridge() -> Html {
///     let counter = use_state(|| 0);
///
///     // a scoped block to clone the state in
///     {
///         let counter = counter.clone();
///         // response will be of type MyWorker::Output, i.e. WorkerResponseType
///         let bridge: UseBridgeHandle<MyWorker> = use_bridge(move |response| match response {
///             WorkerResponseType::IncrementCounter => {
///                 counter.set(*counter + 1);
///             }
///         });
///     }
///
///     html! {
///         <div>
///             {*counter}
///         </div>
///     }
/// }
/// # }
/// ```
#[hook]
pub fn use_bridge<T, F>(on_output: F) -> UseBridgeHandle<T>
where
    T: Bridged,
    F: Fn(T::Output) + 'static,
{
    let on_output = Rc::new(on_output);

    let on_output_clone = on_output.clone();
    let on_output_ref = use_mut_ref(move || on_output_clone);

    // Refresh the callback on every render.
    {
        let mut on_output_ref = on_output_ref.borrow_mut();
        *on_output_ref = on_output;
    }

    let bridge = use_mut_ref(move || {
        T::bridge({
            Rc::new(move |output| {
                let on_output = on_output_ref.borrow().clone();
                on_output(output);
            })
        })
    });

    UseBridgeHandle { inner: bridge }
}

impl<T: Worker> Clone for UseBridgeHandle<T> {
    fn clone(&self) -> Self {
        Self {
            inner: self.inner.clone(),
        }
    }
}