new hashtag filter option
All checks were successful
Android Build Final Fixed / build-android (push) Successful in 7m46s

This commit is contained in:
2026-01-31 13:06:47 +01:00
parent e355c1014b
commit 1e1614b96d
4 changed files with 186 additions and 31 deletions

View File

@@ -7,8 +7,8 @@ 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;
#[wasm_bindgen(js_namespace = ["window", "__TAURI__", "core"], catch)]
async fn invoke(cmd: &str, args: JsValue) -> Result<JsValue, JsValue>;
}
// Wir definieren Post lokal, damit Yew weiß, wie die JSON-Daten vom Backend aussehen
@@ -24,13 +24,17 @@ pub struct Post {
pub fn home() -> Html {
let posts = use_state(|| None::<Vec<Post>>);
let error = use_state(|| false);
let hashtag_input = use_state(|| String::new());
let active_tags = use_state(Vec::<String>::new);
let load_posts = {
let posts = posts.clone();
let error = error.clone();
let active_tags = active_tags.clone();
Callback::from(move |_e: MouseEvent| {
let posts = posts.clone();
let error = error.clone();
let tags = (*active_tags).clone();
// UI zurücksetzen & Scrollen
posts.set(None);
@@ -43,14 +47,25 @@ pub fn home() -> Html {
}
spawn_local(async move {
// Daten vom Rust-Backend anfordern
let res = invoke("fetch_nostr_posts", JsValue::NULL).await;
#[derive(Serialize)]
struct HashtagArgs {
hashtags: Vec<String>,
}
let args = serde_wasm_bindgen::to_value(&HashtagArgs { hashtags: tags }).unwrap();
// 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);
// Daten vom Rust-Backend anfordern
match invoke("fetch_nostr_posts", args).await {
Ok(res) => {
// 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);
}
}
Err(_) => {
error.set(true);
}
}
});
})
@@ -68,10 +83,73 @@ pub fn home() -> Html {
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 class="header-main">
<h1 class="feed-title">{"✨ Entdecken"}</h1>
<button class="reload-btn" onclick={load_posts.clone()}>
{"🔄"}
</button>
</div>
</div>
<div class="hashtag-section">
<div class="hashtag-input-row">
<input
type="text"
class="hashtag-input"
placeholder="Tags hinzufügen (z.B. bitcoin, rust)..."
value={(*hashtag_input).clone()}
oninput={
let hashtag_input = hashtag_input.clone();
move |e: InputEvent| {
let target: web_sys::HtmlInputElement = e.target_unchecked_into();
hashtag_input.set(target.value());
}
}
onkeydown={
let hashtag_input = hashtag_input.clone();
let active_tags = active_tags.clone();
let load_posts = load_posts.clone();
move |e: KeyboardEvent| {
if e.key() == "Enter" {
let val = (*hashtag_input).trim().to_string();
if !val.is_empty() {
let mut tags = (*active_tags).clone();
// Mehrere Tags per Komma trennen
for t in val.split(',').map(|s| s.trim()).filter(|s| !s.is_empty()) {
if !tags.contains(&t.to_string()) {
tags.push(t.to_string());
}
}
active_tags.set(tags);
hashtag_input.set(String::new());
load_posts.emit(MouseEvent::new("click").unwrap());
}
}
}
}
/>
</div>
if !active_tags.is_empty() {
<div class="tag-chips">
{ for active_tags.iter().map(|t| {
let t_clone = t.clone();
let active_tags = active_tags.clone();
let load_posts = load_posts.clone();
let remove = move |_| {
let mut tags = (*active_tags).clone();
tags.retain(|x| x != &t_clone);
active_tags.set(tags);
load_posts.emit(MouseEvent::new("click").unwrap());
};
html! {
<span class="tag-chip" onclick={remove}>
{ format!("#{}", t) }
<span class="tag-remove">{"×"}</span>
</span>
}
}) }
</div>
}
</div>
{