use leptos::prelude::*; use leptos::html::Div; use leptos::wasm_bindgen::JsCast; use web_sys::HtmlElement; #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq)] pub struct Project { pub id: usize, pub title: String, pub description: String, pub image_url: String, pub download_url: String, pub code_url: String, } fn get_all_projects() -> Vec { vec![ Project { id: 1, title: "Malxte.de Rust Website".into(), description: "Eine voll in Rust mit Yew Programmierte Website mit allen Links und allem über mich.".into(), image_url: "/assets/malxte_de.png".into(), download_url: "https://github.com/.../releases".into(), // Dein Link code_url: "https://gitea.malxte.de/Bytemalte/malxte_de.git".into(), // Dein Code Link }, Project { id: 2, title: "Bytemalte.de Community Website".into(), description: "Die Webseite auf der du dich gerade befindest, gebaut mit Leptos für die Bytemalte Community.".into(), image_url: "/assets/bytemalte_de.png".into(), download_url: "#".into(), code_url: "https://gitea.malxte.de/Bytemalte/bytemalte_de".into(), }, Project { id: 3, title: "Logo Design".into(), description: "Das offizielle Bytemalte Logo. (Nicht frei verwendbar!)".into(), image_url: "/assets/Logo.jpg".into(), // JPG geht natürlich auch download_url: "/assets/Logo.jpg".into(), // Man kann Bilder auch direkt verlinken code_url: "#".into(), }, ] } #[component] pub fn HomePage() -> impl IntoView { let (projects, set_projects) = signal(Vec::::new()); let (page, set_page) = signal(1); let (loading, set_loading) = signal(false); let (all_loaded, set_all_loaded) = signal(false); let items_per_page = 3; // Wie viele Projekte pro Scroll-Schub geladen werden let sentinel = NodeRef::
::new(); let fetch_projects = move |p: usize| { if all_loaded.get_untracked() { return; } set_loading.set(true); // Simuliert eine kurze Ladezeit für das Feeling set_timeout(move || { let all_my_projects = get_all_projects(); let start = (p - 1) * items_per_page; let end = (start + items_per_page).min(all_my_projects.len()); if start >= all_my_projects.len() { set_all_loaded.set(true); set_loading.set(false); return; } let slice = &all_my_projects[start..end]; set_projects.update(|projs| projs.extend(slice.to_vec())); if end >= all_my_projects.len() { set_all_loaded.set(true); } set_loading.set(false); }, std::time::Duration::from_millis(400)); }; // Initialer Load Effect::new(move |_| { fetch_projects(page.get()); }); // Scroll-Event-Listener Effect::new(move |_| { let w = window(); let w_clone = w.clone(); let handle = gloo_events::EventListener::new(&w, "scroll", move |_| { if all_loaded.get_untracked() || loading.get_untracked() { return; } if let Some(doc) = w_clone.document() { if let Some(el) = doc.document_element().and_then(|e| e.dyn_into::().ok()) { let scroll_y = w_clone.scroll_y().unwrap_or(0.0); let inner_height = w_clone.inner_height().unwrap().as_f64().unwrap_or(0.0); let total_height = el.offset_height() as f64; if scroll_y + inner_height >= total_height - 200.0 { set_page.update(|p| *p += 1); fetch_projects(page.get_untracked()); } } } }); handle.forget(); }); view! {

"Bytemalte."

"Entdecke meine neuesten Projekte - Bytemalte."

p.title.clone()

{p.title}

{p.description}

} } />
{move || { if loading.get() { view! {
}.into_any() } else if all_loaded.get() { view! {

"Das waren alle Projekte ✨"

}.into_any() } else { view! {

"Mehr laden..."

}.into_any() } }}
} }