summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar cel 🌸 <cel@bunny.garden>2025-05-07 02:16:38 +0100
committerLibravatar cel 🌸 <cel@bunny.garden>2025-05-07 02:16:38 +0100
commit3302bdedebb734d9b7306bb361c2aa9168a527d7 (patch)
treefc02edf10cff35d102cf9bcb2604c7a16d097d82
parentd83baa78b0b029a5c25b781ee3f77ae3cbecf60d (diff)
downloadmacaw-web-3302bdedebb734d9b7306bb361c2aa9168a527d7.tar.gz
macaw-web-3302bdedebb734d9b7306bb361c2aa9168a527d7.tar.bz2
macaw-web-3302bdedebb734d9b7306bb361c2aa9168a527d7.zip
feat: filesmem base64 img srcs
-rw-r--r--Cargo.lock1
-rw-r--r--Cargo.toml1
-rw-r--r--src/lib.rs33
3 files changed, 27 insertions, 8 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 3bfef73..a47f772 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1904,6 +1904,7 @@ dependencies = [
name = "macaw-web"
version = "0.1.0"
dependencies = [
+ "base64",
"chrono",
"chrono-humanize",
"console_error_panic_hook",
diff --git a/Cargo.toml b/Cargo.toml
index 8ba81a1..b535a9a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"
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>