Home Tab + Tauri backend
This commit is contained in:
38
src/app.rs
Normal file
38
src/app.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use crate::navbar::Navbar;
|
||||
use crate::pages::{chat::Chat, home::Home, news::News};
|
||||
use yew::prelude::*;
|
||||
use yew_router::prelude::*;
|
||||
|
||||
#[derive(Clone, Routable, PartialEq)]
|
||||
pub enum Route {
|
||||
#[at("/")]
|
||||
Home,
|
||||
#[at("/news")]
|
||||
News,
|
||||
#[at("/chat")]
|
||||
Chat,
|
||||
#[not_found]
|
||||
#[at("/404")]
|
||||
NotFound,
|
||||
}
|
||||
|
||||
fn switch(routes: Route) -> Html {
|
||||
match routes {
|
||||
Route::Home => html! { <Home /> },
|
||||
Route::News => html! { <News /> },
|
||||
Route::Chat => html! { <Chat /> },
|
||||
Route::NotFound => html! { <h1 class="status-msg">{ "404 - Not Found" }</h1> },
|
||||
}
|
||||
}
|
||||
|
||||
#[function_component(App)]
|
||||
pub fn app() -> Html {
|
||||
html! {
|
||||
<BrowserRouter>
|
||||
<div class="feed-container">
|
||||
<Switch<Route> render={switch} />
|
||||
</div>
|
||||
<Navbar /> // Die Navbar schwebt dank 'fixed' über dem Container
|
||||
</BrowserRouter>
|
||||
}
|
||||
}
|
||||
10
src/main.rs
Normal file
10
src/main.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
mod app;
|
||||
mod navbar; // Deklariert das Verzeichnis src/navbar/
|
||||
mod pages; // Deklariert das Verzeichnis src/pages/
|
||||
|
||||
use app::App;
|
||||
|
||||
fn main() {
|
||||
console_error_panic_hook::set_once();
|
||||
yew::Renderer::<App>::new().render();
|
||||
}
|
||||
2
src/navbar/mod.rs
Normal file
2
src/navbar/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod navbar;
|
||||
pub use navbar::Navbar;
|
||||
33
src/navbar/navbar.rs
Normal file
33
src/navbar/navbar.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use crate::app::Route;
|
||||
use yew::prelude::*;
|
||||
use yew_router::prelude::*;
|
||||
|
||||
#[function_component(Navbar)]
|
||||
pub fn navbar() -> Html {
|
||||
let route = use_route::<Route>().unwrap_or(Route::Home);
|
||||
|
||||
html! {
|
||||
<nav class="navbar-dock">
|
||||
<Link<Route>
|
||||
to={Route::Home}
|
||||
classes={classes!("nav-link", if route == Route::Home { "active" } else { "" })}
|
||||
>
|
||||
{ "Home" }
|
||||
</Link<Route>>
|
||||
|
||||
<Link<Route>
|
||||
to={Route::News}
|
||||
classes={classes!("nav-link", if route == Route::News { "active" } else { "" })}
|
||||
>
|
||||
{ "News" }
|
||||
</Link<Route>>
|
||||
|
||||
<Link<Route>
|
||||
to={Route::Chat}
|
||||
classes={classes!("nav-link", if route == Route::Chat { "active" } else { "" })}
|
||||
>
|
||||
{ "Chat" }
|
||||
</Link<Route>>
|
||||
</nav>
|
||||
}
|
||||
}
|
||||
10
src/pages/chat.rs
Normal file
10
src/pages/chat.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
use yew::prelude::*;
|
||||
|
||||
#[function_component(Chat)]
|
||||
pub fn chat() -> Html {
|
||||
html! {
|
||||
<div>
|
||||
<h1>{"Chat Page"}</h1>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
116
src/pages/home.rs
Normal file
116
src/pages/home.rs
Normal file
@@ -0,0 +1,116 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen_futures::spawn_local;
|
||||
use web_sys::{window, MouseEvent, ScrollBehavior, ScrollToOptions};
|
||||
use yew::prelude::*;
|
||||
|
||||
// Tauri 'invoke' Funktion deklarieren
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_namespace = ["window", "__TAURI__", "core"])]
|
||||
async fn invoke(cmd: &str, args: JsValue) -> JsValue;
|
||||
}
|
||||
|
||||
// Wir definieren Post lokal, damit Yew weiß, wie die JSON-Daten vom Backend aussehen
|
||||
// In src/pages/home.rs
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
|
||||
pub struct Post {
|
||||
pub content: String,
|
||||
pub author: String,
|
||||
pub created_at: u64,
|
||||
}
|
||||
|
||||
#[function_component(Home)]
|
||||
pub fn home() -> Html {
|
||||
let posts = use_state(|| None::<Vec<Post>>);
|
||||
let error = use_state(|| false);
|
||||
|
||||
let load_posts = {
|
||||
let posts = posts.clone();
|
||||
let error = error.clone();
|
||||
Callback::from(move |_e: MouseEvent| {
|
||||
let posts = posts.clone();
|
||||
let error = error.clone();
|
||||
|
||||
// UI zurücksetzen & Scrollen
|
||||
posts.set(None);
|
||||
error.set(false);
|
||||
if let Some(win) = window() {
|
||||
let options = ScrollToOptions::new();
|
||||
options.set_top(0.0);
|
||||
options.set_behavior(ScrollBehavior::Smooth);
|
||||
let _ = win.scroll_to_with_scroll_to_options(&options);
|
||||
}
|
||||
|
||||
spawn_local(async move {
|
||||
// Daten vom Rust-Backend anfordern
|
||||
let res = invoke("fetch_nostr_posts", JsValue::NULL).await;
|
||||
|
||||
// JSON-Resultat in Vec<Post> umwandeln
|
||||
if let Ok(new_posts) = serde_wasm_bindgen::from_value::<Vec<Post>>(res) {
|
||||
posts.set(Some(new_posts));
|
||||
} else {
|
||||
error.set(true);
|
||||
}
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
// Initiales Laden beim Start
|
||||
{
|
||||
let load_posts = load_posts.clone();
|
||||
use_effect_with((), move |_| {
|
||||
load_posts.emit(MouseEvent::new("click").unwrap());
|
||||
|| ()
|
||||
});
|
||||
}
|
||||
|
||||
html! {
|
||||
<div class="feed-container">
|
||||
<div class="feed-header">
|
||||
<h1 class="feed-title">{"✨ Entdecken"}</h1>
|
||||
<button class="reload-btn" onclick={load_posts.clone()}>
|
||||
{"🔄"}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{
|
||||
if *error {
|
||||
html! { <p class="status-msg">{"Fehler beim Laden der Posts."}</p> }
|
||||
} else if let Some(ref posts_list) = *posts {
|
||||
html! {
|
||||
<div class="posts-list">
|
||||
{ for posts_list.iter().map(|p| html! { <PostCard post={p.clone()} /> }) }
|
||||
<div class="footer-action">
|
||||
<button class="reload-btn-large" onclick={load_posts}>
|
||||
{"Nach oben & Neu laden"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
} else {
|
||||
html! { <p class="status-msg">{"Verbinde mit Nostr über Tauri..."}</p> }
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct PostProps {
|
||||
pub post: Post,
|
||||
}
|
||||
|
||||
#[function_component(PostCard)]
|
||||
pub fn post_card(props: &PostProps) -> Html {
|
||||
let post = &props.post;
|
||||
html! {
|
||||
<div class="post-card">
|
||||
<div class="post-header">
|
||||
<span class="post-author">{ format!("{:.8}...", post.author) }</span>
|
||||
<span class="post-date">{ post.created_at }</span>
|
||||
</div>
|
||||
<div class="post-content">{ &post.content }</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
3
src/pages/mod.rs
Normal file
3
src/pages/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod chat;
|
||||
pub mod home;
|
||||
pub mod news;
|
||||
10
src/pages/news.rs
Normal file
10
src/pages/news.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
use yew::prelude::*;
|
||||
|
||||
#[function_component(News)]
|
||||
pub fn news() -> Html {
|
||||
html! {
|
||||
<div>
|
||||
<h1>{"News Page"}</h1>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user