aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2021-08-10 13:01:50 +0200
committerdec05eba <dec05eba@protonmail.com>2021-08-10 13:01:50 +0200
commit8af5901fb893c3fc25b31bf484a90b47d1c24c2c (patch)
treea2898fa4357a264b1ba98746379cabf0c9e55b85
parentfde558b220118ff873cc2e735f6ec167ebcf5167 (diff)
Matrix: add ctrl+p/ctrl+d to pin/unpin messages
-rw-r--r--README.md7
-rw-r--r--TODO3
-rw-r--r--plugins/Matrix.hpp9
-rw-r--r--src/QuickMedia.cpp51
-rw-r--r--src/plugins/Matrix.cpp93
5 files changed, 140 insertions, 23 deletions
diff --git a/README.md b/README.md
index 24e6072..04bb32f 100644
--- a/README.md
+++ b/README.md
@@ -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.
diff --git a/TODO b/TODO
index 0aecd84..e1b9f1f 100644
--- a/TODO
+++ b/TODO
@@ -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); });