aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2021-08-01 03:09:19 +0200
committerdec05eba <dec05eba@protonmail.com>2021-08-01 03:09:19 +0200
commit738411fcb52f2dce050f200eeff307c7a0a1f13c (patch)
treee0162daa3a86f43769e502a86033a388bcefba6b /src
parentaead89cca00cba7bef8752163752f6a9c213ae6f (diff)
Matrix: implement power level change messages
Diffstat (limited to 'src')
-rw-r--r--src/Body.cpp8
-rw-r--r--src/QuickMedia.cpp11
-rw-r--r--src/plugins/Matrix.cpp147
3 files changed, 127 insertions, 39 deletions
diff --git a/src/Body.cpp b/src/Body.cpp
index 693086e..0804399 100644
--- a/src/Body.cpp
+++ b/src/Body.cpp
@@ -806,7 +806,7 @@ namespace QuickMedia {
}
void Body::update_dirty_state(BodyItem *body_item, float width) {
- if(body_item->dirty || (body_size_changed && body_item->title_text)) {
+ if((body_item->dirty && !body_item->get_title().empty()) || (body_size_changed && body_item->title_text)) {
body_item->dirty = false;
// TODO: Find a way to optimize fromUtf8
sf::String str = sf::String::fromUtf8(body_item->get_title().begin(), body_item->get_title().end());
@@ -820,7 +820,7 @@ namespace QuickMedia {
body_item->title_text->updateGeometry();
}
- if(body_item->dirty_description || (body_size_changed && body_item->description_text)) {
+ if((body_item->dirty_description && !body_item->get_description().empty()) || (body_size_changed && body_item->description_text)) {
body_item->dirty_description = false;
sf::String str = sf::String::fromUtf8(body_item->get_description().begin(), body_item->get_description().end());
if(body_item->description_text) {
@@ -833,7 +833,7 @@ namespace QuickMedia {
body_item->description_text->updateGeometry();
}
- if(body_item->dirty_author || (body_size_changed && body_item->author_text)) {
+ if((body_item->dirty_author && !body_item->get_author().empty()) || (body_size_changed && body_item->author_text)) {
body_item->dirty_author = false;
sf::String str = sf::String::fromUtf8(body_item->get_author().begin(), body_item->get_author().end());
if(body_item->author_text) {
@@ -846,7 +846,7 @@ namespace QuickMedia {
body_item->author_text->updateGeometry();
}
- if(body_item->dirty_timestamp || (body_size_changed && body_item->timestamp_text)) {
+ if((body_item->dirty_timestamp && body_item->get_timestamp() != 0) || (body_size_changed && body_item->timestamp_text)) {
body_item->dirty_timestamp = false;
if(body_item->get_timestamp() != 0) {
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 4a3003b..286e766 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -4526,6 +4526,12 @@ namespace QuickMedia {
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 || message->related_event_type == RelatedEventType::REACTION)
body_item->visible = false;
+ if(is_system_message_type(message->type)) {
+ body_item->set_author("System");
+ body_item->set_author_color(get_current_theme().text_color);
+ body_item->set_description_color(get_current_theme().faded_text_color);
+ body_item->thumbnail_url.clear();
+ }
if(message->user->user_id != my_user_id && (message_contains_user_mention(body_item->get_description(), my_display_name) || message_contains_user_mention(body_item->get_description(), my_user_id)))
body_item->set_description_color(get_current_theme().attention_alert_text_color);
return body_item;
@@ -4577,6 +4583,9 @@ namespace QuickMedia {
};
static void user_update_display_info(BodyItem *body_item, RoomData *room, Message *message) {
+ if(is_system_message_type(message->type))
+ return;
+
body_item->set_author(extract_first_line_remove_newline_elipses(room->get_user_display_name(message->user), AUTHOR_MAX_LENGTH));
if(!is_visual_media_message_type(message->type)) {
@@ -5413,7 +5422,7 @@ namespace QuickMedia {
tabs[MESSAGES_TAB_INDEX].body->body_item_merge_handler = [](BodyItem *prev_item, BodyItem *this_item) {
Message *message = static_cast<Message*>(this_item->userdata);
- if(!message || !prev_item || !prev_item->userdata)
+ if(!message || !prev_item || !prev_item->userdata || is_system_message_type(message->type))
return false;
if(is_visual_media_message_type(message->type) && !this_item->thumbnail_url.empty())
diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp
index ca211e1..2e758c5 100644
--- a/src/plugins/Matrix.cpp
+++ b/src/plugins/Matrix.cpp
@@ -25,43 +25,42 @@
// TODO: Verify if buffer of size 512 is enough for endpoints
// Remove older messages (outside screen) to save memory. Reload them when the selected body item is the top/bottom one.
-static const char* SERVICE_NAME = "matrix";
-static const char* OTHERS_ROOM_TAG = "tld.name.others";
-// Filter without account data
-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 std::string capitalize(const std::string &str) {
- if(str.size() >= 1)
- return QuickMedia::to_upper(str[0]) + str.substr(1);
- else
- return "";
-}
-
-// TODO: According to spec: "Any tag in the tld.name.* form but not matching the namespace of the current client should be ignored",
-// should we follow this?
-static std::string tag_get_name(const std::string &tag) {
- 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, "lowpriority") == 0)
- return "Low priority";
- else if(strcmp(tag.c_str() + 2, "server_notice") == 0)
- return "Server notice";
+namespace QuickMedia {
+ static const sf::Vector2i thumbnail_max_size(600, 337);
+ static const char* SERVICE_NAME = "matrix";
+ static const char* OTHERS_ROOM_TAG = "tld.name.others";
+ // Filter without account data
+ 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.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.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)
+ return QuickMedia::to_upper(str[0]) + str.substr(1);
else
- return capitalize(tag.substr(2));
- } else if(tag.size() >= 2 && memcmp(tag.data(), "u.", 2) == 0) {
- return capitalize(tag.substr(2));
- } else if(tag.size() >= 9 && memcmp(tag.data(), "tld.name.", 9) == 0) {
- return capitalize(tag.substr(9));
- } else {
- return "";
+ return "";
}
-}
-namespace QuickMedia {
- static const sf::Vector2i thumbnail_max_size(600, 337);
+ // TODO: According to spec: "Any tag in the tld.name.* form but not matching the namespace of the current client should be ignored",
+ // should we follow this?
+ static std::string tag_get_name(const std::string &tag) {
+ 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, "lowpriority") == 0)
+ return "Low priority";
+ else if(strcmp(tag.c_str() + 2, "server_notice") == 0)
+ return "Server notice";
+ else
+ return capitalize(tag.substr(2));
+ } else if(tag.size() >= 2 && memcmp(tag.data(), "u.", 2) == 0) {
+ return capitalize(tag.substr(2));
+ } else if(tag.size() >= 9 && memcmp(tag.data(), "tld.name.", 9) == 0) {
+ return capitalize(tag.substr(9));
+ } else {
+ return "";
+ }
+ }
std::string extract_first_line_remove_newline_elipses(const std::string &str, size_t max_length) {
std::string result = str;
@@ -132,6 +131,10 @@ namespace QuickMedia {
return message_type == MessageType::VIDEO || message_type == MessageType::IMAGE;
}
+ bool is_system_message_type(MessageType message_type) {
+ return message_type >= MessageType::MEMBERSHIP && message_type <= MessageType::SYSTEM;
+ }
+
std::shared_ptr<UserInfo> RoomData::get_user_by_id(const std::string &user_id) {
std::lock_guard<std::recursive_mutex> lock(room_mutex);
auto user_it = user_info_by_user_id.find(user_id);
@@ -2038,6 +2041,41 @@ namespace QuickMedia {
return num_new_messages;
}
+ // TODO: Custom names for power levels
+ static std::string power_level_to_name(int power_level) {
+ switch(power_level) {
+ case 0:
+ return "Default";
+ case 50:
+ return "Moderator";
+ case 100:
+ return "Administrator";
+ default:
+ return "Custom (" + std::to_string(power_level) + ")";
+ }
+ }
+
+ struct UserPowerLevelChange {
+ int new_power_level = 0;
+ int old_power_level = 0;
+ };
+
+ // TODO: Use user display names instead of id. Also update display names after retrieving them
+ static std::string power_levels_change_to_string(RoomData *room, const std::shared_ptr<UserInfo> &changed_by, const std::map<std::string, UserPowerLevelChange> &power_levels_change) {
+ const std::string changed_by_name = room->get_user_display_name(changed_by);
+ std::string result;
+ for(const auto &change : power_levels_change) {
+ if(change.second.new_power_level == change.second.old_power_level)
+ continue;
+
+ if(!result.empty())
+ result += '\n';
+
+ result += changed_by_name + " changed the power level of " + change.first + " from " + power_level_to_name(change.second.old_power_level) + " to " + power_level_to_name(change.second.new_power_level);
+ }
+ return result;
+ }
+
std::shared_ptr<Message> Matrix::parse_message_event(const rapidjson::Value &event_item_json, RoomData *room_data) {
if(!event_item_json.IsObject())
return nullptr;
@@ -2301,6 +2339,47 @@ namespace QuickMedia {
message->timestamp = timestamp;
message->transaction_id = std::move(transaction_id);
return message;
+ } else if(strcmp(type_json.GetString(), "m.room.power_levels") == 0) {
+ const rapidjson::Value &users_json = GetMember(*content_json, "users");
+ if(!users_json.IsObject())
+ return nullptr;
+
+ std::map<std::string, UserPowerLevelChange> power_level_changes;
+ for(auto const &user_json : users_json.GetObject()) {
+ if(!user_json.name.IsString() || !user_json.value.IsInt())
+ continue;
+ power_level_changes[std::string(user_json.name.GetString(), user_json.name.GetStringLength())].new_power_level = user_json.value.GetInt();
+ }
+
+ if(unsigned_json.IsObject()) {
+ // TODO: What about top level prev_content?
+ const rapidjson::Value &unsigned_prev_content_json = GetMember(unsigned_json, "prev_content");
+ if(unsigned_prev_content_json.IsObject()) {
+ const rapidjson::Value &prev_content_users_json = GetMember(unsigned_prev_content_json, "users");
+ if(prev_content_users_json.IsObject()) {
+ for(auto const &user_json : prev_content_users_json.GetObject()) {
+ if(!user_json.name.IsString() || !user_json.value.IsInt())
+ continue;
+ power_level_changes[std::string(user_json.name.GetString(), user_json.name.GetStringLength())].old_power_level = user_json.value.GetInt();
+ }
+ }
+ }
+ }
+
+ auto message = std::make_shared<Message>();
+ message->type = MessageType::SYSTEM;
+ message->user = user;
+ message->event_id = event_id_str;
+ message->body = power_levels_change_to_string(room_data, user_sender, power_level_changes);
+ message->related_event_id = std::move(related_event_id);
+ message->related_event_type = related_event_type;
+ message->timestamp = timestamp;
+ message->transaction_id = std::move(transaction_id);
+
+ if(message->body.empty())
+ return nullptr;
+
+ return message;
} else {
auto message = std::make_shared<Message>();
message->type = MessageType::UNIMPLEMENTED;