rate_ui/widget/
wired_widget.rs

1use crate::agents::live::wire::{DoAction, Subscription, WireEnvelope};
2use crate::agents::live::{LiveAgent, LiveResponse};
3//use crate::common;
4use crate::widget::wired_bridge::AgentHook;
5use crate::widget::{Context, OnWireEvent, Widget};
6use anyhow::Error;
7use rill_protocol::diff::diff;
8use rill_protocol::flow::core::Flow;
9use rill_protocol::io::client::{ClientReqId, ClientResponse};
10use rill_protocol::io::provider::Path;
11use std::collections::{HashMap, HashSet};
12use yew::{Callback, Properties};
13
14pub trait WiredWidget<M>: Widget<Tag = Option<Path>, Meta = M> {
15    type Flow: Flow;
16
17    fn state_changed(&mut self, _reloaded: bool, _ctx: &mut Context<Self>) {}
18
19    fn state_update(
20        &mut self,
21        _tag: &Path,
22        _event: &<Self::Flow as Flow>::Event,
23        _reloaded: &mut bool,
24        _ctx: &mut Context<Self>,
25    ) {
26    }
27}
28
29#[derive(Properties, Clone, PartialEq)]
30pub struct SingleFlowProps {
31    pub path: Path,
32    #[prop_or_default]
33    pub triggered: Option<Callback<()>>,
34}
35
36impl SingleFlowProps {
37    pub fn notify(&self) {
38        if let Some(callback) = self.triggered.as_ref() {
39            callback.emit(());
40        }
41    }
42}
43
44pub struct SingleFlowMeta<T: WiredWidget<Self>> {
45    state: Option<T::Flow>,
46    wire: Option<Path>,
47}
48
49impl<T> Default for SingleFlowMeta<T>
50where
51    T: WiredWidget<Self>,
52{
53    fn default() -> Self {
54        Self {
55            state: None,
56            wire: None,
57        }
58    }
59}
60
61impl<T> SingleFlowMeta<T>
62where
63    T: WiredWidget<Self>,
64{
65    pub fn state(&self) -> Option<&T::Flow> {
66        self.state.as_ref()
67    }
68
69    /*
70    pub fn active(&self) -> Option<&Path> {
71        self.wire.as_ref()
72    }
73    */
74}
75
76impl<T> Context<T>
77where
78    T: WiredWidget<SingleFlowMeta<T>>,
79{
80    pub fn do_action(&mut self, action: <T::Flow as Flow>::Action) {
81        if let Some(path) = self.meta().wire.clone() {
82            let do_action = DoAction::<T::Flow>::new(path, action);
83            self.live().wire(None, do_action);
84            // TODO: Redraw is not needed here. Remove.
85            //self.redraw();
86        } else {
87            log::error!("No path to do action: {:?}", action);
88        }
89    }
90
91    /// SingleFlow mode also compatible with any other actions.
92    pub fn do_action_of<F: Flow>(&mut self, path: Path, action: F::Action) {
93        let do_action = DoAction::<F>::new(path, action);
94        self.live().wire(None, do_action);
95    }
96
97    /// It unwires automatically.
98    pub fn rewire(&mut self, path: Path) {
99        // Don't rewire if path the same
100        if self.meta().wire.as_ref() != Some(&path) {
101            self.unwire();
102            self.meta_mut().state.take();
103            let wire_task = Subscription::new(path.clone());
104            let new_wire = Some(path);
105            self.meta_mut().wire = new_wire.clone();
106            self.live().wire(new_wire, wire_task);
107            self.redraw();
108        }
109    }
110
111    pub fn unwire(&mut self) {
112        if let Some(path) = self.meta_mut().wire.take() {
113            self.live().unwire(&Some(path));
114        }
115    }
116}
117
118/*
119pub fn loading_view() -> Html {
120    html! {
121        <common::Spinner />
122    }
123}
124*/
125
126pub struct SingleHook;
127
128impl AgentHook for SingleHook {
129    type Agent = LiveAgent;
130}
131
132impl<T> OnWireEvent<SingleHook> for T
133where
134    T: WiredWidget<SingleFlowMeta<Self>>,
135{
136    fn on_wire(
137        &mut self,
138        tag: &Self::Tag,
139        event: WireEnvelope<ClientReqId, LiveResponse>,
140        ctx: &mut Context<Self>,
141    ) -> Result<(), Error> {
142        match (tag.as_ref(), event.data) {
143            (Some(path), event) => {
144                if let LiveResponse::Forwarded(response) = event {
145                    let mut reloaded = false;
146                    match response {
147                        ClientResponse::State(data) => {
148                            let res = T::Flow::unpack_state(&data);
149                            match res {
150                                Ok(state) => {
151                                    ctx.meta_mut().state = Some(state);
152                                    reloaded = true;
153                                }
154                                Err(err) => {
155                                    log::error!("Can't unpack the state: {}", err);
156                                }
157                            }
158                        }
159                        ClientResponse::Delta(data) => {
160                            let res = T::Flow::unpack_event(&data);
161                            match res {
162                                Ok(event) => {
163                                    self.state_update(path, &event, &mut reloaded, ctx);
164                                    if let Some(state) = ctx.meta_mut().state.as_mut() {
165                                        state.apply(event);
166                                    }
167                                }
168                                Err(err) => {
169                                    log::error!("Can't unpack the delta: {}", err);
170                                }
171                            }
172                        }
173                        ClientResponse::Done => {
174                            // TODO: What to do when the stream is finished completely?
175                        }
176                        other => {
177                            log::error!("Unexpected message for the single flow: {:?}", other);
178                        }
179                    }
180                    self.state_changed(reloaded, ctx);
181                }
182            }
183            (None, _) => {
184                // Redraw on action
185                ctx.redraw();
186            }
187        }
188        Ok(())
189    }
190}
191
192#[derive(Properties, Clone, PartialEq)]
193pub struct MultiFlowProps {
194    pub paths: HashSet<Path>,
195}
196
197pub struct MultiFlowMeta<T: WiredWidget<Self>> {
198    states: HashMap<Path, T::Flow>,
199    wires: HashSet<Path>,
200}
201
202impl<T> Default for MultiFlowMeta<T>
203where
204    T: WiredWidget<Self>,
205{
206    fn default() -> Self {
207        Self {
208            states: HashMap::new(),
209            wires: HashSet::new(),
210        }
211    }
212}
213
214impl<T> MultiFlowMeta<T>
215where
216    T: WiredWidget<Self>,
217{
218    pub fn states(&self) -> &HashMap<Path, T::Flow> {
219        &self.states
220    }
221}
222
223impl<T> Context<T>
224where
225    T: WiredWidget<MultiFlowMeta<T>>,
226{
227    pub fn rewire_many(&mut self, paths: &HashSet<Path>) {
228        let (to_add, to_remove) = diff(&self.meta().wires, paths);
229        for path in to_add {
230            let wire_task = Subscription::new(path.clone());
231            self.live().wire(Some(path.clone()), wire_task);
232            self.meta_mut().wires.insert(path);
233            // `State/Flow` will be received using `wire`
234            self.redraw();
235        }
236        for path in to_remove {
237            self.meta_mut().wires.remove(&path);
238            self.meta_mut().states.remove(&path);
239            self.live().unwire(&Some(path));
240            self.redraw();
241        }
242    }
243}
244
245pub struct MultiHook;
246
247impl AgentHook for MultiHook {
248    type Agent = LiveAgent;
249}
250
251impl<T> OnWireEvent<MultiHook> for T
252where
253    T: WiredWidget<MultiFlowMeta<Self>>,
254{
255    fn on_wire(
256        &mut self,
257        tag: &Self::Tag,
258        event: WireEnvelope<ClientReqId, LiveResponse>,
259        ctx: &mut Context<Self>,
260    ) -> Result<(), Error> {
261        match (tag.as_ref(), event.data) {
262            (Some(path), event) => {
263                if let LiveResponse::Forwarded(response) = event {
264                    let mut reloaded = false;
265                    match response {
266                        ClientResponse::State(data) => {
267                            let state = T::Flow::unpack_state(&data).unwrap();
268                            ctx.meta_mut().states.insert(path.clone(), state);
269                            reloaded = true;
270                        }
271                        ClientResponse::Delta(data) => {
272                            // TODO: Don't `unwrap` here
273                            let event = T::Flow::unpack_event(&data).unwrap();
274                            self.state_update(path, &event, &mut reloaded, ctx);
275                            if let Some(state) = ctx.meta_mut().states.get_mut(path) {
276                                state.apply(event);
277                            }
278                        }
279                        ClientResponse::Done => {
280                            // TODO: What to do when the stream is finished completely?
281                        }
282                        other => {
283                            log::error!("Unexpected message for the multi flow: {:?}", other);
284                        }
285                    }
286                    self.state_changed(reloaded, ctx);
287                }
288            }
289            (None, _) => {
290                // Redraw on action
291                ctx.redraw();
292            }
293        }
294        Ok(())
295    }
296}