add bans to guild settings, tweak some other things

This commit is contained in:
ouwou 2021-01-20 02:08:48 -05:00
parent 5d1db982cb
commit a010aa11a5
14 changed files with 469 additions and 9 deletions

View File

@ -175,13 +175,31 @@
.app-window button {
background: @secondary_color;
color: @text_color;
border: 1px solid #1c2e40;
text-shadow: none;
box-shadow: none;
}
.app-window button:checked {
border-top: 0px;
border-left: 0px;
border-right: 0px;
border-bottom: 3px solid #39a2ed;
color: #ffffff;
}
.app-window button:not(:checked) {
border: 3px #0000ff;
}
.app-window.background {
background: @background_color;
}
.app-window treeview {
color: @text_color;
background: @secondary_color;
}
.app-window listbox {
background: @background_color;
}

6
discord/ban.cpp Normal file
View File

@ -0,0 +1,6 @@
#include "ban.hpp"
void from_json(const nlohmann::json &j, BanData &m) {
JS_N("reason", m.Reason);
JS_D("user", m.User);
}

10
discord/ban.hpp Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include <string>
#include "user.hpp"
struct BanData {
std::string Reason; // null
UserData User; // access id
friend void from_json(const nlohmann::json &j, BanData &m);
};

View File

@ -179,6 +179,10 @@ std::optional<GuildMember> DiscordClient::GetMember(Snowflake user_id, Snowflake
return m_store.GetGuildMember(guild_id, user_id);
}
std::optional<BanData> DiscordClient::GetBan(Snowflake guild_id, Snowflake user_id) const {
return m_store.GetBan(guild_id, user_id);
}
std::optional<PermissionOverwrite> DiscordClient::GetPermissionOverwrite(Snowflake channel_id, Snowflake id) const {
return m_store.GetPermissionOverwrite(channel_id, id);
}
@ -467,6 +471,49 @@ void DiscordClient::SetGuildIcon(Snowflake id, const std::string &data, sigc::sl
});
}
void DiscordClient::UnbanUser(Snowflake guild_id, Snowflake user_id) {
UnbanUser(guild_id, user_id, [](const auto) {});
}
void DiscordClient::UnbanUser(Snowflake guild_id, Snowflake user_id, sigc::slot<void(bool success)> callback) {
sigc::signal<void, bool> signal;
signal.connect(callback);
m_http.MakeDELETE("/guilds/" + std::to_string(guild_id) + "/bans/" + std::to_string(user_id), [this, callback](const cpr::Response &response) {
callback(response.status_code == 204);
});
}
std::vector<BanData> DiscordClient::GetBansInGuild(Snowflake guild_id) {
return m_store.GetBans(guild_id);
}
void DiscordClient::FetchGuildBan(Snowflake guild_id, Snowflake user_id, sigc::slot<void(BanData)> callback) {
sigc::signal<void, BanData> signal;
signal.connect(callback);
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/bans/" + std::to_string(user_id), [this, callback, guild_id](const cpr::Response &response) {
if (response.status_code != 200) return;
auto ban = nlohmann::json::parse(response.text).get<BanData>();
m_store.SetBan(guild_id, ban.User.ID, ban);
m_store.SetUser(ban.User.ID, ban.User);
callback(ban);
});
}
void DiscordClient::FetchGuildBans(Snowflake guild_id, sigc::slot<void(std::vector<BanData>)> callback) {
sigc::signal<void, std::vector<BanData>> signal;
signal.connect(callback);
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/bans", [this, callback, guild_id](const cpr::Response &response) {
auto bans = nlohmann::json::parse(response.text).get<std::vector<BanData>>();
m_store.BeginTransaction();
for (const auto &ban : bans) {
m_store.SetBan(guild_id, ban.User.ID, ban);
m_store.SetUser(ban.User.ID, ban.User);
}
m_store.EndTransaction();
callback(bans);
});
}
void DiscordClient::UpdateToken(std::string token) {
if (!IsStarted()) {
m_token = token;
@ -623,6 +670,12 @@ void DiscordClient::HandleGatewayMessage(std::string str) {
case GatewayEvent::TYPING_START: {
HandleGatewayTypingStart(m);
} break;
case GatewayEvent::GUILD_BAN_REMOVE: {
HandleGatewayGuildBanRemove(m);
} break;
case GatewayEvent::GUILD_BAN_ADD: {
HandleGatewayGuildBanAdd(m);
} break;
}
} break;
default:
@ -724,7 +777,7 @@ void DiscordClient::HandleGatewayMessageDeleteBulk(const GatewayMessage &msg) {
for (const auto &id : data.IDs) {
auto cur = m_store.GetMessage(id);
if (!cur.has_value())
return;
continue;
cur->SetDeleted();
m_store.SetMessage(id, *cur);
@ -930,6 +983,23 @@ void DiscordClient::HandleGatewayTypingStart(const GatewayMessage &msg) {
m_signal_typing_start.emit(data.UserID, data.ChannelID);
}
void DiscordClient::HandleGatewayGuildBanRemove(const GatewayMessage &msg) {
GuildBanRemoveObject data = msg.Data;
m_store.SetUser(data.User.ID, data.User);
m_store.ClearBan(data.GuildID, data.User.ID);
m_signal_guild_ban_remove.emit(data.GuildID, data.User.ID);
}
void DiscordClient::HandleGatewayGuildBanAdd(const GatewayMessage &msg) {
GuildBanAddObject data = msg.Data;
BanData ban;
ban.Reason = "";
ban.User = data.User;
m_store.SetUser(data.User.ID, data.User);
m_store.SetBan(data.GuildID, data.User.ID, ban);
m_signal_guild_ban_add.emit(data.GuildID, data.User.ID);
}
void DiscordClient::HandleGatewayReconnect(const GatewayMessage &msg) {
m_signal_disconnected.emit(true);
inflateEnd(&m_zstream);
@ -1151,6 +1221,8 @@ void DiscordClient::LoadEventMap() {
m_event_map["CHANNEL_RECIPIENT_ADD"] = GatewayEvent::CHANNEL_RECIPIENT_ADD;
m_event_map["CHANNEL_RECIPIENT_REMOVE"] = GatewayEvent::CHANNEL_RECIPIENT_REMOVE;
m_event_map["TYPING_START"] = GatewayEvent::TYPING_START;
m_event_map["GUILD_BAN_REMOVE"] = GatewayEvent::GUILD_BAN_REMOVE;
m_event_map["GUILD_BAN_ADD"] = GatewayEvent::GUILD_BAN_ADD;
}
DiscordClient::type_signal_gateway_ready DiscordClient::signal_gateway_ready() {
@ -1232,3 +1304,11 @@ DiscordClient::type_signal_typing_start DiscordClient::signal_typing_start() {
DiscordClient::type_signal_guild_member_update DiscordClient::signal_guild_member_update() {
return m_signal_guild_member_update;
}
DiscordClient::type_signal_guild_ban_remove DiscordClient::signal_guild_ban_remove() {
return m_signal_guild_ban_remove;
}
DiscordClient::type_signal_guild_ban_add DiscordClient::signal_guild_ban_add() {
return m_signal_guild_ban_add;
}

View File

@ -86,6 +86,7 @@ public:
std::optional<RoleData> GetRole(Snowflake id) const;
std::optional<GuildData> GetGuild(Snowflake id) const;
std::optional<GuildMember> GetMember(Snowflake user_id, Snowflake guild_id) const;
std::optional<BanData> GetBan(Snowflake guild_id, Snowflake user_id) const;
Snowflake GetMemberHoistedRole(Snowflake guild_id, Snowflake user_id, bool with_color = false) const;
Snowflake GetMemberHighestRole(Snowflake guild_id, Snowflake user_id) const;
std::unordered_set<Snowflake> GetUsersInGuild(Snowflake id) const;
@ -114,6 +115,13 @@ public:
void SetGuildName(Snowflake id, const Glib::ustring &name, sigc::slot<void(bool success)> callback);
void SetGuildIcon(Snowflake id, const std::string &data);
void SetGuildIcon(Snowflake id, const std::string &data, sigc::slot<void(bool success)> callback);
void UnbanUser(Snowflake guild_id, Snowflake user_id);
void UnbanUser(Snowflake guild_id, Snowflake user_id, sigc::slot<void(bool success)> callback);
// 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);
void FetchGuildBans(Snowflake guild_id, sigc::slot<void(std::vector<BanData>)> callback);
void UpdateToken(std::string token);
void SetUserAgent(std::string agent);
@ -151,6 +159,8 @@ private:
void HandleGatewayChannelRecipientAdd(const GatewayMessage &msg);
void HandleGatewayChannelRecipientRemove(const GatewayMessage &msg);
void HandleGatewayTypingStart(const GatewayMessage &msg);
void HandleGatewayGuildBanRemove(const GatewayMessage &msg);
void HandleGatewayGuildBanAdd(const GatewayMessage &msg);
void HandleGatewayReconnect(const GatewayMessage &msg);
void HeartbeatThread();
void SendIdentify();
@ -217,9 +227,11 @@ public:
typedef sigc::signal<void, Snowflake> type_signal_role_delete;
typedef sigc::signal<void, Snowflake, Glib::ustring> type_signal_reaction_add;
typedef sigc::signal<void, Snowflake, Glib::ustring> type_signal_reaction_remove;
typedef sigc::signal<void, Snowflake, Snowflake> type_signal_typing_start; // user id, channel id
typedef sigc::signal<void, Snowflake, Snowflake> type_signal_typing_start; // user id, channel id
typedef sigc::signal<void, Snowflake, Snowflake> type_signal_guild_member_update; // guild id, user id
typedef sigc::signal<void, bool> type_signal_disconnected; // bool true if reconnecting
typedef sigc::signal<void, Snowflake, Snowflake> type_signal_guild_ban_remove; // guild id, user id
typedef sigc::signal<void, Snowflake, Snowflake> type_signal_guild_ban_add; // guild id, user id
typedef sigc::signal<void, bool> type_signal_disconnected; // bool true if reconnecting
typedef sigc::signal<void> type_signal_connected;
type_signal_gateway_ready signal_gateway_ready();
@ -240,6 +252,8 @@ public:
type_signal_reaction_remove signal_reaction_remove();
type_signal_typing_start signal_typing_start();
type_signal_guild_member_update signal_guild_member_update();
type_signal_guild_ban_remove signal_guild_ban_remove();
type_signal_guild_ban_add signal_guild_ban_add();
type_signal_disconnected signal_disconnected();
type_signal_connected signal_connected();
@ -262,6 +276,8 @@ protected:
type_signal_reaction_remove m_signal_reaction_remove;
type_signal_typing_start m_signal_typing_start;
type_signal_guild_member_update m_signal_guild_member_update;
type_signal_guild_ban_remove m_signal_guild_ban_remove;
type_signal_guild_ban_add m_signal_guild_ban_add;
type_signal_disconnected m_signal_disconnected;
type_signal_connected m_signal_connected;
};

View File

@ -252,3 +252,13 @@ void to_json(nlohmann::json &j, const ModifyGuildObject &m) {
JS_IF("name", m.Name);
JS_IF("icon", m.IconData);
}
void from_json(const nlohmann::json &j, GuildBanRemoveObject &m) {
JS_D("guild_id", m.GuildID);
JS_D("user", m.User);
}
void from_json(const nlohmann::json &j, GuildBanAddObject &m) {
JS_D("guild_id", m.GuildID);
JS_D("user", m.User);
}

View File

@ -16,6 +16,7 @@
#include "emoji.hpp"
#include "activity.hpp"
#include "sticker.hpp"
#include "ban.hpp"
// most stuff below should just be objects that get processed and thrown away immediately
@ -54,6 +55,8 @@ enum class GatewayEvent : int {
CHANNEL_RECIPIENT_ADD,
CHANNEL_RECIPIENT_REMOVE,
TYPING_START,
GUILD_BAN_REMOVE,
GUILD_BAN_ADD,
};
struct GatewayMessage {
@ -353,3 +356,17 @@ struct ModifyGuildObject {
friend void to_json(nlohmann::json &j, const ModifyGuildObject &m);
};
struct GuildBanRemoveObject {
Snowflake GuildID;
UserData User;
friend void from_json(const nlohmann::json &j, GuildBanRemoveObject &m);
};
struct GuildBanAddObject {
Snowflake GuildID;
UserData User;
friend void from_json(const nlohmann::json &j, GuildBanAddObject &m);
};

View File

@ -51,6 +51,15 @@ bool Store::IsValid() const {
return m_db_err == SQLITE_OK;
}
void Store::SetBan(Snowflake guild_id, Snowflake user_id, const BanData &ban) {
Bind(m_set_ban_stmt, 1, guild_id);
Bind(m_set_ban_stmt, 2, user_id);
Bind(m_set_ban_stmt, 3, ban.Reason);
if (!RunInsert(m_set_ban_stmt))
fprintf(stderr, "ban insert failed: %s\n", sqlite3_errstr(m_db_err));
}
void Store::SetChannel(Snowflake id, const ChannelData &chan) {
Bind(m_set_chan_stmt, 1, id);
Bind(m_set_chan_stmt, 2, static_cast<int>(chan.Type));
@ -273,6 +282,41 @@ void Store::SetUser(Snowflake id, const UserData &user) {
}
}
std::optional<BanData> Store::GetBan(Snowflake guild_id, Snowflake user_id) const {
Bind(m_get_ban_stmt, 1, guild_id);
Bind(m_get_ban_stmt, 2, user_id);
if (!FetchOne(m_get_ban_stmt)) {
if (m_db_err != SQLITE_DONE)
fprintf(stderr, "error while fetching ban: %s\n", sqlite3_errstr(m_db_err));
Reset(m_get_ban_stmt);
return std::nullopt;
}
BanData ret;
ret.User.ID = user_id;
Get(m_get_ban_stmt, 2, ret.Reason);
Reset(m_get_ban_stmt);
return ret;
}
std::vector<BanData> Store::GetBans(Snowflake guild_id) const {
Bind(m_get_bans_stmt, 1, guild_id);
std::vector<BanData> ret;
while (FetchOne(m_get_bans_stmt)) {
auto &ban = ret.emplace_back();
Get(m_get_bans_stmt, 1, ban.User.ID);
Get(m_get_bans_stmt, 2, ban.Reason);
}
Reset(m_get_bans_stmt);
if (m_db_err != SQLITE_DONE)
fprintf(stderr, "error while fetching bans: %s\n", sqlite3_errstr(m_db_err));
return ret;
}
std::optional<ChannelData> Store::GetChannel(Snowflake id) const {
Bind(m_get_chan_stmt, 1, id);
if (!FetchOne(m_get_chan_stmt)) {
@ -595,6 +639,16 @@ void Store::ClearChannel(Snowflake id) {
m_channels.erase(id);
}
void Store::ClearBan(Snowflake guild_id, Snowflake user_id) {
Bind(m_clear_ban_stmt, 1, guild_id);
Bind(m_clear_ban_stmt, 2, user_id);
if ((m_db_err = sqlite3_step(m_clear_ban_stmt)) != SQLITE_DONE)
printf("clearing ban failed: %s\n", sqlite3_errstr(m_db_err));
Reset(m_clear_ban_stmt);
}
const std::unordered_set<Snowflake> &Store::GetChannels() const {
return m_channels;
}
@ -779,6 +833,15 @@ bool Store::CreateTables() {
)
)";
constexpr const char *create_bans = R"(
CREATE TABLE IF NOT EXISTS bans (
guild_id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
reason TEXT,
PRIMARY KEY(user_id, guild_id)
)
)";
m_db_err = sqlite3_exec(m_db, create_users, nullptr, nullptr, nullptr);
if (m_db_err != SQLITE_OK) {
fprintf(stderr, "failed to create user table: %s\n", sqlite3_errstr(m_db_err));
@ -827,6 +890,12 @@ bool Store::CreateTables() {
return false;
}
m_db_err = sqlite3_exec(m_db, create_bans, nullptr, nullptr, nullptr);
if (m_db_err != SQLITE_OK) {
fprintf(stderr, "failed to create bans table: %s\n", sqlite3_errstr(m_db_err));
return false;
}
return true;
}
@ -911,6 +980,24 @@ bool Store::CreateStatements() {
SELECT * FROM channels WHERE id = ?
)";
constexpr const char *set_ban = R"(
REPLACE INTO bans VALUES (
?, ?, ?
)
)";
constexpr const char *get_ban = R"(
SELECT * FROM bans WHERE guild_id = ? AND user_id = ?
)";
constexpr const char *clear_ban = R"(
DELETE FROM bans WHERE guild_id = ? AND user_id = ?
)";
constexpr const char *get_bans = R"(
SELECT * FROM bans WHERE guild_id = ?
)";
m_db_err = sqlite3_prepare_v2(m_db, set_user, -1, &m_set_user_stmt, nullptr);
if (m_db_err != SQLITE_OK) {
fprintf(stderr, "failed to prepare set user statement: %s\n", sqlite3_errstr(m_db_err));
@ -1007,6 +1094,30 @@ bool Store::CreateStatements() {
return false;
}
m_db_err = sqlite3_prepare_v2(m_db, set_ban, -1, &m_set_ban_stmt, nullptr);
if (m_db_err != SQLITE_OK) {
fprintf(stderr, "failed to prepare set ban statement: %s\n", sqlite3_errstr(m_db_err));
return false;
}
m_db_err = sqlite3_prepare_v2(m_db, get_ban, -1, &m_get_ban_stmt, nullptr);
if (m_db_err != SQLITE_OK) {
fprintf(stderr, "failed to prepare get ban statement: %s\n", sqlite3_errstr(m_db_err));
return false;
}
m_db_err = sqlite3_prepare_v2(m_db, clear_ban, -1, &m_clear_ban_stmt, nullptr);
if (m_db_err != SQLITE_OK) {
fprintf(stderr, "failed to prepare clear ban statement: %s\n", sqlite3_errstr(m_db_err));
return false;
}
m_db_err = sqlite3_prepare_v2(m_db, get_bans, -1, &m_get_bans_stmt, nullptr);
if (m_db_err != SQLITE_OK) {
fprintf(stderr, "failed to prepare get bans statement: %s\n", sqlite3_errstr(m_db_err));
return false;
}
return true;
}
@ -1027,6 +1138,10 @@ void Store::Cleanup() {
sqlite3_finalize(m_get_guild_stmt);
sqlite3_finalize(m_set_chan_stmt);
sqlite3_finalize(m_get_chan_stmt);
sqlite3_finalize(m_set_ban_stmt);
sqlite3_finalize(m_get_ban_stmt);
sqlite3_finalize(m_clear_ban_stmt);
sqlite3_finalize(m_get_bans_stmt);
}
void Store::Bind(sqlite3_stmt *stmt, int index, int num) const {

View File

@ -26,6 +26,7 @@ public:
void SetGuildMember(Snowflake guild_id, Snowflake user_id, const GuildMember &data);
void SetPermissionOverwrite(Snowflake channel_id, Snowflake id, const PermissionOverwrite &perm);
void SetEmoji(Snowflake id, const EmojiData &emoji);
void SetBan(Snowflake guild_id, Snowflake user_id, const BanData &ban);
// slap const on everything even tho its not *really* const
@ -37,9 +38,12 @@ public:
std::optional<PermissionOverwrite> GetPermissionOverwrite(Snowflake channel_id, Snowflake id) const;
std::optional<RoleData> GetRole(Snowflake id) const;
std::optional<UserData> GetUser(Snowflake id) const;
std::optional<BanData> GetBan(Snowflake guild_id, Snowflake user_id) const;
std::vector<BanData> GetBans(Snowflake guild_id) const;
void ClearGuild(Snowflake id);
void ClearChannel(Snowflake id);
void ClearBan(Snowflake guild_id, Snowflake user_id);
using users_type = std::unordered_map<Snowflake, UserData>;
using channels_type = std::unordered_map<Snowflake, ChannelData>;
@ -104,6 +108,10 @@ private:
mutable sqlite3_stmt *m_get_guild_stmt;
mutable sqlite3_stmt *m_set_chan_stmt;
mutable sqlite3_stmt *m_get_chan_stmt;
mutable sqlite3_stmt *m_set_ban_stmt;
mutable sqlite3_stmt *m_get_ban_stmt;
mutable sqlite3_stmt *m_clear_ban_stmt;
mutable sqlite3_stmt *m_get_bans_stmt;
};
template<typename T>

View File

@ -0,0 +1,136 @@
#include "banspane.hpp"
#include "../../abaddon.hpp"
// gtk_list_store_set_value: assertion 'column >= 0 && column < priv->n_columns' failed
// dont care to figure out why this happens cuz it doesnt seem to break anything
GuildSettingsBansPane::GuildSettingsBansPane(Snowflake id)
: GuildID(id)
, m_menu_unban("Unban")
, m_menu_copy_id("Copy ID")
, m_model(Gtk::ListStore::create(m_columns)) {
set_name("guild-bans-pane");
set_hexpand(true);
set_vexpand(true);
auto &discord = Abaddon::Get().GetDiscordClient();
discord.signal_guild_ban_add().connect(sigc::mem_fun(*this, &GuildSettingsBansPane::OnBanAdd));
discord.signal_guild_ban_remove().connect(sigc::mem_fun(*this, &GuildSettingsBansPane::OnBanRemove));
const auto self_id = discord.GetUserData().ID;
const auto can_ban = discord.HasGuildPermission(self_id, GuildID, Permission::BAN_MEMBERS);
if (can_ban) {
discord.FetchGuildBans(id, sigc::mem_fun(*this, &GuildSettingsBansPane::OnGuildBansFetch));
} else {
for (const auto &ban : discord.GetBansInGuild(id))
OnGuildBanFetch(ban);
}
m_menu_unban.signal_activate().connect(sigc::mem_fun(*this, &GuildSettingsBansPane::OnMenuUnban));
m_menu_copy_id.signal_activate().connect(sigc::mem_fun(*this, &GuildSettingsBansPane::OnMenuCopyID));
m_menu_unban.show();
m_menu_copy_id.show();
m_menu.append(m_menu_unban);
m_menu.append(m_menu_copy_id);
m_view.signal_button_press_event().connect(sigc::mem_fun(*this, &GuildSettingsBansPane::OnTreeButtonPress), false);
m_view.show();
add(m_view);
m_view.set_model(m_model);
m_view.append_column("User", m_columns.m_col_user);
m_view.append_column("Reason", m_columns.m_col_reason);
}
void GuildSettingsBansPane::OnGuildBanFetch(const BanData &ban) {
const auto user = Abaddon::Get().GetDiscordClient().GetUser(ban.User.ID);
auto &row = *m_model->append();
row[m_columns.m_col_id] = ban.User.ID;
if (user.has_value())
row[m_columns.m_col_user] = user->Username + "#" + user->Discriminator;
else
row[m_columns.m_col_user] = "<@" + std::to_string(ban.User.ID) + ">";
row[m_columns.m_col_reason] = ban.Reason;
}
void GuildSettingsBansPane::OnGuildBansFetch(const std::vector<BanData> &bans) {
for (const auto &ban : bans) {
const auto user = Abaddon::Get().GetDiscordClient().GetUser(ban.User.ID);
auto &row = *m_model->append();
row[m_columns.m_col_id] = user->ID;
row[m_columns.m_col_user] = user->Username + "#" + user->Discriminator;
row[m_columns.m_col_reason] = ban.Reason;
}
}
void GuildSettingsBansPane::OnMenuUnban() {
auto selected_row = *m_view.get_selection()->get_selected();
if (selected_row) {
Snowflake id = selected_row[m_columns.m_col_id];
auto cb = [this](bool success) {
if (!success) {
Gtk::MessageDialog dlg("Failed to unban user", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
dlg.run();
}
};
Abaddon::Get().GetDiscordClient().UnbanUser(GuildID, id, sigc::track_obj(cb, *this));
}
}
void GuildSettingsBansPane::OnMenuCopyID() {
auto selected_row = *m_view.get_selection()->get_selected();
if (selected_row)
Gtk::Clipboard::get()->set_text(std::to_string(static_cast<Snowflake>(selected_row[m_columns.m_col_id])));
}
bool GuildSettingsBansPane::OnTreeButtonPress(GdkEventButton *event) {
if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_SECONDARY) {
auto &discord = Abaddon::Get().GetDiscordClient();
const auto self_id = discord.GetUserData().ID;
const auto can_ban = discord.HasGuildPermission(self_id, GuildID, Permission::BAN_MEMBERS);
m_menu_unban.set_sensitive(can_ban);
auto selection = m_view.get_selection();
Gtk::TreeModel::Path path;
if (m_view.get_path_at_pos(event->x, event->y, path)) {
m_view.get_selection()->select(path);
m_menu.popup_at_pointer(reinterpret_cast<GdkEvent *>(event));
}
return true;
}
return false;
}
void GuildSettingsBansPane::OnBanRemove(Snowflake guild_id, Snowflake user_id) {
if (guild_id != GuildID) return;
for (auto &child : m_model->children()) {
if (static_cast<Snowflake>(child[m_columns.m_col_id]) == user_id) {
m_model->erase(child);
break;
}
}
}
void GuildSettingsBansPane::OnBanAdd(Snowflake guild_id, Snowflake user_id) {
if (guild_id != GuildID) return;
auto &discord = Abaddon::Get().GetDiscordClient();
if (discord.HasGuildPermission(discord.GetUserData().ID, guild_id, Permission::BAN_MEMBERS)) {
discord.FetchGuildBan(guild_id, user_id, sigc::mem_fun(*this, &GuildSettingsBansPane::OnGuildBanFetch));
} else {
auto user = *discord.GetUser(user_id);
auto &row = *m_model->append();
row[m_columns.m_col_id] = user_id;
row[m_columns.m_col_user] = user.Username + "#" + user.Discriminator;
row[m_columns.m_col_reason] = "";
}
}
GuildSettingsBansPane::ModelColumns::ModelColumns() {
add(m_col_id);
add(m_col_user);
add(m_col_reason);
}

View File

@ -0,0 +1,38 @@
#pragma once
#include <gtkmm.h>
#include "../../discord/snowflake.hpp"
#include "../../discord/ban.hpp"
class GuildSettingsBansPane : public Gtk::ScrolledWindow {
public:
GuildSettingsBansPane(Snowflake id);
private:
void OnGuildBanFetch(const BanData &ban);
void OnGuildBansFetch(const std::vector<BanData> &bans);
void OnMenuUnban();
void OnMenuCopyID();
bool OnTreeButtonPress(GdkEventButton *event);
void OnBanRemove(Snowflake guild_id, Snowflake user_id);
void OnBanAdd(Snowflake guild_id, Snowflake user_id);
Gtk::TreeView m_view;
Snowflake GuildID;
class ModelColumns : public Gtk::TreeModel::ColumnRecord {
public:
ModelColumns();
Gtk::TreeModelColumn<Glib::ustring> m_col_user;
Gtk::TreeModelColumn<Glib::ustring> m_col_reason;
Gtk::TreeModelColumn<Snowflake> m_col_id;
};
ModelColumns m_columns;
Glib::RefPtr<Gtk::ListStore> m_model;
Gtk::Menu m_menu;
Gtk::MenuItem m_menu_unban;
Gtk::MenuItem m_menu_copy_id;
};

View File

@ -12,10 +12,6 @@ GuildSettingsInfoPane::GuildSettingsInfoPane(Snowflake id)
const auto can_modify = discord.HasGuildPermission(self_id, id, Permission::MANAGE_GUILD);
set_name("guild-info-pane");
set_margin_top(10);
set_margin_bottom(10);
set_margin_left(10);
set_margin_top(10);
m_guild_name.set_sensitive(can_modify);
m_guild_name.set_text(guild.Name);

View File

@ -4,7 +4,8 @@
GuildSettingsWindow::GuildSettingsWindow(Snowflake id)
: m_main(Gtk::ORIENTATION_VERTICAL)
, GuildID(id)
, m_pane_info(id) {
, m_pane_info(id)
, m_pane_bans(id) {
auto &discord = Abaddon::Get().GetDiscordClient();
const auto guild = *discord.GetGuild(id);
@ -34,8 +35,15 @@ GuildSettingsWindow::GuildSettingsWindow(Snowflake id)
m_switcher.show();
m_pane_info.show();
m_pane_bans.show();
m_stack.set_margin_top(10);
m_stack.set_margin_bottom(10);
m_stack.set_margin_left(10);
m_stack.set_margin_right(10);
m_stack.add(m_pane_info, "info", "Info");
m_stack.add(m_pane_bans, "bans", "Bans");
m_stack.show();
m_main.add(m_switcher);

View File

@ -2,6 +2,7 @@
#include <gtkmm.h>
#include "../discord/snowflake.hpp"
#include "guildsettings/infopane.hpp"
#include "guildsettings/banspane.hpp"
class GuildSettingsWindow : public Gtk::Window {
public:
@ -13,6 +14,7 @@ private:
Gtk::StackSwitcher m_switcher;
GuildSettingsInfoPane m_pane_info;
GuildSettingsBansPane m_pane_bans;
Snowflake GuildID;
};