aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO3
-rw-r--r--plugins/Matrix.hpp5
-rw-r--r--src/QuickMedia.cpp33
-rw-r--r--src/Text.cpp37
-rw-r--r--src/plugins/Matrix.cpp130
5 files changed, 130 insertions, 78 deletions
diff --git a/TODO b/TODO
index 676147a..7df55ec 100644
--- a/TODO
+++ b/TODO
@@ -86,4 +86,5 @@ Save the original event message, so when replying for example we can use the ori
Remove tidy dependency and use my own html-parser.
Add option to sort by other than timestamp for nyaa.si.
Add url preview for matrix (using matrix api, fallback to client url preview (using our own url preview project)).
-Cleanup old messages in matrix (from matrix plugin), and instead either save them to disk or refetch them from server when going up to read old messages. \ No newline at end of file
+Cleanup old messages in matrix (from matrix plugin), and instead either save them to disk or refetch them from server when going up to read old messages.
+Use memberName() instead of key() when iterating json object. key() creates a copy, memberName() doesn't. \ No newline at end of file
diff --git a/plugins/Matrix.hpp b/plugins/Matrix.hpp
index fae6f28..be4a59c 100644
--- a/plugins/Matrix.hpp
+++ b/plugins/Matrix.hpp
@@ -11,6 +11,7 @@ namespace QuickMedia {
std::string display_name;
std::string avatar_url;
sf::Color display_name_color;
+ std::string read_marker_event_id;
};
enum class MessageType {
@@ -111,10 +112,14 @@ namespace QuickMedia {
// Cached
PluginResult get_config(int *upload_size);
+
+ // Note: the returned UserInfo is o
+ const UserInfo* get_me(const std::string &room_id) const;
private:
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_user_read_markers(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, bool has_unread_notifications);
void events_set_room_name(const Json::Value &events_json, RoomData *room_data);
PluginResult upload_file(const std::string &room_id, const std::string &filepath, UploadInfo &file_info, UploadInfo &thumbnail_info, std::string &err_msg);
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 2607873..50b0608 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -3362,13 +3362,6 @@ namespace QuickMedia {
const int MESSAGES_TAB_INDEX = 0;
const int ROOMS_TAB_INDEX = 1;
- /*
- if(matrix->get_cached_sync(tabs[MESSAGES_TAB_INDEX].body->items) != PluginResult::OK) {
- fprintf(stderr, "Failed to get matrix cached sync\n");
- } else {
- fprintf(stderr, "Loaded matrix sync from cache, num items: %zu\n", tabs[MESSAGES_TAB_INDEX].body->items.size());
- }
- */
// This is needed to get initial data, with joined rooms etc. TODO: Remove this once its cached
// and allow asynchronous update of rooms
bool synced = false;
@@ -3398,7 +3391,15 @@ namespace QuickMedia {
if(room_body_item_it == body_items_by_room_id.end())
continue;
- if(only_show_mentions) {
+ // TODO: this wont always 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_previous_session = false;
+ if(!messages.empty()) {
+ const UserInfo *me = matrix->get_me(room->id);
+ if(me->read_marker_event_id != messages.back()->event_id)
+ unread_messages_previous_session = true;
+ }
+
+ if(only_show_mentions && !unread_messages_previous_session) {
std::string room_desc;
if(!messages.empty())
room_desc = matrix->message_get_author_displayname(room, messages.back().get()) + ": " + extract_first_line(messages.back()->body, 150);
@@ -3419,7 +3420,6 @@ namespace QuickMedia {
}
};
- // 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;
RoomBodyData *current_room_body_data = nullptr;
@@ -3586,7 +3586,8 @@ 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;
+ const sf::Int32 read_marker_timeout_ms_default = 3000;
+ sf::Int32 read_marker_timeout_ms = 0;
std::future<void> set_read_marker_future;
bool setting_read_marker = false;
@@ -3891,6 +3892,7 @@ namespace QuickMedia {
room_avatar_thumbnail_data = std::make_shared<ThumbnailData>();
}
+ read_marker_timeout_ms = 0;
redraw = true;
}
}
@@ -4068,15 +4070,14 @@ namespace QuickMedia {
tabs[MESSAGES_TAB_INDEX].body->select_last_item();
}
+ // Initial sync
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;
@@ -4204,9 +4205,9 @@ namespace QuickMedia {
tabs[MESSAGES_TAB_INDEX].body->draw_item(window, currently_operating_on_item.get(), body_item_pos, body_item_size);
}
- if(tabs[selected_tab].type == ChatTabType::MESSAGES) {
+ if(tabs[selected_tab].type == ChatTabType::MESSAGES && current_room_body_data) {
if(tabs[selected_tab].body->is_last_item_fully_visible()) {
- if(current_room_body_data && !current_room_body_data->last_message_read) {
+ if(!current_room_body_data->last_message_read) {
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);
@@ -4217,7 +4218,7 @@ namespace QuickMedia {
current_room_body_data->body_item->title_color = sf::Color::White;
current_room_body_data->last_message_read = true;
}
- } else if(current_room_body_data && !current_room_body_data->last_message_read) {
+ } else if(!current_room_body_data->last_message_read) {
window.draw(more_messages_below_rect);
}
}
@@ -4233,7 +4234,9 @@ namespace QuickMedia {
BodyItem *last_visible_item = tabs[selected_tab].body->get_last_fully_visible_item();
if(is_window_focused && chat_state != ChatState::URL_SELECTION && current_room_body_data && last_visible_item && !setting_read_marker && read_marker_timer.getElapsedTime().asMilliseconds() >= read_marker_timeout_ms) {
Message *message = (Message*)last_visible_item->userdata;
+ // TODO: What if two messages have the same timestamp?
if(message->timestamp > current_room_body_data->last_read_message_timestamp) {
+ read_marker_timeout_ms = read_marker_timeout_ms_default;
current_room_body_data->last_read_message_timestamp = message->timestamp;
// TODO: What if the message is no longer valid?
setting_read_marker = true;
diff --git a/src/Text.cpp b/src/Text.cpp
index e311f14..d58473d 100644
--- a/src/Text.cpp
+++ b/src/Text.cpp
@@ -298,8 +298,7 @@ namespace QuickMedia
vertices_index = 1;
}
- usize vertexOffset = vertices[vertices_index].getVertexCount();
- vertices[vertices_index].resize(vertices[vertices_index].getVertexCount() + 4 * textElement.text.size); // TODO: Precalculate
+ //vertices[vertices_index].resize(vertices[vertices_index].getVertexCount() + 4 * textElement.text.size); // TODO: Precalculate
textElement.position = glyphPos;
for(size_t i = 0; i < textElement.text.size; ++i)
{
@@ -310,16 +309,16 @@ namespace QuickMedia
prevCodePoint = codePoint;
glyphPos.x += kerning;
- int vertexStart = vertexOffset + i * 4;
+ int vertexStart = vertices[vertices_index].getVertexCount();
switch(codePoint)
{
case ' ':
{
- vertices[vertices_index][vertexStart + 0] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
- vertices[vertices_index][vertexStart + 1] = { sf::Vector2f(glyphPos.x + hspace, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
- vertices[vertices_index][vertexStart + 2] = { sf::Vector2f(glyphPos.x + hspace, glyphPos.y), sf::Color::Transparent, sf::Vector2f() };
- vertices[vertices_index][vertexStart + 3] = { sf::Vector2f(glyphPos.x, glyphPos.y), sf::Color::Transparent, sf::Vector2f() };
+ vertices[vertices_index].append({ sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() });
+ vertices[vertices_index].append({ sf::Vector2f(glyphPos.x + hspace, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() });
+ vertices[vertices_index].append({ sf::Vector2f(glyphPos.x + hspace, glyphPos.y), sf::Color::Transparent, sf::Vector2f() });
+ vertices[vertices_index].append({ sf::Vector2f(glyphPos.x, glyphPos.y), sf::Color::Transparent, sf::Vector2f() });
glyphPos.x += hspace;
vertices_linear.push_back({vertices_index, vertexStart, 0, codePoint});
continue;
@@ -327,20 +326,20 @@ namespace QuickMedia
case '\t':
{
const float char_width = hspace * TAB_WIDTH;
- vertices[vertices_index][vertexStart + 0] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
- vertices[vertices_index][vertexStart + 1] = { sf::Vector2f(glyphPos.x + char_width, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
- vertices[vertices_index][vertexStart + 2] = { sf::Vector2f(glyphPos.x + char_width, glyphPos.y), sf::Color::Transparent, sf::Vector2f() };
- vertices[vertices_index][vertexStart + 3] = { sf::Vector2f(glyphPos.x, glyphPos.y), sf::Color::Transparent, sf::Vector2f() };
+ vertices[vertices_index].append({ sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() });
+ vertices[vertices_index].append({ sf::Vector2f(glyphPos.x + char_width, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() });
+ vertices[vertices_index].append({ sf::Vector2f(glyphPos.x + char_width, glyphPos.y), sf::Color::Transparent, sf::Vector2f() });
+ vertices[vertices_index].append({ sf::Vector2f(glyphPos.x, glyphPos.y), sf::Color::Transparent, sf::Vector2f() });
glyphPos.x += char_width;
vertices_linear.push_back({vertices_index, vertexStart, 0, codePoint});
continue;
}
case '\n':
{
- vertices[vertices_index][vertexStart + 0] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
- vertices[vertices_index][vertexStart + 1] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
- vertices[vertices_index][vertexStart + 2] = { sf::Vector2f(glyphPos.x, glyphPos.y), sf::Color::Transparent, sf::Vector2f() };
- vertices[vertices_index][vertexStart + 3] = { sf::Vector2f(glyphPos.x, glyphPos.y), sf::Color::Transparent, sf::Vector2f() };
+ vertices[vertices_index].append({ sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() });
+ vertices[vertices_index].append({ sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() });
+ vertices[vertices_index].append({ sf::Vector2f(glyphPos.x, glyphPos.y), sf::Color::Transparent, sf::Vector2f() });
+ vertices[vertices_index].append({ sf::Vector2f(glyphPos.x, glyphPos.y), sf::Color::Transparent, sf::Vector2f() });
glyphPos.x = 0.0f;
glyphPos.y += floor(vspace + lineSpacing);
vertices_linear.push_back({vertices_index, vertexStart, 0, codePoint});
@@ -362,10 +361,10 @@ namespace QuickMedia
sf::Color fontColor = (textElement.type == TextElement::Type::TEXT ? color : urlColor);
- vertices[vertices_index][vertexStart + 0] = { vertexTopLeft, fontColor, textureTopLeft };
- vertices[vertices_index][vertexStart + 1] = { vertexTopRight, fontColor, textureTopRight };
- vertices[vertices_index][vertexStart + 2] = { vertexBottomRight, fontColor, textureBottomRight };
- vertices[vertices_index][vertexStart + 3] = { vertexBottomLeft, fontColor, textureBottomLeft };
+ vertices[vertices_index].append({ vertexTopLeft, fontColor, textureTopLeft });
+ vertices[vertices_index].append({ vertexTopRight, fontColor, textureTopRight });
+ vertices[vertices_index].append({ vertexBottomRight, fontColor, textureBottomRight });
+ vertices[vertices_index].append({ vertexBottomLeft, fontColor, textureBottomLeft });
glyphPos.x += glyph.advance + characterSpacing;
vertices_linear.push_back({vertices_index, vertexStart, 0, codePoint});
diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp
index 69c2686..ec7064b 100644
--- a/src/plugins/Matrix.cpp
+++ b/src/plugins/Matrix.cpp
@@ -239,56 +239,41 @@ namespace QuickMedia {
}
const Json::Value &state_json = (*it)["state"];
- if(!state_json.isObject())
- continue;
-
- const Json::Value &events_json = state_json["events"];
- events_add_user_info(events_json, room_it->second.get());
- events_set_room_name(events_json, room_it->second.get());
- }
-
- for(Json::Value::const_iterator it = join_json.begin(); it != join_json.end(); ++it) {
- if(!it->isObject())
- continue;
-
- Json::Value room_id = it.key();
- if(!room_id.isString())
- continue;
-
- std::string room_id_str = room_id.asString();
-
- auto room_it = room_data_by_id.find(room_id_str);
- if(room_it == room_data_by_id.end()) {
- auto room_data = std::make_unique<RoomData>();
- room_data->id = room_id_str;
- room_data_by_id.insert(std::make_pair(room_id_str, std::move(room_data)));
- room_it = room_data_by_id.find(room_id_str); // TODO: Get iterator from above insert
+ if(state_json.isObject()) {
+ const Json::Value &events_json = state_json["events"];
+ events_add_user_info(events_json, room_it->second.get());
+ events_set_room_name(events_json, room_it->second.get());
}
const Json::Value &timeline_json = (*it)["timeline"];
- if(!timeline_json.isObject())
- continue;
+ if(timeline_json.isObject()) {
+ if(room_it->second->prev_batch.empty()) {
+ // This may be non-existent if this is the first event in the room
+ const Json::Value &prev_batch_json = timeline_json["prev_batch"];
+ if(prev_batch_json.isString())
+ room_it->second->prev_batch = prev_batch_json.asString();
+ }
- if(room_it->second->prev_batch.empty()) {
- // This may be non-existent if this is the first event in the room
- const Json::Value &prev_batch_json = timeline_json["prev_batch"];
- if(prev_batch_json.isString())
- 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;
+ }
- // 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, has_unread_notifications);
+ events_set_room_name(events_json, room_it->second.get());
}
- 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, has_unread_notifications);
- events_set_room_name(events_json, room_it->second.get());
+ const Json::Value &ephemeral_json = (*it)["ephemeral"];
+ if(ephemeral_json.isObject()) {
+ const Json::Value &events_json = ephemeral_json["events"];
+ events_add_user_read_markers(events_json, room_it->second.get());
+ }
}
return PluginResult::OK;
@@ -362,6 +347,56 @@ namespace QuickMedia {
}
}
+ void Matrix::events_add_user_read_markers(const Json::Value &events_json, RoomData *room_data) {
+ if(!events_json.isArray())
+ return;
+
+ for(const Json::Value &event_item_json : events_json) {
+ if(!event_item_json.isObject())
+ continue;
+
+ const Json::Value &type_json = event_item_json["type"];
+ if(!type_json.isString() || strcmp(type_json.asCString(), "m.receipt") != 0)
+ continue;
+
+ const Json::Value &content_json = event_item_json["content"];
+ if(!content_json.isObject())
+ continue;
+
+ for(Json::Value::const_iterator it = content_json.begin(); it != content_json.end(); ++it) {
+ if(!it->isObject())
+ continue;
+
+ Json::Value event_id_json = it.key();
+ if(!event_id_json.isString())
+ continue;
+
+ const Json::Value &read_json = (*it)["m.read"];
+ if(!read_json.isObject())
+ continue;
+
+ std::string event_id_str = event_id_json.asString();
+
+ for(Json::Value::const_iterator user_id_it = read_json.begin(); user_id_it != read_json.end(); ++user_id_it) {
+ if(!user_id_it->isObject())
+ continue;
+
+ Json::Value user_id_json = user_id_it.key();
+ if(!user_id_json.isString())
+ continue;
+
+ auto user_it = room_data->user_info_by_user_id.find(user_id_json.asString());
+ if(user_it == room_data->user_info_by_user_id.end()) {
+ fprintf(stderr, "Receipt read receipt for unknown user: %s, ignoring...\n", user_id_json.asCString());
+ continue;
+ }
+
+ room_data->user_info[user_it->second].read_marker_event_id = event_id_str;
+ }
+ }
+ }
+ }
+
static std::string message_content_extract_thumbnail_url(const Json::Value &content_json, const std::string &homeserver) {
const Json::Value &info_json = content_json["info"];
if(info_json.isObject()) {
@@ -1725,4 +1760,13 @@ namespace QuickMedia {
*upload_size = upload_limit.value();
return PluginResult::OK;
}
+
+ const UserInfo* Matrix::get_me(const std::string &room_id) const {
+ auto room_it = room_data_by_id.find(room_id);
+ if(room_it == room_data_by_id.end()) {
+ fprintf(stderr, "Error: no such room: %s\n", room_id.c_str());
+ return nullptr;
+ }
+ return &room_it->second->user_info[room_it->second->user_info_by_user_id[user_id]];
+ }
} \ No newline at end of file