aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2020-11-22 10:29:58 +0100
committerdec05eba <dec05eba@protonmail.com>2020-11-22 10:29:58 +0100
commit5dd0248e16522a3672c58a7892d549840257e8dd (patch)
tree5c2dc3a8b925627216f28453862413f03644766b /src
parent4ddc6897dd53274bb68eb6401715c718a212d9ab (diff)
Matrix: add reactions
Diffstat (limited to 'src')
-rw-r--r--src/Body.cpp114
-rw-r--r--src/QuickMedia.cpp63
-rw-r--r--src/plugins/Matrix.cpp37
3 files changed, 170 insertions, 44 deletions
diff --git a/src/Body.cpp b/src/Body.cpp
index 430c28f..0b4ec0f 100644
--- a/src/Body.cpp
+++ b/src/Body.cpp
@@ -17,6 +17,11 @@ static const float padding_y = 5.0f;
static const float embedded_item_padding_y = 0.0f;
static const double thumbnail_fade_duration_sec = 0.1;
+const float reaction_background_padding_x = 7.0f;
+const float reaction_background_padding_y = 3.0f;
+const float reaction_spacing_x = 5.0f;
+static const float reaction_padding_y = 7.0f;
+
namespace QuickMedia {
BodyItem::BodyItem(std::string _title) :
visible(true),
@@ -75,6 +80,13 @@ namespace QuickMedia {
}
thumbnail_mask_type = other.thumbnail_mask_type;
thumbnail_size = other.thumbnail_size;
+ reactions.clear();
+ for(auto &reaction : other.reactions) {
+ Reaction reaction_copy;
+ reaction_copy.text = std::make_unique<Text>(*reaction.text);
+ reaction_copy.userdata = reaction.userdata;
+ reactions.push_back(std::move(reaction_copy));
+ }
title = other.title;
description = other.description;
author = other.author;
@@ -549,7 +561,7 @@ namespace QuickMedia {
if(body_item->title_text)
body_item->title_text->setString(std::move(str));
else
- body_item->title_text = std::make_unique<Text>(std::move(str), false, 16, width - 50 - image_padding_x * 2.0f);
+ body_item->title_text = std::make_unique<Text>(std::move(str), false, 16, width);
body_item->title_text->setFillColor(body_item->get_title_color());
body_item->title_text->updateGeometry();
}
@@ -560,7 +572,7 @@ namespace QuickMedia {
if(body_item->description_text)
body_item->description_text->setString(std::move(str));
else
- body_item->description_text = std::make_unique<Text>(std::move(str), false, 14, width - 50 - image_padding_x * 2.0f);
+ body_item->description_text = std::make_unique<Text>(std::move(str), false, 14, width);
body_item->description_text->setFillColor(body_item->get_description_color());
body_item->description_text->updateGeometry();
}
@@ -571,7 +583,7 @@ namespace QuickMedia {
if(body_item->author_text)
body_item->author_text->setString(std::move(str));
else
- body_item->author_text = std::make_unique<Text>(std::move(str), true, 14, width - 50 - image_padding_x * 2.0f);
+ body_item->author_text = std::make_unique<Text>(std::move(str), true, 14, width);
body_item->author_text->setFillColor(body_item->get_author_color());
body_item->author_text->updateGeometry();
}
@@ -706,7 +718,6 @@ namespace QuickMedia {
only_show_thumbnail = true;
}
- bool has_thumbnail_texture = false;
// TODO: Verify if this is safe. The thumbnail is being modified in another thread
if(item_thumbnail->loading_state == LoadingState::APPLIED_TO_TEXTURE && item_thumbnail->texture.getNativeHandle() != 0) {
image.setTexture(item_thumbnail->texture, true);
@@ -723,7 +734,6 @@ namespace QuickMedia {
else
window.draw(image);
text_offset_x += image_padding_x + new_image_size.x;
- has_thumbnail_texture = true;
// We want the next image fallback to have the same size as the successful image rendering, because its likely the image fallback will have the same size (for example thumbnails on youtube)
//image_fallback.setSize(sf::Vector2f(width_ratio * image_size.x, height_ratio * image_size.y));
} else if(!item->thumbnail_url.empty() && !only_show_thumbnail) {
@@ -753,9 +763,7 @@ namespace QuickMedia {
loading_icon.setRotation(elapsed_time_sec * 400.0);
loading_icon.setColor(sf::Color(255, 255, 255, fallback_fade_alpha));
window.draw(loading_icon);
-
- if(!has_thumbnail_texture)
- text_offset_x += image_padding_x + content_size.x;
+ text_offset_x += image_padding_x + content_size.x;
}
}
@@ -820,6 +828,31 @@ namespace QuickMedia {
item_pos.y += item->description_text->getHeight() - 2.0f;
}
+ if(!item->reactions.empty()) {
+ sf::RoundedRectangleShape reaction_background(sf::Vector2f(1.0f, 1.0f), 10.0f, 10);
+ reaction_background.setFillColor(sf::Color(33, 38, 44));
+ float reaction_offset_x = 0.0f;
+ item_pos.y += reaction_padding_y;
+ float reaction_max_height = 0.0f;
+ for(auto &reaction : item->reactions) {
+ reaction.text->setMaxWidth(size.x - text_offset_x - image_padding_x);
+ reaction.text->updateGeometry();
+ reaction_max_height = std::max(reaction_max_height, reaction.text->getHeight());
+ if(text_offset_x + reaction_offset_x + reaction.text->getWidth() + reaction_background_padding_x * 2.0f > size.x) {
+ reaction_offset_x = 0.0f;
+ item_pos.y += reaction.text->getHeight() + reaction_padding_y + 6.0f;
+ reaction_max_height = reaction.text->getHeight();
+ }
+ reaction.text->setPosition(std::floor(item_pos.x + text_offset_x + reaction_offset_x + reaction_background_padding_x), std::floor(item_pos.y + padding_y - 4.0f + reaction_background_padding_y));
+ reaction_background.setPosition(std::floor(item_pos.x + text_offset_x + reaction_offset_x), std::floor(item_pos.y + padding_y));
+ reaction_background.setSize(sf::Vector2f(reaction.text->getWidth() + reaction_background_padding_x * 2.0f, reaction.text->getHeight() + reaction_background_padding_y * 2.0f));
+ window.draw(reaction_background);
+ reaction_offset_x += reaction.text->getWidth() + reaction_background_padding_x * 2.0f + reaction_spacing_x;
+ reaction.text->draw(window);
+ }
+ item_pos.y += reaction_max_height + reaction_padding_y;
+ }
+
if(item->timestamp_text) {
item->timestamp_text->setPosition(std::floor(item_pos.x + size.x - item->timestamp_text->getLocalBounds().width - padding_x), timestamp_text_y + 8.0f);
window.draw(*item->timestamp_text);
@@ -844,27 +877,11 @@ namespace QuickMedia {
}
float Body::get_item_height(BodyItem *item, float width, bool load_texture, bool include_embedded_item) {
- if(load_texture)
- update_dirty_state(item, width);
- float item_height = 0.0f;
- if(item->title_text) {
- item_height += item->title_text->getHeight() - 2.0f;
- }
- if(item->author_text) {
- item_height += item->author_text->getHeight() - 2.0f;
- }
- if(include_embedded_item && item->embedded_item_status != FetchStatus::NONE) {
- if(item->embedded_item)
- item_height += (get_item_height(item->embedded_item.get(), width, load_texture, false) + 4.0f + embedded_item_padding_y * 2.0f);
- else
- item_height += (embedded_item_load_text.getLocalBounds().height + 4.0f + embedded_item_padding_y * 2.0f);
- }
- if(item->description_text) {
- item_height += item->description_text->getHeight() - 2.0f;
- }
+ float image_height = 0.0f;
+ float text_offset_x = padding_x;
if(draw_thumbnails && !item->thumbnail_url.empty()) {
sf::Vector2i content_size = get_item_thumbnail_size(item);
- float image_height = content_size.y;
+ image_height = content_size.y;
std::shared_ptr<ThumbnailData> item_thumbnail;
auto item_thumbnail_it = item_thumbnail_textures.find(item->thumbnail_url);
@@ -899,10 +916,51 @@ namespace QuickMedia {
sf::Vector2f image_size_f(image_size.x, image_size.y);
auto new_image_size = clamp_to_size(image_size_f, to_vec2f(content_size));
image_height = new_image_size.y;
+ text_offset_x += image_padding_x + new_image_size.x;
+ } else {
+ text_offset_x += image_padding_x + content_size.x;
}
+ }
- item_height = std::max(item_height, image_height);
+ if(load_texture)
+ update_dirty_state(item, width - text_offset_x - image_padding_x);
+
+ float item_height = 0.0f;
+ if(item->title_text) {
+ item_height += item->title_text->getHeight() - 2.0f;
+ }
+ if(item->author_text) {
+ item_height += item->author_text->getHeight() - 2.0f;
}
+ if(include_embedded_item && item->embedded_item_status != FetchStatus::NONE) {
+ if(item->embedded_item)
+ item_height += (get_item_height(item->embedded_item.get(), width, load_texture, false) + 4.0f + embedded_item_padding_y * 2.0f);
+ else
+ item_height += (embedded_item_load_text.getLocalBounds().height + 4.0f + embedded_item_padding_y * 2.0f);
+ }
+ if(item->description_text) {
+ item_height += item->description_text->getHeight() - 2.0f;
+ }
+
+ if(!item->reactions.empty()) {
+ float reaction_offset_x = 0.0f;
+ item_height += reaction_padding_y;
+ float reaction_max_height = 0.0f;
+ for(auto &reaction : item->reactions) {
+ reaction.text->setMaxWidth(width - text_offset_x - image_padding_x);
+ reaction.text->updateGeometry();
+ reaction_max_height = std::max(reaction_max_height, reaction.text->getHeight());
+ if(text_offset_x + reaction_offset_x + reaction.text->getWidth() + reaction_background_padding_x * 2.0f > width) {
+ reaction_offset_x = 0.0f;
+ item_height += reaction.text->getHeight() + reaction_padding_y + 6.0f;
+ reaction_max_height = reaction.text->getHeight();
+ }
+ reaction_offset_x += reaction.text->getWidth() + reaction_background_padding_x * 2.0f + reaction_spacing_x;
+ }
+ item_height += reaction_max_height + reaction_padding_y;
+ }
+
+ item_height = std::max(item_height, image_height);
return item_height + padding_y * 2.0f;
}
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 1bb7a04..d9b1041 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -3076,7 +3076,7 @@ namespace QuickMedia {
body_item->url = message->url;
body_item->set_author_color(message->user->display_name_color);
body_item->userdata = (void*)message; // Note: message has to be valid as long as body_item is used!
- if(message->related_event_type == RelatedEventType::REDACTION || message->related_event_type == RelatedEventType::EDIT)
+ if(message->related_event_type == RelatedEventType::REDACTION || message->related_event_type == RelatedEventType::EDIT || message->related_event_type == RelatedEventType::REACTION)
body_item->visible = false;
if(message_contains_user_mention(message->body, my_display_name) || message_contains_user_mention(message->body, my_user_id))
body_item->set_description_color(sf::Color(255, 100, 100));
@@ -3218,8 +3218,8 @@ namespace QuickMedia {
body_item->embedded_item = nullptr;
body_item->embedded_item_status = FetchStatus::NONE;
message->type = MessageType::REDACTION;
- message->related_event_id.clear();
- message->related_event_type = RelatedEventType::NONE;
+ //message->related_event_id.clear();
+ //message->related_event_type = RelatedEventType::NONE;
body_item->thumbnail_url = current_room->get_user_avatar_url(message->user);
body_item->thumbnail_mask_type = ThumbnailMaskType::CIRCLE;
body_item->set_description("Message deleted");
@@ -3253,7 +3253,7 @@ namespace QuickMedia {
}
};
- // TODO: Optimize with hash map?
+ // TODO: Optimize find_body_item_by_event_id hash map?
auto modify_related_messages_in_current_room = [&set_body_as_deleted, &unreferenced_events, &find_body_item_by_event_id, &tabs](Messages &messages) {
if(messages.empty())
return;
@@ -3279,6 +3279,53 @@ namespace QuickMedia {
}
};
+ std::vector<std::shared_ptr<Message>> unresolved_reactions;
+ // TODO: Optimize find_body_item_by_event_id hash map?
+ auto process_reactions = [&tabs, &find_body_item_by_event_id, &unresolved_reactions, &current_room](Messages &messages) {
+ if(messages.empty())
+ return;
+
+ auto &body_items = tabs[MESSAGES_TAB_INDEX].body->items;
+
+ // TODO: Check in |messages| instead
+ for(auto it = unresolved_reactions.begin(); it != unresolved_reactions.end();) {
+ auto body_item = find_body_item_by_event_id(body_items.data(), body_items.size(), (*it)->related_event_id);
+ if(body_item) {
+ body_item->add_reaction(current_room->get_user_display_name((*it)->user) + ": " + (*it)->body, (*it).get());
+ it = unresolved_reactions.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ for(auto &message : messages) {
+ if(message->type == MessageType::REACTION) {
+ auto body_item = find_body_item_by_event_id(body_items.data(), body_items.size(), message->related_event_id);
+ if(body_item)
+ body_item->add_reaction(current_room->get_user_display_name(message->user) + ": " + message->body, message.get());
+ else
+ unresolved_reactions.push_back(message);
+ } else if(message->type == MessageType::REDACTION) {
+ auto body_item = find_body_item_by_event_id(body_items.data(), body_items.size(), message->related_event_id);
+ if(body_item && static_cast<Message*>(body_item->userdata)) {
+ Message *reaction_message = static_cast<Message*>(body_item->userdata);
+ if(reaction_message->type == MessageType::REACTION) {
+ auto body_item = find_body_item_by_event_id(body_items.data(), body_items.size(), reaction_message->related_event_id);
+ if(body_item)
+ body_item->remove_reaction_by_userdata(reaction_message);
+ }
+ } else {
+ for(auto it = unresolved_reactions.begin(); it != unresolved_reactions.end(); ++it) {
+ if(message->related_event_id == (*it)->event_id) {
+ unresolved_reactions.erase(it);
+ break;
+ }
+ }
+ }
+ }
+ }
+ };
+
auto pinned_body_items_contains_event = [&tabs](const std::string &event_id) {
for(auto &body_item : tabs[PINNED_TAB_INDEX].body->items) {
if(static_cast<PinnedEventData*>(body_item->userdata)->event_id == event_id)
@@ -3345,6 +3392,7 @@ namespace QuickMedia {
auto me = matrix->get_me(current_room);
tabs[MESSAGES_TAB_INDEX].body->insert_items_by_timestamps(messages_to_body_items(current_room, all_messages, current_room->get_user_display_name(me), me->user_id));
modify_related_messages_in_current_room(all_messages);
+ process_reactions(all_messages);
tabs[MESSAGES_TAB_INDEX].body->select_last_item();
std::vector<std::string> pinned_events;
@@ -4059,6 +4107,8 @@ namespace QuickMedia {
if((selected_tab == MESSAGES_TAB_INDEX || selected_tab == PINNED_TAB_INDEX) && event.key.code == sf::Keyboard::Enter) {
BodyItem *selected = tabs[selected_tab].body->get_selected();
if(selected) {
+ Message *mess = static_cast<Message*>(selected->userdata);
+ fprintf(stderr, "Mess type: %d\n", mess->type);
if(!display_url_or_image(selected))
display_url_or_image(selected->embedded_item.get());
}
@@ -4268,6 +4318,7 @@ namespace QuickMedia {
cleanup_tasks();
tabs.clear();
unreferenced_events.clear();
+ unresolved_reactions.clear();
all_messages.clear();
new_page = PageType::CHAT;
matrix->stop_sync();
@@ -4388,6 +4439,7 @@ namespace QuickMedia {
}
add_new_messages_to_current_room(sync_data.messages);
modify_related_messages_in_current_room(sync_data.messages);
+ process_reactions(sync_data.messages);
process_pinned_events(sync_data.pinned_events);
if(set_read_marker_future.ready()) {
@@ -4418,6 +4470,7 @@ namespace QuickMedia {
tabs[MESSAGES_TAB_INDEX].body->set_selected_item(selected_item_index);
}
modify_related_messages_in_current_room(new_messages);
+ process_reactions(new_messages);
// TODO: Do not loop all items, only loop the new items
resolve_unreferenced_events_with_body_items(tabs[MESSAGES_TAB_INDEX].body->items.data(), tabs[MESSAGES_TAB_INDEX].body->items.size());
}
@@ -4653,6 +4706,8 @@ namespace QuickMedia {
filter_sent_messages(all_messages_new);
add_new_messages_to_current_room(all_messages_new);
modify_related_messages_in_current_room(all_messages_new);
+ unresolved_reactions.clear();
+ process_reactions(all_messages_new);
std::vector<std::string> pinned_events;
matrix->get_all_pinned_events(current_room, pinned_events);
diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp
index 6dabad9..e5e4922 100644
--- a/src/plugins/Matrix.cpp
+++ b/src/plugins/Matrix.cpp
@@ -1754,19 +1754,30 @@ namespace QuickMedia {
std::string related_event_id;
const rapidjson::Value &relates_to_json = GetMember(*content_json, "m.relates_to");
if(relates_to_json.IsObject()) {
- const rapidjson::Value &replaces_event_id_json = GetMember(relates_to_json, "event_id");
+ const rapidjson::Value &relates_to_event_id_json = GetMember(relates_to_json, "event_id");
const rapidjson::Value &rel_type_json = GetMember(relates_to_json, "rel_type");
- if(replaces_event_id_json.IsString() && rel_type_json.IsString() && strcmp(rel_type_json.GetString(), "m.replace") == 0) {
- related_event_id = replaces_event_id_json.GetString();
+ const rapidjson::Value &in_reply_to_json = GetMember(relates_to_json, "m.in_reply_to");
+ const rapidjson::Value &key_json = GetMember(relates_to_json, "key");
+ if(relates_to_event_id_json.IsString() && rel_type_json.IsString() && strcmp(rel_type_json.GetString(), "m.replace") == 0) {
+ related_event_id = relates_to_event_id_json.GetString();
related_event_type = RelatedEventType::EDIT;
- } else {
- const rapidjson::Value &in_reply_to_json = GetMember(relates_to_json, "m.in_reply_to");
- if(in_reply_to_json.IsObject()) {
- const rapidjson::Value &in_reply_to_event_id = GetMember(in_reply_to_json, "event_id");
- if(in_reply_to_event_id.IsString()) {
- related_event_id = in_reply_to_event_id.GetString();
- related_event_type = RelatedEventType::REPLY;
- }
+ } else if(in_reply_to_json.IsObject()) {
+ const rapidjson::Value &in_reply_to_event_id = GetMember(in_reply_to_json, "event_id");
+ if(in_reply_to_event_id.IsString()) {
+ related_event_id = in_reply_to_event_id.GetString();
+ related_event_type = RelatedEventType::REPLY;
+ }
+ } else if(relates_to_event_id_json.IsString() && rel_type_json.IsString() && strcmp(rel_type_json.GetString(), "m.annotation") == 0 && key_json.IsString()) {
+ if(strcmp(type_json.GetString(), "m.reaction") == 0) {
+ auto message = std::make_shared<Message>();
+ message->type = MessageType::REACTION;
+ message->user = user;
+ message->event_id = event_id_str;
+ message->body = key_json.GetString();
+ message->related_event_id = relates_to_event_id_json.GetString();
+ message->related_event_type = RelatedEventType::REACTION;
+ message->timestamp = timestamp;
+ return message;
}
}
}
@@ -1799,7 +1810,9 @@ namespace QuickMedia {
}
if(strcmp(type_json.GetString(), "m.room.message") == 0 || strcmp(type_json.GetString(), "m.sticker") == 0) {
-
+ } else if(strcmp(type_json.GetString(), "m.reaction") == 0) {
+ // An old reaction that has been removed. New reactions are removed with m.redact
+ return nullptr;
} else if(strcmp(type_json.GetString(), "m.room.member") == 0) {
std::string user_display_name = room_data->get_user_display_name(user);
std::string sender_display_name = room_data->get_user_display_name(user_sender);