mirror of
https://github.com/uowuo/abaddon.git
synced 2024-11-10 14:10:10 +00:00
display replies
This commit is contained in:
parent
73c5b49e99
commit
6985448eb9
@ -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) {
|
||||
void ChatMessageItemContainer::CleanupEmojis(Glib::RefPtr<Gtk::TextBuffer> buf) {
|
||||
static auto rgx = Glib::Regex::create(R"(<a?:([\w\d_]+):(\d+)>)");
|
||||
|
||||
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+)>)");
|
||||
|
||||
tv->signal_button_press_event().connect(sigc::mem_fun(*this, &ChatMessageItemContainer::OnClickChannel), false);
|
||||
|
||||
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;
|
||||
|
@ -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);
|
||||
|
||||
|
13
css/main.css
13
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 {
|
||||
|
@ -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() {
|
||||
|
@ -156,7 +156,7 @@ private:
|
||||
|
||||
bool CheckCode(const cpr::Response &r);
|
||||
|
||||
void StoreMessageData(const Message &msg);
|
||||
void StoreMessageData(Message &msg);
|
||||
|
||||
std::string m_token;
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user