Skip to content

Commit

Permalink
Merge pull request #192 from uowuo/message-editing
Browse files Browse the repository at this point in the history
Improve message editing
  • Loading branch information
ouwou authored Jul 16, 2023
2 parents 52a340e + 52b52eb commit 49bbc92
Show file tree
Hide file tree
Showing 15 changed files with 135 additions and 92 deletions.
4 changes: 4 additions & 0 deletions res/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@
border: 1px solid #026FB9;
}

.message-input.editing {
border: 1px solid #b9026f;
}

.message-input.bad-input {
border: 1px solid #dd3300;
}
Expand Down
18 changes: 7 additions & 11 deletions src/abaddon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#include "audio/manager.hpp"
#include "discord/discord.hpp"
#include "dialogs/token.hpp"
#include "dialogs/editmessage.hpp"
#include "dialogs/confirm.hpp"
#include "dialogs/setstatus.hpp"
#include "dialogs/friendpicker.hpp"
Expand Down Expand Up @@ -948,19 +947,15 @@ void Abaddon::ActionChatInputSubmit(ChatSubmitParams data) {

if (!m_discord.HasChannelPermission(m_discord.GetUserData().ID, data.ChannelID, Permission::VIEW_CHANNEL)) return;

m_discord.SendChatMessage(data, NOOP_CALLBACK);
if (data.EditingID.IsValid()) {
m_discord.EditMessage(data.ChannelID, data.EditingID, data.Message);
} else {
m_discord.SendChatMessage(data, NOOP_CALLBACK);
}
}

void Abaddon::ActionChatEditMessage(Snowflake channel_id, Snowflake id) {
const auto msg = m_discord.GetMessage(id);
if (!msg.has_value()) return;
EditMessageDialog dlg(*m_main_window);
dlg.SetContent(msg->Content);
auto response = dlg.run();
if (response == Gtk::RESPONSE_OK) {
auto new_content = dlg.GetContent();
m_discord.EditMessage(channel_id, id, new_content);
}
m_main_window->EditMessage(id);
}

void Abaddon::ActionInsertMention(Snowflake id) {
Expand Down Expand Up @@ -1156,6 +1151,7 @@ int main(int argc, char **argv) {
#endif

spdlog::cfg::load_env_levels();
auto log_ui = spdlog::stdout_color_mt("ui");
auto log_audio = spdlog::stdout_color_mt("audio");
auto log_voice = spdlog::stdout_color_mt("voice");
auto log_discord = spdlog::stdout_color_mt("discord");
Expand Down
24 changes: 23 additions & 1 deletion src/components/chatinput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,10 @@ void ChatInput::InsertText(const Glib::ustring &text) {
m_input.Get().InsertText(text);
}

void ChatInput::Clear() {
GetBuffer()->set_text("");
}

Glib::RefPtr<Gtk::TextBuffer> ChatInput::GetBuffer() {
return m_input.Get().GetBuffer();
}
Expand Down Expand Up @@ -565,6 +569,24 @@ void ChatInput::StopReplying() {
m_input.Get().get_style_context()->remove_class("replying");
}

void ChatInput::StartEditing(const Message &message) {
m_is_editing = true;
m_input.Get().grab_focus();
m_input.Get().get_style_context()->add_class("editing");
GetBuffer()->set_text(message.Content);
m_attachments.Clear();
m_attachments_revealer.set_reveal_child(false);
}

void ChatInput::StopEditing() {
m_is_editing = false;
m_input.Get().get_style_context()->remove_class("editing");
}

bool ChatInput::IsEmpty() {
return GetBuffer()->get_char_count() == 0;
}

bool ChatInput::AddFileAsImageAttachment(const Glib::RefPtr<Gio::File> &file) {
try {
const auto read_stream = file->read();
Expand All @@ -577,7 +599,7 @@ bool ChatInput::AddFileAsImageAttachment(const Glib::RefPtr<Gio::File> &file) {
}

bool ChatInput::CanAttachFiles() {
return Abaddon::Get().GetDiscordClient().HasSelfChannelPermission(m_active_channel, Permission::ATTACH_FILES | Permission::SEND_MESSAGES);
return !m_is_editing && Abaddon::Get().GetDiscordClient().HasSelfChannelPermission(m_active_channel, Permission::ATTACH_FILES | Permission::SEND_MESSAGES);
}

ChatInput::type_signal_submit ChatInput::signal_submit() {
Expand Down
8 changes: 8 additions & 0 deletions src/components/chatinput.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class ChatInput : public Gtk::Box {
ChatInput();

void InsertText(const Glib::ustring &text);
void Clear();
Glib::RefPtr<Gtk::TextBuffer> GetBuffer();
bool ProcessKeyPress(GdkEventKey *event);
void AddAttachment(const Glib::RefPtr<Gio::File> &file);
Expand All @@ -139,6 +140,11 @@ class ChatInput : public Gtk::Box {
void StartReplying();
void StopReplying();

void StartEditing(const Message &message);
void StopEditing();

bool IsEmpty();

private:
bool AddFileAsImageAttachment(const Glib::RefPtr<Gio::File> &file);
bool CanAttachFiles();
Expand All @@ -149,6 +155,8 @@ class ChatInput : public Gtk::Box {

Snowflake m_active_channel;

bool m_is_editing = false;

public:
using type_signal_submit = sigc::signal<bool, ChatSubmitParams>;
using type_signal_escape = sigc::signal<void>;
Expand Down
24 changes: 21 additions & 3 deletions src/components/chatlist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,9 @@ void ChatList::ProcessNewMessage(const Message &data, bool prepend) {
m_menu_delete_message->set_sensitive(false);
m_menu_edit_message->set_sensitive(false);
} else {
const bool can_edit = client.GetUserData().ID == data->Author.ID;
const bool can_delete = can_edit || has_manage;
const bool can_delete = (client.GetUserData().ID == data->Author.ID) || has_manage;
m_menu_delete_message->set_sensitive(can_delete);
m_menu_edit_message->set_sensitive(can_edit);
m_menu_edit_message->set_sensitive(data->IsEditable());
}

m_menu.popup_at_pointer(reinterpret_cast<GdkEvent *>(ev));
Expand Down Expand Up @@ -253,6 +252,25 @@ void ChatList::ActuallyRemoveMessage(Snowflake id) {
RemoveMessageAndHeader(it->second);
}

std::optional<Snowflake> ChatList::GetLastSentEditableMessage() {
const auto &discord = Abaddon::Get().GetDiscordClient();
const auto self_id = discord.GetUserData().ID;

std::map<Snowflake, Gtk::Widget *> ordered(m_id_to_widget.begin(), m_id_to_widget.end());

for (auto it = ordered.crbegin(); it != ordered.crend(); it++) {
const auto *widget = dynamic_cast<ChatMessageItemContainer *>(it->second);
if (widget == nullptr) continue;
const auto msg = discord.GetMessage(widget->ID);
if (!msg.has_value()) continue;
if (msg->Author.ID != self_id) continue;
if (!msg->IsEditable()) continue;
return msg->ID;
}

return std::nullopt;
}

void ChatList::SetupMenu() {
m_menu_copy_id = Gtk::manage(new Gtk::MenuItem("Copy ID"));
m_menu_copy_id->signal_activate().connect([this] {
Expand Down
1 change: 1 addition & 0 deletions src/components/chatlist.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class ChatList : public Gtk::ScrolledWindow {
void SetSeparateAll(bool separate);
void SetUsePinnedMenu(); // i think i need a better way to do menus
void ActuallyRemoveMessage(Snowflake id); // perhaps not the best method name
std::optional<Snowflake> GetLastSentEditableMessage();

private:
void SetupMenu();
Expand Down
56 changes: 48 additions & 8 deletions src/components/chatwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ ChatWindow::ChatWindow() {

m_input->signal_submit().connect(sigc::mem_fun(*this, &ChatWindow::OnInputSubmit));
m_input->signal_escape().connect([this]() {
if (m_is_replying)
StopReplying();
if (m_is_replying) StopReplying();
if (m_is_editing) StopEditing();
});
m_input->signal_key_press_event().connect(sigc::mem_fun(*this, &ChatWindow::OnKeyPressEvent), false);
m_input->show();
Expand Down Expand Up @@ -132,8 +132,8 @@ void ChatWindow::SetActiveChannel(Snowflake id) {
m_input->SetActiveChannel(id);
m_input_indicator->SetActiveChannel(id);
m_rate_limit_indicator->SetActiveChannel(id);
if (m_is_replying)
StopReplying();
if (m_is_replying) StopReplying();
if (m_is_editing) StopEditing();

#ifdef WITH_LIBHANDY
m_tab_switcher->ReplaceActiveTab(id);
Expand Down Expand Up @@ -274,22 +274,41 @@ bool ChatWindow::OnInputSubmit(ChatSubmitParams data) {

data.ChannelID = m_active_channel;
data.InReplyToID = m_replying_to;
data.EditingID = m_editing_id;

if (m_active_channel.IsValid())
m_signal_action_chat_submit.emit(data); // m_replying_to is checked for invalid in the handler
if (m_is_replying)
StopReplying();

if (m_is_replying) StopReplying();
if (m_is_editing) StopEditing();

return true;
}

bool ChatWindow::ProcessKeyEvent(GdkEventKey *e) {
if (e->type != GDK_KEY_PRESS) return false;
if (e->keyval == GDK_KEY_Up && !(e->state & GDK_SHIFT_MASK) && m_input->IsEmpty()) {
const auto edit_id = m_chat->GetLastSentEditableMessage();
if (edit_id.has_value()) {
StartEditing(*edit_id);
}

return true;
}

return false;
}

bool ChatWindow::OnKeyPressEvent(GdkEventKey *e) {
if (m_completer.ProcessKeyPress(e))
return true;

if (m_input->ProcessKeyPress(e))
return true;

if (ProcessKeyEvent(e))
return true;

return false;
}

Expand All @@ -300,10 +319,11 @@ void ChatWindow::StartReplying(Snowflake message_id) {
m_replying_to = message_id;
m_is_replying = true;
m_input->StartReplying();
if (author.has_value())
if (author.has_value()) {
m_input_indicator->SetCustomMarkup("Replying to " + author->GetUsernameEscapedBold());
else
} else {
m_input_indicator->SetCustomMarkup("Replying...");
}
}

void ChatWindow::StopReplying() {
Expand All @@ -313,6 +333,26 @@ void ChatWindow::StopReplying() {
m_input_indicator->ClearCustom();
}

void ChatWindow::StartEditing(Snowflake message_id) {
const auto message = Abaddon::Get().GetDiscordClient().GetMessage(message_id);
if (!message.has_value()) {
spdlog::get("ui")->warn("ChatWindow::StartEditing message is nullopt");
return;
}
m_is_editing = true;
m_editing_id = message_id;
m_input->StartEditing(*message);
m_input_indicator->SetCustomMarkup("Editing...");
}

void ChatWindow::StopEditing() {
m_is_editing = false;
m_editing_id = Snowflake::Invalid;
m_input->StopEditing();
m_input->Clear();
m_input_indicator->ClearCustom();
}

void ChatWindow::OnScrollEdgeOvershot(Gtk::PositionType pos) {
if (pos == Gtk::POS_TOP)
m_signal_action_chat_load_history.emit(m_active_channel);
Expand Down
7 changes: 7 additions & 0 deletions src/components/chatwindow.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ class ChatWindow {
void SetTopic(const std::string &text);
void AddAttachment(const Glib::RefPtr<Gio::File> &file);

void StartEditing(Snowflake message_id);
void StopEditing();

#ifdef WITH_LIBHANDY
void OpenNewTab(Snowflake id);
TabsState GetTabsState();
Expand All @@ -55,10 +58,14 @@ class ChatWindow {
void StartReplying(Snowflake message_id);
void StopReplying();

bool m_is_editing = false;
Snowflake m_editing_id;

Snowflake m_active_channel;

bool OnInputSubmit(ChatSubmitParams data);

bool ProcessKeyEvent(GdkEventKey *e);
bool OnKeyPressEvent(GdkEventKey *e);
void OnScrollEdgeOvershot(Gtk::PositionType pos);

Expand Down
45 changes: 0 additions & 45 deletions src/dialogs/editmessage.cpp

This file was deleted.

20 changes: 0 additions & 20 deletions src/dialogs/editmessage.hpp

This file was deleted.

1 change: 1 addition & 0 deletions src/discord/chatsubmitparams.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ struct ChatSubmitParams {
bool Silent = false;
Snowflake ChannelID;
Snowflake InReplyToID;
Snowflake EditingID;
Glib::ustring Message;
std::vector<Attachment> Attachments;
};
4 changes: 4 additions & 0 deletions src/discord/message.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,10 @@ bool Message::IsEdited() const {
return m_edited;
}

bool Message::IsEditable() const noexcept {
return (Abaddon::Get().GetDiscordClient().GetUserData().ID == Author.ID) && !IsDeleted() && !IsPending && (Type == MessageType::DEFAULT || Type == MessageType::INLINE_REPLY);
}

bool Message::DoesMentionEveryoneOrUser(Snowflake id) const noexcept {
if (DoesMentionEveryone) return true;
return std::any_of(Mentions.begin(), Mentions.end(), [id](const UserData &user) {
Expand Down
Loading

0 comments on commit 49bbc92

Please sign in to comment.