feat: add missing server fn redirect hook to router (#2815)

This commit is contained in:
Greg Johnston 2024-08-12 08:07:45 -04:00 committed by GitHub
parent 27dbadb7d2
commit 893d47f1c5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 60 additions and 6 deletions

View file

@ -69,25 +69,33 @@ where
Chil: IntoView,
{
#[cfg(feature = "ssr")]
let current_url = {
let (current_url, redirect_hook) = {
let req = use_context::<RequestUrl>().expect("no RequestUrl provided");
let parsed = req.parse().expect("could not parse RequestUrl");
ArcRwSignal::new(parsed)
let current_url = ArcRwSignal::new(parsed);
(current_url, Box::new(move |_: &str| {}))
};
#[cfg(not(feature = "ssr"))]
let current_url = {
let (current_url, redirect_hook) = {
let location =
BrowserUrl::new().expect("could not access browser navigation"); // TODO options here
location.init(base.clone());
provide_context(location.clone());
location.as_url().clone()
let current_url = location.as_url().clone();
let redirect_hook = Box::new(|loc: &str| BrowserUrl::redirect(loc));
(current_url, redirect_hook)
};
// provide router context
let state = ArcRwSignal::new(State::new(None));
let location = Location::new(current_url.read_only(), state.read_only());
// TODO server function redirect hook
// set server function redirect hook
_ = server_fn::redirect::set_redirect_hook(redirect_hook);
provide_context(RouterContext {
base,
current_url,

View file

@ -1,8 +1,9 @@
use super::{handle_anchor_click, LocationChange, LocationProvider, Url};
use crate::params::ParamsMap;
use crate::{hooks::use_navigate, params::ParamsMap};
use core::fmt;
use futures::channel::oneshot;
use js_sys::{try_iter, Array, JsString};
use leptos::prelude::*;
use or_poisoned::OrPoisoned;
use reactive_graph::{
signal::ArcRwSignal,
@ -206,6 +207,24 @@ impl LocationProvider for BrowserUrl {
// scroll to el
Self::scroll_to_el(loc.scroll);
}
fn redirect(loc: &str) {
let navigate = use_navigate();
let Some(url) = resolve_redirect_url(loc) else {
return; // resolve_redirect_url() already logs an error
};
let current_origin = helpers::location().origin().unwrap();
if url.origin() == current_origin {
let navigate = navigate.clone();
// delay by a tick here, so that the Action updates *before* the redirect
request_animation_frame(move || {
navigate(&url.href(), Default::default());
});
// Use set_href() if the conditions for client-side navigation were not satisfied
} else if let Err(e) = helpers::location().set_href(&url.href()) {
leptos::logging::error!("Failed to redirect: {e:#?}");
}
}
}
fn search_params_from_web_url(
@ -225,3 +244,28 @@ fn search_params_from_web_url(
})
.collect()
}
/// Resolves a redirect location to an (absolute) URL.
pub(crate) fn resolve_redirect_url(loc: &str) -> Option<web_sys::Url> {
let origin = match window().location().origin() {
Ok(origin) => origin,
Err(e) => {
leptos::logging::error!("Failed to get origin: {:#?}", e);
return None;
}
};
// TODO: Use server function's URL as base instead.
let base = origin;
match web_sys::Url::new_with_base(loc, &base) {
Ok(url) => Some(url),
Err(e) => {
leptos::logging::error!(
"Invalid redirect location: {}",
e.as_string().unwrap_or_default(),
);
None
}
}
}

View file

@ -165,6 +165,8 @@ pub trait LocationProvider: Clone + 'static {
}
fn parse_with_base(url: &str, base: &str) -> Result<Url, Self::Error>;
fn redirect(loc: &str);
}
#[derive(Debug, Clone, Default)]