Skip to content

Commit

Permalink
feat(misc): Profile picture/banner split (#1775)
Browse files Browse the repository at this point in the history
  • Loading branch information
Flemmli97 authored Feb 7, 2024
1 parent 93b5c6c commit 0e5f783
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 38 deletions.
1 change: 1 addition & 0 deletions common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod language;
pub mod notifications;
pub mod profile_update_channel;
pub mod sounds;
pub mod state;
pub mod testing;
Expand Down
74 changes: 74 additions & 0 deletions common/src/profile_update_channel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use std::sync::Arc;

use futures::channel::oneshot;
use once_cell::sync::Lazy;
use tokio::sync::Mutex;
use tracing::log;
use warp::crypto::DID;

use crate::{
state::Identity,
warp_runner::{MultiPassCmd, WarpCmd},
WARP_CMD_CH,
};

pub struct ProfileUpdateChannel {
pub tx: tokio::sync::mpsc::UnboundedSender<ProfileDataUpdate>,
pub rx: Arc<Mutex<tokio::sync::mpsc::UnboundedReceiver<ProfileDataUpdate>>>,
}

pub static PROFILE_CHANNEL_LISTENER: Lazy<ProfileUpdateChannel> = Lazy::new(|| {
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
ProfileUpdateChannel {
tx,
rx: Arc::new(Mutex::new(rx)),
}
});

pub struct ProfileDataUpdate {
pub did: DID,
pub picture: Option<String>,
pub banner: Option<String>,
}

pub fn fetch_identity_data(identities: &[Identity]) {
let identities: Vec<_> = identities.iter().map(|id| id.did_key()).collect();
tokio::spawn(async move {
let warp_cmd_tx = WARP_CMD_CH.tx.clone();

for identity in identities {
let (tx, rx) = oneshot::channel();
let _ = warp_cmd_tx.send(WarpCmd::MultiPass(MultiPassCmd::GetProfilePicture {
did: identity.clone(),
rsp: tx,
}));

let profile_picture = match rx.await {
Ok(res) => res.ok(),
Err(e) => {
log::error!("error fetching profile pic {e}");
return;
}
};
let (tx, rx) = oneshot::channel();
let _ = warp_cmd_tx.send(WarpCmd::MultiPass(MultiPassCmd::GetProfileBanner {
did: identity.clone(),
rsp: tx,
}));
let profile_banner = match rx.await {
Ok(res) => res.ok(),
Err(e) => {
log::error!("error fetching profile banner {e}");
return;
}
};
if profile_picture.is_some() || profile_banner.is_some() {
let _ = PROFILE_CHANNEL_LISTENER.tx.send(ProfileDataUpdate {
did: identity,
picture: profile_picture,
banner: profile_banner,
});
}
}
});
}
6 changes: 6 additions & 0 deletions common/src/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1689,6 +1689,12 @@ impl State {
*friend = ident;
}
}

pub fn update_identity_with(&mut self, id: DID, mut ident: impl FnMut(&mut Identity)) {
if let Some(friend) = self.identities.get_mut(&id) {
ident(friend);
}
}
// identities are updated once a minute for friends. but if someone sends you a message, they should be seen as online.
// this function checks if the friend is offline and if so, sets them to online. This may be incorrect, but should
// be corrected when the identity list is periodically updated
Expand Down
2 changes: 1 addition & 1 deletion common/src/warp_runner/manager/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ mod tesseract_commands;
// this shortens the path required to use the functions and structs
pub use blink_commands::{handle_blink_cmd, BlinkCmd};
pub use constellation_commands::{handle_constellation_cmd, thumbnail_to_base64, ConstellationCmd};
pub use multipass_commands::{handle_multipass_cmd, identity_image_to_base64, MultiPassCmd};
pub use multipass_commands::{handle_multipass_cmd, MultiPassCmd};
pub use other_commands::*;
pub use raygun_commands::{handle_raygun_cmd, RayGunCmd};
pub use tesseract_commands::{handle_tesseract_cmd, TesseractCmd};
26 changes: 15 additions & 11 deletions common/src/warp_runner/manager/commands/multipass_commands.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{collections::HashMap, str::FromStr};
use std::{collections::HashMap, slice, str::FromStr};

use derive_more::Display;

Expand All @@ -16,6 +16,7 @@ use warp::{
use tracing::log;

use crate::{
profile_update_channel::fetch_identity_data,
state::{self, Identity},
warp_runner::{ui_adapter::dids_to_identity, Account},
};
Expand Down Expand Up @@ -93,12 +94,12 @@ pub enum MultiPassCmd {
#[display(fmt = "UpdateProfilePicture")]
GetProfilePicture {
did: DID,
rsp: oneshot::Sender<Result<IdentityImage, warp::error::Error>>,
rsp: oneshot::Sender<Result<String, warp::error::Error>>,
},
#[display(fmt = "UpdateProfilePicture")]
GetProfileBanner {
did: DID,
rsp: oneshot::Sender<Result<IdentityImage, warp::error::Error>>,
rsp: oneshot::Sender<Result<String, warp::error::Error>>,
},
#[display(fmt = "UpdateProfilePicture")]
UpdateProfilePicture {
Expand Down Expand Up @@ -250,11 +251,19 @@ pub async fn handle_multipass_cmd(cmd: MultiPassCmd, warp: &mut super::super::Wa
let _ = rsp.send(r);
}
MultiPassCmd::GetProfilePicture { did, rsp } => {
let pfp = warp.multipass.identity_picture(&did).await;
let pfp = warp
.multipass
.identity_picture(&did)
.await
.map(|img| identity_image_to_base64(&img));
let _ = rsp.send(pfp);
}
MultiPassCmd::GetProfileBanner { did, rsp } => {
let pfb = warp.multipass.identity_banner(&did).await;
let pfb = warp
.multipass
.identity_banner(&did)
.await
.map(|img| identity_image_to_base64(&img));
let _ = rsp.send(pfb);
}
MultiPassCmd::ClearProfilePicture { rsp } => {
Expand Down Expand Up @@ -438,12 +447,7 @@ pub async fn handle_multipass_cmd(cmd: MultiPassCmd, warp: &mut super::super::Wa
}

async fn update_identity(id: &mut Identity, warp: &mut crate::warp_runner::manager::Warp) {
if let Ok(picture) = warp.multipass.identity_picture(&id.did_key()).await {
id.set_profile_picture(&identity_image_to_base64(&picture));
}
if let Ok(banner) = warp.multipass.identity_banner(&id.did_key()).await {
id.set_profile_banner(&identity_image_to_base64(&banner));
}
fetch_identity_data(slice::from_ref(id));
if let Ok(status) = warp.multipass.identity_status(&id.did_key()).await {
id.set_identity_status(status);
}
Expand Down
36 changes: 11 additions & 25 deletions common/src/warp_runner/ui_adapter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ pub use multipass_event::{convert_multipass_event, MultiPassEvent};
pub use raygun_event::{convert_raygun_event, RayGunEvent};
use uuid::Uuid;

use crate::state::{self, chats, utils::mention_regex_epattern, Identity, MAX_PINNED_MESSAGES};
use crate::{
profile_update_channel::fetch_identity_data,
state::{self, chats, utils::mention_regex_epattern, Identity, MAX_PINNED_MESSAGES},
};
use futures::{stream::FuturesOrdered, FutureExt, StreamExt};
use serde::{Deserialize, Serialize};
use std::{
collections::{HashSet, VecDeque},
ops::Range,
slice,
};
use warp::{
constellation::file::File,
Expand All @@ -29,9 +33,7 @@ use warp::{

use tracing::log;

use super::{
manager::commands::identity_image_to_base64, FetchMessagesConfig, FetchMessagesResponse,
};
use super::{FetchMessagesConfig, FetchMessagesResponse};

/// the UI needs additional information for message replies, namely the text of the message being replied to.
/// fetch that before sending the message to the UI.
Expand Down Expand Up @@ -149,16 +151,9 @@ pub async fn did_to_identity(
.identity_platform(&id.did_key())
.await
.unwrap_or(Platform::Unknown);
let mut id = state::Identity::new(id, status, platform);

if let Ok(picture) = account.identity_picture(&id.did_key()).await {
id.set_profile_picture(&identity_image_to_base64(&picture));
}

if let Ok(banner) = account.identity_banner(&id.did_key()).await {
id.set_profile_banner(&identity_image_to_base64(&banner));
}
let id = state::Identity::new(id, status, platform);

fetch_identity_data(slice::from_ref(&id));
id
}
None => get_uninitialized_identity(did)?,
Expand All @@ -180,19 +175,10 @@ pub async fn dids_to_identity(
.identity_platform(&id.did_key())
.await
.unwrap_or(Platform::Unknown);
let mut id = state::Identity::new(id, status, platform);

if let Ok(picture) = account.identity_picture(&id.did_key()).await {
id.set_profile_picture(&identity_image_to_base64(&picture));
}

if let Ok(banner) = account.identity_banner(&id.did_key()).await {
id.set_profile_banner(&identity_image_to_base64(&banner));
}

id
state::Identity::new(id, status, platform)
});
let converted_ids = FuturesOrdered::from_iter(ids).collect().await;
let converted_ids: Vec<Identity> = FuturesOrdered::from_iter(ids).collect().await;
fetch_identity_data(&converted_ids);
Ok(converted_ids)
}

Expand Down
6 changes: 5 additions & 1 deletion ui/src/components/settings/sub_pages/profile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,11 @@ pub fn ProfileSettings(cx: Scope) -> Element {

if let Some(ident) = should_update.get() {
log::trace!("Updating ProfileSettings");
state.write().set_own_identity(ident.clone());
let mut ident = ident.clone();
let current = state.read().get_own_identity();
ident.set_profile_banner(&current.profile_banner());
ident.set_profile_picture(&current.profile_picture());
state.write().set_own_identity(ident);
state
.write()
.mutate(common::state::Action::AddToastNotification(
Expand Down
35 changes: 35 additions & 0 deletions ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use common::icons::outline::Shape as Icon;
use common::icons::Icon as IconElement;
use common::language::{get_local_text, get_local_text_with_args};
use common::notifications::{NotificationAction, NOTIFICATION_LISTENER};
use common::profile_update_channel::PROFILE_CHANNEL_LISTENER;
use common::state::settings::GlobalShortcut;
use common::state::ToastNotification;
use common::warp_runner::ui_adapter::MessageEvent;
Expand Down Expand Up @@ -537,6 +538,40 @@ fn use_app_coroutines(cx: &ScopeState) -> Option<()> {
}
});

// Listen to profile updates
use_future(cx, (), |_| {
to_owned![state];
async move {
while !state.read().initialized {
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
}
let channel = PROFILE_CHANNEL_LISTENER.rx.clone();
let mut ch = channel.lock().await;
while let Some(action) = ch.recv().await {
let mut id = state.read().get_own_identity();
let did = action.did;
if did.eq(&id.did_key()) {
if let Some(picture) = action.picture.as_ref() {
id.set_profile_picture(picture);
}
if let Some(banner) = action.banner.as_ref() {
id.set_profile_banner(banner);
}
state.write().set_own_identity(id);
} else {
state.write().update_identity_with(did, |id| {
if let Some(picture) = action.picture.as_ref() {
id.set_profile_picture(picture);
}
if let Some(banner) = action.banner.as_ref() {
id.set_profile_banner(banner);
}
});
}
}
}
});

// Listen to async tasks actions that should be handled on main thread
use_future(cx, (), |_| {
to_owned![state];
Expand Down

0 comments on commit 0e5f783

Please sign in to comment.