aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Body.cpp143
-rw-r--r--src/DownloadUtils.cpp1
-rw-r--r--src/QuickMedia.cpp148
-rw-r--r--src/Storage.cpp1
-rw-r--r--src/Text.cpp9
-rw-r--r--src/plugins/Matrix.cpp399
6 files changed, 389 insertions, 312 deletions
diff --git a/src/Body.cpp b/src/Body.cpp
index 9b12f43..f6800b8 100644
--- a/src/Body.cpp
+++ b/src/Body.cpp
@@ -6,13 +6,14 @@
#include <assert.h>
#include <cmath>
-const sf::Color front_color(32, 36, 42);
-const sf::Color back_color(33, 35, 37);
-float image_max_height = 100.0f;
-const float spacing_y = 15.0f;
-const float padding_x = 10.0f;
-const float image_padding_x = 5.0f;
-const float padding_y = 5.0f;
+static const sf::Color front_color(32, 36, 42);
+static const sf::Color back_color(33, 35, 37);
+static float image_max_height = 100.0f;
+static const float spacing_y = 15.0f;
+static const float padding_x = 10.0f;
+static const float image_padding_x = 5.0f;
+static const float padding_y = 5.0f;
+static const float embedded_item_padding_y = 0.0f;
namespace QuickMedia {
BodyItem::BodyItem(std::string _title) :
@@ -32,50 +33,13 @@ namespace QuickMedia {
set_title(std::move(_title));
}
- BodyItem::BodyItem(const BodyItem &other) {
- title = other.title;
- description = other.description;
- url = other.url;
- thumbnail_url = other.thumbnail_url;
- attached_content_url = other.attached_content_url;
- author = other.author;
- visible = other.visible;
- dirty = other.dirty;
- dirty_description = other.dirty_description;
- dirty_author = other.dirty_author;
- dirty_timestamp = other.dirty_timestamp;
- thumbnail_is_local = other.thumbnail_is_local;
- if(other.title_text)
- title_text = std::make_unique<Text>(*other.title_text);
- else
- title_text = nullptr;
- if(other.description_text)
- description_text = std::make_unique<Text>(*other.description_text);
- else
- description_text = nullptr;
- if(other.author_text)
- author_text = std::make_unique<Text>(*other.author_text);
- else
- author_text = nullptr;
- if(other.timestamp_text)
- timestamp_text = std::make_unique<sf::Text>(*other.timestamp_text);
- else
- timestamp_text = nullptr;
- replies = other.replies;
- post_number = other.post_number;
- title_color = other.title_color;
- author_color = other.author_color;
- userdata = other.userdata;
- last_drawn_time = other.last_drawn_time;
- timestamp = other.timestamp;
- }
-
Body::Body(Program *program, sf::Font *font, sf::Font *bold_font, sf::Font *cjk_font) :
font(font),
bold_font(bold_font),
cjk_font(cjk_font),
progress_text("", *font, 14),
replies_text("", *font, 14),
+ embedded_item_load_text("", *font, 14),
draw_thumbnails(false),
wrap_around(false),
line_seperator_color(sf::Color(32, 37, 43, 255)),
@@ -305,7 +269,7 @@ namespace QuickMedia {
}
void Body::draw(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size) {
- draw(window, pos, size, Json::nullValue);
+ draw(window, pos, size, Json::Value::nullSingleton());
}
// TODO: Use a render target for the whole body so all images can be put into one.
@@ -395,6 +359,7 @@ namespace QuickMedia {
continue;
update_dirty_state(item.get(), size);
+ item->last_drawn_time = elapsed_time;
float item_height = get_item_height(item.get());
prev_pos.y -= (item_height + spacing_y);
@@ -402,7 +367,6 @@ namespace QuickMedia {
if(prev_pos.y + item_height + spacing_y < start_y)
break;
- item->last_drawn_time = elapsed_time;
// This is needed here rather than above the loop, since update_dirty_text cant be called inside scissor because it corrupts the text for some reason
glEnable(GL_SCISSOR_TEST);
glScissor(scissor_pos.x, (int)window_size.y - (int)scissor_pos.y - (int)scissor_size.y, scissor_size.x, scissor_size.y);
@@ -420,6 +384,7 @@ namespace QuickMedia {
continue;
update_dirty_state(item.get(), size);
+ item->last_drawn_time = elapsed_time;
float item_height = get_item_height(item.get());
@@ -431,7 +396,6 @@ namespace QuickMedia {
if(after_pos.y - start_y >= size.y)
break;
- item->last_drawn_time = elapsed_time;
// This is needed here rather than above the loop, since update_dirty_text cant be called inside scissor because it corrupts the text for some reason
glEnable(GL_SCISSOR_TEST);
glScissor(scissor_pos.x, (int)window_size.y - (int)scissor_pos.y - (int)scissor_size.y, scissor_size.x, scissor_size.y);
@@ -543,17 +507,32 @@ namespace QuickMedia {
}
}
- void Body::draw_item(sf::RenderWindow &window, BodyItem *item, sf::Vector2f pos, sf::Vector2f size) {
+ void Body::draw_item(sf::RenderWindow &window, BodyItem *item, sf::Vector2f pos, sf::Vector2f size, bool include_embedded_item) {
update_dirty_state(item, size);
item->last_drawn_time = draw_timer.getElapsedTime().asMilliseconds();
sf::Vector2u window_size = window.getSize();
glEnable(GL_SCISSOR_TEST);
glScissor(pos.x, (int)window_size.y - (int)pos.y - (int)size.y, size.x, size.y);
- draw_item(window, item, pos, size, get_item_height(item) + spacing_y, -1, Json::nullValue);
+ draw_item(window, item, pos, size, size.y + spacing_y, -1, Json::Value::nullSingleton(), include_embedded_item);
glDisable(GL_SCISSOR_TEST);
}
- void Body::draw_item(sf::RenderWindow &window, BodyItem *item, const sf::Vector2f &pos, const sf::Vector2f &size, const float item_height, const int item_index, const Json::Value &content_progress) {
+ // TODO: Better message? maybe fallback to the reply message, or message status (such as message redacted)
+ static const char* embedded_item_status_to_string(EmbeddedItemStatus embedded_item_status) {
+ switch(embedded_item_status) {
+ case EmbeddedItemStatus::NONE:
+ return "";
+ case EmbeddedItemStatus::LOADING:
+ return "Loading message...";
+ case EmbeddedItemStatus::FINISHED_LOADING:
+ return "Finished loading message...";
+ case EmbeddedItemStatus::FAILED_TO_LOAD:
+ return "Failed to load message!";
+ }
+ return "";
+ }
+
+ void Body::draw_item(sf::RenderWindow &window, BodyItem *item, const sf::Vector2f &pos, const sf::Vector2f &size, const float item_height, const int item_index, const Json::Value &content_progress, bool include_embedded_item) {
// TODO: Instead of generating a new hash everytime to access textures, cache the hash of the thumbnail url
std::shared_ptr<ThumbnailData> item_thumbnail;
if(draw_thumbnails) {
@@ -567,6 +546,9 @@ namespace QuickMedia {
item_thumbnail->referenced = true;
}
+ if(body_item_render_callback)
+ body_item_render_callback(item);
+
sf::Vector2f item_pos;
item_pos.x = std::floor(pos.x);
item_pos.y = std::floor(pos.y);
@@ -605,9 +587,10 @@ namespace QuickMedia {
}
}
+ const float timestamp_text_y = std::floor(item_pos.y + padding_y - 6.0f);
if(item->author_text) {
item->author_text->setPosition(std::floor(item_pos.x + text_offset_x), std::floor(item_pos.y + padding_y - 6.0f));
- item->author_text->setMaxWidth(size.x - text_offset_x - image_padding_x * 2.0f);
+ item->author_text->setMaxWidth(size.x - text_offset_x - image_padding_x);
item->author_text->draw(window);
sf::Vector2f replies_text_pos = item->author_text->getPosition() + sf::Vector2f(0.0f, 5.0f);
@@ -625,28 +608,48 @@ namespace QuickMedia {
item_pos.y += item->author_text->getHeight() - 2.0f;
}
+
+ if(include_embedded_item && item->embedded_item_status != EmbeddedItemStatus::NONE) {
+ float embedded_item_height = item->embedded_item ? get_item_height(item->embedded_item.get(), true, false) : (embedded_item_load_text.getLocalBounds().height + embedded_item_padding_y * 2.0f);
+ const float border_width = 4.0f;
+ sf::RectangleShape border_left(sf::Vector2f(border_width, std::floor(embedded_item_height)));
+ border_left.setFillColor(sf::Color::White);
+ border_left.setPosition(std::floor(item_pos.x + text_offset_x), std::floor(item_pos.y + 4.0f));
+ window.draw(border_left);
+
+ if(item->embedded_item) {
+ sf::Vector2f embedded_item_pos(std::floor(item_pos.x + text_offset_x + border_width + padding_x), std::floor(item_pos.y + embedded_item_padding_y + 4.0f));
+ sf::Vector2f embedded_item_size(std::floor(size.x - text_offset_x - border_width - padding_x), embedded_item_height);
+ draw_item(window, item->embedded_item.get(), embedded_item_pos, embedded_item_size, false);
+ } else {
+ embedded_item_load_text.setString(embedded_item_status_to_string(item->embedded_item_status));
+ embedded_item_load_text.setPosition(std::floor(item_pos.x + text_offset_x + border_width + padding_x), std::floor(item_pos.y + embedded_item_height * 0.5f - embedded_item_load_text.getLocalBounds().height * 0.5f + 4.0f));
+ window.draw(embedded_item_load_text);
+ }
+ item_pos.y += embedded_item_height + 4.0f;
+ }
+
//title_text.setString(item->title);
//title_text.setPosition(std::floor(item_pos.x + text_offset_x), std::floor(item_pos.y + padding_y));
//window.draw(title_text);
if(item->title_text) {
item->title_text->setFillColor(item->title_color);
- item->title_text->setPosition(std::floor(item_pos.x + text_offset_x), std::floor(item_pos.y + padding_y - 8.0f));
- item->title_text->setMaxWidth(size.x - text_offset_x - image_padding_x * 2.0f);
+ item->title_text->setPosition(std::floor(item_pos.x + text_offset_x), std::floor(item_pos.y + padding_y - 6.0f));
+ item->title_text->setMaxWidth(size.x - text_offset_x - image_padding_x);
item->title_text->draw(window);
+ item_pos.y += item->title_text->getHeight() - 2.0f;
}
if(item->description_text) {
float height_offset = 0.0f;
- if(!item->get_title().empty()) {
- height_offset = item->title_text->getHeight();
- }
- item->description_text->setPosition(std::floor(item_pos.x + text_offset_x), std::floor(item_pos.y + padding_y - 8.0f + height_offset));
- item->description_text->setMaxWidth(size.x - text_offset_x - image_padding_x * 2.0f);
+ item->description_text->setPosition(std::floor(item_pos.x + text_offset_x), std::floor(item_pos.y + padding_y - 6.0f + height_offset));
+ item->description_text->setMaxWidth(size.x - text_offset_x - image_padding_x);
item->description_text->draw(window);
+ item_pos.y += item->description_text->getHeight() - 2.0f;
}
if(item->timestamp_text) {
- item->timestamp_text->setPosition(std::floor(item_pos.x + size.x - item->timestamp_text->getLocalBounds().width - padding_x), std::floor(item_pos.y + padding_y - 18.0f));
+ item->timestamp_text->setPosition(std::floor(item_pos.x + size.x - item->timestamp_text->getLocalBounds().width - padding_x), timestamp_text_y + 4.0f);
window.draw(*item->timestamp_text);
}
@@ -662,13 +665,13 @@ namespace QuickMedia {
if(current_json.isNumeric() && total_json.isNumeric()) {
progress_text.setString(std::string("Page: ") + std::to_string(current_json.asInt()) + "/" + std::to_string(total_json.asInt()));
auto bounds = progress_text.getLocalBounds();
- progress_text.setPosition(std::floor(item_pos.x + size.x - bounds.width - padding_x), std::floor(item_pos.y + padding_y));
+ progress_text.setPosition(std::floor(item_pos.x + size.x - bounds.width - padding_x), timestamp_text_y + 6.0f);
window.draw(progress_text);
}
}
}
- float Body::get_item_height(BodyItem *item, bool load_texture) {
+ float Body::get_item_height(BodyItem *item, bool load_texture, bool include_embedded_item) {
float item_height = 0.0f;
if(item->title_text) {
item_height += item->title_text->getHeight() - 2.0f;
@@ -676,6 +679,12 @@ namespace QuickMedia {
if(item->author_text) {
item_height += item->author_text->getHeight() - 2.0f;
}
+ if(include_embedded_item && item->embedded_item_status != EmbeddedItemStatus::NONE) {
+ if(item->embedded_item)
+ item_height += (get_item_height(item->embedded_item.get(), 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;
}
@@ -757,4 +766,14 @@ namespace QuickMedia {
}
return true;
}
+
+ bool Body::is_selected_item_last_visible_item() const {
+ if(selected_item < 0 || selected_item >= (int)items.size() || !items[selected_item]->visible)
+ return false;
+ for(int i = selected_item + 1; i < (int)items.size(); ++i) {
+ if(items[i]->visible)
+ return false;
+ }
+ return true;
+ }
} \ No newline at end of file
diff --git a/src/DownloadUtils.cpp b/src/DownloadUtils.cpp
index 5b39bdd..8782020 100644
--- a/src/DownloadUtils.cpp
+++ b/src/DownloadUtils.cpp
@@ -3,6 +3,7 @@
#include "../include/Storage.hpp"
#include "../include/base64_url.hpp"
#include <SFML/System/Clock.hpp>
+#include <rapidjson/document.h>
#include <rapidjson/filereadstream.h>
static const bool debug_download = false;
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);
diff --git a/src/Storage.cpp b/src/Storage.cpp
index a1dc777..086f6d8 100644
--- a/src/Storage.cpp
+++ b/src/Storage.cpp
@@ -5,6 +5,7 @@
#include <assert.h>
#include <json/reader.h>
#include <json/writer.h>
+#include <rapidjson/document.h>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>
#include <unordered_set>
diff --git a/src/Text.cpp b/src/Text.cpp
index c1adc5d..a00d068 100644
--- a/src/Text.cpp
+++ b/src/Text.cpp
@@ -38,6 +38,7 @@ namespace QuickMedia
dirtyCaret(false),
editable(false),
caretMoveDirection(CaretMoveDirection::NONE),
+ num_lines(1),
lineSpacing(0.0f),
characterSpacing(0.0f),
caretIndex(0),
@@ -96,8 +97,10 @@ namespace QuickMedia
if(std::abs(maxWidth - this->maxWidth) > 1.0f)
{
this->maxWidth = maxWidth;
- dirty = true;
- dirtyCaret = true;
+ if(num_lines > 1 || maxWidth < boundingBox.width) {
+ dirty = true;
+ dirtyCaret = true;
+ }
}
}
@@ -383,7 +386,7 @@ namespace QuickMedia
float text_wrap_offset = 0.0f;
float text_offset_y = 0.0f;
int last_space_index = -1;
- int num_lines = 1;
+ num_lines = 1;
// TODO: Binary search?
for(int i = 0; i < (int)vertices_linear.size(); ++i) {
VertexRef &vertex_ref = vertices_linear[i];
diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp
index 0866bac..1cb2aa5 100644
--- a/src/plugins/Matrix.cpp
+++ b/src/plugins/Matrix.cpp
@@ -1,6 +1,7 @@
#include "../../plugins/Matrix.hpp"
#include "../../include/Storage.hpp"
#include "../../include/StringUtils.hpp"
+#include <rapidjson/document.h>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>
#include <fcntl.h>
@@ -447,173 +448,181 @@ namespace QuickMedia {
std::vector<std::shared_ptr<Message>> new_messages;
for(const rapidjson::Value &event_item_json : events_json.GetArray()) {
- if(!event_item_json.IsObject())
+ std::shared_ptr<Message> new_message = parse_message_event(event_item_json, room_data.get());
+ if(!new_message)
continue;
- const rapidjson::Value &sender_json = GetMember(event_item_json, "sender");
- if(!sender_json.IsString())
- continue;
+ // TODO: Is @room ok? shouldn't we also check if the user has permission to do @room? (only when notifications are limited to @mentions)
+ if(has_unread_notifications && !username.empty())
+ new_message->mentions_me = message_contains_user_mention(new_message->body, username) || message_contains_user_mention(new_message->body, "@room");
- std::string sender_json_str = sender_json.GetString();
+ new_messages.push_back(std::move(new_message));
+ }
- const rapidjson::Value &event_id_json = GetMember(event_item_json, "event_id");
- if(!event_id_json.IsString())
- continue;
+ // TODO: Add directly to this instead when set? otherwise add to new_messages
+ if(room_messages)
+ (*room_messages)[room_data] = new_messages;
- std::string event_id_str = event_id_json.GetString();
-
- const rapidjson::Value *content_json = &GetMember(event_item_json, "content");
- if(!content_json->IsObject())
- continue;
+ // TODO: Loop and std::move instead? doesn't insert create copies?
+ if(message_dir == MessageDirection::BEFORE) {
+ room_data->prepend_messages_reverse(std::move(new_messages));
+ } else if(message_dir == MessageDirection::AFTER) {
+ room_data->append_messages(std::move(new_messages));
+ }
+ }
- auto user = room_data->get_user_by_id(sender_json_str);
- if(!user) {
- // Note: this is important because otherwise replying and such is broken
- fprintf(stderr, "Warning: skipping unknown user: %s\n", sender_json_str.c_str());
- continue;
- }
+ std::shared_ptr<Message> Matrix::parse_message_event(const rapidjson::Value &event_item_json, RoomData *room_data) {
+ if(!event_item_json.IsObject())
+ return nullptr;
- time_t timestamp = 0;
- const rapidjson::Value &origin_server_ts = GetMember(event_item_json, "origin_server_ts");
- if(origin_server_ts.IsNumber())
- timestamp = origin_server_ts.GetInt64();
+ const rapidjson::Value &sender_json = GetMember(event_item_json, "sender");
+ if(!sender_json.IsString())
+ return nullptr;
- const rapidjson::Value &type_json = GetMember(event_item_json, "type");
- if(!type_json.IsString())
- continue;
+ std::string sender_json_str = sender_json.GetString();
- RelatedEventType related_event_type = RelatedEventType::NONE;
- 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 &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();
- 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;
- }
+ const rapidjson::Value &event_id_json = GetMember(event_item_json, "event_id");
+ if(!event_id_json.IsString())
+ return nullptr;
+
+ std::string event_id_str = event_id_json.GetString();
+
+ const rapidjson::Value *content_json = &GetMember(event_item_json, "content");
+ if(!content_json->IsObject())
+ return nullptr;
+
+ auto user = room_data->get_user_by_id(sender_json_str);
+ if(!user) {
+ // Note: this is important because otherwise replying and such is broken
+ fprintf(stderr, "Warning: skipping unknown user: %s\n", sender_json_str.c_str());
+ return nullptr;
+ }
+
+ time_t timestamp = 0;
+ const rapidjson::Value &origin_server_ts = GetMember(event_item_json, "origin_server_ts");
+ if(origin_server_ts.IsNumber())
+ timestamp = origin_server_ts.GetInt64();
+
+ const rapidjson::Value &type_json = GetMember(event_item_json, "type");
+ if(!type_json.IsString())
+ return nullptr;
+
+ RelatedEventType related_event_type = RelatedEventType::NONE;
+ 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 &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();
+ 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;
}
}
}
+ }
- const rapidjson::Value &new_content_json = GetMember(*content_json, "m.new_content");
- if(new_content_json.IsObject())
- content_json = &new_content_json;
-
- const rapidjson::Value &content_type = GetMember(*content_json, "msgtype");
- if(!content_type.IsString() || strcmp(type_json.GetString(), "m.room.redaction") == 0) {
- auto message = std::make_shared<Message>();
- message->type = MessageType::REDACTION;
- message->user = user;
- message->event_id = event_id_str;
- message->body = "Message deleted";
- message->timestamp = timestamp;
- message->related_event_type = RelatedEventType::REDACTION;
-
- const rapidjson::Value &reason_json = GetMember(*content_json, "reason");
- if(reason_json.IsString()) {
- message->body += ", reason: ";
- message->body += reason_json.GetString();
- }
+ const rapidjson::Value &new_content_json = GetMember(*content_json, "m.new_content");
+ if(new_content_json.IsObject())
+ content_json = &new_content_json;
- const rapidjson::Value &redacts_json = GetMember(event_item_json, "redacts");
- if(redacts_json.IsString())
- message->related_event_id = redacts_json.GetString();
+ const rapidjson::Value &content_type = GetMember(*content_json, "msgtype");
+ if(!content_type.IsString() || strcmp(type_json.GetString(), "m.room.redaction") == 0) {
+ auto message = std::make_shared<Message>();
+ message->type = MessageType::REDACTION;
+ message->user = user;
+ message->event_id = event_id_str;
+ message->body = "Message deleted";
+ message->timestamp = timestamp;
+ message->related_event_type = RelatedEventType::REDACTION;
- new_messages.push_back(message);
- continue;
+ const rapidjson::Value &reason_json = GetMember(*content_json, "reason");
+ if(reason_json.IsString()) {
+ message->body += ", reason: ";
+ message->body += reason_json.GetString();
}
- if(strcmp(type_json.GetString(), "m.room.message") != 0)
- continue;
-
- const rapidjson::Value &body_json = GetMember(*content_json, "body");
- if(!body_json.IsString())
- continue;
+ const rapidjson::Value &redacts_json = GetMember(event_item_json, "redacts");
+ if(redacts_json.IsString())
+ message->related_event_id = redacts_json.GetString();
- auto message = std::make_shared<Message>();
- std::string prefix;
+ return message;
+ }
- // TODO: Also show joins, leave, invites, bans, kicks, mutes, etc
+ if(strcmp(type_json.GetString(), "m.room.message") != 0)
+ return nullptr;
- if(strcmp(content_type.GetString(), "m.text") == 0) {
- message->type = MessageType::TEXT;
- } else if(strcmp(content_type.GetString(), "m.image") == 0) {
- const rapidjson::Value &url_json = GetMember(*content_json, "url");
- if(!url_json.IsString() || strncmp(url_json.GetString(), "mxc://", 6) != 0)
- continue;
+ const rapidjson::Value &body_json = GetMember(*content_json, "body");
+ if(!body_json.IsString())
+ return nullptr;
- message->url = homeserver + "/_matrix/media/r0/download/" + (url_json.GetString() + 6);
- message->thumbnail_url = message_content_extract_thumbnail_url(*content_json, homeserver);
- message->type = MessageType::IMAGE;
- } else if(strcmp(content_type.GetString(), "m.video") == 0) {
- const rapidjson::Value &url_json = GetMember(*content_json, "url");
- if(!url_json.IsString() || strncmp(url_json.GetString(), "mxc://", 6) != 0)
- continue;
+ auto message = std::make_shared<Message>();
+ std::string prefix;
- message->url = homeserver + "/_matrix/media/r0/download/" + (url_json.GetString() + 6);
- message->thumbnail_url = message_content_extract_thumbnail_url(*content_json, homeserver);
- message->type = MessageType::VIDEO;
- } else if(strcmp(content_type.GetString(), "m.audio") == 0) {
- const rapidjson::Value &url_json = GetMember(*content_json, "url");
- if(!url_json.IsString() || strncmp(url_json.GetString(), "mxc://", 6) != 0)
- continue;
+ // TODO: Also show joins, leave, invites, bans, kicks, mutes, etc
- message->url = homeserver + "/_matrix/media/r0/download/" + (url_json.GetString() + 6);
- message->type = MessageType::AUDIO;
- } else if(strcmp(content_type.GetString(), "m.file") == 0) {
- const rapidjson::Value &url_json = GetMember(*content_json, "url");
- if(!url_json.IsString() || strncmp(url_json.GetString(), "mxc://", 6) != 0)
- continue;
+ if(strcmp(content_type.GetString(), "m.text") == 0) {
+ message->type = MessageType::TEXT;
+ } else if(strcmp(content_type.GetString(), "m.image") == 0) {
+ const rapidjson::Value &url_json = GetMember(*content_json, "url");
+ if(!url_json.IsString() || strncmp(url_json.GetString(), "mxc://", 6) != 0)
+ return nullptr;
- message->url = homeserver + "/_matrix/media/r0/download/" + (url_json.GetString() + 6);
- message->type = MessageType::FILE;
- } else if(strcmp(content_type.GetString(), "m.emote") == 0) { // this is a /me message, TODO: show /me messages differently
- message->type = MessageType::TEXT;
- prefix = "*" + user->display_name + "* ";
- } else if(strcmp(content_type.GetString(), "m.notice") == 0) { // TODO: show notices differently
- message->type = MessageType::TEXT;
- prefix = "* NOTICE * ";
- } else if(strcmp(content_type.GetString(), "m.location") == 0) { // TODO: show locations differently
- const rapidjson::Value &geo_uri_json = GetMember(*content_json, "geo_uri");
- if(geo_uri_json.IsString())
- prefix = geo_uri_json.GetString() + std::string(" | ");
-
- message->type = MessageType::TEXT;
- message->thumbnail_url = message_content_extract_thumbnail_url(*content_json, homeserver);
- } else {
- continue;
- }
+ message->url = homeserver + "/_matrix/media/r0/download/" + (url_json.GetString() + 6);
+ message->thumbnail_url = message_content_extract_thumbnail_url(*content_json, homeserver);
+ message->type = MessageType::IMAGE;
+ } else if(strcmp(content_type.GetString(), "m.video") == 0) {
+ const rapidjson::Value &url_json = GetMember(*content_json, "url");
+ if(!url_json.IsString() || strncmp(url_json.GetString(), "mxc://", 6) != 0)
+ return nullptr;
- message->user = user;
- message->event_id = event_id_str;
- message->body = prefix + body_json.GetString();
- message->related_event_id = std::move(related_event_id);
- message->related_event_type = related_event_type;
- // TODO: Is @room ok? shouldn't we also check if the user has permission to do @room? (only when notifications are limited to @mentions)
- if(has_unread_notifications && !username.empty())
- message->mentions_me = message_contains_user_mention(message->body, username) || message_contains_user_mention(message->body, "@room");
- message->timestamp = timestamp;
- new_messages.push_back(message);
- }
+ message->url = homeserver + "/_matrix/media/r0/download/" + (url_json.GetString() + 6);
+ message->thumbnail_url = message_content_extract_thumbnail_url(*content_json, homeserver);
+ message->type = MessageType::VIDEO;
+ } else if(strcmp(content_type.GetString(), "m.audio") == 0) {
+ const rapidjson::Value &url_json = GetMember(*content_json, "url");
+ if(!url_json.IsString() || strncmp(url_json.GetString(), "mxc://", 6) != 0)
+ return nullptr;
- // TODO: Add directly to this instead when set? otherwise add to new_messages
- if(room_messages)
- (*room_messages)[room_data] = new_messages;
+ message->url = homeserver + "/_matrix/media/r0/download/" + (url_json.GetString() + 6);
+ message->type = MessageType::AUDIO;
+ } else if(strcmp(content_type.GetString(), "m.file") == 0) {
+ const rapidjson::Value &url_json = GetMember(*content_json, "url");
+ if(!url_json.IsString() || strncmp(url_json.GetString(), "mxc://", 6) != 0)
+ return nullptr;
- // TODO: Loop and std::move instead? doesn't insert create copies?
- if(message_dir == MessageDirection::BEFORE) {
- room_data->prepend_messages_reverse(std::move(new_messages));
- } else if(message_dir == MessageDirection::AFTER) {
- room_data->append_messages(std::move(new_messages));
+ message->url = homeserver + "/_matrix/media/r0/download/" + (url_json.GetString() + 6);
+ message->type = MessageType::FILE;
+ } else if(strcmp(content_type.GetString(), "m.emote") == 0) { // this is a /me message, TODO: show /me messages differently
+ message->type = MessageType::TEXT;
+ prefix = "*" + user->display_name + "* ";
+ } else if(strcmp(content_type.GetString(), "m.notice") == 0) { // TODO: show notices differently
+ message->type = MessageType::TEXT;
+ prefix = "* NOTICE * ";
+ } else if(strcmp(content_type.GetString(), "m.location") == 0) { // TODO: show locations differently
+ const rapidjson::Value &geo_uri_json = GetMember(*content_json, "geo_uri");
+ if(geo_uri_json.IsString())
+ prefix = geo_uri_json.GetString() + std::string(" | ");
+
+ message->type = MessageType::TEXT;
+ message->thumbnail_url = message_content_extract_thumbnail_url(*content_json, homeserver);
+ } else {
+ return nullptr;
}
+
+ message->user = user;
+ message->event_id = event_id_str;
+ message->body = prefix + body_json.GetString();
+ message->related_event_id = std::move(related_event_id);
+ message->related_event_type = related_event_type;
+ message->timestamp = timestamp;
+ return message;
}
// Returns empty string on error
@@ -851,7 +860,7 @@ namespace QuickMedia {
}
if(file_info->duration_seconds) {
// TODO: Check for overflow?
- info_json.AddMember("duration", (int)file_info->duration_seconds.value() * 1000, request_data.GetAllocator());
+ info_json.AddMember("duration", (int)(file_info->duration_seconds.value() * 1000.0), request_data.GetAllocator());
}
if(thumbnail_info) {
@@ -932,6 +941,8 @@ namespace QuickMedia {
case MessageType::TEXT: {
if(message->related_event_type != RelatedEventType::NONE)
related_to_body = remove_reply_formatting(message->body);
+ else
+ related_to_body = message->body;
break;
}
case MessageType::IMAGE:
@@ -962,6 +973,8 @@ namespace QuickMedia {
std::string related_to_body = get_reply_message(message);
html_escape_sequences(formatted_body);
html_escape_sequences(related_to_body);
+ // TODO: Fix invalid.url, etc to use same as element. This is required to navigate to reply message in element mobile.
+ // TODO: Add keybind to navigate to the reply message, which would also depend on this formatting.
return "<mx-reply>"
"<blockquote>"
"<a href=\"https://invalid.url\">In reply to</a>"
@@ -1125,88 +1138,54 @@ namespace QuickMedia {
// TODO: Right now this recursively calls /rooms/<room_id>/context/<event_id> and trusts server to not make it recursive. To make this robust, check iteration count and do not trust server.
// TODO: Optimize?
std::shared_ptr<Message> Matrix::get_edited_message_original_message(RoomData *room_data, std::shared_ptr<Message> message) {
- if(message->related_event_type != RelatedEventType::EDIT)
+ if(!message || message->related_event_type != RelatedEventType::EDIT)
return message;
+ return get_edited_message_original_message(room_data, get_message_by_id(room_data, message->related_event_id));
+ }
- auto replaced_message = room_data->get_message_by_id(message->related_event_id);
- if(!replaced_message) {
- rapidjson::Document request_data(rapidjson::kObjectType);
- request_data.AddMember("lazy_load_members", true, request_data.GetAllocator());
-
- rapidjson::StringBuffer buffer;
- rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
- request_data.Accept(writer);
-
- std::vector<CommandArg> additional_args = {
- { "-H", "Authorization: Bearer " + access_token }
- };
-
- std::string filter = url_param_encode(buffer.GetString());
-
- char url[512];
- snprintf(url, sizeof(url), "%s/_matrix/client/r0/rooms/%s/context/%s?limit=0&filter=%s", homeserver.c_str(), room_data->id.c_str(), message->event_id.c_str(), filter.c_str());
-
- rapidjson::Document json_root;
- DownloadResult download_result = download_json(json_root, url, std::move(additional_args), true);
- if(download_result != DownloadResult::OK) return nullptr;
-
- if(!json_root.IsObject())
- return nullptr;
+ std::shared_ptr<Message> Matrix::get_message_by_id(RoomData *room, const std::string &event_id) {
+ std::shared_ptr<Message> existing_room_message = room->get_message_by_id(event_id);
+ if(existing_room_message)
+ return existing_room_message;
- const rapidjson::Value &event_json = GetMember(json_root, "event");
- if(!event_json.IsObject())
- return nullptr;
+ auto fetched_message_it = room->fetched_messages_by_event_id.find(event_id);
+ if(fetched_message_it != room->fetched_messages_by_event_id.end())
+ return fetched_message_it->second;
+
+ rapidjson::Document request_data(rapidjson::kObjectType);
+ request_data.AddMember("lazy_load_members", true, request_data.GetAllocator());
- const rapidjson::Value &event_id_json = GetMember(event_json, "event_id");
- if(!event_id_json.IsString())
- return nullptr;
+ rapidjson::StringBuffer buffer;
+ rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
+ request_data.Accept(writer);
- const rapidjson::Value &content_json = GetMember(event_json, "content");
- if(!content_json.IsObject())
- return nullptr;
+ std::vector<CommandArg> additional_args = {
+ { "-H", "Authorization: Bearer " + access_token }
+ };
- const rapidjson::Value &body_json = GetMember(content_json, "body");
- if(!body_json.IsString())
- return nullptr;
+ std::string filter = url_param_encode(buffer.GetString());
- RelatedEventType related_event_type = RelatedEventType::NONE;
- 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 &event_id_json = GetMember(relates_to_json, "event_id");
- const rapidjson::Value &rel_type_json = GetMember(relates_to_json, "rel_type");
- if(event_id_json.IsString() && rel_type_json.IsString() && strcmp(rel_type_json.GetString(), "m.replace") == 0) {
- related_event_id = event_id_json.GetString();
- related_event_type = RelatedEventType::EDIT;
- }
- }
+ char url[512];
+ snprintf(url, sizeof(url), "%s/_matrix/client/r0/rooms/%s/context/%s?limit=0&filter=%s", homeserver.c_str(), room->id.c_str(), event_id.c_str(), filter.c_str());
- const rapidjson::Value &content_type = GetMember(content_json, "msgtype");
- if(!content_type.IsString())
- return nullptr;
+ std::string err_msg;
+ rapidjson::Document json_root;
+ DownloadResult download_result = download_json(json_root, url, std::move(additional_args), true, &err_msg);
+ if(download_result != DownloadResult::OK) return nullptr;
- auto new_message = std::make_shared<Message>();
- new_message->event_id = event_id_json.GetString();
- new_message->related_event_id = std::move(related_event_id);
- new_message->related_event_type = related_event_type;
- if(strcmp(content_type.GetString(), "m.text") == 0) {
- new_message->type = MessageType::TEXT;
- } else if(strcmp(content_type.GetString(), "m.image") == 0) {
- new_message->type = MessageType::IMAGE;
- } else if(strcmp(content_type.GetString(), "m.video") == 0) {
- new_message->type = MessageType::VIDEO;
- } else if(strcmp(content_type.GetString(), "m.audio") == 0) {
- new_message->type = MessageType::AUDIO;
- } else if(strcmp(content_type.GetString(), "m.file") == 0) {
- new_message->type = MessageType::FILE;
- } else {
+ if(json_root.IsObject()) {
+ const rapidjson::Value &error_json = GetMember(json_root, "error");
+ if(error_json.IsString()) {
+ fprintf(stderr, "Matrix::get_message_by_id, error: %s\n", error_json.GetString());
+ room->fetched_messages_by_event_id.insert(std::make_pair(event_id, nullptr));
return nullptr;
}
-
- return get_edited_message_original_message(room_data, std::move(new_message));
- } else {
- return get_edited_message_original_message(room_data, replaced_message);
}
+
+ const rapidjson::Value &event_json = GetMember(json_root, "event");
+ std::shared_ptr<Message> new_message = parse_message_event(event_json, room);
+ room->fetched_messages_by_event_id.insert(std::make_pair(event_id, new_message));
+ return new_message;
}
// Returns empty string on error