1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
|
use std::str::FromStr;
use filamento::{chat::Chat, error::{CommandError, DatabaseError}, user::User};
use jid::{BareJID, JID};
use leptos::{html::Input, prelude::*};
use leptos_fetch::QueryClient;
use reactive_stores::{ArcStore, Store};
use thiserror::Error;
use crate::{chat::MacawChat, client::Client, open_chats::OpenChatsPanel, user::MacawUser};
#[derive(Clone, Debug, Error)]
pub enum NewChatError {
#[error("Missing JID")]
MissingJID,
#[error("Invalid JID: {0}")]
InvalidJID(#[from] jid::ParseError),
#[error("Database: {0}")]
Db(#[from] CommandError<DatabaseError>),
}
// TODO: remove
#[derive(Clone)]
pub struct GotChat(Chat);
async fn get_chat(jid: BareJID) -> ArcStore<Chat> {
// let client: Client = use_context().expect("no client in context");
// ArcStore::new(client.get_chat(jid).await.unwrap())
let GotChat(chat) = use_context().expect("no chat in context");
ArcStore::new(chat)
}
#[derive(Clone)]
pub struct GotUser(User);
async fn get_user(jid: BareJID) -> ArcStore<User> {
// let client: Client = use_context().expect("no client in context");
// ArcStore::new(client.get_user(jid).await.unwrap())
let GotUser(user) = use_context().expect("no user in context");
ArcStore::new(user)
}
#[component]
pub fn NewChatWidget(set_open_new_chat: WriteSignal<bool>) -> impl IntoView {
let jid = RwSignal::new("".to_string());
// TODO: compartmentalise into error component, form component...
let (error, set_error) = signal(None::<NewChatError>);
let error_message = move || {
error.with(|error| {
if let Some(error) = error {
view! { <div class="error">{error.to_string()}</div> }.into_any()
} else {
view! {}.into_any()
}
})
};
let (new_chat_pending, set_new_chat_pending) = signal(false);
let open_chats: Store<OpenChatsPanel> =
use_context().expect("no open chats panel store in context");
let client = use_context::<Client>().expect("client not in context");
let query_client: QueryClient = expect_context();
let open_chat = Action::new_local(move |_| {
let client = client.clone();
async move {
set_new_chat_pending.set(true);
if jid.read_untracked().is_empty() {
set_error.set(Some(NewChatError::MissingJID));
set_new_chat_pending.set(false);
return;
}
let jid = match JID::from_str(&jid.read_untracked()) {
// TODO: ability to direct address a resource?
Ok(j) => j.to_bare(),
Err(e) => {
set_error.set(Some(e.into()));
set_new_chat_pending.set(false);
return;
}
};
let chat_jid = jid;
let (chat, user) = match client.get_chat_and_user(chat_jid).await {
Ok(c) => c,
Err(e) => {
set_error.set(Some(e.into()));
set_new_chat_pending.set(false);
return;
},
};
let chat = {
// let user = MacawUser::got_user(user);
let jid = chat.correspondent.clone();
provide_context(GotChat(chat));
let chat = query_client.local_resource(get_chat, move || jid.clone());
let user = {
let jid = user.jid.clone();
provide_context(GotUser(user));
MacawUser {
user: query_client.local_resource(get_user, move || jid.clone())
}
};
MacawChat { chat, user }
};
open_chats.update(|open_chats| open_chats.open(chat.clone()));
set_open_new_chat.set(false);
}
});
let jid_input = NodeRef::<Input>::new();
let _focus = Effect::new(move |_| {
if let Some(input) = jid_input.get() {
let _ = input.focus();
input.set_text_content(Some(""));
// input.style("height: 0");
// let height = input.scroll_height();
// input.style(format!("height: {}px", height));
}
});
view! {
<div class="new-chat-widget">
<form on:submit=move |ev| {
ev.prevent_default();
open_chat.dispatch(());
}>
{error_message}
<input
disabled=new_chat_pending
placeholder="JID"
type="text"
node_ref=jid_input
bind:value=jid
name="jid"
id="jid"
autofocus="true"
/>
<input disabled=new_chat_pending class="button" type="submit" value="Start Chat" />
</form>
</div>
}
}
|