Browse Source

room: Add PowerLevels GObject

merge-requests/1327/merge
Kai A. Hiller 5 years ago
parent
commit
aa649a89b0
  1. 1
      src/meson.build
  2. 2
      src/session/room/mod.rs
  3. 141
      src/session/room/power_levels.rs
  4. 20
      src/session/room/room.rs
  5. 9
      src/utils.rs

1
src/meson.build

@ -58,6 +58,7 @@ sources = files(
'session/room/item.rs',
'session/room/member.rs',
'session/room/mod.rs',
'session/room/power_levels.rs',
'session/room/room.rs',
'session/room/room_type.rs',
'session/room_list.rs',

2
src/session/room/mod.rs

@ -2,6 +2,7 @@ mod event;
mod highlight_flags;
mod item;
mod member;
mod power_levels;
mod room;
mod room_type;
mod timeline;
@ -11,6 +12,7 @@ pub use self::highlight_flags::HighlightFlags;
pub use self::item::Item;
pub use self::item::ItemType;
pub use self::member::Member;
pub use self::power_levels::{PowerLevels, RoomAction};
pub use self::room::Room;
pub use self::room_type::RoomType;
pub use self::timeline::Timeline;

141
src/session/room/power_levels.rs

@ -0,0 +1,141 @@
use gtk::glib;
use gtk::prelude::*;
use gtk::subclass::prelude::*;
use matrix_sdk::ruma::events::room::power_levels::PowerLevelsEventContent;
use matrix_sdk::ruma::events::{EventType, SyncStateEvent};
use crate::session::room::Member;
use crate::utils::prop_expr;
#[derive(Clone, Debug, Default, glib::GBoxed)]
#[gboxed(type_name = "BoxedPowerLevelsEventContent")]
pub struct BoxedPowerLevelsEventContent(PowerLevelsEventContent);
mod imp {
use super::*;
use once_cell::sync::Lazy;
use std::cell::RefCell;
#[derive(Debug, Default)]
pub struct PowerLevels {
pub content: RefCell<BoxedPowerLevelsEventContent>,
}
#[glib::object_subclass]
impl ObjectSubclass for PowerLevels {
const NAME: &'static str = "PowerLevels";
type Type = super::PowerLevels;
type ParentType = glib::Object;
}
impl ObjectImpl for PowerLevels {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
vec![glib::ParamSpec::new_boxed(
"power-levels",
"Power levels",
"Ruma struct containing all power level information of a room",
BoxedPowerLevelsEventContent::static_type(),
glib::ParamFlags::READABLE,
)]
});
PROPERTIES.as_ref()
}
fn property(&self, obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
match pspec.name() {
"power-levels" => obj.power_levels().to_value(),
_ => unimplemented!(),
}
}
}
}
glib::wrapper! {
pub struct PowerLevels(ObjectSubclass<imp::PowerLevels>);
}
impl PowerLevels {
pub fn new() -> Self {
glib::Object::new(&[]).expect("Failed to create PowerLevels")
}
pub fn power_levels(&self) -> BoxedPowerLevelsEventContent {
let priv_ = imp::PowerLevels::from_instance(self);
priv_.content.borrow().clone()
}
/// Returns the power level minimally required to perform the given action.
pub fn min_level_for_room_action(&self, room_action: &RoomAction) -> u32 {
let priv_ = imp::PowerLevels::from_instance(self);
let content = priv_.content.borrow();
min_level_for_room_action(&content.0, room_action)
}
/// Creates an expression that is true when the user is allowed the given action.
pub fn new_allowed_expr(&self, member: &Member, room_action: RoomAction) -> gtk::Expression {
gtk::ClosureExpression::new(
move |args| {
let power_level: u32 = args[1].get().unwrap();
let content = args[2].get::<BoxedPowerLevelsEventContent>().unwrap().0;
power_level >= min_level_for_room_action(&content, &room_action)
},
&[
prop_expr(member, "power-level"),
prop_expr(self, "power-levels"),
],
)
.upcast()
}
/// Updates the power levels from the given event.
pub fn update_from_event(&self, event: SyncStateEvent<PowerLevelsEventContent>) {
let priv_ = imp::PowerLevels::from_instance(self);
let content = BoxedPowerLevelsEventContent(event.content);
priv_.content.replace(content);
self.notify("power-levels");
}
}
impl Default for PowerLevels {
fn default() -> Self {
Self::new()
}
}
/// Returns the power level minimally required to perform the given action.
fn min_level_for_room_action(content: &PowerLevelsEventContent, room_action: &RoomAction) -> u32 {
let power_level = i64::from(match room_action {
RoomAction::Ban => content.ban,
RoomAction::Invite => content.invite,
RoomAction::Kick => content.kick,
RoomAction::Redact => content.redact,
RoomAction::RoomNotification => content.notifications.room,
RoomAction::StateEvent(event_type) => *content
.events
.get(event_type)
.unwrap_or(&content.state_default),
RoomAction::MessageEvent(event_type) => *content
.events
.get(event_type)
.unwrap_or(&content.events_default),
});
if (0..=100).contains(&power_level) {
power_level as u32
} else {
0
}
}
/// Actions that require different power levels to perform them.
pub enum RoomAction {
Ban,
Invite,
Kick,
Redact,
RoomNotification,
StateEvent(EventType),
MessageEvent(EventType),
}

20
src/session/room/room.rs

@ -31,7 +31,9 @@ use std::convert::TryFrom;
use crate::components::{LabelWithWidgets, Pill};
use crate::prelude::*;
use crate::session::room::{Event, HighlightFlags, Member, RoomType, Timeline};
use crate::session::room::{
Event, HighlightFlags, Member, PowerLevels, RoomAction, RoomType, Timeline,
};
use crate::session::{Avatar, Session};
use crate::utils::do_async;
use crate::Error;
@ -56,6 +58,7 @@ mod imp {
/// The user who sent the invite to this room. This is only set when this room is an invitiation.
pub inviter: RefCell<Option<Member>>,
pub members_loaded: Cell<bool>,
pub power_levels: RefCell<PowerLevels>,
}
#[glib::object_subclass]
@ -486,6 +489,11 @@ impl Room {
.filter(|topic| !topic.is_empty() && topic.find(|c: char| !c.is_whitespace()).is_some())
}
pub fn power_levels(&self) -> PowerLevels {
let priv_ = imp::Room::from_instance(self);
priv_.power_levels.borrow().clone()
}
pub fn inviter(&self) -> Option<Member> {
let priv_ = imp::Room::from_instance(self);
priv_.inviter.borrow().clone()
@ -563,6 +571,9 @@ impl Room {
AnySyncRoomEvent::State(AnySyncStateEvent::RoomTopic(_)) => {
self.notify("topic");
}
AnySyncRoomEvent::State(AnySyncStateEvent::RoomPowerLevels(event)) => {
self.power_levels().update_from_event(event);
}
_ => {}
}
}
@ -695,6 +706,13 @@ impl Room {
}
}
/// Creates an expression that is true when the user is allowed the given action.
pub fn new_allowed_expr(&self, room_action: RoomAction) -> gtk::Expression {
let user_id = self.session().user().user_id();
let member = self.member_by_id(user_id);
self.power_levels().new_allowed_expr(&member, room_action)
}
pub async fn accept_invite(&self) -> Result<(), Error> {
let matrix_room = self.matrix_room();

9
src/utils.rs

@ -33,7 +33,8 @@ macro_rules! event_from_sync_event {
}
use crate::RUNTIME;
use gtk::glib;
use gtk::gio::prelude::*;
use gtk::glib::{self, Object};
use std::future::Future;
/// Execute a future on a tokio runtime and spawn a future on the local thread to handle the result
pub fn do_async<
@ -54,3 +55,9 @@ pub fn do_async<
RUNTIME.spawn(async move { sender.send(tokio_fut.await) });
}
/// Returns an expression looking up the given property on `object`.
pub fn prop_expr<T: IsA<Object>>(object: &T, prop: &str) -> gtk::Expression {
let obj_expr = gtk::ConstantExpression::new(object).upcast();
gtk::PropertyExpression::new(T::static_type(), Some(&obj_expr), prop).upcast()
}

Loading…
Cancel
Save