From 9d2d13a3898f05f9e8189f99087198726d476e1b Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Tue, 27 Jul 2021 22:54:17 -0400 Subject: very rudimentary thread support --- discord/store.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'discord/store.cpp') diff --git a/discord/store.cpp b/discord/store.cpp index 2439da5..3cac46e 100644 --- a/discord/store.cpp +++ b/discord/store.cpp @@ -194,6 +194,12 @@ void Store::SetGuild(Snowflake id, const GuildData &guild) { Bind(m_set_guild_stmt, 37, guild.ApproximateMemberCount); Bind(m_set_guild_stmt, 38, guild.ApproximatePresenceCount); Bind(m_set_guild_stmt, 39, guild.IsLazy); + if (guild.Threads.has_value()) { + snowflakes.clear(); + for (const auto &x : *guild.Threads) snowflakes.push_back(x.ID); + Bind(m_set_guild_stmt, 40, nlohmann::json(snowflakes).dump()); + } else + Bind(m_set_guild_stmt, 40, "[]"s); if (!RunInsert(m_set_guild_stmt)) fprintf(stderr, "guild insert failed: %s\n", sqlite3_errstr(m_db_err)); @@ -626,6 +632,10 @@ std::optional Store::GetGuild(Snowflake id) const { Get(m_get_guild_stmt, 36, ret.ApproximateMemberCount); Get(m_get_guild_stmt, 37, ret.ApproximatePresenceCount); Get(m_get_guild_stmt, 38, ret.IsLazy); + Get(m_get_guild_stmt, 39, tmp); + ret.Threads.emplace(); + for (const auto &id : nlohmann::json::parse(tmp).get>()) + ret.Threads->emplace_back().ID = id; Reset(m_get_guild_stmt); @@ -934,7 +944,8 @@ bool Store::CreateTables() { max_video_users INTEGER, approx_members INTEGER, approx_presences INTEGER, - lazy BOOL + lazy BOOL, + threads TEXT NOT NULL /* json */ ) )"; @@ -1116,7 +1127,7 @@ bool Store::CreateStatements() { const char *set_guild = R"( REPLACE INTO guilds VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? + ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) )"; -- cgit v1.2.3 From 40897ece3ced8bfc051708a8d85f413f330631a9 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Wed, 4 Aug 2021 21:30:24 -0400 Subject: basic window to view threads --- abaddon.cpp | 10 ++++++++++ abaddon.hpp | 1 + components/channels.cpp | 3 ++- discord/discord.cpp | 4 ++++ discord/discord.hpp | 1 + discord/store.cpp | 32 +++++++++++++++++++++++++++++++- discord/store.hpp | 2 ++ windows/mainwindow.cpp | 18 ++++++++++++++++-- windows/mainwindow.hpp | 5 +++++ 9 files changed, 72 insertions(+), 4 deletions(-) (limited to 'discord/store.cpp') diff --git a/abaddon.cpp b/abaddon.cpp index ad33e45..22ca576 100644 --- a/abaddon.cpp +++ b/abaddon.cpp @@ -15,6 +15,7 @@ #include "windows/guildsettingswindow.hpp" #include "windows/profilewindow.hpp" #include "windows/pinnedwindow.hpp" +#include "windows/threadswindow.hpp" #ifdef _WIN32 #pragma comment(lib, "crypt32.lib") @@ -94,6 +95,7 @@ int Abaddon::StartGTK() { m_main_window->signal_action_set_status().connect(sigc::mem_fun(*this, &Abaddon::ActionSetStatus)); m_main_window->signal_action_add_recipient().connect(sigc::mem_fun(*this, &Abaddon::ActionAddRecipient)); m_main_window->signal_action_view_pins().connect(sigc::mem_fun(*this, &Abaddon::ActionViewPins)); + m_main_window->signal_action_view_threads().connect(sigc::mem_fun(*this, &Abaddon::ActionViewThreads)); m_main_window->GetChannelList()->signal_action_channel_item_select().connect(sigc::mem_fun(*this, &Abaddon::ActionChannelOpened)); m_main_window->GetChannelList()->signal_action_guild_leave().connect(sigc::mem_fun(*this, &Abaddon::ActionLeaveGuild)); @@ -616,6 +618,14 @@ void Abaddon::ActionViewPins(Snowflake channel_id) { window->show(); } +void Abaddon::ActionViewThreads(Snowflake channel_id) { + const auto data = m_discord.GetChannel(channel_id); + if (!data.has_value()) return; + auto window = new ThreadsWindow(*data); + ManageHeapWindow(window); + window->show(); +} + bool Abaddon::ShowConfirm(const Glib::ustring &prompt, Gtk::Window *window) { ConfirmDialog dlg(window != nullptr ? *window : *m_main_window); dlg.SetConfirmText(prompt); diff --git a/abaddon.hpp b/abaddon.hpp index 320f621..0bd5d97 100644 --- a/abaddon.hpp +++ b/abaddon.hpp @@ -47,6 +47,7 @@ public: void ActionGuildSettings(Snowflake id); void ActionAddRecipient(Snowflake channel_id); void ActionViewPins(Snowflake channel_id); + void ActionViewThreads(Snowflake channel_id); bool ShowConfirm(const Glib::ustring &prompt, Gtk::Window *window = nullptr); diff --git a/components/channels.cpp b/components/channels.cpp index 644e090..f12d56a 100644 --- a/components/channels.cpp +++ b/components/channels.cpp @@ -308,7 +308,8 @@ void ChannelList::SetActiveChannel(Snowflake 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(); } Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) { diff --git a/discord/discord.cpp b/discord/discord.cpp index 82d2131..49684ae 100644 --- a/discord/discord.cpp +++ b/discord/discord.cpp @@ -249,6 +249,10 @@ std::set DiscordClient::GetChannelsInGuild(Snowflake id) const { return {}; } +std::vector DiscordClient::GetPublicThreads(Snowflake channel_id) const { + return m_store.GetThreads(channel_id); +} + bool DiscordClient::HasGuildPermission(Snowflake user_id, Snowflake guild_id, Permission perm) const { const auto base = ComputePermissions(user_id, guild_id); return (base & perm) == perm; diff --git a/discord/discord.hpp b/discord/discord.hpp index 619a898..fa4b4c3 100644 --- a/discord/discord.hpp +++ b/discord/discord.hpp @@ -86,6 +86,7 @@ public: std::optional GetMemberHighestRole(Snowflake guild_id, Snowflake user_id) const; std::set GetUsersInGuild(Snowflake id) const; std::set GetChannelsInGuild(Snowflake id) const; + std::vector GetPublicThreads(Snowflake channel_id) const; bool HasGuildPermission(Snowflake user_id, Snowflake guild_id, Permission perm) const; diff --git a/discord/store.cpp b/discord/store.cpp index 3cac46e..ba8e59a 100644 --- a/discord/store.cpp +++ b/discord/store.cpp @@ -499,6 +499,25 @@ std::vector Store::GetPinnedMessages(Snowflake channel_id) const { return ret; } +std::vector Store::GetThreads(Snowflake channel_id) const { + std::vector ret; + + Bind(m_get_threads_stmt, 1, channel_id); + while (FetchOne(m_get_threads_stmt)) { + Snowflake x; + Get(m_get_threads_stmt, 0, x); + auto chan = GetChannel(x); + if (chan.has_value()) + ret.push_back(*chan); + } + + Reset(m_get_threads_stmt); + + if (m_db_err != SQLITE_DONE) + fprintf(stderr, "error while fetching threads: %s\n", sqlite3_errstr(m_db_err)); + return ret; +} + std::optional Store::GetChannel(Snowflake id) const { Bind(m_get_chan_stmt, 1, id); if (!FetchOne(m_get_chan_stmt)) { @@ -1186,6 +1205,10 @@ bool Store::CreateStatements() { SELECT id FROM messages WHERE channel_id = ? AND pinned = 1 ORDER BY id ASC )"; + const char *get_threads = R"( + SELECT id FROM channels WHERE parent_id = ? AND type = 11 + )"; + 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)); @@ -1326,7 +1349,13 @@ bool Store::CreateStatements() { m_db_err = sqlite3_prepare_v2(m_db, get_pins, -1, &m_get_pins_stmt, nullptr); if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare getp ins statement: %s\n", sqlite3_errstr(m_db_err)); + fprintf(stderr, "failed to prepare get pins statement: %s\n", sqlite3_errstr(m_db_err)); + return false; + } + + m_db_err = sqlite3_prepare_v2(m_db, get_threads, -1, &m_get_threads_stmt, nullptr); + if (m_db_err != SQLITE_OK) { + fprintf(stderr, "failed to prepare get threads statement: %s\n", sqlite3_errstr(m_db_err)); return false; } @@ -1358,6 +1387,7 @@ void Store::Cleanup() { sqlite3_finalize(m_get_last_msgs_stmt); sqlite3_finalize(m_get_msg_ids_stmt); sqlite3_finalize(m_get_pins_stmt); + sqlite3_finalize(m_get_threads_stmt); } void Store::Bind(sqlite3_stmt *stmt, int index, int num) const { diff --git a/discord/store.hpp b/discord/store.hpp index 7e7d6ea..76c8a8e 100644 --- a/discord/store.hpp +++ b/discord/store.hpp @@ -44,6 +44,7 @@ public: std::vector GetLastMessages(Snowflake id, size_t num) const; std::vector GetChannelMessageIDs(Snowflake id) const; std::vector GetPinnedMessages(Snowflake channel_id) const; + std::vector GetThreads(Snowflake channel_id) const; // public void ClearGuild(Snowflake id); void ClearChannel(Snowflake id); @@ -135,6 +136,7 @@ private: mutable sqlite3_stmt *m_get_last_msgs_stmt; mutable sqlite3_stmt *m_get_msg_ids_stmt; mutable sqlite3_stmt *m_get_pins_stmt; + mutable sqlite3_stmt *m_get_threads_stmt; }; template diff --git a/windows/mainwindow.cpp b/windows/mainwindow.cpp index 0d77e96..7ddaaf2 100644 --- a/windows/mainwindow.cpp +++ b/windows/mainwindow.cpp @@ -41,8 +41,10 @@ MainWindow::MainWindow() m_menu_view.set_submenu(m_menu_view_sub); m_menu_view_friends.set_label("Friends"); m_menu_view_pins.set_label("Pins"); + m_menu_view_threads.set_label("Threads"); 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.signal_popped_up().connect(sigc::mem_fun(*this, &MainWindow::OnViewSubmenuPopup)); m_menu_bar.append(m_menu_file); @@ -92,6 +94,10 @@ MainWindow::MainWindow() m_signal_action_view_pins.emit(GetChatActiveChannel()); }); + m_menu_view_threads.signal_activate().connect([this] { + m_signal_action_view_threads.emit(GetChatActiveChannel()); + }); + m_content_box.set_hexpand(true); m_content_box.set_vexpand(true); m_content_box.show(); @@ -243,8 +249,12 @@ void MainWindow::OnViewSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gd auto channel_id = GetChatActiveChannel(); auto channel = Abaddon::Get().GetDiscordClient().GetChannel(channel_id); m_menu_view_pins.set_sensitive(false); - if (channel.has_value()) - m_menu_view_pins.set_sensitive(channel->Type == ChannelType::GUILD_TEXT); + m_menu_view_threads.set_sensitive(false); + if (channel.has_value()) { + const bool b = channel->Type == ChannelType::GUILD_TEXT; + m_menu_view_pins.set_sensitive(b); + m_menu_view_threads.set_sensitive(b); + } } ChannelList *MainWindow::GetChannelList() { @@ -290,3 +300,7 @@ MainWindow::type_signal_action_add_recipient MainWindow::signal_action_add_recip MainWindow::type_signal_action_view_pins MainWindow::signal_action_view_pins() { return m_signal_action_view_pins; } + +MainWindow::type_signal_action_view_threads MainWindow::signal_action_view_threads() { + return m_signal_action_view_threads; +} diff --git a/windows/mainwindow.hpp b/windows/mainwindow.hpp index d037796..3b41d16 100644 --- a/windows/mainwindow.hpp +++ b/windows/mainwindow.hpp @@ -35,8 +35,10 @@ public: typedef sigc::signal type_signal_action_reload_css; typedef sigc::signal type_signal_action_join_guild; typedef sigc::signal type_signal_action_set_status; + // this should probably be removed typedef sigc::signal type_signal_action_add_recipient; // channel id typedef sigc::signal type_signal_action_view_pins; // channel id + typedef sigc::signal type_signal_action_view_threads; // channel id type_signal_action_connect signal_action_connect(); type_signal_action_disconnect signal_action_disconnect(); @@ -46,6 +48,7 @@ public: type_signal_action_set_status signal_action_set_status(); type_signal_action_add_recipient signal_action_add_recipient(); type_signal_action_view_pins signal_action_view_pins(); + type_signal_action_view_threads signal_action_view_threads(); protected: type_signal_action_connect m_signal_action_connect; @@ -56,6 +59,7 @@ protected: type_signal_action_set_status m_signal_action_set_status; type_signal_action_add_recipient m_signal_action_add_recipient; type_signal_action_view_pins m_signal_action_view_pins; + type_signal_action_view_threads m_signal_action_view_threads; protected: Gtk::Box m_main_box; @@ -90,5 +94,6 @@ protected: Gtk::Menu m_menu_view_sub; Gtk::MenuItem m_menu_view_friends; Gtk::MenuItem m_menu_view_pins; + Gtk::MenuItem m_menu_view_threads; void OnViewSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y); }; -- cgit v1.2.3 From 7ffded5b1373444cf3442d759a3bd8ae0940bd7d Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Wed, 11 Aug 2021 03:32:09 -0400 Subject: rest of view threads window --- discord/discord.cpp | 18 +++++++- discord/discord.hpp | 3 +- discord/objects.cpp | 6 +++ discord/objects.hpp | 8 ++++ discord/store.cpp | 27 ++++++++++-- discord/store.hpp | 2 +- windows/threadswindow.cpp | 107 ++++++++++++++++++++++++++++++++++++++++++++++ windows/threadswindow.hpp | 60 ++++++++++++++++++++++++++ 8 files changed, 223 insertions(+), 8 deletions(-) create mode 100644 windows/threadswindow.cpp create mode 100644 windows/threadswindow.hpp (limited to 'discord/store.cpp') diff --git a/discord/discord.cpp b/discord/discord.cpp index aa363f5..7dfacc0 100644 --- a/discord/discord.cpp +++ b/discord/discord.cpp @@ -249,8 +249,22 @@ std::set DiscordClient::GetChannelsInGuild(Snowflake id) const { return {}; } -std::vector DiscordClient::GetPublicThreads(Snowflake channel_id) const { - return m_store.GetThreads(channel_id); +// there is an endpoint for this but it should be synced before this is called anyways +std::vector DiscordClient::GetActiveThreads(Snowflake channel_id) const { + return m_store.GetActiveThreads(channel_id); +} + +void DiscordClient::GetArchivedPublicThreads(Snowflake channel_id, sigc::slot callback) { + m_http.MakeGET("/channels/" + std::to_string(channel_id) + "/threads/archived/public", [this, callback](const http::response_type &r) { + if (CheckCode(r)) { + const auto data = nlohmann::json::parse(r.text).get(); + for (const auto &thread : data.Threads) + m_store.SetChannel(thread.ID, thread); + callback(DiscordError::NONE, data); + } else { + callback(GetCodeFromResponse(r), {}); + } + }); } bool DiscordClient::IsThreadJoined(Snowflake thread_id) const { diff --git a/discord/discord.hpp b/discord/discord.hpp index 11b8cbd..f001461 100644 --- a/discord/discord.hpp +++ b/discord/discord.hpp @@ -86,7 +86,8 @@ public: std::optional GetMemberHighestRole(Snowflake guild_id, Snowflake user_id) const; std::set GetUsersInGuild(Snowflake id) const; std::set GetChannelsInGuild(Snowflake id) const; - std::vector GetPublicThreads(Snowflake channel_id) const; + std::vector GetActiveThreads(Snowflake channel_id) const; + void GetArchivedPublicThreads(Snowflake channel_id, sigc::slot callback); bool IsThreadJoined(Snowflake thread_id) const; bool HasGuildPermission(Snowflake user_id, Snowflake guild_id, Permission perm) const; diff --git a/discord/objects.cpp b/discord/objects.cpp index 41164a4..723ca09 100644 --- a/discord/objects.cpp +++ b/discord/objects.cpp @@ -497,3 +497,9 @@ void from_json(const nlohmann::json &j, ThreadMembersUpdateData &m) { JS_O("added_members", m.AddedMembers); JS_O("removed_member_ids", m.RemovedMemberIDs); } + +void from_json(const nlohmann::json &j, ArchivedThreadsResponseData &m) { + JS_D("threads", m.Threads); + JS_D("members", m.Members); + JS_D("has_more", m.HasMore); +} diff --git a/discord/objects.hpp b/discord/objects.hpp index 889671f..9046162 100644 --- a/discord/objects.hpp +++ b/discord/objects.hpp @@ -700,3 +700,11 @@ struct ThreadMembersUpdateData { friend void from_json(const nlohmann::json &j, ThreadMembersUpdateData &m); }; + +struct ArchivedThreadsResponseData { + std::vector Threads; + std::vector Members; + bool HasMore; + + friend void from_json(const nlohmann::json &j, ArchivedThreadsResponseData &m); +}; diff --git a/discord/store.cpp b/discord/store.cpp index ba8e59a..3b00949 100644 --- a/discord/store.cpp +++ b/discord/store.cpp @@ -105,6 +105,16 @@ void Store::SetChannel(Snowflake id, const ChannelData &chan) { Bind(m_set_chan_stmt, 17, chan.ParentID); Bind(m_set_chan_stmt, 18, chan.LastPinTimestamp); + if (chan.ThreadMetadata.has_value()) { + Bind(m_set_chan_stmt, 19, chan.ThreadMetadata->IsArchived); + Bind(m_set_chan_stmt, 20, chan.ThreadMetadata->AutoArchiveDuration); + Bind(m_set_chan_stmt, 21, chan.ThreadMetadata->ArchiveTimestamp); + } else { + Bind(m_set_chan_stmt, 19, nullptr); + Bind(m_set_chan_stmt, 20, nullptr); + Bind(m_set_chan_stmt, 21, nullptr); + } + if (!RunInsert(m_set_chan_stmt)) fprintf(stderr, "channel insert failed: %s\n", sqlite3_errstr(m_db_err)); @@ -499,7 +509,7 @@ std::vector Store::GetPinnedMessages(Snowflake channel_id) const { return ret; } -std::vector Store::GetThreads(Snowflake channel_id) const { +std::vector Store::GetActiveThreads(Snowflake channel_id) const { std::vector ret; Bind(m_get_threads_stmt, 1, channel_id); @@ -552,6 +562,12 @@ std::optional Store::GetChannel(Snowflake id) const { Get(m_get_chan_stmt, 15, ret.ApplicationID); Get(m_get_chan_stmt, 16, ret.ParentID); Get(m_get_chan_stmt, 17, ret.LastPinTimestamp); + if (!IsNull(m_get_chan_stmt, 18)) { + ret.ThreadMetadata.emplace(); + Get(m_get_chan_stmt, 18, ret.ThreadMetadata->IsArchived); + Get(m_get_chan_stmt, 19, ret.ThreadMetadata->AutoArchiveDuration); + Get(m_get_chan_stmt, 20, ret.ThreadMetadata->ArchiveTimestamp); + } Reset(m_get_chan_stmt); @@ -987,7 +1003,10 @@ bool Store::CreateTables() { owner_id INTEGER, application_id INTEGER, parent_id INTEGER, - last_pin_timestamp TEXT + last_pin_timestamp TEXT, + archived BOOL, /* threads */ + auto_archive INTEGER, /* threads */ + archived_ts TEXT /* threads */ ) )"; @@ -1156,7 +1175,7 @@ bool Store::CreateStatements() { const char *set_chan = R"( REPLACE INTO channels VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? + ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) )"; @@ -1206,7 +1225,7 @@ bool Store::CreateStatements() { )"; const char *get_threads = R"( - SELECT id FROM channels WHERE parent_id = ? AND type = 11 + SELECT id FROM channels WHERE parent_id = ? AND (type = 10 OR type = 11 OR type = 12) AND archived = FALSE )"; m_db_err = sqlite3_prepare_v2(m_db, set_user, -1, &m_set_user_stmt, nullptr); diff --git a/discord/store.hpp b/discord/store.hpp index 76c8a8e..8e19e20 100644 --- a/discord/store.hpp +++ b/discord/store.hpp @@ -44,7 +44,7 @@ public: std::vector GetLastMessages(Snowflake id, size_t num) const; std::vector GetChannelMessageIDs(Snowflake id) const; std::vector GetPinnedMessages(Snowflake channel_id) const; - std::vector GetThreads(Snowflake channel_id) const; // public + std::vector GetActiveThreads(Snowflake channel_id) const; // public void ClearGuild(Snowflake id); void ClearChannel(Snowflake id); diff --git a/windows/threadswindow.cpp b/windows/threadswindow.cpp new file mode 100644 index 0000000..a772eeb --- /dev/null +++ b/windows/threadswindow.cpp @@ -0,0 +1,107 @@ +#include "threadswindow.hpp" +#include "../abaddon.hpp" + +ThreadsWindow::ThreadsWindow(const ChannelData &channel) + : m_channel_id(channel.ID) + , m_box(Gtk::ORIENTATION_VERTICAL) + , m_active(channel) + , m_archived(channel) { + set_name("threads-window"); + set_default_size(450, 375); + set_title("#" + *channel.Name + " - Threads"); + set_position(Gtk::WIN_POS_CENTER); + get_style_context()->add_class("app-window"); + get_style_context()->add_class("app-popup"); + get_style_context()->add_class("threads-window"); + + const auto cb = [this](Snowflake id) { + Abaddon::Get().ActionChannelOpened(id); + hide(); + }; + m_active.signal_thread_open().connect(cb); + m_archived.signal_thread_open().connect(cb); + + m_switcher.set_halign(Gtk::ALIGN_CENTER); + m_switcher.set_stack(m_stack); + + m_stack.add(m_active, "active", "Active Threads"); + m_stack.add(m_archived, "archived", "Archived Threads"); + + m_active.show(); + m_archived.show(); + m_switcher.show(); + m_stack.show(); + m_box.show(); + + m_box.add(m_switcher); + m_box.add(m_stack); + add(m_box); +} + +ThreadListRow::ThreadListRow(const ChannelData &channel) + : ID(channel.ID) + , m_label(*channel.Name, Gtk::ALIGN_START) { + m_label.show(); + add(m_label); +} + +ActiveThreadsList::ActiveThreadsList(const ChannelData &channel) { + set_vexpand(true); + + m_list.set_selection_mode(Gtk::SELECTION_SINGLE); + m_list.set_hexpand(true); + m_list.show(); + + add(m_list); + + m_list.signal_button_press_event().connect([this](GdkEventButton *ev) -> bool { + if (ev->button == GDK_BUTTON_PRIMARY && ev->type == GDK_2BUTTON_PRESS) { + if (auto row = dynamic_cast(m_list.get_selected_row())) + m_signal_thread_open.emit(row->ID); + } + return false; + }); + + const auto threads = Abaddon::Get().GetDiscordClient().GetActiveThreads(channel.ID); + for (const auto &thread : threads) { + auto row = Gtk::manage(new ThreadListRow(thread)); + row->show(); + m_list.add(*row); + } +} + +ActiveThreadsList::type_signal_thread_open ActiveThreadsList::signal_thread_open() { + return m_signal_thread_open; +} + +ArchivedThreadsList::ArchivedThreadsList(const ChannelData &channel) { + set_vexpand(true); + + m_list.set_selection_mode(Gtk::SELECTION_SINGLE); + m_list.set_hexpand(true); + m_list.show(); + + add(m_list); + + m_list.signal_button_press_event().connect([this](GdkEventButton *ev) -> bool { + if (ev->button == GDK_BUTTON_PRIMARY && ev->type == GDK_2BUTTON_PRESS) { + if (auto row = dynamic_cast(m_list.get_selected_row())) + m_signal_thread_open.emit(row->ID); + } + return false; + }); + + Abaddon::Get().GetDiscordClient().GetArchivedPublicThreads(channel.ID, sigc::mem_fun(*this, &ArchivedThreadsList::OnPublicFetched)); +} + +void ArchivedThreadsList::OnPublicFetched(DiscordError code, const ArchivedThreadsResponseData &data) { + for (const auto &thread : data.Threads) { + auto row = Gtk::manage(new ThreadListRow(thread)); + row->show(); + m_list.add(*row); + } +} + +ArchivedThreadsList::type_signal_thread_open ArchivedThreadsList::signal_thread_open() { + return m_signal_thread_open; +} diff --git a/windows/threadswindow.hpp b/windows/threadswindow.hpp new file mode 100644 index 0000000..a0c77e8 --- /dev/null +++ b/windows/threadswindow.hpp @@ -0,0 +1,60 @@ +#pragma once +#include +#include "../discord/objects.hpp" + +class ActiveThreadsList : public Gtk::ScrolledWindow { +public: + ActiveThreadsList(const ChannelData &channel); + +private: + Gtk::ListBox m_list; + + using type_signal_thread_open = sigc::signal; + type_signal_thread_open m_signal_thread_open; + +public: + type_signal_thread_open signal_thread_open(); +}; + +class ArchivedThreadsList : public Gtk::ScrolledWindow { +public: + ArchivedThreadsList(const ChannelData &channel); + +private: + Gtk::ListBox m_list; + + void OnPublicFetched(DiscordError code, const ArchivedThreadsResponseData &data); + + using type_signal_thread_open = sigc::signal; + type_signal_thread_open m_signal_thread_open; + +public: + type_signal_thread_open signal_thread_open(); +}; + +// view all threads in a channel +class ThreadsWindow : public Gtk::Window { +public: + ThreadsWindow(const ChannelData &channel); + +private: + Snowflake m_channel_id; + + Gtk::StackSwitcher m_switcher; + Gtk::Stack m_stack; + + Gtk::Box m_box; + + ActiveThreadsList m_active; + ArchivedThreadsList m_archived; +}; + +class ThreadListRow : public Gtk::ListBoxRow { +public: + ThreadListRow(const ChannelData &channel); + + Snowflake ID; + +private: + Gtk::Label m_label; +}; -- cgit v1.2.3 From 3333e2ce8c79687b3e01d014f711898dac0a7979 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Tue, 24 Aug 2021 23:04:11 -0400 Subject: make ClearChannel actually remove the channel from the db so that the threads window does not show deleted threads in active --- discord/store.cpp | 17 +++++++++++++++++ discord/store.hpp | 1 + 2 files changed, 18 insertions(+) (limited to 'discord/store.cpp') diff --git a/discord/store.cpp b/discord/store.cpp index 3b00949..57cfe73 100644 --- a/discord/store.cpp +++ b/discord/store.cpp @@ -805,6 +805,12 @@ void Store::ClearGuild(Snowflake id) { void Store::ClearChannel(Snowflake id) { m_channels.erase(id); + Bind(m_clear_chan_stmt, 1, id); + + if ((m_db_err = sqlite3_step(m_clear_chan_stmt)) != SQLITE_DONE) + printf("clearing channel failed: %s\n", sqlite3_errstr(m_db_err)); + + Reset(m_clear_chan_stmt); } void Store::ClearBan(Snowflake guild_id, Snowflake user_id) { @@ -1228,6 +1234,10 @@ bool Store::CreateStatements() { SELECT id FROM channels WHERE parent_id = ? AND (type = 10 OR type = 11 OR type = 12) AND archived = FALSE )"; + const char *clear_chan = R"( + DELETE FROM channels WHERE 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)); @@ -1378,6 +1388,12 @@ bool Store::CreateStatements() { return false; } + m_db_err = sqlite3_prepare_v2(m_db, clear_chan, -1, &m_clear_chan_stmt, nullptr); + if (m_db_err != SQLITE_OK) { + fprintf(stderr, "failed to prepare clear channel statement: %s\n", sqlite3_errstr(m_db_err)); + return false; + } + return true; } @@ -1407,6 +1423,7 @@ void Store::Cleanup() { sqlite3_finalize(m_get_msg_ids_stmt); sqlite3_finalize(m_get_pins_stmt); sqlite3_finalize(m_get_threads_stmt); + sqlite3_finalize(m_clear_chan_stmt); } void Store::Bind(sqlite3_stmt *stmt, int index, int num) const { diff --git a/discord/store.hpp b/discord/store.hpp index 8e19e20..f84d13e 100644 --- a/discord/store.hpp +++ b/discord/store.hpp @@ -137,6 +137,7 @@ private: mutable sqlite3_stmt *m_get_msg_ids_stmt; mutable sqlite3_stmt *m_get_pins_stmt; mutable sqlite3_stmt *m_get_threads_stmt; + mutable sqlite3_stmt *m_clear_chan_stmt; }; template -- cgit v1.2.3