Review feedback plus avatar rounding

This commit is contained in:
Austin 2023-03-23 19:29:46 +00:00
parent d60d869eba
commit d509dc82a0
4 changed files with 59 additions and 21 deletions

View File

@ -1,9 +1,11 @@
use crate::{ use crate::{
components::avatar_event_bus::AvatarEventBus, components::avatar_event_bus::{AvatarEventBus, Response},
infra::common_component::{CommonComponent, CommonComponentParts}}; infra::common_component::{CommonComponent, CommonComponentParts},
};
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use graphql_client::GraphQLQuery; use graphql_client::GraphQLQuery;
use yew::{prelude::*}; use serde::{Deserialize, Serialize};
use yew::prelude::*;
use yew_agent::{Bridge, Bridged}; use yew_agent::{Bridge, Bridged};
#[derive(GraphQLQuery)] #[derive(GraphQLQuery)]
@ -15,11 +17,18 @@ use yew_agent::{Bridge, Bridged};
)] )]
pub struct GetUserAvatar; 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 { pub struct Avatar {
common: CommonComponentParts<Self>, common: CommonComponentParts<Self>,
/// The user info. If none, the error is in `error`. If `error` is None, then we haven't avatar: Option<AvatarData>,
/// received the server response yet.
avatar: Option<String>,
_producer: Box<dyn Bridge<AvatarEventBus>>, _producer: Box<dyn Bridge<AvatarEventBus>>,
} }
@ -28,7 +37,7 @@ pub struct Avatar {
pub enum Msg { pub enum Msg {
/// Received the user details response, either the user data or an error. /// Received the user details response, either the user data or an error.
UserAvatarResponse(Result<get_user_avatar::ResponseData>), UserAvatarResponse(Result<get_user_avatar::ResponseData>),
Update Update(Response),
} }
#[derive(yew::Properties, Clone, PartialEq, Eq)] #[derive(yew::Properties, Clone, PartialEq, Eq)]
@ -39,16 +48,26 @@ pub struct Props {
} }
impl CommonComponent<Avatar> for Avatar { impl CommonComponent<Avatar> for Avatar {
fn handle_msg(&mut self, ctx: &Context<Self>, msg: <Self as Component>::Message) -> Result<bool> { fn handle_msg(
&mut self,
ctx: &Context<Self>,
msg: <Self as Component>::Message,
) -> Result<bool> {
match msg { match msg {
Msg::UserAvatarResponse(response) => match response { Msg::UserAvatarResponse(response) => match response {
Ok(user) => self.avatar = user.user.avatar, Ok(user) => self.avatar = user.user.avatar.map(AvatarData::new),
Err(e) => { Err(e) => {
self.avatar = None; self.avatar = None;
bail!("Error getting user avatar: {}", e); 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) Ok(true)
} }
@ -79,7 +98,7 @@ impl Component for Avatar {
let mut avatar = Self { let mut avatar = Self {
common: CommonComponentParts::<Self>::create(), common: CommonComponentParts::<Self>::create(),
avatar: None, avatar: None,
_producer: AvatarEventBus::bridge(ctx.link().callback(|_| Msg::Update)), _producer: AvatarEventBus::bridge(ctx.link().callback(Msg::Update)),
}; };
avatar.get_user_avatar(ctx); avatar.get_user_avatar(ctx);
avatar avatar
@ -93,8 +112,8 @@ impl Component for Avatar {
match &self.avatar { match &self.avatar {
Some(avatar) => html! { Some(avatar) => html! {
<img <img
id="avatarDisplay" class="avatar"
src={format!("data:image/jpeg;base64, {}", avatar)} src={format!("data:image/jpeg;base64, {}", avatar.0)}
style={format!("max-height:{}px;max-width:{}px;height:auto;width:auto;", ctx.props().height,ctx.props().width)} style={format!("max-height:{}px;max-width:{}px;height:auto;width:auto;", ctx.props().height,ctx.props().width)}
alt="Avatar" /> alt="Avatar" />
}, },

View File

@ -2,9 +2,16 @@ use serde::{Deserialize, Serialize};
use std::collections::HashSet; use std::collections::HashSet;
use yew_agent::{Agent, AgentLink, Context, HandlerId}; use yew_agent::{Agent, AgentLink, Context, HandlerId};
use super::avatar::AvatarData;
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub enum Request { pub enum Request {
Update, Update((String, Option<AvatarData>)),
}
#[derive(Serialize, Deserialize, Debug)]
pub enum Response {
Update((String, Option<AvatarData>)),
} }
pub struct AvatarEventBus { pub struct AvatarEventBus {
@ -16,7 +23,7 @@ impl Agent for AvatarEventBus {
type Reach = Context<Self>; type Reach = Context<Self>;
type Message = (); type Message = ();
type Input = Request; type Input = Request;
type Output = (); type Output = Response;
fn create(link: AgentLink<Self>) -> Self { fn create(link: AgentLink<Self>) -> Self {
Self { Self {
@ -29,9 +36,10 @@ impl Agent for AvatarEventBus {
fn handle_input(&mut self, msg: Self::Input, _id: HandlerId) { fn handle_input(&mut self, msg: Self::Input, _id: HandlerId) {
match msg { match msg {
Request::Update => { Request::Update((username, avatar)) => {
for sub in self.subscribers.iter() { 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) { fn disconnected(&mut self, id: HandlerId) {
self.subscribers.remove(&id); self.subscribers.remove(&id);
} }
} }

View File

@ -1,8 +1,9 @@
use std::str::FromStr; use std::str::FromStr;
use crate::{ use crate::{
components::user_details::User, components::avatar::AvatarData,
components::avatar_event_bus::{AvatarEventBus, Request}, components::avatar_event_bus::{AvatarEventBus, Request},
components::user_details::User,
infra::common_component::{CommonComponent, CommonComponentParts}, infra::common_component::{CommonComponent, CommonComponentParts},
}; };
use anyhow::{bail, Error, Result}; use anyhow::{bail, Error, Result};
@ -14,8 +15,8 @@ use graphql_client::GraphQLQuery;
use validator_derive::Validate; use validator_derive::Validate;
use web_sys::{FileList, HtmlInputElement, InputEvent}; use web_sys::{FileList, HtmlInputElement, InputEvent};
use yew::prelude::*; use yew::prelude::*;
use yew_form_derive::Model;
use yew_agent::{Dispatched, Dispatcher}; use yew_agent::{Dispatched, Dispatcher};
use yew_form_derive::Model;
#[derive(Default)] #[derive(Default)]
struct JsFile { struct JsFile {
@ -406,7 +407,13 @@ impl UserDetailsForm {
self.user.avatar = Some(avatar); self.user.avatar = Some(avatar);
} }
self.just_updated = true; 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) Ok(true)
} }

View File

@ -29,4 +29,8 @@ html.dark .nav-link {
.nav-link { .nav-link {
color: #212529 color: #212529
}
.avatar {
border-radius: 50%;
} }