aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/QuickMedia.cpp76
-rw-r--r--src/plugins/Matrix.cpp54
2 files changed, 90 insertions, 40 deletions
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 90abfcc..0021f96 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -2967,22 +2967,44 @@ namespace QuickMedia {
RoomData *current_room = nullptr;
bool is_window_focused = window.hasFocus();
- auto process_new_room_messages = [this, &selected_tab, &current_room, &is_window_focused](RoomSyncMessages &room_sync_messages, bool is_first_sync) mutable {
- for(auto &[room, messages] : room_sync_messages) {
- if(messages.empty())
- continue;
+ // Returns -1 if no rooms or no unread rooms
+ auto find_top_body_position_for_unread_room = [&tabs](BodyItem *item_to_swap, int start_index) {
+ for(int i = start_index; i < (int)tabs[ROOMS_TAB_INDEX].body->items.size(); ++i) {
+ const auto &body_item = tabs[ROOMS_TAB_INDEX].body->items[i];
+ if(static_cast<RoomData*>(body_item->userdata)->last_message_read || body_item.get() == item_to_swap)
+ return i;
+ }
+ return -1;
+ };
- bool was_mentioned = false;
+ // Returns -1 if no rooms or all rooms have unread mentions
+ auto find_top_body_position_for_mentioned_room = [&tabs](BodyItem *item_to_swap, int start_index) {
+ for(int i = start_index; i < (int)tabs[ROOMS_TAB_INDEX].body->items.size(); ++i) {
+ const auto &body_item = tabs[ROOMS_TAB_INDEX].body->items[i];
+ if(!static_cast<RoomData*>(body_item->userdata)->has_unread_mention || body_item.get() == item_to_swap)
+ return i;
+ }
+ return -1;
+ };
+
+ auto process_new_room_messages =
+ [this, &selected_tab, &current_room, &is_window_focused, &tabs, &find_top_body_position_for_unread_room, &find_top_body_position_for_mentioned_room]
+ (RoomSyncMessages &room_sync_messages, bool is_first_sync) mutable
+ {
+ for(auto &[room, messages] : room_sync_messages) {
for(auto &message : messages) {
if(message->mentions_me) {
- was_mentioned = true;
room->has_unread_mention = 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
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);
}
}
+ }
+
+ for(auto &[room, messages] : room_sync_messages) {
+ if(messages.empty())
+ continue;
BodyItem *room_body_item = static_cast<BodyItem*>(room->userdata);
assert(room_body_item);
@@ -2990,27 +3012,41 @@ namespace QuickMedia {
// TODO: this wont always work because we dont display all types of messages from server, such as "joined", "left", "kicked", "banned", "changed avatar", "changed display name", etc.
bool unread_messages = false;
std::shared_ptr<UserInfo> me = matrix->get_me(room);
- if(me && room->get_user_read_marker(me) != messages.back()->event_id)
+ if(room->has_unread_mention || (me && room->get_user_read_marker(me) != messages.back()->event_id))
unread_messages = true;
if(unread_messages) {
std::string room_desc = "Unread: " + matrix->message_get_author_displayname(messages.back().get()) + ": " + extract_first_line(messages.back()->body, 150);
- if(was_mentioned)
+ if(room->has_unread_mention)
room_desc += "\n** You were mentioned **"; // TODO: Better notification?
room_body_item->set_description(std::move(room_desc));
room_body_item->set_title_color(sf::Color(255, 100, 100));
room->last_message_read = false;
- // Swap room with the top one
+ // Swap order of rooms in body list to put rooms with mentions at the top and then unread messages and then all the other rooms
// TODO: Optimize with hash map instead of linear search? or cache the index
- // int room_body_index = tabs[ROOMS_TAB_INDEX].body->get_index_by_body_item(room_body_item);
- // if(room_body_index != -1) {
- // std::swap(tabs[ROOMS_TAB_INDEX].body->items[room_body_index], tabs[ROOMS_TAB_INDEX].body->items[room_swap_index]);
- // ++room_swap_index;
- // }
+ int room_body_index = tabs[ROOMS_TAB_INDEX].body->get_index_by_body_item(room_body_item);
+ if(room_body_index != -1) {
+ int body_swap_index = 0;
+ while(true) {
+ BodyItem *body_item = tabs[ROOMS_TAB_INDEX].body->items[room_body_index].get();
+ RoomData *room = static_cast<RoomData*>(body_item->userdata);
+ if(room->has_unread_mention)
+ body_swap_index = find_top_body_position_for_mentioned_room(body_item, body_swap_index);
+ else if(!room->last_message_read)
+ body_swap_index = find_top_body_position_for_unread_room(body_item, body_swap_index);
+ else
+ break;
+
+ if(body_swap_index == -1)
+ break;
+
+ std::swap(tabs[ROOMS_TAB_INDEX].body->items[room_body_index], tabs[ROOMS_TAB_INDEX].body->items[body_swap_index]);
+ ++body_swap_index;
+ }
+ }
} else if(is_first_sync) {
room_body_item->set_description(matrix->message_get_author_displayname(messages.back().get()) + ": " + extract_first_line(messages.back()->body, 150));
- room->has_unread_mention = false;
}
}
};
@@ -3184,8 +3220,6 @@ namespace QuickMedia {
}
}
- tabs[selected_tab].body->select_last_item();
-
if(chat_state == ChatState::TYPING_MESSAGE) {
// TODO: Make asynchronous
if(matrix->post_message(current_room, text, std::nullopt, std::nullopt) == PluginResult::OK) {
@@ -3346,9 +3380,11 @@ namespace QuickMedia {
}
};
- auto add_new_messages_to_current_room = [this, &tabs, &current_room](Messages &messages) {
+ auto add_new_messages_to_current_room = [this, &tabs, &selected_tab, &current_room, &is_window_focused](Messages &messages) {
int num_items = tabs[MESSAGES_TAB_INDEX].body->items.size();
- bool scroll_to_end = (num_items == 0 || tabs[MESSAGES_TAB_INDEX].body->is_selected_item_last_visible_item());
+ bool scroll_to_end = num_items == 0;
+ if(tabs[MESSAGES_TAB_INDEX].body->is_selected_item_last_visible_item() && selected_tab == MESSAGES_TAB_INDEX && is_window_focused)
+ scroll_to_end = true;
BodyItem *selected_item = tabs[MESSAGES_TAB_INDEX].body->get_selected();
tabs[MESSAGES_TAB_INDEX].body->insert_items_by_timestamps(messages_to_body_items(messages, matrix->get_me(current_room).get()));
diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp
index ff0d498..7de8af9 100644
--- a/src/plugins/Matrix.cpp
+++ b/src/plugins/Matrix.cpp
@@ -55,7 +55,7 @@ namespace QuickMedia {
return user->read_marker_event_id;
}
- void RoomData::prepend_messages_reverse(std::vector<std::shared_ptr<Message>> new_messages) {
+ void RoomData::prepend_messages_reverse(const std::vector<std::shared_ptr<Message>> &new_messages) {
std::lock_guard<std::mutex> lock(room_mutex);
for(auto it = new_messages.begin(); it != new_messages.end(); ++it) {
if(message_by_event_id.find((*it)->event_id) == message_by_event_id.end()) {
@@ -65,7 +65,7 @@ namespace QuickMedia {
}
}
- void RoomData::append_messages(std::vector<std::shared_ptr<Message>> new_messages) {
+ void RoomData::append_messages(const std::vector<std::shared_ptr<Message>> &new_messages) {
std::lock_guard<std::mutex> lock(room_mutex);
for(auto it = new_messages.begin(); it != new_messages.end(); ++it) {
if(message_by_event_id.find((*it)->event_id) == message_by_event_id.end()) {
@@ -208,6 +208,8 @@ namespace QuickMedia {
events_set_room_name(events_json, room);
}
+ const rapidjson::Value &ephemeral_json = GetMember(it.value, "ephemeral");
+
const rapidjson::Value &timeline_json = GetMember(it.value, "timeline");
if(timeline_json.IsObject()) {
if(room->prev_batch.empty()) {
@@ -217,7 +219,7 @@ namespace QuickMedia {
room->prev_batch = prev_batch_json.GetString();
}
- // TODO: Is there no better way to check for notifications? this is not robust...
+ // TODO: Use /_matrix/client/r0/notifications ? or remove this and always look for displayname/user_id in messages
bool has_unread_notifications = false;
const rapidjson::Value &unread_notification_json = GetMember(it.value, "unread_notifications");
if(unread_notification_json.IsObject()) {
@@ -228,14 +230,18 @@ namespace QuickMedia {
const rapidjson::Value &events_json = GetMember(timeline_json, "events");
events_add_user_info(events_json, room);
- events_add_messages(events_json, room, MessageDirection::AFTER, &room_messages, has_unread_notifications);
events_set_room_name(events_json, room);
- }
-
- const rapidjson::Value &ephemeral_json = GetMember(it.value, "ephemeral");
- if(ephemeral_json.IsObject()) {
- const rapidjson::Value &events_json = GetMember(ephemeral_json, "events");
- events_add_user_read_markers(events_json, room);
+ // We want to do this before adding messages to know if a message that mentions us is a new mention
+ if(ephemeral_json.IsObject()) {
+ const rapidjson::Value &events_json = GetMember(ephemeral_json, "events");
+ events_add_user_read_markers(events_json, room);
+ }
+ events_add_messages(events_json, room, MessageDirection::AFTER, &room_messages, has_unread_notifications);
+ } else {
+ if(ephemeral_json.IsObject()) {
+ const rapidjson::Value &events_json = GetMember(ephemeral_json, "events");
+ events_add_user_read_markers(events_json, room);
+ }
}
}
@@ -457,25 +463,33 @@ namespace QuickMedia {
for(const rapidjson::Value &event_item_json : events_json.GetArray()) {
std::shared_ptr<Message> new_message = parse_message_event(event_item_json, room_data);
- if(!new_message)
- continue;
-
- // 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 && me)
- new_message->mentions_me = message_contains_user_mention(new_message->body, me->display_name) || message_contains_user_mention(new_message->body, me->user_id) || message_contains_user_mention(new_message->body, "@room");
-
- new_messages.push_back(std::move(new_message));
+ if(new_message)
+ new_messages.push_back(std::move(new_message));
}
+ if(new_messages.empty())
+ return;
+
// TODO: Add directly to this instead when set? otherwise add to new_messages
if(room_messages)
(*room_messages)[room_data] = new_messages;
// TODO: Loop and std::move instead? doesn't insert create copies?
if(message_dir == MessageDirection::BEFORE) {
- room_data->prepend_messages_reverse(std::move(new_messages));
+ room_data->prepend_messages_reverse(new_messages);
} else if(message_dir == MessageDirection::AFTER) {
- room_data->append_messages(std::move(new_messages));
+ room_data->append_messages(new_messages);
+ }
+
+ std::shared_ptr<Message> read_marker_message;
+ if(me)
+ read_marker_message = room_data->get_message_by_id(room_data->get_user_read_marker(me));
+
+ for(auto &message : new_messages) {
+ // TODO: Is @room ok? shouldn't we also check if the user has permission to do @room? (only when notifications are limited to @mentions)
+ // TODO: Is comparing against read marker timestamp ok enough?
+ if(has_unread_notifications && me && (!read_marker_message || read_marker_message->timestamp < message->timestamp))
+ message->mentions_me = message_contains_user_mention(message->body, me->display_name) || message_contains_user_mention(message->body, me->user_id) || message_contains_user_mention(message->body, "@room");
}
}