use dioxus::prelude::*;
use crate::state::app_state::AppState;
#[derive(Clone, Debug, PartialEq)]
pub struct RoomWidget {
pub id: String,
pub name: String,
pub widget_type: String,
pub url: String,
pub creator_user_id: String,
pub waiting_for_iframe: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub enum WidgetConnectionStatus {
Connected,
NotConnected,
}
#[component]
pub fn WidgetFrame(
widget: RoomWidget,
on_close: EventHandler<String>,
) -> Element {
let widget_name = widget.name.clone();
let widget_id = widget.id.clone();
let widget_type = widget.widget_type.clone();
let url_for_open = widget.url.clone();
let url_for_display = widget.url.clone();
let url_for_content = widget.url.clone();
let connection_status = if widget.waiting_for_iframe {
WidgetConnectionStatus::NotConnected
} else {
WidgetConnectionStatus::Connected
};
let status_text = match connection_status {
WidgetConnectionStatus::Connected => "Connected",
WidgetConnectionStatus::NotConnected => "Not connected",
};
let status_class = match connection_status {
WidgetConnectionStatus::Connected => "widget-frame__status widget-frame__status--connected",
WidgetConnectionStatus::NotConnected => "widget-frame__status widget-frame__status--disconnected",
};
let close_id = widget_id.clone();
rsx! {
div {
class: "widget-frame",
div {
class: "widget-frame__header",
div {
class: "widget-frame__info",
span { class: "widget-frame__name", "{widget_name}" }
span { class: "widget-frame__url-badge", "{url_for_display}" }
}
span {
class: "{status_class}",
"{status_text}"
}
div {
class: "widget-frame__actions",
a {
class: "widget-frame__action-btn",
title: "Open in browser",
href: "{url_for_open}",
target: "_blank",
rel: "noopener noreferrer",
"Open in browser"
}
button {
class: "widget-frame__action-btn",
title: "Reload widget",
onclick: move |_| {
tracing::info!("Reload requested for widget: {}", widget_id);
},
"Reload"
}
button {
class: "widget-frame__action-btn widget-frame__action-btn--close",
title: "Close widget",
onclick: move |_| {
on_close.call(close_id.clone());
},
"Close"
}
}
}
div {
class: "widget-frame__content",
div {
class: "widget-frame__placeholder",
div {
class: "widget-frame__icon",
"{widget_type}"
}
h3 {
class: "widget-frame__title",
"{widget_name}"
}
p {
class: "widget-frame__description",
"This widget is running in an external browser window. Use the \"Open in browser\" button above to interact with it."
}
div {
class: "widget-frame__url-display",
code { "{url_for_content}" }
}
}
}
}
}
}
#[component]
pub fn WidgetLayout(room_id: String) -> Element {
let state = use_context::<Signal<AppState>>();
let mut widgets = use_signal(Vec::<RoomWidget>::new);
let mut loading = use_signal(|| true);
let rid = room_id.clone();
use_effect(move || {
let rid = rid.clone();
spawn(async move {
let client = { state.read().client.clone() };
if let Some(client) = client {
if let Ok(room_id) = matrix_sdk::ruma::OwnedRoomId::try_from(rid.as_str()) {
if let Some(_room) = client.get_room(&room_id) {
tracing::debug!("Checking room {room_id} for widgets");
}
}
}
loading.set(false);
});
});
if widgets.read().is_empty() {
return rsx! {};
}
let on_close_widget = move |widget_id: String| {
let mut current = widgets.write();
current.retain(|w| w.id != widget_id);
tracing::info!("Widget closed: {}", widget_id);
};
rsx! {
div {
class: "widget-layout",
for widget in widgets.read().iter() {
{
let w = widget.clone();
let w_id = w.id.clone();
rsx! {
WidgetFrame {
key: "{w_id}",
widget: w,
on_close: on_close_widget,
}
}
}
}
}
}
}