forked from OpenGamers/abaddon
add file upload via dnd + rework http
This commit is contained in:
parent
d0fa308f6e
commit
4ee7025ab0
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<Gdk::Pixbuf> &pb)
|
||||
return false;
|
||||
}
|
||||
|
||||
auto *item = Gtk::make_managed<ChatInputAttachmentItem>(path, pb);
|
||||
auto *item = Gtk::make_managed<ChatInputAttachmentItem>(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<Gio::File> &file) {
|
||||
if (m_attachments.size() == 10) return false;
|
||||
|
||||
auto *item = Gtk::make_managed<ChatInputAttachmentItem>(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<Gdk::Pixbuf> &pb)
|
||||
|
||||
std::vector<ChatSubmitParams::Attachment> ChatInputAttachmentContainer::GetAttachments() const {
|
||||
std::vector<ChatSubmitParams::Attachment> 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<Gdk::Pixbuf> &pb)
|
||||
: m_path(std::move(path))
|
||||
ChatInputAttachmentItem::ChatInputAttachmentItem(const Glib::RefPtr<Gio::File> &file)
|
||||
: m_file(file)
|
||||
, m_img(Gtk::make_managed<Gtk::Image>(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<Gio::File> &file, const Glib::RefPtr<Gdk::Pixbuf> &pb)
|
||||
: m_file(file)
|
||||
, m_img(Gtk::make_managed<Gtk::Image>())
|
||||
, 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<Gio::File> 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<Gio::File> &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;
|
||||
}
|
||||
|
@ -5,10 +5,14 @@
|
||||
|
||||
class ChatInputAttachmentItem : public Gtk::EventBox {
|
||||
public:
|
||||
ChatInputAttachmentItem(std::string path, const Glib::RefPtr<Gdk::Pixbuf> &pb);
|
||||
ChatInputAttachmentItem(const Glib::RefPtr<Gio::File> &file);
|
||||
ChatInputAttachmentItem(const Glib::RefPtr<Gio::File> &file, const Glib::RefPtr<Gdk::Pixbuf> &pb);
|
||||
|
||||
[[nodiscard]] std::string GetPath() const;
|
||||
[[nodiscard]] Glib::RefPtr<Gio::File> 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<Gio::File> m_file;
|
||||
ChatSubmitParams::AttachmentType m_type;
|
||||
std::string m_filename;
|
||||
|
||||
private:
|
||||
using type_signal_remove = sigc::signal<void>;
|
||||
using type_signal_item_removed = sigc::signal<void>;
|
||||
|
||||
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<Gdk::Pixbuf> &pb);
|
||||
bool AddFile(const Glib::RefPtr<Gio::File> &file);
|
||||
[[nodiscard]] std::vector<ChatSubmitParams::Attachment> GetAttachments() const;
|
||||
|
||||
private:
|
||||
@ -92,6 +98,7 @@ public:
|
||||
void InsertText(const Glib::ustring &text);
|
||||
Glib::RefPtr<Gtk::TextBuffer> GetBuffer();
|
||||
bool ProcessKeyPress(GdkEventKey *event);
|
||||
void AddAttachment(const Glib::RefPtr<Gio::File> &file);
|
||||
|
||||
private:
|
||||
Gtk::Revealer m_attachments_revealer;
|
||||
|
@ -170,6 +170,10 @@ void ChatWindow::SetTopic(const std::string &text) {
|
||||
m_topic.set_visible(text.length() > 0);
|
||||
}
|
||||
|
||||
void ChatWindow::AddAttachment(const Glib::RefPtr<Gio::File> &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)
|
||||
|
@ -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<Gio::File> &file);
|
||||
|
||||
#ifdef WITH_LIBHANDY
|
||||
void OpenNewTab(Snowflake id);
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <glibmm/ustring.h>
|
||||
#include <giomm/file.h>
|
||||
#include "discord/snowflake.hpp"
|
||||
|
||||
struct ChatSubmitParams {
|
||||
@ -11,8 +12,9 @@ struct ChatSubmitParams {
|
||||
};
|
||||
|
||||
struct Attachment {
|
||||
std::string Path;
|
||||
Glib::RefPtr<Gio::File> File;
|
||||
AttachmentType Type;
|
||||
std::string Filename;
|
||||
};
|
||||
|
||||
Snowflake ChannelID;
|
||||
|
@ -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);
|
||||
|
24
src/http.cpp
24
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<Gio::File> &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
|
||||
|
@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <curl/curl.h>
|
||||
|
||||
// i regret not using snake case for everything oh well
|
||||
#include <giomm/file.h>
|
||||
|
||||
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<Gio::File> &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<char, CURL_ERROR_SIZE> m_error_buf = { 0 };
|
||||
curl_mime *m_form = nullptr;
|
||||
|
||||
std::set<Glib::RefPtr<Gio::FileInputStream>> m_read_streams;
|
||||
};
|
||||
|
||||
using response_type = response;
|
||||
|
@ -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<Gtk::TargetEntry> 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<Gdk::DragContext> &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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user