summaryrefslogtreecommitdiffstats
path: root/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs33
1 files changed, 25 insertions, 8 deletions
diff --git a/src/lib.rs b/src/lib.rs
index d45bfc3..0be757e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11,6 +11,7 @@ use std::{
time::{self, Duration},
};
+use base64::{prelude::BASE64_STANDARD, Engine};
use chrono::{NaiveDateTime, TimeDelta};
use filamento::{
chat::{Body, Chat, ChatStoreFields, Delivery, Message, MessageStoreFields}, db::Db, error::{CommandError, ConnectionError, DatabaseError}, files::FilesMem, roster::{Contact, ContactStoreFields}, user::{User, UserStoreFields}, UpdateMessage
@@ -342,6 +343,7 @@ pub fn open_chat(open_chats: Store<OpenChatsPanel>, chat: MacawChat) {
impl OpenChatsPanel {
pub fn open(&mut self, chat: MacawChat) {
if let Some(jid) = &mut self.chat_view {
+ debug!("a chat was already open");
if let Some((index, _jid, entry)) = self.chats.shift_remove_full(jid) {
let new_jid = chat.chat.correspondent().read().clone();
self.chats.insert_before(index, new_jid.clone(), chat);
@@ -356,6 +358,7 @@ impl OpenChatsPanel {
self.chats.insert(new_jid.clone(), chat);
*&mut self.chat_view = Some(new_jid);
}
+ debug!("opened chat");
}
// TODO:
@@ -505,13 +508,15 @@ pub fn OpenChatView(chat: MacawChat) -> impl IntoView {
#[component]
pub fn ChatViewHeader(chat: MacawChat) -> impl IntoView {
let chat_user = *chat.user;
- let avatar = move || get_avatar(chat_user);
+ let avatar = LocalResource::new(move || get_avatar(chat_user));
let name = move || get_name(chat_user);
let jid = move || chat_user.jid().read().to_string();
view! {
<div class="chat-view-header panel">
- <img class="avatar" src=avatar />
+ <Transition fallback=|| view! { <img class="avatar" src=NO_AVATAR /> } >
+ <img class="avatar" src=move || avatar.read().as_deref().map(|avatar| avatar.clone()).unwrap_or_default() />
+ </Transition>
<div class="user-info">
<h2 class="name">{name}</h2>
<h3>{jid}</h3>
@@ -713,7 +718,7 @@ pub fn Delivery(delivery: Delivery) -> impl IntoView {
pub fn Message(message: MacawMessage, major: bool, r#final: bool) -> impl IntoView {
let message_message = *message.message;
let message_user = *message.user;
- let avatar = move || get_avatar(message_user);
+ let avatar = LocalResource::new(move || get_avatar(message_user));
let name = move || get_name(message_user);
// TODO: chrono-humanize?
@@ -722,7 +727,11 @@ pub fn Message(message: MacawMessage, major: bool, r#final: bool) -> impl IntoVi
if major {
view! {
<div class:final=r#final class="chat-message major">
- <div class="left"><img class="avatar" src=avatar /></div>
+ <div class="left">
+ <Transition fallback=|| view! { <img class="avatar" src=NO_AVATAR /> } >
+ <img class="avatar" src=move || avatar.read().as_deref().map(|avatar| avatar.clone()).unwrap_or_default() />
+ </Transition>
+ </div>
<div class="middle">
<div class="message-info">
<div class="message-user-name">{name}</div>
@@ -1185,9 +1194,15 @@ fn ChatsList() -> impl IntoView {
}
}
-pub fn get_avatar(user: Store<User>) -> String {
+pub async fn get_avatar(user: Store<User>) -> String {
if let Some(avatar) = &user.read().avatar {
- NO_AVATAR.to_string()
+ let client = use_context::<Client>().expect("client not in context");
+ if let Some(data) = client.file_store.get_file(avatar).await {
+ let data = BASE64_STANDARD.encode(data);
+ format!("data:image/jpg;base64, {}", data)
+ } else {
+ NO_AVATAR.to_string()
+ }
// TODO: enable avatar fetching
// format!("/files/{}", avatar)
} else {
@@ -1215,7 +1230,7 @@ pub fn get_name(user: Store<User>) -> String {
#[component]
fn ChatsListItem(chat: MacawChat, message: MacawMessage) -> impl IntoView {
let chat_user = *chat.user;
- let avatar = move || get_avatar(chat_user);
+ let avatar = LocalResource::new(move || get_avatar(chat_user));
let name = move || get_name(chat_user);
// TODO: store fine-grained reactivity
@@ -1229,7 +1244,9 @@ fn ChatsListItem(chat: MacawChat, message: MacawMessage) -> impl IntoView {
view! {
<div class="chats-list-item" on:click=open_chat>
- <img class="avatar" src=avatar />
+ <Transition fallback=|| view! { <img class="avatar" src=NO_AVATAR /> } >
+ <img class="avatar" src=move || avatar.read().as_deref().map(|avatar| avatar.clone()).unwrap_or_default() />
+ </Transition>
<div class="item-info">
<h3>{name}</h3>
<p>{latest_message_body}</p>