use std::rc::Rc; use dioxus::prelude::*; use futures::join; turf::style_sheet!("src/ui/layouts/conversations.scss"); use crate::ui::components::chat_panel::ChatPanel; use crate::ui::components::conversations::Conversations as ConversationsComponent; use crate::ui::components::wallpaper::Wallpaper; // TODO: Get from SCSS const WIDGET_HEIGHT_RATIO: f64 = 0.95; const ASPECT_RATIO: f64 = 1.0 / 1.618; async fn on_carousel_scroll( parent_div: &Rc, first_div: &Rc, last_div: &Rc, ) { let results = join!( parent_div.get_scroll_offset(), parent_div.get_scroll_size(), last_div.get_client_rect() ); if let (Ok(offset), Ok(size), Ok(last_div_rect)) = results { let left = offset.x; let width = size.width; // The left border of the first div has been exceeded, scrool to the last div. if left <= 0.0 { let _ = last_div.scroll_to(ScrollBehavior::Smooth).await; } // The left border of the last div has been exceeded, scrool to the first div. else { let last_div_width = last_div_rect.size.width; let distance_to_tail = width - left - last_div_width; if distance_to_tail < 1.0 { let first_div = first_div.as_ref(); //.unwrap(); let _ = first_div.scroll_to(ScrollBehavior::Smooth).await; } } } } fn LayoutSmall() -> Element { let mut carousel_div = use_signal(|| None::>); let mut first_div = use_signal(|| None::>); let mut last_div = use_signal(|| None::>); let conversation_panels_nb = 3; let conversation_panels = (0..conversation_panels_nb + 1).map(|i| { let inner = rsx! { div { class: ClassName::CONVERSATIONS_VIEW_SMALL_PANEL_INNER, ChatPanel { name: format!("CHAT #{i}") }, } }; if i == conversation_panels_nb { rsx! { div { class: ClassName::CONVERSATIONS_VIEW_SMALL_PANEL, onmounted: move |cx: Event| last_div.set(Some(cx.data())), {inner} } } } else { rsx! { div { class: ClassName::CONVERSATIONS_VIEW_SMALL_PANEL, {inner} } } } }); rsx! { style { {STYLE_SHEET} }, div { class: ClassName::CONVERSATIONS_VIEW_SMALL, onmounted: move |cx| async move { let data = cx.data(); carousel_div.set(Some(data)); }, onscroll: move |_| { async move { if let (Some(carousel_div), Some(first_div), Some(last_div)) = ( carousel_div.read().as_ref(), first_div.read().as_ref(), last_div.read().as_ref(), ) { on_carousel_scroll(carousel_div, first_div, last_div).await; } } }, div { class: ClassName::CONVERSATIONS_VIEW_HEAD, } div { class: ClassName::CONVERSATIONS_VIEW_SMALL_CONVERSATIONS_PANEL, onmounted: move |cx| async move { let data = cx.data(); let _ = data.as_ref().scroll_to(ScrollBehavior::Smooth).await; first_div.set(Some(data)); }, div { class: ClassName::CONVERSATIONS_VIEW_SMALL_CONVERSATIONS_PANEL_INNER, ConversationsComponent {}, }, }, {conversation_panels} div { class: ClassName::CONVERSATIONS_VIEW_TAIL, } } } } fn LayoutBig() -> Element { let mut carousel_div = use_signal(|| None::>); let mut first_div = use_signal(|| None::>); let mut last_div = use_signal(|| None::>); let conversation_panels_nb = 3; let conversation_panels = (0..conversation_panels_nb + 1).map(|i| { if i == 0 { rsx! { div { class: ClassName::CONVERSATIONS_VIEW_BIG_CONVERSATION_PANELS_PANEL, onmounted: move |cx| async move { let data = cx.data(); let _ = data.as_ref().scroll_to(ScrollBehavior::Smooth).await; first_div.set(Some(data)); }, ChatPanel { name: format!("CHAT #{i}") }, } } } else if i == conversation_panels_nb { rsx! { div { class: ClassName::CONVERSATIONS_VIEW_BIG_CONVERSATION_PANELS_PANEL, onmounted: move |cx: Event| last_div.set(Some(cx.data())), ChatPanel { name: format!("CHAT #{i}") }, } } } else { rsx! { div { class: ClassName::CONVERSATIONS_VIEW_BIG_CONVERSATION_PANELS_PANEL, ChatPanel { name: format!("CHAT #{i}") }, } } } }); rsx! { style { {STYLE_SHEET} }, div { class: ClassName::CONVERSATIONS_VIEW_BIG, div { class: ClassName::CONVERSATIONS_VIEW_BIG_CONVERSATIONS_PANEL, ConversationsComponent {}, }, div { class: ClassName::CONVERSATIONS_VIEW_BIG_CONVERSATION_PANELS, onmounted: move |cx| async move { let data = cx.data(); carousel_div.set(Some(data)); }, onscroll: move |_| { async move { if let (Some(carousel_div), Some(first_div), Some(last_div)) = ( carousel_div.read().as_ref(), first_div.read().as_ref(), last_div.read().as_ref(), ) { on_carousel_scroll(carousel_div, first_div, last_div).await; } } }, div { class: ClassName::CONVERSATIONS_VIEW_HEAD, } {conversation_panels} div { class: ClassName::CONVERSATIONS_VIEW_TAIL, } }, } } } pub fn Conversations() -> Element { let mut view_size = use_signal(|| None::<(f64, f64)>); // TODO: Make the layout reactive (on window resize) let layout = { move || { if let Some((width, height)) = view_size.read().as_ref() { let component_width = height * WIDGET_HEIGHT_RATIO * ASPECT_RATIO; let breakpoint_width = component_width * 2_f64; if *width >= breakpoint_width { return rsx! { LayoutBig {} }; } } rsx! {LayoutSmall {}} } }(); rsx! { style { {STYLE_SHEET} }, Wallpaper { display_version: true }, div { class: ClassName::CONVERSATIONS_VIEW, onmounted: move |cx| { async move { let data = cx.data(); if let Ok(client_rect) = data.get_client_rect().await { view_size.set(Some((client_rect.size.width, client_rect.size.height))); } } }, {layout} } } }