some lovely member list optimizations
This commit is contained in:
@@ -29,14 +29,8 @@ MemberList::MemberList()
|
|||||||
m_view.append_column(*column);
|
m_view.append_column(*column);
|
||||||
|
|
||||||
m_model->set_sort_column(m_columns.m_sort, Gtk::SORT_ASCENDING);
|
m_model->set_sort_column(m_columns.m_sort, Gtk::SORT_ASCENDING);
|
||||||
m_model->set_sort_func(m_columns.m_sort, [this](const Gtk::TreeModel::iterator &a, const Gtk::TreeModel::iterator &b) -> int {
|
m_model->set_default_sort_func([](const Gtk::TreeModel::iterator &, const Gtk::TreeModel::iterator &) -> int { return 0; });
|
||||||
if ((*a)[m_columns.m_type] == MemberListRenderType::Role) {
|
m_model->set_sort_func(m_columns.m_sort, sigc::mem_fun(*this, &MemberList::SortFunc));
|
||||||
return (*b)[m_columns.m_sort] - (*a)[m_columns.m_sort];
|
|
||||||
} else if ((*a)[m_columns.m_type] == MemberListRenderType::Member) {
|
|
||||||
return static_cast<Glib::ustring>((*a)[m_columns.m_name]).compare((*b)[m_columns.m_name]);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
renderer->signal_render().connect(sigc::mem_fun(*this, &MemberList::OnCellRender));
|
renderer->signal_render().connect(sigc::mem_fun(*this, &MemberList::OnCellRender));
|
||||||
|
|
||||||
@@ -88,24 +82,31 @@ void MemberList::UpdateMemberList() {
|
|||||||
|
|
||||||
std::unordered_map<Snowflake, std::vector<UserData>> role_to_users;
|
std::unordered_map<Snowflake, std::vector<UserData>> role_to_users;
|
||||||
std::unordered_map<Snowflake, int> user_to_color;
|
std::unordered_map<Snowflake, int> user_to_color;
|
||||||
std::vector<Snowflake> roleless_users;
|
std::vector<UserData> roleless_users;
|
||||||
|
|
||||||
for (const auto user_id : ids) {
|
const auto users = discord.GetUsersBulk(ids.begin(), ids.end());
|
||||||
auto user = discord.GetUser(user_id);
|
|
||||||
if (!user.has_value() || user->IsDeleted()) continue;
|
|
||||||
|
|
||||||
const auto pos_role_id = discord.GetMemberHoistedRole(m_active_guild, user_id);
|
std::unordered_map<Snowflake, RoleData> role_cache;
|
||||||
const auto col_role_id = discord.GetMemberHoistedRole(m_active_guild, user_id, true);
|
if (guild->Roles.has_value()) {
|
||||||
const auto pos_role = discord.GetRole(pos_role_id);
|
for (const auto &role : *guild->Roles) {
|
||||||
const auto col_role = discord.GetRole(col_role_id);
|
role_cache[role.ID] = role;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto &user : users) {
|
||||||
|
if (user.IsDeleted()) continue;
|
||||||
|
const auto member = discord.GetMember(user.ID, m_active_guild);
|
||||||
|
if (!member.has_value()) continue;
|
||||||
|
|
||||||
|
const auto pos_role = discord.GetMemberHoistedRoleCached(*member, role_cache);
|
||||||
|
const auto col_role = discord.GetMemberHoistedRoleCached(*member, role_cache, true);
|
||||||
|
|
||||||
if (!pos_role.has_value()) {
|
if (!pos_role.has_value()) {
|
||||||
roleless_users.push_back(user_id);
|
roleless_users.push_back(user);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
role_to_users[pos_role->ID].push_back(std::move(*user));
|
role_to_users[pos_role->ID].push_back(user);
|
||||||
if (col_role.has_value()) user_to_color[user_id] = col_role->Color;
|
if (col_role.has_value()) user_to_color[user.ID] = col_role->Color;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto add_user = [this, &guild, &user_to_color](const UserData &user, const Gtk::TreeRow &parent) {
|
const auto add_user = [this, &guild, &user_to_color](const UserData &user, const Gtk::TreeRow &parent) {
|
||||||
@@ -135,12 +136,15 @@ void MemberList::UpdateMemberList() {
|
|||||||
return row;
|
return row;
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const auto &[role_id, users] : role_to_users) {
|
// Kill sorting
|
||||||
const auto role = discord.GetRole(role_id);
|
m_view.freeze_child_notify();
|
||||||
if (!role.has_value()) continue;
|
m_model->set_sort_column(Gtk::TreeSortable::DEFAULT_SORT_COLUMN_ID, Gtk::SORT_ASCENDING);
|
||||||
|
|
||||||
auto role_row = add_role(*role);
|
for (const auto &[role_id, users] : role_to_users) {
|
||||||
for (const auto &user : users) add_user(user, role_row);
|
if (const auto iter = role_cache.find(role_id); iter != role_cache.end()) {
|
||||||
|
auto role_row = add_role(iter->second);
|
||||||
|
for (const auto &user : users) add_user(user, role_row);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto everyone_role = *m_model->append();
|
auto everyone_role = *m_model->append();
|
||||||
@@ -149,12 +153,14 @@ void MemberList::UpdateMemberList() {
|
|||||||
everyone_role[m_columns.m_name] = "<b>@everyone</b>";
|
everyone_role[m_columns.m_name] = "<b>@everyone</b>";
|
||||||
everyone_role[m_columns.m_sort] = 0;
|
everyone_role[m_columns.m_sort] = 0;
|
||||||
|
|
||||||
for (const auto id : roleless_users) {
|
for (const auto &user : roleless_users) {
|
||||||
const auto user = discord.GetUser(id);
|
add_user(user, everyone_role);
|
||||||
if (user.has_value()) add_user(*user, everyone_role);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restore sorting
|
||||||
|
m_model->set_sort_column(m_columns.m_sort, Gtk::SORT_ASCENDING);
|
||||||
m_view.expand_all();
|
m_view.expand_all();
|
||||||
|
m_view.thaw_child_notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemberList::Clear() {
|
void MemberList::Clear() {
|
||||||
@@ -217,6 +223,15 @@ bool MemberList::OnButtonPressEvent(GdkEventButton *ev) {
|
|||||||
void MemberList::OnRoleSubmenuPopup() {
|
void MemberList::OnRoleSubmenuPopup() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int MemberList::SortFunc(const Gtk::TreeModel::iterator &a, const Gtk::TreeModel::iterator &b) {
|
||||||
|
if ((*a)[m_columns.m_type] == MemberListRenderType::Role) {
|
||||||
|
return (*b)[m_columns.m_sort] - (*a)[m_columns.m_sort];
|
||||||
|
} else if ((*a)[m_columns.m_type] == MemberListRenderType::Member) {
|
||||||
|
return static_cast<Glib::ustring>((*a)[m_columns.m_name]).compare((*b)[m_columns.m_name]);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
MemberList::ModelColumns::ModelColumns() {
|
MemberList::ModelColumns::ModelColumns() {
|
||||||
add(m_type);
|
add(m_type);
|
||||||
add(m_id);
|
add(m_id);
|
||||||
|
@@ -24,6 +24,8 @@ private:
|
|||||||
|
|
||||||
void OnRoleSubmenuPopup();
|
void OnRoleSubmenuPopup();
|
||||||
|
|
||||||
|
int SortFunc(const Gtk::TreeModel::iterator &a, const Gtk::TreeModel::iterator &b);
|
||||||
|
|
||||||
class ModelColumns : public Gtk::TreeModel::ColumnRecord {
|
class ModelColumns : public Gtk::TreeModel::ColumnRecord {
|
||||||
public:
|
public:
|
||||||
ModelColumns();
|
ModelColumns();
|
||||||
|
@@ -261,6 +261,21 @@ Snowflake DiscordClient::GetMemberHoistedRole(Snowflake guild_id, Snowflake user
|
|||||||
return top_role.has_value() ? top_role->ID : Snowflake::Invalid;
|
return top_role.has_value() ? top_role->ID : Snowflake::Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<RoleData> DiscordClient::GetMemberHoistedRoleCached(const GuildMember &member, const std::unordered_map<Snowflake, RoleData> &roles, bool with_color) const {
|
||||||
|
std::optional<RoleData> top_role;
|
||||||
|
for (const auto id : member.Roles) {
|
||||||
|
if (const auto iter = roles.find(id); iter != roles.end()) {
|
||||||
|
const auto &role = iter->second;
|
||||||
|
if ((with_color && role.Color != 0x000000) || (!with_color && role.IsHoisted)) {
|
||||||
|
if (!top_role.has_value() || top_role->Position < role.Position) {
|
||||||
|
top_role = role;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return top_role;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<RoleData> DiscordClient::GetMemberHighestRole(Snowflake guild_id, Snowflake user_id) const {
|
std::optional<RoleData> DiscordClient::GetMemberHighestRole(Snowflake guild_id, Snowflake user_id) const {
|
||||||
const auto data = GetMember(user_id, guild_id);
|
const auto data = GetMember(user_id, guild_id);
|
||||||
if (!data.has_value()) return std::nullopt;
|
if (!data.has_value()) return std::nullopt;
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
#include <queue>
|
#include <queue>
|
||||||
|
|
||||||
#ifdef GetMessage
|
#ifdef GetMessage
|
||||||
#undef GetMessage
|
#undef GetMessage
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class Abaddon;
|
class Abaddon;
|
||||||
@@ -55,6 +55,7 @@ public:
|
|||||||
std::optional<GuildData> GetGuild(Snowflake id) const;
|
std::optional<GuildData> GetGuild(Snowflake id) const;
|
||||||
std::optional<GuildMember> GetMember(Snowflake user_id, Snowflake guild_id) const;
|
std::optional<GuildMember> GetMember(Snowflake user_id, Snowflake guild_id) const;
|
||||||
Snowflake GetMemberHoistedRole(Snowflake guild_id, Snowflake user_id, bool with_color = false) const;
|
Snowflake GetMemberHoistedRole(Snowflake guild_id, Snowflake user_id, bool with_color = false) const;
|
||||||
|
std::optional<RoleData> GetMemberHoistedRoleCached(const GuildMember &member, const std::unordered_map<Snowflake, RoleData> &roles, bool with_color = false) const;
|
||||||
std::optional<RoleData> GetMemberHighestRole(Snowflake guild_id, Snowflake user_id) const;
|
std::optional<RoleData> GetMemberHighestRole(Snowflake guild_id, Snowflake user_id) const;
|
||||||
std::set<Snowflake> GetUsersInGuild(Snowflake id) const;
|
std::set<Snowflake> GetUsersInGuild(Snowflake id) const;
|
||||||
std::set<Snowflake> GetChannelsInGuild(Snowflake id) const;
|
std::set<Snowflake> GetChannelsInGuild(Snowflake id) const;
|
||||||
@@ -162,6 +163,11 @@ public:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Iter>
|
||||||
|
std::vector<UserData> GetUsersBulk(Iter begin, Iter end) {
|
||||||
|
return m_store.GetUsersBulk(begin, end);
|
||||||
|
}
|
||||||
|
|
||||||
// FetchGuildBans fetches all bans+reasons via api, this func fetches stored bans (so usually just GUILD_BAN_ADD data)
|
// FetchGuildBans fetches all bans+reasons via api, this func fetches stored bans (so usually just GUILD_BAN_ADD data)
|
||||||
std::vector<BanData> GetBansInGuild(Snowflake guild_id);
|
std::vector<BanData> GetBansInGuild(Snowflake guild_id);
|
||||||
void FetchGuildBan(Snowflake guild_id, Snowflake user_id, const sigc::slot<void(BanData)> &callback);
|
void FetchGuildBan(Snowflake guild_id, Snowflake user_id, const sigc::slot<void(BanData)> &callback);
|
||||||
|
@@ -11,6 +11,9 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
class Store {
|
class Store {
|
||||||
|
private:
|
||||||
|
class Statement;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Store(bool mem_store = false);
|
Store(bool mem_store = false);
|
||||||
~Store();
|
~Store();
|
||||||
@@ -51,6 +54,48 @@ public:
|
|||||||
std::unordered_set<Snowflake> GetMembersInGuild(Snowflake guild_id) const;
|
std::unordered_set<Snowflake> GetMembersInGuild(Snowflake guild_id) const;
|
||||||
// ^ not the same as GetUsersInGuild since users in a guild may include users who do not have retrieved member data
|
// ^ not the same as GetUsersInGuild since users in a guild may include users who do not have retrieved member data
|
||||||
|
|
||||||
|
template<typename Iter>
|
||||||
|
std::vector<UserData> GetUsersBulk(Iter begin, Iter end) {
|
||||||
|
const int size = std::distance(begin, end);
|
||||||
|
if (size == 0) return {};
|
||||||
|
|
||||||
|
std::string query = "SELECT * FROM USERS WHERE id IN (";
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
query += "?, ";
|
||||||
|
}
|
||||||
|
query.resize(query.size() - 2); // chop off extra ", "
|
||||||
|
query += ")";
|
||||||
|
|
||||||
|
Statement s(m_db, query.c_str());
|
||||||
|
if (!s.OK()) {
|
||||||
|
printf("failed to prepare bulk users: %s\n", m_db.ErrStr());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; begin != end; i++, begin++) {
|
||||||
|
s.Bind(i, *begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<UserData> r;
|
||||||
|
r.reserve(size);
|
||||||
|
while (s.FetchOne()) {
|
||||||
|
UserData u;
|
||||||
|
s.Get(0, u.ID);
|
||||||
|
s.Get(1, u.Username);
|
||||||
|
s.Get(2, u.Discriminator);
|
||||||
|
s.Get(3, u.Avatar);
|
||||||
|
s.Get(4, u.IsBot);
|
||||||
|
s.Get(5, u.IsSystem);
|
||||||
|
s.Get(6, u.IsMFAEnabled);
|
||||||
|
s.Get(7, u.PremiumType);
|
||||||
|
s.Get(8, u.PublicFlags);
|
||||||
|
s.Get(9, u.GlobalName);
|
||||||
|
r.push_back(u);
|
||||||
|
}
|
||||||
|
printf("fetched %llu\n", r.size());
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
void AddReaction(const MessageReactionAddObject &data, bool byself);
|
void AddReaction(const MessageReactionAddObject &data, bool byself);
|
||||||
void RemoveReaction(const MessageReactionRemoveObject &data, bool byself);
|
void RemoveReaction(const MessageReactionRemoveObject &data, bool byself);
|
||||||
|
|
||||||
@@ -69,7 +114,6 @@ public:
|
|||||||
void EndTransaction();
|
void EndTransaction();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Statement;
|
|
||||||
class Database {
|
class Database {
|
||||||
public:
|
public:
|
||||||
Database(const char *path);
|
Database(const char *path);
|
||||||
|
Reference in New Issue
Block a user