aboutsummaryrefslogtreecommitdiff
path: root/src/plugins
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2021-05-20 22:33:32 +0200
committerdec05eba <dec05eba@protonmail.com>2021-05-20 22:33:32 +0200
commit80b48b270ed66e3557b98d9fc8e82ad868bcde80 (patch)
tree74e553cbcfbfed4eb6f5a2a931e46689e43a38cf /src/plugins
parent5b11322c33ef61d3aa8d6bc9c9bc0355f35afa52 (diff)
Add notifications tab to matrix
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/Matrix.cpp212
1 files changed, 181 insertions, 31 deletions
diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp
index e015ada..7992772 100644
--- a/src/plugins/Matrix.cpp
+++ b/src/plugins/Matrix.cpp
@@ -348,8 +348,8 @@ namespace QuickMedia {
//body_item.reset();
}
- MatrixQuickMedia::MatrixQuickMedia(Program *program, Matrix *matrix, MatrixRoomsPage *rooms_page, MatrixRoomTagsPage *room_tags_page, MatrixInvitesPage *invites_page) :
- program(program), matrix(matrix), chat_page(nullptr), rooms_page(rooms_page), room_tags_page(room_tags_page), invites_page(invites_page)
+ MatrixQuickMedia::MatrixQuickMedia(Program *program, Matrix *matrix, MatrixRoomsPage *rooms_page, MatrixRoomTagsPage *room_tags_page, MatrixInvitesPage *invites_page, MatrixNotificationsPage *notifications_page) :
+ program(program), matrix(matrix), chat_page(nullptr), rooms_page(rooms_page), room_tags_page(room_tags_page), invites_page(invites_page), notifications_page(notifications_page)
{
rooms_page->matrix_delegate = this;
room_tags_page->matrix_delegate = this;
@@ -405,7 +405,17 @@ namespace QuickMedia {
if(message->notification_mentions_me) {
// 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) && message->related_event_type != RelatedEventType::EDIT && message->related_event_type != RelatedEventType::REDACTION) {
- show_notification("QuickMedia matrix - " + extract_first_line_remove_newline_elipses(matrix->message_get_author_displayname(message.get()), AUTHOR_MAX_LENGTH) + " (" + room->get_name() + ")", message->body);
+ std::string body = remove_reply_formatting(message->body);
+ show_notification("QuickMedia matrix - " + extract_first_line_remove_newline_elipses(matrix->message_get_author_displayname(message.get()), AUTHOR_MAX_LENGTH) + " (" + room->get_name() + ")", body);
+
+ MatrixNotification notification;
+ notification.room = room;
+ notification.event_id = message->event_id;
+ notification.sender_user_id = message->user->user_id;
+ notification.body = std::move(body);
+ notification.timestamp = message->timestamp;
+ notification.read = false;
+ notifications_page->add_notification(std::move(notification));
}
}
}
@@ -433,8 +443,8 @@ namespace QuickMedia {
invites_page->remove_body_item_by_room_id(room_id);
}
- void MatrixQuickMedia::add_unread_notification(RoomData *room, std::string, std::string sender, std::string body) {
- show_notification("QuickMedia matrix - " + sender + " (" + room->get_name() + ")", body);
+ void MatrixQuickMedia::add_unread_notification(MatrixNotification notification) {
+ show_notification("QuickMedia matrix - " + notification.sender_user_id + " (" + notification.room->get_name() + ")", notification.body);
}
static UsersByRoom::iterator find_user_data_by_id(UsersByRoom &users_by_room, const MatrixEventUserInfo &user_info) {
@@ -481,6 +491,10 @@ namespace QuickMedia {
}
}
+ void MatrixQuickMedia::set_room_as_read(RoomData *room) {
+ notifications_page->set_room_as_read(room);
+ }
+
static void sort_room_body_items(std::vector<std::shared_ptr<BodyItem>> &room_body_items) {
std::sort(room_body_items.begin(), room_body_items.end(), [](const std::shared_ptr<BodyItem> &body_item1, const std::shared_ptr<BodyItem> &body_item2) {
RoomData *room1 = static_cast<RoomData*>(body_item1->userdata);
@@ -695,6 +709,10 @@ namespace QuickMedia {
current_chat_page = chat_page;
}
+ void MatrixRoomsPage::set_room_as_read(RoomData *room) {
+ matrix_delegate->set_room_as_read(room);
+ }
+
void MatrixRoomsPage::clear_data() {
body->clear_items();
if(current_chat_page)
@@ -966,6 +984,10 @@ namespace QuickMedia {
return users_body ? users_body->items.size() : 0;
}
+ void MatrixChatPage::set_room_as_read(RoomData *room) {
+ rooms_page->set_room_as_read(room);
+ }
+
PluginResult MatrixRoomDirectoryPage::submit(const std::string &title, const std::string&, std::vector<Tab> &result_tabs) {
std::string server_name = title;
@@ -1011,6 +1033,77 @@ namespace QuickMedia {
return PluginResult::OK;
}
+ class NotificationsExtraData : public BodyItemExtra {
+ public:
+ RoomData *room;
+ bool read;
+ };
+
+ static std::shared_ptr<BodyItem> notification_to_body_item(Body *body, const MatrixNotification &notification) {
+ auto body_item = BodyItem::create("");
+ body_item->set_author(notification.room->get_name());
+ body_item->set_description(notification.sender_user_id + ":\n" + notification.body);
+ body_item->set_timestamp(notification.timestamp);
+ body_item->url = notification.event_id;
+
+ if(!notification.read) {
+ body_item->set_author_color(sf::Color(255, 100, 100));
+ body_item->set_description_color(sf::Color(255, 100, 100));
+ }
+
+ body_item->thumbnail_mask_type = ThumbnailMaskType::CIRCLE;
+ body_item->thumbnail_size = sf::Vector2i(32, 32);
+ body_item->thumbnail_url = notification.room->get_avatar_url();
+
+ auto extra_data = std::make_shared<NotificationsExtraData>();
+ extra_data->room = notification.room;
+ extra_data->read = notification.read;
+ body_item->extra = std::move(extra_data);
+
+ body->apply_search_filter_for_item(body_item.get());
+ return body_item;
+ }
+
+ MatrixNotificationsPage::MatrixNotificationsPage(Program *program, Matrix *matrix, Body *notifications_body) :
+ LazyFetchPage(program), matrix(matrix), notifications_body(notifications_body) {}
+
+ PluginResult MatrixNotificationsPage::get_page(const std::string&, int, BodyItems &result_items) {
+ return matrix->get_previous_notifications([this, &result_items](const MatrixNotification &notification) {
+ result_items.push_back(notification_to_body_item(notifications_body, notification));
+ });
+ }
+
+ PluginResult MatrixNotificationsPage::lazy_fetch(BodyItems &result_items) {
+ matrix->get_cached_notifications([this, &result_items](const MatrixNotification &notification) {
+ result_items.push_back(notification_to_body_item(notifications_body, notification));
+ });
+ return PluginResult::OK;
+ }
+
+ bool MatrixNotificationsPage::is_ready() {
+ return matrix->has_finished_fetching_notifications();
+ }
+
+ void MatrixNotificationsPage::add_notification(MatrixNotification notification) {
+ //int prev_selected_item = notifications_body->get_selected_item();
+ //notifications_body->items.push_back(notification_to_body_item(notifications_body, notification));
+ //notifications_body->set_selected_item(prev_selected_item - 1);
+ int prev_selected_item = notifications_body->get_selected_item();
+ notifications_body->items.insert(notifications_body->items.begin(), notification_to_body_item(notifications_body, notification));
+ notifications_body->set_selected_item(prev_selected_item + 1, false);
+ }
+
+ void MatrixNotificationsPage::set_room_as_read(RoomData *room) {
+ for(auto &body_item : notifications_body->items) {
+ NotificationsExtraData *extra_data = static_cast<NotificationsExtraData*>(body_item->extra.get());
+ if(!extra_data->read && extra_data->room == room) {
+ extra_data->read = true;
+ body_item->set_author_color(sf::Color::White);
+ body_item->set_description_color(sf::Color::White);
+ }
+ }
+ }
+
static std::array<const char*, 7> sync_fail_error_codes = {
"M_FORBIDDEN",
"M_UNKNOWN_TOKEN",
@@ -1184,24 +1277,17 @@ namespace QuickMedia {
if(initial_sync) {
notification_thread = std::thread([this]() {
- std::vector<CommandArg> additional_args = {
- { "-H", "Authorization: Bearer " + access_token }
- };
-
- // 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];
- snprintf(url, sizeof(url), "%s/_matrix/client/r0/notifications?limit=100&only=highlight", homeserver.c_str());
-
- rapidjson::Document json_root;
- DownloadResult download_result = download_json(json_root, url, std::move(additional_args), true);
- if(download_result != DownloadResult::OK || !json_root.IsObject()) {
- fprintf(stderr, "Fetching notifications failed!\n");
- return;
- }
+ get_previous_notifications([this](const MatrixNotification &notification) {
+ if(notification.read)
+ return;
+
+ MatrixDelegate *delegate = this->delegate;
+ ui_thread_tasks.push([delegate, notification] {
+ delegate->add_unread_notification(std::move(notification));
+ });
+ });
- const rapidjson::Value &notification_json = GetMember(json_root, "notifications");
- parse_notifications(notification_json);
+ finished_fetching_notifications = true;
{
std::vector<CommandArg> additional_args = {
@@ -1288,10 +1374,12 @@ namespace QuickMedia {
delegate = nullptr;
sync_failed = false;
sync_fail_reason.clear();
- set_next_batch("");
+ next_batch.clear();
+ next_notifications_token.clear();
invites.clear();
filter_cached.reset();
my_events_transaction_ids.clear();
+ finished_fetching_notifications = false;
}
bool Matrix::is_initial_sync_finished() const {
@@ -1307,6 +1395,10 @@ namespace QuickMedia {
}
}
+ bool Matrix::has_finished_fetching_notifications() const {
+ return finished_fetching_notifications;
+ }
+
void Matrix::get_room_sync_data(RoomData *room, SyncData &sync_data) {
room->acquire_room_lock();
auto &room_messages = room->get_messages_thread_unsafe();
@@ -1356,6 +1448,45 @@ namespace QuickMedia {
return PluginResult::OK;
}
+ PluginResult Matrix::get_previous_notifications(std::function<void(const MatrixNotification&)> callback_func) {
+ std::vector<CommandArg> additional_args = {
+ { "-H", "Authorization: Bearer " + access_token }
+ };
+
+ std::string from = get_next_notifications_token();
+
+ // 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];
+ if(from.empty())
+ snprintf(url, sizeof(url), "%s/_matrix/client/r0/notifications?limit=100&only=highlight", homeserver.c_str());
+ else
+ snprintf(url, sizeof(url), "%s/_matrix/client/r0/notifications?limit=100&only=highlight&from=%s", homeserver.c_str(), from.c_str());
+
+ rapidjson::Document json_root;
+ DownloadResult download_result = download_json(json_root, url, std::move(additional_args), true);
+ if(download_result != DownloadResult::OK || !json_root.IsObject()) {
+ fprintf(stderr, "Fetching notifications failed!\n");
+ return PluginResult::ERR;
+ }
+
+ const rapidjson::Value &notification_json = GetMember(json_root, "notifications");
+ parse_notifications(notification_json, std::move(callback_func));
+
+ const rapidjson::Value &next_token_json = GetMember(json_root, "next_token");
+ if(next_token_json.IsString())
+ set_next_notifications_token(next_token_json.GetString());
+
+ return PluginResult::OK;
+ }
+
+ void Matrix::get_cached_notifications(std::function<void(const MatrixNotification&)> callback_func) {
+ std::lock_guard<std::mutex> lock(notifications_mutex);
+ for(const auto &notification : notifications) {
+ callback_func(notification);
+ }
+ }
+
PluginResult Matrix::parse_sync_response(const rapidjson::Document &root, bool is_additional_messages_sync, bool initial_sync) {
if(!root.IsObject())
return PluginResult::ERR;
@@ -1371,22 +1502,28 @@ namespace QuickMedia {
return PluginResult::OK;
}
- PluginResult Matrix::parse_notifications(const rapidjson::Value &notifications_json) {
+ PluginResult Matrix::parse_notifications(const rapidjson::Value &notifications_json, std::function<void(const MatrixNotification&)> callback_func) {
if(!notifications_json.IsArray())
return PluginResult::ERR;
+ std::lock_guard<std::mutex> lock(notifications_mutex);
for(const rapidjson::Value &notification_json : notifications_json.GetArray()) {
if(!notification_json.IsObject())
continue;
const rapidjson::Value &read_json = GetMember(notification_json, "read");
- if(!read_json.IsBool() || read_json.GetBool())
+ if(!read_json.IsBool())
continue;
const rapidjson::Value &room_id_json = GetMember(notification_json, "room_id");
if(!room_id_json.IsString())
continue;
+ time_t timestamp = 0;
+ const rapidjson::Value &ts_json = GetMember(notification_json, "ts");
+ if(ts_json.IsInt64())
+ timestamp = ts_json.GetInt64();
+
const rapidjson::Value &event_json = GetMember(notification_json, "event");
if(!event_json.IsObject())
continue;
@@ -1414,12 +1551,15 @@ namespace QuickMedia {
continue;
}
- std::string event_id(event_id_json.GetString(), event_id_json.GetStringLength());
- std::string sender(sender_json.GetString(), sender_json.GetStringLength());
- std::string body(body_json.GetString(), body_json.GetStringLength());
- ui_thread_tasks.push([this, room, event_id{std::move(event_id)}, sender{std::move(sender)}, body{std::move(body)}] {
- delegate->add_unread_notification(room, std::move(event_id), std::move(sender), std::move(body));
- });
+ MatrixNotification notification;
+ notification.room = room;
+ notification.event_id.assign(event_id_json.GetString(), event_id_json.GetStringLength());
+ notification.sender_user_id.assign(sender_json.GetString(), sender_json.GetStringLength());
+ notification.body = remove_reply_formatting(body_json.GetString());
+ notification.timestamp = timestamp;
+ notification.read = read_json.GetBool();
+ callback_func(notification);
+ notifications.push_back(std::move(notification));
}
return PluginResult::OK;
}
@@ -3967,6 +4107,16 @@ namespace QuickMedia {
return next_batch;
}
+ void Matrix::set_next_notifications_token(std::string new_next_token) {
+ std::lock_guard<std::mutex> lock(next_batch_mutex);
+ next_notifications_token = std::move(new_next_token);
+ }
+
+ std::string Matrix::get_next_notifications_token() {
+ std::lock_guard<std::mutex> lock(next_batch_mutex);
+ return next_notifications_token;
+ }
+
void Matrix::clear_sync_cache_for_new_sync() {
std::lock_guard<std::recursive_mutex> room_data_lock(room_data_mutex);
std::lock_guard<std::mutex> invites_lock(invite_mutex);