aboutsummaryrefslogtreecommitdiff
path: root/src
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
parent5b11322c33ef61d3aa8d6bc9c9bc0355f35afa52 (diff)
Add notifications tab to matrix
Diffstat (limited to 'src')
-rw-r--r--src/Body.cpp51
-rw-r--r--src/QuickMedia.cpp128
-rw-r--r--src/SearchBar.cpp4
-rw-r--r--src/plugins/Matrix.cpp212
4 files changed, 311 insertions, 84 deletions
diff --git a/src/Body.cpp b/src/Body.cpp
index 1b1d35a..5a23694 100644
--- a/src/Body.cpp
+++ b/src/Body.cpp
@@ -288,11 +288,12 @@ namespace QuickMedia {
//item_background.set_position(sf::Vector2f(body_pos.x, item_background_target_pos_y));
}
- void Body::select_last_item() {
+ void Body::select_last_item(bool reset_prev_select) {
int new_selected_item = std::max(0, (int)items.size() - 1);
selected_scrolled = 0.0f;
selected_item = new_selected_item;
- //prev_selected_item = selected_item;
+ if(reset_prev_select)
+ prev_selected_item = selected_item;
//page_scroll = 0.0f;
clamp_selection();
clamp_selected_item_to_body_count = 1;
@@ -312,8 +313,8 @@ namespace QuickMedia {
//item_background.set_position(sf::Vector2f(body_pos.x, item_background_target_pos_y));
}
- void Body::prepend_items(BodyItems new_items) {
- items.insert(items.begin(), std::make_move_iterator(new_items.begin()), std::make_move_iterator(new_items.end()));
+ void Body::prepend_items_reverse(BodyItems new_items) {
+ items.insert(items.begin(), std::make_move_iterator(new_items.rbegin()), std::make_move_iterator(new_items.rend()));
items_set_dirty();
}
@@ -1006,30 +1007,32 @@ namespace QuickMedia {
if(body_item->dirty_timestamp) {
body_item->dirty_timestamp = false;
- //time_t time_now = time(NULL);
- //struct tm *now_tm = localtime(&time_now);
+ if(body_item->get_timestamp() != 0) {
+ //time_t time_now = time(NULL);
+ //struct tm *now_tm = localtime(&time_now);
- time_t message_timestamp = body_item->get_timestamp() / 1000;
- struct tm message_tm;
- localtime_r(&message_timestamp, &message_tm);
+ time_t message_timestamp = body_item->get_timestamp() / 1000;
+ struct tm message_tm;
+ localtime_r(&message_timestamp, &message_tm);
- //bool is_same_year = message_tm->tm_year == now_tm->tm_year;
-
- char time_str[128] = {0};
- /*
- if(is_same_year)
- strftime(time_str, sizeof(time_str) - 1, "%a %b %d %H:%M:%S", message_tm);
- else
- strftime(time_str, sizeof(time_str) - 1, "%a %b %d %H:%M:%S %Y", message_tm);
- */
- strftime(time_str, sizeof(time_str) - 1, "%a %b %d %H:%M", &message_tm);
+ //bool is_same_year = message_tm->tm_year == now_tm->tm_year;
+
+ char time_str[128] = {0};
+ /*
+ if(is_same_year)
+ strftime(time_str, sizeof(time_str) - 1, "%a %b %d %H:%M:%S", message_tm);
+ else
+ strftime(time_str, sizeof(time_str) - 1, "%a %b %d %H:%M:%S %Y", message_tm);
+ */
+ strftime(time_str, sizeof(time_str) - 1, "%a %b %d %H:%M", &message_tm);
- if(body_item->timestamp_text)
- body_item->timestamp_text->setString(time_str);
- else
- body_item->timestamp_text = std::make_unique<sf::Text>(time_str, *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(10 * get_ui_scale()));
+ if(body_item->timestamp_text)
+ body_item->timestamp_text->setString(time_str);
+ else
+ body_item->timestamp_text = std::make_unique<sf::Text>(time_str, *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(10 * get_ui_scale()));
- body_item->timestamp_text->setFillColor(sf::Color(185, 190, 198, 100));
+ body_item->timestamp_text->setFillColor(sf::Color(185, 190, 198, 100));
+ }
}
}
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index a9956d3..13361c6 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -1482,6 +1482,15 @@ namespace QuickMedia {
window.draw(tab_associated_data.search_result_text);
}
+ if(!tabs[selected_tab].page->is_ready()) {
+ sf::Text loading_text("Loading...", *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(30 * get_ui_scale()));
+ auto text_bounds = loading_text.getLocalBounds();
+ loading_text.setPosition(
+ std::floor(body_pos.x + body_size.x * 0.5f - text_bounds.width * 0.5f),
+ std::floor(body_pos.y + body_size.y * 0.5f - text_bounds.height * 0.5f));
+ window.draw(loading_text);
+ }
+
if(matrix)
matrix->update();
@@ -1518,6 +1527,8 @@ namespace QuickMedia {
bool redraw = true;
for(Tab &tab : tabs) {
+ if(tab.body->attach_side == AttachSide::BOTTOM)
+ tab.body->select_last_item();
tab.body->thumbnail_max_size = tab.page->get_thumbnail_max_size();
tab.page->on_navigate_to_page(tab.body.get());
}
@@ -1591,9 +1602,9 @@ namespace QuickMedia {
tabs[selected_tab].search_bar->clear();
tabs[selected_tab].search_bar->onTextUpdateCallback("");
} else {
- int selected_item_index = tabs[selected_tab].body->get_selected_item();
- tabs[selected_tab].body->select_first_item();
- tabs[selected_tab].body->set_selected_item(selected_item_index, false);
+ //int selected_item_index = tabs[selected_tab].body->get_selected_item();
+ //tabs[selected_tab].body->select_first_item();
+ //tabs[selected_tab].body->set_selected_item(selected_item_index, false);
}
}
@@ -1716,13 +1727,14 @@ namespace QuickMedia {
hide_virtual_keyboard();
};
- std::function<void()> on_bottom_reached = [&ui_tabs, &tabs, &tab_associated_data, &gradient_inc] {
+ std::function<void()> on_reached_end = [&ui_tabs, &tabs, &tab_associated_data, &gradient_inc] {
const int selected_tab = ui_tabs.get_selected();
if(tab_associated_data[selected_tab].fetch_status == FetchStatus::NONE
&& !tab_associated_data[selected_tab].fetching_next_page_running
&& !tab_associated_data[selected_tab].fetching_next_page_failed
- && !tabs[selected_tab].body->items.empty()
- && tabs[selected_tab].page
+ && (!tabs[selected_tab].search_bar || tabs[selected_tab].search_bar->is_empty())
+ && tabs[selected_tab].body->get_num_visible_items() > 0
+ && tabs[selected_tab].page->is_ready()
&& (!tabs[selected_tab].page->is_lazy_fetch_page() || tab_associated_data[selected_tab].lazy_fetch_finished))
{
gradient_inc = 0.0;
@@ -1746,7 +1758,10 @@ namespace QuickMedia {
submit_handler(body_item->get_title());
};
- tab.body->on_bottom_reached = on_bottom_reached;
+ if(tab.body->attach_side == AttachSide::TOP)
+ tab.body->on_bottom_reached = on_reached_end;
+ else if(tab.body->attach_side == AttachSide::BOTTOM)
+ tab.body->on_top_reached = on_reached_end;
TabAssociatedData &associated_data = tab_associated_data[i];
if(tab.search_bar) {
@@ -1762,7 +1777,10 @@ namespace QuickMedia {
associated_data.search_text_updated = true;
} else {
tabs[i].body->filter_search_fuzzy(text);
- tabs[i].body->select_first_item();
+ if(tabs[i].body->attach_side == AttachSide::TOP)
+ tabs[i].body->select_first_item();
+ else if(tabs[i].body->attach_side == AttachSide::BOTTOM)
+ tabs[i].body->select_last_item();
}
associated_data.typing = false;
};
@@ -1816,7 +1834,7 @@ namespace QuickMedia {
}
} else if(event.key.code == sf::Keyboard::T && event.key.control) {
BodyItem *selected_item = tabs[selected_tab].body->get_selected();
- if(selected_item && tabs[selected_tab].page && tabs[selected_tab].page->is_trackable()) {
+ if(selected_item && tabs[selected_tab].page->is_trackable()) {
TrackablePage *trackable_page = dynamic_cast<TrackablePage*>(tabs[selected_tab].page.get());
run_task_with_loading_screen([trackable_page, selected_item](){
return trackable_page->track(selected_item->get_title()) == TrackResult::OK;
@@ -1857,17 +1875,31 @@ namespace QuickMedia {
// TODO: Dont show tabs if there is only one tab
get_body_dimensions(window_size, tabs[selected_tab].search_bar.get(), body_pos, body_size, true);
- gradient_points[0].position.x = 0.0f;
- gradient_points[0].position.y = window_size.y - gradient_height;
+ if(tabs[selected_tab].body->attach_side == AttachSide::TOP) {
+ gradient_points[0].position.x = 0.0f;
+ gradient_points[0].position.y = window_size.y - gradient_height;
- gradient_points[1].position.x = window_size.x;
- gradient_points[1].position.y = window_size.y - gradient_height;
+ gradient_points[1].position.x = window_size.x;
+ gradient_points[1].position.y = window_size.y - gradient_height;
- gradient_points[2].position.x = window_size.x;
- gradient_points[2].position.y = window_size.y;
+ gradient_points[2].position.x = window_size.x;
+ gradient_points[2].position.y = window_size.y;
+
+ gradient_points[3].position.x = 0.0f;
+ gradient_points[3].position.y = window_size.y;
+ } else if(tabs[selected_tab].body->attach_side == AttachSide::BOTTOM) {
+ gradient_points[0].position.x = 0.0f;
+ gradient_points[0].position.y = body_pos.y;
- gradient_points[3].position.x = 0.0f;
- gradient_points[3].position.y = window_size.y;
+ gradient_points[1].position.x = window_size.x;
+ gradient_points[1].position.y = body_pos.y;
+
+ gradient_points[2].position.x = window_size.x;
+ gradient_points[2].position.y = body_pos.y + gradient_height;
+
+ gradient_points[3].position.x = 0.0f;
+ gradient_points[3].position.y = body_pos.y + gradient_height;
+ }
}
if(tab_associated_data[selected_tab].fetching_next_page_running) {
@@ -1875,15 +1907,22 @@ namespace QuickMedia {
gradient_inc += (frame_time_ms * 0.5);
sf::Color bottom_color = interpolate_colors(back_color, sf::Color(175, 180, 188), progress);
- gradient_points[0].color = back_color;
- gradient_points[1].color = back_color;
- gradient_points[2].color = bottom_color;
- gradient_points[3].color = bottom_color;
+ if(tabs[selected_tab].body->attach_side == AttachSide::TOP) {
+ gradient_points[0].color = back_color;
+ gradient_points[1].color = back_color;
+ gradient_points[2].color = bottom_color;
+ gradient_points[3].color = bottom_color;
+ } else if(tabs[selected_tab].body->attach_side == AttachSide::BOTTOM) {
+ gradient_points[0].color = bottom_color;
+ gradient_points[1].color = bottom_color;
+ gradient_points[2].color = back_color;
+ gradient_points[3].color = back_color;
+ }
}
if(tabs[selected_tab].search_bar) tabs[selected_tab].search_bar->update();
- if(tabs[selected_tab].page->is_lazy_fetch_page() && tab_associated_data[selected_tab].fetch_status == FetchStatus::NONE && !tab_associated_data[selected_tab].lazy_fetch_finished) {
+ if(tabs[selected_tab].page->is_ready() && tabs[selected_tab].page->is_lazy_fetch_page() && tab_associated_data[selected_tab].fetch_status == FetchStatus::NONE && !tab_associated_data[selected_tab].lazy_fetch_finished) {
tab_associated_data[selected_tab].fetch_status = FetchStatus::LOADING;
tab_associated_data[selected_tab].fetch_type = FetchType::LAZY;
tab_associated_data[selected_tab].search_result_text.setString("Loading...");
@@ -1897,18 +1936,33 @@ namespace QuickMedia {
for(size_t i = 0; i < tabs.size(); ++i) {
TabAssociatedData &associated_data = tab_associated_data[i];
+ if(!tabs[i].page->is_ready())
+ continue;
if(associated_data.fetching_next_page_running && associated_data.next_page_future.ready()) {
+ const bool body_was_empty = tabs[i].body->items.empty();
BodyItems new_body_items = associated_data.next_page_future.get();
fprintf(stderr, "Finished fetching page %d, num new items: %zu\n", associated_data.fetched_page + 1, new_body_items.size());
+ int prev_selected_item = tabs[i].body->get_selected_item();
size_t num_new_messages = new_body_items.size();
if(num_new_messages > 0) {
- tabs[i].body->append_items(std::move(new_body_items));
+ if(tabs[i].body->attach_side == AttachSide::TOP)
+ tabs[i].body->append_items(std::move(new_body_items));
+ else if(tabs[i].body->attach_side == AttachSide::BOTTOM)
+ tabs[i].body->prepend_items_reverse(std::move(new_body_items));
associated_data.fetched_page++;
} else {
associated_data.fetching_next_page_failed = true;
}
associated_data.fetching_next_page_running = false;
+
+ if(tabs[i].body->attach_side == AttachSide::BOTTOM) {
+ if(body_was_empty) {
+ tabs[i].body->select_last_item();
+ } else {
+ tabs[i].body->set_selected_item(prev_selected_item + num_new_messages, true);
+ }
+ }
}
if(associated_data.search_text_updated && associated_data.fetch_status == FetchStatus::NONE && !associated_data.fetching_next_page_running) {
@@ -1929,7 +1983,12 @@ namespace QuickMedia {
if(!associated_data.search_text_updated) {
FetchResult fetch_result = associated_data.fetch_future.get();
tabs[i].body->items = std::move(fetch_result.body_items);
- tabs[i].body->select_first_item();
+ if(tabs[i].body->attach_side == AttachSide::TOP) {
+ tabs[i].body->select_first_item();
+ } else if(tabs[i].body->attach_side == AttachSide::BOTTOM) {
+ std::reverse(tabs[i].body->items.begin(), tabs[i].body->items.end());
+ tabs[i].body->select_last_item();
+ }
associated_data.fetched_page = 0;
associated_data.fetching_next_page_failed = false;
if(fetch_result.result != PluginResult::OK)
@@ -1949,6 +2008,10 @@ namespace QuickMedia {
FetchResult fetch_result = associated_data.fetch_future.get();
tabs[i].body->items = std::move(fetch_result.body_items);
if(tabs[i].search_bar) tabs[i].body->filter_search_fuzzy(tabs[i].search_bar->get_text());
+ if(tabs[i].body->attach_side == AttachSide::BOTTOM) {
+ std::reverse(tabs[i].body->items.begin(), tabs[i].body->items.end());
+ tabs[i].body->select_last_item();
+ }
LazyFetchPage *lazy_fetch_page = static_cast<LazyFetchPage*>(tabs[i].page.get());
if(fetch_result.result != PluginResult::OK)
associated_data.search_result_text.setString("Failed to fetch page!");
@@ -1984,8 +2047,12 @@ namespace QuickMedia {
AsyncImageLoader::get_instance().update();
window.display();
- if(!tabs[selected_tab].body->items.empty() && tabs[selected_tab].body->is_last_item_fully_visible())
- on_bottom_reached();
+ if(!tabs[selected_tab].body->items.empty()) {
+ if(tabs[selected_tab].body->attach_side == AttachSide::TOP && tabs[selected_tab].body->is_last_item_fully_visible())
+ on_reached_end();
+ else if(tabs[selected_tab].body->attach_side == AttachSide::BOTTOM && tabs[selected_tab].body->is_first_item_fully_visible())
+ on_reached_end();
+ }
if(go_to_previous_page) {
go_to_previous_page = false;
@@ -3373,7 +3440,7 @@ namespace QuickMedia {
//thread_body->clamp_selection();
//thread_body->set_page_scroll(0.0f);
int prev_sel = thread_body->get_selected_item();
- thread_body->select_last_item();
+ thread_body->select_first_item();
thread_body->set_selected_item(prev_sel, false);
} else if(event.key.code == sf::Keyboard::BackSpace && !comment_navigation_stack.empty()) {
size_t previous_selected = comment_navigation_stack.top();
@@ -5692,6 +5759,8 @@ namespace QuickMedia {
// TODO: Maybe set this instead when the mention is visible on the screen?
current_room->unread_notification_count = 0;
+ matrix_chat_page->set_room_as_read(current_room);
+
Message *read_message = last_visible_timeline_message;
if(read_message->replaced_by)
read_message = read_message->replaced_by.get();
@@ -5825,7 +5894,8 @@ namespace QuickMedia {
exit(exit_code);
auto notifications_body = create_body();
- auto matrix_notifications_page = std::make_unique<MatrixNotificationsPage>(this, notifications_body.get());
+ //notifications_body->attach_side = AttachSide::BOTTOM;
+ auto matrix_notifications_page = std::make_unique<MatrixNotificationsPage>(this, matrix, notifications_body.get());
auto rooms_tags_body = create_body();
auto matrix_rooms_tag_page = std::make_unique<MatrixRoomTagsPage>(this, rooms_tags_body.get());
@@ -5854,7 +5924,7 @@ namespace QuickMedia {
add_body_item_unique_title(room_directory_body->items, "jupiterbroadcasting.com");
auto matrix_room_directory_page = std::make_unique<MatrixRoomDirectoryPage>(this, matrix);
- MatrixQuickMedia matrix_handler(this, matrix, matrix_rooms_page.get(), matrix_rooms_tag_page.get(), matrix_invites_page.get());
+ MatrixQuickMedia matrix_handler(this, matrix, matrix_rooms_page.get(), matrix_rooms_tag_page.get(), matrix_invites_page.get(), matrix_notifications_page.get());
bool sync_cached = false;
if(!matrix->start_sync(&matrix_handler, sync_cached)) {
show_notification("QuickMedia", "Failed to start sync", Urgency::CRITICAL);
diff --git a/src/SearchBar.cpp b/src/SearchBar.cpp
index a7e3890..c0a8aad 100644
--- a/src/SearchBar.cpp
+++ b/src/SearchBar.cpp
@@ -358,4 +358,8 @@ namespace QuickMedia {
return "";
return text.getString();
}
+
+ bool SearchBar::is_empty() const {
+ return show_placeholder;
+ }
} \ No newline at end of file
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);