1use crate::Store;
2use std::{cell::RefCell, fmt::Debug, rc::Rc};
3use yew::{
4 html, html::ChildrenRenderer, virtual_dom::VChild, ChildrenWithProps, Component, ComponentLink,
5 Properties,
6};
7
8#[derive(Clone)]
9pub struct MapStateToProps<C: Component, State>(
10 fn(&Rc<State>, &C::Properties) -> Option<C::Properties>,
11);
12
13impl<C, State> PartialEq for MapStateToProps<C, State>
14where
15 C: Component,
16{
17 fn eq(&self, other: &MapStateToProps<C, State>) -> bool {
18 (self.0 as *const ()) == (other.0 as *const ())
19 }
20}
21
22impl<C, State> MapStateToProps<C, State>
23where
24 C: Component,
25{
26 pub fn new(function: fn(&Rc<State>, &C::Properties) -> Option<C::Properties>) -> Self {
27 Self(function)
28 }
29
30 pub fn perform(&self, state: &Rc<State>, props: &C::Properties) -> Option<C::Properties> {
31 (self.0)(state, props)
32 }
33}
34
35impl<C: Component, State> Debug for MapStateToProps<C, State> {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37 write!(f, "MapStateToProps(function @ {:p})", &self.0)
38 }
39}
40
41#[derive(Clone, Properties)]
42struct Props<C, State, Action>
43where
44 C: Component + Clone,
45 C::Properties: PartialEq,
46 State: Clone,
47 Action: Clone,
48{
49 pub map_state_to_props: MapStateToProps<C, State>,
50 pub store: Rc<RefCell<Store<State, Action, (), ()>>>,
51 pub children: ChildrenWithProps<C>,
52}
53
54impl<C, State, Action> Debug for Props<C, State, Action>
55where
56 C: Component + Clone,
57 C::Properties: PartialEq,
58 State: Clone,
59 Action: Clone,
60{
61 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 write!(
63 f,
64 "Provider::Props{{map_state_to_props: {0:?}, store @ {1:p}, children: {2:?}}}",
65 self.map_state_to_props, &*self.store, self.children
66 )
67 }
68}
69
70impl<C, State, Action> PartialEq for Props<C, State, Action>
71where
72 C: Component + Clone,
73 C::Properties: PartialEq,
74 State: Clone,
75 Action: Clone,
76{
77 fn eq(&self, other: &Props<C, State, Action>) -> bool {
78 Rc::ptr_eq(&self.store, &other.store)
80 && self.map_state_to_props == other.map_state_to_props
81 && self.children == other.children
82 }
83}
84
85enum Msg<State> {
86 StateUpdate(Rc<State>),
87}
88
89struct Provider<C, State, Action, Event>
90where
91 C: Component + Clone,
92 C::Properties: PartialEq,
93 State: Clone + 'static,
94 Action: Clone + 'static,
95 Event: Clone + 'static,
96{
97 props: Props<C, State, Action>,
98 children: ChildrenWithProps<C>,
99 _link: ComponentLink<Provider<C, State, Action, Event>>,
100 _callback: crate::Callback<State, Event>,
101}
102
103impl<C, State, Action, Event> Provider<C, State, Action, Event>
104where
105 C: Component + Clone,
106 C::Properties: PartialEq,
107 State: Clone + 'static,
108 Action: Clone + 'static,
109 Event: Clone + 'static,
110{
111 fn update_children_props(
112 children: &ChildrenWithProps<C>,
113 state: &Rc<State>,
114 map_state_to_props: &MapStateToProps<C, State>,
115 ) -> Option<ChildrenWithProps<C>> {
116 let mut children_vec: Vec<VChild<C>> = children.iter().collect();
119 let mut child_props_changed = false;
120
121 for child in &mut children_vec {
122 if let Some(properties) = map_state_to_props.perform(state, &child.props) {
123 child.props = properties;
124 child_props_changed = true;
125 }
126 }
127
128 if child_props_changed {
129 Some(ChildrenRenderer::new(children_vec))
130 } else {
131 None
132 }
133 }
134}
135
136impl<C, State, Action, Event> Component for Provider<C, State, Action, Event>
137where
138 C: Component + Clone,
139 C::Properties: PartialEq,
140 State: Clone + 'static,
141 Action: Clone + 'static,
142 Event: Clone + 'static,
143{
144 type Message = Msg<State>;
145 type Properties = Props<C, State, Action>;
146
147 fn create(props: Props<C, State, Action>, link: yew::ComponentLink<Self>) -> Self {
148 let callback = link.callback(|(state, _)| Msg::StateUpdate(state)).into();
149
150 let children = match Self::update_children_props(
151 &props.children,
152 &props.store.borrow().state(),
153 &props.map_state_to_props,
154 ) {
155 None => props.children.clone(),
156 Some(children) => children,
157 };
158
159 Self {
160 props,
161 children,
162 _link: link,
163 _callback: callback,
164 }
165 }
166
167 fn update(&mut self, msg: Msg<State>) -> yew::ShouldRender {
168 match msg {
169 Msg::StateUpdate(state) => {
170 let result: Option<ChildrenWithProps<C>> = Self::update_children_props(
171 &self.props.children,
172 &state,
173 &self.props.map_state_to_props,
174 );
175 match result {
176 Some(new_children) => {
177 self.children = new_children;
178 true
179 }
180 None => false,
181 }
182 }
183 }
184 }
185
186 fn change(&mut self, props: Props<C, State, Action>) -> yew::ShouldRender {
187 if self.props != props {
188 if self.props.children != props.children {
189 match Self::update_children_props(
190 &props.children,
191 &props.store.borrow().state(),
192 &props.map_state_to_props,
193 ) {
194 None => self.children = props.children.clone(),
195 Some(children) => self.children = children,
196 };
197 }
198
199 self.props = props;
200 true
201 } else {
202 false
203 }
204 }
205
206 fn view(&self) -> yew::Html {
207 html! { <>{ self.children.clone() }</> }
208 }
209}