mirror of
https://github.com/tuono-labs/tuono
synced 2026-07-29 13:52:46 -07:00
feat: enable dynamic routes on frontend
This commit is contained in:
@@ -20,10 +20,11 @@ pub const AXUM_ENTRY_POINT: &str = r##"
|
|||||||
// File automatically generated
|
// File automatically generated
|
||||||
// Do not manually change it
|
// Do not manually change it
|
||||||
|
|
||||||
use axum::extract::Request;
|
use axum::extract::{Path, Request};
|
||||||
use axum::response::Html;
|
use axum::response::Html;
|
||||||
use axum::{routing::get, Router};
|
use axum::{routing::get, Router};
|
||||||
use tower_http::services::ServeDir;
|
use tower_http::services::ServeDir;
|
||||||
|
use std::collections::HashMap;
|
||||||
use tuono_lib::{ssr, Ssr};
|
use tuono_lib::{ssr, Ssr};
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
|
|
||||||
@@ -44,11 +45,11 @@ async fn main() {
|
|||||||
axum::serve(listener, app).await.unwrap();
|
axum::serve(listener, app).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn catch_all(request: Request) -> Html<String> {
|
async fn catch_all(Path(params): Path<HashMap<String, String>>, request: Request) -> Html<String> {
|
||||||
let pathname = &request.uri();
|
let pathname = &request.uri();
|
||||||
let headers = &request.headers();
|
let headers = &request.headers();
|
||||||
|
|
||||||
let req = tuono_lib::Request::new(pathname, headers);
|
let req = tuono_lib::Request::new(pathname, headers, params);
|
||||||
|
|
||||||
|
|
||||||
// TODO: remove unwrap
|
// TODO: remove unwrap
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use axum::http::{HeaderMap, Uri};
|
use axum::http::{HeaderMap, Uri};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Request<'a> {
|
pub struct Request<'a> {
|
||||||
uri: &'a Uri,
|
uri: &'a Uri,
|
||||||
headers: &'a HeaderMap,
|
headers: &'a HeaderMap,
|
||||||
|
pub params: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Location must match client side interface
|
/// Location must match client side interface
|
||||||
@@ -21,8 +22,16 @@ pub struct Location<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Request<'a> {
|
impl<'a> Request<'a> {
|
||||||
pub fn new(uri: &'a Uri, headers: &'a HeaderMap) -> Request<'a> {
|
pub fn new(
|
||||||
Request { uri, headers }
|
uri: &'a Uri,
|
||||||
|
headers: &'a HeaderMap,
|
||||||
|
params: HashMap<String, String>,
|
||||||
|
) -> Request<'a> {
|
||||||
|
Request {
|
||||||
|
uri,
|
||||||
|
headers,
|
||||||
|
params,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn location(&self) -> Location<'a> {
|
pub fn location(&self) -> Location<'a> {
|
||||||
|
|||||||
@@ -9,16 +9,21 @@ pub fn handler_core(_args: TokenStream, item: TokenStream) -> TokenStream {
|
|||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
use axum::response::{Html, IntoResponse};
|
use axum::response::{Html, IntoResponse};
|
||||||
use axum::extract::State;
|
use std::collections::HashMap;
|
||||||
|
use axum::extract::{State, Path};
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
|
|
||||||
#item
|
#item
|
||||||
|
|
||||||
pub async fn route(State(client): State<Client>, request: axum::extract::Request) -> Html<String> {
|
pub async fn route(
|
||||||
|
Path(params): Path<HashMap<String, String>>,
|
||||||
|
State(client): State<Client>,
|
||||||
|
request: axum::extract::Request
|
||||||
|
) -> Html<String> {
|
||||||
let pathname = &request.uri();
|
let pathname = &request.uri();
|
||||||
let headers = &request.headers();
|
let headers = &request.headers();
|
||||||
|
|
||||||
let req = tuono_lib::Request::new(pathname, headers);
|
let req = tuono_lib::Request::new(pathname, headers, params);
|
||||||
|
|
||||||
let local_response = #fn_name(req.clone(), client).await;
|
let local_response = #fn_name(req.clone(), client).await;
|
||||||
|
|
||||||
@@ -40,11 +45,15 @@ pub fn handler_core(_args: TokenStream, item: TokenStream) -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn api(State(client): State<Client>, request: axum::extract::Request) -> axum::response::Response {
|
pub async fn api(
|
||||||
|
Path(params): Path<HashMap<String, String>>,
|
||||||
|
State(client): State<Client>,
|
||||||
|
request: axum::extract::Request
|
||||||
|
) -> axum::response::Response {
|
||||||
let pathname = &request.uri();
|
let pathname = &request.uri();
|
||||||
let headers = &request.headers();
|
let headers = &request.headers();
|
||||||
|
|
||||||
let req = tuono_lib::Request::new(pathname, headers);
|
let req = tuono_lib::Request::new(pathname, headers, params);
|
||||||
|
|
||||||
let local_response = #fn_name(req.clone(), client).await;
|
let local_response = #fn_name(req.clone(), client).await;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useEffect, useRef } from 'react'
|
import { useState, useEffect, useRef } from 'react'
|
||||||
import type { Route } from '../route'
|
import type { Route } from '../route'
|
||||||
|
import { useRouterStore } from './useRouterStore'
|
||||||
|
|
||||||
const isServer = typeof document === 'undefined'
|
const isServer = typeof document === 'undefined'
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ export function useServerSideProps(
|
|||||||
serverSideProps: any,
|
serverSideProps: any,
|
||||||
): UseServerSidePropsReturn {
|
): UseServerSidePropsReturn {
|
||||||
const isFirstRendering = useRef<boolean>(true)
|
const isFirstRendering = useRef<boolean>(true)
|
||||||
|
const location = useRouterStore((st) => st.location)
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(
|
const [isLoading, setIsLoading] = useState<boolean>(
|
||||||
// Force loading if has handler
|
// Force loading if has handler
|
||||||
route.options.hasHandler &&
|
route.options.hasHandler &&
|
||||||
@@ -41,15 +43,14 @@ export function useServerSideProps(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// After client side routing load again the remote data
|
// After client side routing load again the remote data
|
||||||
if (route.options.hasHandler && !data) {
|
if (route.options.hasHandler) {
|
||||||
;(async (): Promise<void> => {
|
;(async (): Promise<void> => {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
try {
|
try {
|
||||||
const path = route.path === '/' ? '' : route.path
|
const res = await fetch(`/__tuono/data${location.pathname}`)
|
||||||
const res = await fetch(`/__tuono/data/${path}`)
|
|
||||||
setData(await res.json())
|
setData(await res.json())
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Handle here error
|
throw Error('Failed loading Server Side Data', { cause: error })
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}
|
}
|
||||||
@@ -60,7 +61,7 @@ export function useServerSideProps(
|
|||||||
return (): void => {
|
return (): void => {
|
||||||
setData(undefined)
|
setData(undefined)
|
||||||
}
|
}
|
||||||
}, [route.path])
|
}, [location.pathname])
|
||||||
|
|
||||||
return { isLoading, data }
|
return { isLoading, data }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user