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
106
107
108
109
110
111
112
113
114
115
116
117
118
use gloo::history::{BrowserHistory, History, HistoryListener};
use std::{
    cell::{Cell, RefCell},
    collections::HashMap,
    rc::Rc,
};

use dioxus_core::ScopeId;

pub struct RouterService {
    pub(crate) regen_route: Rc<dyn Fn(ScopeId)>,
    history: Rc<RefCell<BrowserHistory>>,
    registerd_routes: RefCell<RouteSlot>,
    slots: Rc<RefCell<Vec<(ScopeId, String)>>>,
    root_found: Rc<Cell<bool>>,
    cur_root: RefCell<String>,
    listener: HistoryListener,
}

enum RouteSlot {
    Routes {
        // the partial route
        partial: String,

        // the total route
        total: String,

        // Connections to other routs
        rest: Vec<RouteSlot>,
    },
}

impl RouterService {
    pub fn new(regen_route: Rc<dyn Fn(ScopeId)>, root_scope: ScopeId) -> Self {
        let history = BrowserHistory::default();
        let location = history.location();
        let path = location.path();

        let slots: Rc<RefCell<Vec<(ScopeId, String)>>> = Default::default();

        let _slots = slots.clone();

        let root_found = Rc::new(Cell::new(false));
        let regen = regen_route.clone();
        let _root_found = root_found.clone();
        let listener = history.listen(move || {
            _root_found.set(false);
            // checking if the route is valid is cheap, so we do it
            for (slot, _) in _slots.borrow_mut().iter().rev() {
                log::trace!("regenerating slot {:?}", slot);
                regen(*slot);
            }
        });

        Self {
            registerd_routes: RefCell::new(RouteSlot::Routes {
                partial: String::from("/"),
                total: String::from("/"),
                rest: Vec::new(),
            }),
            root_found,
            history: Rc::new(RefCell::new(history)),
            regen_route,
            slots,
            cur_root: RefCell::new(path.to_string()),
            listener,
        }
    }

    pub fn push_route(&self, route: &str) {
        self.history.borrow_mut().push(route);
    }

    pub fn register_total_route(&self, route: String, scope: ScopeId, fallback: bool) {
        self.slots.borrow_mut().push((scope, route));
    }

    pub fn should_render(&self, scope: ScopeId) -> bool {
        if self.root_found.get() {
            return false;
        }

        let location = self.history.borrow().location();
        let path = location.path();

        let roots = self.slots.borrow();

        let root = roots.iter().find(|(id, route)| id == &scope);

        // fallback logic
        match root {
            Some((_id, route)) => {
                if route == path {
                    self.root_found.set(true);
                    true
                } else {
                    if route == "" {
                        self.root_found.set(true);
                        true
                    } else {
                        false
                    }
                }
            }
            None => false,
        }
    }
}

pub struct RouterCfg {
    initial_route: String,
}

impl RouterCfg {
    pub fn new(initial_route: String) -> Self {
        Self { initial_route }
    }
}