summaryrefslogtreecommitdiff
path: root/src/components/channels.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/channels.cpp')
-rw-r--r--src/components/channels.cpp212
1 files changed, 191 insertions, 21 deletions
diff --git a/src/components/channels.cpp b/src/components/channels.cpp
index b691ab5..e45b5c0 100644
--- a/src/components/channels.cpp
+++ b/src/components/channels.cpp
@@ -19,8 +19,16 @@ ChannelList::ChannelList()
, m_menu_channel_open_tab("Open in New _Tab", true)
, m_menu_dm_open_tab("Open in New _Tab", true)
#endif
+#ifdef WITH_VOICE
+ , m_menu_voice_channel_join("_Join", true)
+ , m_menu_voice_channel_disconnect("_Disconnect", true)
+#endif
, m_menu_dm_copy_id("_Copy ID", true)
, m_menu_dm_close("") // changes depending on if group or not
+#ifdef WITH_VOICE
+ , m_menu_dm_join_voice("Join _Voice", true)
+ , m_menu_dm_disconnect_voice("_Disconnect Voice", true)
+#endif
, m_menu_thread_copy_id("_Copy ID", true)
, m_menu_thread_leave("_Leave", true)
, m_menu_thread_archive("_Archive", true)
@@ -34,7 +42,11 @@ ChannelList::ChannelList()
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
+#ifdef WITH_VOICE
+ if (type != RenderType::TextChannel && type != RenderType::VoiceChannel) {
+#else
if (type != RenderType::TextChannel) {
+#endif
if (row[m_columns.m_expanded]) {
m_view.collapse_row(path);
row[m_columns.m_expanded] = false;
@@ -44,7 +56,11 @@ ChannelList::ChannelList()
}
}
+#ifdef WITH_VOICE
+ if (type == RenderType::TextChannel || type == RenderType::DM || type == RenderType::Thread || type == RenderType::VoiceChannel) {
+#else
if (type == RenderType::TextChannel || type == RenderType::DM || type == RenderType::Thread) {
+#endif
const auto id = static_cast<Snowflake>(row[m_columns.m_id]);
m_signal_action_channel_item_select.emit(id);
Abaddon::Get().GetDiscordClient().MarkChannelAsRead(id, [](...) {});
@@ -160,6 +176,21 @@ ChannelList::ChannelList()
m_menu_channel.append(m_menu_channel_copy_id);
m_menu_channel.show_all();
+#ifdef WITH_VOICE
+ m_menu_voice_channel_join.signal_activate().connect([this]() {
+ const auto id = static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]);
+ m_signal_action_join_voice_channel.emit(id);
+ });
+
+ m_menu_voice_channel_disconnect.signal_activate().connect([this]() {
+ m_signal_action_disconnect_voice.emit();
+ });
+
+ m_menu_voice_channel.append(m_menu_voice_channel_join);
+ m_menu_voice_channel.append(m_menu_voice_channel_disconnect);
+ m_menu_voice_channel.show_all();
+#endif
+
m_menu_dm_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]));
});
@@ -191,6 +222,17 @@ ChannelList::ChannelList()
#endif
m_menu_dm.append(m_menu_dm_toggle_mute);
m_menu_dm.append(m_menu_dm_close);
+#ifdef WITH_VOICE
+ m_menu_dm_join_voice.signal_activate().connect([this]() {
+ const auto id = static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]);
+ m_signal_action_join_voice_channel.emit(id);
+ });
+ m_menu_dm_disconnect_voice.signal_activate().connect([this]() {
+ m_signal_action_disconnect_voice.emit();
+ });
+ m_menu_dm.append(m_menu_dm_join_voice);
+ m_menu_dm.append(m_menu_dm_disconnect_voice);
+#endif
m_menu_dm.append(m_menu_dm_copy_id);
m_menu_dm.show_all();
@@ -244,6 +286,11 @@ ChannelList::ChannelList()
discord.signal_channel_unmuted().connect(sigc::mem_fun(*this, &ChannelList::OnChannelUnmute));
discord.signal_guild_muted().connect(sigc::mem_fun(*this, &ChannelList::OnGuildMute));
discord.signal_guild_unmuted().connect(sigc::mem_fun(*this, &ChannelList::OnGuildUnmute));
+
+#if WITH_VOICE
+ discord.signal_voice_user_connect().connect(sigc::mem_fun(*this, &ChannelList::OnVoiceUserConnect));
+ discord.signal_voice_user_disconnect().connect(sigc::mem_fun(*this, &ChannelList::OnVoiceUserDisconnect));
+#endif
}
void ChannelList::UsePanedHack(Gtk::Paned &paned) {
@@ -332,13 +379,13 @@ void ChannelList::UpdateRemoveGuild(Snowflake id) {
}
void ChannelList::UpdateRemoveChannel(Snowflake id) {
- auto iter = GetIteratorForChannelFromID(id);
+ auto iter = GetIteratorForRowFromID(id);
if (!iter) return;
m_model->erase(iter);
}
void ChannelList::UpdateChannel(Snowflake id) {
- auto iter = GetIteratorForChannelFromID(id);
+ auto iter = GetIteratorForRowFromID(id);
auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id);
if (!iter || !channel.has_value()) return;
if (channel->Type == ChannelType::GUILD_CATEGORY) return UpdateChannelCategory(*channel);
@@ -353,7 +400,7 @@ void ChannelList::UpdateChannel(Snowflake id) {
// check if the parent has changed
Gtk::TreeModel::iterator new_parent;
if (channel->ParentID.has_value())
- new_parent = GetIteratorForChannelFromID(*channel->ParentID);
+ new_parent = GetIteratorForRowFromID(*channel->ParentID);
else if (channel->GuildID.has_value())
new_parent = GetIteratorForGuildFromID(*channel->GuildID);
@@ -370,7 +417,7 @@ void ChannelList::UpdateCreateChannel(const ChannelData &channel) {
bool orphan;
if (channel.ParentID.has_value()) {
orphan = false;
- auto iter = GetIteratorForChannelFromID(*channel.ParentID);
+ auto iter = GetIteratorForRowFromID(*channel.ParentID);
channel_row = *m_model->append(iter->children());
} else {
orphan = true;
@@ -412,10 +459,10 @@ void ChannelList::UpdateGuild(Snowflake id) {
}
void ChannelList::OnThreadJoined(Snowflake id) {
- if (GetIteratorForChannelFromID(id)) return;
+ if (GetIteratorForRowFromID(id)) return;
const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id);
if (!channel.has_value()) return;
- const auto parent = GetIteratorForChannelFromID(*channel->ParentID);
+ const auto parent = GetIteratorForRowFromID(*channel->ParentID);
if (parent)
CreateThreadRow(parent->children(), *channel);
}
@@ -430,7 +477,7 @@ void ChannelList::OnThreadDelete(const ThreadDeleteData &data) {
// 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);
+ auto iter = GetIteratorForRowFromID(data.Thread.ID);
if (iter)
(*iter)[m_columns.m_name] = "- " + Glib::Markup::escape_text(*data.Thread.Name);
@@ -459,7 +506,7 @@ void ChannelList::OnThreadListSync(const ThreadListSyncData &data) {
// 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);
+ auto iter = GetIteratorForRowFromID(thread_id);
m_model->erase(iter);
}
}
@@ -467,25 +514,49 @@ void ChannelList::OnThreadListSync(const ThreadListSyncData &data) {
// delete all archived threads
for (auto thread : data.Threads) {
if (thread.ThreadMetadata->IsArchived) {
- if (auto iter = GetIteratorForChannelFromID(thread.ID))
+ if (auto iter = GetIteratorForRowFromID(thread.ID))
m_model->erase(iter);
}
}
}
+#ifdef WITH_VOICE
+void ChannelList::OnVoiceUserConnect(Snowflake user_id, Snowflake channel_id) {
+ auto parent_iter = GetIteratorForRowFromIDOfType(channel_id, RenderType::VoiceChannel);
+ if (!parent_iter) return;
+ const auto user = Abaddon::Get().GetDiscordClient().GetUser(user_id);
+ if (!user.has_value()) return;
+
+ auto user_row = *m_model->append(parent_iter->children());
+ user_row[m_columns.m_type] = RenderType::VoiceParticipant;
+ user_row[m_columns.m_id] = user_id;
+ if (user.has_value()) {
+ user_row[m_columns.m_name] = user->GetEscapedName();
+ } else {
+ user_row[m_columns.m_name] = "<i>Unknown</i>";
+ }
+}
+
+void ChannelList::OnVoiceUserDisconnect(Snowflake user_id, Snowflake channel_id) {
+ if (auto iter = GetIteratorForRowFromIDOfType(user_id, RenderType::VoiceParticipant)) {
+ m_model->erase(iter);
+ }
+}
+#endif
+
void ChannelList::DeleteThreadRow(Snowflake id) {
- auto iter = GetIteratorForChannelFromID(id);
+ auto iter = GetIteratorForRowFromID(id);
if (iter)
m_model->erase(iter);
}
void ChannelList::OnChannelMute(Snowflake id) {
- if (auto iter = GetIteratorForChannelFromID(id))
+ if (auto iter = GetIteratorForRowFromID(id))
m_model->row_changed(m_model->get_path(iter), iter);
}
void ChannelList::OnChannelUnmute(Snowflake id) {
- if (auto iter = GetIteratorForChannelFromID(id))
+ if (auto iter = GetIteratorForRowFromID(id))
m_model->row_changed(m_model->get_path(iter), iter);
}
@@ -516,7 +587,7 @@ void ChannelList::SetActiveChannel(Snowflake id, bool expand_to) {
m_temporary_thread_row = {};
}
- const auto channel_iter = GetIteratorForChannelFromID(id);
+ const auto channel_iter = GetIteratorForRowFromID(id);
if (channel_iter) {
if (expand_to) {
m_view.expand_to_path(m_model->get_path(channel_iter));
@@ -526,7 +597,7 @@ void ChannelList::SetActiveChannel(Snowflake id, bool expand_to) {
m_view.get_selection()->unselect_all();
const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id);
if (!channel.has_value() || !channel->IsThread()) return;
- auto parent_iter = GetIteratorForChannelFromID(*channel->ParentID);
+ auto parent_iter = GetIteratorForRowFromID(*channel->ParentID);
if (!parent_iter) return;
m_temporary_thread_row = CreateThreadRow(parent_iter->children(), *channel);
m_view.get_selection()->select(m_temporary_thread_row);
@@ -662,7 +733,11 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild, const Gtk
for (const auto &channel_ : *guild.Channels) {
const auto channel = discord.GetChannel(channel_.ID);
if (!channel.has_value()) continue;
+#ifdef WITH_VOICE
+ if (channel->Type == ChannelType::GUILD_TEXT || channel->Type == ChannelType::GUILD_NEWS || channel->Type == ChannelType::GUILD_VOICE) {
+#else
if (channel->Type == ChannelType::GUILD_TEXT || channel->Type == ChannelType::GUILD_NEWS) {
+#endif
if (channel->ParentID.has_value())
categories[*channel->ParentID].push_back(*channel);
else
@@ -688,9 +763,33 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild, const Gtk
m_tmp_row_map[thread.ID] = CreateThreadRow(row.children(), thread);
};
+#ifdef WITH_VOICE
+ auto add_voice_participants = [this, &discord](const ChannelData &channel, const Gtk::TreeNodeChildren &root) {
+ for (auto user_id : discord.GetUsersInVoiceChannel(channel.ID)) {
+ const auto user = discord.GetUser(user_id);
+
+ auto user_row = *m_model->append(root);
+ user_row[m_columns.m_type] = RenderType::VoiceParticipant;
+ user_row[m_columns.m_id] = user_id;
+ if (user.has_value()) {
+ user_row[m_columns.m_name] = user->GetEscapedName();
+ } else {
+ user_row[m_columns.m_name] = "<i>Unknown</i>";
+ }
+ }
+ };
+#endif
+
for (const auto &channel : orphan_channels) {
auto channel_row = *m_model->append(guild_row.children());
- channel_row[m_columns.m_type] = RenderType::TextChannel;
+ if (IsTextChannel(channel.Type))
+ channel_row[m_columns.m_type] = RenderType::TextChannel;
+#ifdef WITH_VOICE
+ else {
+ channel_row[m_columns.m_type] = RenderType::VoiceChannel;
+ add_voice_participants(channel, channel_row->children());
+ }
+#endif
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_sort] = *channel.Position + OrphanChannelSortOffset;
@@ -713,7 +812,14 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild, const Gtk
for (const auto &channel : channels) {
auto channel_row = *m_model->append(cat_row.children());
- channel_row[m_columns.m_type] = RenderType::TextChannel;
+ if (IsTextChannel(channel.Type))
+ channel_row[m_columns.m_type] = RenderType::TextChannel;
+#ifdef WITH_VOICE
+ else {
+ channel_row[m_columns.m_type] = RenderType::VoiceChannel;
+ add_voice_participants(channel, channel_row->children());
+ }
+#endif
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_sort] = *channel.Position;
@@ -753,7 +859,7 @@ Gtk::TreeModel::iterator ChannelList::CreateThreadRow(const Gtk::TreeNodeChildre
}
void ChannelList::UpdateChannelCategory(const ChannelData &channel) {
- auto iter = GetIteratorForChannelFromID(channel.ID);
+ auto iter = GetIteratorForRowFromID(channel.ID);
if (!iter) return;
(*iter)[m_columns.m_sort] = *channel.Position;
@@ -791,7 +897,7 @@ Gtk::TreeModel::iterator ChannelList::GetIteratorForGuildFromID(Snowflake id) {
return {};
}
-Gtk::TreeModel::iterator ChannelList::GetIteratorForChannelFromID(Snowflake id) {
+Gtk::TreeModel::iterator ChannelList::GetIteratorForRowFromID(Snowflake id) {
std::queue<Gtk::TreeModel::iterator> queue;
for (const auto &child : m_model->children())
for (const auto &child2 : child.children())
@@ -808,6 +914,23 @@ Gtk::TreeModel::iterator ChannelList::GetIteratorForChannelFromID(Snowflake id)
return {};
}
+Gtk::TreeModel::iterator ChannelList::GetIteratorForRowFromIDOfType(Snowflake id, RenderType type) {
+ std::queue<Gtk::TreeModel::iterator> queue;
+ for (const auto &child : m_model->children())
+ for (const auto &child2 : child.children())
+ queue.push(child2);
+
+ while (!queue.empty()) {
+ auto item = queue.front();
+ if ((*item)[m_columns.m_type] == type && (*item)[m_columns.m_id] == id) return item;
+ for (const auto &child : item->children())
+ queue.push(child);
+ queue.pop();
+ }
+
+ return {};
+}
+
bool ChannelList::IsTextChannel(ChannelType type) {
return type == ChannelType::GUILD_TEXT || type == ChannelType::GUILD_NEWS;
}
@@ -838,7 +961,11 @@ 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];
+#ifdef WITH_VOICE
+ return type == RenderType::TextChannel || type == RenderType::DM || type == RenderType::Thread || type == RenderType::VoiceChannel;
+#else
return type == RenderType::TextChannel || type == RenderType::DM || type == RenderType::Thread;
+#endif
}
void ChannelList::AddPrivateChannels() {
@@ -914,7 +1041,7 @@ void ChannelList::UpdateCreateDMChannel(const ChannelData &dm) {
void ChannelList::OnMessageAck(const MessageAckData &data) {
// trick renderer into redrawing
m_model->row_changed(Gtk::TreeModel::Path("0"), m_model->get_iter("0")); // 0 is always path for dm header
- auto iter = GetIteratorForChannelFromID(data.ChannelID);
+ auto iter = GetIteratorForRowFromID(data.ChannelID);
if (iter) m_model->row_changed(m_model->get_path(iter), iter);
auto channel = Abaddon::Get().GetDiscordClient().GetChannel(data.ChannelID);
if (channel.has_value() && channel->GuildID.has_value()) {
@@ -924,7 +1051,7 @@ void ChannelList::OnMessageAck(const MessageAckData &data) {
}
void ChannelList::OnMessageCreate(const Message &msg) {
- auto iter = GetIteratorForChannelFromID(msg.ChannelID);
+ auto iter = GetIteratorForRowFromID(msg.ChannelID);
if (iter) m_model->row_changed(m_model->get_path(iter), iter); // redraw
const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(msg.ChannelID);
if (!channel.has_value()) return;
@@ -954,6 +1081,12 @@ bool ChannelList::OnButtonPressEvent(GdkEventButton *ev) {
OnChannelSubmenuPopup();
m_menu_channel.popup_at_pointer(reinterpret_cast<GdkEvent *>(ev));
break;
+#ifdef WITH_VOICE
+ case RenderType::VoiceChannel:
+ OnVoiceChannelSubmenuPopup();
+ m_menu_voice_channel.popup_at_pointer(reinterpret_cast<GdkEvent *>(ev));
+ break;
+#endif
case RenderType::DM: {
OnDMSubmenuPopup();
const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(static_cast<Snowflake>(row[m_columns.m_id]));
@@ -1046,14 +1179,41 @@ void ChannelList::OnChannelSubmenuPopup() {
m_menu_channel_toggle_mute.set_label("Mute");
}
+#ifdef WITH_VOICE
+void ChannelList::OnVoiceChannelSubmenuPopup() {
+ const auto iter = m_model->get_iter(m_path_for_menu);
+ if (!iter) return;
+ const auto id = static_cast<Snowflake>((*iter)[m_columns.m_id]);
+ auto &discord = Abaddon::Get().GetDiscordClient();
+ if (discord.IsVoiceConnected() || discord.IsVoiceConnecting()) {
+ m_menu_voice_channel_join.set_sensitive(false);
+ m_menu_voice_channel_disconnect.set_sensitive(discord.GetVoiceChannelID() == id);
+ } else {
+ m_menu_voice_channel_join.set_sensitive(true);
+ m_menu_voice_channel_disconnect.set_sensitive(false);
+ }
+}
+#endif
+
void ChannelList::OnDMSubmenuPopup() {
auto iter = m_model->get_iter(m_path_for_menu);
if (!iter) return;
const auto id = static_cast<Snowflake>((*iter)[m_columns.m_id]);
- if (Abaddon::Get().GetDiscordClient().IsChannelMuted(id))
+ auto &discord = Abaddon::Get().GetDiscordClient();
+ if (discord.IsChannelMuted(id))
m_menu_dm_toggle_mute.set_label("Unmute");
else
m_menu_dm_toggle_mute.set_label("Mute");
+
+#ifdef WITH_VOICE
+ if (discord.IsVoiceConnected() || discord.IsVoiceConnecting()) {
+ m_menu_dm_join_voice.set_sensitive(false);
+ m_menu_dm_disconnect_voice.set_sensitive(discord.GetVoiceChannelID() == id);
+ } else {
+ m_menu_dm_join_voice.set_sensitive(true);
+ m_menu_dm_disconnect_voice.set_sensitive(false);
+ }
+#endif
}
void ChannelList::OnThreadSubmenuPopup() {
@@ -1096,6 +1256,16 @@ ChannelList::type_signal_action_open_new_tab ChannelList::signal_action_open_new
}
#endif
+#ifdef WITH_VOICE
+ChannelList::type_signal_action_join_voice_channel ChannelList::signal_action_join_voice_channel() {
+ return m_signal_action_join_voice_channel;
+}
+
+ChannelList::type_signal_action_disconnect_voice ChannelList::signal_action_disconnect_voice() {
+ return m_signal_action_disconnect_voice;
+}
+#endif
+
ChannelList::ModelColumns::ModelColumns() {
add(m_type);
add(m_id);