Fix braindump display and switching issues
- Add echo API endpoint for Echo component
- Export Echo component from ui crate to fix compilation errors
- Fix notes not showing after save by adding refresh_notes callback that updates the notes resource
- Fix note content not updating when switching notes by using reactive signal for note_id which triggers note resource reload on prop changes
💘 Generated with Crush
This commit is contained in:
@@ -113,3 +113,8 @@ pub async fn toggle_pin_note(id: String) -> Result<(), ServerFnError> {
|
|||||||
Err(ServerFnError::new("Note not found".to_string()))
|
Err(ServerFnError::new("Note not found".to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[post("/api/echo")]
|
||||||
|
pub async fn echo(message: String) -> Result<String, ServerFnError> {
|
||||||
|
Ok(format!("Echo: {}", message))
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
//! This crate contains all shared UI for the workspace.
|
//! This is the UI package for the braindump application.
|
||||||
|
|
||||||
mod hero;
|
mod hero;
|
||||||
pub use hero::Hero;
|
pub use hero::Hero;
|
||||||
|
|
||||||
mod navbar;
|
mod navbar;
|
||||||
pub use navbar::Navbar;
|
pub use navbar::Navbar;
|
||||||
|
|
||||||
|
mod echo;
|
||||||
|
pub use echo::Echo;
|
||||||
|
|||||||
@@ -13,6 +13,17 @@ fn BraindumpLayout() -> Element {
|
|||||||
let notes = use_resource(|| async move {
|
let notes = use_resource(|| async move {
|
||||||
api::list_notes().await.unwrap_or_default()
|
api::list_notes().await.unwrap_or_default()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let refresh_notes = {
|
||||||
|
let mut notes_ref = notes.clone();
|
||||||
|
move || {
|
||||||
|
spawn(async move {
|
||||||
|
if let Ok(new_notes) = api::list_notes().await {
|
||||||
|
notes_ref.set(Some(new_notes));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let mut selected_note_id = use_signal(|| None::<String>);
|
let mut selected_note_id = use_signal(|| None::<String>);
|
||||||
let search_query = use_signal(|| String::new());
|
let search_query = use_signal(|| String::new());
|
||||||
@@ -40,6 +51,11 @@ fn BraindumpLayout() -> Element {
|
|||||||
on_go_back: move |_| {
|
on_go_back: move |_| {
|
||||||
selected_note_id.set(None);
|
selected_note_id.set(None);
|
||||||
},
|
},
|
||||||
|
on_save: refresh_notes,
|
||||||
|
on_delete: move |_| {
|
||||||
|
selected_note_id.set(None);
|
||||||
|
refresh_notes();
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => rsx! {
|
None => rsx! {
|
||||||
@@ -52,6 +68,7 @@ fn BraindumpLayout() -> Element {
|
|||||||
vec![]
|
vec![]
|
||||||
).await {
|
).await {
|
||||||
selected_note_id.set(Some(id));
|
selected_note_id.set(Some(id));
|
||||||
|
refresh_notes();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -158,7 +175,6 @@ fn SidebarContent(
|
|||||||
div { class: "tags-section",
|
div { class: "tags-section",
|
||||||
div { class: "tags-label", "Tags" }
|
div { class: "tags-label", "Tags" }
|
||||||
div { class: "tags-list",
|
div { class: "tags-list",
|
||||||
// Tag filtering placeholder
|
|
||||||
button { class: "tag", "Work" }
|
button { class: "tag", "Work" }
|
||||||
button { class: "tag", "Personal" }
|
button { class: "tag", "Personal" }
|
||||||
button { class: "tag", "Ideas" }
|
button { class: "tag", "Ideas" }
|
||||||
@@ -243,10 +259,16 @@ fn EmptyState(on_new_note: EventHandler<MouseEvent>) -> Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
fn NoteEditor(note_id: String, on_go_back: EventHandler<MouseEvent>) -> Element {
|
fn NoteEditor(
|
||||||
let note_id_clone_for_resource = note_id.clone();
|
note_id: String,
|
||||||
|
on_go_back: Callback<MouseEvent>,
|
||||||
|
on_save: Callback<()>,
|
||||||
|
on_delete: Callback<()>,
|
||||||
|
) -> Element {
|
||||||
|
let mut note_id_signal = use_signal(|| note_id.clone());
|
||||||
|
|
||||||
let note = use_resource(move || {
|
let note = use_resource(move || {
|
||||||
let id = note_id_clone_for_resource.clone();
|
let id = note_id_signal().clone();
|
||||||
async move {
|
async move {
|
||||||
api::get_note(id).await.ok()
|
api::get_note(id).await.ok()
|
||||||
}
|
}
|
||||||
@@ -257,6 +279,10 @@ fn NoteEditor(note_id: String, on_go_back: EventHandler<MouseEvent>) -> Element
|
|||||||
let mut tags = use_signal(|| String::new());
|
let mut tags = use_signal(|| String::new());
|
||||||
let mut is_pinned = use_signal(|| false);
|
let mut is_pinned = use_signal(|| false);
|
||||||
|
|
||||||
|
use_effect(move || {
|
||||||
|
note_id_signal.set(note_id.clone());
|
||||||
|
});
|
||||||
|
|
||||||
use_effect(move || {
|
use_effect(move || {
|
||||||
let note_val = note.read_unchecked();
|
let note_val = note.read_unchecked();
|
||||||
if let Some(Some(n)) = note_val.as_ref() {
|
if let Some(Some(n)) = note_val.as_ref() {
|
||||||
@@ -267,9 +293,8 @@ fn NoteEditor(note_id: String, on_go_back: EventHandler<MouseEvent>) -> Element
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let note_id_for_save = note_id.clone();
|
|
||||||
let save_note = move |_: MouseEvent| {
|
let save_note = move |_: MouseEvent| {
|
||||||
let id = note_id_for_save.clone();
|
let id = note_id_signal().clone();
|
||||||
let title_val = title();
|
let title_val = title();
|
||||||
let content_val = content();
|
let content_val = content();
|
||||||
let tags_input = tags();
|
let tags_input = tags();
|
||||||
@@ -282,24 +307,25 @@ fn NoteEditor(note_id: String, on_go_back: EventHandler<MouseEvent>) -> Element
|
|||||||
|
|
||||||
spawn(async move {
|
spawn(async move {
|
||||||
let _ = api::update_note(id, title_val, content_val, tags_list, pinned).await;
|
let _ = api::update_note(id, title_val, content_val, tags_list, pinned).await;
|
||||||
|
on_save(());
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let note_id_for_delete = note_id.clone();
|
|
||||||
let delete_note = move |_: MouseEvent| {
|
let delete_note = move |_: MouseEvent| {
|
||||||
let id = note_id_for_delete.clone();
|
let id = note_id_signal().clone();
|
||||||
spawn(async move {
|
spawn(async move {
|
||||||
let _ = api::delete_note(id).await;
|
let _ = api::delete_note(id).await;
|
||||||
|
on_delete(());
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let note_id_for_toggle = note_id.clone();
|
|
||||||
let toggle_pin = move |_: MouseEvent| {
|
let toggle_pin = move |_: MouseEvent| {
|
||||||
let id = note_id_for_toggle.clone();
|
let id = note_id_signal().clone();
|
||||||
let current_pin = is_pinned();
|
let current_pin = is_pinned();
|
||||||
spawn(async move {
|
spawn(async move {
|
||||||
let _ = api::toggle_pin_note(id).await;
|
let _ = api::toggle_pin_note(id).await;
|
||||||
is_pinned.set(!current_pin);
|
is_pinned.set(!current_pin);
|
||||||
|
on_save(());
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -308,7 +334,7 @@ fn NoteEditor(note_id: String, on_go_back: EventHandler<MouseEvent>) -> Element
|
|||||||
div { class: "editor-header",
|
div { class: "editor-header",
|
||||||
button {
|
button {
|
||||||
class: "back-button",
|
class: "back-button",
|
||||||
onclick: move |evt| on_go_back.call(evt),
|
onclick: on_go_back,
|
||||||
"← Back"
|
"← Back"
|
||||||
}
|
}
|
||||||
div { class: "editor-actions",
|
div { class: "editor-actions",
|
||||||
|
|||||||
Reference in New Issue
Block a user