aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2020-10-23 10:25:08 +0200
committerdec05eba <dec05eba@protonmail.com>2020-10-23 11:06:25 +0200
commit2a973ff9402dab9d6c751a146a9f83617d0e5211 (patch)
treeaacd0ebae55cbe622974d64b464652ffd65a1268 /src
parent96c9ed391270347c4c7036179fd2815f679ca7cf (diff)
Matrix: start on room tags, fix thread race condition on accessing room variables (name, avatar url, prev batch)
Diffstat (limited to 'src')
-rw-r--r--src/Body.cpp1
-rw-r--r--src/QuickMedia.cpp12
-rw-r--r--src/plugins/Matrix.cpp137
3 files changed, 128 insertions, 22 deletions
diff --git a/src/Body.cpp b/src/Body.cpp
index 3fd2324..0af6407 100644
--- a/src/Body.cpp
+++ b/src/Body.cpp
@@ -835,6 +835,7 @@ namespace QuickMedia {
return spacing_y;
}
+ // TODO: Support utf-8 case insensitive find
//static
bool Body::string_find_case_insensitive(const std::string &str, const std::string &substr) {
auto it = std::search(str.begin(), str.end(), substr.begin(), substr.end(),
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 48d3689..33ebb06 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -1012,7 +1012,7 @@ namespace QuickMedia {
tab.body->clear_cache();
}
- if(new_tabs.size() == 1 && new_tabs[0].page->is_manga_images_page()) {
+ if(new_tabs.size() == 1 && new_tabs[0].page->get_type() == PageTypez::MANGA_IMAGES) {
select_episode(selected_item, false);
Body *chapters_body = tabs[selected_tab].body.get();
chapters_body->filter_search_fuzzy(""); // Needed (or not really) to go to the next chapter when reaching the last page of a chapter
@@ -1048,11 +1048,11 @@ namespace QuickMedia {
}
window.setKeyRepeatEnabled(true);
redraw = true;
- } else if(new_tabs.size() == 1 && new_tabs[0].page->is_image_board_thread_page()) {
+ } else if(new_tabs.size() == 1 && new_tabs[0].page->get_type() == PageTypez::IMAGE_BOARD_THREAD) {
current_page = PageType::IMAGE_BOARD_THREAD;
image_board_thread_page(static_cast<ImageBoardThreadPage*>(new_tabs[0].page.get()), new_tabs[0].body.get());
redraw = true;
- } else if(new_tabs.size() == 1 && new_tabs[0].page->is_video_page()) {
+ } else if(new_tabs.size() == 1 && new_tabs[0].page->get_type() == PageTypez::VIDEO) {
current_page = PageType::VIDEO_CONTENT;
video_content_page(new_tabs[0].page.get(), selected_item->url, selected_item->get_title());
redraw = true;
@@ -3049,7 +3049,7 @@ namespace QuickMedia {
room->has_unread_mention = 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 || is_first_sync || selected_tab == ROOMS_TAB_INDEX)
- show_notification("QuickMedia matrix - " + matrix->message_get_author_displayname(message.get()) + " (" + room->name + ")", message->body);
+ show_notification("QuickMedia matrix - " + matrix->message_get_author_displayname(message.get()) + " (" + room->get_name() + ")", message->body);
}
}
}
@@ -3537,12 +3537,12 @@ namespace QuickMedia {
for(size_t i = 0; i < rooms.size(); ++i) {
auto &room = rooms[i];
- std::string room_name = room->name;
+ std::string room_name = room->get_name();
if(room_name.empty())
room_name = room->id;
auto body_item = BodyItem::create(std::move(room_name));
- body_item->thumbnail_url = room->avatar_url;
+ body_item->thumbnail_url = room->get_avatar_url();
body_item->userdata = room; // Note: this has to be valid as long as the room list is valid!
body_item->thumbnail_mask_type = ThumbnailMaskType::CIRCLE;
body_item->thumbnail_size = sf::Vector2i(32, 32);
diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp
index 9a2f70b..99d6bed 100644
--- a/src/plugins/Matrix.cpp
+++ b/src/plugins/Matrix.cpp
@@ -115,6 +115,51 @@ namespace QuickMedia {
return pinned_events;
}
+ bool RoomData::has_prev_batch() {
+ std::lock_guard<std::mutex> lock(room_mutex);
+ return !prev_batch.empty();
+ }
+
+ void RoomData::set_prev_batch(const std::string &new_prev_batch) {
+ std::lock_guard<std::mutex> lock(room_mutex);
+ prev_batch = new_prev_batch;
+ }
+
+ std::string RoomData::get_prev_batch() {
+ std::lock_guard<std::mutex> lock(room_mutex);
+ return prev_batch;
+ }
+
+ bool RoomData::has_name() {
+ std::lock_guard<std::mutex> lock(room_mutex);
+ return !name.empty();
+ }
+
+ void RoomData::set_name(const std::string &new_name) {
+ std::lock_guard<std::mutex> lock(room_mutex);
+ name = new_name;
+ }
+
+ std::string RoomData::get_name() {
+ std::lock_guard<std::mutex> lock(room_mutex);
+ return name;
+ }
+
+ bool RoomData::has_avatar_url() {
+ std::lock_guard<std::mutex> lock(room_mutex);
+ return !avatar_url.empty();
+ }
+
+ void RoomData::set_avatar_url(const std::string &new_avatar_url) {
+ std::lock_guard<std::mutex> lock(room_mutex);
+ avatar_url = new_avatar_url;
+ }
+
+ std::string RoomData::get_avatar_url() {
+ std::lock_guard<std::mutex> lock(room_mutex);
+ return avatar_url;
+ }
+
PluginResult Matrix::sync(RoomSyncData &room_sync_data) {
std::vector<CommandArg> additional_args = {
{ "-H", "Authorization: Bearer " + access_token },
@@ -227,11 +272,11 @@ namespace QuickMedia {
const rapidjson::Value &timeline_json = GetMember(it.value, "timeline");
if(timeline_json.IsObject()) {
- if(room->prev_batch.empty()) {
+ if(!room->has_prev_batch()) {
// This may be non-existent if this is the first event in the room
const rapidjson::Value &prev_batch_json = GetMember(timeline_json, "prev_batch");
if(prev_batch_json.IsString())
- room->prev_batch = prev_batch_json.GetString();
+ room->set_prev_batch(prev_batch_json.GetString());
}
// TODO: Use /_matrix/client/r0/notifications ? or remove this and always look for displayname/user_id in messages
@@ -258,6 +303,12 @@ namespace QuickMedia {
events_add_user_read_markers(events_json, room);
}
}
+
+ const rapidjson::Value &account_data_json = GetMember(it.value, "account_data");
+ if(account_data_json.IsObject()) {
+ const rapidjson::Value &events_json = GetMember(account_data_json, "events");
+ events_add_room_to_tags(events_json, room);
+ }
}
return PluginResult::OK;
@@ -709,11 +760,14 @@ namespace QuickMedia {
if(!name_json.IsString())
continue;
- room_data->name = name_json.GetString();
+ room_data->set_name(name_json.GetString());
}
+ bool has_room_name = room_data->has_name();
+ bool has_room_avatar_url = room_data->has_avatar_url();
+
std::vector<std::shared_ptr<UserInfo>> users_excluding_me;
- if(room_data->name.empty() || room_data->avatar_url.empty())
+ if(!has_room_name || !has_room_avatar_url)
users_excluding_me = room_data->get_users_excluding_me(user_id); // TODO: What about thread safety with user_id? its reset in /logout
for(const rapidjson::Value &event_item_json : events_json.GetArray()) {
@@ -732,18 +786,21 @@ namespace QuickMedia {
if(!creator_json.IsString())
continue;
- if(room_data->name.empty())
- room_data->name = combine_user_display_names_for_room_name(users_excluding_me, creator_json.GetString());
+ if(!has_room_name) {
+ room_data->set_name(combine_user_display_names_for_room_name(users_excluding_me, creator_json.GetString()));
+ has_room_name = true;
+ }
- if(room_data->avatar_url.empty()) {
+ if(!has_room_avatar_url) {
if(users_excluding_me.empty()) {
auto user = room_data->get_user_by_id(creator_json.GetString());
if(user)
- room_data->avatar_url = user->avatar_url;
+ room_data->set_avatar_url(user->avatar_url);
} else {
// TODO: If there are multiple users, then we want to use some other type of avatar, not the first users avatar
- room_data->avatar_url = users_excluding_me.front()->avatar_url;
+ room_data->set_avatar_url(users_excluding_me.front()->avatar_url);
}
+ has_room_avatar_url = true;
}
}
@@ -764,7 +821,7 @@ namespace QuickMedia {
continue;
std::string url_json_str = url_json.GetString() + 6;
- room_data->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/" + std::move(url_json_str) + "?width=32&height=32&method=crop");
}
}
@@ -801,8 +858,54 @@ namespace QuickMedia {
room_data->append_pinned_events(std::move(pinned_events));
}
+ // 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 const char* tag_get_name(const char *name, size_t size) {
+ if(size >= 2 && (memcmp(name, "m.", 2) == 0 || memcmp(name, "u.", 2) == 0))
+ return name + 2;
+ else if(size >= 9 && memcmp(name, "tld.name.", 9) == 0)
+ return name + 9;
+ else
+ return name;
+ }
+
+ void Matrix::events_add_room_to_tags(const rapidjson::Value &events_json, RoomData *room_data) {
+ if(!events_json.IsArray())
+ return;
+
+ std::vector<std::string> pinned_events;
+ 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.tag") != 0)
+ continue;
+
+ const rapidjson::Value &content_json = GetMember(event_item_json, "content");
+ if(!content_json.IsObject())
+ continue;
+
+ const rapidjson::Value &tags_json = GetMember(content_json, "tags");
+ if(!tags_json.IsObject())
+ continue;
+
+ for(auto const &tag_json : tags_json.GetObject()) {
+ if(!tag_json.name.IsString() || !tag_json.value.IsObject())
+ continue;
+
+ const char *tag_name = tag_get_name(tag_json.name.GetString(), tag_json.name.GetStringLength());
+ if(!tag_name)
+ continue;
+
+ // TODO: Support tag order
+ rooms_by_tag_name[tag_name].push_back(room_data->index);
+ }
+ }
+ }
+
PluginResult Matrix::get_previous_room_messages(RoomData *room_data) {
- std::string from = room_data->prev_batch;
+ std::string from = room_data->get_prev_batch();
if(from.empty()) {
fprintf(stderr, "Info: missing previous batch for room: %s, using /sync next batch\n", room_data->id.c_str());
from = next_batch;
@@ -835,9 +938,9 @@ namespace QuickMedia {
if(!json_root.IsObject())
return PluginResult::ERR;
- const rapidjson::Value &state_json = GetMember(json_root, "state");
- events_add_user_info(state_json, room_data);
- events_set_room_name(state_json, room_data);
+ //const rapidjson::Value &state_json = GetMember(json_root, "state");
+ //events_add_user_info(state_json, room_data);
+ //events_set_room_name(state_json, room_data);
const rapidjson::Value &chunk_json = GetMember(json_root, "chunk");
events_add_messages(chunk_json, room_data, MessageDirection::BEFORE, nullptr, false);
@@ -848,7 +951,7 @@ namespace QuickMedia {
return PluginResult::OK;
}
- room_data->prev_batch = end_json.GetString();
+ room_data->set_prev_batch(end_json.GetString());
return PluginResult::OK;
}
@@ -1478,6 +1581,7 @@ namespace QuickMedia {
rooms.clear();
room_list_read_index = 0;
room_data_by_id.clear();
+ rooms_by_tag_name.clear();
user_id.clear();
username.clear();
access_token.clear();
@@ -1722,7 +1826,8 @@ namespace QuickMedia {
void Matrix::add_room(std::unique_ptr<RoomData> room) {
std::lock_guard<std::mutex> lock(room_data_mutex);
- room_data_by_id.insert(std::make_pair(room->id, rooms.size()));
+ room->index = rooms.size();
+ room_data_by_id.insert(std::make_pair(room->id, room->index));
rooms.push_back(std::move(room));
}