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
use std::rc::Rc;
use std::ops::Deref;

use router_rs::prelude::Router;

use crate::state::Msg;
use crate::state::State;

/// Manages the state wrapper and routing of the Roost application
pub struct Store {
    /// A tuple struct that holds the application state
    state: StateWrapper,
    /// An function that will execute after the route has changed
    after_route: Option<Box<Fn(&str) -> ()>>,
    /// Handles routing operations once created accordingly in `app/src/lib.rs`
    router: Option<Rc<Router>>,
    /// A `Vec` of callback functions provided by subscribed clients that will execute after a
    /// change of state
    listeners: Vec<Box<Fn() -> ()>>,
}

impl Store {
    /// Returns a `Store` with a `StateWrapper` that is holding the provided `State`
    pub fn new(state: State) -> Store {
        Store {
            state: StateWrapper(state),
            after_route: None,
            router: None,
            listeners: vec![],
        }
    }

    /// The function that handles a change of application state.
    /// Available changes that can be made to the application state can be found in
    /// `app/src/state/msg.rs`
    pub fn msg(&mut self, msg: &Msg) {
        match msg {
            // TODO: Right now `on_visit` cannot borrow store since it's already borrowed.
            // So we might want to explore wraping our `on_visit` in requestAnimationFrame
            // so that by the time it runs we are no longer borrowing store ... or something ...
            Msg::SetPath(path) => {
                if let Some(router) = &self.router {
                    if let Some(route_handler) = router.matching_routerhandler(path.as_str()) {
                        route_handler.on_visit(path.as_str());
                    }
                }

                self.state.msg(msg);

                if let Some(after_route) = &self.after_route {
                    after_route(path.as_str());
                }
            }
            _ => self.state.msg(msg),
        }

        // Whenever we update state we'll let all of our listeners know that state was updated
        for callback in self.listeners.iter() {
            callback();
        }
    }

    /// For a client to call when they want their provided callback function to be executed on a
    /// change of application state
    pub fn subscribe(&mut self, callback: Box<Fn() -> ()>) {
        self.listeners.push(callback)
    }

    /// Set the function that will execute when the current route has changed
    pub fn set_after_route(&mut self, after_route: Box<Fn(&str) -> ()>) {
        self.after_route = Some(after_route);
    }

    /// Set the router to handle available routes in our application
    pub fn set_router(&mut self, router: Rc<Router>) {
        self.router = Some(router);
    }
}

impl Deref for Store {
    type Target = State;

    fn deref(&self) -> &<Self as Deref>::Target {
        &self.state
    }
}

/// Holds the state of the application
struct StateWrapper(State);

/// Wrapper function to call its corresponding `State` declaration
impl StateWrapper {
    /// Pass a change of application state to the `State` itself
    fn msg(&mut self, msg: &Msg) {
        self.0.msg(msg)
    }
}

impl Deref for StateWrapper {
    type Target = State;

    fn deref(&self) -> &<Self as Deref>::Target {
        &self.0
    }
}