Add support for user-defined avatars

Fixes: https://gitlab.gnome.org/GNOME/fractal/-/issues/785
This commit is contained in:
Julian Sparber 2021-06-01 17:14:28 +02:00
parent a4d7f87bac
commit 2adec1644b
18 changed files with 476 additions and 34 deletions

View file

@ -23,6 +23,7 @@
<file compressed="true" preprocess="xml-stripblanks" alias="pill.ui">ui/pill.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="spinner-button.ui">ui/spinner-button.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="in-app-notification.ui">ui/in-app-notification.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="components-avatar.ui">ui/components-avatar.ui</file>
<file compressed="true">style.css</file>
<file preprocess="xml-stripblanks">icons/scalable/actions/send-symbolic.svg</file>
<file preprocess="xml-stripblanks">icons/scalable/status/welcome.svg</file>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="ComponentsAvatar" parent="AdwBin">
<property name="child">
<object class="AdwAvatar" id="avatar">
<property name="show-initials">True</property>
<binding name="text">
<lookup name="display-name" type="Room">
<lookup name="item">ComponentsAvatar</lookup>
</lookup>
</binding>
<binding name="text">
<lookup name="display-name" type="User">
<lookup name="item">ComponentsAvatar</lookup>
</lookup>
</binding>
<binding name="custom-image">
<lookup name="image" type="Avatar">
<lookup name="avatar" type="Room">
<lookup name="item">ComponentsAvatar</lookup>
</lookup>
</lookup>
</binding>
<binding name="custom-image">
<lookup name="image" type="Avatar">
<lookup name="avatar" type="User">
<lookup name="item">ComponentsAvatar</lookup>
</lookup>
</lookup>
</binding>
</object>
</property>
</template>
</interface>

View file

@ -46,10 +46,11 @@
<property name="label" translatable="yes">Invite</property>
</accessibility>
<child>
<object class="AdwAvatar">
<property name="show-initials">True</property>
<object class="ComponentsAvatar">
<property name="size">150</property>
<property name="text" bind-source="display_name" bind-property="label" bind-flags="sync-create"/>
<binding name="item">
<lookup name="room">ContentInvite</lookup>
</binding>
</object>
</child>
<child>

View file

@ -5,11 +5,9 @@
<object class="GtkBox">
<property name="spacing">10</property>
<child>
<object class="AdwAvatar" id="avatar">
<property name="show-initials">True</property>
<object class="ComponentsAvatar" id="avatar">
<property name="size">36</property>
<property name="valign">start</property>
<property name="text" bind-source="display_name" bind-property="label" bind-flags="sync-create"/>
</object>
</child>
<child>
@ -55,3 +53,4 @@
</child>
</template>
</interface>

View file

@ -10,10 +10,8 @@
<object class="GtkBox">
<property name="spacing">6</property>
<child>
<object class="AdwAvatar" id="avatar">
<property name="show-initials">True</property>
<object class="ComponentsAvatar" id="avatar">
<property name="size">24</property>
<property name="text" bind-source="display_name" bind-property="label" bind-flags="sync-create" />
</object>
</child>
<child>

View file

@ -5,10 +5,8 @@
<object class="GtkBox">
<property name="spacing">12</property>
<child>
<object class="AdwAvatar" id="avatar">
<property name="show-initials">True</property>
<object class="ComponentsAvatar" id="avatar">
<property name="size">24</property>
<property name="text" bind-source="display_name" bind-property="label" bind-flags="sync-create" />
</object>
</child>
<child>
@ -31,3 +29,4 @@
</child>
</template>
</interface>

View file

@ -5,6 +5,7 @@ data/org.gnome.FractalNext.gschema.xml.in
data/org.gnome.FractalNext.metainfo.xml.in.in
# UI files
data/resources/ui/components-avatar.ui
data/resources/ui/content-divider-row.ui
data/resources/ui/content-item-row-menu.ui
data/resources/ui/content-item.ui
@ -30,6 +31,7 @@ data/resources/ui/window.ui
# Rust files
src/application.rs
src/components/avatar.rs
src/components/context_menu_bin.rs
src/components/label_with_widgets.rs
src/components/in_app_notification.rs

155
src/components/avatar.rs Normal file
View file

@ -0,0 +1,155 @@
use adw::subclass::prelude::*;
use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*, CompositeTemplate};
use crate::session::{Room, User};
mod imp {
use super::*;
use glib::subclass::InitializingObject;
use std::cell::RefCell;
#[derive(Debug, Default, CompositeTemplate)]
#[template(resource = "/org/gnome/FractalNext/components-avatar.ui")]
pub struct Avatar {
/// A `Room` or `User`
pub item: RefCell<Option<glib::Object>>,
#[template_child]
pub avatar: TemplateChild<adw::Avatar>,
}
#[glib::object_subclass]
impl ObjectSubclass for Avatar {
const NAME: &'static str = "ComponentsAvatar";
type Type = super::Avatar;
type ParentType = adw::Bin;
fn class_init(klass: &mut Self::Class) {
Self::bind_template(klass);
}
fn instance_init(obj: &InitializingObject<Self>) {
obj.init_template();
}
}
impl ObjectImpl for Avatar {
fn properties() -> &'static [glib::ParamSpec] {
use once_cell::sync::Lazy;
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
vec![
glib::ParamSpec::new_object(
"item",
"Item",
"The Room or User of this Avatar",
glib::Object::static_type(),
glib::ParamFlags::READWRITE | glib::ParamFlags::EXPLICIT_NOTIFY,
),
glib::ParamSpec::new_int(
"size",
"Size",
"The size of the Avatar",
-1,
i32::MAX,
-1,
glib::ParamFlags::READWRITE,
),
]
});
PROPERTIES.as_ref()
}
fn set_property(
&self,
obj: &Self::Type,
_id: usize,
value: &glib::Value,
pspec: &glib::ParamSpec,
) {
match pspec.name() {
"item" => obj.set_item(value.get().unwrap()),
"size" => self.avatar.set_size(value.get().unwrap()),
_ => unimplemented!(),
}
}
fn property(&self, obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
match pspec.name() {
"item" => obj.item().to_value(),
"size" => self.avatar.size().to_value(),
_ => unimplemented!(),
}
}
fn constructed(&self, obj: &Self::Type) {
self.parent_constructed(obj);
obj.connect_map(clone!(@weak obj => move |_| {
obj.request_custom_avatar();
}));
}
}
impl WidgetImpl for Avatar {}
impl BinImpl for Avatar {}
}
glib::wrapper! {
pub struct Avatar(ObjectSubclass<imp::Avatar>)
@extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
}
/// A widget displaying an `Avatar` for a `Room` or `User`
impl Avatar {
pub fn new() -> Self {
glib::Object::new(&[]).expect("Failed to create Avatar")
}
pub fn set_room(&self, room: Option<Room>) {
self.set_item(room.map(glib::object::Cast::upcast));
}
pub fn room(&self) -> Option<Room> {
self.item().and_then(|item| item.downcast().ok())
}
pub fn set_user(&self, user: Option<User>) {
self.set_item(user.map(glib::object::Cast::upcast));
}
pub fn user(&self) -> Option<User> {
self.item().and_then(|item| item.downcast().ok())
}
fn set_item(&self, item: Option<glib::Object>) {
let priv_ = imp::Avatar::from_instance(self);
if *priv_.item.borrow() == item {
return;
}
priv_.item.replace(item);
if self.is_mapped() {
self.request_custom_avatar();
}
self.notify("item");
}
fn item(&self) -> Option<glib::Object> {
let priv_ = imp::Avatar::from_instance(self);
priv_.item.borrow().clone()
}
fn request_custom_avatar(&self) {
let priv_ = imp::Avatar::from_instance(self);
if let Some(item) = &*priv_.item.borrow() {
if let Some(room) = item.downcast_ref::<Room>() {
room.avatar().set_needed(true);
} else if let Some(user) = item.downcast_ref::<User>() {
user.avatar().set_needed(true);
}
}
}
}

View file

@ -1,9 +1,11 @@
mod avatar;
mod context_menu_bin;
mod in_app_notification;
mod label_with_widgets;
mod pill;
mod spinner_button;
pub use self::avatar::Avatar;
pub use self::context_menu_bin::{ContextMenuBin, ContextMenuBinExt, ContextMenuBinImpl};
pub use self::in_app_notification::InAppNotification;
pub use self::label_with_widgets::LabelWithWidgets;

View file

@ -1,3 +1,4 @@
use crate::components::Avatar;
use adw::subclass::prelude::*;
use gtk::prelude::*;
use gtk::subclass::prelude::*;
@ -20,7 +21,7 @@ mod imp {
#[template_child]
pub display_name: TemplateChild<gtk::Label>,
#[template_child]
pub avatar: TemplateChild<adw::Avatar>,
pub avatar: TemplateChild<Avatar>,
pub bindings: RefCell<Vec<glib::Binding>>,
}
@ -124,6 +125,7 @@ impl Pill {
priv_.bindings.borrow_mut().push(display_name_binding);
}
priv_.avatar.set_user(user.clone());
priv_.user.replace(user);
self.notify("user");
@ -155,6 +157,7 @@ impl Pill {
priv_.bindings.borrow_mut().push(display_name_binding);
}
priv_.avatar.set_room(room.clone());
priv_.room.replace(room);
self.notify("room");

View file

@ -20,6 +20,7 @@ run_command(
sources = files(
'application.rs',
'components/avatar.rs',
'components/context_menu_bin.rs',
'components/label_with_widgets.rs',
'components/mod.rs',
@ -33,6 +34,7 @@ sources = files(
'login.rs',
'secret.rs',
'utils.rs',
'session/avatar.rs',
'session/event_source_dialog.rs',
'session/user.rs',
'session/mod.rs',

211
src/session/avatar.rs Normal file
View file

@ -0,0 +1,211 @@
use gtk::{gdk, gdk_pixbuf::Pixbuf, gio, glib, glib::clone, prelude::*, subclass::prelude::*};
use log::error;
use matrix_sdk::{
identifiers::MxcUri,
media::{MediaFormat, MediaRequest, MediaType},
};
use crate::utils::do_async;
use crate::session::Session;
mod imp {
use super::*;
use once_cell::sync::{Lazy, OnceCell};
use std::cell::{Cell, RefCell};
#[derive(Debug, Default)]
pub struct Avatar {
pub image: RefCell<Option<gdk::Paintable>>,
pub needed: Cell<bool>,
pub url: RefCell<Option<MxcUri>>,
pub session: OnceCell<Session>,
}
#[glib::object_subclass]
impl ObjectSubclass for Avatar {
const NAME: &'static str = "Avatar";
type Type = super::Avatar;
type ParentType = glib::Object;
}
impl ObjectImpl for Avatar {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
vec![
glib::ParamSpec::new_object(
"image",
"Image",
"The user defined image if any",
gdk::Paintable::static_type(),
glib::ParamFlags::READABLE | glib::ParamFlags::EXPLICIT_NOTIFY,
),
glib::ParamSpec::new_object(
"needed",
"Needed",
"Whether the user defnied image should be loaded or it's not needed",
gdk::Paintable::static_type(),
glib::ParamFlags::READWRITE | glib::ParamFlags::EXPLICIT_NOTIFY,
),
glib::ParamSpec::new_string(
"url",
"Url",
"The url of the Avatar",
None,
glib::ParamFlags::READWRITE | glib::ParamFlags::EXPLICIT_NOTIFY,
),
glib::ParamSpec::new_object(
"session",
"Session",
"The session",
Session::static_type(),
glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT_ONLY,
),
]
});
PROPERTIES.as_ref()
}
fn set_property(
&self,
obj: &Self::Type,
_id: usize,
value: &glib::Value,
pspec: &glib::ParamSpec,
) {
match pspec.name() {
"needed" => obj.set_needed(value.get().unwrap()),
"url" => obj.set_url(value.get::<Option<&str>>().unwrap().map(Into::into)),
"session" => self.session.set(value.get().unwrap()).unwrap(),
_ => unimplemented!(),
}
}
fn property(&self, obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
match pspec.name() {
"image" => obj.image().to_value(),
"needed" => obj.needed().to_value(),
"url" => obj.url().map_or_else(
|| {
let none: Option<&str> = None;
none.to_value()
},
|url| url.as_str().to_value(),
),
_ => unimplemented!(),
}
}
}
}
glib::wrapper! {
pub struct Avatar(ObjectSubclass<imp::Avatar>);
}
/// This an object that holds information about a Users or Rooms `Avatar`
impl Avatar {
pub fn new(session: &Session, url: Option<MxcUri>) -> Self {
glib::Object::new(&[
("session", session),
("url", &url.map(|url| url.to_string())),
])
.expect("Failed to create Avatar")
}
fn session(&self) -> &Session {
let priv_ = imp::Avatar::from_instance(self);
priv_.session.get().unwrap()
}
pub fn image(&self) -> Option<gdk::Paintable> {
let priv_ = imp::Avatar::from_instance(self);
priv_.image.borrow().clone()
}
fn set_image_data(&self, data: Option<Vec<u8>>) {
let priv_ = imp::Avatar::from_instance(self);
let image = if let Some(data) = data {
let stream = gio::MemoryInputStream::from_bytes(&glib::Bytes::from(&data));
Pixbuf::from_stream(&stream, gio::NONE_CANCELLABLE)
.ok()
.and_then(|pixbuf| Some(gdk::Texture::for_pixbuf(&pixbuf).upcast()))
} else {
None
};
priv_.image.replace(image);
self.notify("image");
}
fn load(&self) {
// Don't do anything here if we don't need the avatar
if !self.needed() {
return;
}
if let Some(url) = self.url() {
let client = self.session().client().clone();
let request = MediaRequest {
media_type: MediaType::Uri(url),
format: MediaFormat::File,
};
do_async(
glib::PRIORITY_LOW,
async move { client.get_media_content(&request, true).await },
clone!(@weak self as obj => move |result| async move {
// FIXME: We should retry if the request failed
match result {
Ok(data) => obj.set_image_data(Some(data)),
Err(error) => error!("Couldn't fetch avatar: {}", error),
};
}),
);
}
}
pub fn set_needed(&self, needed: bool) {
let priv_ = imp::Avatar::from_instance(self);
if self.needed() == needed {
return;
}
priv_.needed.set(needed);
if needed {
self.load();
}
self.notify("needed");
}
pub fn needed(&self) -> bool {
let priv_ = imp::Avatar::from_instance(self);
priv_.needed.get()
}
pub fn set_url(&self, url: Option<MxcUri>) {
let priv_ = imp::Avatar::from_instance(self);
if priv_.url.borrow().as_ref() == url.as_ref() {
return;
}
let has_url = url.is_some();
priv_.url.replace(url);
if has_url {
self.load();
} else {
self.set_image_data(None);
}
self.notify("url");
}
pub fn url(&self) -> Option<MxcUri> {
let priv_ = imp::Avatar::from_instance(self);
priv_.url.borrow().to_owned()
}
}

View file

@ -1,5 +1,5 @@
use crate::{
components::{LabelWithWidgets, Pill, SpinnerButton},
components::{Avatar, LabelWithWidgets, Pill, SpinnerButton},
session::{categories::CategoryType, room::Room},
};
use adw::subclass::prelude::*;
@ -40,6 +40,7 @@ mod imp {
Pill::static_type();
SpinnerButton::static_type();
LabelWithWidgets::static_type();
Avatar::static_type();
Self::bind_template(klass);
klass.set_accessible_role(gtk::AccessibleRole::Group);

View file

@ -1,3 +1,4 @@
use crate::components::Avatar;
use adw::{prelude::*, subclass::prelude::*};
use gtk::{
gio, glib, glib::clone, glib::signal::SignalHandlerId, prelude::*, subclass::prelude::*,
@ -28,7 +29,7 @@ mod imp {
#[template(resource = "/org/gnome/FractalNext/content-message-row.ui")]
pub struct MessageRow {
#[template_child]
pub avatar: TemplateChild<adw::Avatar>,
pub avatar: TemplateChild<Avatar>,
#[template_child]
pub header: TemplateChild<gtk::Box>,
#[template_child]
@ -49,6 +50,7 @@ mod imp {
type ParentType = adw::Bin;
fn class_init(klass: &mut Self::Class) {
Avatar::static_type();
Self::bind_template(klass);
}
@ -147,7 +149,8 @@ impl MessageRow {
}
}
//TODO: bind the user's avatar to the message row
priv_.avatar.set_user(Some(event.sender().clone()));
let display_name_binding = event
.sender()
.bind_property("display-name", &priv_.display_name.get(), "label")

View file

@ -1,3 +1,4 @@
mod avatar;
mod categories;
mod content;
mod event_source_dialog;
@ -6,6 +7,7 @@ mod room_list;
mod sidebar;
mod user;
pub use self::avatar::Avatar;
use self::categories::Categories;
use self::content::Content;
pub use self::room::Room;

View file

@ -1,5 +1,5 @@
use gettextrs::gettext;
use gtk::{gio, glib, glib::clone, prelude::*, subclass::prelude::*};
use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*};
use log::{debug, error, warn};
use matrix_sdk::{
api::r0::sync::sync_events::InvitedRoom,
@ -29,7 +29,7 @@ use crate::event_from_sync_event;
use crate::session::{
categories::CategoryType,
room::{HighlightFlags, Timeline},
Session, User,
Avatar, Session, User,
};
use crate::utils::do_async;
use crate::Error;
@ -47,7 +47,7 @@ mod imp {
pub matrix_room: RefCell<Option<MatrixRoom>>,
pub session: OnceCell<Session>,
pub name: RefCell<Option<String>>,
pub avatar: RefCell<Option<gio::LoadableIcon>>,
pub avatar: OnceCell<Avatar>,
pub category: Cell<CategoryType>,
pub timeline: OnceCell<Timeline>,
pub room_members: RefCell<HashMap<UserId, User>>,
@ -97,8 +97,8 @@ mod imp {
glib::ParamSpec::new_object(
"avatar",
"Avatar",
"The url of the avatar of this room",
gio::LoadableIcon::static_type(),
"The Avatar of this room",
Avatar::static_type(),
glib::ParamFlags::READABLE,
),
glib::ParamSpec::new_object(
@ -161,7 +161,7 @@ mod imp {
}
"room-id" => self
.room_id
.set(RoomId::try_from(value.get::<String>().unwrap()).unwrap())
.set(RoomId::try_from(value.get::<&str>().unwrap()).unwrap())
.unwrap(),
_ => unimplemented!(),
}
@ -175,7 +175,7 @@ mod imp {
"session" => obj.session().to_value(),
"inviter" => obj.inviter().to_value(),
"display-name" => obj.display_name().to_value(),
"avatar" => self.avatar.borrow().to_value(),
"avatar" => obj.avatar().to_value(),
"timeline" => self.timeline.get().unwrap().to_value(),
"category" => obj.category().to_value(),
"highlight" => obj.highlight().to_value(),
@ -200,6 +200,9 @@ mod imp {
obj.set_matrix_room(obj.session().client().get_room(obj.room_id()).unwrap());
self.timeline.set(Timeline::new(obj)).unwrap();
self.avatar
.set(Avatar::new(obj.session(), obj.matrix_room().avatar_url()))
.unwrap();
}
}
}
@ -461,6 +464,11 @@ impl Room {
);
}
pub fn avatar(&self) -> &Avatar {
let priv_ = imp::Room::from_instance(&self);
priv_.avatar.get().unwrap()
}
pub fn topic(&self) -> Option<String> {
self.matrix_room()
.topic()
@ -533,6 +541,9 @@ impl Room {
AnyRoomEvent::State(AnyStateEvent::RoomMember(ref event)) => {
self.update_member_for_member_event(event)
}
AnyRoomEvent::State(AnyStateEvent::RoomAvatar(event)) => {
self.avatar().set_url(event.content.url.to_owned());
}
AnyRoomEvent::State(AnyStateEvent::RoomName(_)) => {
// FIXME: this doesn't take in account changes in the calculated name
self.load_display_name()

View file

@ -1,3 +1,4 @@
use crate::components::Avatar;
use adw::subclass::prelude::BinImpl;
use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*, CompositeTemplate};
@ -16,7 +17,7 @@ mod imp {
pub bindings: RefCell<Vec<glib::Binding>>,
pub signal_handler: RefCell<Option<SignalHandlerId>>,
#[template_child]
pub avatar: TemplateChild<adw::Avatar>,
pub avatar: TemplateChild<Avatar>,
#[template_child]
pub display_name: TemplateChild<gtk::Label>,
#[template_child]
@ -30,6 +31,7 @@ mod imp {
type ParentType = adw::Bin;
fn class_init(klass: &mut Self::Class) {
Avatar::static_type();
Self::bind_template(klass);
}
@ -115,8 +117,6 @@ impl RoomRow {
}
if let Some(ref room) = room {
// TODO: set custom avatar https://gitlab.gnome.org/exalm/libadwaita/-/issues/29
let display_name_binding = room
.bind_property("display-name", &priv_.display_name.get(), "label")
.flags(glib::BindingFlags::SYNC_CREATE)
@ -158,7 +158,7 @@ impl RoomRow {
notification_count_vislbe_binding,
]);
}
priv_.avatar.set_room(room.clone());
priv_.room.replace(room);
self.notify("room");
}

View file

@ -1,4 +1,4 @@
use gtk::{gio, glib, prelude::*, subclass::prelude::*};
use gtk::{glib, prelude::*, subclass::prelude::*};
use crate::session::Session;
use matrix_sdk::{
@ -7,6 +7,8 @@ use matrix_sdk::{
RoomMember,
};
use crate::session::Avatar;
mod imp {
use super::*;
use once_cell::sync::{Lazy, OnceCell};
@ -16,8 +18,8 @@ mod imp {
pub struct User {
pub user_id: OnceCell<String>,
pub display_name: RefCell<Option<String>>,
pub avatar: RefCell<Option<gio::LoadableIcon>>,
pub session: OnceCell<Session>,
pub avatar: OnceCell<Avatar>,
}
#[glib::object_subclass]
@ -49,7 +51,7 @@ mod imp {
"avatar",
"Avatar",
"The avatar of this user",
gio::LoadableIcon::static_type(),
Avatar::static_type(),
glib::ParamFlags::READABLE,
),
glib::ParamSpec::new_object(
@ -86,11 +88,18 @@ mod imp {
match pspec.name() {
"display-name" => obj.display_name().to_value(),
"user-id" => self.user_id.get().to_value(),
"avatar" => self.avatar.borrow().to_value(),
"session" => obj.session().to_value(),
"avatar" => obj.avatar().to_value(),
_ => unimplemented!(),
}
}
fn constructed(&self, obj: &Self::Type) {
self.parent_constructed(obj);
let avatar = Avatar::new(obj.session(), None);
self.avatar.set(avatar).unwrap();
}
}
}
@ -131,11 +140,16 @@ impl User {
}
}
pub fn avatar(&self) -> &Avatar {
let priv_ = imp::User::from_instance(&self);
priv_.avatar.get().unwrap()
}
/// Update the user based on the the room member state event
//TODO: create the GLoadableIcon and set `avatar`
pub fn update_from_room_member(&self, member: &RoomMember) {
let changed = {
let priv_ = imp::User::from_instance(&self);
let user_id = priv_.user_id.get().unwrap();
if member.user_id().as_str() != user_id {
return;
@ -143,6 +157,7 @@ impl User {
//let content = event.content;
let display_name = member.display_name().map(|name| name.to_owned());
self.avatar().set_url(member.avatar_url().cloned());
let mut current_display_name = priv_.display_name.borrow_mut();
if *current_display_name != display_name {
@ -159,7 +174,6 @@ impl User {
}
/// Update the user based on the the room member state event
//TODO: create the GLoadableIcon and set `avatar`
pub fn update_from_member_event(&self, event: &StateEvent<MemberEventContent>) {
let changed = {
let priv_ = imp::User::from_instance(&self);
@ -178,6 +192,8 @@ impl User {
.map(|i| i.display_name.to_owned())
};
self.avatar().set_url(event.content.avatar_url.to_owned());
let mut current_display_name = priv_.display_name.borrow_mut();
if *current_display_name != display_name {
*current_display_name = display_name;
@ -193,7 +209,6 @@ impl User {
}
/// Update the user based on the the stripped room member state event
//TODO: create the GLoadableIcon and set `avatar`
pub fn update_from_stripped_member_event(
&self,
event: &StrippedStateEvent<MemberEventContent>,
@ -215,6 +230,8 @@ impl User {
.map(|i| i.display_name.to_owned())
};
self.avatar().set_url(event.content.avatar_url.to_owned());
let mut current_display_name = priv_.display_name.borrow_mut();
if *current_display_name != display_name {
*current_display_name = display_name;