@@ -266,10 +311,13 @@ pub fn MessageRow(
})}
{/* Error */}
- {message.ai_error.as_ref().map(|e| view! {
-
+ {message.ai_error.as_ref().map(|e| {
+ let error_str = e.clone();
+ view! {
+
+ }
})}
{/* Re-analyze */}
@@ -316,11 +364,14 @@ pub fn MessageCard(
-
{&first.username}
- {loc_label.as_ref().map(|l| view! {
-
- "#" " " {l}
-
+
{first.username.clone()}
+ {loc_label.as_ref().map(|l| {
+ let label_str = l.clone();
+ view! {
+
+ "#" " " {label_str}
+
+ }
})}
{time_ago(first.created_at)}
diff --git a/services/frontend-leptos/frontend/src/features/messages/components/message_feed.rs b/services/frontend-leptos/frontend/src/features/messages/components/message_feed.rs
index cced974..6f1758b 100644
--- a/services/frontend-leptos/frontend/src/features/messages/components/message_feed.rs
+++ b/services/frontend-leptos/frontend/src/features/messages/components/message_feed.rs
@@ -43,6 +43,7 @@ pub fn MessageFeed(
create_effect(move |_| {
let _ = intersecting.get(); // track signal
if let Some(node) = sentinel_ref.get() {
+ let on_load_more = on_load_more.clone();
let cb = Closure::)>::new(move |entries: Vec| {
for entry in entries {
if let Some(entry) = entry.dyn_ref::() {
diff --git a/services/frontend-leptos/frontend/src/features/messages/mod.rs b/services/frontend-leptos/frontend/src/features/messages/mod.rs
index 07a724a..5af8065 100644
--- a/services/frontend-leptos/frontend/src/features/messages/mod.rs
+++ b/services/frontend-leptos/frontend/src/features/messages/mod.rs
@@ -53,10 +53,10 @@ pub fn MessagesPanel() -> impl IntoView {
}).collect()
});
- // Search handler
- let handle_search = {
+ // Search handler - takes any event type and triggers the search
+ let do_search = {
let q = search_query;
- move |_| {
+ move || {
let query = q.get();
if query.trim().is_empty() {
set_show_search.set(false);
@@ -79,6 +79,9 @@ pub fn MessagesPanel() -> impl IntoView {
});
}
};
+ // Separate closures for different event types so on:click/on:keydown type-check
+ let handle_search_click = move |_: web_sys::MouseEvent| do_search();
+ let handle_search_keydown = move |_: web_sys::KeyboardEvent| do_search();
// Clear search
let clear_search = move |_| {
@@ -141,13 +144,22 @@ pub fn MessagesPanel() -> impl IntoView {
create_effect(move |_| {
if let Some(config) = use_context::() {
if let Some(ref guild_id) = config.monitor_guild_id {
- state.fetch_messages(guild_id.clone());
+ (state.fetch_messages)(guild_id.clone());
}
}
});
// ─── View ────────────────────────────────────────────────
- let (total, clean, flagged, error, pending, deleted, edited) = move || stats.get();
+ let get_stats = move || stats.get();
+ let (total, clean, flagged, error, pending, deleted, edited) = (
+ move || get_stats().0,
+ move || get_stats().1,
+ move || get_stats().2,
+ move || get_stats().3,
+ move || get_stats().4,
+ move || get_stats().5,
+ move || get_stats().6,
+ );
view! {
@@ -190,15 +202,15 @@ pub fn MessagesPanel() -> impl IntoView {
prop:value=search_query
on:input=move |ev| set_search_query.set(event_target_value(&ev))
on:keydown=move |ev| {
- if ev.key() == "Enter" { handle_search(()); }
+ if ev.key() == "Enter" { handle_search_keydown(ev); }
}
- disabled=is_searching.get()
+ disabled=move || is_searching.get()
/>
{move || if is_searching.get() { "Searching..." } else { "Search" }}
@@ -211,7 +223,7 @@ pub fn MessagesPanel() -> impl IntoView {
{/* Rotate CCW icon as SVN */}
@@ -271,18 +283,22 @@ pub fn MessagesPanel() -> impl IntoView {
- )
+ {
+ let load_more_cb = state.load_more.clone();
+ let empty_text: &'static str = if show_search.get() { "No messages found matching your search." } else { "No captures yet." };
+ let has_more = if show_search.get() { false } else { state.has_more.get() };
+ view! {
+
+ on_reanalyze=state.reanalyze.clone()
+ />
}
- on_reanalyze=state.reanalyze.clone()
- />
+ }
diff --git a/services/frontend-leptos/shared-types/src/message.rs b/services/frontend-leptos/shared-types/src/message.rs
index 5f156d4..1d9e696 100644
--- a/services/frontend-leptos/shared-types/src/message.rs
+++ b/services/frontend-leptos/shared-types/src/message.rs
@@ -34,7 +34,7 @@ pub enum AiRecommendedAction {
}
// ── Message Metadata ──────────────────────────────────────
-#[derive(Debug, Clone, Serialize, Deserialize, Default)]
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct MessageMetadata {
#[serde(skip_serializing_if = "Option::is_none")]
pub stickers: Option>,
@@ -46,7 +46,7 @@ pub struct MessageMetadata {
pub channel: Option,
}
-#[derive(Debug, Clone, Serialize, Deserialize)]
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct StickerInfo {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option,
@@ -54,7 +54,7 @@ pub struct StickerInfo {
pub url: Option,
}
-#[derive(Debug, Clone, Serialize, Deserialize)]
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AttachmentRef {
pub name: String,
pub url: String,
@@ -63,7 +63,7 @@ pub struct AttachmentRef {
pub content_type: Option,
}
-#[derive(Debug, Clone, Serialize, Deserialize)]
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct EmbedInfo {
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option,
@@ -73,7 +73,7 @@ pub struct EmbedInfo {
pub thumbnail: Option,
}
-#[derive(Debug, Clone, Serialize, Deserialize)]
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct EmbedMedia {
pub url: String,
#[serde(skip_serializing_if = "Option::is_none")]
@@ -82,7 +82,7 @@ pub struct EmbedMedia {
pub height: Option,
}
-#[derive(Debug, Clone, Serialize, Deserialize)]
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ChannelRef {
pub channel_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
@@ -94,7 +94,7 @@ pub struct ChannelRef {
}
// ── Message Record ────────────────────────────────────────
-#[derive(Debug, Clone, Serialize, Deserialize)]
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct MessageRecord {
pub id: String,
pub guild_id: String,
@@ -140,7 +140,7 @@ pub struct MessageRecord {
}
// ── Pagination ────────────────────────────────────────────
-#[derive(Debug, Clone, Serialize, Deserialize)]
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PageResult {
pub data: Vec,
#[serde(rename = "nextCursor")]
@@ -148,7 +148,7 @@ pub struct PageResult {
}
// ── Attachment ────────────────────────────────────────────
-#[derive(Debug, Clone, Serialize, Deserialize)]
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AttachmentRecord {
pub id: String,
pub message_id: String,