diff options
author | ouwou <26526779+ouwou@users.noreply.github.com> | 2021-02-28 02:00:15 -0500 |
---|---|---|
committer | ouwou <26526779+ouwou@users.noreply.github.com> | 2021-02-28 02:00:15 -0500 |
commit | d6a8b8c33372fe41d9e7f8453d15f491b25680a6 (patch) | |
tree | 5510f4ae1065b55e950a013658eef336e5e876a8 | |
parent | 11358da24e0e73044aed389b1dbbf479808ca036 (diff) | |
download | abaddon-portaudio-d6a8b8c33372fe41d9e7f8453d15f491b25680a6.tar.gz abaddon-portaudio-d6a8b8c33372fe41d9e7f8453d15f491b25680a6.zip |
add members panel/change member roles
-rw-r--r-- | abaddon.hpp | 3 | ||||
-rw-r--r-- | css/main.css | 23 | ||||
-rw-r--r-- | discord/discord.cpp | 9 | ||||
-rw-r--r-- | discord/discord.hpp | 11 | ||||
-rw-r--r-- | discord/guild.cpp | 11 | ||||
-rw-r--r-- | discord/guild.hpp | 1 | ||||
-rw-r--r-- | discord/objects.cpp | 4 | ||||
-rw-r--r-- | discord/objects.hpp | 11 | ||||
-rw-r--r-- | discord/user.cpp | 4 | ||||
-rw-r--r-- | discord/user.hpp | 1 | ||||
-rw-r--r-- | util.cpp | 8 | ||||
-rw-r--r-- | util.hpp | 1 | ||||
-rw-r--r-- | windows/guildsettings/memberspane.cpp | 394 | ||||
-rw-r--r-- | windows/guildsettings/memberspane.hpp | 133 | ||||
-rw-r--r-- | windows/guildsettingswindow.cpp | 5 | ||||
-rw-r--r-- | windows/guildsettingswindow.hpp | 2 |
16 files changed, 619 insertions, 2 deletions
diff --git a/abaddon.hpp b/abaddon.hpp index 884f66e..9d99bee 100644 --- a/abaddon.hpp +++ b/abaddon.hpp @@ -78,10 +78,11 @@ public: Glib::RefPtr<Gtk::CssProvider> GetStyleProvider(); + void ShowUserMenu(const GdkEvent *event, Snowflake id, Snowflake guild_id); + protected: Snowflake m_shown_user_menu_id; Snowflake m_shown_user_menu_guild_id; - void ShowUserMenu(const GdkEvent *event, Snowflake id, Snowflake guild_id); Gtk::Menu *m_user_menu; Gtk::MenuItem *m_user_menu_info; diff --git a/css/main.css b/css/main.css index 961f990..45a711b 100644 --- a/css/main.css +++ b/css/main.css @@ -292,3 +292,26 @@ .app-window textview text { caret-color: #ababab; } + +.guild-members-pane-info { + padding: 10px; +} + + +check, +radio { + background-clip: padding-box; + background: #32506e; + border-color: #070707; + box-shadow: 0 1px rgba(0, 0, 0, 0); + color: #dddddd; +} + +check:checked, +radio:checked { + background-clip: border-box; + background: #185fb4; + border-color: #092444; + box-shadow: 0 1px rgba(0, 0, 0, 0); + color: #dddddd; +} diff --git a/discord/discord.cpp b/discord/discord.cpp index fbe6549..7cd87bc 100644 --- a/discord/discord.cpp +++ b/discord/discord.cpp @@ -1477,6 +1477,15 @@ bool DiscordClient::CheckCode(const http::response_type &r) { return true; } +bool DiscordClient::CheckCode(const http::response_type &r, int expected) { + if (!CheckCode(r)) return false; + if (r.status_code != expected) { + fprintf(stderr, "api request to %s returned %d, expected %d\n", r.url.c_str(), r.status_code, expected); + return false; + } + return true; +} + void DiscordClient::StoreMessageData(Message &msg) { const auto chan = m_store.GetChannel(msg.ChannelID); if (chan.has_value() && chan->GuildID.has_value()) diff --git a/discord/discord.hpp b/discord/discord.hpp index c6723cd..c086aec 100644 --- a/discord/discord.hpp +++ b/discord/discord.hpp @@ -124,6 +124,16 @@ public: void AddGroupDMRecipient(Snowflake channel_id, Snowflake user_id); void RemoveGroupDMRecipient(Snowflake channel_id, Snowflake user_id); + // real client doesn't seem to use the single role endpoints so neither do we + template<typename Iter> + auto SetMemberRoles(Snowflake guild_id, Snowflake user_id, Iter begin, Iter end, sigc::slot<void(bool success)> callback) { + ModifyGuildMemberObject obj; + obj.Roles = { begin, end }; + m_http.MakePATCH("/guilds/" + std::to_string(guild_id) + "/members/" + std::to_string(user_id), nlohmann::json(obj).dump(), [this, callback](const http::response_type &response) { + callback(CheckCode(response, 200)); + }); + } + // 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, sigc::slot<void(BanData)> callback); @@ -196,6 +206,7 @@ private: void HandleSocketClose(uint16_t code); bool CheckCode(const http::response_type &r); + bool CheckCode(const http::response_type &r, int expected); void StoreMessageData(Message &msg); diff --git a/discord/guild.cpp b/discord/guild.cpp index bf99943..5fb524e 100644 --- a/discord/guild.cpp +++ b/discord/guild.cpp @@ -186,3 +186,14 @@ std::vector<Snowflake> GuildData::GetSortedChannels(Snowflake ignore) const { return ret; } + +std::vector<RoleData> GuildData::FetchRoles() const { + if (!Roles.has_value()) return {}; + std::vector<RoleData> ret; + for (const auto thing : *Roles) + ret.push_back(*Abaddon::Get().GetDiscordClient().GetRole(thing.ID)); + std::sort(ret.begin(), ret.end(), [](const RoleData &a, const RoleData &b) -> bool { + return a.Position > b.Position; + }); + return ret; +} diff --git a/discord/guild.hpp b/discord/guild.hpp index e8132e0..c2d7333 100644 --- a/discord/guild.hpp +++ b/discord/guild.hpp @@ -75,4 +75,5 @@ struct GuildData { bool HasAnimatedIcon() const; std::string GetIconURL(std::string ext = "png", std::string size = "32") const; std::vector<Snowflake> GetSortedChannels(Snowflake ignore = Snowflake::Invalid) const; + std::vector<RoleData> FetchRoles() const; // sorted }; diff --git a/discord/objects.cpp b/discord/objects.cpp index af82759..878c459 100644 --- a/discord/objects.cpp +++ b/discord/objects.cpp @@ -363,3 +363,7 @@ void from_json(const nlohmann::json &j, UserNoteUpdateMessage &m) { void from_json(const nlohmann::json &j, RelationshipsData &m) { j.get_to(m.Users); } + +void to_json(nlohmann::json &j, const ModifyGuildMemberObject &m) { + JS_IF("roles", m.Roles); +} diff --git a/discord/objects.hpp b/discord/objects.hpp index bdc16f8..b95be9f 100644 --- a/discord/objects.hpp +++ b/discord/objects.hpp @@ -503,3 +503,14 @@ struct RelationshipsData { friend void from_json(const nlohmann::json &j, RelationshipsData &m); }; + +struct ModifyGuildMemberObject { + // std::optional<std::string> Nick; + // std::optional<bool> IsMuted; + // std::optional<bool> IsDeaf; + // std::optional<Snowflake> ChannelID; + + std::optional<std::vector<Snowflake>> Roles; + + friend void to_json(nlohmann::json &j, const ModifyGuildMemberObject &m); +}; diff --git a/discord/user.cpp b/discord/user.cpp index b399930..0ecf725 100644 --- a/discord/user.cpp +++ b/discord/user.cpp @@ -1,6 +1,10 @@ #include "user.hpp" #include "../abaddon.hpp" +bool UserData::IsDeleted() const { + return Discriminator == "0000"; +} + bool UserData::HasAvatar() const { return Avatar.size() > 0; } diff --git a/discord/user.hpp b/discord/user.hpp index c66a086..025139e 100644 --- a/discord/user.hpp +++ b/discord/user.hpp @@ -49,6 +49,7 @@ struct UserData { friend void to_json(nlohmann::json &j, const UserData &m); void update_from_json(const nlohmann::json &j); + bool IsDeleted() const; bool HasAvatar() const; bool HasAnimatedAvatar() const; std::string GetAvatarURL(std::string ext = "png", std::string size = "32") const; @@ -134,6 +134,14 @@ std::string IntToCSSColor(int color) { return ss.str(); } +Gdk::RGBA IntToRGBA(int color) { + Gdk::RGBA ret; + ret.set_red(((color & 0xFF0000) >> 16) / 255.0); + ret.set_green(((color & 0x00FF00) >> 8) / 255.0); + ret.set_blue(((color & 0x0000FF) >> 0) / 255.0); + return ret; +} + void AddWidgetMenuHandler(Gtk::Widget *widget, Gtk::Menu &menu) { AddWidgetMenuHandler(widget, menu, []() {}); } @@ -38,6 +38,7 @@ private: void LaunchBrowser(Glib::ustring url); void GetImageDimensions(int inw, int inh, int &outw, int &outh, int clampw = 400, int clamph = 300); std::string IntToCSSColor(int color); +Gdk::RGBA IntToRGBA(int color); void AddWidgetMenuHandler(Gtk::Widget *widget, Gtk::Menu &menu); void AddWidgetMenuHandler(Gtk::Widget *widget, Gtk::Menu &menu, sigc::slot<void()> pre_callback); std::vector<std::string> StringSplit(const std::string &str, const char *delim); diff --git a/windows/guildsettings/memberspane.cpp b/windows/guildsettings/memberspane.cpp new file mode 100644 index 0000000..0f29223 --- /dev/null +++ b/windows/guildsettings/memberspane.cpp @@ -0,0 +1,394 @@ +#include "memberspane.hpp" +#include "../../abaddon.hpp" + +GuildSettingsMembersPane::GuildSettingsMembersPane(Snowflake id) + : Gtk::Box(Gtk::ORIENTATION_VERTICAL) + , m_layout(Gtk::ORIENTATION_HORIZONTAL) + , m_member_list(id) + , m_member_info(id) + , GuildID(id) { + set_name("guild-members-pane"); + set_hexpand(true); + set_vexpand(true); + + m_member_list.signal_member_select().connect(sigc::mem_fun(m_member_info, &GuildSettingsMembersPaneInfo::SetUser)); + + m_note.set_label("Some members may not be shown if the client is not aware of them"); + m_note.set_single_line_mode(true); + m_note.set_ellipsize(Pango::ELLIPSIZE_END); + + m_layout.set_homogeneous(true); + m_layout.add(m_member_list); + m_layout.add(m_member_info); + add(m_note); + add(m_layout); + + m_member_list.show(); + m_member_info.show(); + m_note.show(); + m_layout.show(); +} + +GuildSettingsMembersPaneMembers::GuildSettingsMembersPaneMembers(Snowflake id) + : Gtk::Box(Gtk::ORIENTATION_VERTICAL) + , GuildID(id) { + m_list_scroll.get_style_context()->add_class("guild-members-pane-list"); + + m_list_scroll.set_hexpand(true); + m_list_scroll.set_vexpand(true); + m_list_scroll.set_propagate_natural_height(true); + + auto &discord = Abaddon::Get().GetDiscordClient(); + auto members = discord.GetUsersInGuild(id); + for (const auto member_id : members) { + auto member = *discord.GetMember(member_id, GuildID); + member.User = discord.GetUser(member_id); + if (member.User->IsDeleted()) continue; + auto *row = Gtk::manage(new GuildSettingsMembersListItem(GuildID, member)); + row->show(); + m_list.add(*row); + } + + m_list.set_selection_mode(Gtk::SELECTION_SINGLE); + m_list.signal_row_selected().connect([this](Gtk::ListBoxRow *selected_) { + if (auto *selected = dynamic_cast<GuildSettingsMembersListItem *>(selected_)) + m_signal_member_select.emit(selected->UserID); + }); + + m_search.set_placeholder_text("Filter"); + m_search.signal_changed().connect([this] { + m_list.invalidate_filter(); + }); + + m_list.set_filter_func([this](Gtk::ListBoxRow *row_) -> bool { + const auto search_term = m_search.get_text(); + if (search_term.size() == 0) return true; + if (auto *row = dynamic_cast<GuildSettingsMembersListItem *>(row_)) + return StringContainsCaseless(row->DisplayTerm, m_search.get_text()); + return true; + }); + + m_list_scroll.add(m_list); + add(m_search); + add(m_list_scroll); + + m_search.show(); + m_list.show(); + m_list_scroll.show(); +} + +GuildSettingsMembersPaneMembers::type_signal_member_select GuildSettingsMembersPaneMembers::signal_member_select() { + return m_signal_member_select; +} + +GuildSettingsMembersListItem::GuildSettingsMembersListItem(Snowflake guild_id, const GuildMember &member) + : UserID(member.User->ID) + , GuildID(guild_id) + , m_avatar(32, 32) { + m_avatar.SetAnimated(true); + + m_ev.signal_button_press_event().connect([this](GdkEventButton *event) -> bool { + if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_SECONDARY) { + Abaddon::Get().ShowUserMenu(reinterpret_cast<GdkEvent *>(event), UserID, GuildID); + return true; + } + return false; + }); + + auto &discord = Abaddon::Get().GetDiscordClient(); + + if (member.User->HasAvatar()) { + if (member.User->HasAnimatedAvatar() && Abaddon::Get().GetSettings().GetShowAnimations()) + m_avatar.SetURL(member.User->GetAvatarURL("gif", "32")); + else + m_avatar.SetURL(member.User->GetAvatarURL("png", "32")); + } + + DisplayTerm = member.User->Username + "#" + member.User->Discriminator; + + const auto member_update_cb = [this](Snowflake guild_id, Snowflake user_id) { + if (user_id == UserID) + UpdateColor(); + }; + discord.signal_guild_member_update().connect(sigc::track_obj(member_update_cb, *this)); + UpdateColor(); + + m_avatar.set_margin_end(5); + m_avatar.set_halign(Gtk::ALIGN_START); + m_avatar.set_valign(Gtk::ALIGN_CENTER); + m_name.set_halign(Gtk::ALIGN_START); + m_name.set_valign(Gtk::ALIGN_CENTER); + + m_main.add(m_avatar); + m_main.add(m_name); + + m_ev.add(m_main); + add(m_ev); + + m_avatar.show(); + m_name.show(); + m_main.show(); + m_ev.show(); +} + +void GuildSettingsMembersListItem::UpdateColor() { + auto &discord = Abaddon::Get().GetDiscordClient(); + const auto user = *discord.GetUser(UserID); + if (auto color_id = discord.GetMemberHoistedRole(GuildID, UserID, true); color_id.IsValid()) { + auto role = *discord.GetRole(color_id); + m_name.set_markup("<span color='#" + IntToCSSColor(role.Color) + "'>" + user.GetEscapedBoldString<false>() + "</span>"); + } else + m_name.set_markup(user.GetEscapedBoldString<false>()); +} + +GuildSettingsMembersPaneInfo::GuildSettingsMembersPaneInfo(Snowflake guild_id) + : m_box(Gtk::ORIENTATION_VERTICAL) + , GuildID(guild_id) + , m_roles(guild_id) { + get_style_context()->add_class("guild-members-pane-info"); + + const auto label = [](Gtk::Label &lbl) { + lbl.set_single_line_mode(true); + lbl.set_halign(Gtk::ALIGN_START); + lbl.set_valign(Gtk::ALIGN_START); + lbl.set_ellipsize(Pango::ELLIPSIZE_END); + lbl.set_margin_bottom(5); + lbl.show(); + }; + + m_bot.set_text("User is a bot"); + + label(m_bot); + label(m_id); + label(m_created); + label(m_joined); + label(m_nickname); + label(m_boosting); + + m_box.set_halign(Gtk::ALIGN_FILL); + m_box.set_valign(Gtk::ALIGN_START); + m_box.set_hexpand(true); + m_box.set_vexpand(true); + m_box.add(m_bot); + m_box.add(m_id); + m_box.add(m_created); + m_box.add(m_joined); + m_box.add(m_nickname); + m_box.add(m_boosting); + m_box.add(m_roles); + + m_bot.hide(); + m_box.show(); + + add(m_box); +} + +void GuildSettingsMembersPaneInfo::SetUser(Snowflake user_id) { + auto &discord = Abaddon::Get().GetDiscordClient(); + const auto guild = *discord.GetGuild(GuildID); + auto member = *discord.GetMember(user_id, GuildID); + member.User = discord.GetUser(user_id); + + m_bot.set_visible(member.User->IsBot.has_value() && *member.User->IsBot); + + m_id.set_text("User ID: " + std::to_string(user_id)); + m_created.set_text("Account created: " + user_id.GetLocalTimestamp()); + if (member.JoinedAt != "") + m_joined.set_text("Joined server: " + FormatISO8601(member.JoinedAt)); + else + m_joined.set_text("Joined server: Unknown"); + m_nickname.set_text("Nickname: " + member.Nickname); + m_nickname.set_visible(member.Nickname != ""); + if (member.PremiumSince.has_value()) { + m_boosting.set_text("Boosting since " + FormatISO8601(*member.PremiumSince)); + m_boosting.show(); + } else + m_boosting.hide(); + + m_roles.show(); + m_roles.SetRoles(user_id, member.Roles, guild.OwnerID == discord.GetUserData().ID); +} + +GuildSettingsMembersPaneRoles::GuildSettingsMembersPaneRoles(Snowflake guild_id) + : GuildID(guild_id) { + auto &discord = Abaddon::Get().GetDiscordClient(); + const auto self_id = discord.GetUserData().ID; + const bool can_modify = discord.HasGuildPermission(self_id, guild_id, Permission::MANAGE_ROLES); + const auto highest_id = discord.GetMemberHighestRole(GuildID, self_id); + if (highest_id.IsValid()) + m_hoisted_position = discord.GetRole(highest_id)->Position; + + discord.signal_role_create().connect(sigc::mem_fun(*this, &GuildSettingsMembersPaneRoles::OnRoleCreate)); + discord.signal_role_update().connect(sigc::mem_fun(*this, &GuildSettingsMembersPaneRoles::OnRoleUpdate)); + discord.signal_role_delete().connect(sigc::mem_fun(*this, &GuildSettingsMembersPaneRoles::OnRoleDelete)); + + const auto guild = *discord.GetGuild(guild_id); + const auto roles = guild.FetchRoles(); + for (const auto &role : roles) { + CreateRow(can_modify, role, guild.OwnerID == self_id); + } + + m_list.set_sort_func([this](Gtk::ListBoxRow *a, Gtk::ListBoxRow *b) -> int { + auto *rowa = dynamic_cast<GuildSettingsMembersPaneRolesItem *>(a); + auto *rowb = dynamic_cast<GuildSettingsMembersPaneRolesItem *>(b); + return rowb->Position - rowa->Position; + }); + + set_propagate_natural_height(true); + set_propagate_natural_width(true); + set_hexpand(true); + set_vexpand(true); + set_halign(Gtk::ALIGN_FILL); + set_valign(Gtk::ALIGN_START); + + m_list.show(); + + add(m_list); +} + +void GuildSettingsMembersPaneRoles::SetRoles(Snowflake user_id, const std::vector<Snowflake> &roles, bool is_owner) { + UserID = user_id; + + for (auto it = m_update_connection.begin(); it != m_update_connection.end();) { + it->disconnect(); + it = m_update_connection.erase(it); + } + + m_set_role_ids = { roles.begin(), roles.end() }; + for (const auto &[role_id, row] : m_rows) { + auto role = *Abaddon::Get().GetDiscordClient().GetRole(role_id); + if (role.ID == GuildID) { + row->SetChecked(true); + row->SetToggleable(false); + } else { + row->SetToggleable(role.Position < m_hoisted_position || is_owner); + row->SetChecked(m_set_role_ids.find(role_id) != m_set_role_ids.end()); + } + } +} + +void GuildSettingsMembersPaneRoles::CreateRow(bool has_manage_roles, const RoleData &role, bool is_owner) { + auto *row = Gtk::manage(new GuildSettingsMembersPaneRolesItem(has_manage_roles, role)); + if (role.ID == GuildID) { + row->SetChecked(true); + row->SetToggleable(false); + } else + row->SetToggleable(role.Position < m_hoisted_position || is_owner); + row->signal_role_click().connect(sigc::mem_fun(*this, &GuildSettingsMembersPaneRoles::OnRoleToggle)); + row->show(); + m_rows[role.ID] = row; + m_list.add(*row); +} + +void GuildSettingsMembersPaneRoles::OnRoleToggle(Snowflake role_id, bool new_set) { + auto row = m_rows.at(role_id); + row->SetToggleable(false); + auto &discord = Abaddon::Get().GetDiscordClient(); + auto cb = [this, new_set, role_id, row](bool success) { + if (!success) { // undo + if (new_set) + m_set_role_ids.erase(role_id); + else + m_set_role_ids.insert(role_id); + } else + row->SetChecked(new_set); + + row->SetToggleable(true); + }; + + if (new_set) + m_set_role_ids.insert(role_id); + else + m_set_role_ids.erase(role_id); + + // hack to prevent cb from being called if SetRoles is called before callback completion + sigc::signal<void, bool> tmp; + m_update_connection.push_back(tmp.connect(std::move(cb))); + const auto tmp_cb = [this, tmp = std::move(tmp)](bool success) { tmp.emit(success); }; + discord.SetMemberRoles(GuildID, UserID, m_set_role_ids.begin(), m_set_role_ids.end(), sigc::track_obj(tmp_cb, *this)); +} + +void GuildSettingsMembersPaneRoles::OnRoleCreate(Snowflake guild_id, Snowflake role_id) { + if (guild_id != GuildID) return; + auto &discord = Abaddon::Get().GetDiscordClient(); + const auto self_id = discord.GetUserData().ID; + const bool can_modify = discord.HasGuildPermission(self_id, GuildID, Permission::MANAGE_ROLES); + const auto role = *discord.GetRole(role_id); + CreateRow(can_modify, role, discord.GetGuild(guild_id)->OwnerID == self_id); +} + +void GuildSettingsMembersPaneRoles::OnRoleUpdate(Snowflake guild_id, Snowflake role_id) { + if (guild_id != GuildID) return; + auto role = *Abaddon::Get().GetDiscordClient().GetRole(role_id); + m_rows.at(role_id)->UpdateRoleData(role); + m_list.invalidate_sort(); +} + +void GuildSettingsMembersPaneRoles::OnRoleDelete(Snowflake guild_id, Snowflake role_id) { + if (guild_id != GuildID) return; + delete m_rows.at(role_id); +} + +GuildSettingsMembersPaneRolesItem::GuildSettingsMembersPaneRolesItem(bool sensitive, const RoleData &role) + : RoleID(role.ID) { + UpdateRoleData(role); + + m_main.set_hexpand(true); + m_main.set_vexpand(true); + + const auto cb = [this](GdkEventButton *event) -> bool { + if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_PRIMARY) { + m_signal_role_click.emit(RoleID, !m_check.get_active()); + return true; + } + return false; + }; + m_check.signal_button_press_event().connect(cb, false); + + m_desired_sensitivity = sensitive; + ComputeSensitivity(); + + m_check.set_margin_start(5); + m_label.set_margin_start(5); + + m_main.add(m_check); + m_main.add(m_label); + add(m_main); + m_check.show(); + m_label.show(); + m_main.show(); +} + +void GuildSettingsMembersPaneRolesItem::SetChecked(bool checked) { + m_check.set_active(checked); +} + +void GuildSettingsMembersPaneRolesItem::SetToggleable(bool toggleable) { + m_desired_sensitivity = toggleable; + ComputeSensitivity(); +} + +void GuildSettingsMembersPaneRolesItem::UpdateRoleData(const RoleData &role) { + m_role = role; + Position = role.Position; + UpdateLabel(); +} + +void GuildSettingsMembersPaneRolesItem::UpdateLabel() { + if (m_role.Color) + m_label.set_markup("<span color='#" + IntToCSSColor(m_role.Color) + "'>" + Glib::Markup::escape_text(m_role.Name) + "</span>"); + else + m_label.set_text(m_role.Name); +} + +void GuildSettingsMembersPaneRolesItem::ComputeSensitivity() { + if (m_role.IsManaged) { + m_check.set_sensitive(false); + return; + } + m_check.set_sensitive(m_desired_sensitivity); +} + +GuildSettingsMembersPaneRolesItem::type_signal_role_click GuildSettingsMembersPaneRolesItem::signal_role_click() { + return m_signal_role_click; +} diff --git a/windows/guildsettings/memberspane.hpp b/windows/guildsettings/memberspane.hpp new file mode 100644 index 0000000..abffce5 --- /dev/null +++ b/windows/guildsettings/memberspane.hpp @@ -0,0 +1,133 @@ +#pragma once +#include <unordered_set> +#include <gtkmm.h> +#include "../../discord/member.hpp" +#include "../../components/lazyimage.hpp" + +class GuildSettingsMembersPaneRolesItem : public Gtk::ListBoxRow { +public: + GuildSettingsMembersPaneRolesItem(bool sensitive, const RoleData &role); + void SetChecked(bool checked); + void SetToggleable(bool toggleable); + void UpdateRoleData(const RoleData &role); + + Snowflake RoleID; + int Position; + +private: + void UpdateLabel(); + void ComputeSensitivity(); + bool m_desired_sensitivity = true; + + RoleData m_role; + + Gtk::Box m_main; + Gtk::CheckButton m_check; + Gtk::Label m_label; + + // own thing so we can stop it from actually changing + typedef sigc::signal<void, Snowflake, bool> type_signal_role_click; + + type_signal_role_click m_signal_role_click; + +public: + type_signal_role_click signal_role_click(); +}; + +class GuildSettingsMembersPaneRoles : public Gtk::ScrolledWindow { +public: + GuildSettingsMembersPaneRoles(Snowflake guild_id); + + void SetRoles(Snowflake user_id, const std::vector<Snowflake> &roles, bool is_owner); + +private: + void CreateRow(bool has_manage_roles, const RoleData &role, bool is_owner); + + void OnRoleToggle(Snowflake role_id, bool new_set); + + void OnRoleCreate(Snowflake guild_id, Snowflake role_id); + void OnRoleUpdate(Snowflake guild_id, Snowflake role_id); + void OnRoleDelete(Snowflake guild_id, Snowflake role_id); + + int m_hoisted_position = 0; + + std::vector<sigc::connection> m_update_connection; + + std::unordered_set<Snowflake> m_set_role_ids; + + Snowflake GuildID; + Snowflake UserID; + + Gtk::ListBox m_list; + + std::unordered_map<Snowflake, GuildSettingsMembersPaneRolesItem *> m_rows; +}; + +class GuildSettingsMembersPaneInfo : public Gtk::ScrolledWindow { +public: + GuildSettingsMembersPaneInfo(Snowflake guild_id); + + void SetUser(Snowflake user_id); + +private: + Snowflake GuildID; + Snowflake UserID; + + Gtk::Label m_bot; + Gtk::Label m_id; + Gtk::Label m_created; + Gtk::Label m_joined; + Gtk::Label m_nickname; + Gtk::Label m_boosting; + GuildSettingsMembersPaneRoles m_roles; + Gtk::Box m_box; +}; + +class GuildSettingsMembersPaneMembers : public Gtk::Box { +public: + GuildSettingsMembersPaneMembers(Snowflake id); + +private: + Snowflake GuildID; + + Gtk::Entry m_search; + Gtk::ScrolledWindow m_list_scroll; + Gtk::ListBox m_list; + + typedef sigc::signal<void, Snowflake> type_signal_member_select; + type_signal_member_select m_signal_member_select; + +public: + type_signal_member_select signal_member_select(); +}; + +class GuildSettingsMembersListItem : public Gtk::ListBoxRow { +public: + GuildSettingsMembersListItem(Snowflake guild_id, const GuildMember &member); + + Glib::ustring DisplayTerm; + + Snowflake UserID; + Snowflake GuildID; + +private: + void UpdateColor(); + + Gtk::EventBox m_ev; + LazyImage m_avatar; + Gtk::Label m_name; + Gtk::Box m_main; +}; + +class GuildSettingsMembersPane : public Gtk::Box { +public: + GuildSettingsMembersPane(Snowflake id); + +private: + Snowflake GuildID; + + Gtk::Box m_layout; + Gtk::Label m_note; + GuildSettingsMembersPaneMembers m_member_list; + GuildSettingsMembersPaneInfo m_member_info; +}; diff --git a/windows/guildsettingswindow.cpp b/windows/guildsettingswindow.cpp index 7666917..33844e1 100644 --- a/windows/guildsettingswindow.cpp +++ b/windows/guildsettingswindow.cpp @@ -7,7 +7,8 @@ GuildSettingsWindow::GuildSettingsWindow(Snowflake id) , m_pane_info(id) , m_pane_bans(id) , m_pane_invites(id) - , m_pane_audit_log(id) { + , m_pane_audit_log(id) + , m_pane_members(id) { auto &discord = Abaddon::Get().GetDiscordClient(); const auto guild = *discord.GetGuild(id); @@ -39,6 +40,7 @@ GuildSettingsWindow::GuildSettingsWindow(Snowflake id) m_switcher.show(); m_pane_info.show(); + m_pane_members.show(); m_pane_bans.show(); m_pane_invites.show(); m_pane_audit_log.show(); @@ -53,6 +55,7 @@ GuildSettingsWindow::GuildSettingsWindow(Snowflake id) const auto self_id = discord.GetUserData().ID; m_stack.add(m_pane_info, "info", "Info"); + m_stack.add(m_pane_members, "members", "Members"); m_stack.add(m_pane_bans, "bans", "Bans"); if (discord.HasGuildPermission(self_id, GuildID, Permission::MANAGE_GUILD)) m_stack.add(m_pane_invites, "invites", "Invites"); diff --git a/windows/guildsettingswindow.hpp b/windows/guildsettingswindow.hpp index 20435e2..bd6cfd6 100644 --- a/windows/guildsettingswindow.hpp +++ b/windows/guildsettingswindow.hpp @@ -5,6 +5,7 @@ #include "guildsettings/banspane.hpp" #include "guildsettings/invitespane.hpp" #include "guildsettings/auditlogpane.hpp" +#include "guildsettings/memberspane.hpp" class GuildSettingsWindow : public Gtk::Window { public: @@ -19,6 +20,7 @@ private: Gtk::StackSwitcher m_switcher; GuildSettingsInfoPane m_pane_info; + GuildSettingsMembersPane m_pane_members; GuildSettingsBansPane m_pane_bans; GuildSettingsInvitesPane m_pane_invites; GuildSettingsAuditLogPane m_pane_audit_log; |