summaryrefslogtreecommitdiffstats
path: root/src/components/personal_status.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/personal_status.rs')
-rw-r--r--src/components/personal_status.rs178
1 files changed, 178 insertions, 0 deletions
diff --git a/src/components/personal_status.rs b/src/components/personal_status.rs
new file mode 100644
index 0000000..f830a1b
--- /dev/null
+++ b/src/components/personal_status.rs
@@ -0,0 +1,178 @@
+use filamento::{presence::{Offline, Online, PresenceType, Show}, user::{User, UserStoreFields}};
+use leptos::{html, prelude::*};
+use reactive_stores::{ArcStore, Store};
+use tracing::{debug, error};
+
+use crate::{client::Client, components::{avatar::AvatarWithPresence, overlay::Overlay}, user::{get_name, MacawUser}, user_presences::UserPresences, views::{macaw::settings::SettingsPage, AppState}};
+
+#[component]
+pub fn PersonalStatus() -> impl IntoView {
+ let user: LocalResource<MacawUser> = use_context().expect("no local user in context");
+
+ let (open, set_open) = signal(false);
+ move || if let Some(user) = user.get() {
+ let user: Store<User> = <ArcStore<filamento::user::User> as Clone>::clone(&(*user.user)).into();
+ view! {
+ <div class="dock-item" class:focused=move || *open.read() on:click=move |_| {
+ debug!("set open to true");
+ set_open.update(|state| *state = !*state)
+ }>
+ <AvatarWithPresence user=user />
+ <div class="dock-pill"></div>
+ </div>
+ {move || {
+ let open = open.get();
+ debug!("open = {:?}", open);
+ if open {
+ view! {
+ <Overlay set_open>
+ <PersonalStatusMenu user set_open/>
+ </Overlay>
+ }.into_any()
+ } else {
+ view! {}.into_any()
+ }}}
+ }.into_any()
+ } else {
+ view! {}.into_any()
+ }
+}
+
+#[component]
+pub fn PersonalStatusMenu(user: Store<User>, set_open: WriteSignal<bool>) -> impl IntoView {
+ let set_app: WriteSignal<AppState> = use_context().unwrap();
+ let show_settings: RwSignal<Option<SettingsPage>> = use_context().unwrap();
+ let user_presences: Store<UserPresences> = use_context().expect("no user presence store");
+
+ let client = use_context::<Client>().expect("client not in context");
+ let client1 = client.clone();
+ let (show_value, set_show_value) = signal({
+ let show = match user_presences.write().get_user_presences(&user.jid().read()).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<html::Select> = NodeRef::new();
+
+ let disconnect = Action::new_local(move |()| {
+ let client = client.clone();
+ async move {
+ client.disconnect(Offline::default()).await;
+ }
+ });
+ let set_status = Action::new_local(move |show_value: &i32| {
+ let show_value = show_value.to_owned();
+ let client = client1.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! {
+ <div class="personal-status-menu menu">
+ <div class="user">
+ <AvatarWithPresence user=user />
+ <div class="user-info">
+ <div class="nick">{move || get_name(user, false)}</div>
+ <div class="jid">{move || user.jid().with(|jid| jid.to_string())}</div>
+ </div>
+ </div>
+ <div class="status-edit">
+ <select
+ node_ref=show_select
+ on:change:target=move |ev| {
+ let show_value = ev.target().value().parse().unwrap();
+ set_status.dispatch(show_value);
+ }
+ prop:show_value=move || show_value.get().to_string()
+ >
+ <option value="0" selected=move || show_value.get_untracked() == 0>Available to Chat</option>
+ <option value="1" selected=move || show_value.get_untracked() == 1>Online</option>
+ <option value="2" selected=move || show_value.get_untracked() == 2>Do not disturb</option>
+ <option value="3" selected=move || show_value.get_untracked() == 3>Away</option>
+ <option value="4" selected=move || show_value.get_untracked() == 4>Extended Away</option>
+ <option value="5" selected=move || show_value.get_untracked() == 5>Offline</option>
+ </select>
+ </div>
+ <hr />
+ <div class="menu-item" on:click=move |_| {
+ show_settings.set(Some(SettingsPage::Profile));
+ set_open.set(false);
+ }>
+ Profile
+ </div>
+ <div class="menu-item" on:click=move |_| {
+ show_settings.set(Some(SettingsPage::Account));
+ set_open.set(false);
+ }>
+ Settings
+ </div>
+ <hr />
+ <div class="menu-item" on:click=move |_| {
+ // TODO: check if client is actually dropped/shutdown eventually
+ disconnect.dispatch(());
+ set_app.set(AppState::LoggedOut)
+ }>
+ Log out
+ </div>
+ </div>
+ }
+}
+