From 4ff87535e7aae35bfbd66fe88402dcb513af249c Mon Sep 17 00:00:00 2001 From: dec05eba Date: Thu, 19 Aug 2021 16:40:42 +0200 Subject: Matrix: add ctrl+r to navigate to replied to message and navigate to message from notifications tab --- src/plugins/Matrix.cpp | 194 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 155 insertions(+), 39 deletions(-) (limited to 'src/plugins') diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index 8c7c884..df4fa52 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -182,7 +182,7 @@ namespace QuickMedia { user->avatar_url = std::move(avatar_url); } - size_t RoomData::prepend_messages_reverse(const std::vector> &new_messages) { + size_t RoomData::prepend_messages_reverse(const Messages &new_messages) { std::lock_guard lock(room_mutex); int64_t last_new_message_timestamp = last_message_timestamp; size_t num_new_messages = 0; @@ -199,7 +199,7 @@ namespace QuickMedia { return num_new_messages; } - size_t RoomData::append_messages(const std::vector> &new_messages) { + size_t RoomData::append_messages(const Messages &new_messages) { std::lock_guard lock(room_mutex); int64_t last_new_message_timestamp = last_message_timestamp; size_t num_new_messages = 0; @@ -253,7 +253,7 @@ namespace QuickMedia { room_mutex.unlock(); } - const std::vector>& RoomData::get_messages_thread_unsafe() const { + const Messages& RoomData::get_messages_thread_unsafe() const { return messages; } @@ -268,9 +268,7 @@ namespace QuickMedia { void RoomData::set_prev_batch(const std::string &new_prev_batch) { std::lock_guard lock(room_mutex); - // TODO: Check if this always works and if it also works for other homeservers than synapse - if(prev_batch.empty() || new_prev_batch < prev_batch) - prev_batch = new_prev_batch; + prev_batch = new_prev_batch; } std::string RoomData::get_prev_batch() { @@ -884,7 +882,9 @@ namespace QuickMedia { title = "Invites (0)"; } - MatrixChatPage::MatrixChatPage(Program *program, std::string room_id, MatrixRoomsPage *rooms_page) : Page(program), room_id(std::move(room_id)), rooms_page(rooms_page) { + 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)) + { assert(rooms_page); rooms_page->set_current_chat_page(this); rooms_page->matrix_delegate->chat_page = this; @@ -1059,7 +1059,7 @@ namespace QuickMedia { return PluginResult::OK; NotificationsExtraData *extra_data = static_cast(selected_item->extra.get()); - result_tabs.push_back(Tab{nullptr, std::make_unique(program, extra_data->room->id, all_rooms_page), nullptr}); + result_tabs.push_back(Tab{nullptr, std::make_unique(program, extra_data->room->id, all_rooms_page, selected_item->url), nullptr}); return PluginResult::OK; } @@ -1450,9 +1450,59 @@ namespace QuickMedia { room->release_room_lock(); } - PluginResult Matrix::get_previous_room_messages(RoomData *room, Messages &messages, bool latest_messages) { + PluginResult Matrix::get_messages_in_direction(RoomData *room, const std::string &token, MessageDirection message_dir, Messages &messages, std::string &new_token) { + // TODO: Retry on failure (after a timeout) instead of setting new token to an empty string + new_token.clear(); + + rapidjson::Document request_data(rapidjson::kObjectType); + request_data.AddMember("lazy_load_members", true, request_data.GetAllocator()); + + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + request_data.Accept(writer); + + std::vector 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/messages?from=%s&limit=20&dir=%s&filter=%s", homeserver.c_str(), room->id.c_str(), token.c_str(), message_dir == MessageDirection::BEFORE ? "b" : "f", 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 download_result_to_plugin_result(download_result); + + if(!json_root.IsObject()) + return PluginResult::ERR; + + const rapidjson::Value &state_json = GetMember(json_root, "state"); + events_add_user_info(state_json, room); + //events_set_room_info(state_json, room_data); + + const rapidjson::Value &chunk_json = GetMember(json_root, "chunk"); + if(chunk_json.IsArray()) { + for(const rapidjson::Value &event_item_json : chunk_json.GetArray()) { + std::shared_ptr new_message = parse_message_event(event_item_json, room); + if(new_message) + messages.push_back(std::move(new_message)); + } + } + + const rapidjson::Value &end_json = GetMember(json_root, "end"); + if(end_json.IsString()) + new_token.assign(end_json.GetString(), end_json.GetStringLength()); + + if(new_token == token) + new_token.clear(); + + return PluginResult::OK; + } + + PluginResult Matrix::get_previous_room_messages(RoomData *room, Messages &messages, bool latest_messages, bool *reached_end) { size_t num_new_messages = 0; - PluginResult result = get_previous_room_messages(room, latest_messages, num_new_messages); + PluginResult result = get_previous_room_messages(room, latest_messages, num_new_messages, reached_end); if(result != PluginResult::OK) return result; @@ -1993,7 +2043,7 @@ namespace QuickMedia { return 0; // TODO: Preallocate - std::vector> new_messages; + Messages new_messages; auto me = get_me(room_data); std::string my_display_name = room_data->get_user_display_name(me); @@ -2839,7 +2889,7 @@ namespace QuickMedia { } } - PluginResult Matrix::get_previous_room_messages(RoomData *room_data, bool latest_messages, size_t &num_new_messages) { + PluginResult Matrix::get_previous_room_messages(RoomData *room_data, bool latest_messages, size_t &num_new_messages, bool *reached_end) { num_new_messages = 0; std::string from = room_data->get_prev_batch(); if(from.empty() || latest_messages) @@ -2869,6 +2919,7 @@ namespace QuickMedia { return PluginResult::ERR; const rapidjson::Value &state_json = GetMember(json_root, "state"); + // TODO: Remove? events_add_user_info(state_json, room_data); //events_set_room_info(state_json, room_data); @@ -2879,9 +2930,14 @@ namespace QuickMedia { if(!end_json.IsString()) { room_data->set_prev_batch("invalid"); fprintf(stderr, "Warning: matrix messages response is missing 'end', this could happen if we received the very first messages in the room\n"); + if(reached_end) + *reached_end = true; return PluginResult::OK; } + if(reached_end) + *reached_end = strcmp(end_json.GetString(), from.c_str()) == 0; + room_data->set_prev_batch(end_json.GetString()); return PluginResult::OK; } @@ -3424,30 +3480,14 @@ namespace QuickMedia { 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; -#if 0 - rapidjson::Document request_data(rapidjson::kObjectType); - request_data.AddMember("lazy_load_members", true, request_data.GetAllocator()); - - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - request_data.Accept(writer); std::vector 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->id.c_str(), event_id.c_str(), filter.c_str()); -#else - std::vector additional_args = { - { "-H", "Authorization: Bearer " + access_token } - }; - char url[512]; snprintf(url, sizeof(url), "%s/_matrix/client/r0/rooms/%s/event/%s", homeserver.c_str(), room->id.c_str(), event_id.c_str()); -#endif + std::string response; DownloadResult download_result = download_to_string_cache(url, response, std::move(additional_args), true, [](std::string &response) { rapidjson::Document json_root; @@ -3491,21 +3531,97 @@ namespace QuickMedia { room->fetched_messages_by_event_id.insert(std::make_pair(event_id, nullptr)); return nullptr; } -#if 0 - const rapidjson::Value &state_json = GetMember(json_root, "state"); - events_add_user_info(state_json, room); -#endif - //events_set_room_info(state_json, room); -#if 0 - const rapidjson::Value &event_json = GetMember(json_root, "event"); - std::shared_ptr new_message = parse_message_event(event_json, room); -#else + + // TODO: Do this? what about state apply order? + //const rapidjson::Value &state_json = GetMember(json_root, "state"); + //events_add_user_info(state_json, room); + std::shared_ptr new_message = parse_message_event(json_root, room); -#endif room->fetched_messages_by_event_id.insert(std::make_pair(event_id, new_message)); return new_message; } + PluginResult Matrix::get_message_context(RoomData *room, const std::string &event_id, std::shared_ptr &message, Messages &before_messages, Messages &after_messages, std::string &before_token, std::string &after_token) { + rapidjson::Document request_data(rapidjson::kObjectType); + request_data.AddMember("lazy_load_members", true, request_data.GetAllocator()); + + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + request_data.Accept(writer); + + std::vector 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=20&filter=%s", homeserver.c_str(), room->id.c_str(), event_id.c_str(), filter.c_str()); + + rapidjson::Document json_root; + std::string err_msg; + DownloadResult download_result = download_json(json_root, url, std::move(additional_args), true, &err_msg); + if(download_result != DownloadResult::OK) { + show_notification("QuickMedia", "Failed to get message", Urgency::CRITICAL); + return download_result_to_plugin_result(download_result); + } + + if(!json_root.IsObject()) { + show_notification("QuickMedia", "Failed to parse server response", Urgency::CRITICAL); + return PluginResult::ERR; + } + + const rapidjson::Value &errcode_json = GetMember(json_root, "errcode"); + if(errcode_json.IsString() && strcmp(errcode_json.GetString(), "M_NOT_FOUND") != 0) { + const rapidjson::Value &error_json = GetMember(json_root, "error"); + if(error_json.IsString()) { + show_notification("QuickMedia", "Failed to get message, error: " + std::string(error_json.GetString(), error_json.GetStringLength()), Urgency::CRITICAL); + return PluginResult::ERR; + } + } + + // TODO: Do this? what about state apply order? + //const rapidjson::Value &state_json = GetMember(json_root, "state"); + //events_add_user_info(state_json, room); + + const rapidjson::Value &start_json = GetMember(json_root, "start"); + const rapidjson::Value &end_json = GetMember(json_root, "end"); + + const rapidjson::Value &event_json = GetMember(json_root, "event"); + const rapidjson::Value &events_before_json = GetMember(json_root, "events_before"); + const rapidjson::Value &events_after_json = GetMember(json_root, "events_after"); + + if(start_json.IsString()) + before_token.assign(start_json.GetString(), start_json.GetStringLength()); + + if(end_json.IsString()) + after_token.assign(end_json.GetString(), end_json.GetStringLength()); + + message = parse_message_event(event_json, room); + + if(events_before_json.IsArray()) { + for(const rapidjson::Value &event_item_json : events_before_json.GetArray()) { + std::shared_ptr new_message = parse_message_event(event_item_json, room); + if(new_message) + before_messages.push_back(std::move(new_message)); + } + } + + if(events_after_json.IsArray()) { + for(const rapidjson::Value &event_item_json : events_after_json.GetArray()) { + std::shared_ptr new_message = parse_message_event(event_item_json, room); + if(new_message) + after_messages.push_back(std::move(new_message)); + } + } + + return PluginResult::OK; + } + + void Matrix::clear_previous_messages_token(RoomData *room) { + room->set_prev_batch(""); + } + static const char* file_get_filename(const std::string &filepath) { size_t index = filepath.rfind('/'); if(index == std::string::npos) -- cgit v1.2.3