Skip to content

Commit

Permalink
refactor: made basic examples use reactive state
Browse files Browse the repository at this point in the history
Not the showcase example yet, but that's getting a major overhaul.
  • Loading branch information
arctic-hen7 committed Jan 29, 2022
1 parent a355157 commit 1570e5d
Show file tree
Hide file tree
Showing 10 changed files with 61 additions and 74 deletions.
14 changes: 5 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,15 @@ Here's a taste of Perseus (see [the _tiny_ example](https://github.com/arctic-he

```rust
use perseus::{define_app, ErrorPages, Template};
use sycamore::template;
use sycamore::view;
define_app! {
templates: [
Template::<G>::new("index").template(|_| {
template! {
p { "Hello World!" }
}
Template::<G>::new("index").template(|_| view! {
p { "Hello World!" }
})
],
error_pages: ErrorPages::new(|url, status, err, _| {
template! {
p { (format!("An error with HTTP code {} occurred at '{}': '{}'.", status, url, err)) }
}
error_pages: ErrorPages::new(|url, status, err, _| view! {
p { (format!("An error with HTTP code {} occurred at '{}': '{}'.", status, url, err)) }
})
}
```
Expand Down
5 changes: 2 additions & 3 deletions examples/basic/src/templates/about.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use perseus::Template;
use sycamore::prelude::{component, view, Html, SsrNode, View};
use sycamore::prelude::{view, Html, SsrNode, View};

#[perseus::template(AboutPage)]
#[component(AboutPage<G>)]
#[perseus::template_rx(AboutPage)]
pub fn about_page() -> View<G> {
view! {
p { "About." }
Expand Down
26 changes: 12 additions & 14 deletions examples/basic/src/templates/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,48 @@ use perseus::{
http::header::{HeaderMap, HeaderName},
Html, RenderFnResultWithCause, SsrNode, Template,
};
use serde::{Deserialize, Serialize};
use sycamore::prelude::{component, view, View};
use sycamore::prelude::{view, View};

#[derive(Serialize, Deserialize, Debug)]
pub struct IndexPageProps {
#[perseus::make_rx(IndexPageStateRx)]
pub struct IndexPageState {
pub greeting: String,
}

#[perseus::template(IndexPage)]
#[component(IndexPage<G>)]
pub fn index_page(props: IndexPageProps) -> View<G> {
#[perseus::template_rx(IndexPage)]
pub fn index_page(state: IndexPageStateRx) -> View<G> {
view! {
p {(props.greeting)}
p { (state.greeting.get()) }
a(href = "about", id = "about-link") { "About!" }
}
}

pub fn get_template<G: Html>() -> Template<G> {
Template::new("index")
.build_state_fn(get_build_props)
.build_state_fn(get_build_state)
.template(index_page)
.head(head)
.set_headers_fn(set_headers)
}

#[perseus::head]
pub fn head(_props: IndexPageProps) -> View<SsrNode> {
pub fn head(_props: IndexPageState) -> View<SsrNode> {
view! {
title { "Index Page | Perseus Example – Basic" }
}
}

#[perseus::autoserde(build_state)]
pub async fn get_build_props(
pub async fn get_build_state(
_path: String,
_locale: String,
) -> RenderFnResultWithCause<IndexPageProps> {
Ok(IndexPageProps {
) -> RenderFnResultWithCause<IndexPageState> {
Ok(IndexPageState {
greeting: "Hello World!".to_string(),
})
}

#[perseus::autoserde(set_headers)]
pub fn set_headers(props: Option<IndexPageProps>) -> HeaderMap {
pub fn set_headers(props: Option<IndexPageState>) -> HeaderMap {
let mut map = HeaderMap::new();
map.insert(
HeaderName::from_lowercase(b"x-greeting").unwrap(),
Expand Down
38 changes: 21 additions & 17 deletions examples/fetching/src/index.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
use perseus::{Html, RenderFnResultWithCause, Template};
use serde::{Deserialize, Serialize};
use sycamore::prelude::*;

#[derive(Serialize, Deserialize)]
pub struct IndexProps {
ip: String,
#[perseus::make_rx(IndexPageStateRx)]
pub struct IndexPageState {
server_ip: String,
browser_ip: String,
}

#[perseus::template(IndexPage)]
#[component(IndexPage<G>)]
pub fn index_page(IndexProps { ip }: IndexProps) -> View<G> {
// This will store the message that we get
// Until we've got it, we'll display `fetching...`
let message = Signal::new("fetching...".to_string());

#[perseus::template_rx(IndexPage)]
pub fn index_page(
IndexPageStateRx {
server_ip,
browser_ip,
}: IndexPageStateRx,
) -> View<G> {
// This will only run in the browser
// `reqwasm` wraps browser-specific APIs, so we don't want it running on the server
if G::IS_BROWSER {
// Spawn a `Future` on this thread to fetch the data (`spawn_local` is re-exported from `wasm-bindgen-futures`)
// Don't worry, this doesn't need to be sent to JavaScript for execution
//
// We want to access the `message` `Signal`, so we'll clone it in (and then we need `move` because this has to be `'static`)
perseus::spawn_local(cloned!(message => async move {
perseus::spawn_local(cloned!(browser_ip => async move {
// This interface may seem weird, that's because it wraps the browser's Fetch API
// We request from a local path here because of CORS restrictions (see the book)
let body = reqwasm::http::Request::get("/message")
Expand All @@ -31,13 +31,13 @@ pub fn index_page(IndexProps { ip }: IndexProps) -> View<G> {
.text()
.await
.unwrap();
message.set(body);
browser_ip.set(body);
}));
}

view! {
p { (format!("IP address of the builder was: {}", ip)) }
p { (format!("The message at `/message` is: {}", message.get())) }
p { (format!("IP address of the server was: {}", server_ip.get())) }
p { (format!("The message at `/message` is: {}", browser_ip.get())) }
}
}

Expand All @@ -51,7 +51,7 @@ pub fn get_template<G: Html>() -> Template<G> {
pub async fn get_build_state(
_path: String,
_locale: String,
) -> RenderFnResultWithCause<IndexProps> {
) -> RenderFnResultWithCause<IndexPageState> {
// We'll cache the result with `try_cache_res`, which means we only make the request once, and future builds will use the cached result (speeds up development)
let body = perseus::cache_fallible_res(
"ipify",
Expand All @@ -63,5 +63,9 @@ pub async fn get_build_state(
false,
)
.await?;
Ok(IndexProps { ip: body })
// We'll start with a placeholder for the browser's IP, which will be fetched on the client-side
Ok(IndexPageState {
server_ip: body,
browser_ip: "fetching...".to_string(),
})
}
5 changes: 2 additions & 3 deletions examples/i18n/src/templates/about.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use perseus::{t, Template};
use sycamore::prelude::{component, view, Html, View};
use sycamore::prelude::{view, Html, View};

#[perseus::template(AboutPage)]
#[component(AboutPage<G>)]
#[perseus::template_rx(AboutPage)]
pub fn about_page() -> View<G> {
view! {
p { (t!("about")) }
Expand Down
5 changes: 2 additions & 3 deletions examples/i18n/src/templates/index.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use perseus::{link, t, Template};
use sycamore::prelude::{component, view, Html, View};
use sycamore::prelude::{view, Html, View};

#[perseus::template(IndexPage)]
#[component(IndexPage<G>)]
#[perseus::template_rx(IndexPage)]
pub fn index_page() -> View<G> {
let username = "User";
view! {
Expand Down
20 changes: 9 additions & 11 deletions examples/i18n/src/templates/post.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
use perseus::{link, RenderFnResult, RenderFnResultWithCause, Template};
use serde::{Deserialize, Serialize};
use sycamore::prelude::{component, view, Html, View};
use sycamore::prelude::{view, Html, View};

#[derive(Serialize, Deserialize)]
pub struct PostPageProps {
#[perseus::make_rx(PostPageStateRx)]
pub struct PostPageState {
title: String,
content: String,
}

#[perseus::template(PostPage)]
#[component(PostPage<G>)]
pub fn post_page(props: PostPageProps) -> View<G> {
#[perseus::template_rx(PostPage)]
pub fn post_page(props: PostPageStateRx) -> View<G> {
let title = props.title;
let content = props.content;
view! {
h1 {
(title)
(title.get())
}
p {
(content)
(content.get())
}
a(href = link!("/post")) { "Root post page" }
br()
Expand All @@ -37,15 +35,15 @@ pub fn get_template<G: Html>() -> Template<G> {
pub async fn get_static_props(
path: String,
_locale: String,
) -> RenderFnResultWithCause<PostPageProps> {
) -> RenderFnResultWithCause<PostPageState> {
// This is just an example
let title = urlencoding::decode(&path).unwrap();
let content = format!(
"This is a post entitled '{}'. Its original slug was '{}'.",
title, path
);

Ok(PostPageProps {
Ok(PostPageState {
title: title.to_string(),
content,
}) // This `?` declares the default, that the server is the cause of the error
Expand Down
5 changes: 2 additions & 3 deletions examples/plugins/src/templates/about.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use perseus::Template;
use sycamore::prelude::{component, view, Html, SsrNode, View};
use sycamore::prelude::{view, Html, SsrNode, View};

// This page will actually be replaced entirely by a plugin!
#[perseus::template(AboutPage)]
#[component(AboutPage<G>)]
#[perseus::template_rx(AboutPage)]
pub fn about_page() -> View<G> {
view! {
p { "About." }
Expand Down
5 changes: 2 additions & 3 deletions examples/plugins/src/templates/index.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use perseus::{Html, Template};
use sycamore::prelude::{component, view, SsrNode, View};
use sycamore::prelude::{view, SsrNode, View};

#[perseus::template(IndexPage)]
#[component(IndexPage<G>)]
#[perseus::template_rx(IndexPage)]
pub fn index_page() -> View<G> {
view! {
p { "Hello World!" }
Expand Down
12 changes: 4 additions & 8 deletions examples/tiny/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,11 @@ use perseus::{define_app, ErrorPages, Template};
use sycamore::view;
define_app! {
templates: [
Template::<G>::new("index").template(|_| {
view! {
p { "Hello World!" }
}
Template::<G>::new("index").template(|_| view! {
p { "Hello World!" }
})
],
error_pages: ErrorPages::new(|url, status, err, _| {
view! {
p { (format!("An error with HTTP code {} occurred at '{}': '{}'.", status, url, err)) }
}
error_pages: ErrorPages::new(|url, status, err, _| view! {
p { (format!("An error with HTTP code {} occurred at '{}': '{}'.", status, url, err)) }
})
}

0 comments on commit 1570e5d

Please sign in to comment.