summaryrefslogtreecommitdiff
path: root/components
diff options
context:
space:
mode:
Diffstat (limited to 'components')
-rw-r--r--components/channels.cpp220
-rw-r--r--components/channels.hpp29
-rw-r--r--components/chatmessage.cpp22
-rw-r--r--components/memberlist.cpp7
4 files changed, 251 insertions, 27 deletions
diff --git a/components/channels.cpp b/components/channels.cpp
index 3e7862b..b36eb4e 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -17,20 +17,27 @@ ChannelList::ChannelList()
, m_menu_category_copy_id("_Copy ID", true)
, m_menu_channel_copy_id("_Copy ID", true)
, m_menu_dm_close("") // changes depending on if group or not
- , m_menu_dm_copy_id("_Copy ID", true) {
+ , m_menu_dm_copy_id("_Copy ID", true)
+ , m_menu_thread_copy_id("_Copy ID", true)
+ , m_menu_thread_leave("_Leave", true) {
get_style_context()->add_class("channel-list");
const auto cb = [this](const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column) {
auto row = *m_model->get_iter(path);
- if (row[m_columns.m_expanded]) {
- m_view.collapse_row(path);
- row[m_columns.m_expanded] = false;
- } else {
- m_view.expand_row(path, false);
- row[m_columns.m_expanded] = true;
+ const auto type = row[m_columns.m_type];
+ // text channels should not be allowed to be collapsed
+ // maybe they should be but it seems a little difficult to handle expansion to permit this
+ if (type != RenderType::TextChannel) {
+ if (row[m_columns.m_expanded]) {
+ m_view.collapse_row(path);
+ row[m_columns.m_expanded] = false;
+ } else {
+ m_view.expand_row(path, false);
+ row[m_columns.m_expanded] = true;
+ }
}
- if (row[m_columns.m_type] == RenderType::TextChannel || row[m_columns.m_type] == RenderType::DM) {
+ if (type == RenderType::TextChannel || type == RenderType::DM || type == RenderType::Thread) {
m_signal_action_channel_item_select.emit(static_cast<Snowflake>(row[m_columns.m_id]));
}
};
@@ -116,6 +123,17 @@ ChannelList::ChannelList()
m_menu_dm.append(m_menu_dm_close);
m_menu_dm.show_all();
+ m_menu_thread_copy_id.signal_activate().connect([this] {
+ Gtk::Clipboard::get()->set_text(std::to_string((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]));
+ });
+ m_menu_thread_leave.signal_activate().connect([this] {
+ if (Abaddon::Get().ShowConfirm("Are you sure you want to leave this thread?"))
+ Abaddon::Get().GetDiscordClient().LeaveThread(static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]), "Context%20Menu", [](...) {});
+ });
+ m_menu_thread.append(m_menu_thread_copy_id);
+ m_menu_thread.append(m_menu_thread_leave);
+ m_menu_thread.show_all();
+
auto &discord = Abaddon::Get().GetDiscordClient();
discord.signal_message_create().connect(sigc::mem_fun(*this, &ChannelList::OnMessageCreate));
discord.signal_guild_create().connect(sigc::mem_fun(*this, &ChannelList::UpdateNewGuild));
@@ -123,6 +141,11 @@ ChannelList::ChannelList()
discord.signal_channel_delete().connect(sigc::mem_fun(*this, &ChannelList::UpdateRemoveChannel));
discord.signal_channel_update().connect(sigc::mem_fun(*this, &ChannelList::UpdateChannel));
discord.signal_channel_create().connect(sigc::mem_fun(*this, &ChannelList::UpdateCreateChannel));
+ discord.signal_thread_delete().connect(sigc::mem_fun(*this, &ChannelList::OnThreadDelete));
+ discord.signal_thread_update().connect(sigc::mem_fun(*this, &ChannelList::OnThreadUpdate));
+ discord.signal_thread_list_sync().connect(sigc::mem_fun(*this, &ChannelList::OnThreadListSync));
+ discord.signal_added_to_thread().connect(sigc::mem_fun(*this, &ChannelList::OnThreadJoined));
+ discord.signal_removed_from_thread().connect(sigc::mem_fun(*this, &ChannelList::OnThreadRemoved));
discord.signal_guild_update().connect(sigc::mem_fun(*this, &ChannelList::UpdateGuild));
}
@@ -206,32 +229,31 @@ void ChannelList::UpdateChannel(Snowflake id) {
channel_row[m_columns.m_sort] = *channel->Position;
}
-void ChannelList::UpdateCreateChannel(Snowflake id) {
- const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id);
- if (!channel.has_value()) return;
- if (channel->Type == ChannelType::GUILD_CATEGORY) return (void)UpdateCreateChannelCategory(*channel);
- if (channel->Type == ChannelType::DM || channel->Type == ChannelType::GROUP_DM) return UpdateCreateDMChannel(*channel);
- if (channel->Type != ChannelType::GUILD_TEXT && channel->Type != ChannelType::GUILD_NEWS) return;
+void ChannelList::UpdateCreateChannel(const ChannelData &channel) {
+ ;
+ if (channel.Type == ChannelType::GUILD_CATEGORY) return (void)UpdateCreateChannelCategory(channel);
+ if (channel.Type == ChannelType::DM || channel.Type == ChannelType::GROUP_DM) return UpdateCreateDMChannel(channel);
+ if (channel.Type != ChannelType::GUILD_TEXT && channel.Type != ChannelType::GUILD_NEWS) return;
Gtk::TreeRow channel_row;
bool orphan;
- if (channel->ParentID.has_value()) {
+ if (channel.ParentID.has_value()) {
orphan = false;
- auto iter = GetIteratorForChannelFromID(*channel->ParentID);
+ auto iter = GetIteratorForChannelFromID(*channel.ParentID);
channel_row = *m_model->append(iter->children());
} else {
orphan = true;
- auto iter = GetIteratorForGuildFromID(*channel->GuildID);
+ auto iter = GetIteratorForGuildFromID(*channel.GuildID);
channel_row = *m_model->append(iter->children());
}
channel_row[m_columns.m_type] = RenderType::TextChannel;
- channel_row[m_columns.m_id] = channel->ID;
- channel_row[m_columns.m_name] = "#" + Glib::Markup::escape_text(*channel->Name);
- channel_row[m_columns.m_nsfw] = channel->NSFW();
+ channel_row[m_columns.m_id] = channel.ID;
+ channel_row[m_columns.m_name] = "#" + Glib::Markup::escape_text(*channel.Name);
+ channel_row[m_columns.m_nsfw] = channel.NSFW();
if (orphan)
- channel_row[m_columns.m_sort] = *channel->Position + OrphanChannelSortOffset;
+ channel_row[m_columns.m_sort] = *channel.Position + OrphanChannelSortOffset;
else
- channel_row[m_columns.m_sort] = *channel->Position;
+ channel_row[m_columns.m_sort] = *channel.Position;
}
void ChannelList::UpdateGuild(Snowflake id) {
@@ -260,11 +282,88 @@ void ChannelList::UpdateGuild(Snowflake id) {
}
}
+void ChannelList::OnThreadJoined(Snowflake id) {
+ if (GetIteratorForChannelFromID(id)) return;
+ const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id);
+ if (!channel.has_value()) return;
+ const auto parent = GetIteratorForChannelFromID(*channel->ParentID);
+ if (parent)
+ CreateThreadRow(parent->children(), *channel);
+}
+
+void ChannelList::OnThreadRemoved(Snowflake id) {
+ DeleteThreadRow(id);
+}
+
+void ChannelList::OnThreadDelete(const ThreadDeleteData &data) {
+ DeleteThreadRow(data.ID);
+}
+
+// todo probably make the row stick around if its selected until the selection changes
+void ChannelList::OnThreadUpdate(const ThreadUpdateData &data) {
+ auto iter = GetIteratorForChannelFromID(data.Thread.ID);
+ if (iter)
+ (*iter)[m_columns.m_name] = "- " + Glib::Markup::escape_text(*data.Thread.Name);
+
+ if (data.Thread.ThreadMetadata->IsArchived)
+ DeleteThreadRow(data.Thread.ID);
+}
+
+void ChannelList::OnThreadListSync(const ThreadListSyncData &data) {
+ // get the threads in the guild
+ std::vector<Snowflake> threads;
+ auto guild_iter = GetIteratorForGuildFromID(data.GuildID);
+ std::queue<Gtk::TreeModel::iterator> queue;
+ queue.push(guild_iter);
+
+ while (!queue.empty()) {
+ auto item = queue.front();
+ queue.pop();
+ if ((*item)[m_columns.m_type] == RenderType::Thread)
+ threads.push_back(static_cast<Snowflake>((*item)[m_columns.m_id]));
+ for (auto child : item->children())
+ queue.push(child);
+ }
+
+ // delete all threads not present in the synced data
+ for (auto thread_id : threads) {
+ if (std::find_if(data.Threads.begin(), data.Threads.end(), [thread_id](const auto &x) { return x.ID == thread_id; }) == data.Threads.end()) {
+ auto iter = GetIteratorForChannelFromID(thread_id);
+ m_model->erase(iter);
+ }
+ }
+}
+
+void ChannelList::DeleteThreadRow(Snowflake id) {
+ auto iter = GetIteratorForChannelFromID(id);
+ if (iter)
+ m_model->erase(iter);
+}
+
+// create a temporary channel row for non-joined threads
+// and delete them when the active channel switches off of them if still not joined
void ChannelList::SetActiveChannel(Snowflake id) {
+ if (m_temporary_thread_row) {
+ const auto thread_id = static_cast<Snowflake>((*m_temporary_thread_row)[m_columns.m_id]);
+ const auto thread = Abaddon::Get().GetDiscordClient().GetChannel(thread_id);
+ if (thread.has_value() && (!thread->IsJoinedThread() || thread->ThreadMetadata->IsArchived))
+ m_model->erase(m_temporary_thread_row);
+ m_temporary_thread_row = {};
+ }
+
const auto channel_iter = GetIteratorForChannelFromID(id);
if (channel_iter) {
m_view.expand_to_path(m_model->get_path(channel_iter));
m_view.get_selection()->select(channel_iter);
+ } else {
+ m_view.get_selection()->unselect_all();
+ // SetActiveChannel should probably just take the channel object
+ const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id);
+ if (!channel.has_value() || !channel->IsThread()) return;
+ auto parent_iter = GetIteratorForChannelFromID(*channel->ParentID);
+ if (!parent_iter) return;
+ m_temporary_thread_row = CreateThreadRow(parent_iter->children(), *channel);
+ m_view.get_selection()->select(m_temporary_thread_row);
}
}
@@ -313,6 +412,22 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) {
}
}
+ std::map<Snowflake, std::vector<ChannelData>> threads;
+ for (const auto &tmp : *guild.Threads) {
+ const auto thread = discord.GetChannel(tmp.ID);
+ if (thread.has_value())
+ threads[*thread->ParentID].push_back(*thread);
+ }
+ const auto add_threads = [&](const ChannelData &channel, Gtk::TreeRow row) {
+ row[m_columns.m_expanded] = true;
+
+ const auto it = threads.find(channel.ID);
+ if (it == threads.end()) return;
+
+ for (const auto &thread : it->second)
+ CreateThreadRow(row.children(), thread);
+ };
+
for (const auto &channel : orphan_channels) {
auto channel_row = *m_model->append(guild_row.children());
channel_row[m_columns.m_type] = RenderType::TextChannel;
@@ -320,6 +435,7 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) {
channel_row[m_columns.m_name] = "#" + Glib::Markup::escape_text(*channel.Name);
channel_row[m_columns.m_sort] = *channel.Position + OrphanChannelSortOffset;
channel_row[m_columns.m_nsfw] = channel.NSFW();
+ add_threads(channel, channel_row);
}
for (const auto &[category_id, channels] : categories) {
@@ -340,6 +456,7 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) {
channel_row[m_columns.m_name] = "#" + Glib::Markup::escape_text(*channel.Name);
channel_row[m_columns.m_sort] = *channel.Position;
channel_row[m_columns.m_nsfw] = channel.NSFW();
+ add_threads(channel, channel_row);
}
}
@@ -360,6 +477,18 @@ Gtk::TreeModel::iterator ChannelList::UpdateCreateChannelCategory(const ChannelD
return cat_row;
}
+Gtk::TreeModel::iterator ChannelList::CreateThreadRow(const Gtk::TreeNodeChildren &children, const ChannelData &channel) {
+ auto thread_iter = m_model->append(children);
+ auto thread_row = *thread_iter;
+ thread_row[m_columns.m_type] = RenderType::Thread;
+ thread_row[m_columns.m_id] = channel.ID;
+ thread_row[m_columns.m_name] = "- " + Glib::Markup::escape_text(*channel.Name);
+ thread_row[m_columns.m_sort] = channel.ID;
+ thread_row[m_columns.m_nsfw] = false;
+
+ return thread_iter;
+}
+
void ChannelList::UpdateChannelCategory(const ChannelData &channel) {
auto iter = GetIteratorForChannelFromID(channel.ID);
if (!iter) return;
@@ -423,7 +552,7 @@ bool ChannelList::SelectionFunc(const Glib::RefPtr<Gtk::TreeModel> &model, const
m_last_selected = m_model->get_path(row);
auto type = (*m_model->get_iter(path))[m_columns.m_type];
- return type == RenderType::TextChannel || type == RenderType::DM;
+ return type == RenderType::TextChannel || type == RenderType::DM || type == RenderType::Thread;
}
void ChannelList::AddPrivateChannels() {
@@ -530,6 +659,10 @@ bool ChannelList::OnButtonPressEvent(GdkEventButton *ev) {
m_menu_dm_close.hide();
m_menu_dm.popup_at_pointer(reinterpret_cast<GdkEvent *>(ev));
} break;
+ case RenderType::Thread: {
+ m_menu_thread.popup_at_pointer(reinterpret_cast<GdkEvent *>(ev));
+ break;
+ } break;
default:
break;
}
@@ -614,6 +747,8 @@ void CellRendererChannels::get_preferred_width_vfunc(Gtk::Widget &widget, int &m
return get_preferred_width_vfunc_category(widget, minimum_width, natural_width);
case RenderType::TextChannel:
return get_preferred_width_vfunc_channel(widget, minimum_width, natural_width);
+ case RenderType::Thread:
+ return get_preferred_width_vfunc_thread(widget, minimum_width, natural_width);
case RenderType::DMHeader:
return get_preferred_width_vfunc_dmheader(widget, minimum_width, natural_width);
case RenderType::DM:
@@ -629,6 +764,8 @@ void CellRendererChannels::get_preferred_width_for_height_vfunc(Gtk::Widget &wid
return get_preferred_width_for_height_vfunc_category(widget, height, minimum_width, natural_width);
case RenderType::TextChannel:
return get_preferred_width_for_height_vfunc_channel(widget, height, minimum_width, natural_width);
+ case RenderType::Thread:
+ return get_preferred_width_for_height_vfunc_thread(widget, height, minimum_width, natural_width);
case RenderType::DMHeader:
return get_preferred_width_for_height_vfunc_dmheader(widget, height, minimum_width, natural_width);
case RenderType::DM:
@@ -644,6 +781,8 @@ void CellRendererChannels::get_preferred_height_vfunc(Gtk::Widget &widget, int &
return get_preferred_height_vfunc_category(widget, minimum_height, natural_height);
case RenderType::TextChannel:
return get_preferred_height_vfunc_channel(widget, minimum_height, natural_height);
+ case RenderType::Thread:
+ return get_preferred_height_vfunc_thread(widget, minimum_height, natural_height);
case RenderType::DMHeader:
return get_preferred_height_vfunc_dmheader(widget, minimum_height, natural_height);
case RenderType::DM:
@@ -659,6 +798,8 @@ void CellRendererChannels::get_preferred_height_for_width_vfunc(Gtk::Widget &wid
return get_preferred_height_for_width_vfunc_category(widget, width, minimum_height, natural_height);
case RenderType::TextChannel:
return get_preferred_height_for_width_vfunc_channel(widget, width, minimum_height, natural_height);
+ case RenderType::Thread:
+ return get_preferred_height_for_width_vfunc_thread(widget, width, minimum_height, natural_height);
case RenderType::DMHeader:
return get_preferred_height_for_width_vfunc_dmheader(widget, width, minimum_height, natural_height);
case RenderType::DM:
@@ -674,6 +815,8 @@ void CellRendererChannels::render_vfunc(const Cairo::RefPtr<Cairo::Context> &cr,
return render_vfunc_category(cr, widget, background_area, cell_area, flags);
case RenderType::TextChannel:
return render_vfunc_channel(cr, widget, background_area, cell_area, flags);
+ case RenderType::Thread:
+ return render_vfunc_thread(cr, widget, background_area, cell_area, flags);
case RenderType::DMHeader:
return render_vfunc_dmheader(cr, widget, background_area, cell_area, flags);
case RenderType::DM:
@@ -883,6 +1026,37 @@ void CellRendererChannels::render_vfunc_channel(const Cairo::RefPtr<Cairo::Conte
m_renderer_text.property_foreground_set() = false;
}
+// thread
+
+void CellRendererChannels::get_preferred_width_vfunc_thread(Gtk::Widget &widget, int &minimum_width, int &natural_width) const {
+ m_renderer_text.get_preferred_width(widget, minimum_width, natural_width);
+}
+
+void CellRendererChannels::get_preferred_width_for_height_vfunc_thread(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const {
+ get_preferred_width_vfunc_thread(widget, minimum_width, natural_width);
+}
+
+void CellRendererChannels::get_preferred_height_vfunc_thread(Gtk::Widget &widget, int &minimum_height, int &natural_height) const {
+ m_renderer_text.get_preferred_height(widget, minimum_height, natural_height);
+}
+
+void CellRendererChannels::get_preferred_height_for_width_vfunc_thread(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const {
+ get_preferred_height_vfunc_thread(widget, minimum_height, natural_height);
+}
+
+void CellRendererChannels::render_vfunc_thread(const Cairo::RefPtr<Cairo::Context> &cr, Gtk::Widget &widget, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area, Gtk::CellRendererState flags) {
+ Gtk::Requisition minimum_size, natural_size;
+ m_renderer_text.get_preferred_size(widget, minimum_size, natural_size);
+
+ const int text_x = background_area.get_x() + 26;
+ const int text_y = background_area.get_y() + background_area.get_height() / 2 - natural_size.height / 2;
+ const int text_w = natural_size.width;
+ const int text_h = natural_size.height;
+
+ Gdk::Rectangle text_cell_area(text_x, text_y, text_w, text_h);
+ m_renderer_text.render(cr, widget, background_area, text_cell_area, flags);
+}
+
// dm header
void CellRendererChannels::get_preferred_width_vfunc_dmheader(Gtk::Widget &widget, int &minimum_width, int &natural_width) const {
diff --git a/components/channels.hpp b/components/channels.hpp
index 69f1f77..506ad93 100644
--- a/components/channels.hpp
+++ b/components/channels.hpp
@@ -16,6 +16,7 @@ enum class RenderType : uint8_t {
Guild,
Category,
TextChannel,
+ Thread,
DMHeader,
DM,
@@ -77,6 +78,17 @@ protected:
const Gdk::Rectangle &cell_area,
Gtk::CellRendererState flags);
+ // thread
+ void get_preferred_width_vfunc_thread(Gtk::Widget &widget, int &minimum_width, int &natural_width) const;
+ void get_preferred_width_for_height_vfunc_thread(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const;
+ void get_preferred_height_vfunc_thread(Gtk::Widget &widget, int &minimum_height, int &natural_height) const;
+ void get_preferred_height_for_width_vfunc_thread(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const;
+ void render_vfunc_thread(const Cairo::RefPtr<Cairo::Context> &cr,
+ Gtk::Widget &widget,
+ const Gdk::Rectangle &background_area,
+ const Gdk::Rectangle &cell_area,
+ Gtk::CellRendererState flags);
+
// dm header
void get_preferred_width_vfunc_dmheader(Gtk::Widget &widget, int &minimum_width, int &natural_width) const;
void get_preferred_width_for_height_vfunc_dmheader(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const;
@@ -127,8 +139,15 @@ protected:
void UpdateRemoveGuild(Snowflake id);
void UpdateRemoveChannel(Snowflake id);
void UpdateChannel(Snowflake id);
- void UpdateCreateChannel(Snowflake id);
+ void UpdateCreateChannel(const ChannelData &channel);
void UpdateGuild(Snowflake id);
+ void DeleteThreadRow(Snowflake id);
+
+ void OnThreadJoined(Snowflake id);
+ void OnThreadRemoved(Snowflake id);
+ void OnThreadDelete(const ThreadDeleteData &data);
+ void OnThreadUpdate(const ThreadUpdateData &data);
+ void OnThreadListSync(const ThreadListSyncData &data);
Gtk::TreeView m_view;
@@ -156,6 +175,7 @@ protected:
Gtk::TreeModel::iterator AddGuild(const GuildData &guild);
Gtk::TreeModel::iterator UpdateCreateChannelCategory(const ChannelData &channel);
+ Gtk::TreeModel::iterator CreateThreadRow(const Gtk::TreeNodeChildren &children, const ChannelData &channel);
void UpdateChannelCategory(const ChannelData &channel);
@@ -179,6 +199,9 @@ protected:
void OnMessageCreate(const Message &msg);
Gtk::TreeModel::Path m_path_for_menu;
+ // cant be recovered through selection
+ Gtk::TreeModel::iterator m_temporary_thread_row;
+
Gtk::Menu m_menu_guild;
Gtk::MenuItem m_menu_guild_copy_id;
Gtk::MenuItem m_menu_guild_settings;
@@ -194,6 +217,10 @@ protected:
Gtk::MenuItem m_menu_dm_copy_id;
Gtk::MenuItem m_menu_dm_close;
+ Gtk::Menu m_menu_thread;
+ Gtk::MenuItem m_menu_thread_copy_id;
+ Gtk::MenuItem m_menu_thread_leave;
+
bool m_updating_listing = false;
public:
diff --git a/components/chatmessage.cpp b/components/chatmessage.cpp
index 63db7e9..88e3f2c 100644
--- a/components/chatmessage.cpp
+++ b/components/chatmessage.cpp
@@ -38,8 +38,10 @@ ChatMessageItemContainer *ChatMessageItemContainer::FromMessage(const Message &d
if ((data.MessageReference.has_value() || data.Interaction.has_value()) && data.Type != MessageType::CHANNEL_FOLLOW_ADD) {
auto *widget = container->CreateReplyComponent(data);
- container->m_main.add(*widget);
- container->m_main.child_property_position(*widget) = 0; // eek
+ if (widget != nullptr) {
+ container->m_main.add(*widget);
+ container->m_main.child_property_position(*widget) = 0; // eek
+ }
}
// there should only ever be 1 embed (i think?)
@@ -274,6 +276,20 @@ void ChatMessageItemContainer::UpdateTextComponent(Gtk::TextView *tv) {
case MessageType::GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING: {
b->insert_markup(s, "<i><span color='#999999'>This server has failed Discovery activity requirements for 3 weeks in a row. If this server fails for 1 more week, it will be removed from Discovery.</span></i>");
} break;
+ case MessageType::THREAD_CREATED: {
+ const auto author = Abaddon::Get().GetDiscordClient().GetUser(data->Author.ID);
+ if (data->MessageReference.has_value() && data->MessageReference->ChannelID.has_value()) {
+ auto iter = b->insert_markup(s, "<i><span color='#999999'>" + author->GetEscapedBoldName() + " started a thread: </span></i>");
+ auto tag = b->create_tag();
+ tag->property_weight() = Pango::WEIGHT_BOLD;
+ m_channel_tagmap[tag] = *data->MessageReference->ChannelID;
+ b->insert_with_tag(iter, data->Content, tag);
+
+ tv->signal_button_press_event().connect(sigc::mem_fun(*this, &ChatMessageItemContainer::OnClickChannel), false);
+ } else {
+ b->insert_markup(s, "<i><span color='#999999'>" + author->GetEscapedBoldName() + " started a thread: </span><b>" + Glib::Markup::escape_text(data->Content) + "</b></i>");
+ }
+ } break;
default: break;
}
}
@@ -607,6 +623,8 @@ Gtk::Widget *ChatMessageItemContainer::CreateReactionsComponent(const Message &d
}
Gtk::Widget *ChatMessageItemContainer::CreateReplyComponent(const Message &data) {
+ if (data.Type == MessageType::THREAD_CREATED) return nullptr;
+
auto *box = Gtk::manage(new Gtk::Box);
auto *lbl = Gtk::manage(new Gtk::Label);
lbl->set_single_line_mode(true);
diff --git a/components/memberlist.cpp b/components/memberlist.cpp
index e91ce88..804f443 100644
--- a/components/memberlist.cpp
+++ b/components/memberlist.cpp
@@ -127,7 +127,12 @@ void MemberList::UpdateMemberList() {
return;
}
- auto ids = discord.GetUsersInGuild(m_guild_id);
+ std::set<Snowflake> ids;
+ if (chan->IsThread()) {
+ const auto x = discord.GetUsersInThread(m_chan_id);
+ ids = { x.begin(), x.end() };
+ } else
+ ids = discord.GetUsersInGuild(m_guild_id);
// process all the shit first so its in proper order
std::map<int, RoleData> pos_to_role;