This commit is contained in:
Austin 2023-03-22 20:08:43 +00:00
parent 970af4f01a
commit 590825fe73
7 changed files with 110 additions and 38 deletions

26
Cargo.lock generated
View File

@ -352,6 +352,12 @@ version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
[[package]]
name = "anymap2"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c"
[[package]]
name = "arrayref"
version = "0.3.6"
@ -2434,6 +2440,7 @@ dependencies = [
"wasm-bindgen-futures",
"web-sys",
"yew",
"yew-agent",
"yew-router",
"yew_form",
"yew_form_derive",
@ -4798,6 +4805,25 @@ dependencies = [
"yew-macro",
]
[[package]]
name = "yew-agent"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "616700dc3851945658c44ba4477ede6b77c795462fbbb9b0ad9a8b6273a3ca77"
dependencies = [
"anymap2",
"bincode",
"gloo-console",
"gloo-utils",
"js-sys",
"serde",
"slab",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"yew",
]
[[package]]
name = "yew-macro"
version = "0.19.3"

View File

@ -24,6 +24,7 @@ wasm-bindgen = "0.2"
wasm-bindgen-futures = "*"
yew = "0.19.3"
yew-router = "0.16"
yew-agent = "0.1.0"
# Needed because of https://github.com/tkaitchuck/aHash/issues/95
indexmap = "=1.6.2"

View File

@ -350,15 +350,7 @@ impl App {
id="dropdownUser"
data-bs-toggle="dropdown"
aria-expanded="false">
<svg xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
fill="currentColor"
class="bi bi-person-circle"
viewBox="0 0 16 16">
<path d="M11 6a3 3 0 1 1-6 0 3 3 0 0 1 6 0z"/>
<path fill-rule="evenodd" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.757 1.225 5.468 2.37A7 7 0 0 0 8 1z"/>
</svg>
<Avatar username={user_id.clone()} width=32 height=32 />
<span class="ms-2">
{user_id}
</span>

View File

@ -1,7 +1,10 @@
use crate::infra::common_component::{CommonComponent, CommonComponentParts};
use crate::{
components::avatar_event_bus::AvatarEventBus,
infra::common_component::{CommonComponent, CommonComponentParts}};
use anyhow::{bail, Result};
use graphql_client::GraphQLQuery;
use yew::prelude::*;
use yew::{prelude::*};
use yew_agent::{Bridge, Bridged};
#[derive(GraphQLQuery)]
#[graphql(
@ -16,7 +19,8 @@ pub struct Avatar {
common: CommonComponentParts<Self>,
/// 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<String>,
avatar: Option<String>,
_producer: Box<dyn Bridge<AvatarEventBus>>,
}
/// State machine describing the possible transitions of the component state.
@ -35,16 +39,16 @@ pub struct Props {
}
impl CommonComponent<Avatar> for Avatar {
fn handle_msg(&mut 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 {
Msg::UserAvatarResponse(response) => match response {
Ok(user) => self.avatar = user.user.avatar,
Err(e) => {
self.avatar = None;
bail!("Error getting user details: {}", e);
bail!("Error getting user avatar: {}", e);
}
},
Msg::Update => self.get_user_avatar(),
Msg::Update => self.get_user_avatar(ctx),
}
Ok(true)
}
@ -55,16 +59,15 @@ impl CommonComponent<Avatar> for Avatar {
}
impl Avatar {
fn get_user_avatar(&mut self) {
if self.common.username.len() > 0 {
self.common.call_graphql::<GetUserAvatar, _>(
get_user_avatar::Variables {
id: self.common.username.clone(),
},
Msg::UserAvatarResponse,
"Error trying to fetch user avatar",
)
}
fn get_user_avatar(&mut self, ctx: &Context<Self>) {
self.common.call_graphql::<GetUserAvatar, _>(
ctx,
get_user_avatar::Variables {
id: ctx.props().username.clone(),
},
Msg::UserAvatarResponse,
"Error trying to fetch user avatar",
)
}
}
@ -72,36 +75,33 @@ impl Component for Avatar {
type Message = Msg;
type Properties = Props;
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
let mut avatar = Self {
common: CommonComponentParts::<Self>::create(props, link),
common: CommonComponentParts::<Self>::create(),
avatar: None,
_producer: AvatarEventBus::bridge(ctx.link().callback(|_| Msg::Update)),
};
avatar.get_user_avatar();
avatar.get_user_avatar(ctx);
avatar
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
CommonComponentParts::<Self>::update(self, msg)
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
CommonComponentParts::<Self>::update(self, ctx, msg)
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
self.common.change(props)
}
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
match &self.avatar {
Some(avatar) => html! {
<img
id="avatarDisplay"
src={format!("data:image/jpeg;base64, {}", avatar)}
style={format!("max-height:{}px;max-width:{}px;height:auto;width:auto;", self.common.props.height,self.common.props.width)}
style={format!("max-height:{}px;max-width:{}px;height:auto;width:auto;", ctx.props().height,ctx.props().width)}
alt="Avatar" />
},
None => html! {
<svg xmlns="http://www.w3.org/2000/svg"
width={self.common.props.width.to_string()}
height={self.common.props.height.to_string()}
width={ctx.props().width.to_string()}
height={ctx.props().height.to_string()}
fill="currentColor"
class="bi bi-person-circle"
viewBox="0 0 16 16">

View File

@ -0,0 +1,47 @@
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use yew_agent::{Agent, AgentLink, Context, HandlerId};
#[derive(Serialize, Deserialize, Debug)]
pub enum Request {
Update,
}
pub struct AvatarEventBus {
link: AgentLink<AvatarEventBus>,
subscribers: HashSet<HandlerId>,
}
impl Agent for AvatarEventBus {
type Reach = Context<Self>;
type Message = ();
type Input = Request;
type Output = ();
fn create(link: AgentLink<Self>) -> Self {
Self {
link,
subscribers: HashSet::new(),
}
}
fn update(&mut self, _msg: Self::Message) {}
fn handle_input(&mut self, msg: Self::Input, _id: HandlerId) {
match msg {
Request::Update => {
for sub in self.subscribers.iter() {
self.link.respond(*sub, ());
}
}
}
}
fn connected(&mut self, id: HandlerId) {
self.subscribers.insert(id);
}
fn disconnected(&mut self, id: HandlerId) {
self.subscribers.remove(&id);
}
}

View File

@ -2,6 +2,7 @@ pub mod add_group_member;
pub mod add_user_to_group;
pub mod app;
pub mod avatar;
pub mod avatar_event_bus;
pub mod change_password;
pub mod create_group;
pub mod create_user;

View File

@ -2,6 +2,7 @@ use std::str::FromStr;
use crate::{
components::user_details::User,
components::avatar_event_bus::{AvatarEventBus, Request},
infra::common_component::{CommonComponent, CommonComponentParts},
};
use anyhow::{bail, Error, Result};
@ -14,6 +15,7 @@ use validator_derive::Validate;
use web_sys::{FileList, HtmlInputElement, InputEvent};
use yew::prelude::*;
use yew_form_derive::Model;
use yew_agent::{Dispatched, Dispatcher};
#[derive(Default)]
struct JsFile {
@ -72,6 +74,7 @@ pub struct UserDetailsForm {
/// True if we just successfully updated the user, to display a success message.
just_updated: bool,
user: User,
avatar_event_bus: Dispatcher<AvatarEventBus>,
}
pub enum Msg {
@ -163,6 +166,7 @@ impl Component for UserDetailsForm {
just_updated: false,
reader: None,
user: ctx.props().user.clone(),
avatar_event_bus: AvatarEventBus::dispatcher(),
}
}
@ -402,6 +406,7 @@ impl UserDetailsForm {
self.user.avatar = Some(avatar);
}
self.just_updated = true;
self.avatar_event_bus.send(Request::Update);
Ok(true)
}