use std::cell::RefCell;
use std::collections::HashSet;
use std::ops::Deref;
use std::rc::Rc;
use yew::agent::{Agent, AgentLink, Context, Discoverer, Dispatcher, HandlerId};
use yew::prelude::*;
pub trait Store: Sized + 'static {
type Input;
type Action;
fn new() -> Self;
fn handle_input(&self, link: AgentLink<StoreWrapper<Self>>, msg: Self::Input);
fn reduce(&mut self, msg: Self::Action);
}
#[derive(Debug)]
pub struct StoreWrapper<S: Store> {
pub handlers: HashSet<HandlerId>,
pub link: AgentLink<Self>,
pub state: Shared<S>,
pub self_dispatcher: Dispatcher<Self>,
}
type Shared<T> = Rc<RefCell<T>>;
#[derive(Debug)]
pub struct ReadOnly<S> {
state: Shared<S>,
}
impl<S> ReadOnly<S> {
pub fn borrow(&self) -> impl Deref<Target = S> + '_ {
self.state.borrow()
}
}
impl<S: Store> Agent for StoreWrapper<S> {
type Reach = Context<Self>;
type Message = S::Action;
type Input = S::Input;
type Output = ReadOnly<S>;
fn create(link: AgentLink<Self>) -> Self {
let state = Rc::new(RefCell::new(S::new()));
let handlers = HashSet::new();
let self_dispatcher = Self::dispatcher();
StoreWrapper {
handlers,
link,
state,
self_dispatcher,
}
}
fn update(&mut self, msg: Self::Message) {
{
self.state.borrow_mut().reduce(msg);
}
for handler in self.handlers.iter() {
self.link.respond(
*handler,
ReadOnly {
state: self.state.clone(),
},
);
}
}
fn connected(&mut self, id: HandlerId) {
self.handlers.insert(id);
self.link.respond(
id,
ReadOnly {
state: self.state.clone(),
},
);
}
fn handle_input(&mut self, msg: Self::Input, _id: HandlerId) {
self.state.borrow().handle_input(self.link.clone(), msg);
}
fn disconnected(&mut self, id: HandlerId) {
self.handlers.remove(&id);
}
}
pub trait Bridgeable: Sized + 'static {
type Wrapper: Agent;
fn bridge(
callback: Callback<<Self::Wrapper as Agent>::Output>,
) -> Box<dyn Bridge<Self::Wrapper>>;
}
impl<T> Bridgeable for T
where
T: Store,
{
type Wrapper = StoreWrapper<T>;
fn bridge(
callback: Callback<<Self::Wrapper as Agent>::Output>,
) -> Box<dyn Bridge<Self::Wrapper>> {
<Self::Wrapper as Agent>::Reach::spawn_or_join(Some(callback))
}
}