diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/components/memberlist.cpp | 69 | ||||
-rw-r--r-- | src/components/memberlist.hpp | 2 | ||||
-rw-r--r-- | src/discord/discord.cpp | 15 | ||||
-rw-r--r-- | src/discord/discord.hpp | 8 | ||||
-rw-r--r-- | src/discord/store.hpp | 46 |
5 files changed, 111 insertions, 29 deletions
diff --git a/src/components/memberlist.cpp b/src/components/memberlist.cpp index 0796e85..df14ad5 100644 --- a/src/components/memberlist.cpp +++ b/src/components/memberlist.cpp @@ -29,14 +29,8 @@ MemberList::MemberList() m_view.append_column(*column); 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 { - 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; - }); + m_model->set_default_sort_func([](const Gtk::TreeModel::iterator &, const Gtk::TreeModel::iterator &) -> int { return 0; }); + m_model->set_sort_func(m_columns.m_sort, sigc::mem_fun(*this, &MemberList::SortFunc)); 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, int> user_to_color; - std::vector<Snowflake> roleless_users; + std::vector<UserData> roleless_users; + + const auto users = discord.GetUsersBulk(ids.begin(), ids.end()); - for (const auto user_id : ids) { - auto user = discord.GetUser(user_id); - if (!user.has_value() || user->IsDeleted()) continue; + std::unordered_map<Snowflake, RoleData> role_cache; + if (guild->Roles.has_value()) { + for (const auto &role : *guild->Roles) { + 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_id = discord.GetMemberHoistedRole(m_active_guild, user_id); - const auto col_role_id = discord.GetMemberHoistedRole(m_active_guild, user_id, true); - const auto pos_role = discord.GetRole(pos_role_id); - const auto col_role = discord.GetRole(col_role_id); + const auto pos_role = discord.GetMemberHoistedRoleCached(*member, role_cache); + const auto col_role = discord.GetMemberHoistedRoleCached(*member, role_cache, true); if (!pos_role.has_value()) { - roleless_users.push_back(user_id); + roleless_users.push_back(user); continue; } - role_to_users[pos_role->ID].push_back(std::move(*user)); - if (col_role.has_value()) user_to_color[user_id] = col_role->Color; + role_to_users[pos_role->ID].push_back(user); + 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) { @@ -135,12 +136,15 @@ void MemberList::UpdateMemberList() { return row; }; - for (const auto &[role_id, users] : role_to_users) { - const auto role = discord.GetRole(role_id); - if (!role.has_value()) continue; + // Kill sorting + m_view.freeze_child_notify(); + m_model->set_sort_column(Gtk::TreeSortable::DEFAULT_SORT_COLUMN_ID, Gtk::SORT_ASCENDING); - auto role_row = add_role(*role); - for (const auto &user : users) add_user(user, role_row); + for (const auto &[role_id, users] : role_to_users) { + 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(); @@ -149,12 +153,14 @@ void MemberList::UpdateMemberList() { everyone_role[m_columns.m_name] = "<b>@everyone</b>"; everyone_role[m_columns.m_sort] = 0; - for (const auto id : roleless_users) { - const auto user = discord.GetUser(id); - if (user.has_value()) add_user(*user, everyone_role); + for (const auto &user : roleless_users) { + 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.thaw_child_notify(); } void MemberList::Clear() { @@ -217,6 +223,15 @@ bool MemberList::OnButtonPressEvent(GdkEventButton *ev) { 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() { add(m_type); add(m_id); diff --git a/src/components/memberlist.hpp b/src/components/memberlist.hpp index 658114e..cb6c191 100644 --- a/src/components/memberlist.hpp +++ b/src/components/memberlist.hpp @@ -24,6 +24,8 @@ private: void OnRoleSubmenuPopup(); + int SortFunc(const Gtk::TreeModel::iterator &a, const Gtk::TreeModel::iterator &b); + class ModelColumns : public Gtk::TreeModel::ColumnRecord { public: ModelColumns(); diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp index ccc61b4..0618e72 100644 --- a/src/discord/discord.cpp +++ b/src/discord/discord.cpp @@ -261,6 +261,21 @@ Snowflake DiscordClient::GetMemberHoistedRole(Snowflake guild_id, Snowflake user 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 { const auto data = GetMember(user_id, guild_id); if (!data.has_value()) return std::nullopt; diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp index ebbf5f9..cb14a52 100644 --- a/src/discord/discord.hpp +++ b/src/discord/discord.hpp @@ -18,7 +18,7 @@ #include <queue> #ifdef GetMessage - #undef GetMessage +#undef GetMessage #endif class Abaddon; @@ -55,6 +55,7 @@ public: std::optional<GuildData> GetGuild(Snowflake 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; + 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::set<Snowflake> GetUsersInGuild(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) std::vector<BanData> GetBansInGuild(Snowflake guild_id); void FetchGuildBan(Snowflake guild_id, Snowflake user_id, const sigc::slot<void(BanData)> &callback); diff --git a/src/discord/store.hpp b/src/discord/store.hpp index b6979d0..ccf8d3e 100644 --- a/src/discord/store.hpp +++ b/src/discord/store.hpp @@ -11,6 +11,9 @@ #endif class Store { +private: + class Statement; + public: Store(bool mem_store = false); ~Store(); @@ -51,6 +54,48 @@ public: 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 + 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 RemoveReaction(const MessageReactionRemoveObject &data, bool byself); @@ -69,7 +114,6 @@ public: void EndTransaction(); private: - class Statement; class Database { public: Database(const char *path); |