diff options
author | dec05eba <dec05eba@protonmail.com> | 2021-08-10 13:01:50 +0200 |
---|---|---|
committer | dec05eba <dec05eba@protonmail.com> | 2021-08-10 13:01:50 +0200 |
commit | 8af5901fb893c3fc25b31bf484a90b47d1c24c2c (patch) | |
tree | a2898fa4357a264b1ba98746379cabf0c9e55b85 | |
parent | fde558b220118ff873cc2e735f6ec167ebcf5167 (diff) |
Matrix: add ctrl+p/ctrl+d to pin/unpin messages
-rw-r--r-- | README.md | 7 | ||||
-rw-r--r-- | TODO | 3 | ||||
-rw-r--r-- | plugins/Matrix.hpp | 9 | ||||
-rw-r--r-- | src/QuickMedia.cpp | 51 | ||||
-rw-r--r-- | src/plugins/Matrix.cpp | 93 |
5 files changed, 140 insertions, 23 deletions
@@ -100,7 +100,10 @@ Type text and then wait and QuickMedia will automatically search.\ `Alt+Up`/`Ctrl+Alt+K`: Select the room above the currently selected room.\ `Alt+Down`/`Ctrl+Alt+J`: Select the room below the currently selected room.\ `Alt+Home`: Select the first room in the room list.\ -`Alt+End`: Select the last room in the room list. +`Alt+End`: Select the last room in the room list.\ +`Ctrl+P`: Pin the selected message. +#### Pinned messages page controls +`Ctrl+D`: Unpin the selected message. #### Message input controls `Esc`: Stop typing (also clears the input text).\ `Enter`: Post message.\ @@ -136,7 +139,7 @@ Type text and then wait and QuickMedia will automatically search.\ `/logout`: Logout.\ `/leave`: Leave the current room.\ `/me [text]`: Send a message of type "m.emote".\ -`/react [text]`: React to the selected message or if you are replying to message then it reacts to that message. +`/react [text]`: React to the selected message (also works if you are replying to a message). ## Environment variables Set `QM_PHONE_FACTOR=1` to disable the room list side panel in matrix.\ Set `QM_THEME` to one of the following: `default, nord` to change the theme. @@ -191,4 +191,5 @@ Synapse is gay and mentions do not actually include the whole mxid. It only incl while the user might have disable username mentions. In those cases the user wont get a notification for mxid mention. Mention name instead? Make it possible to redact invites. Reapply filter when changing body item text. -When fetching previous messages in matrix, ignore user info updates.
\ No newline at end of file +When fetching previous messages in matrix, ignore user info updates. +Add throttling to youtube live stream (MediaProxy).
\ No newline at end of file diff --git a/plugins/Matrix.hpp b/plugins/Matrix.hpp index e94cc13..39e0a57 100644 --- a/plugins/Matrix.hpp +++ b/plugins/Matrix.hpp @@ -119,7 +119,7 @@ namespace QuickMedia { void release_room_lock(); const std::vector<std::shared_ptr<Message>>& get_messages_thread_unsafe() const; - const std::vector<std::string>& get_pinned_events_unsafe() const; + const std::vector<std::string>& get_pinned_events_thread_unsafe() const; bool has_prev_batch(); void set_prev_batch(const std::string &new_prev_batch); @@ -137,7 +137,7 @@ namespace QuickMedia { std::string get_avatar_url(); void set_pinned_events(std::vector<std::string> new_pinned_events); - std::set<std::string>& get_tags_unsafe(); + std::set<std::string>& get_tags_thread_unsafe(); void clear_data(); @@ -543,6 +543,9 @@ namespace QuickMedia { // |message| is from |BodyItem.userdata| and is of type |Message*| PluginResult delete_message(RoomData *room, void *message, std::string &err_msg); + PluginResult pin_message(RoomData *room, const std::string &event_id); + PluginResult unpin_message(RoomData *room, const std::string &event_id); + PluginResult load_cached_session(); PluginResult on_start_typing(RoomData *room); @@ -588,6 +591,8 @@ namespace QuickMedia { std::string body_to_formatted_body(RoomData *room, const std::string &body); std::string create_formatted_body_for_message_reply(RoomData *room, const Message *message, const std::string &body); + PluginResult set_pinned_events(RoomData *room, const std::vector<std::string> &pinned_events, bool is_add); + PluginResult set_qm_last_read_message_timestamp(RoomData *room, int64_t timestamp); PluginResult parse_sync_response(const rapidjson::Document &root, bool is_additional_messages_sync, bool initial_sync); diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index a21cf11..a45da87 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -5876,17 +5876,15 @@ namespace QuickMedia { } } - if(current_room) { - if(event.key.control && event.key.code == sf::Keyboard::C) { - BodyItem *selected = tabs[selected_tab].body->get_selected(); - if(selected) { - auto desc = selected->get_description(); - sf::Clipboard::setString(sf::String::fromUtf8(desc.begin(), desc.end())); - } + if(event.key.control && event.key.code == sf::Keyboard::C) { + BodyItem *selected = tabs[selected_tab].body->get_selected(); + if(selected) { + auto desc = selected->get_description(); + sf::Clipboard::setString(sf::String::fromUtf8(desc.begin(), desc.end())); } } - if(selected_tab == MESSAGES_TAB_INDEX && current_room) { + if(selected_tab == MESSAGES_TAB_INDEX) { if(event.key.code == sf::Keyboard::U) { frame_skip_text_entry = true; new_page = PageType::FILE_MANAGER; @@ -5983,6 +5981,43 @@ namespace QuickMedia { show_notification("QuickMedia", "No message selected for deletion"); } } + + if(event.key.control && event.key.code == sf::Keyboard::P && !chat_input.is_editable()) { + frame_skip_text_entry = true; + BodyItem *selected = tabs[selected_tab].body->get_selected(); + if(selected) { + Message *selected_message = static_cast<Message*>(selected->userdata); + if(!is_state_message_type(selected_message)) { + if(selected_message->event_id.empty()) { + // TODO: Show inline notification + show_notification("QuickMedia", "You can't pin a message that hasn't been sent yet"); + } else { + run_task_with_loading_screen([this, current_room, selected_message] { + return matrix->pin_message(current_room, selected_message->event_id) == PluginResult::OK; + }); + } + } + } else { + // TODO: Show inline notification + show_notification("QuickMedia", "No message selected for pinning"); + } + } + } else if(selected_tab == PINNED_TAB_INDEX) { + if(event.key.control && event.key.code == sf::Keyboard::D && !chat_input.is_editable()) { + frame_skip_text_entry = true; + BodyItem *selected = tabs[selected_tab].body->get_selected(); + if(selected) { + PinnedEventData *selected_pinned_event_data = static_cast<PinnedEventData*>(selected->userdata); + if(selected_pinned_event_data) { + run_task_with_loading_screen([this, current_room, selected_pinned_event_data] { + return matrix->unpin_message(current_room, selected_pinned_event_data->event_id) == PluginResult::OK; + }); + } + } else { + // TODO: Show inline notification + show_notification("QuickMedia", "No message selected for unpinning"); + } + } } } else if(event.type == sf::Event::KeyPressed && chat_state == ChatState::URL_SELECTION) { if(event.key.code == sf::Keyboard::Escape) { diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index b471522..d59e6d5 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -29,8 +29,9 @@ namespace QuickMedia { static const sf::Vector2i thumbnail_max_size(600, 337); static const char* SERVICE_NAME = "matrix"; static const char* OTHERS_ROOM_TAG = "tld.name.others"; - // Filter without account data - static const char* INITIAL_FILTER = "{\"presence\":{\"limit\":0,\"types\":[\"\"]},\"account_data\":{\"limit\":0,\"types\":[\"\"]},\"room\":{\"state\":{\"not_types\":[\"m.room.related_groups\",\"m.room.power_levels\",\"m.room.join_rules\",\"m.room.history_visibility\"],\"lazy_load_members\":true},\"timeline\":{\"types\":[\"m.room.message\"],\"limit\":1,\"lazy_load_members\":true},\"ephemeral\":{\"limit\":0,\"types\":[\"\"],\"lazy_load_members\":true},\"account_data\":{\"limit\":1,\"types\":[\"m.fully_read\",\"m.tag\",\"qm.last_read_message_timestamp\"],\"lazy_load_members\":true}}}"; + // Filter without account data. TODO: We include pinned events but limit events to 1. That means if the last event is a pin, + // then we cant see room message preview. TODO: Fix this somehow. + static const char* INITIAL_FILTER = "{\"presence\":{\"limit\":0,\"types\":[\"\"]},\"account_data\":{\"limit\":0,\"types\":[\"\"]},\"room\":{\"state\":{\"not_types\":[\"m.room.related_groups\",\"m.room.power_levels\",\"m.room.join_rules\",\"m.room.history_visibility\"],\"lazy_load_members\":true},\"timeline\":{\"types\":[\"m.room.message\",\"m.room.pinned_events\"],\"limit\":1,\"lazy_load_members\":true},\"ephemeral\":{\"limit\":0,\"types\":[\"\"],\"lazy_load_members\":true},\"account_data\":{\"limit\":1,\"types\":[\"m.fully_read\",\"m.tag\",\"qm.last_read_message_timestamp\"],\"lazy_load_members\":true}}}"; static const char* ADDITIONAL_MESSAGES_FILTER = "{\"presence\":{\"limit\":0,\"types\":[\"\"]},\"account_data\":{\"limit\":0,\"types\":[\"\"]},\"room\":{\"state\":{\"not_types\":[\"m.room.related_groups\",\"m.room.power_levels\",\"m.room.join_rules\",\"m.room.history_visibility\"],\"lazy_load_members\":true},\"timeline\":{\"limit\":20,\"lazy_load_members\":true},\"ephemeral\":{\"limit\":0,\"types\":[\"\"],\"lazy_load_members\":true},\"account_data\":{\"limit\":0,\"types\":[\"\"],\"lazy_load_members\":true}}}"; static const char* CONTINUE_FILTER = "{\"presence\":{\"limit\":0,\"types\":[\"\"]},\"account_data\":{\"limit\":0,\"types\":[\"\"]},\"room\":{\"state\":{\"not_types\":[\"m.room.related_groups\",\"m.room.power_levels\",\"m.room.join_rules\",\"m.room.history_visibility\"],\"lazy_load_members\":true},\"timeline\":{\"lazy_load_members\":true},\"ephemeral\":{\"limit\":0,\"types\":[\"\"],\"lazy_load_members\":true},\"account_data\":{\"types\":[\"m.fully_read\",\"m.tag\",\"qm.last_read_message_timestamp\"],\"lazy_load_members\":true}}}"; @@ -255,7 +256,7 @@ namespace QuickMedia { return messages; } - const std::vector<std::string>& RoomData::get_pinned_events_unsafe() const { + const std::vector<std::string>& RoomData::get_pinned_events_thread_unsafe() const { return pinned_events; } @@ -322,7 +323,7 @@ namespace QuickMedia { pinned_events_updated = true; } - std::set<std::string>& RoomData::get_tags_unsafe() { + std::set<std::string>& RoomData::get_tags_thread_unsafe() { return tags; } @@ -635,7 +636,7 @@ namespace QuickMedia { rooms_page->move_room_to_top(room); room_tags_page->move_room_to_top(room); - } else if(is_initial_sync) { + } else { room->body_item->set_description(extract_first_line_remove_newline_elipses(matrix->message_get_author_displayname(last_new_message.get()), AUTHOR_MAX_LENGTH) + ": " + message_to_room_description_text(last_new_message.get())); room->body_item->set_description_color(get_current_theme().faded_text_color); } @@ -1410,7 +1411,7 @@ namespace QuickMedia { room->messages_read_index = room_messages.size(); } if(room->pinned_events_updated) { - sync_data.pinned_events = room->get_pinned_events_unsafe(); + sync_data.pinned_events = room->get_pinned_events_thread_unsafe(); room->pinned_events_updated = false; } room->release_room_lock(); @@ -1425,7 +1426,7 @@ namespace QuickMedia { void Matrix::get_all_pinned_events(RoomData *room, std::vector<std::string> &events) { room->acquire_room_lock(); - events = room->get_pinned_events_unsafe(); + events = room->get_pinned_events_thread_unsafe(); room->pinned_events_updated = false; room->release_room_lock(); } @@ -1709,7 +1710,7 @@ namespace QuickMedia { if(is_new_room) { room->acquire_room_lock(); - std::set<std::string> &room_tags = room->get_tags_unsafe(); + std::set<std::string> &room_tags = room->get_tags_thread_unsafe(); if(room_tags.empty()) { room_tags.insert(OTHERS_ROOM_TAG); ui_thread_tasks.push([this, room]{ delegate->room_add_tag(room, OTHERS_ROOM_TAG); }); @@ -2641,7 +2642,7 @@ namespace QuickMedia { // When we receive a list of tags its always the full list of tags if(has_tags) { room_data->acquire_room_lock(); - std::set<std::string> &room_tags = room_data->get_tags_unsafe(); + std::set<std::string> &room_tags = room_data->get_tags_thread_unsafe(); for(const std::string &room_tag : room_tags) { auto it = new_tags.find(room_tag); @@ -3784,6 +3785,78 @@ namespace QuickMedia { return PluginResult::OK; } + PluginResult Matrix::pin_message(RoomData *room, const std::string &event_id) { + if(!is_initial_sync_finished()) { + show_notification("QuickMedia", "Can't pin messages while sync is in progress", Urgency::CRITICAL); + return PluginResult::ERR; + } + + room->acquire_room_lock(); + auto pinned_events = room->get_pinned_events_thread_unsafe(); + room->release_room_lock(); + + pinned_events.push_back(event_id); + return set_pinned_events(room, pinned_events, true); + } + + PluginResult Matrix::unpin_message(RoomData *room, const std::string &event_id) { + if(!is_initial_sync_finished()) { + show_notification("QuickMedia", "Can't unpin messages while sync is in progress", Urgency::CRITICAL); + return PluginResult::ERR; + } + + room->acquire_room_lock(); + auto pinned_events = room->get_pinned_events_thread_unsafe(); + room->release_room_lock(); + + auto find_it = std::find(pinned_events.begin(), pinned_events.end(), event_id); + if(find_it != pinned_events.end()) + pinned_events.erase(find_it); + + return set_pinned_events(room, pinned_events, false); + } + + PluginResult Matrix::set_pinned_events(RoomData *room, const std::vector<std::string> &pinned_events, bool is_add) { + rapidjson::Document request_data(rapidjson::kObjectType); + rapidjson::Value pinned_events_json(rapidjson::kArrayType); + for(auto &pinned_event : pinned_events) { + pinned_events_json.PushBack(rapidjson::StringRef(pinned_event.c_str()), request_data.GetAllocator()); + } + request_data.AddMember("pinned", std::move(pinned_events_json), request_data.GetAllocator()); + + rapidjson::StringBuffer buffer; + rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); + request_data.Accept(writer); + + std::vector<CommandArg> additional_args = { + { "-X", "PUT" }, + { "-H", "content-type: application/json" }, + { "-H", "Authorization: Bearer " + access_token }, + { "--data-binary", buffer.GetString() } + }; + + char url[512]; + snprintf(url, sizeof(url), "%s/_matrix/client/r0/rooms/%s/state/m.room.pinned_events/", homeserver.c_str(), room->id.c_str()); + + rapidjson::Document json_root; + std::string err_msg; + DownloadResult download_result = download_json(json_root, url, std::move(additional_args), true, &err_msg); + if(download_result != DownloadResult::OK) return download_result_to_plugin_result(download_result); + + if(!json_root.IsObject()) { + show_notification("QuickMedia", "Failed to parse server response", Urgency::CRITICAL); + return PluginResult::ERR; + } + + const rapidjson::Value &error_json = GetMember(json_root, "error"); + if(error_json.IsString()) { + show_notification("QuickMedia", std::string("Failed to ") + (is_add ? "pin" : "unpin") + " message, error: " + std::string(error_json.GetString(), error_json.GetStringLength()), Urgency::CRITICAL); + return PluginResult::ERR; + } + + return PluginResult::OK; + } + PluginResult Matrix::load_cached_session() { Path session_path = get_storage_dir().join(SERVICE_NAME).join("session.json"); std::string session_json_content; @@ -3964,7 +4037,7 @@ namespace QuickMedia { ui_thread_tasks.push([this, room]{ delegate->join_room(room); }); room->acquire_room_lock(); - std::set<std::string> &room_tags = room->get_tags_unsafe(); + std::set<std::string> &room_tags = room->get_tags_thread_unsafe(); if(room_tags.empty()) { room_tags.insert(OTHERS_ROOM_TAG); ui_thread_tasks.push([this, room]{ delegate->room_add_tag(room, OTHERS_ROOM_TAG); }); |