diff options
| -rw-r--r-- | Cargo.lock | 1 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | src/lib.rs | 33 | 
3 files changed, 27 insertions, 8 deletions
@@ -1904,6 +1904,7 @@ dependencies = [  name = "macaw-web"  version = "0.1.0"  dependencies = [ + "base64",   "chrono",   "chrono-humanize",   "console_error_panic_hook", @@ -4,6 +4,7 @@ version = "0.1.0"  edition = "2024"  [dependencies] +base64 = "0.22.1"  chrono = "0.4.41"  chrono-humanize = "0.2.3"  console_error_panic_hook = "0.1.7" @@ -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>  | 
