Skip to content

Commit

Permalink
chat-info-members: Put members list in a separate widget
Browse files Browse the repository at this point in the history
also move MemberRow inside chat_info_window module
  • Loading branch information
yuraiz committed Jan 21, 2023
1 parent c8c4f28 commit 4084625
Show file tree
Hide file tree
Showing 7 changed files with 304 additions and 180 deletions.
2 changes: 1 addition & 1 deletion data/resources/resources.gresource.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
<file compressed="true" preprocess="xml-stripblanks">ui/add-account-row.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/avatar-with-selection.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/components-avatar.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/components-chat-member-row.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/components-message-entry.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/content.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/content-chat-action-bar.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/content-chat-history.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/content-chat-info-member-row.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/content-chat-info-window.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/content-event-row.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/content-message-document.ui</file>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="ComponentsChatMemberRow" parent="GtkBox">
<property name="spacing">12</property>
<template class="ContentChatInfoMemberRow" parent="GtkWidget">
<property name="layout-manager">
<object class="GtkBoxLayout">
<property name="spacing">12</property>
</object>
</property>
<child>
<object class="ComponentsAvatar" id="avatar">
<property name="size">40</property>
Expand Down
26 changes: 8 additions & 18 deletions data/resources/ui/content-chat-info-window.ui
Original file line number Diff line number Diff line change
Expand Up @@ -85,25 +85,15 @@
<property name="name">members</property>
<property name="title" translatable="yes">Members</property>
<property name="icon-name">avatar-default-symbolic</property>
<property name="visible">false</property>
<property name="visible">False</property>
<binding name="visible">
<lookup name="visible">members_list</lookup>
</binding>
<property name="child">
<object class="GtkScrolledWindow">
<property name="child">
<object class="AdwClampScrollable">
<property name="maximum-size">440</property>
<property name="tightening-threshold">200</property>
<property name="child">
<object class="GtkListView" id="members_list">
<property name="model">
<object class="GtkNoSelection" id="selection"/>
</property>
<style>
<class name="navigation-sidebar"/>
</style>
</object>
</property>
</object>
</property>
<object class="ContentChatInfoMembers" id="members_list">
<binding name="chat">
<lookup name="chat">ContentChatInfoWindow</lookup>
</binding>
</object>
</property>
</object>
Expand Down
2 changes: 0 additions & 2 deletions src/session/components/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
mod avatar;
mod chat_member_row;
mod message_entry;
mod snow;

pub(crate) use self::avatar::Avatar;
pub(crate) use self::chat_member_row::ChatMemberRow;
pub(crate) use self::message_entry::MessageEntry;
pub(crate) use self::snow::Snow;
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ use gtk::prelude::*;
use gtk::subclass::prelude::*;
use gtk::{glib, CompositeTemplate};

use super::Avatar;
use crate::session::components::Avatar;
use crate::{expressions, strings};
use tdlib::enums::{UserStatus, UserType};

mod imp {
use super::*;
#[derive(Debug, Default, CompositeTemplate)]
#[template(resource = "/com/github/melix99/telegrand/ui/components-chat-member-row.ui")]
pub(crate) struct ChatMemberRow {
#[template(resource = "/com/github/melix99/telegrand/ui/content-chat-info-member-row.ui")]
pub(crate) struct MemberRow {
#[template_child]
pub(super) avatar: TemplateChild<Avatar>,
#[template_child]
Expand All @@ -24,10 +24,10 @@ mod imp {
}

#[glib::object_subclass]
impl ObjectSubclass for ChatMemberRow {
const NAME: &'static str = "ComponentsChatMemberRow";
type Type = super::ChatMemberRow;
type ParentType = gtk::Box;
impl ObjectSubclass for MemberRow {
const NAME: &'static str = "ContentChatInfoMemberRow";
type Type = super::MemberRow;
type ParentType = gtk::Widget;

fn class_init(klass: &mut Self::Class) {
Self::bind_template(klass);
Expand All @@ -39,19 +39,19 @@ mod imp {
}
}

impl ObjectImpl for ChatMemberRow {}
impl ObjectImpl for MemberRow {}

impl WidgetImpl for ChatMemberRow {}
impl WidgetImpl for MemberRow {}

impl BoxImpl for ChatMemberRow {}
impl BoxImpl for MemberRow {}
}

glib::wrapper! {
pub(crate) struct ChatMemberRow(ObjectSubclass<imp::ChatMemberRow>)
pub(crate) struct MemberRow(ObjectSubclass<imp::MemberRow>)
@extends gtk::Widget;
}

impl ChatMemberRow {
impl MemberRow {
pub fn new() -> Self {
glib::Object::new(&[])
}
Expand Down
266 changes: 266 additions & 0 deletions src/session/content/chat_info_window/members_page.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
use super::member_row::MemberRow;

use adw::prelude::*;
use adw::subclass::prelude::*;
use glib::clone;
use gtk::{glib, CompositeTemplate};
use tdlib::enums::BasicGroupFullInfo::BasicGroupFullInfo as TdBasicGroupFullInfo;
use tdlib::enums::ChatMembers::ChatMembers as TdChatMembers;
use tdlib::enums::MessageSender;
use tdlib::enums::SupergroupFullInfo::SupergroupFullInfo as TdSupergroupFullInfo;
use tdlib::enums::User::User as TdUser;
use tdlib::functions;
use tdlib::types::{ChatMember as TdChatMember, ChatMembers};

use crate::tdlib::{BasicGroup, Chat, ChatMember, ChatType, Supergroup, User};
use crate::utils::spawn;

mod imp {
use super::*;
use once_cell::sync::{Lazy, OnceCell};
use std::cell::Cell;

#[derive(Debug, Default, CompositeTemplate)]
#[template(string = r#"
<interface>
<template class="ContentChatInfoMembers" parent="AdwBin">
<property name="child">
<object class="GtkScrolledWindow">
<property name="child">
<object class="AdwClampScrollable">
<property name="maximum-size">440</property>
<property name="tightening-threshold">200</property>
<property name="child">
<object class="GtkListView" id="members_list">
<property name="model">
<object class="GtkNoSelection" id="selection"/>
</property>
<style>
<class name="navigation-sidebar"/>
</style>
</object>
</property>
</object>
</property>
</object>
</property>
</template>
</interface>
"#)]
pub(crate) struct ChatInfoMembers {
pub(super) loading: Cell<bool>,
pub(super) chat: OnceCell<Chat>,
#[template_child]
pub(super) members_list: TemplateChild<gtk::ListView>,
}

#[glib::object_subclass]
impl ObjectSubclass for ChatInfoMembers {
const NAME: &'static str = "ContentChatInfoMembers";
type Type = super::ChatInfoMembers;
type ParentType = adw::Bin;

fn class_init(klass: &mut Self::Class) {
klass.bind_template();
}

fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
obj.init_template();
}
}

impl ObjectImpl for ChatInfoMembers {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
vec![glib::ParamSpecObject::builder::<Chat>("chat")
// .construct_only()
.build()]
});
PROPERTIES.as_ref()
}

fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
match pspec.name() {
"chat" => {
if let Some(chat) = value.get().unwrap() {
self.chat.set(chat).unwrap();
self.obj().setup_page();
}
}
_ => unimplemented!(),
}
}

fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
let obj = self.obj();

match pspec.name() {
"chat" => obj.chat().to_value(),
_ => unimplemented!(),
}
}
}

impl WidgetImpl for ChatInfoMembers {}
impl BinImpl for ChatInfoMembers {}
}

glib::wrapper! {
pub(crate) struct ChatInfoMembers(ObjectSubclass<imp::ChatInfoMembers>)
@extends gtk::Widget;
}

impl ChatInfoMembers {
fn setup_page(&self) {
match self.chat().unwrap().type_() {
ChatType::BasicGroup(basic_group) => {
self.setup_basic_group_info(basic_group);
}
ChatType::Supergroup(supergroup) => {
self.setup_supergroup_info(supergroup);
}
_ => {
self.set_visible(false);
}
}
}

fn setup_basic_group_info(&self, basic_group: &BasicGroup) {
let client_id = self.chat().unwrap().session().client_id();
let basic_group_id = basic_group.id();

spawn(clone!(@weak self as obj => async move {
let result = functions::get_basic_group_full_info(basic_group_id, client_id).await;
if let Ok(TdBasicGroupFullInfo(full_info)) = result {
obj.append_members(full_info.members).await;
}
}));
}

fn setup_supergroup_info(&self, supergroup: &Supergroup) {
let client_id = self.chat().unwrap().session().client_id();
let supergroup_id = supergroup.id();

spawn(clone!(@weak self as obj => async move {
let imp = obj.imp();
let result = functions::get_supergroup_full_info(supergroup_id, client_id).await;
if let Ok(TdSupergroupFullInfo(full_info)) = result {
if full_info.can_get_members {
imp.loading.set(true);
let result = functions::get_supergroup_members(
supergroup_id,
None,
0,
200,
client_id,
).await;
if let Ok(TdChatMembers(ChatMembers {members, total_count})) = result {
obj.append_members(members).await;

if total_count > 200 {
obj.imp().members_list.vadjustment().unwrap()
.connect_changed(clone!(@weak obj => move |adj| {
obj.load_more_members(adj, supergroup_id);
}));
}
}
imp.loading.set(false);
}
}
}));
}

fn load_more_members(&self, adj: &gtk::Adjustment, supergroup_id: i64) {
let imp = self.imp();
if imp.loading.get() {
return;
}
imp.loading.set(true);

if adj.value() > adj.page_size() * 2.0 || adj.upper() >= adj.page_size() * 2.0 {
let offset = imp.members_list.model().unwrap().n_items() as i32;
let limit = 200;
let client_id = self.chat().unwrap().session().client_id();

spawn(clone!(@weak self as obj => async move {
let result = functions::get_supergroup_members(
supergroup_id,
None,
offset,
limit,
client_id,
).await;
if let Ok(TdChatMembers(ChatMembers {members, ..})) = result {
obj.append_members(members).await;
obj.imp().loading.set(false);
} else {
log::error!("can't load members {result:?}");
}
}));
}
}

async fn append_members(&self, members: Vec<TdChatMember>) {
let members: Vec<_> = {
let mut users: Vec<User> = vec![];

let session = self.chat().unwrap().session();
let client_id = session.client_id();

for member in &members {
let user = match member.member_id {
MessageSender::User(ref user) => {
let TdUser(user) =
functions::get_user(user.user_id, client_id).await.unwrap();
User::from_td_object(user, &session)
}
MessageSender::Chat(_) => unreachable!(),
};
users.push(user);
}

members
.into_iter()
.zip(users.into_iter())
.map(|(member, user)| ChatMember::new(member, user))
.collect()
};

let members_list = &self.imp().members_list;

let selection_model: gtk::NoSelection = members_list.model().unwrap().downcast().unwrap();

let model: gtk::gio::ListStore = if let Some(model) = selection_model.model() {
model.downcast().unwrap()
} else {
let model = gtk::gio::ListStore::new(ChatMember::static_type());
selection_model.set_model(Some(&model));
model
};

model.extend_from_slice(&members);

if members_list.factory().is_none() {
let factory = gtk::SignalListItemFactory::new();

factory.connect_setup(move |_, list_item| {
list_item.set_property("child", MemberRow::new());
});

factory.connect_bind(move |_, list_item| {
let list_item: &gtk::ListItem = list_item.downcast_ref().unwrap();

let user_row: MemberRow = list_item.child().unwrap().downcast().unwrap();
let member: ChatMember = list_item.item().unwrap().downcast().unwrap();

user_row.bind_member(member);
});

members_list.set_factory(Some(&factory));
}
}

pub(crate) fn chat(&self) -> Option<&Chat> {
self.imp().chat.get()
}
}
Loading

0 comments on commit 4084625

Please sign in to comment.