forked from OpenGamers/abaddon
very rudimentary thread support
This commit is contained in:
@@ -22,15 +22,20 @@ ChannelList::ChannelList()
|
||||
|
||||
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]));
|
||||
}
|
||||
};
|
||||
@@ -313,6 +318,28 @@ 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) {
|
||||
auto thread_row = *m_model->append(row.children());
|
||||
thread_row[m_columns.m_type] = RenderType::Thread;
|
||||
thread_row[m_columns.m_id] = thread.ID;
|
||||
thread_row[m_columns.m_name] = "- " + Glib::Markup::escape_text(*thread.Name);
|
||||
thread_row[m_columns.m_sort] = thread.ID;
|
||||
thread_row[m_columns.m_nsfw] = false;
|
||||
}
|
||||
};
|
||||
|
||||
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 +347,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 +368,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,7 +452,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() {
|
||||
@@ -614,6 +643,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 +660,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 +677,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 +694,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 +711,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 +922,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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
#include "../abaddon.hpp"
|
||||
#include "channel.hpp"
|
||||
|
||||
void from_json(const nlohmann::json &j, ThreadMetadata &m) {
|
||||
JS_D("archived", m.IsArchived);
|
||||
JS_D("auto_archive_duration", m.AutoArchiveDuration);
|
||||
JS_D("archive_timestamp", m.ArchiveTimestamp);
|
||||
JS_O("locked", m.IsLocked);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ChannelData &m) {
|
||||
JS_D("id", m.ID);
|
||||
JS_D("type", m.Type);
|
||||
@@ -21,6 +28,7 @@ void from_json(const nlohmann::json &j, ChannelData &m) {
|
||||
JS_O("application_id", m.ApplicationID);
|
||||
JS_ON("parent_id", m.ParentID);
|
||||
JS_ON("last_pin_timestamp", m.LastPinTimestamp);
|
||||
JS_O("thread_metadata", m.ThreadMetadata);
|
||||
}
|
||||
|
||||
void ChannelData::update_from_json(const nlohmann::json &j) {
|
||||
|
||||
@@ -15,9 +15,10 @@ enum class ChannelType : int {
|
||||
GUILD_NEWS = 5,
|
||||
GUILD_STORE = 6,
|
||||
/* 7 and 8 were used for LFG */
|
||||
/* 9 and 10 were used for threads */
|
||||
PUBLIC_THREAD = 11,
|
||||
PRIVATE_THREAD = 12,
|
||||
/* 9 was used for threads */
|
||||
GUILD_NEWS_THREAD = 10,
|
||||
GUILD_PUBLIC_THREAD = 11,
|
||||
GUILD_PRIVATE_THREAD = 12,
|
||||
GUILD_STAGE_VOICE = 13,
|
||||
};
|
||||
|
||||
@@ -37,6 +38,15 @@ constexpr const char *GetStagePrivacyDisplayString(StagePrivacy e) {
|
||||
}
|
||||
}
|
||||
|
||||
struct ThreadMetadata {
|
||||
bool IsArchived;
|
||||
int AutoArchiveDuration;
|
||||
std::string ArchiveTimestamp;
|
||||
std::optional<bool> IsLocked;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ThreadMetadata &m);
|
||||
};
|
||||
|
||||
struct ChannelData {
|
||||
Snowflake ID;
|
||||
ChannelType Type;
|
||||
@@ -57,6 +67,7 @@ struct ChannelData {
|
||||
std::optional<Snowflake> ApplicationID;
|
||||
std::optional<Snowflake> ParentID; // null
|
||||
std::optional<std::string> LastPinTimestamp; // null
|
||||
std::optional<ThreadMetadata> ThreadMetadata;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ChannelData &m);
|
||||
void update_from_json(const nlohmann::json &j);
|
||||
|
||||
@@ -1209,7 +1209,7 @@ void DiscordClient::ProcessNewGuild(GuildData &guild) {
|
||||
m_store.BeginTransaction();
|
||||
|
||||
m_store.SetGuild(guild.ID, guild);
|
||||
if (guild.Channels.has_value())
|
||||
if (guild.Channels.has_value()) {
|
||||
for (auto &c : *guild.Channels) {
|
||||
c.GuildID = guild.ID;
|
||||
m_store.SetChannel(c.ID, c);
|
||||
@@ -1218,6 +1218,14 @@ void DiscordClient::ProcessNewGuild(GuildData &guild) {
|
||||
m_store.SetPermissionOverwrite(c.ID, p.ID, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (guild.Threads.has_value()) {
|
||||
for (auto& c : *guild.Threads) {
|
||||
c.GuildID = guild.ID;
|
||||
m_store.SetChannel(c.ID, c);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &r : *guild.Roles)
|
||||
m_store.SetRole(r.ID, r);
|
||||
|
||||
@@ -43,6 +43,7 @@ void from_json(const nlohmann::json &j, GuildData &m) {
|
||||
// JS_O("voice_states", m.VoiceStates);
|
||||
// JS_O("members", m.Members);
|
||||
JS_O("channels", m.Channels);
|
||||
JS_O("threads", m.Threads);
|
||||
// JS_O("presences", m.Presences);
|
||||
JS_ON("max_presences", m.MaxPresences);
|
||||
JS_O("max_members", m.MaxMembers);
|
||||
|
||||
@@ -80,6 +80,7 @@ struct GuildData {
|
||||
std::optional<int> MaxVideoChannelUsers;
|
||||
std::optional<int> ApproximateMemberCount;
|
||||
std::optional<int> ApproximatePresenceCount;
|
||||
std::optional<std::vector<ChannelData>> Threads; // only with permissions to view, id only
|
||||
|
||||
// undocumented
|
||||
// std::map<std::string, Unknown> GuildHashes;
|
||||
|
||||
@@ -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<GuildData> 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<std::vector<Snowflake>>())
|
||||
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 (
|
||||
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
||||
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
||||
)
|
||||
)";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user