use dioxus::prelude::*;
use matrix_sdk::ruma::events::room::message::RoomMessageEventContent;
use matrix_sdk::ruma::OwnedRoomId;
use crate::components::avatar::Avatar;
use crate::components::modal::Modal;
use crate::state::app_state::AppState;
#[derive(Clone, PartialEq)]
struct ForwardRoomEntry {
room_id: String,
display_name: String,
avatar_url: Option<String>,
}
#[component]
pub fn ForwardDialog(
message_body: String,
on_close: EventHandler<()>,
) -> Element {
let state = use_context::<Signal<AppState>>();
let mut search_query = use_signal(|| String::new());
let mut selected_room_id = use_signal(|| Option::<String>::None);
let mut is_sending = use_signal(|| false);
let mut send_result = use_signal(|| Option::<Result<(), String>>::None);
let query = search_query.read().to_lowercase();
let rooms: Vec<ForwardRoomEntry> = {
let s = state.read();
s.rooms
.values()
.filter(|r| {
if query.is_empty() {
true
} else {
r.display_name.to_lowercase().contains(&query)
}
})
.map(|r| ForwardRoomEntry {
room_id: r.room_id.to_string(),
display_name: r.display_name.clone(),
avatar_url: r.avatar_url.clone(),
})
.collect()
};
let body_for_send = message_body.clone();
let on_forward = move |_| {
let selected = selected_room_id.read().clone();
let Some(room_id_str) = selected else { return };
let body = body_for_send.clone();
is_sending.set(true);
send_result.set(None);
spawn(async move {
let client = { state.read().client.clone() };
let Some(client) = client else {
send_result.set(Some(Err("Not logged in".to_string())));
is_sending.set(false);
return;
};
let room_id: OwnedRoomId = match room_id_str.as_str().try_into() {
Ok(id) => id,
Err(e) => {
send_result.set(Some(Err(format!("Invalid room ID: {e}"))));
is_sending.set(false);
return;
}
};
let Some(room) = client.get_room(&room_id) else {
send_result.set(Some(Err("Room not found".to_string())));
is_sending.set(false);
return;
};
let content = RoomMessageEventContent::text_plain(&body);
match room.send(content).await {
Ok(_) => {
tracing::info!("Forwarded message to {room_id}");
send_result.set(Some(Ok(())));
}
Err(e) => {
send_result.set(Some(Err(format!("Failed to forward: {e}"))));
}
}
is_sending.set(false);
});
};
let has_selection = selected_room_id.read().is_some();
let is_busy = *is_sending.read();
let succeeded = matches!(&*send_result.read(), Some(Ok(())));
let error_msg = match &*send_result.read() {
Some(Err(e)) => Some(e.clone()),
_ => None,
};
let send_btn_opacity = if has_selection && !is_busy { "1" } else { "0.5" };
let send_btn_style = format!(
"padding: 8px 16px; border-radius: 6px; border: none; background: var(--accent-color, #4a9eff); color: white; cursor: pointer; opacity: {};",
send_btn_opacity
);
let send_btn_label = if is_busy { "Forwarding..." } else { "Forward" };
rsx! {
Modal {
title: "Forward Message".to_string(),
on_close: move |_| on_close.call(()),
wide: true,
div {
class: "forward-dialog",
div {
class: "forward-dialog__preview",
p {
class: "forward-dialog__preview-label",
"Message:"
}
div {
class: "forward-dialog__preview-body",
style: "padding: 8px 12px; background: var(--bg-secondary, #1a1a2e); border-radius: 6px; margin-bottom: 12px; font-size: 13px; max-height: 80px; overflow: auto;",
"{message_body}"
}
}
if succeeded {
div {
class: "forward-dialog__success",
style: "padding: 8px 12px; background: var(--success-bg, #1a3a2a); color: var(--success-text, #4caf50); border-radius: 6px; margin-bottom: 12px; text-align: center;",
"Message forwarded successfully!"
}
}
if let Some(ref err) = error_msg {
div {
class: "forward-dialog__error",
style: "padding: 8px 12px; background: var(--error-bg, #3a1a1a); color: var(--error-text, #f44336); border-radius: 6px; margin-bottom: 12px;",
"{err}"
}
}
if !succeeded {
div {
class: "forward-dialog__selector",
input {
class: "forward-dialog__search-input",
style: "width: 100%; padding: 8px 12px; border: 1px solid var(--border-color, #333); border-radius: 6px; background: var(--bg-primary, #0d0d1a); color: var(--text-primary, #e0e0e0); font-size: 14px; margin-bottom: 12px;",
r#type: "text",
placeholder: "Search rooms...",
value: "{search_query}",
oninput: move |evt| search_query.set(evt.value()),
}
div {
class: "forward-dialog__room-list",
style: "max-height: 300px; overflow-y: auto; border: 1px solid var(--border-color, #333); border-radius: 6px;",
if rooms.is_empty() {
p {
style: "padding: 16px; text-align: center; color: var(--text-secondary, #888);",
"No rooms found."
}
}
for entry in rooms.iter() {
{
let rid = entry.room_id.clone();
let name = entry.display_name.clone();
let avatar = entry.avatar_url.clone();
let is_selected = selected_room_id.read().as_ref() == Some(&entry.room_id);
let item_style = if is_selected {
"display: flex; align-items: center; gap: 10px; padding: 8px 12px; cursor: pointer; background: var(--accent-bg, #1a2a4a); border-left: 3px solid var(--accent-color, #4a9eff);"
} else {
"display: flex; align-items: center; gap: 10px; padding: 8px 12px; cursor: pointer; border-left: 3px solid transparent;"
};
rsx! {
div {
class: "forward-dialog__room-item",
style: "{item_style}",
onclick: move |_| {
selected_room_id.set(Some(rid.clone()));
},
Avatar {
name: name.clone(),
url: avatar,
size: 32,
}
span {
style: "font-size: 14px;",
"{name}"
}
}
}
}
}
}
div {
style: "margin-top: 12px; display: flex; justify-content: flex-end; gap: 8px;",
button {
style: "padding: 8px 16px; border-radius: 6px; border: 1px solid var(--border-color, #333); background: transparent; color: var(--text-primary, #e0e0e0); cursor: pointer;",
onclick: move |_| on_close.call(()),
"Cancel"
}
button {
style: "{send_btn_style}",
disabled: !has_selection || is_busy,
onclick: on_forward,
"{send_btn_label}"
}
}
}
}
}
}
}
}