yew_state/
service.rs

1//! Wrapper for components with shared state.
2use std::collections::HashSet;
3use std::rc::Rc;
4
5use yew::{
6    agent::{Agent, AgentLink, Context, Dispatcher, HandlerId},
7    prelude::*,
8};
9
10use crate::handler::{HandlerLink, Reduction, ReductionOnce, StateHandler};
11
12/// Message send to [StateService](StateService).
13pub enum ServiceRequest<H>
14where
15    H: StateHandler,
16{
17    /// Apply a state change.
18    Apply(Reduction<H::Model>),
19    /// Apply a state change once.
20    ApplyOnce(ReductionOnce<H::Model>),
21    /// Registers the sender to be [notified](ServiceResponse) when state changes.
22    Subscribe,
23}
24
25/// Message sent to [StateService](StateService) subscribers.
26pub enum ServiceResponse<H>
27where
28    H: StateHandler,
29{
30    /// Current state, sent every time state changes.
31    State(Rc<H::Model>),
32    /// Link to state handler. Sent once on [subscribe](ServiceRequest::Subscribe).
33    Link(HandlerLink<H>),
34}
35
36/// Input message for either [StateService](StateService) or
37/// [StateHandler](crate::handler::StateHandler).
38pub enum ServiceInput<H>
39where
40    H: StateHandler,
41{
42    Service(ServiceRequest<H>),
43    Handler(H::Input),
44}
45
46/// Output message from either [StateService](StateService) or
47/// [StateHandler](crate::handler::StateHandler).
48pub enum ServiceOutput<H>
49where
50    H: StateHandler,
51{
52    Service(ServiceResponse<H>),
53    Handler(H::Output),
54}
55
56/// Context agent for managing shared state. In charge of applying changes to state then notifying
57/// subscribers of new state.
58pub struct StateService<HANDLER, SCOPE = HANDLER>
59where
60    HANDLER: StateHandler + 'static,
61    SCOPE: 'static,
62{
63    handler: HANDLER,
64    subscriptions: HashSet<HandlerId>,
65    link: AgentLink<StateService<HANDLER, SCOPE>>,
66    #[allow(dead_code)]
67    self_dispatcher: Dispatcher<Self>,
68}
69
70impl<HANDLER, SCOPE> Agent for StateService<HANDLER, SCOPE>
71where
72    HANDLER: StateHandler + 'static,
73    SCOPE: 'static,
74{
75    type Message = HANDLER::Message;
76    type Reach = Context<Self>;
77    type Input = ServiceInput<HANDLER>;
78    type Output = ServiceOutput<HANDLER>;
79
80    fn create(link: AgentLink<Self>) -> Self {
81        Self {
82            handler: <HANDLER as StateHandler>::new(HandlerLink::new(link.clone())),
83            subscriptions: Default::default(),
84            self_dispatcher: Self::dispatcher(),
85            link,
86        }
87    }
88
89    fn update(&mut self, msg: Self::Message) {
90        let changed = self.handler.update(msg);
91        if changed {
92            self.handler.changed();
93            self.notify_subscribers();
94        }
95    }
96
97    fn handle_input(&mut self, msg: Self::Input, who: HandlerId) {
98        match msg {
99            ServiceInput::Service(msg) => match msg {
100                ServiceRequest::Apply(reduce) => {
101                    reduce(Rc::make_mut(self.handler.state()));
102                    self.handler.changed();
103                }
104                ServiceRequest::ApplyOnce(reduce) => {
105                    reduce(Rc::make_mut(self.handler.state()));
106                    self.handler.changed();
107                }
108                ServiceRequest::Subscribe => {
109                    // Add component to subscriptions.
110                    self.subscriptions.insert(who);
111                    // Send current state.
112                    let state = Rc::clone(self.handler.state());
113                    self.link
114                        .respond(who, ServiceOutput::Service(ServiceResponse::State(state)));
115                    // Send handler link.
116                    self.link.respond(
117                        who,
118                        ServiceOutput::Service(ServiceResponse::Link(HandlerLink::new(
119                            self.link.clone(),
120                        ))),
121                    );
122                }
123            },
124            ServiceInput::Handler(msg) => {
125                let changed = self.handler.handle_input(msg, who);
126                if changed {
127                    self.handler.changed();
128                    self.notify_subscribers();
129                }
130            }
131        }
132
133        self.notify_subscribers();
134    }
135
136    fn disconnected(&mut self, who: HandlerId) {
137        self.subscriptions.remove(&who);
138    }
139}
140
141impl<HANDLER, SCOPE> StateService<HANDLER, SCOPE>
142where
143    HANDLER: StateHandler + 'static,
144    SCOPE: 'static,
145{
146    fn notify_subscribers(&mut self) {
147        let state = self.handler.state();
148        for who in self.subscriptions.iter().cloned() {
149            self.link.respond(
150                who,
151                ServiceOutput::Service(ServiceResponse::State(Rc::clone(state))),
152            );
153        }
154    }
155}
156
157/// A bridge to a [StateService]. This allows message passing with state handlers, as well as their
158/// parent service. Useful when you want to access the [events](ServiceResponse) emitted by
159/// [StateService].
160///
161/// [StateService]: StateService
162pub struct ServiceBridge<H, SCOPE = H>
163where
164    H: StateHandler + 'static,
165    SCOPE: 'static,
166{
167    bridge: Box<dyn Bridge<StateService<H, SCOPE>>>,
168}
169
170impl<H, SCOPE> ServiceBridge<H, SCOPE>
171where
172    H: StateHandler + 'static,
173{
174    /// Create a new bridge, automatically [subscribing](ServiceRequest::Subscribe).
175    pub fn new(callback: Callback<ServiceOutput<H>>) -> Self {
176        let mut bridge = StateService::bridge(callback);
177        bridge.send(ServiceInput::Service(ServiceRequest::Subscribe));
178
179        Self { bridge }
180    }
181
182    /// Send message to service.
183    pub fn send_service(&mut self, msg: ServiceRequest<H>) {
184        self.bridge.send(ServiceInput::Service(msg));
185    }
186
187    /// Send message to handler.
188    pub fn send_handler(&mut self, msg: H::Input) {
189        self.bridge.send(ServiceInput::Handler(msg));
190    }
191}
192
193impl<H> From<ServiceRequest<H>> for ServiceInput<H>
194where
195    H: StateHandler,
196{
197    fn from(msg: ServiceRequest<H>) -> Self {
198        ServiceInput::Service(msg)
199    }
200}
201
202impl<H> From<ServiceResponse<H>> for ServiceOutput<H>
203where
204    H: StateHandler,
205{
206    fn from(msg: ServiceResponse<H>) -> Self {
207        ServiceOutput::Service(msg)
208    }
209}