aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2020-10-01 22:21:14 +0200
committerdec05eba <dec05eba@protonmail.com>2020-10-01 22:21:14 +0200
commitef1dd33682ae26b4af1343aaecf443e7cd883674 (patch)
treecf3c959f1cf9f8b6b401d384537685191ae3e4ba
parent30dbaeb2b175c1e67f57aba748ced1a2280fb56d (diff)
Matrix: implement mention/reply notifications
-rw-r--r--TODO6
-rw-r--r--include/Body.hpp4
-rw-r--r--plugins/Matrix.hpp7
-rw-r--r--src/Body.cpp15
-rw-r--r--src/QuickMedia.cpp150
-rw-r--r--src/plugins/Matrix.cpp105
6 files changed, 235 insertions, 52 deletions
diff --git a/TODO b/TODO
index 474200c..c24878c 100644
--- a/TODO
+++ b/TODO
@@ -57,7 +57,7 @@ When deleting an edited message on matrix, delete the original message instead o
Implement mentions in matrix with an autofill list, like on element. Also do the same with / commands.
Add option to disable autosearch and search when pressing enter instead or something? this would be needed for mobile phones where typing is slow.
Sleep when idle, to reduce cpu usage from 1-2% to 0%, important for mobile devices. Also render view to a rendertexture and render that instead of redrawing every time every time.
-Show a notification when somebody replies to you on matrix and also provide a way to notify when notifications should be received (using matrix api) and also read the notification config from matrix. Also provide a way to disable notifications globally. Also send read markers to not notify every time we restart QuickMedia.
+Provide a way to specify when notifications should be received (using matrix api) and also read the notification config from matrix. Also provide a way to disable notifications globally.
Direct message room name should always be the name of the other peer, or in an anonymous room the room name should be some of their names and "X others".
Add room search.
Use quickmedia to show image in matrix rooms, instead of mpv.
@@ -66,4 +66,6 @@ Merge body items in matrix if they are posted by the same author (there is a git
Add joining/leaving room in matrix, and also show invites and add command to ban users. Also add joining by invite, and show invites in the rooms list.
Support peertube (works with mpv, but need to implement search and related videos).
Scroll to bottom when receiving a new message even if the selected message is not the last one. It should instead school if the last message is visible on the screen.
-Add ".." directory to file-manager, to go up one directory. Also add a tab for common directories and recently accessed files/directories (the directories would be the directory of used files). \ No newline at end of file
+Add ".." directory to file-manager, to go up one directory. Also add a tab for common directories and recently accessed files/directories (the directories would be the directory of used files).
+Provide a way to go to the first unread message in matrix and also show a marker in the body (maybe a red line?) where the first unread message is.
+Sort matrix messages by timestamp. This may be needed to make notification messages show properly in the timeline? \ No newline at end of file
diff --git a/include/Body.hpp b/include/Body.hpp
index d2a3424..47de784 100644
--- a/include/Body.hpp
+++ b/include/Body.hpp
@@ -114,6 +114,9 @@ namespace QuickMedia {
BodyItem* get_selected() const;
std::shared_ptr<BodyItem> get_selected_shared();
+ // Returns null if not visible item
+ BodyItem* get_last_fully_visible_item();
+
void clamp_selection();
void draw(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size);
void draw(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size, const Json::Value &content_progress);
@@ -169,5 +172,6 @@ namespace QuickMedia {
std::future<void> load_thumbnail_future;
int num_visible_items;
bool last_item_fully_visible;
+ int last_fully_visible_item;
};
} \ No newline at end of file
diff --git a/plugins/Matrix.hpp b/plugins/Matrix.hpp
index 7df29e1..36b8072 100644
--- a/plugins/Matrix.hpp
+++ b/plugins/Matrix.hpp
@@ -26,6 +26,8 @@ namespace QuickMedia {
std::string url;
std::string thumbnail_url;
std::string replaces_event_id;
+ bool mentions_me = false;
+ time_t timestamp = 0;
MessageType type;
};
@@ -98,6 +100,8 @@ namespace QuickMedia {
PluginResult on_start_typing(const std::string &room_id);
PluginResult on_stop_typing(const std::string &room_id);
+ PluginResult set_read_marker(const std::string &room_id, const Message *message);
+
// |message| is from |BodyItem.userdata| and is of type |Message*|
bool was_message_posted_by_me(const std::string &room_id, void *message) const;
@@ -106,13 +110,14 @@ namespace QuickMedia {
PluginResult sync_response_to_body_items(const Json::Value &root, RoomSyncMessages &room_messages);
PluginResult get_previous_room_messages(const std::string &room_id, RoomData *room_data);
void events_add_user_info(const Json::Value &events_json, RoomData *room_data);
- void events_add_messages(const Json::Value &events_json, RoomData *room_data, MessageDirection message_dir, RoomSyncMessages *room_messages);
+ void events_add_messages(const Json::Value &events_json, RoomData *room_data, MessageDirection message_dir, RoomSyncMessages *room_messages, bool has_unread_notifications);
void events_set_room_name(const Json::Value &events_json, RoomData *room_data);
std::shared_ptr<Message> get_edited_message_original_message(RoomData *room_data, std::shared_ptr<Message> message);
private:
std::unordered_map<std::string, std::unique_ptr<RoomData>> room_data_by_id;
std::string user_id;
+ std::string username;
std::string access_token;
std::string homeserver;
std::string next_batch;
diff --git a/src/Body.cpp b/src/Body.cpp
index 843a1b1..673a095 100644
--- a/src/Body.cpp
+++ b/src/Body.cpp
@@ -75,7 +75,8 @@ namespace QuickMedia {
page_scroll(0.0f),
item_background(sf::Vector2f(1.0f, 1.0f), 10.0f, 10),
num_visible_items(0),
- last_item_fully_visible(true)
+ last_item_fully_visible(true),
+ last_fully_visible_item(-1)
{
progress_text.setFillColor(sf::Color::White);
replies_text.setFillColor(sf::Color(129, 162, 190));
@@ -238,6 +239,12 @@ namespace QuickMedia {
return items[selected_item];
}
+ BodyItem* Body::get_last_fully_visible_item() {
+ if(last_fully_visible_item < 0 || last_fully_visible_item >= (int)items.size() || !items[last_fully_visible_item]->visible)
+ return nullptr;
+ return items[last_fully_visible_item].get();
+ }
+
void Body::clamp_selection() {
int num_items = (int)items.size();
if(items.empty())
@@ -291,6 +298,7 @@ namespace QuickMedia {
item_background_shadow.setFillColor(line_seperator_color);
num_visible_items = 0;
last_item_fully_visible = true;
+ last_fully_visible_item = -1;
int num_items = items.size();
if(num_items == 0 || size.y <= 0.0f) {
@@ -413,6 +421,8 @@ namespace QuickMedia {
if((after_pos.y - start_y) + item_height + spacing_y > size.y)
last_item_fully_visible = false;
+ else
+ last_fully_visible_item = i;
if(after_pos.y - start_y >= size.y)
break;
@@ -422,6 +432,9 @@ namespace QuickMedia {
++num_visible_items;
}
+ if(last_fully_visible_item == -1)
+ last_fully_visible_item = selected_item;
+
glDisable(GL_SCISSOR_TEST);
for(auto it = item_thumbnail_textures.begin(); it != item_thumbnail_textures.end();) {
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 909bdd7..da5453e 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -3331,50 +3331,58 @@ namespace QuickMedia {
*/
// This is needed to get initial data, with joined rooms etc. TODO: Remove this once its cached
// and allow asynchronous update of rooms
- RoomSyncMessages room_sync_messages;
- if(matrix->sync(room_sync_messages) != PluginResult::OK) {
- show_notification("QuickMedia", "Intial matrix sync failed", Urgency::CRITICAL);
- current_page = Page::EXIT;
- return;
- }
-
- if(matrix->get_joined_rooms(tabs[ROOMS_TAB_INDEX].body->items) != PluginResult::OK) {
- show_notification("QuickMedia", "Failed to get a list of joined rooms", Urgency::CRITICAL);
- current_page = Page::EXIT;
- return;
- }
+ bool synced = false;
struct RoomBodyData {
std::shared_ptr<BodyItem> body_item;
bool last_message_read;
+ time_t last_read_message_timestamp;
};
std::unordered_map<std::string, RoomBodyData> body_items_by_room_id;
- for(auto body_item : tabs[ROOMS_TAB_INDEX].body->items) {
- // TODO: Set |last_message_read| depending on read markers (either remote matrix read markers or locally saved ones)
- body_items_by_room_id[body_item->url] = { body_item, true };
- }
- for(auto &[room, messages] : room_sync_messages) {
- auto room_body_item_it = body_items_by_room_id.find(room->id);
- if(room_body_item_it != body_items_by_room_id.end() && !messages.empty()) {
- room_body_item_it->second.body_item->set_description(matrix->message_get_author_displayname(room, messages.back().get()) + ": " + extract_first_line(messages.back()->body));
+ auto process_new_room_messages = [matrix, &body_items_by_room_id](RoomSyncMessages &room_sync_messages, bool only_show_mentions) mutable {
+ for(auto &[room, messages] : room_sync_messages) {
+ bool was_mentioned = false;
+ for(auto &message : messages) {
+ if(message->mentions_me) {
+ was_mentioned = true;
+ message->mentions_me = false;
+ // TODO: What if the message or username begins with "-"? also make the notification image be the avatar of the user
+ std::string desc = "QuickMedia Matrix\n\n" + message->body;
+ show_notification(matrix->message_get_author_displayname(room, message.get()), desc.c_str());
+ }
+ }
+
+ auto room_body_item_it = body_items_by_room_id.find(room->id);
+ if(room_body_item_it == body_items_by_room_id.end())
+ continue;
+
+ if(only_show_mentions) {
+ std::string room_desc;
+ if(!messages.empty())
+ room_desc = matrix->message_get_author_displayname(room, messages.back().get()) + ": " + extract_first_line(messages.back()->body);
+ if(was_mentioned) {
+ room_desc += "\n** You were mentioned **"; // TODO: Better notification?
+ room_body_item_it->second.body_item->title_color = sf::Color(255, 100, 100);
+ room_body_item_it->second.last_message_read = false;
+ }
+ room_body_item_it->second.body_item->set_description(std::move(room_desc));
+ } else if(!messages.empty()) {
+ std::string room_desc = "Unread: " + matrix->message_get_author_displayname(room, messages.back().get()) + ": " + extract_first_line(messages.back()->body);
+ if(was_mentioned)
+ room_desc += "\n** You were mentioned **"; // TODO: Better notification?
+ room_body_item_it->second.body_item->set_description(std::move(room_desc));
+ room_body_item_it->second.body_item->title_color = sf::Color(255, 100, 100);
+ room_body_item_it->second.last_message_read = false;
+ }
}
- }
+ };
// TODO: the initial room to view should be the last viewed room when closing QuickMedia.
// The room id should be saved in a file when changing viewed room.
std::string current_room_id;
- if(!tabs[ROOMS_TAB_INDEX].body->items.empty())
- current_room_id = tabs[ROOMS_TAB_INDEX].body->items[0]->url;
-
- // TODO: Allow empty initial room (if the user hasn't joined any room yet).
- assert(!current_room_id.empty());
-
RoomBodyData *current_room_body_data = nullptr;
- auto room_body_item_it = body_items_by_room_id.find(current_room_id);
- if(room_body_item_it != body_items_by_room_id.end())
- current_room_body_data = &room_body_item_it->second;
// get_all_room_messages is not needed here because its done in the loop, where the initial timeout is 0ms
@@ -3461,6 +3469,7 @@ namespace QuickMedia {
struct SyncFutureResult {
BodyItems body_items;
+ BodyItems rooms_body_items;
RoomSyncMessages room_sync_messages;
};
@@ -3503,9 +3512,7 @@ namespace QuickMedia {
const float tab_vertical_offset = 10.0f;
sf::Text room_name_text("", *font, 18);
- if(current_room_body_data)
- room_name_text.setString(current_room_body_data->body_item->get_title());
- const float room_name_text_height = std::floor(room_name_text.getLocalBounds().height);
+ const float room_name_text_height = 20.0f;
const float room_name_text_padding_y = 10.0f;
const float room_name_total_height = room_name_text_height + room_name_text_padding_y * 2.0f;
const float room_avatar_height = 32.0f;
@@ -3534,6 +3541,9 @@ namespace QuickMedia {
Body url_selection_body(this, font.get(), bold_font.get(), cjk_font.get());
+ sf::Clock read_marker_timer;
+ const sf::Int32 read_marker_timeout_ms = 3000;
+
auto launch_url = [this, &redraw](const std::string &url) mutable {
if(url.empty())
return;
@@ -3608,6 +3618,7 @@ namespace QuickMedia {
} else if(event.key.code == sf::Keyboard::Left) {
tabs[selected_tab].body->clear_thumbnails();
selected_tab = std::max(0, selected_tab - 1);
+ read_marker_timer.restart();
if(typing) {
fprintf(stderr, "Stopped typing\n");
typing = false;
@@ -3616,6 +3627,7 @@ namespace QuickMedia {
} else if(event.key.code == sf::Keyboard::Right) {
tabs[selected_tab].body->clear_thumbnails();
selected_tab = std::min((int)tabs.size() - 1, selected_tab + 1);
+ read_marker_timer.restart();
if(typing) {
fprintf(stderr, "Stopped typing\n");
typing = false;
@@ -3907,6 +3919,8 @@ namespace QuickMedia {
}
chat_input_height_full = chat_input.get_height() + chat_input_padding_y * 2.0f;
+ if(selected_tab != MESSAGES_TAB_INDEX)
+ chat_input_height_full = 0.0f;
chat_input_shade.setSize(sf::Vector2f(window_size.x, chat_input_height_full));
chat_input_shade.setPosition(0.0f, window_size.y - chat_input_shade.getSize().y);
@@ -3927,12 +3941,21 @@ namespace QuickMedia {
sync_running = true;
sync_timer.restart();
sync_future_room_id = current_room_id;
- sync_future = std::async(std::launch::async, [this, &sync_future_room_id]() {
+ sync_future = std::async(std::launch::async, [this, &sync_future_room_id, synced]() {
Matrix *matrix = static_cast<Matrix*>(current_plugin);
SyncFutureResult result;
if(matrix->sync(result.room_sync_messages) == PluginResult::OK) {
fprintf(stderr, "Synced matrix\n");
+
+ if(!synced) {
+ if(matrix->get_joined_rooms(result.rooms_body_items) != PluginResult::OK) {
+ show_notification("QuickMedia", "Failed to get a list of joined rooms", Urgency::CRITICAL);
+ current_page = Page::EXIT;
+ return result;
+ }
+ }
+
if(matrix->get_new_room_messages(sync_future_room_id, result.body_items) != PluginResult::OK) {
fprintf(stderr, "Failed to get new matrix messages in room: %s\n", sync_future_room_id.c_str());
}
@@ -3954,23 +3977,39 @@ namespace QuickMedia {
if(scroll_to_end)
tabs[MESSAGES_TAB_INDEX].body->select_last_item();
}
- for(auto &[room, messages] : sync_result.room_sync_messages) {
- auto room_body_item_it = body_items_by_room_id.find(room->id);
- if(room_body_item_it != body_items_by_room_id.end() && !messages.empty()) {
- room_body_item_it->second.body_item->set_description("Unread: " + matrix->message_get_author_displayname(room, messages.back().get()) + ": " + extract_first_line(messages.back()->body));
- room_body_item_it->second.body_item->title_color = sf::Color(255, 100, 100);
- room_body_item_it->second.last_message_read = false;
+
+ if(!synced) {
+ tabs[ROOMS_TAB_INDEX].body->items = std::move(sync_result.rooms_body_items);
+
+ for(auto body_item : tabs[ROOMS_TAB_INDEX].body->items) {
+ // TODO: Set |last_message_read| depending on read markers (either remote matrix read markers or locally saved ones)
+ body_items_by_room_id[body_item->url] = { body_item, true, 0 };
}
+
+ // TODO: the initial room to view should be the last viewed room when closing QuickMedia.
+ // The room id should be saved in a file when changing viewed room.
+ if(!tabs[ROOMS_TAB_INDEX].body->items.empty())
+ current_room_id = tabs[ROOMS_TAB_INDEX].body->items[0]->url;
+
+ auto room_body_item_it = body_items_by_room_id.find(current_room_id);
+ if(room_body_item_it != body_items_by_room_id.end()) {
+ current_room_body_data = &room_body_item_it->second;
+ room_name_text.setString(current_room_body_data->body_item->get_title());
+ }
+ redraw = true;
}
+
+ process_new_room_messages(sync_result.room_sync_messages, !synced);
sync_running = false;
+ synced = true;
}
if(fetching_previous_messages_running && previous_messages_future.valid() && previous_messages_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
BodyItems new_body_items = previous_messages_future.get();
fprintf(stderr, "Finished fetching older messages, num new messages: %zu\n", new_body_items.size());
// Ignore finished fetch of messages if it happened in another room. When we navigate back to the room we will get the messages again
- if(previous_messages_future_room_id == current_room_id) {
- size_t num_new_messages = new_body_items.size();
+ size_t num_new_messages = new_body_items.size();
+ if(previous_messages_future_room_id == current_room_id && num_new_messages > 0) {
int selected_item_index = tabs[MESSAGES_TAB_INDEX].body->get_selected_item();
tabs[MESSAGES_TAB_INDEX].body->prepend_items(std::move(new_body_items));
tabs[MESSAGES_TAB_INDEX].body->set_selected_item(selected_item_index + num_new_messages);
@@ -4073,8 +4112,12 @@ namespace QuickMedia {
if(tabs[selected_tab].type == ChatTabType::MESSAGES) {
if(tabs[selected_tab].body->is_last_item_fully_visible()) {
if(current_room_body_data && !current_room_body_data->last_message_read) {
- if(strncmp(current_room_body_data->body_item->get_description().c_str(), "Unread: ", 8) == 0)
- current_room_body_data->body_item->set_description(current_room_body_data->body_item->get_description().c_str() + 8);
+ std::string room_desc = current_room_body_data->body_item->get_description();
+ if(strncmp(room_desc.c_str(), "Unread: ", 8) == 0)
+ room_desc = room_desc.substr(8);
+ if(room_desc.size() >= 26 && strncmp(room_desc.c_str() + room_desc.size() - 26, "\n** You were mentioned **", 26) == 0)
+ room_desc = room_desc.substr(0, room_desc.size() - 26);
+ current_room_body_data->body_item->set_description(std::move(room_desc));
// TODO: Show a line like nheko instead for unread messages, or something else
current_room_body_data->body_item->title_color = sf::Color::White;
current_room_body_data->last_message_read = true;
@@ -4084,7 +4127,26 @@ namespace QuickMedia {
}
}
+ // TODO: Cache /sync, then we wont only see loading text
+ if(!synced) {
+ sf::Text loading_text("Loading...", *font, 24);
+ loading_text.setPosition(body_pos.x + body_size.x * 0.5f - loading_text.getLocalBounds().width * 0.5f, body_pos.y + body_size.y * 0.5f - loading_text.getLocalBounds().height * 0.5f);
+ window.draw(loading_text);
+ }
+
if(tabs[selected_tab].type == ChatTabType::MESSAGES) {
+ BodyItem *last_visible_item = tabs[selected_tab].body->get_last_fully_visible_item();
+ if(chat_state != ChatState::URL_SELECTION && current_room_body_data && last_visible_item && read_marker_timer.getElapsedTime().asMilliseconds() >= read_marker_timeout_ms) {
+ Message *message = (Message*)last_visible_item->userdata;
+ if(message->timestamp > current_room_body_data->last_read_message_timestamp) {
+ current_room_body_data->last_read_message_timestamp = message->timestamp;
+ read_marker_timer.restart();
+ if(matrix->set_read_marker(current_room_id, message) != PluginResult::OK) {
+ fprintf(stderr, "Warning: failed to set read marker to %s\n", message->event_id.c_str());
+ }
+ }
+ }
+
window.draw(chat_input_shade);
chat_input.draw(window); //chat_input.draw(window, false);
window.draw(logo_sprite);
diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp
index 6d89ff4..e182c11 100644
--- a/src/plugins/Matrix.cpp
+++ b/src/plugins/Matrix.cpp
@@ -54,7 +54,7 @@ namespace QuickMedia {
char url[512];
if(next_batch.empty())
- snprintf(url, sizeof(url), "%s/_matrix/client/r0/sync?timeout=0&full_state=true", homeserver.c_str());
+ snprintf(url, sizeof(url), "%s/_matrix/client/r0/sync?timeout=0", homeserver.c_str());
else
snprintf(url, sizeof(url), "%s/_matrix/client/r0/sync?timeout=30000&since=%s", homeserver.c_str(), next_batch.c_str());
@@ -312,9 +312,18 @@ namespace QuickMedia {
room_it->second->prev_batch = prev_batch_json.asString();
}
+ // TODO: Is there no better way to check for notifications? this is not robust...
+ bool has_unread_notifications = false;
+ const Json::Value &unread_notification_json = (*it)["unread_notifications"];
+ if(unread_notification_json.isObject()) {
+ const Json::Value &highlight_count_json = unread_notification_json["highlight_count"];
+ if(highlight_count_json.isNumeric() && highlight_count_json.asInt64() > 0)
+ has_unread_notifications = true;
+ }
+
const Json::Value &events_json = timeline_json["events"];
events_add_user_info(events_json, room_it->second.get());
- events_add_messages(events_json, room_it->second.get(), MessageDirection::AFTER, &room_messages);
+ events_add_messages(events_json, room_it->second.get(), MessageDirection::AFTER, &room_messages, has_unread_notifications);
events_set_room_name(events_json, room_it->second.get());
}
@@ -400,13 +409,65 @@ namespace QuickMedia {
return "";
}
- void Matrix::events_add_messages(const Json::Value &events_json, RoomData *room_data, MessageDirection message_dir, RoomSyncMessages *room_messages) {
+ // TODO: Is this really the proper way to check for username mentions?
+ static bool is_username_seperating_character(char c) {
+ switch(c) {
+ case ' ':
+ case '\n':
+ case '\t':
+ case '\v':
+ case '.':
+ case ',':
+ case '@':
+ case ':':
+ case '?':
+ case '!':
+ case '<':
+ case '>':
+ case '\0':
+ return true;
+ default:
+ return false;
+ }
+ return false;
+ }
+
+ // TODO: Do not show notification if mention is a reply to somebody else that replies to me? also dont show notification everytime a mention is edited
+ static bool message_contains_user_mention(const std::string &msg, const std::string &username) {
+ if(msg.empty())
+ return false;
+
+ size_t index = 0;
+ while(index < msg.size()) {
+ size_t found_index = msg.find(username, index);
+ if(found_index == std::string::npos)
+ return false;
+
+ char prev_char = ' ';
+ if(found_index > 0)
+ prev_char = msg[found_index - 1];
+
+ char next_char = '\0';
+ if(found_index + username.size() < msg.size() - 1)
+ next_char = msg[found_index + username.size()];
+
+ if(is_username_seperating_character(prev_char) && is_username_seperating_character(next_char))
+ return true;
+
+ index += username.size();
+ }
+
+ return false;
+ }
+
+ void Matrix::events_add_messages(const Json::Value &events_json, RoomData *room_data, MessageDirection message_dir, RoomSyncMessages *room_messages, bool has_unread_notifications) {
if(!events_json.isArray())
return;
std::vector<std::shared_ptr<Message>> *room_sync_messages = nullptr;
if(room_messages)
room_sync_messages = &(*room_messages)[room_data];
+
std::vector<std::shared_ptr<Message>> new_messages;
for(const Json::Value &event_item_json : events_json) {
@@ -448,6 +509,11 @@ namespace QuickMedia {
if(!body_json.isString())
continue;
+ time_t timestamp = 0;
+ const Json::Value &origin_server_ts = event_item_json["origin_server_ts"];
+ if(origin_server_ts.isNumeric())
+ timestamp = origin_server_ts.asInt64();
+
std::string replaces_event_id;
const Json::Value &relates_to_json = content_json["m.relates_to"];
if(relates_to_json.isObject()) {
@@ -485,6 +551,10 @@ namespace QuickMedia {
message->event_id = event_id_str;
message->body = body_json.asString();
message->replaces_event_id = std::move(replaces_event_id);
+ // TODO: Is @room ok? shouldn't we also check if the user has permission to do @room? (only when notifications are limited to @mentions)
+ if(has_unread_notifications && !username.empty())
+ message->mentions_me = message_contains_user_mention(message->body, username) || message_contains_user_mention(message->body, "@room");
+ message->timestamp = timestamp;
new_messages.push_back(message);
room_data->message_by_event_id[event_id_str] = message;
if(room_sync_messages)
@@ -631,7 +701,7 @@ namespace QuickMedia {
events_set_room_name(state_json, room_data);
const Json::Value &chunk_json = json_root["chunk"];
- events_add_messages(chunk_json, room_data, MessageDirection::BEFORE, nullptr);
+ events_add_messages(chunk_json, room_data, MessageDirection::BEFORE, nullptr, false);
const Json::Value &end_json = json_root["end"];
if(!end_json.isString()) {
@@ -1232,6 +1302,7 @@ namespace QuickMedia {
json_root["homeserver"] = homeserver;
this->user_id = user_id_json.asString();
+ this->username = extract_user_name_from_user_id(this->user_id);
this->access_token = access_token_json.asString();
this->homeserver = homeserver;
@@ -1266,6 +1337,7 @@ namespace QuickMedia {
// Make sure all fields are reset here!
room_data_by_id.clear();
user_id.clear();
+ username.clear();
access_token.clear();
homeserver.clear();
next_batch.clear();
@@ -1390,6 +1462,7 @@ namespace QuickMedia {
}
this->user_id = std::move(user_id);
+ this->username = extract_user_name_from_user_id(this->user_id);
this->access_token = std::move(access_token);
this->homeserver = std::move(homeserver);
return PluginResult::OK;
@@ -1440,6 +1513,30 @@ namespace QuickMedia {
return PluginResult::OK;
}
+ PluginResult Matrix::set_read_marker(const std::string &room_id, const Message *message) {
+ Json::Value request_data(Json::objectValue);
+ request_data["m.fully_read"] = message->event_id;
+ request_data["m.read"] = message->event_id;
+ request_data["m.hidden"] = false; // What is this for? element sends it but its not part of the documentation. Is it for hiding read receipt from other users? in that case, TODO: make it configurable
+
+ Json::StreamWriterBuilder builder;
+ builder["commentStyle"] = "None";
+ builder["indentation"] = "";
+
+ std::vector<CommandArg> additional_args = {
+ { "-X", "POST" },
+ { "-H", "content-type: application/json" },
+ { "--data-binary", Json::writeString(builder, std::move(request_data)) },
+ { "-H", "Authorization: Bearer " + access_token }
+ };
+
+ std::string server_response;
+ if(download_to_string(homeserver + "/_matrix/client/r0/rooms/" + room_id + "/read_markers", server_response, std::move(additional_args), use_tor, true) != DownloadResult::OK)
+ return PluginResult::NET_ERR;
+
+ return PluginResult::OK;
+ }
+
bool Matrix::was_message_posted_by_me(const std::string &room_id, void *message) const {
auto room_it = room_data_by_id.find(room_id);
if(room_it == room_data_by_id.end()) {