add menu item to add recipient to group dm

This commit is contained in:
ouwou
2021-02-18 14:38:40 -05:00
parent a721600016
commit 3ab3119602
13 changed files with 223 additions and 2 deletions

View File

@@ -8,6 +8,7 @@
#include "dialogs/joinguild.hpp"
#include "dialogs/confirm.hpp"
#include "dialogs/setstatus.hpp"
#include "dialogs/friendpicker.hpp"
#include "abaddon.hpp"
#include "windows/guildsettingswindow.hpp"
#include "windows/profilewindow.hpp"
@@ -124,6 +125,7 @@ int Abaddon::StartGTK() {
m_main_window->signal_action_join_guild().connect(sigc::mem_fun(*this, &Abaddon::ActionJoinGuildDialog));
m_main_window->signal_action_set_status().connect(sigc::mem_fun(*this, &Abaddon::ActionSetStatus));
m_main_window->signal_action_reload_settings().connect(sigc::mem_fun(*this, &Abaddon::ActionReloadSettings));
m_main_window->signal_action_add_recipient().connect(sigc::mem_fun(*this, &Abaddon::ActionAddRecipient));
m_main_window->signal_action_show_user_menu().connect(sigc::mem_fun(*this, &Abaddon::ShowUserMenu));
@@ -547,6 +549,15 @@ void Abaddon::ActionGuildSettings(Snowflake id) {
window->show();
}
void Abaddon::ActionAddRecipient(Snowflake channel_id) {
FriendPickerDialog dlg(*m_main_window);
auto response = dlg.run();
if (response == Gtk::RESPONSE_OK) {
auto user_id = dlg.GetUserID();
m_discord.AddGroupDMRecipient(channel_id, user_id);
}
}
void Abaddon::ActionReloadSettings() {
m_settings.Reload();
}

View File

@@ -46,6 +46,7 @@ public:
void ActionReactionAdd(Snowflake id, const Glib::ustring &param);
void ActionReactionRemove(Snowflake id, const Glib::ustring &param);
void ActionGuildSettings(Snowflake id);
void ActionAddRecipient(Snowflake channel_id);
void ActionReloadSettings();
void ActionReloadCSS();

96
dialogs/friendpicker.cpp Normal file
View File

@@ -0,0 +1,96 @@
#include "friendpicker.hpp"
#include "../abaddon.hpp"
FriendPickerDialog::FriendPickerDialog(Gtk::Window &parent)
: Gtk::Dialog("Pick a friend", parent, true)
, m_bbox(Gtk::ORIENTATION_HORIZONTAL) {
set_default_size(300, 300);
get_style_context()->add_class("app-window");
get_style_context()->add_class("app-popup");
m_ok_button = add_button("OK", Gtk::RESPONSE_OK);
m_cancel_button = add_button("Cancel", Gtk::RESPONSE_CANCEL);
m_ok_button->set_sensitive(false);
auto &discord = Abaddon::Get().GetDiscordClient();
auto relationships = discord.GetRelationships(RelationshipType::Friend);
for (auto id : relationships) {
auto *item = Gtk::manage(new FriendPickerDialogItem(id));
item->show();
m_list.add(*item);
}
m_list.signal_row_activated().connect(sigc::mem_fun(*this, &FriendPickerDialog::OnRowActivated));
m_list.signal_selected_rows_changed().connect(sigc::mem_fun(*this, &FriendPickerDialog::OnSelectionChange));
m_list.set_activate_on_single_click(false);
m_list.set_selection_mode(Gtk::SELECTION_SINGLE);
m_main.set_propagate_natural_height(true);
m_main.add(m_list);
get_content_area()->add(m_main);
show_all_children();
}
Snowflake FriendPickerDialog::GetUserID() const {
return m_chosen_id;
}
void FriendPickerDialog::OnRowActivated(Gtk::ListBoxRow *row) {
auto *x = dynamic_cast<FriendPickerDialogItem *>(row);
if (x != nullptr) {
m_chosen_id = x->ID;
response(Gtk::RESPONSE_OK);
}
}
void FriendPickerDialog::OnSelectionChange() {
auto selection = m_list.get_selected_row();
m_ok_button->set_sensitive(false);
if (selection != nullptr) {
auto *row = dynamic_cast<FriendPickerDialogItem *>(selection);
if (!row) return;
m_chosen_id = row->ID;
m_ok_button->set_sensitive(true);
}
}
FriendPickerDialogItem::FriendPickerDialogItem(Snowflake user_id)
: ID(user_id)
, m_layout(Gtk::ORIENTATION_HORIZONTAL) {
auto user = *Abaddon::Get().GetDiscordClient().GetUser(user_id);
m_name.set_markup("<b>" +
Glib::Markup::escape_text(user.Username) + "</b>#" + user.Discriminator);
m_name.set_single_line_mode(true);
m_avatar.property_pixbuf() = Abaddon::Get().GetImageManager().GetPlaceholder(32);
if (user.HasAvatar()) {
if (user.HasAnimatedAvatar() && Abaddon::Get().GetSettings().GetShowAnimations()) {
auto cb = [this](const Glib::RefPtr<Gdk::PixbufAnimation> &pb) {
m_avatar.property_pixbuf_animation() = pb;
};
Abaddon::Get().GetImageManager().LoadAnimationFromURL(user.GetAvatarURL("gif", "32"), 32, 32, sigc::track_obj(cb, *this));
} else {
auto cb = [this](const Glib::RefPtr<Gdk::Pixbuf> &pb) {
m_avatar.property_pixbuf() = pb->scale_simple(32, 32, Gdk::INTERP_BILINEAR);
};
Abaddon::Get().GetImageManager().LoadFromURL(user.GetAvatarURL("png", "32"), sigc::track_obj(cb, *this));
}
}
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_layout.add(m_avatar);
m_layout.add(m_name);
add(m_layout);
show_all_children();
}

35
dialogs/friendpicker.hpp Normal file
View File

@@ -0,0 +1,35 @@
#pragma once
#include <gtkmm.h>
#include "../discord/snowflake.hpp"
class FriendPickerDialog : public Gtk::Dialog {
public:
FriendPickerDialog(Gtk::Window &parent);
Snowflake GetUserID() const;
protected:
void OnRowActivated(Gtk::ListBoxRow *row);
void OnSelectionChange();
Snowflake m_chosen_id;
Gtk::ScrolledWindow m_main;
Gtk::ListBox m_list;
Gtk::ButtonBox m_bbox;
Gtk::Button *m_ok_button;
Gtk::Button *m_cancel_button;
};
class FriendPickerDialogItem : public Gtk::ListBoxRow {
public:
FriendPickerDialogItem(Snowflake user_id);
Snowflake ID;
private:
Gtk::Box m_layout;
Gtk::Image m_avatar;
Gtk::Label m_name;
};

View File

@@ -522,6 +522,12 @@ void DiscordClient::DeleteInvite(const std::string &code, sigc::slot<void(bool s
});
}
void DiscordClient::AddGroupDMRecipient(Snowflake channel_id, Snowflake user_id) {
m_http.MakePUT("/channels/" + std::to_string(channel_id) + "/recipients/" + std::to_string(user_id), "", [this](const http::response_type &response) {
CheckCode(response);
});
}
void DiscordClient::RemoveGroupDMRecipient(Snowflake channel_id, Snowflake user_id) {
m_http.MakeDELETE("/channels/" + std::to_string(channel_id) + "/recipients/" + std::to_string(user_id), [this](const http::response_type &response) {
CheckCode(response);
@@ -645,6 +651,14 @@ std::optional<PresenceStatus> DiscordClient::GetUserStatus(Snowflake id) const {
return std::nullopt;
}
std::unordered_set<Snowflake> DiscordClient::GetRelationships(RelationshipType type) const {
std::unordered_set<Snowflake> ret;
for (const auto &[id, rtype] : m_user_relationships)
if (rtype == type)
ret.insert(id);
return ret;
}
void DiscordClient::HandleGatewayMessageRaw(std::string str) {
// handles multiple zlib compressed messages, calling HandleGatewayMessage when a full message is received
std::vector<uint8_t> buf(str.begin(), str.end());
@@ -890,6 +904,10 @@ void DiscordClient::HandleGatewayReady(const GatewayMessage &msg) {
}
}
if (data.Relationships.has_value())
for (const auto &relationship : *data.Relationships)
m_user_relationships[relationship.ID] = relationship.Type;
m_store.EndTransaction();
m_session_id = data.SessionID;

View File

@@ -120,6 +120,7 @@ public:
void UnbanUser(Snowflake guild_id, Snowflake user_id, sigc::slot<void(bool success)> callback);
void DeleteInvite(const std::string &code);
void DeleteInvite(const std::string &code, sigc::slot<void(bool success)> callback);
void AddGroupDMRecipient(Snowflake channel_id, Snowflake user_id);
void RemoveGroupDMRecipient(Snowflake channel_id, Snowflake user_id);
// FetchGuildBans fetches all bans+reasons via api, this func fetches stored bans (so usually just GUILD_BAN_ADD data)
@@ -143,6 +144,8 @@ public:
std::optional<PresenceStatus> GetUserStatus(Snowflake id) const;
std::unordered_set<Snowflake> GetRelationships(RelationshipType type) const;
private:
static const constexpr int InflateChunkSize = 0x10000;
std::vector<uint8_t> m_compressed_buf;
@@ -207,6 +210,8 @@ private:
std::unordered_map<Snowflake, PresenceStatus> m_user_to_status;
std::unordered_map<Snowflake, RelationshipType> m_user_relationships;
UserData m_user_data;
UserSettings m_user_settings;

View File

@@ -75,7 +75,8 @@ void HTTPClient::MakePUT(const std::string &path, const std::string &payload, st
m_futures.push_back(std::async(std::launch::async, [this, path, cb, payload] {
http::request req(http::REQUEST_PUT, m_api_base + path);
req.set_header("Authorization", m_authorization);
req.set_header("Content-Type", "application/json");
if (payload != "")
req.set_header("Content-Type", "application/json");
req.set_user_agent(m_agent != "" ? m_agent : "Abaddon");
req.set_body(payload);
#ifdef USE_LOCAL_PROXY

View File

@@ -118,6 +118,7 @@ void from_json(const nlohmann::json &j, ReadyEventData &m) {
JS_D("private_channels", m.PrivateChannels);
JS_O("users", m.Users);
JS_ON("merged_members", m.MergedMembers);
JS_O("relationships", m.Relationships);
}
void from_json(const nlohmann::json &j, MergedPresence &m) {

View File

@@ -18,6 +18,7 @@
#include "sticker.hpp"
#include "ban.hpp"
#include "auditlog.hpp"
#include "relationship.hpp"
// most stuff below should just be objects that get processed and thrown away immediately
@@ -200,6 +201,7 @@ struct ReadyEventData {
std::optional<int> FriendSuggestionCount;
UserSettings Settings;
std::optional<std::vector<std::vector<GuildMember>>> MergedMembers;
std::optional<std::vector<RelationshipData>> Relationships;
// std::vector<Unknown> ConnectedAccounts; // opt
// std::map<std::string, Unknown> Consents; // opt
// std::vector<Unknown> Experiments; // opt
@@ -207,7 +209,6 @@ struct ReadyEventData {
// std::map<Unknown, Unknown> Notes; // opt
// std::vector<PresenceData> Presences; // opt
// std::vector<ReadStateData> ReadStates; // opt
// std::vector<RelationshipData> Relationships; // opt
// Unknown Tutorial; // opt, null
// std::vector<GuildSettingData> UserGuildSettings; // opt

6
discord/relationship.cpp Normal file
View File

@@ -0,0 +1,6 @@
#include "relationship.hpp"
void from_json(const nlohmann::json &j, RelationshipData &m) {
JS_D("type", m.Type);
JS_D("id", m.ID);
}

21
discord/relationship.hpp Normal file
View File

@@ -0,0 +1,21 @@
#pragma once
#include "json.hpp"
#include "user.hpp"
enum class RelationshipType {
None = 0,
Friend = 1,
Blocked = 2,
PendingIncoming = 3,
PendingOutgoing = 4,
Implicit = 5,
};
struct RelationshipData {
// Snowflake UserID; this is the same as ID apparently but it looks new so i wont touch it
RelationshipType Type;
Snowflake ID;
// Unknown Nickname; // null
friend void from_json(const nlohmann::json &j, RelationshipData &m);
};

View File

@@ -20,11 +20,14 @@ MainWindow::MainWindow()
m_menu_discord_join_guild.set_sensitive(false);
m_menu_discord_set_status.set_label("Set Status");
m_menu_discord_set_status.set_sensitive(false);
m_menu_discord_add_recipient.set_label("Add user to DM");
m_menu_discord_sub.append(m_menu_discord_connect);
m_menu_discord_sub.append(m_menu_discord_disconnect);
m_menu_discord_sub.append(m_menu_discord_set_token);
m_menu_discord_sub.append(m_menu_discord_join_guild);
m_menu_discord_sub.append(m_menu_discord_set_status);
m_menu_discord_sub.append(m_menu_discord_add_recipient);
m_menu_discord_sub.signal_popped_up().connect(sigc::mem_fun(*this, &MainWindow::OnDiscordSubmenuPopup)); // this gets called twice for some reason
m_menu_discord.set_submenu(m_menu_discord_sub);
m_menu_file.set_label("File");
@@ -72,6 +75,10 @@ MainWindow::MainWindow()
Abaddon::Get().GetImageManager().ClearCache();
});
m_menu_discord_add_recipient.signal_activate().connect([this] {
m_signal_action_add_recipient.emit(GetChatActiveChannel());
});
m_content_box.set_hexpand(true);
m_content_box.set_vexpand(true);
m_content_box.show();
@@ -237,6 +244,15 @@ void MainWindow::UpdateChatReactionRemove(Snowflake id, const Glib::ustring &par
m_chat.UpdateReactions(id);
}
void MainWindow::OnDiscordSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y) {
auto &discord = Abaddon::Get().GetDiscordClient();
auto channel_id = GetChatActiveChannel();
auto channel = discord.GetChannel(channel_id);
m_menu_discord_add_recipient.set_visible(false);
if (channel.has_value() && channel->GetDMRecipients().size() + 1 < 10)
m_menu_discord_add_recipient.set_visible(channel->Type == ChannelType::GROUP_DM);
}
ChannelList *MainWindow::GetChannelList() {
return &m_channel_list;
}
@@ -280,3 +296,7 @@ MainWindow::type_signal_action_show_user_menu MainWindow::signal_action_show_use
MainWindow::type_signal_action_reload_settings MainWindow::signal_action_reload_settings() {
return m_signal_action_reload_settings;
}
MainWindow::type_signal_action_add_recipient MainWindow::signal_action_add_recipient() {
return m_signal_action_add_recipient;
}

View File

@@ -42,6 +42,7 @@ public:
typedef sigc::signal<void> type_signal_action_set_status;
typedef sigc::signal<void, const GdkEvent *, Snowflake, Snowflake> type_signal_action_show_user_menu;
typedef sigc::signal<void> type_signal_action_reload_settings;
typedef sigc::signal<void, Snowflake> type_signal_action_add_recipient; // channel id
type_signal_action_connect signal_action_connect();
type_signal_action_disconnect signal_action_disconnect();
@@ -51,6 +52,7 @@ public:
type_signal_action_set_status signal_action_set_status();
type_signal_action_show_user_menu signal_action_show_user_menu();
type_signal_action_reload_settings signal_action_reload_settings();
type_signal_action_add_recipient signal_action_add_recipient();
protected:
type_signal_action_connect m_signal_action_connect;
@@ -61,6 +63,7 @@ protected:
type_signal_action_set_status m_signal_action_set_status;
type_signal_action_show_user_menu m_signal_action_show_user_menu;
type_signal_action_reload_settings m_signal_action_reload_settings;
type_signal_action_add_recipient m_signal_action_add_recipient;
protected:
Gtk::Box m_main_box;
@@ -80,6 +83,8 @@ protected:
Gtk::MenuItem m_menu_discord_set_token;
Gtk::MenuItem m_menu_discord_join_guild;
Gtk::MenuItem m_menu_discord_set_status;
Gtk::MenuItem m_menu_discord_add_recipient; // move me somewhere else some day
void OnDiscordSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y);
Gtk::MenuItem m_menu_file;
Gtk::Menu m_menu_file_sub;