summaryrefslogtreecommitdiffstats
path: root/src/components/message_composer.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/components/message_composer.rs134
1 files changed, 134 insertions, 0 deletions
diff --git a/src/components/message_composer.rs b/src/components/message_composer.rs
new file mode 100644
index 0000000..fd4e59b
--- /dev/null
+++ b/src/components/message_composer.rs
@@ -0,0 +1,134 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+use filamento::chat::Body;
+use jid::BareJID;
+use js_sys::{wasm_bindgen::UnwrapThrowExt, Object, Reflect, JSON};
+use leptos::{html::Div, prelude::*, task::spawn_local};
+use overlay_scrollbars::OverlayScrollbars;
+use tracing::debug;
+
+use crate::client::Client;
+
+#[component]
+pub fn ChatViewMessageComposer(chat: BareJID) -> impl IntoView {
+ let message_input: NodeRef<Div> = NodeRef::new();
+
+ // TODO: load last message draft
+ let new_message = RwSignal::new("".to_string());
+ let client: Client = use_context().expect("no client in context");
+ let client = RwSignal::new(client);
+ let (shift_pressed, set_shift_pressed) = signal(false);
+
+ let send_message = move || {
+ let value = chat.clone();
+ spawn_local(async move {
+ match client
+ .read()
+ .send_message(
+ value,
+ Body {
+ body: new_message.get(),
+ },
+ )
+ .await
+ {
+ Ok(_) => {
+ new_message.set("".to_string());
+ message_input
+ .write()
+ .as_ref()
+ .expect("message input div not mounted")
+ .set_text_content(Some(""));
+ }
+ Err(e) => tracing::error!("message send error: {}", e),
+ }
+ })
+ };
+
+ let _focus = Effect::new(move |_| {
+ if let Some(input) = message_input.get() {
+ let _ = input.focus();
+ // TODO: set the last draft
+ input.set_text_content(Some(""));
+ // input.style("height: 0");
+ // let height = input.scroll_height();
+ // input.style(format!("height: {}px", height));
+ }
+ });
+
+ // let on_input = move |ev: Event| {
+ // // let keyboard_event: KeyboardEvent = ev.try_into().unwrap();
+ // debug!("got input event");
+ // let key= event_target_value(&ev);
+ // new_message.set(key);
+ // debug!("set new message");
+ // };
+ //
+
+ let composer: NodeRef<Div> = NodeRef::new();
+
+ let _scrollbars = Effect::new(move |_| {
+ if let Some(buffer) = composer.get() {
+ if let Some(viewport) = message_input.get() {
+ let scrollbars_obj = Object::new();
+ Reflect::set(&scrollbars_obj, &"theme".into(), &"os-macaw".into()).unwrap_throw();
+ Reflect::set(&scrollbars_obj, &"autoHide".into(), &"leave".into()).unwrap_throw();
+ let options_obj = Object::new();
+ Reflect::set(&options_obj, &"scrollbars".into(), &scrollbars_obj).unwrap_throw();
+
+ let elements_obj = Object::new();
+ Reflect::set(&elements_obj, &"viewport".into(), &viewport.into()).unwrap_throw();
+ let element_obj = Object::new();
+ Reflect::set(&elements_obj, &"elements".into(), &elements_obj).unwrap_throw();
+ Reflect::set(&element_obj, &"target".into(), &buffer.into()).unwrap_throw();
+ // let element = Object::define_property(&Object::define_property(&Object::new(), &"target".into(), &buffer.into()), &"elements".into(), &Object::define_property(&Object::new(), &"viewport".into(), &viewport.into()));
+ debug!(
+ "scrollable element: {}",
+ JSON::stringify(&element_obj.clone().into()).unwrap_throw()
+ );
+ debug!(
+ "scrollable options: {}",
+ JSON::stringify(&options_obj.clone().into()).unwrap_throw()
+ );
+ OverlayScrollbars(element_obj, options_obj);
+ }
+ }
+ });
+
+ // TODO: placeholder
+ view! {
+ <form class="new-message-composer panel">
+ <div class="overlay-scroll" node_ref=composer>
+ <div
+ class="text-box"
+ on:input:target=move |ev| {
+ new_message.set(ev.target().text_content().unwrap_or_default())
+ }
+ node_ref=message_input
+ contenteditable
+ on:keydown=move |ev| {
+ match ev.key_code() {
+ 16 => set_shift_pressed.set(true),
+ 13 => {
+ if !shift_pressed.get() {
+ ev.prevent_default();
+ send_message();
+ }
+ }
+ _ => {}
+ }
+ }
+ on:keyup=move |ev| {
+ match ev.key_code() {
+ 16 => set_shift_pressed.set(false),
+ _ => {}
+ }
+ }
+ ></div>
+ </div>
+ // <input hidden type="submit" />
+ </form>
+ }
+}