aboutsummaryrefslogtreecommitdiff
path: root/src/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/Matrix.cpp595
1 files changed, 502 insertions, 93 deletions
diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp
index f79b10c..28b4823 100644
--- a/src/plugins/Matrix.cpp
+++ b/src/plugins/Matrix.cpp
@@ -9,6 +9,7 @@
#include "../../include/AsyncImageLoader.hpp"
#include "../../include/Config.hpp"
#include "../../include/Theme.hpp"
+#include "../../include/Scale.hpp"
#include <rapidjson/document.h>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>
@@ -27,14 +28,15 @@
namespace QuickMedia {
static const mgl::vec2i thumbnail_max_size(600, 337);
+ static const mgl::vec2i custom_emoji_max_size(64, 64);
static const char* SERVICE_NAME = "matrix";
static const char* OTHERS_ROOM_TAG = "tld.name.others";
// Filter without account data. TODO: We include pinned events but limit events to 1. That means if the last event is a pin,
// then we cant see room message preview. TODO: Fix this somehow.
// TODO: What about state events in initial sync in timeline? such as user display name change.
- static const char* INITIAL_FILTER = "{\"presence\":{\"limit\":0,\"types\":[\"\"]},\"account_data\":{\"limit\":0,\"types\":[\"\"]},\"room\":{\"state\":{\"not_types\":[\"m.room.related_groups\",\"m.room.power_levels\",\"m.room.join_rules\",\"m.room.history_visibility\"],\"lazy_load_members\":true},\"timeline\":{\"types\":[\"m.room.message\"],\"limit\":1,\"lazy_load_members\":true},\"ephemeral\":{\"limit\":0,\"types\":[\"\"],\"lazy_load_members\":true},\"account_data\":{\"limit\":1,\"types\":[\"m.fully_read\",\"m.tag\",\"qm.last_read_message_timestamp\"],\"lazy_load_members\":true}}}";
- static const char* ADDITIONAL_MESSAGES_FILTER = "{\"presence\":{\"limit\":0,\"types\":[\"\"]},\"account_data\":{\"limit\":0,\"types\":[\"\"]},\"room\":{\"state\":{\"not_types\":[\"m.room.related_groups\",\"m.room.power_levels\",\"m.room.join_rules\",\"m.room.history_visibility\"],\"lazy_load_members\":true},\"timeline\":{\"limit\":20,\"lazy_load_members\":true},\"ephemeral\":{\"limit\":0,\"types\":[\"\"],\"lazy_load_members\":true},\"account_data\":{\"limit\":0,\"types\":[\"\"],\"lazy_load_members\":true}}}";
- static const char* CONTINUE_FILTER = "{\"presence\":{\"limit\":0,\"types\":[\"\"]},\"account_data\":{\"limit\":0,\"types\":[\"\"]},\"room\":{\"state\":{\"not_types\":[\"m.room.related_groups\",\"m.room.power_levels\",\"m.room.join_rules\",\"m.room.history_visibility\"],\"lazy_load_members\":true},\"timeline\":{\"lazy_load_members\":true},\"ephemeral\":{\"limit\":0,\"types\":[\"\"],\"lazy_load_members\":true},\"account_data\":{\"types\":[\"m.fully_read\",\"m.tag\",\"qm.last_read_message_timestamp\"],\"lazy_load_members\":true}}}";
+ static const char* INITIAL_FILTER = "{\"presence\":{\"limit\":0,\"types\":[\"\"]},\"account_data\":{\"types\":[\"qm.emoji\",\"m.direct\"]},\"room\":{\"state\":{\"not_types\":[\"m.room.related_groups\",\"m.room.power_levels\",\"m.room.join_rules\",\"m.room.history_visibility\"],\"lazy_load_members\":true},\"timeline\":{\"types\":[\"m.room.message\"],\"limit\":1,\"lazy_load_members\":true},\"ephemeral\":{\"limit\":0,\"types\":[\"\"],\"lazy_load_members\":true},\"account_data\":{\"limit\":1,\"types\":[\"m.fully_read\",\"m.tag\",\"qm.last_read_message_timestamp\"],\"lazy_load_members\":true}}}";
+ static const char* ADDITIONAL_MESSAGES_FILTER = "{\"presence\":{\"types\":[\"\"]},\"account_data\":{\"limit\":0,\"types\":[\"\"]},\"room\":{\"state\":{\"not_types\":[\"m.room.related_groups\",\"m.room.power_levels\",\"m.room.join_rules\",\"m.room.history_visibility\"],\"lazy_load_members\":true},\"timeline\":{\"limit\":20,\"lazy_load_members\":true},\"ephemeral\":{\"limit\":0,\"types\":[\"\"],\"lazy_load_members\":true},\"account_data\":{\"limit\":0,\"types\":[\"\"],\"lazy_load_members\":true}}}";
+ static const char* CONTINUE_FILTER = "{\"presence\":{\"limit\":0,\"types\":[\"\"]},\"account_data\":{\"types\":[\"qm.emoji\",\"m.direct\"]},\"room\":{\"state\":{\"not_types\":[\"m.room.related_groups\",\"m.room.power_levels\",\"m.room.join_rules\",\"m.room.history_visibility\"],\"lazy_load_members\":true},\"timeline\":{\"lazy_load_members\":true},\"ephemeral\":{\"limit\":0,\"types\":[\"\"],\"lazy_load_members\":true},\"account_data\":{\"types\":[\"m.fully_read\",\"m.tag\",\"qm.last_read_message_timestamp\"],\"lazy_load_members\":true}}}";
static std::string capitalize(const std::string &str) {
if(str.size() >= 1)
@@ -49,6 +51,8 @@ namespace QuickMedia {
if(tag.size() >= 2 && memcmp(tag.data(), "m.", 2) == 0) {
if(strcmp(tag.c_str() + 2, "favourite") == 0)
return "Favorites";
+ else if(strcmp(tag.c_str() + 2, "direct") == 0)
+ return "Direct messages";
else if(strcmp(tag.c_str() + 2, "lowpriority") == 0)
return "Low priority";
else if(strcmp(tag.c_str() + 2, "server_notice") == 0)
@@ -117,7 +121,7 @@ namespace QuickMedia {
return colors[color_hash_code(user_id) % num_colors];
}
- static std::string remove_reply_formatting(const std::string &str) {
+ static std::string remove_reply_formatting(Matrix *matrix, const std::string &str) {
if(strncmp(str.c_str(), "> <@", 4) == 0) {
size_t index = str.find("> ", 4);
if(index != std::string::npos) {
@@ -126,12 +130,12 @@ namespace QuickMedia {
return str.substr(msg_begin + 2);
}
} else {
- return formatted_text_to_qm_text(str.c_str(), str.size(), false);
+ return formatted_text_to_qm_text(matrix, str.c_str(), str.size(), false);
}
return str;
}
- static std::string remove_reply_formatting(const Message *message, bool keep_formatted = false) {
+ static std::string remove_reply_formatting(Matrix *matrix, const Message *message, bool keep_formatted = false) {
if(!message->body_is_formatted && strncmp(message->body.c_str(), "> <@", 4) == 0) {
size_t index = message->body.find("> ", 4);
if(index != std::string::npos) {
@@ -144,7 +148,7 @@ namespace QuickMedia {
if(keep_formatted)
return message->body;
else
- return formatted_text_to_qm_text(message->body.c_str(), message->body.size(), false);
+ return formatted_text_to_qm_text(matrix, message->body.c_str(), message->body.size(), false);
}
}
@@ -464,7 +468,7 @@ namespace QuickMedia {
if(!sync_is_cache && message_dir == MessageDirection::AFTER) {
for(auto &message : messages) {
if(message->notification_mentions_me) {
- std::string body = remove_reply_formatting(message.get());
+ std::string body = remove_reply_formatting(matrix, message->body);
bool read = true;
// TODO: What if the message or username begins with "-"? also make the notification image be the avatar of the user
if((!is_window_focused || room != current_room) && message->related_event_type != RelatedEventType::EDIT && message->related_event_type != RelatedEventType::REDACTION) {
@@ -629,15 +633,15 @@ namespace QuickMedia {
return nullptr;
}
- static std::string message_to_qm_text(const Message *message, bool allow_formatted_text = true) {
+ std::string message_to_qm_text(Matrix *matrix, const Message *message, bool allow_formatted_text) {
if(message->body_is_formatted)
- return formatted_text_to_qm_text(message->body.c_str(), message->body.size(), allow_formatted_text);
+ return formatted_text_to_qm_text(matrix, message->body.c_str(), message->body.size(), allow_formatted_text);
else
return message->body;
}
- static std::string message_to_room_description_text(Message *message) {
- std::string body = strip(message_to_qm_text(message));
+ static std::string message_to_room_description_text(Matrix *matrix, Message *message) {
+ std::string body = strip(formatted_text_to_qm_text(matrix, message->body.c_str(), message->body.size(), true));
if(message->type == MessageType::REACTION)
return "Reacted with: " + body;
else if(message->related_event_type == RelatedEventType::REPLY)
@@ -704,7 +708,7 @@ namespace QuickMedia {
room_desc += "Unread: ";
if(last_unread_message)
- room_desc += extract_first_line_remove_newline_elipses(matrix->message_get_author_displayname(last_unread_message), AUTHOR_MAX_LENGTH) + ": " + message_to_room_description_text(last_unread_message);
+ room_desc += extract_first_line_remove_newline_elipses(matrix->message_get_author_displayname(last_unread_message), AUTHOR_MAX_LENGTH) + ": " + message_to_room_description_text(matrix, last_unread_message);
int unread_notification_count = room->unread_notification_count;
if(unread_notification_count > 0 && set_room_as_unread) {
@@ -724,7 +728,7 @@ namespace QuickMedia {
rooms_page->move_room_to_top(room);
room_tags_page->move_room_to_top(room);
} else if(last_new_message) {
- room->body_item->set_description(extract_first_line_remove_newline_elipses(matrix->message_get_author_displayname(last_new_message.get()), AUTHOR_MAX_LENGTH) + ": " + message_to_room_description_text(last_new_message.get()));
+ room->body_item->set_description(extract_first_line_remove_newline_elipses(matrix->message_get_author_displayname(last_new_message.get()), AUTHOR_MAX_LENGTH) + ": " + message_to_room_description_text(matrix, last_new_message.get()));
room->body_item->set_description_color(get_theme().faded_text_color);
room->body_item->set_description_max_lines(3);
@@ -974,6 +978,9 @@ namespace QuickMedia {
matrix->logout();
program->set_go_to_previous_page();
return PluginResult::OK;
+ } else if(args.url == "emoji") {
+ result_tabs.push_back(Tab{create_body(), std::make_unique<MatrixCustomEmojiPage>(program, matrix), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
+ return PluginResult::OK;
} else {
return PluginResult::ERR;
}
@@ -991,9 +998,170 @@ namespace QuickMedia {
} else {
show_notification("QuickMedia", "Failed to join " + args.title, Urgency::CRITICAL);
}
+
+ return PluginResult::OK;
+ }
+
+ static const char* file_get_filename(const std::string &filepath) {
+ size_t index = filepath.rfind('/');
+ if(index == std::string::npos)
+ return filepath.c_str();
+ return filepath.c_str() + index + 1;
+ }
+
+ static bool generate_random_characters(char *buffer, int buffer_size) {
+ int fd = open("/dev/urandom", O_RDONLY);
+ if(fd == -1) {
+ perror("/dev/urandom");
+ return false;
+ }
+
+ if(read(fd, buffer, buffer_size) < buffer_size) {
+ fprintf(stderr, "Failed to read %d bytes from /dev/urandom\n", buffer_size);
+ close(fd);
+ return false;
+ }
+
+ close(fd);
+ return true;
+ }
+
+ static std::string random_characters_to_readable_string(const char *buffer, int buffer_size) {
+ std::ostringstream result;
+ result << std::hex;
+ for(int i = 0; i < buffer_size; ++i)
+ result << (int)(unsigned char)buffer[i];
+ return result.str();
+ }
+
+ PluginResult MatrixCustomEmojiPage::submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) {
+ if(args.url == "add") {
+ auto submit_handler = [this](FileManagerPage*, const std::filesystem::path &filepath) {
+ program->run_task_with_loading_screen([this, filepath] {
+ std::string key = filepath.filename().string();
+ if(!key.empty()) {
+ size_t ext_index = key.rfind('.');
+ if(ext_index != std::string::npos)
+ key = key.substr(0, ext_index);
+ }
+
+ if(key.empty()) {
+ char random_characters[10];
+ if(!generate_random_characters(random_characters, sizeof(random_characters))) {
+ show_notification("QuickMedia", "Failed to generate random string", Urgency::CRITICAL);
+ return false;
+ }
+ key = random_characters_to_readable_string(random_characters, sizeof(random_characters));
+ }
+
+ if(matrix->does_custom_emoji_with_name_exist(key)) {
+ show_notification("QuickMedia", "Failed to upload custom emoji. You already have a custom emoji with the name " + key, Urgency::CRITICAL);
+ return false;
+ }
+
+ std::string mxc_url;
+ std::string err_msg;
+ if(matrix->upload_custom_emoji(filepath, key, mxc_url, err_msg) != PluginResult::OK) {
+ show_notification("QuickMedia", "Failed to upload custom emoji, error: " + err_msg, Urgency::CRITICAL);
+ return false;
+ }
+
+ return true;
+ });
+ return std::vector<Tab>{};
+ };
+
+ auto file_manager_body = create_body();
+ auto file_manager_page = std::make_unique<FileManagerPage>(program, FILE_MANAGER_MIME_TYPE_IMAGE, std::move(submit_handler));
+ file_manager_page->set_current_directory(get_home_dir().data);
+ BodyItems body_items;
+ file_manager_page->get_files_in_directory(body_items);
+ file_manager_body->set_items(std::move(body_items));
+
+ result_tabs.push_back(Tab{std::move(file_manager_body), std::move(file_manager_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
+ return PluginResult::OK;
+ } else if(args.url == "rename") {
+ result_tabs.push_back(Tab{create_body(false, true), std::make_unique<MatrixCustomEmojiRenameSelectPage>(program, matrix), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
+ return PluginResult::OK;
+ } else if(args.url == "delete") {
+ auto body = create_body(false, true);
+ BodyItems body_items;
+ for(auto &emoji : matrix->get_custom_emojis()) {
+ auto emoji_item = BodyItem::create(":" + emoji.first + ":");
+ emoji_item->url = emoji.first;
+ emoji_item->thumbnail_url = matrix->get_media_url(emoji.second.url);
+ emoji_item->thumbnail_size = emoji.second.size;
+ body_items.push_back(std::move(emoji_item));
+ }
+ body->set_items(std::move(body_items));
+
+ Body *body_p = body.get();
+ result_tabs.push_back(Tab{std::move(body), std::make_unique<MatrixCustomEmojiDeletePage>(program, matrix, body_p), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
+ return PluginResult::OK;
+ } else {
+ return PluginResult::ERR;
+ }
+ }
+
+ PluginResult MatrixCustomEmojiPage::lazy_fetch(BodyItems &result_items) {
+ auto add_emoji_item = BodyItem::create("Add emoji");
+ add_emoji_item->url = "add";
+ result_items.push_back(std::move(add_emoji_item));
+
+ auto rename_emoji_item = BodyItem::create("Rename emoji");
+ rename_emoji_item->url = "rename";
+ result_items.push_back(std::move(rename_emoji_item));
+
+ auto delete_emoji_item = BodyItem::create("Delete emoji");
+ delete_emoji_item->set_title_color(mgl::Color(255, 45, 47));
+ delete_emoji_item->url = "delete";
+ result_items.push_back(std::move(delete_emoji_item));
+
+ return PluginResult::OK;
+ }
+
+ bool MatrixCustomEmojiPage::is_ready() {
+ return matrix->is_initial_sync_finished();
+ }
+
+ PluginResult MatrixCustomEmojiRenameSelectPage::submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) {
+ result_tabs.push_back(Tab{create_body(), std::make_unique<MatrixCustomEmojiRenamePage>(program, matrix, args.url), create_search_bar("Enter a new name for the emoji...", SEARCH_DELAY_FILTER)});
+ return PluginResult::OK;
+ }
+
+ PluginResult MatrixCustomEmojiRenameSelectPage::lazy_fetch(BodyItems &result_items) {
+ for(auto &emoji : matrix->get_custom_emojis()) {
+ auto emoji_item = BodyItem::create(":" + emoji.first + ":");
+ emoji_item->url = emoji.first;
+ emoji_item->thumbnail_url = matrix->get_media_url(emoji.second.url);
+ emoji_item->thumbnail_size = emoji.second.size;
+ result_items.push_back(std::move(emoji_item));
+ }
return PluginResult::OK;
}
+ PluginResult MatrixCustomEmojiRenamePage::submit(const SubmitArgs &args, std::vector<Tab>&) {
+ if(matrix->rename_custom_emoji(emoji_key, args.title)) {
+ program->set_go_to_previous_page();
+ return PluginResult::OK;
+ } else {
+ show_notification("QuickMedia", "Failed to rename emoji " + emoji_key + " to " + args.title, Urgency::CRITICAL);
+ return PluginResult::OK;
+ }
+ }
+
+ PluginResult MatrixCustomEmojiDeletePage::submit(const SubmitArgs &args, std::vector<Tab>&) {
+ if(matrix->delete_custom_emoji(args.url)) {
+ body->erase_item([&args](std::shared_ptr<BodyItem> &item) {
+ return item->url == args.url;
+ });
+ return PluginResult::OK;
+ } else {
+ show_notification("QuickMedia", "Failed to delete emoji: " + args.url, Urgency::CRITICAL);
+ return PluginResult::OK;
+ }
+ }
+
MatrixChatPage::MatrixChatPage(Program *program, std::string room_id, MatrixRoomsPage *rooms_page, std::string jump_to_event_id) :
Page(program), room_id(std::move(room_id)), rooms_page(rooms_page), jump_to_event_id(std::move(jump_to_event_id))
{
@@ -1533,19 +1701,21 @@ namespace QuickMedia {
notification_thread.join();
}
+ std::lock_guard<std::recursive_mutex> lock(room_data_mutex);
delegate = nullptr;
sync_failed = false;
sync_fail_reason.clear();
- next_batch.clear();
+ set_next_batch("");
next_notifications_token.clear();
invites.clear();
filter_cached.reset();
my_events_transaction_ids.clear();
finished_fetching_notifications = false;
+ custom_emoji_by_key.clear();
}
- bool Matrix::is_initial_sync_finished() const {
- return !next_batch.empty();
+ bool Matrix::is_initial_sync_finished() {
+ return initial_sync_finished;
}
bool Matrix::did_initial_sync_fail(std::string &err_msg) {
@@ -1705,14 +1875,12 @@ namespace QuickMedia {
if(!root.IsObject())
return PluginResult::ERR;
- //const rapidjson::Value &account_data_json = GetMember(root, "account_data");
- //std::optional<std::set<std::string>> dm_rooms;
- //parse_sync_account_data(account_data_json, dm_rooms);
- // TODO: Include "Direct messages" as a tag using |dm_rooms| above
-
const rapidjson::Value &rooms_json = GetMember(root, "rooms");
parse_sync_room_data(rooms_json, is_additional_messages_sync, initial_sync);
+ const rapidjson::Value &account_data_json = GetMember(root, "account_data");
+ parse_sync_account_data(account_data_json);
+
return PluginResult::OK;
}
@@ -1771,7 +1939,7 @@ namespace QuickMedia {
notification.room = room;
notification.event_id = std::move(event_id);
notification.sender_user_id.assign(sender_json.GetString(), sender_json.GetStringLength());
- notification.body = remove_reply_formatting(body_json.GetString());
+ notification.body = remove_reply_formatting(this, body_json.GetString());
notification.timestamp = timestamp;
notification.read = read_json.GetBool();
callback_func(notification);
@@ -1781,7 +1949,7 @@ namespace QuickMedia {
return PluginResult::OK;
}
- PluginResult Matrix::parse_sync_account_data(const rapidjson::Value &account_data_json, std::optional<std::set<std::string>> &dm_rooms) {
+ PluginResult Matrix::parse_sync_account_data(const rapidjson::Value &account_data_json) {
if(!account_data_json.IsObject())
return PluginResult::OK;
@@ -1789,36 +1957,74 @@ namespace QuickMedia {
if(!events_json.IsArray())
return PluginResult::OK;
- bool has_direct_rooms = false;
- std::set<std::string> dm_rooms_tmp;
for(const rapidjson::Value &event_item_json : events_json.GetArray()) {
if(!event_item_json.IsObject())
continue;
const rapidjson::Value &type_json = GetMember(event_item_json, "type");
- if(!type_json.IsString() || strcmp(type_json.GetString(), "m.direct") != 0)
+ if(!type_json.IsString())
continue;
const rapidjson::Value &content_json = GetMember(event_item_json, "content");
if(!content_json.IsObject())
continue;
- has_direct_rooms = true;
- for(auto const &it : content_json.GetObject()) {
- if(!it.value.IsArray())
- continue;
+ if(strcmp(type_json.GetString(), "m.direct") == 0) {
+ for(auto const &it : content_json.GetObject()) {
+ if(!it.name.IsString())
+ continue;
- for(const rapidjson::Value &room_id_json : it.value.GetArray()) {
- if(!room_id_json.IsString())
+ if(!it.value.IsArray())
continue;
-
- dm_rooms_tmp.insert(std::string(room_id_json.GetString(), room_id_json.GetStringLength()));
+
+ for(const rapidjson::Value &room_id_json : it.value.GetArray()) {
+ if(!room_id_json.IsString())
+ continue;
+
+ RoomData *room = get_room_by_id(std::string(room_id_json.GetString(), room_id_json.GetStringLength()));
+ if(!room) {
+ fprintf(stderr, "Warning: got m.direct for room %s that we haven't created yet\n", room_id_json.GetString());
+ continue;
+ }
+
+ auto user = get_user_by_id(room, std::string(it.name.GetString(), it.name.GetStringLength()), nullptr, false);
+ if(!user) {
+ fprintf(stderr, "Warning: got m.direct for user %s that doesn't exist in the room %s yet\n", it.name.GetString(), room_id_json.GetString());
+ continue;
+ }
+
+ room->acquire_room_lock();
+ std::set<std::string> &room_tags = room->get_tags_thread_unsafe();
+ auto room_tag_it = room_tags.find("m.direct");
+ if(room_tag_it == room_tags.end()) {
+ room_tags.insert("m.direct");
+ ui_thread_tasks.push([this, room]{ delegate->room_add_tag(room, "m.direct"); });
+ }
+ room->release_room_lock();
+ }
}
- }
- }
+ } else if(strcmp(type_json.GetString(), "qm.emoji") == 0) {
+ std::lock_guard<std::recursive_mutex> lock(room_data_mutex);
+ for(auto const &emoji_json : content_json.GetObject()) {
+ if(!emoji_json.name.IsString() || !emoji_json.value.IsObject())
+ continue;
- if(has_direct_rooms)
- dm_rooms = std::move(dm_rooms_tmp);
+ const rapidjson::Value &url_json = GetMember(emoji_json.value, "url");
+ const rapidjson::Value &width_json = GetMember(emoji_json.value, "width");
+ const rapidjson::Value &height_json = GetMember(emoji_json.value, "height");
+ if(!url_json.IsString())
+ continue;
+
+ CustomEmoji custom_emoji;
+ custom_emoji.url = url_json.GetString();
+ if(width_json.IsInt() && height_json.IsInt()) {
+ custom_emoji.size.x = width_json.GetInt();
+ custom_emoji.size.y = height_json.GetInt();
+ }
+ custom_emoji_by_key[emoji_json.name.GetString()] = std::move(custom_emoji);
+ }
+ }
+ }
return PluginResult::OK;
}
@@ -1986,6 +2192,20 @@ namespace QuickMedia {
item_timestamp = origin_server_ts.GetInt64();
}
+ const rapidjson::Value &is_direct_json = GetMember(content_json, "is_direct");
+ if(is_direct_json.IsBool() && is_direct_json.GetBool()) {
+ room_data->acquire_room_lock();
+ std::set<std::string> &room_tags = room_data->get_tags_thread_unsafe();
+
+ auto room_tag_it = room_tags.find("m.direct");
+ if(room_tag_it == room_tags.end()) {
+ room_tags.insert("m.direct");
+ ui_thread_tasks.push([this, room_data]{ delegate->room_add_tag(room_data, "m.direct"); });
+ }
+
+ room_data->release_room_lock();
+ }
+
parse_user_info(content_json, sender_json->GetString(), room_data, item_timestamp);
}
}
@@ -2003,7 +2223,7 @@ namespace QuickMedia {
return media_url.substr(start, end - start);
}
- static std::string get_thumbnail_url(const std::string &homeserver, const std::string &mxc_id) {
+ static std::string get_avatar_thumbnail_url(const std::string &homeserver, const std::string &mxc_id) {
if(mxc_id.empty())
return "";
@@ -2011,6 +2231,10 @@ namespace QuickMedia {
return homeserver + "/_matrix/media/r0/thumbnail/" + mxc_id + "?width=" + size + "&height=" + size + "&method=crop";
}
+ std::string Matrix::get_media_url(const std::string &mxc_id) {
+ return homeserver + "/_matrix/media/r0/download/" + thumbnail_url_extract_media_id(mxc_id);
+ }
+
std::shared_ptr<UserInfo> Matrix::parse_user_info(const rapidjson::Value &json, const std::string &user_id, RoomData *room_data, int64_t timestamp) {
assert(json.IsObject());
std::string avatar_url_str;
@@ -2023,7 +2247,7 @@ namespace QuickMedia {
std::string display_name = display_name_json.IsString() ? display_name_json.GetString() : user_id;
std::string avatar_url = thumbnail_url_extract_media_id(avatar_url_str);
if(!avatar_url.empty())
- avatar_url = get_thumbnail_url(homeserver, avatar_url); // TODO: Remove the constant strings around to reduce memory usage (6.3mb)
+ avatar_url = get_avatar_thumbnail_url(homeserver, avatar_url); // TODO: Remove the constant strings around to reduce memory usage (6.3mb)
//auto user_info = std::make_shared<UserInfo>(room_data, user_id, std::move(display_name), std::move(avatar_url));
// Overwrites user data
//room_data->add_user(user_info);
@@ -2195,8 +2419,8 @@ namespace QuickMedia {
return false;
}
- bool message_contains_user_mention(const Message *message, const std::string &username, const std::string &user_id) {
- const std::string formatted_text = message_to_qm_text(message, false);
+ bool message_contains_user_mention(Matrix *matrix, const Message *message, const std::string &username, const std::string &user_id) {
+ const std::string formatted_text = message_to_qm_text(matrix, message, false);
return message_contains_user_mention(formatted_text, username) || message_contains_user_mention(formatted_text, user_id);
}
@@ -2263,7 +2487,7 @@ namespace QuickMedia {
// TODO: Is @room ok? shouldn't we also check if the user has permission to do @room? (only when notifications are limited to @mentions)
// TODO: Is comparing against read marker timestamp ok enough?
if(me && message->timestamp > read_marker_message_timestamp) {
- std::string message_str = message_to_qm_text(message.get(), false);
+ std::string message_str = message_to_qm_text(this, message.get(), false);
message->notification_mentions_me = message_contains_user_mention(message_str, my_display_name) || message_contains_user_mention(message_str, me->user_id) || message_contains_user_mention(message_str, "@room");
}
}
@@ -2359,7 +2583,11 @@ namespace QuickMedia {
bool allow_formatted_text = false;
bool inside_source_highlight = false;
bool supports_syntax_highlight = false;
+ bool inside_img_tag = false;
+ std::string_view img_src;
+ mgl::vec2i img_size;
mgl::Color font_color = mgl::Color(255, 255, 255, 255);
+ Matrix *matrix = nullptr;
};
static int accumulate_string(char *data, int size, void *userdata) {
@@ -2384,6 +2612,10 @@ namespace QuickMedia {
else if(html_parser->tag_name.size == 4 && memcmp(html_parser->tag_name.data, "code", 4) == 0) {
parse_userdata.inside_code_tag = true;
parse_userdata.code_tag_language = std::string_view();
+ } else if(html_parser->tag_name.size == 3 && memcmp(html_parser->tag_name.data, "img", 3) == 0) {
+ parse_userdata.inside_img_tag = true;
+ parse_userdata.img_src = std::string_view();
+ parse_userdata.img_size = { 0, 0 };
}
break;
}
@@ -2397,6 +2629,17 @@ namespace QuickMedia {
parse_userdata.mx_reply_depth = std::max(0, parse_userdata.mx_reply_depth - 1);
} else if(html_parser->tag_name.size == 4 && memcmp(html_parser->tag_name.data, "code", 4) == 0) {
parse_userdata.inside_code_tag = false;
+ } else if(html_parser->tag_name.size == 3 && memcmp(html_parser->tag_name.data, "img", 3) == 0) {
+ if(parse_userdata.matrix && parse_userdata.inside_img_tag && parse_userdata.img_src.size() > 0) {
+ std::string image_url(parse_userdata.img_src);
+ html_unescape_sequences(image_url);
+ mgl::vec2i img_size = parse_userdata.img_size;
+ // TODO: Better solution when size not given?
+ if(img_size.x == 0 || img_size.y == 0)
+ img_size = custom_emoji_max_size;
+ parse_userdata.result += Text::formatted_image(parse_userdata.matrix->get_media_url(image_url), false, img_size);
+ }
+ parse_userdata.inside_img_tag = false;
}
break;
}
@@ -2407,6 +2650,20 @@ namespace QuickMedia {
} else if(parse_userdata.inside_code_tag && html_parser->attribute_key.size == 5 && memcmp(html_parser->attribute_key.data, "class", 5) == 0) {
if(html_parser->attribute_value.size > 9 && memcmp(html_parser->attribute_value.data, "language-", 9) == 0)
parse_userdata.code_tag_language = std::string_view(html_parser->attribute_value.data + 9, html_parser->attribute_value.size - 9);
+ } else if(parse_userdata.allow_formatted_text && parse_userdata.inside_img_tag) {
+ if(html_parser->attribute_key.size == 3 && memcmp(html_parser->attribute_key.data, "src", 3) == 0) {
+ parse_userdata.img_src = std::string_view(html_parser->attribute_value.data, html_parser->attribute_value.size);
+ } else if(html_parser->attribute_key.size == 5 && memcmp(html_parser->attribute_key.data, "width", 5) == 0) {
+ const std::string width(html_parser->attribute_value.data, html_parser->attribute_value.size);
+ parse_userdata.img_size.x = atoi(width.c_str());
+ } else if(html_parser->attribute_key.size == 6 && memcmp(html_parser->attribute_key.data, "height", 6) == 0) {
+ const std::string height(html_parser->attribute_value.data, html_parser->attribute_value.size);
+ parse_userdata.img_size.y = atoi(height.c_str());
+ }
+ } else if(!parse_userdata.allow_formatted_text && parse_userdata.inside_img_tag && html_parser->attribute_key.size == 3 && memcmp(html_parser->attribute_key.data, "alt", 3) == 0) {
+ std::string text_to_add(html_parser->attribute_value.data, html_parser->attribute_value.size);
+ html_unescape_sequences(text_to_add);
+ parse_userdata.result += std::move(text_to_add);
}
break;
}
@@ -2455,10 +2712,11 @@ namespace QuickMedia {
return 0;
}
- std::string formatted_text_to_qm_text(const char *str, size_t size, bool allow_formatted_text) {
+ std::string formatted_text_to_qm_text(Matrix *matrix, const char *str, size_t size, bool allow_formatted_text) {
FormattedTextParseUserdata parse_userdata;
parse_userdata.allow_formatted_text = allow_formatted_text;
parse_userdata.supports_syntax_highlight = is_program_executable_by_name("source-highlight");
+ parse_userdata.matrix = matrix;
html_parser_parse(str, size, formattext_text_parser_callback, &parse_userdata);
return std::move(parse_userdata.result);
}
@@ -2648,7 +2906,7 @@ namespace QuickMedia {
body = user_display_name + " changed his profile picture";
std::string new_avatar_url_str = thumbnail_url_extract_media_id(new_avatar_url_json.GetString());
if(!new_avatar_url_str.empty())
- new_avatar_url_str = get_thumbnail_url(homeserver, new_avatar_url_str); // TODO: Remove the constant strings around to reduce memory usage (6.3mb)
+ new_avatar_url_str = get_avatar_thumbnail_url(homeserver, new_avatar_url_str); // TODO: Remove the constant strings around to reduce memory usage (6.3mb)
new_avatar_url = new_avatar_url_str;
update_user_display_info = room_data->set_user_avatar_url(user, std::move(new_avatar_url_str), timestamp);
} else if((!new_avatar_url_json.IsString() || new_avatar_url_json.GetStringLength() == 0) && prev_avatar_url_json.IsString()) {
@@ -2989,7 +3247,7 @@ namespace QuickMedia {
if(!url_json.IsString() || strncmp(url_json.GetString(), "mxc://", 6) != 0)
continue;
- update_room_avatar_url |= room_data->set_avatar_url(get_thumbnail_url(homeserver, thumbnail_url_extract_media_id(url_json.GetString())), item_timestamp);
+ update_room_avatar_url |= room_data->set_avatar_url(get_avatar_thumbnail_url(homeserver, thumbnail_url_extract_media_id(url_json.GetString())), item_timestamp);
room_data->avatar_is_fallback = false;
} else if(strcmp(type_json.GetString(), "m.room.topic") == 0) {
const rapidjson::Value &content_json = GetMember(event_item_json, "content");
@@ -3144,7 +3402,10 @@ namespace QuickMedia {
ui_thread_tasks.push([this, room_data]{ delegate->room_add_tag(room_data, OTHERS_ROOM_TAG); });
}
+ const bool contains_direct_messaging = room_tags.find("m.direct") != room_tags.end();
room_tags = std::move(new_tags);
+ if(contains_direct_messaging)
+ room_tags.insert("m.direct");
room_data->release_room_lock();
}
}
@@ -3362,31 +3623,6 @@ namespace QuickMedia {
return PluginResult::OK;
}
- static bool generate_random_characters(char *buffer, int buffer_size) {
- int fd = open("/dev/urandom", O_RDONLY);
- if(fd == -1) {
- perror("/dev/urandom");
- return false;
- }
-
- if(read(fd, buffer, buffer_size) < buffer_size) {
- fprintf(stderr, "Failed to read %d bytes from /dev/urandom\n", buffer_size);
- close(fd);
- return false;
- }
-
- close(fd);
- return true;
- }
-
- static std::string random_characters_to_readable_string(const char *buffer, int buffer_size) {
- std::ostringstream result;
- result << std::hex;
- for(int i = 0; i < buffer_size; ++i)
- result << (int)(unsigned char)buffer[i];
- return result.str();
- }
-
std::string create_transaction_id() {
char random_characters[18];
if(!generate_random_characters(random_characters, sizeof(random_characters)))
@@ -3505,11 +3741,29 @@ namespace QuickMedia {
}
}
+ static void replace_emoji_references_with_formatted_images(std::string &str, const std::unordered_map<std::string, CustomEmoji> &custom_emojis) {
+ for(const auto &it : custom_emojis) {
+ std::string keybind = ":" + it.first + ":";
+ std::string url = it.second.url;
+ html_escape_sequences(url);
+ std::string width = std::to_string(it.second.size.x);
+ std::string height = std::to_string(it.second.size.y);
+ std::string tag = "<img src=\"" + url + "\" alt=\"" + keybind + "\" width=\"" + width + "\" height=\"" + height + "\" vertical-align=\"middle\" />";
+ string_replace_all(str, keybind, tag);
+ }
+ }
+
std::string Matrix::body_to_formatted_body(RoomData *room, const std::string &body) {
+ std::unordered_map<std::string, CustomEmoji> custom_emojis_copy;
+ {
+ std::lock_guard<std::recursive_mutex> lock(room_data_mutex);
+ custom_emojis_copy = custom_emoji_by_key;
+ }
+
std::string formatted_body;
bool is_inside_code_block = false;
bool is_first_line = true;
- string_split(body, '\n', [this, room, &formatted_body, &is_inside_code_block, &is_first_line](const char *str, size_t size){
+ string_split(body, '\n', [this, room, &formatted_body, &is_inside_code_block, &is_first_line, &custom_emojis_copy](const char *str, size_t size){
if(!is_first_line) {
if(is_inside_code_block)
formatted_body += '\n';
@@ -3533,6 +3787,9 @@ namespace QuickMedia {
}
is_first_line = true;
} else {
+ if(!is_inside_code_block)
+ replace_emoji_references_with_formatted_images(line_str, custom_emojis_copy);
+
if(!is_inside_code_block && size > 0 && str[0] == '>') {
formatted_body += "<font color=\"#789922\">";
formatted_body_add_line(room, formatted_body, line_str);
@@ -3653,17 +3910,17 @@ namespace QuickMedia {
return result;
}
- static std::string get_reply_message(const Message *message, bool keep_formatted = false) {
+ static std::string get_reply_message(Matrix *matrix, const Message *message, bool keep_formatted = false) {
std::string related_to_body;
switch(message->type) {
case MessageType::TEXT: {
if(message->related_event_type != RelatedEventType::NONE) {
- related_to_body = remove_reply_formatting(message, keep_formatted);
+ related_to_body = remove_reply_formatting(matrix, message, keep_formatted);
} else {
if(keep_formatted && message->body_is_formatted)
related_to_body = message->body;
else
- related_to_body = message_to_qm_text(message, false);
+ related_to_body = message_to_qm_text(matrix, message, false);
}
break;
}
@@ -3683,15 +3940,15 @@ namespace QuickMedia {
if(keep_formatted && message->body_is_formatted)
related_to_body = message->body;
else
- related_to_body = message_to_qm_text(message, false);
+ related_to_body = message_to_qm_text(matrix, message, false);
break;
}
}
return related_to_body;
}
- static std::string create_body_for_message_reply(const Message *message, const std::string &body) {
- return "> <" + message->user->user_id + "> " + block_quote(get_reply_message(message)) + "\n\n" + body;
+ static std::string create_body_for_message_reply(Matrix *matrix, const Message *message, const std::string &body) {
+ return "> <" + message->user->user_id + "> " + block_quote(get_reply_message(matrix, message)) + "\n\n" + body;
}
static std::string extract_homeserver_from_room_id(const std::string &room_id) {
@@ -3703,7 +3960,7 @@ namespace QuickMedia {
std::string Matrix::create_formatted_body_for_message_reply(RoomData *room, const Message *message, const std::string &body) {
std::string formatted_body = body_to_formatted_body(room, body);
- std::string related_to_body = get_reply_message(message, true);
+ std::string related_to_body = get_reply_message(this, message, true);
if(!message->body_is_formatted)
html_escape_sequences(related_to_body);
// TODO: Add keybind to navigate to the reply message, which would also depend on this formatting.
@@ -3746,7 +4003,7 @@ namespace QuickMedia {
rapidjson::Document relates_to_json(rapidjson::kObjectType);
relates_to_json.AddMember("m.in_reply_to", std::move(in_reply_to_json), relates_to_json.GetAllocator());
- std::string message_reply_body = create_body_for_message_reply(related_to_text_message, body); // Yes, the reply is to the edited message but the event_id reference is to the original message...
+ std::string message_reply_body = create_body_for_message_reply(this, related_to_text_message, body); // Yes, the reply is to the edited message but the event_id reference is to the original message...
std::string formatted_message_reply_body = create_formatted_body_for_message_reply(room, related_to_text_message, body);
rapidjson::Document request_data(rapidjson::kObjectType);
@@ -4079,11 +4336,162 @@ namespace QuickMedia {
room->set_prev_batch("");
}
- static const char* file_get_filename(const std::string &filepath) {
- size_t index = filepath.rfind('/');
- if(index == std::string::npos)
- return filepath.c_str();
- return filepath.c_str() + index + 1;
+ PluginResult Matrix::upload_custom_emoji(const std::string &filepath, const std::string &key, std::string &mxc_url, std::string &err_msg) {
+ UploadInfo file_info;
+ UploadInfo thumbnail_info;
+ // TODO: Do not create and upload thumbnail
+ PluginResult upload_file_result = upload_file(filepath, "", file_info, thumbnail_info, err_msg);
+ if(upload_file_result != PluginResult::OK)
+ return upload_file_result;
+
+ mxc_url = std::move(file_info.content_uri);
+
+ rapidjson::Document request_data(rapidjson::kObjectType);
+ {
+ std::lock_guard<std::recursive_mutex> lock(room_data_mutex);
+ for(const auto &it : custom_emoji_by_key) {
+ rapidjson::Document emoji_obj(rapidjson::kObjectType);
+ emoji_obj.AddMember("url", rapidjson::Value(it.second.url.c_str(), request_data.GetAllocator()).Move(), request_data.GetAllocator());
+ emoji_obj.AddMember("width", it.second.size.x, request_data.GetAllocator());
+ emoji_obj.AddMember("height", it.second.size.y, request_data.GetAllocator());
+ request_data.AddMember(rapidjson::Value(it.first.c_str(), request_data.GetAllocator()).Move(), std::move(emoji_obj), request_data.GetAllocator());
+ }
+ }
+
+ CustomEmoji custom_emoji;
+ custom_emoji.url = mxc_url;
+ rapidjson::Document emoji_obj(rapidjson::kObjectType);
+ emoji_obj.AddMember("url", rapidjson::Value(mxc_url.c_str(), request_data.GetAllocator()).Move(), request_data.GetAllocator());
+ if(file_info.dimensions) {
+ custom_emoji.size = clamp_to_size(mgl::vec2i(file_info.dimensions->width, file_info.dimensions->height), custom_emoji_max_size);
+ emoji_obj.AddMember("width", custom_emoji.size.x, request_data.GetAllocator());
+ emoji_obj.AddMember("height", custom_emoji.size.y, request_data.GetAllocator());
+ }
+ request_data.AddMember(rapidjson::Value(key.c_str(), request_data.GetAllocator()).Move(), std::move(emoji_obj), request_data.GetAllocator());
+
+ rapidjson::StringBuffer buffer;
+ rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
+ request_data.Accept(writer);
+
+ std::vector<CommandArg> additional_args = {
+ { "-X", "PUT" },
+ { "-H", "content-type: application/json" },
+ { "-H", "Authorization: Bearer " + access_token },
+ { "--data-binary", buffer.GetString() }
+ };
+
+ std::string server_response;
+ DownloadResult download_result = download_to_string(homeserver + "/_matrix/client/r0/user/" + my_user_id + "/account_data/qm.emoji", server_response, std::move(additional_args), true);
+ if(download_result != DownloadResult::OK)
+ return download_result_to_plugin_result(download_result);
+
+ std::lock_guard<std::recursive_mutex> lock(room_data_mutex);
+ custom_emoji_by_key[key] = std::move(custom_emoji);
+ return PluginResult::OK;
+ }
+
+ bool Matrix::delete_custom_emoji(const std::string &key) {
+ rapidjson::Document request_data(rapidjson::kObjectType);
+ {
+ std::lock_guard<std::recursive_mutex> lock(room_data_mutex);
+ auto it = custom_emoji_by_key.find(key);
+ if(it == custom_emoji_by_key.end())
+ return false;
+
+ for(const auto &it : custom_emoji_by_key) {
+ if(it.first == key)
+ continue;
+
+ rapidjson::Document emoji_obj(rapidjson::kObjectType);
+ emoji_obj.AddMember("url", rapidjson::Value(it.second.url.c_str(), request_data.GetAllocator()).Move(), request_data.GetAllocator());
+ emoji_obj.AddMember("width", it.second.size.x, request_data.GetAllocator());
+ emoji_obj.AddMember("height", it.second.size.y, request_data.GetAllocator());
+ request_data.AddMember(rapidjson::Value(it.first.c_str(), request_data.GetAllocator()).Move(), std::move(emoji_obj), request_data.GetAllocator());
+ }
+ }
+
+ rapidjson::StringBuffer buffer;
+ rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
+ request_data.Accept(writer);
+
+ std::vector<CommandArg> additional_args = {
+ { "-X", "PUT" },
+ { "-H", "content-type: application/json" },
+ { "-H", "Authorization: Bearer " + access_token },
+ { "--data-binary", buffer.GetString() }
+ };
+
+ std::string server_response;
+ DownloadResult download_result = download_to_string(homeserver + "/_matrix/client/r0/user/" + my_user_id + "/account_data/qm.emoji", server_response, std::move(additional_args), true);
+ if(download_result != DownloadResult::OK)
+ return false;
+
+ std::lock_guard<std::recursive_mutex> lock(room_data_mutex);
+ auto it = custom_emoji_by_key.find(key);
+ if(it != custom_emoji_by_key.end())
+ custom_emoji_by_key.erase(it);
+
+ return true;
+ }
+
+ bool Matrix::rename_custom_emoji(const std::string &key, const std::string &new_key) {
+ rapidjson::Document request_data(rapidjson::kObjectType);
+ {
+ std::lock_guard<std::recursive_mutex> lock(room_data_mutex);
+ auto custom_emoji_list_copy = custom_emoji_by_key;
+ auto it = custom_emoji_list_copy.find(key);
+ if(it == custom_emoji_list_copy.end())
+ return false;
+
+ auto custom_emoji_copy = it->second;
+ custom_emoji_list_copy.erase(it);
+ custom_emoji_list_copy[new_key] = std::move(custom_emoji_copy);
+ for(const auto &it : custom_emoji_list_copy) {
+ rapidjson::Document emoji_obj(rapidjson::kObjectType);
+ emoji_obj.AddMember("url", rapidjson::Value(it.second.url.c_str(), request_data.GetAllocator()).Move(), request_data.GetAllocator());
+ emoji_obj.AddMember("width", it.second.size.x, request_data.GetAllocator());
+ emoji_obj.AddMember("height", it.second.size.y, request_data.GetAllocator());
+ request_data.AddMember(rapidjson::Value(it.first.c_str(), request_data.GetAllocator()).Move(), std::move(emoji_obj), request_data.GetAllocator());
+ }
+ }
+
+ rapidjson::StringBuffer buffer;
+ rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
+ request_data.Accept(writer);
+
+ std::vector<CommandArg> additional_args = {
+ { "-X", "PUT" },
+ { "-H", "content-type: application/json" },
+ { "-H", "Authorization: Bearer " + access_token },
+ { "--data-binary", buffer.GetString() }
+ };
+
+ std::string server_response;
+ DownloadResult download_result = download_to_string(homeserver + "/_matrix/client/r0/user/" + my_user_id + "/account_data/qm.emoji", server_response, std::move(additional_args), true);
+ if(download_result != DownloadResult::OK)
+ return false;
+
+ std::lock_guard<std::recursive_mutex> lock(room_data_mutex);
+ auto it = custom_emoji_by_key.find(key);
+ if(it != custom_emoji_by_key.end()) {
+ auto custom_emoji_copy = it->second;
+ custom_emoji_by_key.erase(it);
+ custom_emoji_by_key[new_key] = std::move(custom_emoji_copy);
+ }
+
+ return true;
+ }
+
+ bool Matrix::does_custom_emoji_with_name_exist(const std::string &name) {
+ assert(is_initial_sync_finished());
+ std::lock_guard<std::recursive_mutex> lock(room_data_mutex);
+ return custom_emoji_by_key.find(name) != custom_emoji_by_key.end();
+ }
+
+ std::unordered_map<std::string, CustomEmoji> Matrix::get_custom_emojis() {
+ assert(is_initial_sync_finished());
+ std::lock_guard<std::recursive_mutex> lock(room_data_mutex);
+ return custom_emoji_by_key;
}
PluginResult Matrix::post_file(RoomData *room, const std::string &filepath, std::string filename, std::string &event_id_response, std::string &err_msg, void *relates_to) {
@@ -4092,7 +4500,7 @@ namespace QuickMedia {
UploadInfo file_info;
UploadInfo thumbnail_info;
- PluginResult upload_file_result = upload_file(room, filepath, filename, file_info, thumbnail_info, err_msg);
+ PluginResult upload_file_result = upload_file(filepath, filename, file_info, thumbnail_info, err_msg);
if(upload_file_result != PluginResult::OK)
return upload_file_result;
@@ -4107,7 +4515,7 @@ namespace QuickMedia {
return post_message(room, filename, event_id_response, file_info_opt, thumbnail_info_opt);
}
- PluginResult Matrix::upload_file(RoomData *room, const std::string &filepath, std::string filename, UploadInfo &file_info, UploadInfo &thumbnail_info, std::string &err_msg, bool upload_thumbnail) {
+ PluginResult Matrix::upload_file(const std::string &filepath, std::string filename, UploadInfo &file_info, UploadInfo &thumbnail_info, std::string &err_msg, bool upload_thumbnail) {
FileAnalyzer file_analyzer;
if(!file_analyzer.load_file(filepath.c_str(), true)) {
err_msg = "Failed to load " + filepath;
@@ -4149,7 +4557,7 @@ namespace QuickMedia {
if(video_get_middle_frame(file_analyzer, tmp_filename, thumbnail_max_size.x, thumbnail_max_size.y)) {
UploadInfo upload_info_ignored; // Ignore because it wont be set anyways. Thumbnails dont have thumbnails.
- PluginResult upload_thumbnail_result = upload_file(room, tmp_filename, thumbnail_filename.data, thumbnail_info, upload_info_ignored, err_msg, false);
+ PluginResult upload_thumbnail_result = upload_file(tmp_filename, thumbnail_filename.data, thumbnail_info, upload_info_ignored, err_msg, false);
if(upload_thumbnail_result != PluginResult::OK) {
close(tmp_file);
remove(tmp_filename);
@@ -4177,7 +4585,7 @@ namespace QuickMedia {
thumbnail_filename = thumbnail_filename.filename_no_ext() + ".thumb" + thumbnail_filename.ext();
UploadInfo upload_info_ignored; // Ignore because it wont be set anyways. Thumbnails dont have thumbnails.
- PluginResult upload_thumbnail_result = upload_file(room, thumbnail_path, thumbnail_filename.data, thumbnail_info, upload_info_ignored, err_msg, false);
+ PluginResult upload_thumbnail_result = upload_file(thumbnail_path, thumbnail_filename.data, thumbnail_info, upload_info_ignored, err_msg, false);
if(upload_thumbnail_result != PluginResult::OK) {
close(tmp_file);
remove(tmp_filename);
@@ -4811,7 +5219,7 @@ namespace QuickMedia {
if(avatar_url_json.IsString()) {
std::string avatar_url = thumbnail_url_extract_media_id(avatar_url_json.GetString());
if(!avatar_url.empty())
- avatar_url = get_thumbnail_url(homeserver, avatar_url);
+ avatar_url = get_avatar_thumbnail_url(homeserver, avatar_url);
if(!avatar_url.empty())
room_body_item->thumbnail_url = std::move(avatar_url);
@@ -4883,7 +5291,7 @@ namespace QuickMedia {
if(avatar_url_json.IsString()) {
std::string avatar_url = thumbnail_url_extract_media_id(std::string(avatar_url_json.GetString(), avatar_url_json.GetStringLength()));
if(!avatar_url.empty())
- avatar_url = get_thumbnail_url(homeserver, avatar_url);
+ avatar_url = get_avatar_thumbnail_url(homeserver, avatar_url);
body_item->thumbnail_url = std::move(avatar_url);
}
body_item->thumbnail_mask_type = ThumbnailMaskType::CIRCLE;
@@ -5037,6 +5445,7 @@ namespace QuickMedia {
void Matrix::set_next_batch(std::string new_next_batch) {
std::lock_guard<std::mutex> lock(next_batch_mutex);
next_batch = std::move(new_next_batch);
+ initial_sync_finished = !next_batch.empty();
}
std::string Matrix::get_next_batch() {
@@ -5129,7 +5538,7 @@ namespace QuickMedia {
if(avatar_url_json.IsString())
avatar_url = std::string(avatar_url_json.GetString(), avatar_url_json.GetStringLength());
if(!avatar_url.empty())
- avatar_url = get_thumbnail_url(homeserver, thumbnail_url_extract_media_id(avatar_url)); // TODO: Remove the constant strings around to reduce memory usage (6.3mb)
+ avatar_url = get_avatar_thumbnail_url(homeserver, thumbnail_url_extract_media_id(avatar_url)); // TODO: Remove the constant strings around to reduce memory usage (6.3mb)
room->set_user_avatar_url(user, avatar_url, 0);
room->set_user_display_name(user, display_name, 0);