aboutsummaryrefslogtreecommitdiff
path: root/src/QuickMedia.cpp
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2020-10-17 09:40:10 +0200
committerdec05eba <dec05eba@protonmail.com>2020-10-17 10:49:13 +0200
commit9bf163d51a252fb5a611e88c2e0b4123a98169e1 (patch)
treee742979a7128e3ead913e9348c4b207c0fd95ab4 /src/QuickMedia.cpp
parentfd9178b9d500a0b5f30f388f8d419ac386ce87cb (diff)
Matrix: show reply messages embedded in messages that reply to them, like element does
Diffstat (limited to 'src/QuickMedia.cpp')
-rw-r--r--src/QuickMedia.cpp148
1 files changed, 111 insertions, 37 deletions
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 80567f2..bd4b2d4 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -2872,27 +2872,45 @@ namespace QuickMedia {
}
}
+ static std::string remove_reply_formatting(const std::string &str) {
+ if(strncmp(str.c_str(), "> <@", 4) == 0) {
+ size_t index = str.find("> ", 4);
+ if(index != std::string::npos) {
+ size_t msg_begin = str.find("\n\n", index + 2);
+ if(msg_begin != std::string::npos)
+ return str.substr(msg_begin + 2);
+ }
+ }
+ return str;
+ }
+
+ static std::shared_ptr<BodyItem> message_to_body_item(Message *message) {
+ auto body_item = BodyItem::create("");
+ body_item->set_author(message->user->display_name);
+ std::string text = message->body;
+ if(message->related_event_type == RelatedEventType::REPLY)
+ text = remove_reply_formatting(text);
+ body_item->set_description(std::move(text));
+ body_item->set_timestamp(message->timestamp);
+ if(!message->thumbnail_url.empty())
+ body_item->thumbnail_url = message->thumbnail_url;
+ else if(!message->url.empty() && message->type == MessageType::IMAGE)
+ body_item->thumbnail_url = message->url;
+ else
+ body_item->thumbnail_url = message->user->avatar_url;
+ // TODO: Show image thumbnail inline instead of url to image and showing it as the thumbnail of the body item
+ body_item->url = message->url;
+ body_item->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)
+ body_item->visible = false;
+ return body_item;
+ }
+
static BodyItems messages_to_body_items(const Messages &messages) {
BodyItems result_items(messages.size());
for(size_t i = 0; i < messages.size(); ++i) {
- auto &message = messages[i];
- auto body_item = BodyItem::create("");
- body_item->set_author(message->user->display_name);
- body_item->set_description(message->body);
- body_item->set_timestamp(message->timestamp);
- if(!message->thumbnail_url.empty())
- body_item->thumbnail_url = message->thumbnail_url;
- else if(!message->url.empty() && message->type == MessageType::IMAGE)
- body_item->thumbnail_url = message->url;
- else
- body_item->thumbnail_url = message->user->avatar_url;
- // TODO: Show image thumbnail inline instead of url to image and showing it as the thumbnail of the body item
- body_item->url = message->url;
- body_item->author_color = message->user->display_name_color;
- body_item->userdata = (void*)message.get(); // Note: message has to be valid as long as body_item is used!
- result_items[i] = std::move(body_item);
- if(message->related_event_type == RelatedEventType::REDACTION || message->related_event_type == RelatedEventType::EDIT)
- result_items[i]->visible = false;
+ result_items[i] = message_to_body_item(messages[i].get());
}
return result_items;
}
@@ -3092,6 +3110,50 @@ namespace QuickMedia {
bool fetching_previous_messages_running = false;
std::shared_ptr<RoomData> previous_messages_future_room;
+ std::future<std::shared_ptr<Message>> fetch_reply_message_future;
+ bool fetching_reply_message_running = false;
+ std::shared_ptr<RoomData> fetch_reply_future_room;
+ BodyItem *fetch_reply_body_item = nullptr;
+
+ // TODO: Optimize with hash map?
+ auto find_body_item_by_event_id = [](std::shared_ptr<BodyItem> *body_items, size_t num_body_items, const std::string &event_id) -> std::shared_ptr<BodyItem> {
+ for(size_t i = 0; i < num_body_items; ++i) {
+ auto &body_item = body_items[i];
+ if(static_cast<Message*>(body_item->userdata)->event_id == event_id)
+ return body_item;
+ }
+ return nullptr;
+ };
+
+ // TODO: How about instead fetching all messages we have, not only the visible ones? also fetch with multiple threads.
+ // TODO: Cancel when going to another room?
+ tabs[MESSAGES_TAB_INDEX].body->body_item_render_callback = [this, &current_room, &fetch_reply_message_future, &tabs, &find_body_item_by_event_id, &fetching_reply_message_running, &fetch_reply_future_room, &fetch_reply_body_item](BodyItem *body_item) {
+ if(fetching_reply_message_running || !current_room)
+ return;
+
+ Message *message = static_cast<Message*>(body_item->userdata);
+ if(message->related_event_id.empty() || body_item->embedded_item_status != EmbeddedItemStatus::NONE)
+ return;
+
+ // Check if we already have the referenced message as a body item, so we dont create a new one
+ auto related_body_item = find_body_item_by_event_id(tabs[MESSAGES_TAB_INDEX].body->items.data(), tabs[MESSAGES_TAB_INDEX].body->items.size(), message->related_event_id);
+ if(related_body_item) {
+ body_item->embedded_item = related_body_item;
+ body_item->embedded_item_status = EmbeddedItemStatus::FINISHED_LOADING;
+ return;
+ }
+
+ fetching_reply_message_running = true;
+ std::string message_event_id = message->related_event_id;
+ fetch_reply_future_room = current_room;
+ fetch_reply_body_item = body_item;
+ body_item->embedded_item_status = EmbeddedItemStatus::LOADING;
+ // TODO: Check if the message is already cached before calling async? is this needed? is async creation expensive?
+ fetch_reply_message_future = std::async(std::launch::async, [this, &fetch_reply_future_room, message_event_id]() {
+ return matrix->get_message_by_id(fetch_reply_future_room.get(), message_event_id);
+ });
+ };
+
const float tab_spacer_height = 0.0f;
sf::Vector2f body_pos;
sf::Vector2f body_size;
@@ -3220,7 +3282,7 @@ namespace QuickMedia {
auto add_new_messages_to_current_room = [&tabs](Messages &messages) {
int num_items = tabs[MESSAGES_TAB_INDEX].body->items.size();
- bool scroll_to_end = (num_items == 0 || (num_items > 0 && tabs[MESSAGES_TAB_INDEX].body->get_selected_item() == num_items - 1));
+ bool scroll_to_end = (num_items == 0 || tabs[MESSAGES_TAB_INDEX].body->is_selected_item_last_visible_item());
BodyItem *selected_item = tabs[MESSAGES_TAB_INDEX].body->get_selected();
tabs[MESSAGES_TAB_INDEX].body->insert_items_by_timestamps(messages_to_body_items(messages));
@@ -3269,28 +3331,21 @@ namespace QuickMedia {
return nullptr;
};
- // TODO: Optimize with hash map?
- auto find_body_items_by_event_id = [](std::shared_ptr<BodyItem> *body_items, size_t num_body_items, const std::string &event_id) -> std::shared_ptr<BodyItem> {
- for(size_t i = 0; i < num_body_items; ++i) {
- auto &body_item = body_items[i];
- if(static_cast<Message*>(body_item->userdata)->event_id == event_id)
- return body_item;
- }
- return nullptr;
- };
-
// TODO: What if these never end up referencing events? clean up automatically after a while?
std::unordered_map<std::shared_ptr<RoomData>, Messages> unreferenced_event_by_room;
- auto resolve_unreferenced_events_with_body_items = [&unreferenced_event_by_room, &current_room, &find_body_items_by_event_id](std::shared_ptr<BodyItem> *body_items, size_t num_body_items) {
+ // TODO: Optimize with hash map?
+ auto resolve_unreferenced_events_with_body_items = [&unreferenced_event_by_room, &current_room, &find_body_item_by_event_id](std::shared_ptr<BodyItem> *body_items, size_t num_body_items) {
auto &unreferenced_events = unreferenced_event_by_room[current_room];
for(auto it = unreferenced_events.begin(); it != unreferenced_events.end(); ) {
auto &message = *it;
// TODO: Make redacted/edited events as (redacted)/(edited) in the body
if(message->related_event_type == RelatedEventType::REDACTION || message->related_event_type == RelatedEventType::EDIT) {
- auto body_item = find_body_items_by_event_id(body_items, num_body_items, message->related_event_id);
+ auto body_item = find_body_item_by_event_id(body_items, num_body_items, message->related_event_id);
if(body_item) {
body_item->set_description(message->body);
+ if(message->related_event_type == RelatedEventType::REDACTION)
+ body_item->thumbnail_url = message->user->avatar_url;
it = unreferenced_events.erase(it);
} else {
++it;
@@ -3301,17 +3356,21 @@ namespace QuickMedia {
}
};
- auto modify_related_messages_in_current_room = [&unreferenced_event_by_room, &current_room, &find_body_items_by_event_id, &tabs](Messages &messages) {
+ // TODO: Optimize with hash map?
+ auto modify_related_messages_in_current_room = [&unreferenced_event_by_room, &current_room, &find_body_item_by_event_id, &tabs](Messages &messages) {
auto &unreferenced_events = unreferenced_event_by_room[current_room];
+ auto &body_items = tabs[MESSAGES_TAB_INDEX].body->items;
for(auto &message : messages) {
// TODO: Make redacted/edited events as (redacted)/(edited) in the body
if(message->related_event_type == RelatedEventType::REDACTION || message->related_event_type == RelatedEventType::EDIT) {
- auto &body_items = tabs[MESSAGES_TAB_INDEX].body->items;
- auto body_item = find_body_items_by_event_id(body_items.data(), body_items.size(), message->related_event_id);
- if(body_item)
+ auto body_item = find_body_item_by_event_id(body_items.data(), body_items.size(), message->related_event_id);
+ if(body_item) {
body_item->set_description(message->body);
- else
+ if(message->related_event_type == RelatedEventType::REDACTION)
+ body_item->thumbnail_url = message->user->avatar_url;
+ } else {
unreferenced_events.push_back(message);
+ }
}
}
};
@@ -3770,7 +3829,7 @@ namespace QuickMedia {
fprintf(stderr, "Finished fetching older messages, num new messages: %zu\n", new_messages.size());
// Ignore finished fetch of messages if it happened in another room. When we navigate back to the room we will get the messages again
size_t num_new_messages = new_messages.size();
- if(previous_messages_future_room == current_room && num_new_messages > 0) {
+ if(num_new_messages > 0 && previous_messages_future_room == current_room) {
BodyItem *selected_item = tabs[MESSAGES_TAB_INDEX].body->get_selected();
BodyItems new_body_items = messages_to_body_items(new_messages);
size_t num_new_body_items = new_body_items.size();
@@ -3786,6 +3845,21 @@ namespace QuickMedia {
fetching_previous_messages_running = false;
}
+ if(fetching_reply_message_running && fetch_reply_message_future.valid() && fetch_reply_message_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
+ std::shared_ptr<Message> replied_to_message = fetch_reply_message_future.get();
+ fprintf(stderr, "Finished fetching reply to message: %s\n", replied_to_message ? replied_to_message->event_id.c_str() : "(null)");
+ // Ignore finished fetch of messages if it happened in another room. When we navigate back to the room we will get the messages again
+ if(fetch_reply_future_room == current_room) {
+ if(replied_to_message) {
+ fetch_reply_body_item->embedded_item = message_to_body_item(replied_to_message.get());
+ fetch_reply_body_item->embedded_item_status = EmbeddedItemStatus::FINISHED_LOADING;
+ } else {
+ fetch_reply_body_item->embedded_item_status = EmbeddedItemStatus::FAILED_TO_LOAD;
+ }
+ }
+ fetching_reply_message_running = false;
+ }
+
//chat_input.update();
window.clear(back_color);