diff options
author | ouwou <26526779+ouwou@users.noreply.github.com> | 2021-01-01 02:39:07 -0500 |
---|---|---|
committer | ouwou <26526779+ouwou@users.noreply.github.com> | 2021-01-01 02:39:07 -0500 |
commit | 6985448eb9b03a317a8d33374f79bb0e483ea87c (patch) | |
tree | 56777653c0dc653b828c584bc8d88b8ee83f6189 | |
parent | 73c5b49e995c7af82125694d4ed9f103843bc6c9 (diff) | |
download | abaddon-portaudio-6985448eb9b03a317a8d33374f79bb0e483ea87c.tar.gz abaddon-portaudio-6985448eb9b03a317a8d33374f79bb0e483ea87c.zip |
display replies
-rw-r--r-- | components/chatmessage.cpp | 121 | ||||
-rw-r--r-- | components/chatmessage.hpp | 5 | ||||
-rw-r--r-- | css/main.css | 13 | ||||
-rw-r--r-- | discord/discord.cpp | 14 | ||||
-rw-r--r-- | discord/discord.hpp | 2 | ||||
-rw-r--r-- | discord/message.cpp | 6 | ||||
-rw-r--r-- | discord/message.hpp | 1 | ||||
-rw-r--r-- | discord/store.cpp | 8 |
8 files changed, 157 insertions, 13 deletions
diff --git a/components/chatmessage.cpp b/components/chatmessage.cpp index 56a91bf..7457d51 100644 --- a/components/chatmessage.cpp +++ b/components/chatmessage.cpp @@ -51,6 +51,12 @@ ChatMessageItemContainer *ChatMessageItemContainer::FromMessage(Snowflake id) { container->m_main->add(*container->m_text_component); } + if (data->MessageReference.has_value()) { + auto *widget = container->CreateReplyComponent(&*data); + container->m_main->add(*widget); + container->m_main->child_property_position(*widget) = 0; // eek + } + // there should only ever be 1 embed (i think?) if (data->Embeds.size() == 1) { const auto &embed = data->Embeds[0]; @@ -217,7 +223,7 @@ void ChatMessageItemContainer::UpdateTextComponent(Gtk::TextView *tv) { case MessageType::DEFAULT: case MessageType::INLINE_REPLY: b->insert(s, data->Content); - HandleUserMentions(tv); + HandleUserMentions(b); HandleLinks(tv); HandleChannelMentions(tv); HandleEmojis(tv); @@ -531,6 +537,75 @@ Gtk::Widget *ChatMessageItemContainer::CreateReactionsComponent(const Message *d return flow; } +Gtk::Widget *ChatMessageItemContainer::CreateReplyComponent(const Message *data) { + auto *box = Gtk::manage(new Gtk::Box); + auto *lbl = Gtk::manage(new Gtk::Label); + lbl->set_single_line_mode(true); + lbl->set_line_wrap(false); + lbl->set_use_markup(true); + lbl->set_ellipsize(Pango::ELLIPSIZE_END); + lbl->get_style_context()->add_class("message-text"); // good idea? + lbl->get_style_context()->add_class("message-reply"); + box->add(*lbl); + + if (data->ReferencedMessage.has_value()) { + if (data->ReferencedMessage.value().get() == nullptr) { + lbl->set_markup("<i>deleted message</i>"); + } else { + const auto &referenced = *data->ReferencedMessage.value().get(); + Glib::ustring text; + if (referenced.Content == "") { + if (referenced.Attachments.size() > 0) { + text = "<i>attachment</i>"; + } else if (referenced.Embeds.size() > 0) { + text = "<i>embed</i>"; + } + } else { + auto buf = Gtk::TextBuffer::create(); + Gtk::TextBuffer::iterator start, end; + buf->get_bounds(start, end); + buf->set_text(referenced.Content); + CleanupEmojis(buf); + HandleUserMentions(buf); + HandleChannelMentions(buf); + text = Glib::Markup::escape_text(buf->get_text()); + } + // getting markup out of a textbuffer seems like something that to me should be really simple + // but actually is horribly annoying. replies won't have mention colors because you can't do this + // also no emojis because idk how to make a textview act like a label + // which of course would not be an issue if i could figure out how to get fonts to work on this god-forsaken framework + // oh well + // but ill manually get colors for the user who is being replied to + const auto &discord = Abaddon::Get().GetDiscordClient(); + if (referenced.GuildID.has_value()) { + const auto role_id = discord.GetMemberHoistedRole(*referenced.GuildID, referenced.Author.ID, true); + if (role_id.IsValid()) { + const auto role = discord.GetRole(role_id); + if (role.has_value()) { + const auto author = discord.GetUser(referenced.Author.ID); + // clang-format off + lbl->set_markup( + "<b><span color=\"#" + IntToCSSColor(role->Color) + "\">" + + Glib::Markup::escape_text(author->Username + "#" + author->Discriminator) + + "</span></b>: " + + text + ); + // clang-format on + return box; + } + } + } + + const auto author = discord.GetUser(referenced.Author.ID); + lbl->set_markup("<b>" + Glib::Markup::escape_text(author->Username + "#" + author->Discriminator) + "</b>: " + text); + } + } else { + lbl->set_markup("<i>reply unavailable</i>"); + } + + return box; +} + void ChatMessageItemContainer::ReactionUpdateImage(Gtk::Image *img, const Glib::RefPtr<Gdk::Pixbuf> &pb) { img->property_pixbuf() = pb->scale_simple(16, 16, Gdk::INTERP_BILINEAR); } @@ -565,12 +640,11 @@ bool ChatMessageItemContainer::IsEmbedImageOnly(const EmbedData &data) { return data.Thumbnail->ProxyURL.has_value() && data.Thumbnail->URL.has_value() && data.Thumbnail->Width.has_value() && data.Thumbnail->Height.has_value(); } -void ChatMessageItemContainer::HandleUserMentions(Gtk::TextView *tv) { +void ChatMessageItemContainer::HandleUserMentions(Glib::RefPtr<Gtk::TextBuffer> buf) { constexpr static const auto mentions_regex = R"(<@!?(\d+)>)"; static auto rgx = Glib::Regex::create(mentions_regex); - auto buf = tv->get_buffer(); Glib::ustring text = GetText(buf); const auto &discord = Abaddon::Get().GetDiscordClient(); @@ -709,12 +783,40 @@ void ChatMessageItemContainer::HandleEmojis(Gtk::TextView *tv) { } } -void ChatMessageItemContainer::HandleChannelMentions(Gtk::TextView *tv) { - static auto rgx = Glib::Regex::create(R"(<#(\d+)>)"); +void ChatMessageItemContainer::CleanupEmojis(Glib::RefPtr<Gtk::TextBuffer> buf) { + static auto rgx = Glib::Regex::create(R"(<a?:([\w\d_]+):(\d+)>)"); - tv->signal_button_press_event().connect(sigc::mem_fun(*this, &ChatMessageItemContainer::OnClickChannel), false); + auto &img = Abaddon::Get().GetImageManager(); + + auto text = GetText(buf); + + Glib::MatchInfo match; + int startpos = 0; + while (rgx->match(text, startpos, match)) { + int mstart, mend; + if (!match.fetch_pos(0, mstart, mend)) break; + + const auto chars_start = g_utf8_pointer_to_offset(text.c_str(), text.c_str() + mstart); + const auto chars_end = g_utf8_pointer_to_offset(text.c_str(), text.c_str() + mend); + auto start_it = buf->get_iter_at_offset(chars_start); + auto end_it = buf->get_iter_at_offset(chars_end); + + startpos = mend; + const auto it = buf->erase(start_it, end_it); + const int alen = text.size(); + text = GetText(buf); + const int blen = text.size(); + startpos -= (alen - blen); + + buf->insert(it, ":" + match.fetch(1) + ":"); + + text = GetText(buf); + } +} + +void ChatMessageItemContainer::HandleChannelMentions(Glib::RefPtr<Gtk::TextBuffer> buf) { + static auto rgx = Glib::Regex::create(R"(<#(\d+)>)"); - auto buf = tv->get_buffer(); Glib::ustring text = GetText(buf); const auto &discord = Abaddon::Get().GetDiscordClient(); @@ -749,6 +851,11 @@ void ChatMessageItemContainer::HandleChannelMentions(Gtk::TextView *tv) { } } +void ChatMessageItemContainer::HandleChannelMentions(Gtk::TextView *tv) { + tv->signal_button_press_event().connect(sigc::mem_fun(*this, &ChatMessageItemContainer::OnClickChannel), false); + HandleChannelMentions(tv->get_buffer()); +} + // a lot of repetition here so there should probably just be one slot for textview's button-press bool ChatMessageItemContainer::OnClickChannel(GdkEventButton *ev) { if (m_text_component == nullptr) return false; diff --git a/components/chatmessage.hpp b/components/chatmessage.hpp index e9522c7..d2f2afa 100644 --- a/components/chatmessage.hpp +++ b/components/chatmessage.hpp @@ -27,6 +27,7 @@ protected: Gtk::Widget *CreateAttachmentComponent(const AttachmentData &data); // non-image attachments Gtk::Widget *CreateStickerComponent(const Sticker &data); Gtk::Widget *CreateReactionsComponent(const Message *data); + Gtk::Widget *CreateReplyComponent(const Message *data); void ReactionUpdateImage(Gtk::Image *img, const Glib::RefPtr<Gdk::Pixbuf> &pb); void HandleImage(int w, int h, Gtk::Image *img, std::string url); @@ -36,11 +37,13 @@ protected: static bool IsEmbedImageOnly(const EmbedData &data); - void HandleUserMentions(Gtk::TextView *tv); + void HandleUserMentions(Glib::RefPtr<Gtk::TextBuffer> buf); void HandleStockEmojis(Gtk::TextView *tv); void HandleCustomEmojis(Gtk::TextView *tv); void HandleEmojis(Gtk::TextView *tv); + void CleanupEmojis(Glib::RefPtr<Gtk::TextBuffer> buf); + void HandleChannelMentions(Glib::RefPtr<Gtk::TextBuffer> buf); void HandleChannelMentions(Gtk::TextView *tv); bool OnClickChannel(GdkEventButton *ev); diff --git a/css/main.css b/css/main.css index 890791c..e4bf5a6 100644 --- a/css/main.css +++ b/css/main.css @@ -66,13 +66,24 @@ padding-top: 5px; } +.message-text text, .message-reply { + color: #cfd8dc; +} + +.message-reply { + border-left: 2px solid gray; + padding-left: 20px; + padding-top: 6px; + padding-bottom: 6px; + opacity: 0.7; +} + .message-text + .message-text { padding-top: 5px; } .message-text text { background-color: #263238; - color: #cfd8dc; } .message-input, .message-input text { diff --git a/discord/discord.cpp b/discord/discord.cpp index 63d00c1..129009f 100644 --- a/discord/discord.cpp +++ b/discord/discord.cpp @@ -120,7 +120,7 @@ void DiscordClient::FetchMessagesInChannel(Snowflake id, std::function<void(cons nlohmann::json::parse(r.text).get_to(msgs); m_store.BeginTransaction(); - for (const auto &msg : msgs) { + for (auto &msg : msgs) { StoreMessageData(msg); AddMessageToChannel(msg.ID, id); AddUserToGuild(msg.Author.ID, *msg.GuildID); @@ -143,7 +143,7 @@ void DiscordClient::FetchMessagesInChannelBefore(Snowflake channel_id, Snowflake nlohmann::json::parse(r.text).get_to(msgs); m_store.BeginTransaction(); - for (const auto &msg : msgs) { + for (auto &msg : msgs) { StoreMessageData(msg); AddMessageToChannel(msg.ID, channel_id); AddUserToGuild(msg.Author.ID, *msg.GuildID); @@ -1045,7 +1045,11 @@ bool DiscordClient::CheckCode(const cpr::Response &r) { return true; } -void DiscordClient::StoreMessageData(const Message &msg) { +void DiscordClient::StoreMessageData(Message &msg) { + const auto chan = m_store.GetChannel(msg.ChannelID); + if (chan.has_value() && chan->GuildID.has_value()) + msg.GuildID = *chan->GuildID; + m_store.SetMessage(msg.ID, msg); m_store.SetUser(msg.Author.ID, msg.Author); if (msg.Reactions.has_value()) @@ -1058,6 +1062,10 @@ void DiscordClient::StoreMessageData(const Message &msg) { for (const auto &user : msg.Mentions) m_store.SetUser(user.ID, user); + + if (msg.ReferencedMessage.has_value() && msg.MessageReference.has_value() && msg.MessageReference->ChannelID.has_value()) + if (msg.ReferencedMessage.value() != nullptr) + StoreMessageData(**msg.ReferencedMessage); } void DiscordClient::LoadEventMap() { diff --git a/discord/discord.hpp b/discord/discord.hpp index de8b748..b404291 100644 --- a/discord/discord.hpp +++ b/discord/discord.hpp @@ -156,7 +156,7 @@ private: bool CheckCode(const cpr::Response &r); - void StoreMessageData(const Message &msg); + void StoreMessageData(Message &msg); std::string m_token; diff --git a/discord/message.cpp b/discord/message.cpp index d3ba762..c6457e5 100644 --- a/discord/message.cpp +++ b/discord/message.cpp @@ -211,6 +211,12 @@ void from_json(const nlohmann::json &j, Message &m) { JS_O("message_reference", m.MessageReference); JS_O("flags", m.Flags); JS_O("stickers", m.Stickers); + if (j.contains("referenced_message")) { + if (!j.at("referenced_message").is_null()) { + m.ReferencedMessage = std::make_shared<Message>(j.at("referenced_message").get<Message>()); + } else + m.ReferencedMessage = nullptr; + } } void Message::from_json_edited(const nlohmann::json &j) { diff --git a/discord/message.hpp b/discord/message.hpp index 5968c99..bba6115 100644 --- a/discord/message.hpp +++ b/discord/message.hpp @@ -188,6 +188,7 @@ struct Message { std::optional<MessageReferenceData> MessageReference; std::optional<MessageFlags> Flags = MessageFlags::NONE; std::optional<std::vector<Sticker>> Stickers; + std::optional<std::shared_ptr<Message>> ReferencedMessage; // has_value && null means deleted friend void from_json(const nlohmann::json &j, Message &m); void from_json_edited(const nlohmann::json &j); // for MESSAGE_UPDATE diff --git a/discord/store.cpp b/discord/store.cpp index 07e6a32..927948f 100644 --- a/discord/store.cpp +++ b/discord/store.cpp @@ -496,6 +496,14 @@ std::optional<Message> Store::GetMessage(Snowflake id) const { Reset(m_get_msg_stmt); + if (ret.MessageReference.has_value() && ret.MessageReference->MessageID.has_value()) { + auto ref = GetMessage(*ret.MessageReference->MessageID); + if (ref.has_value()) + ret.ReferencedMessage = std::make_unique<Message>(std::move(*ref)); + else + ret.ReferencedMessage = nullptr; + } + return std::optional<Message>(std::move(ret)); } |