aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugins/Matrix.hpp4
-rw-r--r--src/QuickMedia.cpp78
-rw-r--r--src/plugins/Matrix.cpp122
3 files changed, 162 insertions, 42 deletions
diff --git a/plugins/Matrix.hpp b/plugins/Matrix.hpp
index e7131b0..bf9f715 100644
--- a/plugins/Matrix.hpp
+++ b/plugins/Matrix.hpp
@@ -29,7 +29,9 @@ namespace QuickMedia {
VIDEO,
AUDIO,
FILE,
- REDACTION
+ REDACTION,
+ UNIMPLEMENTED,
+ MEMBERSHIP
};
enum class RelatedEventType {
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index f009cdd..1caf831 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -3073,6 +3073,28 @@ namespace QuickMedia {
return result_items;
}
+ static bool is_state_message_type(const Message *message) {
+ if(!message)
+ return true;
+
+ switch(message->type) {
+ case MessageType::TEXT:
+ return false;
+ case MessageType::IMAGE:
+ return false;
+ case MessageType::VIDEO:
+ return false;
+ case MessageType::AUDIO:
+ return false;
+ case MessageType::FILE:
+ return false;
+ default:
+ return true;
+ }
+
+ return true;
+ }
+
struct PinnedEventData {
std::string event_id;
FetchStatus status = FetchStatus::NONE;
@@ -3730,10 +3752,12 @@ namespace QuickMedia {
frame_skip_text_entry = true;
std::shared_ptr<BodyItem> selected = tabs[selected_tab].body->get_selected_shared();
if(selected) {
- chat_state = ChatState::REPLYING;
- currently_operating_on_item = selected;
- chat_input.set_editable(true);
- replying_to_text.setString("Replying to:");
+ if(!is_state_message_type(static_cast<Message*>(selected->userdata))) {
+ chat_state = ChatState::REPLYING;
+ currently_operating_on_item = selected;
+ chat_input.set_editable(true);
+ replying_to_text.setString("Replying to:");
+ }
} else {
// TODO: Show inline notification
show_notification("QuickMedia", "No message selected for replying");
@@ -3744,19 +3768,21 @@ namespace QuickMedia {
frame_skip_text_entry = true;
std::shared_ptr<BodyItem> selected = tabs[selected_tab].body->get_selected_shared();
if(selected) {
- if(!selected->url.empty()) { // cant edit messages that are image/video posts
- // TODO: Show inline notification
- show_notification("QuickMedia", "You can only edit messages with no file attached to it");
- } else if(!matrix->was_message_posted_by_me(selected->userdata)) {
- // TODO: Show inline notification
- show_notification("QuickMedia", "You can't edit a message that was posted by somebody else");
- } else {
- chat_state = ChatState::EDITING;
- currently_operating_on_item = selected;
- chat_input.set_editable(true);
- chat_input.set_text(selected->get_description()); // TODO: Description? it may change in the future, in which case this should be edited
- chat_input.move_caret_to_end();
- replying_to_text.setString("Editing message:");
+ if(!is_state_message_type(static_cast<Message*>(selected->userdata))) {
+ if(!selected->url.empty()) { // cant edit messages that are image/video posts
+ // TODO: Show inline notification
+ show_notification("QuickMedia", "You can only edit messages with no file attached to it");
+ } else if(!matrix->was_message_posted_by_me(selected->userdata)) {
+ // TODO: Show inline notification
+ show_notification("QuickMedia", "You can't edit a message that was posted by somebody else");
+ } else {
+ chat_state = ChatState::EDITING;
+ currently_operating_on_item = selected;
+ chat_input.set_editable(true);
+ chat_input.set_text(selected->get_description()); // TODO: Description? it may change in the future, in which case this should be edited
+ chat_input.move_caret_to_end();
+ replying_to_text.setString("Editing message:");
+ }
}
} else {
// TODO: Show inline notification
@@ -3768,14 +3794,16 @@ namespace QuickMedia {
frame_skip_text_entry = true;
BodyItem *selected = tabs[selected_tab].body->get_selected();
if(selected) {
- void *selected_message = selected->userdata;
- post_task_queue.push([this, &current_room, selected_message]() {
- std::string err_msg;
- if(matrix->delete_message(current_room, selected_message, err_msg) != PluginResult::OK) {
- // TODO: Show inline notification
- fprintf(stderr, "Failed to delete message, reason: %s\n", err_msg.c_str());
- }
- });
+ if(!is_state_message_type(static_cast<Message*>(selected->userdata))) {
+ void *selected_message = selected->userdata;
+ post_task_queue.push([this, &current_room, selected_message]() {
+ std::string err_msg;
+ if(matrix->delete_message(current_room, selected_message, err_msg) != PluginResult::OK) {
+ // TODO: Show inline notification
+ fprintf(stderr, "Failed to delete message, reason: %s\n", err_msg.c_str());
+ }
+ });
+ }
} else {
// TODO: Show inline notification
show_notification("QuickMedia", "No message selected for deletion");
diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp
index 198b2fd..11d0746 100644
--- a/src/plugins/Matrix.cpp
+++ b/src/plugins/Matrix.cpp
@@ -995,6 +995,7 @@ namespace QuickMedia {
sync_data.messages.insert(sync_data.messages.end(), room_messages.begin() + room->messages_read_index, room_messages.end());
room->messages_read_index = room_messages.size();
} else {
+ // TODO: BUG
fprintf(stderr, "Unexpected behavior!!!! get_room_sync_data said read index is %zu but we only have %zu messages\n", room->messages_read_index, room_messages.size());
room->messages_read_index = room_messages.size();
}
@@ -1032,7 +1033,10 @@ namespace QuickMedia {
size_t num_new_messages = num_messages_after - num_messages_before;
messages.insert(messages.end(), room->get_messages_thread_unsafe().begin(), room->get_messages_thread_unsafe().begin() + num_new_messages);
room->messages_read_index += num_new_messages;
- assert(room->messages_read_index <= room->get_messages_thread_unsafe().size());
+ //assert(room->messages_read_index <= num_messages_after);
+ // TODO: BUG
+ if(room->messages_read_index >= num_messages_after)
+ room->messages_read_index = num_messages_after;
room->release_room_lock();
return PluginResult::OK;
}
@@ -1293,15 +1297,21 @@ namespace QuickMedia {
if(!content_json.IsObject())
continue;
- const rapidjson::Value &membership_json = GetMember(content_json, "membership");
- if(!membership_json.IsString() || strcmp(membership_json.GetString(), "join") != 0)
- continue;
-
std::string sender_json_str = sender_json.GetString();
parse_user_info(content_json, sender_json_str, room_data);
}
}
+ static std::string thumbnail_url_extract_media_id(const std::string &media_url) {
+ size_t start = 0;
+ size_t end = media_url.size();
+ if(strncmp(media_url.c_str(), "mxc://", 6) == 0)
+ start = 6;
+ if(media_url.size() >= start + 5 && strncmp(media_url.c_str() + media_url.size() - 5, "#auto", 5) == 0)
+ end = media_url.size() - 5;
+ return media_url.substr(start, end - start);
+ }
+
std::shared_ptr<UserInfo> Matrix::parse_user_info(const rapidjson::Value &json, const std::string &user_id, RoomData *room_data) {
assert(json.IsObject());
std::string avatar_url_str;
@@ -1313,9 +1323,7 @@ namespace QuickMedia {
auto user_info = std::make_shared<UserInfo>();
user_info->user_id = user_id;
- user_info->avatar_url = std::move(avatar_url_str);
- if(strncmp(user_info->avatar_url.c_str(), "mxc://", 6) == 0)
- user_info->avatar_url.erase(user_info->avatar_url.begin(), user_info->avatar_url.begin() + 6);
+ user_info->avatar_url = thumbnail_url_extract_media_id(avatar_url_str);
if(!user_info->avatar_url.empty())
user_info->avatar_url = homeserver + "/_matrix/media/r0/thumbnail/" + user_info->avatar_url + "?width=32&height=32&method=crop"; // TODO: Remove the constant strings around to reduce memory usage (6.3mb)
user_info->display_name = display_name_json.IsString() ? display_name_json.GetString() : user_id;
@@ -1554,11 +1562,20 @@ namespace QuickMedia {
if(!event_item_json.IsObject())
return nullptr;
- const rapidjson::Value &sender_json = GetMember(event_item_json, "sender");
- if(!sender_json.IsString())
+ const rapidjson::Value *sender_json = &GetMember(event_item_json, "sender");
+ const rapidjson::Value *sender_json_orig = sender_json;
+ if(!sender_json->IsString())
return nullptr;
- std::string sender_json_str = sender_json.GetString();
+ bool sent_by_somebody_else = false;
+ const rapidjson::Value *state_key_json = &GetMember(event_item_json, "state_key");
+ if(state_key_json->IsString() && state_key_json->GetStringLength() != 0) {
+ if(strcmp(sender_json->GetString(), state_key_json->GetString()) != 0)
+ sent_by_somebody_else = true;
+ sender_json = state_key_json;
+ }
+
+ std::string sender_json_str = sender_json->GetString();
const rapidjson::Value &event_id_json = GetMember(event_item_json, "event_id");
if(!event_id_json.IsString())
@@ -1577,6 +1594,10 @@ namespace QuickMedia {
return nullptr;
}
+ auto user_sender = user;
+ if(sent_by_somebody_else)
+ user_sender = get_user_by_id(room_data, sender_json_orig->GetString());
+
time_t timestamp = 0;
const rapidjson::Value &origin_server_ts = GetMember(event_item_json, "origin_server_ts");
if(origin_server_ts.IsNumber())
@@ -1634,8 +1655,68 @@ namespace QuickMedia {
return message;
}
- if(strcmp(type_json.GetString(), "m.room.message") != 0)
- return nullptr;
+ if(strcmp(type_json.GetString(), "m.room.message") == 0) {
+
+ } else if(strcmp(type_json.GetString(), "m.room.member") == 0) {
+ std::string body;
+ const rapidjson::Value &membership_json = GetMember(*content_json, "membership");
+ if(strcmp(membership_json.GetString(), "join") == 0) {
+ const rapidjson::Value &unsigned_json = GetMember(event_item_json, "unsigned");
+ if(unsigned_json.IsObject()) {
+ const rapidjson::Value &prev_content_json = GetMember(unsigned_json, "prev_content");
+ if(prev_content_json.IsObject()) {
+ const rapidjson::Value &prev_displayname_json = GetMember(prev_content_json, "displayname");
+ const rapidjson::Value &prev_avatar_url_json = GetMember(prev_content_json, "avatar_url");
+ const rapidjson::Value &new_displayname_json = GetMember(*content_json, "displayname");
+ const rapidjson::Value &new_avatar_url_json = GetMember(*content_json, "avatar_url");
+ if(new_displayname_json.IsString() && (!prev_displayname_json.IsString() || strcmp(new_displayname_json.GetString(), prev_displayname_json.GetString()) != 0))
+ body = user->display_name + " changed their display name to " + std::string(new_displayname_json.GetString());
+ else if(!new_displayname_json.IsString() && prev_displayname_json.IsString())
+ body = user->display_name + " removed their display name";
+ else if(new_avatar_url_json.IsString() && (!prev_avatar_url_json.IsString() || strcmp(new_avatar_url_json.GetString(), prev_avatar_url_json.GetString()) != 0))
+ body = user->display_name + " changed their profile picture";
+ else if(!new_avatar_url_json.IsString() && prev_avatar_url_json.IsString())
+ body = user->display_name + " removed their profile picture.";
+ else
+ body = user->display_name + " joined the room";
+ } else {
+ body = user->display_name + " joined the room";
+ }
+ } else {
+ body = user->display_name + " joined the room";
+ }
+ } else if(strcmp(membership_json.GetString(), "leave") == 0) {
+ if(sent_by_somebody_else)
+ body = user->display_name + " was kicked from the room by " + user_sender->display_name;
+ else
+ body = user->display_name + " left the room";
+ } else if(strcmp(membership_json.GetString(), "invite") == 0) {
+ body = user->display_name + " was invited to the room by " + user_sender->display_name;
+ } else if(strcmp(membership_json.GetString(), "ban") == 0) {
+ body = user->display_name + " was banned from the room by " + user_sender->display_name;
+ } else {
+ body = "unimplemented membership: " + std::string(membership_json.GetString());
+ }
+ auto message = std::make_shared<Message>();
+ message->type = MessageType::MEMBERSHIP;
+ message->user = user;
+ message->event_id = event_id_str;
+ message->body = std::move(body);
+ message->related_event_id = std::move(related_event_id);
+ message->related_event_type = related_event_type;
+ message->timestamp = timestamp;
+ return message;
+ } else {
+ auto message = std::make_shared<Message>();
+ message->type = MessageType::UNIMPLEMENTED;
+ message->user = user;
+ message->event_id = event_id_str;
+ message->body = "unimplemented event type: " + std::string(type_json.GetString());
+ message->related_event_id = std::move(related_event_id);
+ message->related_event_type = related_event_type;
+ message->timestamp = timestamp;
+ return message;
+ }
const rapidjson::Value &body_json = GetMember(*content_json, "body");
if(!body_json.IsString())
@@ -1767,11 +1848,11 @@ namespace QuickMedia {
continue;
const rapidjson::Value &url_json = GetMember(content_json, "url");
- if(!url_json.IsString() || strncmp(url_json.GetString(), "mxc://", 6) != 0)
+ if(!url_json.IsString())
continue;
std::string url_json_str = url_json.GetString() + 6;
- room_data->set_avatar_url(homeserver + "/_matrix/media/r0/thumbnail/" + std::move(url_json_str) + "?width=32&height=32&method=crop");
+ room_data->set_avatar_url(homeserver + "/_matrix/media/r0/thumbnail/" + thumbnail_url_extract_media_id(url_json_str) + "?width=32&height=32&method=crop");
}
bool has_room_name = room_data->has_name();
@@ -3081,7 +3162,7 @@ namespace QuickMedia {
auto user = room->get_user_by_id(user_id);
if(user)
return user;
-
+ #if 0
// TODO: Instead of guessing notification limit with 100, accumulate rooms unread_notifications count and use that as the limit
// (and take into account that notification response may have notifications after call to sync above).
char url[512];
@@ -3097,6 +3178,15 @@ namespace QuickMedia {
// Is this a synapse bug? sometimes lazy_fetch_members doesn't contain all related clients
fprintf(stderr, "User was not available locally, fetched from server...\n");
return parse_user_info(json_root, user_id, room);
+ #else
+ fprintf(stderr, "Unknown user: %s, creating locally... synapse bug?\n", user_id.c_str());
+ auto user_info = std::make_shared<UserInfo>();
+ user_info->user_id = user_id;
+ user_info->display_name = user_id;
+ user_info->display_name_color = user_id_to_color(user_id);
+ room->add_user(user_info);
+ return user_info;
+ #endif
}
DownloadResult Matrix::download_json(rapidjson::Document &result, const std::string &url, std::vector<CommandArg> additional_args, bool use_browser_useragent, std::string *err_msg) const {