From 270730d9b36c8fc3a221da7e565632c1432d41c6 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sun, 5 Jun 2022 21:41:57 -0400 Subject: start attachments (image paste and upload) --- src/abaddon.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/abaddon.cpp') diff --git a/src/abaddon.cpp b/src/abaddon.cpp index 2e8ecaa..05e1942 100644 --- a/src/abaddon.cpp +++ b/src/abaddon.cpp @@ -743,7 +743,7 @@ void Abaddon::ActionChatLoadHistory(Snowflake id) { }); } -void Abaddon::ActionChatInputSubmit(std::string msg, Snowflake channel, Snowflake referenced_message) { +void Abaddon::ActionChatInputSubmit(std::string msg, const std::vector &attachment_paths, Snowflake channel, Snowflake referenced_message) { if (msg.substr(0, 7) == "/shrug " || msg == "/shrug") msg = msg.substr(6) + "\xC2\xAF\x5C\x5F\x28\xE3\x83\x84\x29\x5F\x2F\xC2\xAF"; // this is important @@ -751,9 +751,9 @@ void Abaddon::ActionChatInputSubmit(std::string msg, Snowflake channel, Snowflak if (!m_discord.HasChannelPermission(m_discord.GetUserData().ID, channel, Permission::VIEW_CHANNEL)) return; if (referenced_message.IsValid()) - m_discord.SendChatMessage(msg, channel, referenced_message); + m_discord.SendChatMessage(msg, attachment_paths, channel, referenced_message); else - m_discord.SendChatMessage(msg, channel); + m_discord.SendChatMessage(msg, attachment_paths, channel); } void Abaddon::ActionChatEditMessage(Snowflake channel_id, Snowflake id) { -- cgit v1.2.3 From 4456c8771da668cbadb411583624fe0e357fa687 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Tue, 14 Jun 2022 02:36:04 -0400 Subject: refactor send message params into one struct --- src/abaddon.cpp | 23 ++++++---- src/abaddon.hpp | 2 +- src/components/chatinput.cpp | 20 +++++--- src/components/chatinput.hpp | 9 ++-- src/components/chatwindow.cpp | 9 ++-- src/components/chatwindow.hpp | 5 +- src/discord/chatsubmitparams.hpp | 22 +++++++++ src/discord/discord.cpp | 99 +++++++++++++++++++++------------------- src/discord/discord.hpp | 11 ++--- 9 files changed, 122 insertions(+), 78 deletions(-) create mode 100644 src/discord/chatsubmitparams.hpp (limited to 'src/abaddon.cpp') diff --git a/src/abaddon.cpp b/src/abaddon.cpp index 05e1942..28b6262 100644 --- a/src/abaddon.cpp +++ b/src/abaddon.cpp @@ -743,17 +743,22 @@ void Abaddon::ActionChatLoadHistory(Snowflake id) { }); } -void Abaddon::ActionChatInputSubmit(std::string msg, const std::vector &attachment_paths, Snowflake channel, Snowflake referenced_message) { - if (msg.substr(0, 7) == "/shrug " || msg == "/shrug") - msg = msg.substr(6) + "\xC2\xAF\x5C\x5F\x28\xE3\x83\x84\x29\x5F\x2F\xC2\xAF"; // this is important +static void ChatMessageSentCallback(const ChatSubmitParams &data) { + printf("completed for %s\n", data.Message.c_str()); + for (const auto &attachment : data.Attachments) { + puts(attachment.Path.c_str()); + } +} - if (!channel.IsValid()) return; - if (!m_discord.HasChannelPermission(m_discord.GetUserData().ID, channel, Permission::VIEW_CHANNEL)) return; +void Abaddon::ActionChatInputSubmit(ChatSubmitParams data) { + if (data.Message.substr(0, 7) == "/shrug " || data.Message == "/shrug") + data.Message = data.Message.substr(6) + "\xC2\xAF\x5C\x5F\x28\xE3\x83\x84\x29\x5F\x2F\xC2\xAF"; // this is important - if (referenced_message.IsValid()) - m_discord.SendChatMessage(msg, attachment_paths, channel, referenced_message); - else - m_discord.SendChatMessage(msg, attachment_paths, channel); + if (!m_discord.HasChannelPermission(m_discord.GetUserData().ID, data.ChannelID, Permission::VIEW_CHANNEL)) return; + + m_discord.SendChatMessage(data, [data](DiscordError code) { + ChatMessageSentCallback(data); + }); } void Abaddon::ActionChatEditMessage(Snowflake channel_id, Snowflake id) { diff --git a/src/abaddon.hpp b/src/abaddon.hpp index 9efb891..1ba1251 100644 --- a/src/abaddon.hpp +++ b/src/abaddon.hpp @@ -36,7 +36,7 @@ public: void ActionSetToken(); void ActionJoinGuildDialog(); void ActionChannelOpened(Snowflake id, bool expand_to = true); - void ActionChatInputSubmit(std::string msg, const std::vector &attachment_paths, Snowflake channel, Snowflake referenced_message); + void ActionChatInputSubmit(ChatSubmitParams data); void ActionChatLoadHistory(Snowflake id); void ActionChatEditMessage(Snowflake channel_id, Snowflake id); void ActionInsertMention(Snowflake id); diff --git a/src/components/chatinput.cpp b/src/components/chatinput.cpp index f9bbce4..2b8b5a5 100644 --- a/src/components/chatinput.cpp +++ b/src/components/chatinput.cpp @@ -152,10 +152,10 @@ bool ChatInputAttachmentContainer::AddImage(const Glib::RefPtr &pb) return true; } -std::vector ChatInputAttachmentContainer::GetFilePaths() const { - std::vector ret; +std::vector ChatInputAttachmentContainer::GetAttachments() const { + std::vector ret; for (auto *x : m_attachments) - ret.push_back(x->GetPath()); + ret.push_back({ x->GetPath(), x->GetType() }); return ret; } @@ -165,7 +165,8 @@ ChatInputAttachmentContainer::type_signal_emptied ChatInputAttachmentContainer:: ChatInputAttachmentItem::ChatInputAttachmentItem(std::string path, const Glib::RefPtr &pb) : m_path(std::move(path)) - , m_img(Gtk::make_managed()) { + , m_img(Gtk::make_managed()) + , m_type(ChatSubmitParams::PastedImage) { get_style_context()->add_class("attachment-item"); int outw, outh; @@ -184,6 +185,10 @@ std::string ChatInputAttachmentItem::GetPath() const { return m_path; } +ChatSubmitParams::AttachmentType ChatInputAttachmentItem::GetType() const { + return m_type; +} + void ChatInputAttachmentItem::SetupMenu() { m_menu_remove.set_label("Remove"); m_menu_remove.signal_activate().connect([this] { @@ -215,8 +220,11 @@ ChatInput::ChatInput() m_signal_escape.emit(); }); m_input.signal_submit().connect([this](const Glib::ustring &input) -> bool { - const auto attachments = m_attachments.GetFilePaths(); - bool b = m_signal_submit.emit(input, attachments); + ChatSubmitParams data; + data.Message = input; + data.Attachments = m_attachments.GetAttachments(); + + bool b = m_signal_submit.emit(data); if (b) { m_attachments_revealer.set_reveal_child(false); m_attachments.ClearNoPurge(); diff --git a/src/components/chatinput.hpp b/src/components/chatinput.hpp index bdbac5c..ba3ab36 100644 --- a/src/components/chatinput.hpp +++ b/src/components/chatinput.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include "discord/chatsubmitparams.hpp" #include "discord/permissions.hpp" class ChatInputAttachmentItem : public Gtk::EventBox { @@ -7,6 +8,7 @@ public: ChatInputAttachmentItem(std::string path, const Glib::RefPtr &pb); [[nodiscard]] std::string GetPath() const; + [[nodiscard]] ChatSubmitParams::AttachmentType GetType() const; private: void SetupMenu(); @@ -18,6 +20,7 @@ private: Gtk::Image *m_img = nullptr; std::string m_path; + ChatSubmitParams::AttachmentType m_type; private: using type_signal_remove = sigc::signal; @@ -35,7 +38,7 @@ public: void Clear(); void ClearNoPurge(); bool AddImage(const Glib::RefPtr &pb); - [[nodiscard]] std::vector GetFilePaths() const; + [[nodiscard]] std::vector GetAttachments() const; private: std::set m_attachments; @@ -96,9 +99,7 @@ private: ChatInputText m_input; public: - // text, attachments -> request sent - // maybe this should be reduced to a single struct, its bound to get more complicated (application commands?) - using type_signal_submit = sigc::signal>; + using type_signal_submit = sigc::signal; using type_signal_escape = sigc::signal; using type_signal_check_permission = sigc::signal; diff --git a/src/components/chatwindow.cpp b/src/components/chatwindow.cpp index 52c2a60..3e1885b 100644 --- a/src/components/chatwindow.cpp +++ b/src/components/chatwindow.cpp @@ -212,15 +212,18 @@ Snowflake ChatWindow::GetActiveChannel() const { return m_active_channel; } -bool ChatWindow::OnInputSubmit(const Glib::ustring &text, const std::vector &attachment_paths) { +bool ChatWindow::OnInputSubmit(ChatSubmitParams data) { if (!m_rate_limit_indicator->CanSpeak()) return false; - if (text.empty() && attachment_paths.empty()) + if (data.Message.empty() && data.Attachments.empty()) return false; + data.ChannelID = m_active_channel; + data.InReplyToID = m_replying_to; + if (m_active_channel.IsValid()) - m_signal_action_chat_submit.emit(text, attachment_paths, m_active_channel, m_replying_to); // m_replying_to is checked for invalid in the handler + m_signal_action_chat_submit.emit(data); // m_replying_to is checked for invalid in the handler if (m_is_replying) StopReplying(); diff --git a/src/components/chatwindow.hpp b/src/components/chatwindow.hpp index ab0bee1..ecbf666 100644 --- a/src/components/chatwindow.hpp +++ b/src/components/chatwindow.hpp @@ -3,6 +3,7 @@ #include #include #include "discord/discord.hpp" +#include "discord/chatsubmitparams.hpp" #include "completer.hpp" #include "state.hpp" @@ -55,7 +56,7 @@ protected: Snowflake m_active_channel; - bool OnInputSubmit(const Glib::ustring &text, const std::vector &attachment_paths); + bool OnInputSubmit(ChatSubmitParams data); bool OnKeyPressEvent(GdkEventKey *e); void OnScrollEdgeOvershot(Gtk::PositionType pos); @@ -84,7 +85,7 @@ protected: public: using type_signal_action_message_edit = sigc::signal; - using type_signal_action_chat_submit = sigc::signal, Snowflake, Snowflake>; + using type_signal_action_chat_submit = sigc::signal; using type_signal_action_chat_load_history = sigc::signal; using type_signal_action_channel_click = sigc::signal; using type_signal_action_insert_mention = sigc::signal; diff --git a/src/discord/chatsubmitparams.hpp b/src/discord/chatsubmitparams.hpp new file mode 100644 index 0000000..0f0a0cd --- /dev/null +++ b/src/discord/chatsubmitparams.hpp @@ -0,0 +1,22 @@ +#pragma once +#include +#include +#include +#include "discord/snowflake.hpp" + +struct ChatSubmitParams { + enum AttachmentType { + PastedImage, + ExtantFile, + }; + + struct Attachment { + std::string Path; + AttachmentType Type; + }; + + Snowflake ChannelID; + Snowflake InReplyToID; + Glib::ustring Message; + std::vector Attachments; +}; diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp index ffc1d4d..9849ae6 100644 --- a/src/discord/discord.cpp +++ b/src/discord/discord.cpp @@ -413,7 +413,7 @@ bool DiscordClient::CanManageMember(Snowflake guild_id, Snowflake actor, Snowfla return actor_highest->Position > target_highest->Position; } -void DiscordClient::ChatMessageCallback(const std::string &nonce, const http::response_type &response) { +void DiscordClient::ChatMessageCallback(const std::string &nonce, const http::response_type &response, const sigc::slot &callback) { if (!CheckCode(response)) { if (response.status_code == http::TooManyRequests) { try { // not sure if this body is guaranteed @@ -425,75 +425,79 @@ void DiscordClient::ChatMessageCallback(const std::string &nonce, const http::re } else { m_signal_message_send_fail.emit(nonce, 0); } + + // todo actually callback with correct error code (not necessary rn) + callback(DiscordError::GENERIC); + } else { + callback(DiscordError::NONE); } } -void DiscordClient::SendChatMessageAttachments(const std::string &content, const std::vector &attachment_paths, Snowflake channel, Snowflake referenced_message) { +void DiscordClient::SendChatMessageNoAttachments(const ChatSubmitParams ¶ms, const sigc::slot &callback) { const auto nonce = std::to_string(Snowflake::FromNow()); + CreateMessageObject obj; - obj.Content = content; + obj.Content = params.Message; obj.Nonce = nonce; - if (referenced_message.IsValid()) - obj.MessageReference.emplace().MessageID = referenced_message; + if (params.InReplyToID.IsValid()) + obj.MessageReference.emplace().MessageID = params.InReplyToID; - auto req = m_http.CreateRequest(http::REQUEST_POST, "/channels/" + std::to_string(channel) + "/messages"); - req.make_form(); - req.add_field("payload_json", nlohmann::json(obj).dump().c_str(), CURL_ZERO_TERMINATED); - for (size_t i = 0; i < attachment_paths.size(); i++) { - const auto field_name = "files[" + std::to_string(i) + "]"; - req.add_file(field_name, attachment_paths.at(i), "unknown.png"); - } - m_http.Execute(std::move(req), [this, attachment_paths, nonce](const http::response_type &res) { - for (const auto &path : attachment_paths) { - std::error_code ec; - std::filesystem::remove(path, ec); - } - ChatMessageCallback(nonce, res); - }); -} + m_http.MakePOST("/channels/" + std::to_string(params.ChannelID) + "/messages", + nlohmann::json(obj).dump(), + [this, nonce, callback](const http::response_type &r) { + ChatMessageCallback(nonce, r, callback); + }); -void DiscordClient::SendChatMessageText(const std::string &content, Snowflake channel, Snowflake referenced_message) { - // @([^@#]{1,32})#(\\d{4}) - const auto nonce = std::to_string(Snowflake::FromNow()); - CreateMessageObject obj; - obj.Content = content; - obj.Nonce = nonce; - if (referenced_message.IsValid()) - obj.MessageReference.emplace().MessageID = referenced_message; - m_http.MakePOST("/channels/" + std::to_string(channel) + "/messages", nlohmann::json(obj).dump(), sigc::bind<0>(sigc::mem_fun(*this, &DiscordClient::ChatMessageCallback), nonce)); - // dummy data so the content can be shown while waiting for MESSAGE_CREATE + // dummy preview data Message tmp; - tmp.Content = content; + tmp.Content = params.Message; tmp.ID = nonce; - tmp.ChannelID = channel; + tmp.ChannelID = params.ChannelID; tmp.Author = GetUserData(); tmp.IsTTS = false; tmp.DoesMentionEveryone = false; tmp.Type = MessageType::DEFAULT; tmp.IsPinned = false; tmp.Timestamp = "2000-01-01T00:00:00.000000+00:00"; - tmp.Nonce = obj.Nonce; + tmp.Nonce = nonce; tmp.IsPending = true; + m_store.SetMessage(tmp.ID, tmp); - m_signal_message_sent.emit(tmp); + m_signal_message_create.emit(tmp); } -void DiscordClient::SendChatMessage(const std::string &content, const std::vector &attachment_paths, Snowflake channel) { - if (attachment_paths.empty()) - SendChatMessageText(content, channel); - else { - puts("attach"); - SendChatMessageAttachments(content, attachment_paths, channel, Snowflake::Invalid); +void DiscordClient::SendChatMessageAttachments(const ChatSubmitParams ¶ms, const sigc::slot &callback) { + const auto nonce = std::to_string(Snowflake::FromNow()); + + CreateMessageObject obj; + obj.Content = params.Message; + obj.Nonce = nonce; + if (params.InReplyToID.IsValid()) + obj.MessageReference.emplace().MessageID = params.InReplyToID; + + auto req = m_http.CreateRequest(http::REQUEST_POST, "/channels/" + std::to_string(params.ChannelID) + "/messages"); + req.make_form(); + req.add_field("payload_json", nlohmann::json(obj).dump().c_str(), CURL_ZERO_TERMINATED); + for (size_t i = 0; i < params.Attachments.size(); i++) { + const auto field_name = "files[" + std::to_string(i) + "]"; + req.add_file(field_name, params.Attachments.at(i).Path, "unknown.png"); } + m_http.Execute(std::move(req), [this, params, nonce, callback](const http::response_type &res) { + for (const auto &attachment : params.Attachments) { + if (attachment.Type == ChatSubmitParams::AttachmentType::PastedImage) { + std::error_code ec; + std::filesystem::remove(attachment.Path, ec); + } + } + ChatMessageCallback(nonce, res, callback); + }); } -void DiscordClient::SendChatMessage(const std::string &content, const std::vector &attachment_paths, Snowflake channel, Snowflake referenced_message) { - if (attachment_paths.empty()) - SendChatMessageText(content, channel, referenced_message); - else { - puts("attach"); - SendChatMessageAttachments(content, attachment_paths, channel, referenced_message); - } +void DiscordClient::SendChatMessage(const ChatSubmitParams ¶ms, const sigc::slot &callback) { + if (params.Attachments.empty()) + SendChatMessageNoAttachments(params, callback); + else + SendChatMessageAttachments(params, callback); } void DiscordClient::DeleteMessage(Snowflake channel_id, Snowflake id) { @@ -1302,6 +1306,7 @@ void DiscordClient::HandleGatewayMessage(std::string str) { HandleGatewayInvalidSession(m); } break; case GatewayOp::Dispatch: { + puts(m.Type.c_str()); auto iter = m_event_map.find(m.Type); if (iter == m_event_map.end()) { printf("Unknown event %s\n", m.Type.c_str()); diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp index 9322e3f..41d5aab 100644 --- a/src/discord/discord.hpp +++ b/src/discord/discord.hpp @@ -3,6 +3,7 @@ #include "httpclient.hpp" #include "objects.hpp" #include "store.hpp" +#include "chatsubmitparams.hpp" #include #include #include @@ -103,10 +104,11 @@ public: Permission ComputeOverwrites(Permission base, Snowflake member_id, Snowflake channel_id) const; bool CanManageMember(Snowflake guild_id, Snowflake actor, Snowflake target) const; // kick, ban, edit nickname (cant think of a better name) - void ChatMessageCallback(const std::string &nonce, const http::response_type &response); + void ChatMessageCallback(const std::string &nonce, const http::response_type &response, const sigc::slot &callback); + void SendChatMessageNoAttachments(const ChatSubmitParams ¶ms, const sigc::slot &callback); + void SendChatMessageAttachments(const ChatSubmitParams ¶ms, const sigc::slot &callback); - void SendChatMessage(const std::string &content, const std::vector &attachment_paths, Snowflake channel); - void SendChatMessage(const std::string &content, const std::vector &attachment_paths, Snowflake channel, Snowflake referenced_message); + void SendChatMessage(const ChatSubmitParams ¶ms, const sigc::slot &callback); void DeleteMessage(Snowflake channel_id, Snowflake id); void EditMessage(Snowflake channel_id, Snowflake id, std::string content); void SendLazyLoad(Snowflake id); @@ -224,9 +226,6 @@ private: std::vector m_decompress_buf; z_stream m_zstream; - void SendChatMessageAttachments(const std::string &content, const std::vector &attachment_paths, Snowflake channel, Snowflake referenced_message = Snowflake::Invalid); - void SendChatMessageText(const std::string &content, Snowflake channel, Snowflake referenced_message = Snowflake::Invalid); - static std::string GetAPIURL(); static std::string GetGatewayURL(); -- cgit v1.2.3 From 4ee7025ab09b606a2556bf9f42c1218d7fd72843 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Fri, 17 Jun 2022 02:46:55 -0400 Subject: add file upload via dnd + rework http --- src/abaddon.cpp | 2 +- src/components/chatinput.cpp | 92 +++++++++++++++++++++++++++++++++------- src/components/chatinput.hpp | 19 ++++++--- src/components/chatwindow.cpp | 4 ++ src/components/chatwindow.hpp | 1 + src/discord/chatsubmitparams.hpp | 4 +- src/discord/discord.cpp | 5 +-- src/http.cpp | 24 +++++++++-- src/http.hpp | 8 ++-- src/windows/mainwindow.cpp | 17 ++++++++ src/windows/mainwindow.hpp | 3 ++ 11 files changed, 146 insertions(+), 33 deletions(-) (limited to 'src/abaddon.cpp') diff --git a/src/abaddon.cpp b/src/abaddon.cpp index 28b6262..cfbf6b8 100644 --- a/src/abaddon.cpp +++ b/src/abaddon.cpp @@ -746,7 +746,7 @@ void Abaddon::ActionChatLoadHistory(Snowflake id) { static void ChatMessageSentCallback(const ChatSubmitParams &data) { printf("completed for %s\n", data.Message.c_str()); for (const auto &attachment : data.Attachments) { - puts(attachment.Path.c_str()); + puts(attachment.File->get_path().c_str()); } } diff --git a/src/components/chatinput.cpp b/src/components/chatinput.cpp index 5a6e71e..269a2ac 100644 --- a/src/components/chatinput.cpp +++ b/src/components/chatinput.cpp @@ -105,8 +105,7 @@ ChatInputAttachmentContainer::ChatInputAttachmentContainer() void ChatInputAttachmentContainer::Clear() { for (auto *item : m_attachments) { - std::error_code ec; - std::filesystem::remove(item->GetPath(), ec); + item->RemoveIfTemp(); delete item; } m_attachments.clear(); @@ -133,16 +132,36 @@ bool ChatInputAttachmentContainer::AddImage(const Glib::RefPtr &pb) return false; } - auto *item = Gtk::make_managed(path, pb); + auto *item = Gtk::make_managed(Gio::File::create_for_path(path), pb); item->show(); item->set_valign(Gtk::ALIGN_CENTER); m_box.add(*item); m_attachments.push_back(item); - item->signal_remove().connect([this, item] { - std::error_code ec; - std::filesystem::remove(item->GetPath(), ec); + item->signal_item_removed().connect([this, item] { + item->RemoveIfTemp(); + if (auto it = std::find(m_attachments.begin(), m_attachments.end(), item); it != m_attachments.end()) + m_attachments.erase(it); + delete item; + if (m_attachments.empty()) + m_signal_emptied.emit(); + }); + + return true; +} + +bool ChatInputAttachmentContainer::AddFile(const Glib::RefPtr &file) { + if (m_attachments.size() == 10) return false; + + auto *item = Gtk::make_managed(file); + item->show(); + item->set_valign(Gtk::ALIGN_CENTER); + m_box.add(*item); + + m_attachments.push_back(item); + + item->signal_item_removed().connect([this, item] { if (auto it = std::find(m_attachments.begin(), m_attachments.end(), item); it != m_attachments.end()) m_attachments.erase(it); delete item; @@ -155,8 +174,11 @@ bool ChatInputAttachmentContainer::AddImage(const Glib::RefPtr &pb) std::vector ChatInputAttachmentContainer::GetAttachments() const { std::vector ret; - for (auto *x : m_attachments) - ret.push_back({ x->GetPath(), x->GetType() }); + for (auto *x : m_attachments) { + if (!x->GetFile()->query_exists()) + puts("bad!"); + ret.push_back({ x->GetFile(), x->GetType(), x->GetFilename() }); + } return ret; } @@ -164,10 +186,28 @@ ChatInputAttachmentContainer::type_signal_emptied ChatInputAttachmentContainer:: return m_signal_emptied; } -ChatInputAttachmentItem::ChatInputAttachmentItem(std::string path, const Glib::RefPtr &pb) - : m_path(std::move(path)) +ChatInputAttachmentItem::ChatInputAttachmentItem(const Glib::RefPtr &file) + : m_file(file) + , m_img(Gtk::make_managed(Abaddon::Get().GetImageManager().GetPlaceholder(AttachmentItemSize))) + , m_type(ChatSubmitParams::ExtantFile) { + get_style_context()->add_class("attachment-item"); + + set_size_request(AttachmentItemSize, AttachmentItemSize); + m_box.add(*m_img); + add(m_box); + show_all_children(); + + SetupMenu(); + + auto info = m_file->query_info(G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME); + m_filename = info->get_attribute_string(G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME); +} + +ChatInputAttachmentItem::ChatInputAttachmentItem(const Glib::RefPtr &file, const Glib::RefPtr &pb) + : m_file(file) , m_img(Gtk::make_managed()) - , m_type(ChatSubmitParams::PastedImage) { + , m_type(ChatSubmitParams::PastedImage) + , m_filename("unknown.png") { get_style_context()->add_class("attachment-item"); int outw, outh; @@ -182,18 +222,31 @@ ChatInputAttachmentItem::ChatInputAttachmentItem(std::string path, const Glib::R SetupMenu(); } -std::string ChatInputAttachmentItem::GetPath() const { - return m_path; +Glib::RefPtr ChatInputAttachmentItem::GetFile() const { + return m_file; } ChatSubmitParams::AttachmentType ChatInputAttachmentItem::GetType() const { return m_type; } +std::string ChatInputAttachmentItem::GetFilename() const { + return m_filename; +} + +bool ChatInputAttachmentItem::IsTemp() const noexcept { + return m_type == ChatSubmitParams::PastedImage; +} + +void ChatInputAttachmentItem::RemoveIfTemp() { + if (IsTemp()) + m_file->remove(); +} + void ChatInputAttachmentItem::SetupMenu() { m_menu_remove.set_label("Remove"); m_menu_remove.signal_activate().connect([this] { - m_signal_remove.emit(); + m_signal_item_removed.emit(); }); m_menu.add(m_menu_remove); @@ -209,8 +262,8 @@ void ChatInputAttachmentItem::SetupMenu() { }); } -ChatInputAttachmentItem::type_signal_remove ChatInputAttachmentItem::signal_remove() { - return m_signal_remove; +ChatInputAttachmentItem::type_signal_item_removed ChatInputAttachmentItem::signal_item_removed() { + return m_signal_item_removed; } ChatInput::ChatInput() @@ -271,6 +324,13 @@ bool ChatInput::ProcessKeyPress(GdkEventKey *event) { return m_input.ProcessKeyPress(event); } +void ChatInput::AddAttachment(const Glib::RefPtr &file) { + const bool can_attach_files = m_signal_check_permission.emit(Permission::ATTACH_FILES); + + if (can_attach_files && m_attachments.AddFile(file)) + m_attachments_revealer.set_reveal_child(true); +} + ChatInput::type_signal_submit ChatInput::signal_submit() { return m_signal_submit; } diff --git a/src/components/chatinput.hpp b/src/components/chatinput.hpp index 254f96f..865b23e 100644 --- a/src/components/chatinput.hpp +++ b/src/components/chatinput.hpp @@ -5,10 +5,14 @@ class ChatInputAttachmentItem : public Gtk::EventBox { public: - ChatInputAttachmentItem(std::string path, const Glib::RefPtr &pb); + ChatInputAttachmentItem(const Glib::RefPtr &file); + ChatInputAttachmentItem(const Glib::RefPtr &file, const Glib::RefPtr &pb); - [[nodiscard]] std::string GetPath() const; + [[nodiscard]] Glib::RefPtr GetFile() const; [[nodiscard]] ChatSubmitParams::AttachmentType GetType() const; + [[nodiscard]] std::string GetFilename() const; + [[nodiscard]] bool IsTemp() const noexcept; + void RemoveIfTemp(); private: void SetupMenu(); @@ -19,16 +23,17 @@ private: Gtk::Box m_box; Gtk::Image *m_img = nullptr; - std::string m_path; + Glib::RefPtr m_file; ChatSubmitParams::AttachmentType m_type; + std::string m_filename; private: - using type_signal_remove = sigc::signal; + using type_signal_item_removed = sigc::signal; - type_signal_remove m_signal_remove; + type_signal_item_removed m_signal_item_removed; public: - type_signal_remove signal_remove(); + type_signal_item_removed signal_item_removed(); }; class ChatInputAttachmentContainer : public Gtk::ScrolledWindow { @@ -38,6 +43,7 @@ public: void Clear(); void ClearNoPurge(); bool AddImage(const Glib::RefPtr &pb); + bool AddFile(const Glib::RefPtr &file); [[nodiscard]] std::vector GetAttachments() const; private: @@ -92,6 +98,7 @@ public: void InsertText(const Glib::ustring &text); Glib::RefPtr GetBuffer(); bool ProcessKeyPress(GdkEventKey *event); + void AddAttachment(const Glib::RefPtr &file); private: Gtk::Revealer m_attachments_revealer; diff --git a/src/components/chatwindow.cpp b/src/components/chatwindow.cpp index 3e1885b..d9d490b 100644 --- a/src/components/chatwindow.cpp +++ b/src/components/chatwindow.cpp @@ -170,6 +170,10 @@ void ChatWindow::SetTopic(const std::string &text) { m_topic.set_visible(text.length() > 0); } +void ChatWindow::AddAttachment(const Glib::RefPtr &file) { + m_input->AddAttachment(file); +} + #ifdef WITH_LIBHANDY void ChatWindow::OpenNewTab(Snowflake id) { // open if its the first tab (in which case it really isnt a tab but whatever) diff --git a/src/components/chatwindow.hpp b/src/components/chatwindow.hpp index ecbf666..917456b 100644 --- a/src/components/chatwindow.hpp +++ b/src/components/chatwindow.hpp @@ -35,6 +35,7 @@ public: Snowflake GetOldestListedMessage(); // oldest message that is currently in the ListBox void UpdateReactions(Snowflake id); void SetTopic(const std::string &text); + void AddAttachment(const Glib::RefPtr &file); #ifdef WITH_LIBHANDY void OpenNewTab(Snowflake id); diff --git a/src/discord/chatsubmitparams.hpp b/src/discord/chatsubmitparams.hpp index 0f0a0cd..6199634 100644 --- a/src/discord/chatsubmitparams.hpp +++ b/src/discord/chatsubmitparams.hpp @@ -2,6 +2,7 @@ #include #include #include +#include #include "discord/snowflake.hpp" struct ChatSubmitParams { @@ -11,8 +12,9 @@ struct ChatSubmitParams { }; struct Attachment { - std::string Path; + Glib::RefPtr File; AttachmentType Type; + std::string Filename; }; Snowflake ChannelID; diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp index 9849ae6..d26f1fe 100644 --- a/src/discord/discord.cpp +++ b/src/discord/discord.cpp @@ -480,13 +480,12 @@ void DiscordClient::SendChatMessageAttachments(const ChatSubmitParams ¶ms, c req.add_field("payload_json", nlohmann::json(obj).dump().c_str(), CURL_ZERO_TERMINATED); for (size_t i = 0; i < params.Attachments.size(); i++) { const auto field_name = "files[" + std::to_string(i) + "]"; - req.add_file(field_name, params.Attachments.at(i).Path, "unknown.png"); + req.add_file(field_name, params.Attachments.at(i).File, params.Attachments.at(i).Filename); } m_http.Execute(std::move(req), [this, params, nonce, callback](const http::response_type &res) { for (const auto &attachment : params.Attachments) { if (attachment.Type == ChatSubmitParams::AttachmentType::PastedImage) { - std::error_code ec; - std::filesystem::remove(attachment.Path, ec); + attachment.File->remove(); } } ChatMessageCallback(nonce, res, callback); diff --git a/src/http.cpp b/src/http.cpp index beb1944..14fe3f5 100644 --- a/src/http.cpp +++ b/src/http.cpp @@ -35,7 +35,8 @@ request::request(request &&other) noexcept , m_method(std::exchange(other.m_method, nullptr)) , m_header_list(std::exchange(other.m_header_list, nullptr)) , m_error_buf(other.m_error_buf) - , m_form(std::exchange(other.m_form, nullptr)) { + , m_form(std::exchange(other.m_form, nullptr)) + , m_read_streams(std::move(other.m_read_streams)) { // i think this is correct??? } @@ -82,12 +83,29 @@ void request::make_form() { m_form = curl_mime_init(m_curl); } +static size_t http_readfunc(char *buffer, size_t size, size_t nitems, void *arg) { + auto stream = Glib::wrap(G_FILE_INPUT_STREAM(arg), true); + int r = stream->read(buffer, size * nitems); + if (r == -1) { + // https://github.com/curl/curl/blob/ad9bc5976d6661cd5b03ebc379313bf657701c14/lib/mime.c#L724 + return size_t(-1); + } + return r; +} + // file must exist until request completes -void request::add_file(std::string_view name, std::string_view file_path, std::string_view filename) { +void request::add_file(std::string_view name, const Glib::RefPtr &file, std::string_view filename) { + if (!file->query_exists()) return; + auto *field = curl_mime_addpart(m_form); curl_mime_name(field, name.data()); - curl_mime_filedata(field, file_path.data()); + auto info = file->query_info(); + auto stream = file->read(); + curl_mime_data_cb(field, info->get_size(), http_readfunc, nullptr, nullptr, stream->gobj()); curl_mime_filename(field, filename.data()); + + // hold ref + m_read_streams.insert(stream); } // copied diff --git a/src/http.hpp b/src/http.hpp index 0c570db..a6f479d 100644 --- a/src/http.hpp +++ b/src/http.hpp @@ -1,9 +1,9 @@ #pragma once #include +#include #include #include - -// i regret not using snake case for everything oh well +#include namespace http { enum EStatusCode : int { @@ -115,7 +115,7 @@ struct request { void set_body(const std::string &data); void set_user_agent(const std::string &data); void make_form(); - void add_file(std::string_view name, std::string_view file_path, std::string_view filename); + void add_file(std::string_view name, const Glib::RefPtr &file, std::string_view filename); void add_field(std::string_view name, const char *data, size_t size); response execute(); @@ -129,6 +129,8 @@ private: curl_slist *m_header_list = nullptr; std::array m_error_buf = { 0 }; curl_mime *m_form = nullptr; + + std::set> m_read_streams; }; using response_type = response; diff --git a/src/windows/mainwindow.cpp b/src/windows/mainwindow.cpp index a6a17c5..17edfa3 100644 --- a/src/windows/mainwindow.cpp +++ b/src/windows/mainwindow.cpp @@ -76,6 +76,7 @@ MainWindow::MainWindow() add(m_main_box); SetupMenu(); + SetupDND(); } void MainWindow::UpdateComponents() { @@ -350,6 +351,22 @@ void MainWindow::SetupMenu() { #endif } +void MainWindow::SetupDND() { + std::vector targets; + targets.emplace_back("text/uri-list", Gtk::TargetFlags(0), 0); + drag_dest_set(targets, Gtk::DEST_DEFAULT_DROP | Gtk::DEST_DEFAULT_MOTION | Gtk::DEST_DEFAULT_HIGHLIGHT, Gdk::DragAction::ACTION_COPY); + signal_drag_data_received().connect([this](const Glib::RefPtr &ctx, int x, int y, const Gtk::SelectionData &selection, guint info, guint time) { + HandleDroppedURIs(selection); + }); +} + +void MainWindow::HandleDroppedURIs(const Gtk::SelectionData &selection) { + for (const auto &uri : selection.get_uris()) { + // not using Glib::get_filename_for_uri or whatever because the conversion is BAD (on windows at least) + m_chat.AddAttachment(Gio::File::create_for_uri(uri)); + } +} + MainWindow::type_signal_action_connect MainWindow::signal_action_connect() { return m_signal_action_connect; } diff --git a/src/windows/mainwindow.hpp b/src/windows/mainwindow.hpp index e67f6d2..b5b6fc1 100644 --- a/src/windows/mainwindow.hpp +++ b/src/windows/mainwindow.hpp @@ -39,6 +39,9 @@ public: private: void SetupMenu(); + void SetupDND(); + + void HandleDroppedURIs(const Gtk::SelectionData &selection); Gtk::Box m_main_box; Gtk::Box m_content_box; -- cgit v1.2.3 From d841a2c862eddc6b2053b48907f8e1400e2d1391 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Thu, 23 Jun 2022 00:48:00 -0400 Subject: add change filename --- src/abaddon.cpp | 10 ++++++++++ src/abaddon.hpp | 1 + src/components/chatinput.cpp | 15 +++++++++++++++ src/components/chatinput.hpp | 2 ++ src/dialogs/textinput.cpp | 26 ++++++++++++++++++++++++++ src/dialogs/textinput.hpp | 14 ++++++++++++++ 6 files changed, 68 insertions(+) create mode 100644 src/dialogs/textinput.cpp create mode 100644 src/dialogs/textinput.hpp (limited to 'src/abaddon.cpp') diff --git a/src/abaddon.cpp b/src/abaddon.cpp index cfbf6b8..a34f444 100644 --- a/src/abaddon.cpp +++ b/src/abaddon.cpp @@ -11,6 +11,7 @@ #include "dialogs/setstatus.hpp" #include "dialogs/friendpicker.hpp" #include "dialogs/verificationgate.hpp" +#include "dialogs/textinput.hpp" #include "abaddon.hpp" #include "windows/guildsettingswindow.hpp" #include "windows/profilewindow.hpp" @@ -867,6 +868,15 @@ void Abaddon::ActionViewThreads(Snowflake channel_id) { window->show(); } +std::optional Abaddon::ShowTextPrompt(const Glib::ustring &prompt, const Glib::ustring &title, const Glib::ustring &placeholder, Gtk::Window *window) { + TextInputDialog dlg(prompt, title, placeholder, window != nullptr ? *window : *m_main_window); + const auto code = dlg.run(); + if (code == Gtk::RESPONSE_OK) + return dlg.GetInput(); + else + return {}; +} + bool Abaddon::ShowConfirm(const Glib::ustring &prompt, Gtk::Window *window) { ConfirmDialog dlg(window != nullptr ? *window : *m_main_window); dlg.SetConfirmText(prompt); diff --git a/src/abaddon.hpp b/src/abaddon.hpp index 1ba1251..c267269 100644 --- a/src/abaddon.hpp +++ b/src/abaddon.hpp @@ -51,6 +51,7 @@ public: void ActionViewPins(Snowflake channel_id); void ActionViewThreads(Snowflake channel_id); + std::optional ShowTextPrompt(const Glib::ustring &prompt, const Glib::ustring &title, const Glib::ustring &placeholder = "", Gtk::Window *window = nullptr); bool ShowConfirm(const Glib::ustring &prompt, Gtk::Window *window = nullptr); void ActionReloadCSS(); diff --git a/src/components/chatinput.cpp b/src/components/chatinput.cpp index 269a2ac..a734dec 100644 --- a/src/components/chatinput.cpp +++ b/src/components/chatinput.cpp @@ -220,6 +220,7 @@ ChatInputAttachmentItem::ChatInputAttachmentItem(const Glib::RefPtr & show_all_children(); SetupMenu(); + UpdateTooltip(); } Glib::RefPtr ChatInputAttachmentItem::GetFile() const { @@ -249,6 +250,16 @@ void ChatInputAttachmentItem::SetupMenu() { m_signal_item_removed.emit(); }); + m_menu_set_filename.set_label("Change Filename"); + m_menu_set_filename.signal_activate().connect([this] { + const auto name = Abaddon::Get().ShowTextPrompt("Enter new filename for attachment", "Enter filename", m_filename); + if (name.has_value()) { + m_filename = *name; + UpdateTooltip(); + } + }); + + m_menu.add(m_menu_set_filename); m_menu.add(m_menu_remove); m_menu.show_all(); @@ -262,6 +273,10 @@ void ChatInputAttachmentItem::SetupMenu() { }); } +void ChatInputAttachmentItem::UpdateTooltip() { + set_tooltip_text(m_filename); +} + ChatInputAttachmentItem::type_signal_item_removed ChatInputAttachmentItem::signal_item_removed() { return m_signal_item_removed; } diff --git a/src/components/chatinput.hpp b/src/components/chatinput.hpp index 865b23e..8a4ad8d 100644 --- a/src/components/chatinput.hpp +++ b/src/components/chatinput.hpp @@ -16,9 +16,11 @@ public: private: void SetupMenu(); + void UpdateTooltip(); Gtk::Menu m_menu; Gtk::MenuItem m_menu_remove; + Gtk::MenuItem m_menu_set_filename; Gtk::Box m_box; Gtk::Image *m_img = nullptr; diff --git a/src/dialogs/textinput.cpp b/src/dialogs/textinput.cpp new file mode 100644 index 0000000..ae75f70 --- /dev/null +++ b/src/dialogs/textinput.cpp @@ -0,0 +1,26 @@ +#include "textinput.hpp" + +TextInputDialog::TextInputDialog(const Glib::ustring &prompt, const Glib::ustring &title, const Glib::ustring &placeholder, Gtk::Window &parent) + : Gtk::Dialog(title, parent, true) + , m_label(prompt) { + get_style_context()->add_class("app-window"); + get_style_context()->add_class("app-popup"); + + auto ok = add_button("OK", Gtk::RESPONSE_OK); + auto cancel = add_button("Cancel", Gtk::RESPONSE_CANCEL); + + get_content_area()->add(m_label); + get_content_area()->add(m_entry); + + m_entry.set_text(placeholder); + + m_entry.set_activates_default(true); + ok->set_can_default(true); + ok->grab_default(); + + show_all_children(); +} + +Glib::ustring TextInputDialog::GetInput() const { + return m_entry.get_text(); +} diff --git a/src/dialogs/textinput.hpp b/src/dialogs/textinput.hpp new file mode 100644 index 0000000..fd2d2b8 --- /dev/null +++ b/src/dialogs/textinput.hpp @@ -0,0 +1,14 @@ +#pragma once +#include +#include + +class TextInputDialog : public Gtk::Dialog { +public: + TextInputDialog(const Glib::ustring &prompt, const Glib::ustring &title, const Glib::ustring &placeholder, Gtk::Window &parent); + + Glib::ustring GetInput() const; + +private: + Gtk::Label m_label; + Gtk::Entry m_entry; +}; -- cgit v1.2.3 From 02741f2c1beb499b188f6840c5384eb9c4f06c6d Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Mon, 8 Aug 2022 02:41:54 -0400 Subject: remove unnecessary verbosity --- src/abaddon.cpp | 11 +---------- src/http.cpp | 1 - 2 files changed, 1 insertion(+), 11 deletions(-) (limited to 'src/abaddon.cpp') diff --git a/src/abaddon.cpp b/src/abaddon.cpp index ce3bdb3..563a42c 100644 --- a/src/abaddon.cpp +++ b/src/abaddon.cpp @@ -744,22 +744,13 @@ void Abaddon::ActionChatLoadHistory(Snowflake id) { }); } -static void ChatMessageSentCallback(const ChatSubmitParams &data) { - printf("completed for %s\n", data.Message.c_str()); - for (const auto &attachment : data.Attachments) { - puts(attachment.File->get_path().c_str()); - } -} - void Abaddon::ActionChatInputSubmit(ChatSubmitParams data) { if (data.Message.substr(0, 7) == "/shrug " || data.Message == "/shrug") data.Message = data.Message.substr(6) + "\xC2\xAF\x5C\x5F\x28\xE3\x83\x84\x29\x5F\x2F\xC2\xAF"; // this is important if (!m_discord.HasChannelPermission(m_discord.GetUserData().ID, data.ChannelID, Permission::VIEW_CHANNEL)) return; - m_discord.SendChatMessage(data, [data](DiscordError code) { - ChatMessageSentCallback(data); - }); + m_discord.SendChatMessage(data, NOOP_CALLBACK); } void Abaddon::ActionChatEditMessage(Snowflake channel_id, Snowflake id) { diff --git a/src/http.cpp b/src/http.cpp index f4d45a2..ba3ce3c 100644 --- a/src/http.cpp +++ b/src/http.cpp @@ -129,7 +129,6 @@ void request::add_file(std::string_view name, const Glib::RefPtr &fil // copied void request::add_field(std::string_view name, const char *data, size_t size) { - puts(name.data()); auto *field = curl_mime_addpart(m_form); curl_mime_name(field, name.data()); curl_mime_data(field, data, size); -- cgit v1.2.3