diff options
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | TODO | 6 | ||||
-rw-r--r-- | icons/matrix_launcher.png | bin | 0 -> 1199 bytes | |||
-rw-r--r-- | launcher/QuickMedia-matrix.desktop | 9 | ||||
-rw-r--r-- | launcher/QuickMedia-youtube.desktop | 2 | ||||
-rw-r--r-- | plugins/Matrix.hpp | 10 | ||||
-rw-r--r-- | src/QuickMedia.cpp | 81 | ||||
-rw-r--r-- | src/plugins/Matrix.cpp | 25 |
8 files changed, 113 insertions, 22 deletions
@@ -5,7 +5,7 @@ Currently supported web services: `youtube`, `nyaa.si`, `manganelo`, `mangatown` **Note:** Posting comments on 4chan doesn't work when used with TOR. However browsing works.\ **Note:** TOR system service needs to be running (`systemctl start tor.service`).\ **Note:** Image pages that were downloaded without --upscale-images and are cached wont get upscaled when running with `--upscale-images`.\ -**Note:** Matrix and file-manager is early in progress, not very usable yet.\ +**Note:** Matrix and file-manager is early in progress.\ Config data, including manga progress is stored under `$HOME/.config/quickmedia`.\ Cache is stored under `$HOME/.cache/quickmedia`. ## Usage @@ -53,4 +53,8 @@ Use Entry instead of SearchBar for 4chan commenting as well. Only add related videos to recommendations if its the first time we watch the video. This is to prevent rewatching a video multiple times from messing up recommendations. Fix incorrect body visible rows count (draws incorrect number of items and incorrect counted, also messed up pg(up/down)). Replace messages on matrix instead of appending edits (messages that begin with " * "). This also fixes edit of already edited messages. -When deleting an edited message on matrix, delete the original message instead of the edit. This should also be fixed when edits actually edits the original message instead of appending, by maybe it should be done anyways to prevent race condition.
\ No newline at end of file +When deleting an edited message on matrix, delete the original message instead of the edit. This should also be fixed when edits actually edits the original message instead of appending, by maybe it should be done anyways to prevent race condition. +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.
\ No newline at end of file diff --git a/icons/matrix_launcher.png b/icons/matrix_launcher.png Binary files differnew file mode 100644 index 0000000..055a9c7 --- /dev/null +++ b/icons/matrix_launcher.png diff --git a/launcher/QuickMedia-matrix.desktop b/launcher/QuickMedia-matrix.desktop new file mode 100644 index 0000000..546ea3d --- /dev/null +++ b/launcher/QuickMedia-matrix.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Application +Name=QuickMedia Matrix +GenericName=Matrix client +Comment=Matrix client +Icon=/usr/share/quickmedia/icons/matrix_launcher.png +Exec=QuickMedia matrix +Terminal=false +Keywords=matrix;chat;quickmedia; diff --git a/launcher/QuickMedia-youtube.desktop b/launcher/QuickMedia-youtube.desktop index c82ffd6..ba97727 100644 --- a/launcher/QuickMedia-youtube.desktop +++ b/launcher/QuickMedia-youtube.desktop @@ -6,4 +6,4 @@ Comment=YouTube search and video playing Icon=/usr/share/quickmedia/icons/yt_launcher.png Exec=QuickMedia youtube Terminal=false -Keywords=youtube;player;quickmedia;mpv; +Keywords=youtube;player;quickmedia;mpv;video;multimedia; diff --git a/plugins/Matrix.hpp b/plugins/Matrix.hpp index fdd3392..7df29e1 100644 --- a/plugins/Matrix.hpp +++ b/plugins/Matrix.hpp @@ -57,6 +57,8 @@ namespace QuickMedia { AFTER }; + using RoomSyncMessages = std::unordered_map<RoomData*, std::vector<std::shared_ptr<Message>>>; + class Matrix : public Plugin { public: Matrix(); @@ -68,7 +70,7 @@ namespace QuickMedia { bool search_suggestion_is_search() const override { return true; } Page get_page_after_search() const override { return Page::EXIT; } PluginResult get_cached_sync(BodyItems &result_items); - PluginResult sync(); + PluginResult sync(RoomSyncMessages &room_messages); PluginResult get_joined_rooms(BodyItems &result_items); PluginResult get_all_synced_room_messages(const std::string &room_id, BodyItems &result_items); PluginResult get_new_room_messages(const std::string &room_id, BodyItems &result_items); @@ -98,11 +100,13 @@ namespace QuickMedia { // |message| is from |BodyItem.userdata| and is of type |Message*| bool was_message_posted_by_me(const std::string &room_id, void *message) const; + + std::string message_get_author_displayname(RoomData *room_data, Message *message) const; private: - PluginResult sync_response_to_body_items(const Json::Value &root); + 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); + void events_add_messages(const Json::Value &events_json, RoomData *room_data, MessageDirection message_dir, RoomSyncMessages *room_messages); 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); diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index cb58a27..15ae39c 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -3279,6 +3279,16 @@ namespace QuickMedia { source.a + diff_a * progress); } + static std::string extract_first_line(const std::string &str) { + size_t index = str.find('\n'); + if(index == std::string::npos) + return str; + else if(index == 0) + return ""; + else + return str.substr(0, index - 1); + } + void Program::chat_page() { assert(current_plugin->name == "matrix"); Matrix *matrix = static_cast<Matrix*>(current_plugin); @@ -3320,7 +3330,8 @@ 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 - if(matrix->sync() != PluginResult::OK) { + 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; @@ -3332,6 +3343,24 @@ namespace QuickMedia { return; } + struct RoomBodyData { + std::shared_ptr<BodyItem> body_item; + bool last_message_read; + }; + + 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)); + } + } + // 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; @@ -3341,6 +3370,11 @@ namespace QuickMedia { // 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 enum class ChatState { @@ -3423,7 +3457,12 @@ namespace QuickMedia { return false; }; - std::future<BodyItems> sync_future; + struct SyncFutureResult { + BodyItems body_items; + RoomSyncMessages room_sync_messages; + }; + + std::future<SyncFutureResult> sync_future; bool sync_running = false; std::string sync_future_room_id; sf::Clock sync_timer; @@ -3642,6 +3681,10 @@ namespace QuickMedia { std::string err_msg = "Failed to get messages in room: " + current_room_id; show_notification("QuickMedia", err_msg, Urgency::CRITICAL); } + + 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; } } } @@ -3757,30 +3800,38 @@ namespace QuickMedia { sync_future = std::async(std::launch::async, [this, &sync_future_room_id]() { Matrix *matrix = static_cast<Matrix*>(current_plugin); - BodyItems result_items; - if(matrix->sync() == PluginResult::OK) { + SyncFutureResult result; + if(matrix->sync(result.room_sync_messages) == PluginResult::OK) { fprintf(stderr, "Synced matrix\n"); - if(matrix->get_new_room_messages(sync_future_room_id, result_items) != PluginResult::OK) { + 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()); } } else { fprintf(stderr, "Failed to sync matrix\n"); } - return result_items; + return result; }); } if(sync_future.valid() && sync_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { - BodyItems new_body_items = sync_future.get(); + SyncFutureResult sync_result = sync_future.get(); // Ignore finished sync if it happened in another room. When we navigate back to the room we will get the messages again if(sync_future_room_id == current_room_id) { int num_items = tabs[MESSAGES_TAB_INDEX].body->items.size(); bool scroll_to_end = (num_items == 0 || (num_items > 0 && tabs[MESSAGES_TAB_INDEX].body->get_selected_item() == num_items - 1)); - tabs[MESSAGES_TAB_INDEX].body->append_items(std::move(new_body_items)); + tabs[MESSAGES_TAB_INDEX].body->append_items(std::move(sync_result.body_items)); 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; + } + } sync_running = false; } @@ -3872,8 +3923,18 @@ 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 && !tabs[selected_tab].body->is_last_item_fully_visible()) { - window.draw(more_messages_below_rect); + 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); + // 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; + } + } else { + window.draw(more_messages_below_rect); + } } if(tabs[selected_tab].type == ChatTabType::MESSAGES) { diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index b5eb617..ed3b0af 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -34,7 +34,7 @@ namespace QuickMedia { return PluginResult::OK; } - PluginResult Matrix::sync() { + PluginResult Matrix::sync(RoomSyncMessages &room_messages) { std::vector<CommandArg> additional_args = { { "-H", "Authorization: Bearer " + access_token }, { "-m", "35" } @@ -73,7 +73,7 @@ namespace QuickMedia { return PluginResult::ERR; } - PluginResult result = sync_response_to_body_items(json_root); + PluginResult result = sync_response_to_body_items(json_root, room_messages); if(result != PluginResult::OK) return result; @@ -244,7 +244,7 @@ namespace QuickMedia { return PluginResult::OK; } - PluginResult Matrix::sync_response_to_body_items(const Json::Value &root) { + PluginResult Matrix::sync_response_to_body_items(const Json::Value &root, RoomSyncMessages &room_messages) { if(!root.isObject()) return PluginResult::ERR; @@ -314,7 +314,7 @@ namespace QuickMedia { 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); + events_add_messages(events_json, room_it->second.get(), MessageDirection::AFTER, &room_messages); events_set_room_name(events_json, room_it->second.get()); } @@ -400,10 +400,13 @@ namespace QuickMedia { return ""; } - void Matrix::events_add_messages(const Json::Value &events_json, RoomData *room_data, MessageDirection message_dir) { + void Matrix::events_add_messages(const Json::Value &events_json, RoomData *room_data, MessageDirection message_dir, RoomSyncMessages *room_messages) { 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) { @@ -484,6 +487,8 @@ namespace QuickMedia { message->replaces_event_id = std::move(replaces_event_id); new_messages.push_back(message); room_data->message_by_event_id[event_id_str] = message; + if(room_sync_messages) + room_sync_messages->push_back(message); } // TODO: Loop and std::move instead? doesn't insert create copies? @@ -626,7 +631,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); + events_add_messages(chunk_json, room_data, MessageDirection::BEFORE, nullptr); const Json::Value &end_json = json_root["end"]; if(!end_json.isString()) { @@ -1431,4 +1436,12 @@ namespace QuickMedia { Message *message_typed = (Message*)message; return user_id == room_it->second->user_info[message_typed->user_id].user_id; } + + std::string Matrix::message_get_author_displayname(RoomData *room_data, Message *message) const { + if(message->user_id >= room_data->user_info.size()) { + fprintf(stderr, "Warning: no user with the index: %zu\n", message->user_id); + return ""; + } + return room_data->user_info[message->user_id].display_name; + } }
\ No newline at end of file |