From eac2ace1c14c1ae0564d757b26a359c6bd4b754a Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 25 Sep 2020 04:15:17 +0200 Subject: Matrix: fetch previous messages when reaching the top --- src/plugins/Matrix.cpp | 117 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 85 insertions(+), 32 deletions(-) (limited to 'src/plugins') diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index 2d4fd4a..529d42a 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -159,9 +159,37 @@ namespace QuickMedia { return PluginResult::OK; } - PluginResult Matrix::get_room_messages(const std::string &room_id, size_t start_index, BodyItems &result_items, size_t &num_new_messages) { - num_new_messages = 0; + static void room_messages_to_body_items(RoomData *room_data, Message *messages, size_t num_messages, BodyItems &result_items) { + // TODO: This prev_user_id should check previous message, otherwise if after a sync there is only 1 new message then it wont + // merge with the previous message. But for that to work then also have to send existing body items to this function, + // to get the body item to merge with + size_t prev_user_id = -1; + for(size_t i = 0; i < num_messages; ++i) { + const UserInfo &user_info = room_data->user_info[messages[i].user_id]; + if(messages[i].user_id == prev_user_id && messages[i].url.empty()) { + assert(!result_items.empty()); + result_items.back()->append_description("\n"); + result_items.back()->append_description(messages[i].body); + } else { + auto body_item = std::make_unique(""); + body_item->set_author(user_info.display_name); + body_item->set_description(messages[i].body); + if(!messages[i].thumbnail_url.empty()) + body_item->thumbnail_url = messages[i].thumbnail_url; + else if(!messages[i].url.empty()) + body_item->thumbnail_url = messages[i].url; + else + body_item->thumbnail_url = user_info.avatar_url; + // TODO: Show image thumbnail inline instead of url to image + body_item->url = messages[i].url; + result_items.push_back(std::move(body_item)); + prev_user_id = messages[i].user_id; + } + } + } + // TODO: Merge common code with |get_new_room_messages| + PluginResult Matrix::get_all_synced_room_messages(const std::string &room_id, BodyItems &result_items) { auto room_it = room_data_by_id.find(room_id); if(room_it == room_data_by_id.end()) { fprintf(stderr, "Error: no such room: %s\n", room_id.c_str()); @@ -169,7 +197,7 @@ namespace QuickMedia { } if(!room_it->second->initial_fetch_finished) { - PluginResult result = load_initial_room_data(room_id, room_it->second.get()); + PluginResult result = get_previous_room_messages(room_id, room_it->second.get()); if(result == PluginResult::OK) { room_it->second->initial_fetch_finished = true; } else { @@ -178,36 +206,49 @@ namespace QuickMedia { } } - // This will happen if there are no new messages - if(start_index >= room_it->second->messages.size()) - return PluginResult::OK; + room_messages_to_body_items(room_it->second.get(), room_it->second->messages.data(), room_it->second->messages.size(), result_items); + room_it->second->last_read_index = room_it->second->messages.size(); + return PluginResult::OK; + } - num_new_messages = room_it->second->messages.size() - start_index; + PluginResult Matrix::get_new_room_messages(const std::string &room_id, BodyItems &result_items) { + auto room_it = room_data_by_id.find(room_id); + if(room_it == room_data_by_id.end()) { + fprintf(stderr, "Error: no such room: %s\n", room_id.c_str()); + return PluginResult::ERR; + } - size_t prev_user_id = -1; - for(auto it = room_it->second->messages.begin() + start_index, end = room_it->second->messages.end(); it != end; ++it) { - const UserInfo &user_info = room_it->second->user_info[it->user_id]; - if(it->user_id == prev_user_id && it->url.empty()) { - assert(!result_items.empty()); - result_items.back()->append_description("\n"); - result_items.back()->append_description(it->body); + if(!room_it->second->initial_fetch_finished) { + PluginResult result = get_previous_room_messages(room_id, room_it->second.get()); + if(result == PluginResult::OK) { + room_it->second->initial_fetch_finished = true; } else { - auto body_item = std::make_unique(""); - body_item->set_author(user_info.display_name); - body_item->set_description(it->body); - if(!it->thumbnail_url.empty()) - body_item->thumbnail_url = it->thumbnail_url; - else if(!it->url.empty()) - body_item->thumbnail_url = it->url; - else - body_item->thumbnail_url = user_info.avatar_url; - // TODO: Show image thumbnail inline instead of url to image - body_item->url = it->url; - result_items.push_back(std::move(body_item)); - prev_user_id = it->user_id; + fprintf(stderr, "Initial sync failed for room: %s\n", room_id.c_str()); + return result; } } + size_t num_new_messages = room_it->second->messages.size() - room_it->second->last_read_index; + room_messages_to_body_items(room_it->second.get(), room_it->second->messages.data() + room_it->second->last_read_index, num_new_messages, result_items); + room_it->second->last_read_index = room_it->second->messages.size(); + return PluginResult::OK; + } + + PluginResult Matrix::get_previous_room_messages(const std::string &room_id, BodyItems &result_items) { + auto room_it = room_data_by_id.find(room_id); + if(room_it == room_data_by_id.end()) { + fprintf(stderr, "Error: no such room: %s\n", room_id.c_str()); + return PluginResult::ERR; + } + + size_t num_messages_before = room_it->second->messages.size(); + PluginResult result = get_previous_room_messages(room_id, room_it->second.get()); + if(result != PluginResult::OK) + return result; + + size_t num_messages_after = room_it->second->messages.size(); + size_t num_new_messages = num_messages_after - num_messages_before; + room_messages_to_body_items(room_it->second.get(), room_it->second->messages.data(), num_new_messages, result_items); return PluginResult::OK; } @@ -270,10 +311,12 @@ namespace QuickMedia { if(!timeline_json.isObject()) continue; - // This may be non-existent if this is the first event in the room - const Json::Value &prev_batch_json = timeline_json["prev_batch"]; - if(prev_batch_json.isString()) - room_it->second->prev_batch = prev_batch_json.asString(); + if(room_it->second->prev_batch.empty()) { + // This may be non-existent if this is the first event in the room + const Json::Value &prev_batch_json = timeline_json["prev_batch"]; + if(prev_batch_json.isString()) + room_it->second->prev_batch = prev_batch_json.asString(); + } const Json::Value &events_json = timeline_json["events"]; events_add_user_info(events_json, room_it->second.get()); @@ -415,8 +458,11 @@ namespace QuickMedia { } } + // TODO: Loop and std::move instead? doesn't insert create copies? if(message_dir == MessageDirection::BEFORE) { room_data->messages.insert(room_data->messages.begin(), new_messages.rbegin(), new_messages.rend()); + if(room_data->last_read_index != 0) + room_data->last_read_index += new_messages.size(); } else if(message_dir == MessageDirection::AFTER) { room_data->messages.insert(room_data->messages.end(), new_messages.begin(), new_messages.end()); } @@ -500,7 +546,7 @@ namespace QuickMedia { } } - PluginResult Matrix::load_initial_room_data(const std::string &room_id, RoomData *room_data) { + PluginResult Matrix::get_previous_room_messages(const std::string &room_id, RoomData *room_data) { std::string from = room_data->prev_batch; if(from.empty()) { fprintf(stderr, "Info: missing previous batch for room: %s, using /sync next batch\n", room_id.c_str()); @@ -554,6 +600,13 @@ namespace QuickMedia { const Json::Value &chunk_json = json_root["chunk"]; events_add_messages(chunk_json, room_data, MessageDirection::BEFORE); + const Json::Value &end_json = json_root["end"]; + if(!end_json.isString()) { + fprintf(stderr, "Warning: matrix messages response is missing 'end', this could happen if we received the very first messages in the room\n"); + return PluginResult::OK; + } + + room_data->prev_batch = end_json.asString(); return PluginResult::OK; } -- cgit v1.2.3