From bcfb2146cdf8f3815a6c4d0042745a0e82ed4918 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Wed, 8 Dec 2021 19:12:35 -0500 Subject: mark guild as read (shift+esc) --- src/windows/mainwindow.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'src/windows/mainwindow.cpp') diff --git a/src/windows/mainwindow.cpp b/src/windows/mainwindow.cpp index 659107a..3fc0b0d 100644 --- a/src/windows/mainwindow.cpp +++ b/src/windows/mainwindow.cpp @@ -5,10 +5,13 @@ MainWindow::MainWindow() : m_main_box(Gtk::ORIENTATION_VERTICAL) , m_content_box(Gtk::ORIENTATION_HORIZONTAL) , m_chan_content_paned(Gtk::ORIENTATION_HORIZONTAL) - , m_content_members_paned(Gtk::ORIENTATION_HORIZONTAL) { + , m_content_members_paned(Gtk::ORIENTATION_HORIZONTAL) + , m_accels(Gtk::AccelGroup::create()) { set_default_size(1200, 800); get_style_context()->add_class("app-window"); + add_accel_group(m_accels); + m_menu_discord.set_label("Discord"); m_menu_discord.set_submenu(m_menu_discord_sub); m_menu_discord_connect.set_label("Connect"); @@ -42,9 +45,12 @@ MainWindow::MainWindow() m_menu_view_friends.set_label("Friends"); m_menu_view_pins.set_label("Pins"); m_menu_view_threads.set_label("Threads"); + m_menu_view_mark_guild_as_read.set_label("Mark Server as Read"); + m_menu_view_mark_guild_as_read.add_accelerator("activate", m_accels, GDK_KEY_Escape, Gdk::SHIFT_MASK, Gtk::ACCEL_VISIBLE); m_menu_view_sub.append(m_menu_view_friends); m_menu_view_sub.append(m_menu_view_pins); m_menu_view_sub.append(m_menu_view_threads); + m_menu_view_sub.append(m_menu_view_mark_guild_as_read); m_menu_view_sub.signal_popped_up().connect(sigc::mem_fun(*this, &MainWindow::OnViewSubmenuPopup)); m_menu_bar.append(m_menu_file); @@ -98,6 +104,15 @@ MainWindow::MainWindow() m_signal_action_view_threads.emit(GetChatActiveChannel()); }); + m_menu_view_mark_guild_as_read.signal_activate().connect([this] { + auto &discord = Abaddon::Get().GetDiscordClient(); + const auto channel_id = GetChatActiveChannel(); + const auto channel = discord.GetChannel(channel_id); + if (channel.has_value() && channel->GuildID.has_value()) { + discord.MarkGuildAsRead(*channel->GuildID, [](...) {}); + } + }); + m_content_box.set_hexpand(true); m_content_box.set_vexpand(true); m_content_box.show(); -- cgit v1.2.3 From 0b0135268ec4684dfcdc0a5ae5c8da947bb17e12 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Fri, 10 Dec 2021 00:15:39 -0500 Subject: basic channel mentions count indicator --- src/components/channels.cpp | 8 ++++++ src/components/channels.hpp | 4 +++ src/components/channelscellrenderer.cpp | 4 +-- src/components/unreadrenderer.cpp | 43 +++++++++++++++++++++++++-------- src/components/unreadrenderer.hpp | 5 ++-- src/discord/discord.cpp | 7 +++--- src/discord/message.cpp | 6 +++++ src/discord/message.hpp | 2 ++ src/windows/mainwindow.cpp | 1 + 9 files changed, 63 insertions(+), 17 deletions(-) (limited to 'src/windows/mainwindow.cpp') diff --git a/src/components/channels.cpp b/src/components/channels.cpp index d8fbde9..c83b5f8 100644 --- a/src/components/channels.cpp +++ b/src/components/channels.cpp @@ -176,6 +176,14 @@ ChannelList::ChannelList() discord.signal_message_ack().connect(sigc::mem_fun(*this, &ChannelList::OnMessageAck)); } +void ChannelList::UsePanedHack(Gtk::Paned& paned) { + paned.property_position().signal_changed().connect(sigc::mem_fun(*this, &ChannelList::OnPanedPositionChanged)); +} + +void ChannelList::OnPanedPositionChanged() { + m_view.queue_draw(); +} + void ChannelList::UpdateListing() { m_updating_listing = true; diff --git a/src/components/channels.hpp b/src/components/channels.hpp index d279907..2f4d045 100644 --- a/src/components/channels.hpp +++ b/src/components/channels.hpp @@ -25,7 +25,11 @@ public: void UseExpansionState(const ExpansionStateRoot &state); ExpansionStateRoot GetExpansionState() const; + void UsePanedHack(Gtk::Paned &paned); + protected: + void OnPanedPositionChanged(); + void UpdateNewGuild(const GuildData &guild); void UpdateRemoveGuild(Snowflake id); void UpdateRemoveChannel(Snowflake id); diff --git a/src/components/channelscellrenderer.cpp b/src/components/channelscellrenderer.cpp index a96668c..f507802 100644 --- a/src/components/channelscellrenderer.cpp +++ b/src/components/channelscellrenderer.cpp @@ -240,7 +240,7 @@ void CellRendererChannels::render_vfunc_guild(const Cairo::RefPtrfill(); } - UnreadRenderer::RenderUnreadOnGuild(m_property_id.get_value(), cr, background_area, cell_area); + UnreadRenderer::RenderUnreadOnGuild(m_property_id.get_value(), widget, cr, background_area, cell_area); } // category @@ -337,7 +337,7 @@ void CellRendererChannels::render_vfunc_channel(const Cairo::RefPtr &cr, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area) { +void UnreadRenderer::RenderUnreadOnGuild(Snowflake id, Gtk::Widget &widget, const Cairo::RefPtr &cr, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area) { // maybe have DiscordClient track this? auto &discord = Abaddon::Get().GetDiscordClient(); const auto channels = discord.GetChannelsInGuild(id); @@ -23,15 +23,38 @@ void UnreadRenderer::RenderUnreadOnGuild(Snowflake id, const Cairo::RefPtrfill(); } -void UnreadRenderer::RenderUnreadOnChannel(Snowflake id, const Cairo::RefPtr &cr, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area) { +void UnreadRenderer::RenderUnreadOnChannel(Snowflake id, Gtk::Widget &widget, const Cairo::RefPtr &cr, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area) { const auto state = Abaddon::Get().GetDiscordClient().GetUnreadStateForChannel(id); - if (state >= 0) { - cr->set_source_rgb(1.0, 1.0, 1.0); - const auto x = background_area.get_x(); - const auto y = background_area.get_y(); - const auto w = background_area.get_width(); - const auto h = background_area.get_height(); - cr->rectangle(x, y, 3, h); - cr->fill(); + if (state < 0) return; + cr->set_source_rgb(1.0, 1.0, 1.0); + const auto x = background_area.get_x(); + const auto y = background_area.get_y(); + const auto w = background_area.get_width(); + const auto h = background_area.get_height(); + cr->rectangle(x, y, 3, h); + cr->fill(); + + if (state < 1) return; + auto *paned = static_cast(widget.get_ancestor(Gtk::Paned::get_type())); + const auto edge = paned->get_position(); + + // surely this shouldnt be run every draw? + // https://developer-old.gnome.org/gtkmm-tutorial/3.22/sec-drawing-text.html.en + + Pango::FontDescription font; + font.set_family("sans 14"); + font.set_weight(Pango::WEIGHT_BOLD); + + auto layout = widget.create_pango_layout(std::to_string(state)); + layout->set_font_description(font); + layout->set_alignment(Pango::ALIGN_RIGHT); + + int width, height; + layout->get_pixel_size(width, height); + { + const auto x = cell_area.get_x() + std::min(edge, cell_area.get_width()) - 14 - 2; + const auto y = cell_area.get_y() + cell_area.get_height() / 2.0 - height / 2.0; + cr->move_to(x, y); + layout->show_in_cairo_context(cr); } } diff --git a/src/components/unreadrenderer.hpp b/src/components/unreadrenderer.hpp index 30446fa..1b4ddc2 100644 --- a/src/components/unreadrenderer.hpp +++ b/src/components/unreadrenderer.hpp @@ -1,10 +1,11 @@ #pragma once #include #include +#include #include "discord/snowflake.hpp" class UnreadRenderer { public: - static void RenderUnreadOnGuild(Snowflake id, const Cairo::RefPtr &cr, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area); - static void RenderUnreadOnChannel(Snowflake id, const Cairo::RefPtr &cr, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area); + static void RenderUnreadOnGuild(Snowflake id, Gtk::Widget &widget, const Cairo::RefPtr &cr, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area); + static void RenderUnreadOnChannel(Snowflake id, Gtk::Widget &widget, const Cairo::RefPtr &cr, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area); }; diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp index a8ceb5b..f21946f 100644 --- a/src/discord/discord.cpp +++ b/src/discord/discord.cpp @@ -1467,9 +1467,10 @@ void DiscordClient::HandleGatewayMessageCreate(const GatewayMessage &msg) { if (data.GuildID.has_value()) AddUserToGuild(data.Author.ID, *data.GuildID); m_last_message_id[data.ChannelID] = data.ID; - const auto iter = m_unread.find(data.ChannelID); - if (iter == m_unread.end()) - m_unread[data.ChannelID] = 0; + m_unread[data.ChannelID]; + if (data.DoesMention(GetUserData().ID)) { + m_unread[data.ChannelID]++; + } m_signal_message_create.emit(data); } diff --git a/src/discord/message.cpp b/src/discord/message.cpp index 70c557d..93d57c2 100644 --- a/src/discord/message.cpp +++ b/src/discord/message.cpp @@ -263,3 +263,9 @@ bool Message::IsDeleted() const { bool Message::IsEdited() const { return m_edited; } + +bool Message::DoesMention(Snowflake id) const noexcept { + return std::any_of(Mentions.begin(), Mentions.end(), [id](const UserData &user) { + return user.ID == id; + }); +} diff --git a/src/discord/message.hpp b/src/discord/message.hpp index 56f4c0f..244b572 100644 --- a/src/discord/message.hpp +++ b/src/discord/message.hpp @@ -212,6 +212,8 @@ struct Message { bool IsDeleted() const; bool IsEdited() const; + bool DoesMention(Snowflake id) const noexcept; + private: bool m_deleted = false; bool m_edited = false; diff --git a/src/windows/mainwindow.cpp b/src/windows/mainwindow.cpp index 3fc0b0d..d171a03 100644 --- a/src/windows/mainwindow.cpp +++ b/src/windows/mainwindow.cpp @@ -153,6 +153,7 @@ MainWindow::MainWindow() m_chan_content_paned.set_position(200); m_chan_content_paned.show(); m_content_box.add(m_chan_content_paned); + m_channel_list.UsePanedHack(m_chan_content_paned); m_content_members_paned.pack1(m_content_stack); m_content_members_paned.pack2(*member_list); -- cgit v1.2.3 From 145504bdd66e93fe13426fc943e903e4d0db08d1 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Wed, 22 Dec 2021 01:44:26 -0500 Subject: add mark all as read --- src/discord/discord.cpp | 20 ++++++++++++++++++++ src/discord/discord.hpp | 1 + src/util.hpp | 2 ++ src/windows/mainwindow.cpp | 19 +++++++++++++++---- src/windows/mainwindow.hpp | 1 + 5 files changed, 39 insertions(+), 4 deletions(-) (limited to 'src/windows/mainwindow.cpp') diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp index 4960ea6..1e9746e 100644 --- a/src/discord/discord.cpp +++ b/src/discord/discord.cpp @@ -940,6 +940,26 @@ void DiscordClient::UnmuteChannel(Snowflake channel_id, sigc::slot callback) { + AckBulkData data; + for (const auto &[unread, mention_count] : m_unread) { + const auto iter = m_last_message_id.find(unread); + if (iter == m_last_message_id.end()) continue; + auto &e = data.ReadStates.emplace_back(); + e.ID = unread; + e.LastMessageID = iter->second; + } + + if (data.ReadStates.empty()) return; + + m_http.MakePOST("/read-states/ack-bulk", nlohmann::json(data).dump(), [this, callback](const http::response_type &response) { + if (CheckCode(response)) + callback(DiscordError::NONE); + else + callback(GetCodeFromResponse(response)); + }); +} + void DiscordClient::FetchPinned(Snowflake id, sigc::slot, DiscordError code)> callback) { // return from db if we know the pins have already been requested if (m_channels_pinned_requested.find(id) != m_channels_pinned_requested.end()) { diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp index 9e526f9..5569123 100644 --- a/src/discord/discord.hpp +++ b/src/discord/discord.hpp @@ -142,6 +142,7 @@ public: void MarkGuildAsRead(Snowflake guild_id, sigc::slot callback); void MuteChannel(Snowflake channel_id, sigc::slot callback); void UnmuteChannel(Snowflake channel_id, sigc::slot callback); + void MarkAllAsRead(sigc::slot callback); bool CanModifyRole(Snowflake guild_id, Snowflake role_id) const; bool CanModifyRole(Snowflake guild_id, Snowflake role_id, Snowflake user_id) const; diff --git a/src/util.hpp b/src/util.hpp index feaf08d..f51a917 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -15,6 +15,8 @@ #include #include +#define NOOP_CALLBACK [](...) {} + namespace util { template struct is_optional : ::std::false_type {}; diff --git a/src/windows/mainwindow.cpp b/src/windows/mainwindow.cpp index d171a03..c8abb75 100644 --- a/src/windows/mainwindow.cpp +++ b/src/windows/mainwindow.cpp @@ -47,10 +47,12 @@ MainWindow::MainWindow() m_menu_view_threads.set_label("Threads"); m_menu_view_mark_guild_as_read.set_label("Mark Server as Read"); m_menu_view_mark_guild_as_read.add_accelerator("activate", m_accels, GDK_KEY_Escape, Gdk::SHIFT_MASK, Gtk::ACCEL_VISIBLE); + m_menu_view_mark_all_as_read.set_label("Mark All as Read"); m_menu_view_sub.append(m_menu_view_friends); m_menu_view_sub.append(m_menu_view_pins); m_menu_view_sub.append(m_menu_view_threads); m_menu_view_sub.append(m_menu_view_mark_guild_as_read); + m_menu_view_sub.append(m_menu_view_mark_all_as_read); m_menu_view_sub.signal_popped_up().connect(sigc::mem_fun(*this, &MainWindow::OnViewSubmenuPopup)); m_menu_bar.append(m_menu_file); @@ -109,10 +111,14 @@ MainWindow::MainWindow() const auto channel_id = GetChatActiveChannel(); const auto channel = discord.GetChannel(channel_id); if (channel.has_value() && channel->GuildID.has_value()) { - discord.MarkGuildAsRead(*channel->GuildID, [](...) {}); + discord.MarkGuildAsRead(*channel->GuildID, NOOP_CALLBACK); } }); + m_menu_view_mark_all_as_read.signal_activate().connect([this] { + Abaddon::Get().GetDiscordClient().MarkAllAsRead(NOOP_CALLBACK); + }); + m_content_box.set_hexpand(true); m_content_box.set_vexpand(true); m_content_box.show(); @@ -262,13 +268,18 @@ void MainWindow::OnDiscordSubmenuPopup(const Gdk::Rectangle *flipped_rect, const } void MainWindow::OnViewSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y) { - m_menu_view_friends.set_sensitive(Abaddon::Get().GetDiscordClient().IsStarted()); + auto &discord = Abaddon::Get().GetDiscordClient(); + const bool discord_active = discord.IsStarted(); + + m_menu_view_friends.set_sensitive(discord_active); + m_menu_view_mark_guild_as_read.set_sensitive(discord_active); + m_menu_view_mark_all_as_read.set_sensitive(discord_active); + auto channel_id = GetChatActiveChannel(); m_menu_view_pins.set_sensitive(false); m_menu_view_threads.set_sensitive(false); if (channel_id.IsValid()) { - auto channel = Abaddon::Get().GetDiscordClient().GetChannel(channel_id); - if (channel.has_value()) { + if (auto channel = discord.GetChannel(channel_id); channel.has_value()) { m_menu_view_threads.set_sensitive(channel->Type == ChannelType::GUILD_TEXT || channel->IsThread()); m_menu_view_pins.set_sensitive(channel->Type == ChannelType::GUILD_TEXT || channel->Type == ChannelType::DM || channel->Type == ChannelType::GROUP_DM || channel->IsThread()); } diff --git a/src/windows/mainwindow.hpp b/src/windows/mainwindow.hpp index 697564e..7afe782 100644 --- a/src/windows/mainwindow.hpp +++ b/src/windows/mainwindow.hpp @@ -98,5 +98,6 @@ protected: Gtk::MenuItem m_menu_view_pins; Gtk::MenuItem m_menu_view_threads; Gtk::MenuItem m_menu_view_mark_guild_as_read; + Gtk::MenuItem m_menu_view_mark_all_as_read; void OnViewSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y); }; -- cgit v1.2.3