From 227661fb8339743f87fc36ca3be1f935050db2d4 Mon Sep 17 00:00:00 2001 From: cel 🌸 Date: Thu, 15 May 2025 19:32:01 +0100 Subject: feat: dock user menu --- src/lib.rs | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 164 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/lib.rs b/src/lib.rs index 5961204..3b26827 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,9 +12,9 @@ use std::{ }; use base64::{Engine, prelude::BASE64_STANDARD}; -use chrono::{NaiveDateTime, TimeDelta}; +use chrono::{NaiveDateTime, TimeDelta, Utc}; use filamento::{ - chat::{Body, Chat, ChatStoreFields, Delivery, Message, MessageStoreFields}, db::Db, error::{CommandError, ConnectionError, DatabaseError}, files::{opfs::OPFSError, FileStore, FilesMem, FilesOPFS}, presence::{Presence, PresenceType, Show}, roster::{Contact, ContactStoreFields}, user::{User, UserStoreFields}, UpdateMessage + chat::{Body, Chat, ChatStoreFields, Delivery, Message, MessageStoreFields}, db::Db, error::{CommandError, ConnectionError, DatabaseError}, files::{opfs::OPFSError, FileStore, FilesMem, FilesOPFS}, presence::{Offline, Online, Presence, PresenceType, Show}, roster::{Contact, ContactStoreFields}, user::{User, UserStoreFields}, UpdateMessage }; use futures::stream::StreamExt; use indexmap::IndexMap; @@ -46,6 +46,7 @@ pub enum AppState { #[derive(Clone)] pub struct Client { client: filamento::Client, + resource: ArcRwSignal>, jid: Arc, file_store: Files, } @@ -224,15 +225,19 @@ fn LoginPage( files.clone(), ); // TODO: remember_me + let resource = ArcRwSignal::new(None::); let client = Client { client, + resource: resource.clone(), jid: Arc::new(jid), file_store: files, }; if *connect_on_login.read_untracked() { match client.connect().await { - Ok(_) => {} + Ok(r) => { + resource.set(Some(r)) + } Err(e) => { set_error.set(Some(e.into())); set_login_pending.set(false); @@ -591,6 +596,17 @@ impl Presences { presence, ); } + + pub fn resource_presence(&mut self, resource: String) -> Presence { + if let Some(presence) = self.presences.get(&resource) { + presence.clone() + } else { + Presence { + timestamp: Utc::now(), + presence: PresenceType::Offline(Offline::default()), + } + } + } } #[component] @@ -851,17 +867,162 @@ pub fn PersonalStatus() -> impl IntoView { let user: Store = as Clone>::clone(&(*user.user)).into(); view! {
+ {move || { + let open = open.get(); + debug!("open = {:?}", open); + if open { + view! { + + + + }.into_any() + } else { + view! {}.into_any() + }}} }.into_any() } else { view! {}.into_any() } } +#[component] +pub fn PersonalStatusMenu(user: Store) -> impl IntoView { + let user_presences: Store = use_context().expect("no user presence store"); + + let client = use_context::().expect("client not in context"); + let (show_value, set_show_value) = signal({ + let show = match user_presences.write().get_user_presences(&user.jid().read().as_bare()).write().resource_presence(client.resource.read().clone().unwrap_or_default()).presence { + PresenceType::Online(online) => match online.show { + Some(s) => match s { + Show::Away => 3, + Show::Chat => 0, + Show::DoNotDisturb => 2, + Show::ExtendedAway => 4, + }, + None => 1, + }, + PresenceType::Offline(_offline) => 5, + }; + debug!("initial show = {show}"); + show + }); + + let show_select: NodeRef = NodeRef::new(); + + let set_status = Action::new_local(move |show_value: &i32| { + let show_value = show_value.to_owned(); + let client = client.clone(); + async move { + if let Err(e) = match show_value { + 0 => { + if let Ok(r) = client.connect().await { + client.resource.set(Some(r)) + }; + client.set_status(Online { show: Some(Show::Chat), ..Default::default() }).await + }, + 1 => { + if let Ok(r) = client.connect().await { + client.resource.set(Some(r)) + }; + client.set_status(Online { show: None, ..Default::default() }).await + }, + 2 => { + if let Ok(r) = client.connect().await { + client.resource.set(Some(r)) + }; + client.set_status(Online { show: Some(Show::DoNotDisturb), ..Default::default() }).await + }, + 3 => { + if let Ok(r) = client.connect().await { + client.resource.set(Some(r)) + }; + client.set_status(Online { show: Some(Show::Away), ..Default::default() }).await + }, + 4 => { + if let Ok(r) = client.connect().await { + client.resource.set(Some(r)) + }; + client.set_status(Online { show: Some(Show::ExtendedAway), ..Default::default() }).await + }, + 5 => { + if let Ok(_) = client.disconnect(Offline::default()).await { + client.resource.set(None) + } + set_show_value.set(5); + return + } + _ => { + error!("invalid availability select"); + return + } + } { + error!("show set error: {e}"); + return + } + set_show_value.set(show_value); + } + }); + + view! { + + } +} + +#[component] +pub fn Overlay(set_open: WriteSignal, children: Children) -> impl IntoView { + view! { +
+
+
{children()}
+
+ } +} + #[component] pub fn OpenChatsPanelView() -> impl IntoView { let open_chats: Store = use_context().expect("no open chats panel in context"); -- cgit