diff --git a/app/src/components/avatar.rs b/app/src/components/avatar.rs index 947fc3c..9689798 100644 --- a/app/src/components/avatar.rs +++ b/app/src/components/avatar.rs @@ -1,9 +1,11 @@ use crate::{ - components::avatar_event_bus::AvatarEventBus, - infra::common_component::{CommonComponent, CommonComponentParts}}; + components::avatar_event_bus::{AvatarEventBus, Response}, + infra::common_component::{CommonComponent, CommonComponentParts}, +}; use anyhow::{bail, Result}; use graphql_client::GraphQLQuery; -use yew::{prelude::*}; +use serde::{Deserialize, Serialize}; +use yew::prelude::*; use yew_agent::{Bridge, Bridged}; #[derive(GraphQLQuery)] @@ -15,11 +17,18 @@ use yew_agent::{Bridge, Bridged}; )] pub struct GetUserAvatar; +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct AvatarData(String); + +impl AvatarData { + pub fn new(data: String) -> Self { + AvatarData(data) + } +} + pub struct Avatar { common: CommonComponentParts, - /// The user info. If none, the error is in `error`. If `error` is None, then we haven't - /// received the server response yet. - avatar: Option, + avatar: Option, _producer: Box>, } @@ -28,7 +37,7 @@ pub struct Avatar { pub enum Msg { /// Received the user details response, either the user data or an error. UserAvatarResponse(Result), - Update + Update(Response), } #[derive(yew::Properties, Clone, PartialEq, Eq)] @@ -39,16 +48,26 @@ pub struct Props { } impl CommonComponent for Avatar { - fn handle_msg(&mut self, ctx: &Context, msg: ::Message) -> Result { + fn handle_msg( + &mut self, + ctx: &Context, + msg: ::Message, + ) -> Result { match msg { Msg::UserAvatarResponse(response) => match response { - Ok(user) => self.avatar = user.user.avatar, + Ok(user) => self.avatar = user.user.avatar.map(AvatarData::new), Err(e) => { self.avatar = None; bail!("Error getting user avatar: {}", e); } }, - Msg::Update => self.get_user_avatar(ctx), + Msg::Update(Response::Update((username, avatar))) => { + if username == ctx.props().username { + self.avatar = avatar; + return Ok(true); + } + return Ok(false); + } } Ok(true) } @@ -79,7 +98,7 @@ impl Component for Avatar { let mut avatar = Self { common: CommonComponentParts::::create(), avatar: None, - _producer: AvatarEventBus::bridge(ctx.link().callback(|_| Msg::Update)), + _producer: AvatarEventBus::bridge(ctx.link().callback(Msg::Update)), }; avatar.get_user_avatar(ctx); avatar @@ -93,8 +112,8 @@ impl Component for Avatar { match &self.avatar { Some(avatar) => html! { Avatar }, diff --git a/app/src/components/avatar_event_bus.rs b/app/src/components/avatar_event_bus.rs index 2a1ea81..5b62e66 100644 --- a/app/src/components/avatar_event_bus.rs +++ b/app/src/components/avatar_event_bus.rs @@ -2,9 +2,16 @@ use serde::{Deserialize, Serialize}; use std::collections::HashSet; use yew_agent::{Agent, AgentLink, Context, HandlerId}; +use super::avatar::AvatarData; + #[derive(Serialize, Deserialize, Debug)] pub enum Request { - Update, + Update((String, Option)), +} + +#[derive(Serialize, Deserialize, Debug)] +pub enum Response { + Update((String, Option)), } pub struct AvatarEventBus { @@ -16,7 +23,7 @@ impl Agent for AvatarEventBus { type Reach = Context; type Message = (); type Input = Request; - type Output = (); + type Output = Response; fn create(link: AgentLink) -> Self { Self { @@ -29,9 +36,10 @@ impl Agent for AvatarEventBus { fn handle_input(&mut self, msg: Self::Input, _id: HandlerId) { match msg { - Request::Update => { + Request::Update((username, avatar)) => { for sub in self.subscribers.iter() { - self.link.respond(*sub, ()); + self.link + .respond(*sub, Response::Update((username.clone(), avatar.clone()))); } } } @@ -44,4 +52,4 @@ impl Agent for AvatarEventBus { fn disconnected(&mut self, id: HandlerId) { self.subscribers.remove(&id); } -} \ No newline at end of file +} diff --git a/app/src/components/user_details_form.rs b/app/src/components/user_details_form.rs index 6ff366b..6872dc1 100644 --- a/app/src/components/user_details_form.rs +++ b/app/src/components/user_details_form.rs @@ -1,8 +1,9 @@ use std::str::FromStr; use crate::{ - components::user_details::User, + components::avatar::AvatarData, components::avatar_event_bus::{AvatarEventBus, Request}, + components::user_details::User, infra::common_component::{CommonComponent, CommonComponentParts}, }; use anyhow::{bail, Error, Result}; @@ -14,8 +15,8 @@ use graphql_client::GraphQLQuery; use validator_derive::Validate; use web_sys::{FileList, HtmlInputElement, InputEvent}; use yew::prelude::*; -use yew_form_derive::Model; use yew_agent::{Dispatched, Dispatcher}; +use yew_form_derive::Model; #[derive(Default)] struct JsFile { @@ -406,7 +407,13 @@ impl UserDetailsForm { self.user.avatar = Some(avatar); } self.just_updated = true; - self.avatar_event_bus.send(Request::Update); + self.avatar_event_bus.send(Request::Update(( + self.user.id.clone(), + self.user + .avatar + .as_ref() + .map(|data| AvatarData::new(data.clone())), + ))); Ok(true) } diff --git a/app/static/style.css b/app/static/style.css index be2db44..26d24a8 100644 --- a/app/static/style.css +++ b/app/static/style.css @@ -29,4 +29,8 @@ html.dark .nav-link { .nav-link { color: #212529 +} + +.avatar { + border-radius: 50%; } \ No newline at end of file