rss bug fixes

This commit is contained in:
Malte Schröder
2025-12-18 15:38:37 +01:00
parent 0bd434e008
commit b7c28f3c4c
2 changed files with 67 additions and 70 deletions

View File

@@ -13,7 +13,6 @@ pub struct Project {
pub code_url: String, pub code_url: String,
} }
// Deine vordefinierte Liste
fn get_all_projects() -> Vec<Project> { fn get_all_projects() -> Vec<Project> {
vec![ vec![
Project { Project {
@@ -27,12 +26,20 @@ fn get_all_projects() -> Vec<Project> {
Project { Project {
id: 2, id: 2,
title: "Anderes Projekt".into(), title: "Anderes Projekt".into(),
description: "Beschreibung deines zweiten Projekts hier.".into(), description: "Ein weiteres Beispielprojekt mit einer etwas längeren Beschreibung, um das Grid zu testen.".into(),
image_url: "/assets/bytemalte_de.png".into(), image_url: "/assets/bytemalte_de.png".into(),
download_url: "#".into(), download_url: "#".into(),
code_url: "#".into(), code_url: "#".into(),
}, },
// Füge hier weitere hinzu... Project {
id: 3,
title: "Projekt Drei".into(),
description: "Hier ist ein drittes Projekt, das ebenfalls in der Liste erscheint.".into(),
image_url: "/assets/Logo.jpg".into(),
download_url: "#".into(),
code_url: "#".into(),
},
// Füge hier weitere Projekte hinzu...
] ]
} }
@@ -41,14 +48,13 @@ pub fn HomePage() -> impl IntoView {
let items_per_page = 3; let items_per_page = 3;
let all_data = get_all_projects(); let all_data = get_all_projects();
// WICHTIG: Wir laden die erste Seite direkt hier für das SSR! // Initialer Zustand für SSR & Client
let initial_projects = all_data.iter().take(items_per_page).cloned().collect::<Vec<_>>(); let initial_projects = all_data.iter().take(items_per_page).cloned().collect::<Vec<_>>();
let has_more_initially = all_data.len() > items_per_page;
let (projects, set_projects) = signal(initial_projects); let (projects, set_projects) = signal(initial_projects);
let (page, set_page) = signal(1); let (page, set_page) = signal(1);
let (loading, set_loading) = signal(false); let (loading, set_loading) = signal(false);
let (all_loaded, set_all_loaded) = signal(!has_more_initially); let (all_loaded, set_all_loaded) = signal(all_data.len() <= items_per_page);
let sentinel = NodeRef::<Div>::new(); let sentinel = NodeRef::<Div>::new();
@@ -66,7 +72,7 @@ pub fn HomePage() -> impl IntoView {
return; return;
} }
// Simuliere Netzwerk-Delay für den Spinner // Kurze Verzögerung für den Lade-Effekt
set_timeout(move || { set_timeout(move || {
let slice = all_my_projects[start..end].to_vec(); let slice = all_my_projects[start..end].to_vec();
set_projects.update(|projs| projs.extend(slice)); set_projects.update(|projs| projs.extend(slice));
@@ -75,10 +81,9 @@ pub fn HomePage() -> impl IntoView {
set_all_loaded.set(true); set_all_loaded.set(true);
} }
set_loading.set(false); set_loading.set(false);
}, std::time::Duration::from_millis(300)); }, std::time::Duration::from_millis(200));
}; };
// Scroll-Event-Listener (Nur im Browser aktiv)
Effect::new(move |_| { Effect::new(move |_| {
let w = window(); let w = window();
let w_clone = w.clone(); let w_clone = w.clone();
@@ -92,10 +97,10 @@ pub fn HomePage() -> impl IntoView {
let inner_height = w_clone.inner_height().unwrap().as_f64().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; let total_height = el.offset_height() as f64;
if scroll_y + inner_height >= total_height - 250.0 { if scroll_y + inner_height >= total_height - 300.0 {
let next_page = page.get_untracked(); let current_page = page.get_untracked();
set_page.set(next_page + 1); set_page.set(current_page + 1);
fetch_projects(next_page); fetch_projects(current_page);
} }
} }
} }
@@ -110,7 +115,8 @@ pub fn HomePage() -> impl IntoView {
<p>"Entdecke meine neuesten Projekte."</p> <p>"Entdecke meine neuesten Projekte."</p>
</header> </header>
<main class="projects-grid"> // Der Container MUSS die Klasse projects-grid haben
<div class="projects-grid">
<For <For
each=move || projects.get() each=move || projects.get()
key=|p| p.id key=|p| p.id
@@ -124,15 +130,15 @@ pub fn HomePage() -> impl IntoView {
<h3>{p.title}</h3> <h3>{p.title}</h3>
<p>{p.description}</p> <p>{p.description}</p>
<div class="project-links"> <div class="project-links">
<a href=p.download_url target="_blank" class="btn-download">"Download"</a> <a href=p.download_url target="_blank" rel="noopener noreferrer" class="btn-download">"Download"</a>
<a href=p.code_url target="_blank" class="btn-code">"Source Code"</a> <a href=p.code_url target="_blank" rel="noopener noreferrer" class="btn-code">"Source Code"</a>
</div> </div>
</div> </div>
</div> </div>
} }
} }
/> />
</main> </div>
<div node_ref=sentinel class="loading-trigger"> <div node_ref=sentinel class="loading-trigger">
{move || match (loading.get(), all_loaded.get()) { {move || match (loading.get(), all_loaded.get()) {

View File

@@ -1,66 +1,63 @@
.home-container { .home-container {
max-width: 1200px; max-width: 1100px;
margin: 0 auto; margin: 0 auto;
padding: 0 1.5rem; padding: 0 1rem;
} }
.projects-grid { .projects-grid {
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); // Sorgt dafür, dass die Karten immer gleich breit sind
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 2rem; gap: 2rem;
margin-top: 2rem; margin: 2rem 0;
// Verhindert Layout-Shifts
align-items: stretch;
} }
.project-card { .project-card {
background: var(--white); background: var(--white);
border-radius: 1.25rem; border-radius: 1rem;
overflow: hidden;
border: 1px solid var(--border); border: 1px solid var(--border);
transition: transform 0.3s ease, box-shadow 0.3s ease; overflow: hidden;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
transition: transform 0.2s ease, box-shadow 0.2s ease;
height: 100%; // Wichtig für einheitliche Höhe im Grid
&:hover { &:hover {
transform: translateY(-8px); transform: translateY(-5px);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1); box-shadow: 0 10px 20px rgba(0,0,0,0.05);
} }
.project-image { .project-image {
width: 100%; height: 180px;
height: 200px; background: #f1f5f9;
background: #eef2ff;
overflow: hidden;
img { img {
width: 100%; width: 100%;
height: 100%; height: 100%;
object-fit: cover; object-fit: cover;
transition: transform 0.5s ease; display: block;
} }
} }
&:hover .project-image img {
transform: scale(1.05);
}
.project-content { .project-content {
padding: 1.5rem; padding: 1.25rem;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: 1; flex-grow: 1;
h3 { h3 {
margin: 0 0 0.75rem 0; margin: 0 0 0.5rem 0;
font-size: 1.25rem; font-size: 1.25rem;
color: var(--text-main); color: var(--text-main);
} }
p { p {
margin: 0 0 1.5rem 0;
color: var(--text-light);
font-size: 0.95rem; font-size: 0.95rem;
line-height: 1.6; color: var(--text-light);
flex: 1; line-height: 1.5;
margin-bottom: 1.5rem;
flex-grow: 1; // Drückt die Buttons nach unten
} }
} }
@@ -70,13 +67,14 @@
gap: 0.75rem; gap: 0.75rem;
a { a {
display: inline-block;
text-decoration: none; text-decoration: none;
padding: 0.6rem; padding: 0.6rem;
border-radius: 0.75rem; border-radius: 0.5rem;
font-size: 0.85rem; font-size: 0.85rem;
font-weight: 600; font-weight: 600;
text-align: center; text-align: center;
transition: all 0.2s; transition: background 0.2s;
} }
.btn-download { .btn-download {
@@ -86,42 +84,35 @@
} }
.btn-code { .btn-code {
background: #f1f5f9; background: var(--bg);
color: var(--text-main); color: var(--text-main);
&:hover { background: #e2e8f0; } border: 1px solid var(--border);
&:hover { background: var(--border); }
} }
} }
} }
.loading-trigger { .loading-trigger {
display: flex; text-align: center;
justify-content: center; padding: 3rem 0;
align-items: center; min-height: 100px;
min-height: 150px; }
padding-bottom: 50px;
color: var(--text-light);
p { font-size: 0.9rem; opacity: 0.7; } .end-msg {
color: var(--text-light);
font-size: 0.9rem;
border-top: 1px solid var(--border);
padding-top: 2rem;
} }
.spinner { .spinner {
width: 40px; width: 30px;
height: 40px; height: 30px;
border: 3px solid rgba(99, 102, 241, 0.1); border: 3px solid var(--border);
border-top-color: var(--primary); border-top-color: var(--primary);
border-radius: 50%; border-radius: 50%;
margin: 0 auto;
animation: spin 0.8s linear infinite; animation: spin 0.8s linear infinite;
} }
@keyframes spin { @keyframes spin { to { transform: rotate(360deg); } }
to { transform: rotate(360deg); }
}
@media (max-width: 640px) {
.projects-grid {
grid-template-columns: 1fr;
}
.home-hero h1 {
font-size: 2.5rem;
}
}