avatar-data: Port to glib::Properties macro
This commit is contained in:
parent
cd93b65ebe
commit
deaee7393d
|
@ -1,145 +0,0 @@
|
|||
use gtk::{gdk, glib, prelude::*, subclass::prelude::*};
|
||||
use tracing::warn;
|
||||
|
||||
use super::AvatarImage;
|
||||
use crate::{
|
||||
application::Application,
|
||||
utils::notifications::{paintable_as_notification_icon, string_as_notification_icon},
|
||||
};
|
||||
|
||||
mod imp {
|
||||
use std::cell::RefCell;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct AvatarData {
|
||||
/// The data of the user-defined image.
|
||||
pub image: RefCell<Option<AvatarImage>>,
|
||||
/// The display name used as a fallback for this avatar.
|
||||
pub display_name: RefCell<Option<String>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for AvatarData {
|
||||
const NAME: &'static str = "AvatarData";
|
||||
type Type = super::AvatarData;
|
||||
}
|
||||
|
||||
impl ObjectImpl for AvatarData {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
glib::ParamSpecObject::builder::<AvatarImage>("image")
|
||||
.explicit_notify()
|
||||
.build(),
|
||||
glib::ParamSpecString::builder("display-name")
|
||||
.explicit_notify()
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
let obj = self.obj();
|
||||
|
||||
match pspec.name() {
|
||||
"image" => obj.set_image(value.get().unwrap()),
|
||||
"display-name" => obj.set_display_name(value.get().unwrap()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
let obj = self.obj();
|
||||
|
||||
match pspec.name() {
|
||||
"image" => obj.image().to_value(),
|
||||
"display-name" => obj.display_name().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
/// Data about a User’s or Room’s avatar.
|
||||
pub struct AvatarData(ObjectSubclass<imp::AvatarData>);
|
||||
}
|
||||
|
||||
impl AvatarData {
|
||||
/// Construct a new empty `AvatarData`.
|
||||
pub fn new() -> Self {
|
||||
glib::Object::new()
|
||||
}
|
||||
|
||||
/// Constructs an `AvatarData` with the given image data.
|
||||
pub fn with_image(image: AvatarImage) -> Self {
|
||||
glib::Object::builder().property("image", image).build()
|
||||
}
|
||||
|
||||
/// The data of the user-defined image.
|
||||
pub fn image(&self) -> Option<AvatarImage> {
|
||||
self.imp().image.borrow().clone()
|
||||
}
|
||||
|
||||
/// Set the data of the user-defined image.
|
||||
pub fn set_image(&self, image: Option<AvatarImage>) {
|
||||
let imp = self.imp();
|
||||
|
||||
if imp.image.borrow().as_ref() == image.as_ref() {
|
||||
return;
|
||||
}
|
||||
|
||||
imp.image.replace(image);
|
||||
self.notify("image");
|
||||
}
|
||||
|
||||
/// Set the display name used as a fallback for this avatar.
|
||||
pub fn set_display_name(&self, display_name: Option<String>) {
|
||||
let imp = self.imp();
|
||||
|
||||
if imp.display_name.borrow().as_ref() == display_name.as_ref() {
|
||||
return;
|
||||
}
|
||||
|
||||
imp.display_name.replace(display_name);
|
||||
self.notify("display-name");
|
||||
}
|
||||
|
||||
/// The display name used as a fallback for this avatar.
|
||||
pub fn display_name(&self) -> Option<String> {
|
||||
self.imp().display_name.borrow().clone()
|
||||
}
|
||||
|
||||
/// Get this avatar as a notification icon.
|
||||
///
|
||||
/// Returns `None` if an error occurred while generating the icon.
|
||||
pub fn as_notification_icon(&self) -> Option<gdk::Texture> {
|
||||
let window = Application::default().active_window()?.upcast();
|
||||
|
||||
let icon = if let Some(paintable) = self.image().and_then(|i| i.paintable()) {
|
||||
paintable_as_notification_icon(paintable.upcast_ref(), &window)
|
||||
} else {
|
||||
string_as_notification_icon(&self.display_name().unwrap_or_default(), &window)
|
||||
};
|
||||
|
||||
match icon {
|
||||
Ok(icon) => Some(icon),
|
||||
Err(error) => {
|
||||
warn!("Failed to generate icon for notification: {error}");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AvatarData {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
|
@ -1,237 +0,0 @@
|
|||
use gtk::{gdk, glib, glib::clone, prelude::*, subclass::prelude::*};
|
||||
use matrix_sdk::{
|
||||
media::{MediaFormat, MediaRequest, MediaThumbnailSize},
|
||||
ruma::{
|
||||
api::client::media::get_content_thumbnail::v3::Method, events::room::MediaSource, MxcUri,
|
||||
OwnedMxcUri,
|
||||
},
|
||||
};
|
||||
use tracing::error;
|
||||
|
||||
use crate::{components::ImagePaintable, session::model::Session, spawn, spawn_tokio};
|
||||
|
||||
/// The source of an avatar's URI.
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, glib::Enum)]
|
||||
#[repr(u32)]
|
||||
#[enum_type(name = "AvatarUriSource")]
|
||||
pub enum AvatarUriSource {
|
||||
/// The URI comes from a Matrix user.
|
||||
#[default]
|
||||
User = 0,
|
||||
/// The URI comes from a Matrix room.
|
||||
Room = 1,
|
||||
}
|
||||
|
||||
mod imp {
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
use once_cell::sync::{Lazy, OnceCell};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct AvatarImage {
|
||||
pub paintable: RefCell<Option<gdk::Paintable>>,
|
||||
pub needed_size: Cell<u32>,
|
||||
pub uri: RefCell<Option<OwnedMxcUri>>,
|
||||
/// The source of the avatar's URI.
|
||||
pub uri_source: Cell<AvatarUriSource>,
|
||||
pub session: OnceCell<Session>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for AvatarImage {
|
||||
const NAME: &'static str = "AvatarImage";
|
||||
type Type = super::AvatarImage;
|
||||
}
|
||||
|
||||
impl ObjectImpl for AvatarImage {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
glib::ParamSpecObject::builder::<gdk::Paintable>("paintable")
|
||||
.read_only()
|
||||
.build(),
|
||||
glib::ParamSpecUInt::builder("needed-size")
|
||||
.minimum(0)
|
||||
.explicit_notify()
|
||||
.build(),
|
||||
glib::ParamSpecString::builder("uri")
|
||||
.explicit_notify()
|
||||
.build(),
|
||||
glib::ParamSpecEnum::builder::<AvatarUriSource>("uri-source")
|
||||
.construct_only()
|
||||
.build(),
|
||||
glib::ParamSpecObject::builder::<Session>("session")
|
||||
.construct_only()
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
let obj = self.obj();
|
||||
|
||||
match pspec.name() {
|
||||
"needed-size" => obj.set_needed_size(value.get().unwrap()),
|
||||
"uri" => obj.set_uri(value.get::<&str>().ok().map(Into::into)),
|
||||
"uri-source" => obj.set_uri_source(value.get().unwrap()),
|
||||
"session" => {
|
||||
if let Some(session) = value.get().unwrap() {
|
||||
if self.session.set(session).is_err() {
|
||||
error!("Trying to set a session while it is already set");
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
let obj = self.obj();
|
||||
|
||||
match pspec.name() {
|
||||
"paintable" => obj.paintable().to_value(),
|
||||
"needed-size" => obj.needed_size().to_value(),
|
||||
"uri" => obj.uri().map_or_else(
|
||||
|| {
|
||||
let none: Option<&str> = None;
|
||||
none.to_value()
|
||||
},
|
||||
|url| url.as_str().to_value(),
|
||||
),
|
||||
"uri-source" => obj.uri_source().to_value(),
|
||||
"session" => obj.session().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
/// The image data for an avatar.
|
||||
pub struct AvatarImage(ObjectSubclass<imp::AvatarImage>);
|
||||
}
|
||||
|
||||
impl AvatarImage {
|
||||
/// Construct a new `AvatarImage` with the given session and Matrix URI.
|
||||
pub fn new(session: &Session, uri: Option<&MxcUri>, uri_source: AvatarUriSource) -> Self {
|
||||
glib::Object::builder()
|
||||
.property("session", session)
|
||||
.property("uri", uri.map(|uri| uri.to_string()))
|
||||
.property("uri-source", uri_source)
|
||||
.build()
|
||||
}
|
||||
|
||||
/// The current session.
|
||||
fn session(&self) -> &Session {
|
||||
self.imp().session.get().unwrap()
|
||||
}
|
||||
|
||||
/// The image content as a paintable, if any.
|
||||
pub fn paintable(&self) -> Option<gdk::Paintable> {
|
||||
self.imp().paintable.borrow().clone()
|
||||
}
|
||||
|
||||
/// Set the content of the image.
|
||||
fn set_image_data(&self, data: Option<Vec<u8>>) {
|
||||
let paintable = data
|
||||
.and_then(|data| ImagePaintable::from_bytes(&glib::Bytes::from(&data), None).ok())
|
||||
.map(|texture| texture.upcast());
|
||||
self.imp().paintable.replace(paintable);
|
||||
self.notify("paintable");
|
||||
}
|
||||
|
||||
fn load(&self) {
|
||||
// Don't do anything here if we don't need the avatar.
|
||||
if self.needed_size() == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(uri) = self.uri() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let client = self.session().client();
|
||||
let needed_size = self.needed_size();
|
||||
let request = MediaRequest {
|
||||
source: MediaSource::Plain(uri),
|
||||
format: MediaFormat::Thumbnail(MediaThumbnailSize {
|
||||
width: needed_size.into(),
|
||||
height: needed_size.into(),
|
||||
method: Method::Scale,
|
||||
}),
|
||||
};
|
||||
let handle =
|
||||
spawn_tokio!(async move { client.media().get_media_content(&request, true).await });
|
||||
|
||||
spawn!(
|
||||
glib::Priority::LOW,
|
||||
clone!(@weak self as obj => async move {
|
||||
match handle.await.unwrap() {
|
||||
Ok(data) => obj.set_image_data(Some(data)),
|
||||
Err(error) => error!("Could not fetch avatar: {error}"),
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/// Set the needed size of the user-defined image.
|
||||
///
|
||||
/// Only the biggest size will be stored.
|
||||
pub fn set_needed_size(&self, size: u32) {
|
||||
let imp = self.imp();
|
||||
|
||||
if imp.needed_size.get() < size {
|
||||
imp.needed_size.set(size);
|
||||
|
||||
self.load();
|
||||
}
|
||||
|
||||
self.notify("needed-size");
|
||||
}
|
||||
|
||||
/// Get the biggest needed size of the user-defined image.
|
||||
///
|
||||
/// If this is `0`, no image will be loaded.
|
||||
pub fn needed_size(&self) -> u32 {
|
||||
self.imp().needed_size.get()
|
||||
}
|
||||
|
||||
/// Set the Matrix URI of the `AvatarImage`.
|
||||
pub fn set_uri(&self, uri: Option<OwnedMxcUri>) {
|
||||
let imp = self.imp();
|
||||
|
||||
if imp.uri.borrow().as_ref() == uri.as_ref() {
|
||||
return;
|
||||
}
|
||||
|
||||
let has_uri = uri.is_some();
|
||||
imp.uri.replace(uri);
|
||||
|
||||
if has_uri {
|
||||
self.load();
|
||||
} else {
|
||||
self.set_image_data(None);
|
||||
}
|
||||
|
||||
self.notify("uri");
|
||||
}
|
||||
|
||||
/// The Matrix URI of the `AvatarImage`.
|
||||
pub fn uri(&self) -> Option<OwnedMxcUri> {
|
||||
self.imp().uri.borrow().to_owned()
|
||||
}
|
||||
|
||||
/// The source of the avatar's URI.
|
||||
pub fn uri_source(&self) -> AvatarUriSource {
|
||||
self.imp().uri_source.get()
|
||||
}
|
||||
|
||||
/// Set the source of the avatar's URI.
|
||||
fn set_uri_source(&self, uri_source: AvatarUriSource) {
|
||||
self.imp().uri_source.set(uri_source);
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
mod data;
|
||||
mod image;
|
||||
|
||||
pub use self::{
|
||||
data::AvatarData,
|
||||
image::{AvatarImage, AvatarUriSource},
|
||||
};
|
|
@ -0,0 +1,161 @@
|
|||
use gtk::{gdk, glib, glib::clone, prelude::*, subclass::prelude::*};
|
||||
use matrix_sdk::{
|
||||
media::{MediaFormat, MediaRequest, MediaThumbnailSize},
|
||||
ruma::{
|
||||
api::client::media::get_content_thumbnail::v3::Method, events::room::MediaSource, MxcUri,
|
||||
OwnedMxcUri,
|
||||
},
|
||||
};
|
||||
use tracing::error;
|
||||
|
||||
use crate::{components::ImagePaintable, session::model::Session, spawn, spawn_tokio};
|
||||
|
||||
/// The source of an avatar's URI.
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, glib::Enum)]
|
||||
#[repr(u32)]
|
||||
#[enum_type(name = "AvatarUriSource")]
|
||||
pub enum AvatarUriSource {
|
||||
/// The URI comes from a Matrix user.
|
||||
#[default]
|
||||
User = 0,
|
||||
/// The URI comes from a Matrix room.
|
||||
Room = 1,
|
||||
}
|
||||
|
||||
mod imp {
|
||||
use std::cell::{Cell, OnceCell, RefCell};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, glib::Properties)]
|
||||
#[properties(wrapper_type = super::AvatarImage)]
|
||||
pub struct AvatarImage {
|
||||
/// The image content as a paintable, if any.
|
||||
#[property(get)]
|
||||
pub paintable: RefCell<Option<gdk::Paintable>>,
|
||||
/// The biggest needed size of the user-defined image.
|
||||
///
|
||||
/// If this is `0`, no image will be loaded.
|
||||
#[property(get, set = Self::set_needed_size, explicit_notify, minimum = 0)]
|
||||
pub needed_size: Cell<u32>,
|
||||
/// The Matrix URI of the `AvatarImage`.
|
||||
#[property(get = Self::uri, set = Self::set_uri, explicit_notify, nullable, type = Option<String>)]
|
||||
pub uri: RefCell<Option<OwnedMxcUri>>,
|
||||
/// The source of the avatar's URI.
|
||||
#[property(get, construct_only, builder(AvatarUriSource::default()))]
|
||||
pub uri_source: Cell<AvatarUriSource>,
|
||||
/// The current session.
|
||||
#[property(get, construct_only)]
|
||||
pub session: OnceCell<Session>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for AvatarImage {
|
||||
const NAME: &'static str = "AvatarImage";
|
||||
type Type = super::AvatarImage;
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for AvatarImage {}
|
||||
|
||||
impl AvatarImage {
|
||||
/// Set the needed size of the user-defined image.
|
||||
///
|
||||
/// Only the biggest size will be stored.
|
||||
fn set_needed_size(&self, size: u32) {
|
||||
if self.needed_size.get() >= size {
|
||||
return;
|
||||
}
|
||||
let obj = self.obj();
|
||||
|
||||
self.needed_size.set(size);
|
||||
obj.load();
|
||||
obj.notify_needed_size();
|
||||
}
|
||||
|
||||
/// The Matrix URI of the `AvatarImage`.
|
||||
fn uri(&self) -> Option<String> {
|
||||
self.uri.borrow().as_ref().map(ToString::to_string)
|
||||
}
|
||||
|
||||
/// Set the Matrix URI of the `AvatarImage`.
|
||||
fn set_uri(&self, uri: Option<String>) {
|
||||
let uri = uri.map(OwnedMxcUri::from);
|
||||
|
||||
if self.uri.borrow().as_ref() == uri.as_ref() {
|
||||
return;
|
||||
}
|
||||
let obj = self.obj();
|
||||
|
||||
let has_uri = uri.is_some();
|
||||
self.uri.replace(uri);
|
||||
|
||||
if has_uri {
|
||||
obj.load();
|
||||
} else {
|
||||
obj.set_image_data(None);
|
||||
}
|
||||
|
||||
obj.notify_uri();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
/// The image data for an avatar.
|
||||
pub struct AvatarImage(ObjectSubclass<imp::AvatarImage>);
|
||||
}
|
||||
|
||||
impl AvatarImage {
|
||||
/// Construct a new `AvatarImage` with the given session and Matrix URI.
|
||||
pub fn new(session: &Session, uri: Option<&MxcUri>, uri_source: AvatarUriSource) -> Self {
|
||||
glib::Object::builder()
|
||||
.property("session", session)
|
||||
.property("uri", uri.map(|uri| uri.to_string()))
|
||||
.property("uri-source", uri_source)
|
||||
.build()
|
||||
}
|
||||
|
||||
/// Set the content of the image.
|
||||
fn set_image_data(&self, data: Option<Vec<u8>>) {
|
||||
let paintable = data
|
||||
.and_then(|data| ImagePaintable::from_bytes(&glib::Bytes::from(&data), None).ok())
|
||||
.map(|texture| texture.upcast());
|
||||
self.imp().paintable.replace(paintable);
|
||||
self.notify("paintable");
|
||||
}
|
||||
|
||||
fn load(&self) {
|
||||
// Don't do anything here if we don't need the avatar.
|
||||
if self.needed_size() == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(uri) = self.imp().uri.borrow().clone() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let client = self.session().client();
|
||||
let needed_size = self.needed_size();
|
||||
let request = MediaRequest {
|
||||
source: MediaSource::Plain(uri),
|
||||
format: MediaFormat::Thumbnail(MediaThumbnailSize {
|
||||
width: needed_size.into(),
|
||||
height: needed_size.into(),
|
||||
method: Method::Scale,
|
||||
}),
|
||||
};
|
||||
let handle =
|
||||
spawn_tokio!(async move { client.media().get_media_content(&request, true).await });
|
||||
|
||||
spawn!(
|
||||
glib::Priority::LOW,
|
||||
clone!(@weak self as obj => async move {
|
||||
match handle.await.unwrap() {
|
||||
Ok(data) => obj.set_image_data(Some(data)),
|
||||
Err(error) => error!("Could not fetch avatar: {error}"),
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
use gtk::{gdk, glib, prelude::*, subclass::prelude::*};
|
||||
use tracing::warn;
|
||||
|
||||
mod avatar_image;
|
||||
|
||||
pub use self::avatar_image::{AvatarImage, AvatarUriSource};
|
||||
use crate::{
|
||||
application::Application,
|
||||
utils::notifications::{paintable_as_notification_icon, string_as_notification_icon},
|
||||
};
|
||||
|
||||
mod imp {
|
||||
use std::cell::RefCell;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, glib::Properties)]
|
||||
#[properties(wrapper_type = super::AvatarData)]
|
||||
pub struct AvatarData {
|
||||
/// The data of the user-defined image.
|
||||
#[property(get, set = Self::set_image, explicit_notify, nullable)]
|
||||
pub image: RefCell<Option<AvatarImage>>,
|
||||
/// The display name used as a fallback for this avatar.
|
||||
#[property(get, set = Self::set_display_name, explicit_notify, nullable)]
|
||||
pub display_name: RefCell<Option<String>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for AvatarData {
|
||||
const NAME: &'static str = "AvatarData";
|
||||
type Type = super::AvatarData;
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for AvatarData {}
|
||||
|
||||
impl AvatarData {
|
||||
/// Set the data of the user-defined image.
|
||||
fn set_image(&self, image: Option<AvatarImage>) {
|
||||
if self.image.borrow().as_ref() == image.as_ref() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.image.replace(image);
|
||||
self.obj().notify_image();
|
||||
}
|
||||
|
||||
/// Set the display name used as a fallback for this avatar.
|
||||
fn set_display_name(&self, display_name: Option<String>) {
|
||||
if self.display_name.borrow().as_ref() == display_name.as_ref() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.display_name.replace(display_name);
|
||||
self.obj().notify_display_name();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
/// Data about a User’s or Room’s avatar.
|
||||
pub struct AvatarData(ObjectSubclass<imp::AvatarData>);
|
||||
}
|
||||
|
||||
impl AvatarData {
|
||||
/// Construct a new empty `AvatarData`.
|
||||
pub fn new() -> Self {
|
||||
glib::Object::new()
|
||||
}
|
||||
|
||||
/// Constructs an `AvatarData` with the given image data.
|
||||
pub fn with_image(image: AvatarImage) -> Self {
|
||||
glib::Object::builder().property("image", image).build()
|
||||
}
|
||||
|
||||
/// Get this avatar as a notification icon.
|
||||
///
|
||||
/// Returns `None` if an error occurred while generating the icon.
|
||||
pub fn as_notification_icon(&self) -> Option<gdk::Texture> {
|
||||
let window = Application::default().active_window()?.upcast();
|
||||
|
||||
let icon = if let Some(paintable) = self.image().and_then(|i| i.paintable()) {
|
||||
paintable_as_notification_icon(paintable.upcast_ref(), &window)
|
||||
} else {
|
||||
string_as_notification_icon(&self.display_name().unwrap_or_default(), &window)
|
||||
};
|
||||
|
||||
match icon {
|
||||
Ok(icon) => Some(icon),
|
||||
Err(error) => {
|
||||
warn!("Failed to generate icon for notification: {error}");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AvatarData {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
mod avatar;
|
||||
mod avatar_data;
|
||||
mod notifications;
|
||||
mod room;
|
||||
mod room_list;
|
||||
|
@ -9,7 +9,7 @@ mod user;
|
|||
mod verification;
|
||||
|
||||
pub use self::{
|
||||
avatar::{AvatarData, AvatarImage, AvatarUriSource},
|
||||
avatar_data::{AvatarData, AvatarImage, AvatarUriSource},
|
||||
notifications::Notifications,
|
||||
room::{
|
||||
Event, EventKey, HighlightFlags, Member, MemberList, MemberRole, Membership, MessageState,
|
||||
|
|
|
@ -199,7 +199,7 @@ impl Member {
|
|||
self.avatar_data()
|
||||
.image()
|
||||
.unwrap()
|
||||
.set_uri(member.avatar_url().map(std::borrow::ToOwned::to_owned));
|
||||
.set_uri(member.avatar_url().map(ToString::to_string));
|
||||
self.set_power_level(member.power_level());
|
||||
self.set_membership(member.membership().into());
|
||||
}
|
||||
|
@ -215,7 +215,7 @@ impl Member {
|
|||
self.avatar_data()
|
||||
.image()
|
||||
.unwrap()
|
||||
.set_uri(event.avatar_url());
|
||||
.set_uri(event.avatar_url().map(String::from));
|
||||
self.set_membership((&event.content().membership).into());
|
||||
|
||||
let session = self.session();
|
||||
|
|
|
@ -1651,7 +1651,10 @@ impl Room {
|
|||
}
|
||||
}
|
||||
|
||||
self.avatar_data().image().unwrap().set_uri(avatar_url);
|
||||
self.avatar_data()
|
||||
.image()
|
||||
.unwrap()
|
||||
.set_uri(avatar_url.map(String::from));
|
||||
}
|
||||
|
||||
/// Whether anyone can join this room.
|
||||
|
|
|
@ -229,7 +229,10 @@ pub trait UserExt: IsA<User> {
|
|||
|
||||
/// Set the avatar URL of this user.
|
||||
fn set_avatar_url(&self, uri: Option<OwnedMxcUri>) {
|
||||
self.avatar_data().image().unwrap().set_uri(uri);
|
||||
self.avatar_data()
|
||||
.image()
|
||||
.unwrap()
|
||||
.set_uri(uri.map(String::from));
|
||||
}
|
||||
|
||||
/// The actions the currently logged-in user is allowed to perform on this
|
||||
|
|
|
@ -5,7 +5,7 @@ use gtk::{
|
|||
glib::{self, clone},
|
||||
CompositeTemplate,
|
||||
};
|
||||
use matrix_sdk::ruma::{api::client::discovery::get_capabilities, OwnedMxcUri};
|
||||
use matrix_sdk::ruma::api::client::discovery::get_capabilities;
|
||||
use tracing::error;
|
||||
|
||||
mod change_password_subpage;
|
||||
|
@ -57,7 +57,7 @@ mod imp {
|
|||
pub deactivate_account_subpage: TemplateChild<DeactivateAccountSubpage>,
|
||||
#[template_child]
|
||||
pub log_out_subpage: TemplateChild<LogOutSubpage>,
|
||||
pub changing_avatar: RefCell<Option<OngoingAsyncAction<OwnedMxcUri>>>,
|
||||
pub changing_avatar: RefCell<Option<OngoingAsyncAction<String>>>,
|
||||
pub changing_display_name: RefCell<Option<OngoingAsyncAction<String>>>,
|
||||
}
|
||||
|
||||
|
@ -167,12 +167,9 @@ impl GeneralPage {
|
|||
.avatar_data()
|
||||
.image()
|
||||
.unwrap()
|
||||
.connect_notify_local(
|
||||
Some("uri"),
|
||||
clone!(@weak self as obj => move |avatar_image, _| {
|
||||
obj.avatar_changed(avatar_image.uri());
|
||||
}),
|
||||
);
|
||||
.connect_uri_notify(clone!(@weak self as obj => move |avatar_image| {
|
||||
obj.avatar_changed(avatar_image.uri());
|
||||
}));
|
||||
self.user().connect_notify_local(
|
||||
Some("display-name"),
|
||||
clone!(@weak self as obj => move |user, _| {
|
||||
|
@ -224,7 +221,7 @@ impl GeneralPage {
|
|||
}));
|
||||
}
|
||||
|
||||
fn avatar_changed(&self, uri: Option<OwnedMxcUri>) {
|
||||
fn avatar_changed(&self, uri: Option<String>) {
|
||||
let imp = self.imp();
|
||||
|
||||
if let Some(action) = imp.changing_avatar.borrow().as_ref() {
|
||||
|
@ -278,7 +275,7 @@ impl GeneralPage {
|
|||
}
|
||||
};
|
||||
|
||||
let (action, weak_action) = OngoingAsyncAction::set(uri.clone());
|
||||
let (action, weak_action) = OngoingAsyncAction::set(uri.to_string());
|
||||
imp.changing_avatar.replace(Some(action));
|
||||
|
||||
let uri_clone = uri.clone();
|
||||
|
|
|
@ -164,12 +164,12 @@ impl PublicRoom {
|
|||
pub fn set_matrix_public_room(&self, room: PublicRoomsChunk) {
|
||||
let imp = self.imp();
|
||||
|
||||
let display_name = room.name.clone().map(Into::into);
|
||||
let display_name = room.name.clone();
|
||||
self.avatar_data().set_display_name(display_name);
|
||||
self.avatar_data()
|
||||
.image()
|
||||
.unwrap()
|
||||
.set_uri(room.avatar_url.clone());
|
||||
.set_uri(room.avatar_url.clone().map(String::from));
|
||||
|
||||
if let Some(room) = self.room_list().get(&room.room_id) {
|
||||
self.set_room(room);
|
||||
|
|
|
@ -14,7 +14,6 @@ use ruma::{
|
|||
room::{avatar::ImageInfo, power_levels::PowerLevelAction},
|
||||
StateEventType,
|
||||
},
|
||||
OwnedMxcUri,
|
||||
};
|
||||
use tracing::error;
|
||||
|
||||
|
@ -63,7 +62,7 @@ mod imp {
|
|||
pub members_count: TemplateChild<gtk::Label>,
|
||||
/// Whether edit mode is enabled.
|
||||
pub edit_mode_enabled: Cell<bool>,
|
||||
pub changing_avatar: RefCell<Option<OngoingAsyncAction<OwnedMxcUri>>>,
|
||||
pub changing_avatar: RefCell<Option<OngoingAsyncAction<String>>>,
|
||||
pub changing_name: RefCell<Option<OngoingAsyncAction<String>>>,
|
||||
pub changing_topic: RefCell<Option<OngoingAsyncAction<String>>>,
|
||||
pub expr_watches: RefCell<Vec<gtk::ExpressionWatch>>,
|
||||
|
@ -232,7 +231,7 @@ impl GeneralPage {
|
|||
self.imp().expr_watches.borrow_mut().push(expr_watch);
|
||||
}
|
||||
|
||||
fn avatar_changed(&self, uri: Option<OwnedMxcUri>) {
|
||||
fn avatar_changed(&self, uri: Option<String>) {
|
||||
let imp = self.imp();
|
||||
|
||||
if let Some(action) = imp.changing_avatar.borrow().as_ref() {
|
||||
|
@ -302,7 +301,7 @@ impl GeneralPage {
|
|||
}
|
||||
};
|
||||
|
||||
let (action, weak_action) = OngoingAsyncAction::set(uri.clone());
|
||||
let (action, weak_action) = OngoingAsyncAction::set(uri.to_string());
|
||||
imp.changing_avatar.replace(Some(action));
|
||||
|
||||
let handle =
|
||||
|
|
Loading…
Reference in New Issue