it almost lives?

This commit is contained in:
Evan Almloff committed 2023-07-25 18:14:48 -07:00
1 parent d637ef187c
commit 5f0dd3af3e
43 files changed
+506 -244

No files matched your search

+5 -5
View File
@@ -1,10 +1,10 @@
use crate::prelude::{ExternalNavigationFailure, GenericRouterContext, NavigationTarget, Routable};
use crate::prelude::{ExternalNavigationFailure, NavigationTarget, Routable, RouterContext};
/// A view into the navigation state of a router.
#[derive(Clone)]
pub struct GenericNavigator<R: Routable>(pub(crate) GenericRouterContext<R>);
pub struct GenericNavigator(pub(crate) RouterContext);
impl<R: Routable> GenericNavigator<R> {
impl GenericNavigator {
/// Check whether there is a previous page to navigate back to.
#[must_use]
pub fn can_go_back(&self) -> bool {
@@ -34,7 +34,7 @@ impl<R: Routable> GenericNavigator<R> {
/// Push a new location.
///
/// The previous location will be available to go back to.
pub fn push(
pub fn push<R: Routable>(
&self,
target: impl Into<NavigationTarget<R>>,
) -> Option<ExternalNavigationFailure> {
@@ -44,7 +44,7 @@ impl<R: Routable> GenericNavigator<R> {
/// Replace the current location.
///
/// The previous location will **not** be available to go back to.
pub fn replace(
pub fn replace<R: Routable>(
&self,
target: impl Into<NavigationTarget<R>>,
) -> Option<ExternalNavigationFailure> {
+2 -2
View File
@@ -31,7 +31,7 @@ impl<R> OutletContext<R> {
where
R: Routable + Clone,
{
let router = use_router_internal::<R>(cx)
let router = use_router_internal(cx)
.as_ref()
.expect("Outlet must be inside of a router");
let outlet: &OutletContext<R> = use_outlet_context(cx);
@@ -51,6 +51,6 @@ impl<R> OutletContext<R> {
}
}
router.current().render(cx, current_level)
router.current::<R>().render(cx, current_level)
}
}
+157 -46
View File
@@ -1,4 +1,5 @@
use std::{
any::Any,
collections::HashSet,
sync::{Arc, RwLock, RwLockWriteGuard},
};
@@ -6,7 +7,7 @@ use std::{
use dioxus::prelude::*;
use crate::{
history::HistoryProvider, navigation::NavigationTarget, routable::Routable,
navigation::NavigationTarget, prelude::AnyHistoryProvider, routable::Routable,
router_cfg::RouterConfig,
};
@@ -15,52 +16,31 @@ use crate::{
pub struct ExternalNavigationFailure(String);
/// A function the router will call after every routing update.
pub(crate) type RoutingCallback<R> =
Arc<dyn Fn(GenericRouterContext<R>) -> Option<NavigationTarget<R>>>;
pub(crate) type RoutingCallback<R> = Arc<dyn Fn(LinkContext<R>) -> Option<NavigationTarget<R>>>;
struct MutableRouterState<R>
where
R: Routable,
{
struct MutableRouterState {
/// The current prefix.
prefix: Option<String>,
history: Box<dyn HistoryProvider<R>>,
history: Box<dyn AnyHistoryProvider>,
unresolved_error: Option<ExternalNavigationFailure>,
}
/// A collection of router data that manages all routing functionality.
pub struct GenericRouterContext<R>
where
R: Routable,
{
state: Arc<RwLock<MutableRouterState<R>>>,
#[derive(Clone)]
pub struct RouterContext {
state: Arc<RwLock<MutableRouterState>>,
subscribers: Arc<RwLock<HashSet<ScopeId>>>,
subscriber_update: Arc<dyn Fn(ScopeId)>,
routing_callback: Option<RoutingCallback<R>>,
routing_callback: Option<Arc<dyn Fn(RouterContext) -> Option<NavigationTarget<Box<dyn Any>>>>>,
failure_external_navigation: fn(Scope) -> Element,
}
impl<R: Routable> Clone for GenericRouterContext<R> {
fn clone(&self) -> Self {
Self {
state: self.state.clone(),
subscribers: self.subscribers.clone(),
subscriber_update: self.subscriber_update.clone(),
routing_callback: self.routing_callback.clone(),
failure_external_navigation: self.failure_external_navigation,
}
}
}
impl<R> GenericRouterContext<R>
where
R: Routable,
{
pub(crate) fn new(
impl RouterContext {
pub(crate) fn new<R: Routable + 'static>(
mut cfg: RouterConfig<R>,
mark_dirty: Arc<dyn Fn(ScopeId) + Sync + Send>,
) -> Self
@@ -82,7 +62,21 @@ where
subscribers: subscribers.clone(),
subscriber_update,
routing_callback: cfg.on_update,
routing_callback: cfg.on_update.map(|update| {
Arc::new(move |ctx| {
let ctx = LinkContext {
inner: ctx,
_marker: std::marker::PhantomData,
};
update(ctx).map(|t| match t {
NavigationTarget::Internal(r) => {
NavigationTarget::Internal(Box::new(r) as Box<dyn Any>)
}
NavigationTarget::External(s) => NavigationTarget::External(s),
})
})
as Arc<dyn Fn(RouterContext) -> Option<NavigationTarget<Box<dyn Any>>>>
}),
failure_external_navigation: cfg.failure_external_navigation,
};
@@ -100,6 +94,11 @@ where
myself
}
pub(crate) fn route_from_str(&self, route: &str) -> Result<Box<dyn Any>, String> {
let state = self.state.read().unwrap();
state.history.parse_route(route)
}
/// Check whether there is a previous page to navigate back to.
#[must_use]
pub fn can_go_back(&self) -> bool {
@@ -134,14 +133,10 @@ where
self.change_route();
}
/// Push a new location.
///
/// The previous location will be available to go back to.
pub fn push(
pub(crate) fn push_any(
&self,
target: impl Into<NavigationTarget<R>>,
target: NavigationTarget<Box<dyn Any>>,
) -> Option<ExternalNavigationFailure> {
let target = target.into();
match target {
NavigationTarget::Internal(p) => {
let mut state = self.state_mut();
@@ -153,10 +148,29 @@ where
self.change_route()
}
/// Push a new location.
///
/// The previous location will be available to go back to.
pub fn push<R: Routable>(
&self,
target: impl Into<NavigationTarget<R>>,
) -> Option<ExternalNavigationFailure> {
let target = target.into();
match target {
NavigationTarget::Internal(p) => {
let mut state = self.state_mut();
state.history.push(Box::new(p))
}
NavigationTarget::External(e) => return self.external(e),
}
self.change_route()
}
/// Replace the current location.
///
/// The previous location will **not** be available to go back to.
pub fn replace(
pub fn replace<R: Routable>(
&self,
target: impl Into<NavigationTarget<R>>,
) -> Option<ExternalNavigationFailure> {
@@ -165,7 +179,7 @@ where
{
let mut state = self.state_mut();
match target {
NavigationTarget::Internal(p) => state.history.replace(p),
NavigationTarget::Internal(p) => state.history.replace(Box::new(p)),
NavigationTarget::External(e) => return self.external(e),
}
}
@@ -174,11 +188,24 @@ where
}
/// The route that is currently active.
pub fn current(&self) -> R
where
R: Clone,
{
self.state.read().unwrap().history.current_route()
pub fn current<R: Routable>(&self) -> R {
self.state
.read()
.unwrap()
.history
.current_route()
.downcast::<R>()
.unwrap()
}
/// The route that is currently active.
pub fn current_route_string(&self) -> String {
self.state
.read()
.unwrap()
.history
.current_route()
.to_string()
}
/// The prefix that is currently active.
@@ -201,7 +228,7 @@ where
}
}
fn state_mut(&self) -> RwLockWriteGuard<MutableRouterState<R>> {
fn state_mut(&self) -> RwLockWriteGuard<MutableRouterState> {
self.state.write().unwrap()
}
@@ -254,3 +281,87 @@ where
None
}
}
pub struct LinkContext<R> {
inner: RouterContext,
_marker: std::marker::PhantomData<R>,
}
impl<R> LinkContext<R>
where
R: Routable,
{
/// Check whether there is a previous page to navigate back to.
#[must_use]
pub fn can_go_back(&self) -> bool {
self.inner.can_go_back()
}
/// Check whether there is a future page to navigate forward to.
#[must_use]
pub fn can_go_forward(&self) -> bool {
self.inner.can_go_forward()
}
/// Go back to the previous location.
///
/// Will fail silently if there is no previous location to go to.
pub fn go_back(&self) {
self.inner.go_back();
}
/// Go back to the next location.
///
/// Will fail silently if there is no next location to go to.
pub fn go_forward(&self) {
self.inner.go_forward();
}
/// Push a new location.
///
/// The previous location will be available to go back to.
pub fn push(
&self,
target: impl Into<NavigationTarget<R>>,
) -> Option<ExternalNavigationFailure> {
self.inner.push(target)
}
/// Replace the current location.
///
/// The previous location will **not** be available to go back to.
pub fn replace(
&self,
target: impl Into<NavigationTarget<R>>,
) -> Option<ExternalNavigationFailure> {
self.inner.replace(target)
}
/// The route that is currently active.
pub fn current(&self) -> R
where
R: Clone,
{
self.inner.current()
}
/// The prefix that is currently active.
pub fn prefix(&self) -> Option<String> {
self.inner.prefix()
}
/// Manually subscribe to the current route
pub fn subscribe(&self, id: ScopeId) {
self.inner.subscribe(id)
}
/// Manually unsubscribe from the current route
pub fn unsubscribe(&self, id: ScopeId) {
self.inner.unsubscribe(id)
}
/// Clear any unresolved errors
pub fn clear_error(&self) {
self.inner.clear_error()
}
}