From 65fab779c5a447efa367c70879cd29e9a57b1960 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 23 Sep 2019 19:36:52 +0200 Subject: [PATCH 01/27] Redo all changes done before breaking the branch. --- chatterino.pro | 2 ++ docs/ENV.md | 10 +++++++ src/common/Env.cpp | 2 ++ src/common/Env.hpp | 1 + src/widgets/helper/ResizingTextEdit.cpp | 36 ++++++++++++++++++++----- src/widgets/helper/ResizingTextEdit.hpp | 4 +++ src/widgets/splits/Split.cpp | 9 ++++++- 7 files changed, 56 insertions(+), 8 deletions(-) diff --git a/chatterino.pro b/chatterino.pro index 0230bba30..169711a94 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -180,6 +180,7 @@ SOURCES += \ src/util/JsonQuery.cpp \ src/util/RapidjsonHelpers.cpp \ src/util/StreamLink.cpp \ + src/util/NuulsUploader.cpp \ src/widgets/AccountSwitchPopup.cpp \ src/widgets/AccountSwitchWidget.cpp \ src/widgets/AttachedWindow.cpp \ @@ -369,6 +370,7 @@ HEADERS += \ src/util/SharedPtrElementLess.hpp \ src/util/StandardItemHelper.hpp \ src/util/StreamLink.hpp \ + src/util/NuulsUploader.hpp \ src/widgets/AccountSwitchPopup.hpp \ src/widgets/AccountSwitchWidget.hpp \ src/widgets/AttachedWindow.hpp \ diff --git a/docs/ENV.md b/docs/ENV.md index 727503ff4..bddef4687 100644 --- a/docs/ENV.md +++ b/docs/ENV.md @@ -18,3 +18,13 @@ Used to change the URL that Chatterino2 uses when trying to get emote set inform Default value: `https://braize.pajlada.com/chatterino/twitchemotes/set/%1/` Arguments: - `%1` = Emote set ID + +### CHATTERINO2_IMAGE_PASTE_SITE_URL +Used to change the URL that Chatterino2 uses when uploading an image by pasting it into the input box. +Default value: `https://i.nuuls.com/upload` + +Arguments: + - None + +Notes: + - The server that's running the web page MUST be compatible with [Nuuls' filehost](https://github.com/nuuls/filehost) diff --git a/src/common/Env.cpp b/src/common/Env.cpp index ff711ec47..046febf53 100644 --- a/src/common/Env.cpp +++ b/src/common/Env.cpp @@ -28,6 +28,8 @@ Env::Env() , twitchEmoteSetResolverUrl(readStringEnv( "CHATTERINO2_TWITCH_EMOTE_SET_RESOLVER_URL", "https://braize.pajlada.com/chatterino/twitchemotes/set/%1/")) + , imagePasteSiteUrl(readStringEnv("CHATTERINO2_IMAGE_PASTE_SITE_URL", + "https://i.nuuls.com/upload")) { } diff --git a/src/common/Env.hpp b/src/common/Env.hpp index 2dc7fa1ba..a47dd3aff 100644 --- a/src/common/Env.hpp +++ b/src/common/Env.hpp @@ -14,6 +14,7 @@ public: const QString recentMessagesApiUrl; const QString linkResolverUrl; const QString twitchEmoteSetResolverUrl; + const QString imagePasteSiteUrl; }; } // namespace chatterino diff --git a/src/widgets/helper/ResizingTextEdit.cpp b/src/widgets/helper/ResizingTextEdit.cpp index edaa11738..4ed3ba0cd 100644 --- a/src/widgets/helper/ResizingTextEdit.cpp +++ b/src/widgets/helper/ResizingTextEdit.cpp @@ -1,7 +1,9 @@ -#include "widgets/helper/ResizingTextEdit.hpp" +#include + #include "common/Common.hpp" #include "common/CompletionModel.hpp" #include "singletons/Settings.hpp" +#include "widgets/helper/ResizingTextEdit.hpp" namespace chatterino { @@ -22,6 +24,7 @@ ResizingTextEdit::ResizingTextEdit() [this] { this->completionInProgress_ = false; }); this->setFocusPolicy(Qt::ClickFocus); + setAcceptDrops(true); } QSize ResizingTextEdit::sizeHint() const @@ -257,11 +260,7 @@ void ResizingTextEdit::insertCompletion(const QString &completion) bool ResizingTextEdit::canInsertFromMimeData(const QMimeData *source) const { - if (source->hasImage()) - { - return false; - } - else if (source->hasFormat("text/plain")) + if (source->hasImage() || source->hasFormat("text/plain")) { return true; } @@ -270,12 +269,35 @@ bool ResizingTextEdit::canInsertFromMimeData(const QMimeData *source) const void ResizingTextEdit::insertFromMimeData(const QMimeData *source) { - if (!source->hasImage()) + if (source->hasImage() || source->hasUrls()) + { + this->pastedImage.invoke(source); + } + else { insertPlainText(source->text()); } } +void ResizingTextEdit::dragEnterEvent(QDragEnterEvent *event) +{ + if (event->mimeData()->hasImage() || event->mimeData()->hasUrls()) + { + event->acceptProposedAction(); + } + // QTextEdit doesn't implement dragEnterEvent, so there's nothing to call here. +} +void ResizingTextEdit::dropEvent(QDropEvent *event) +{ + if (event->mimeData()->hasImage() || event->mimeData()->hasUrls()) + { + this->pastedImage.invoke(event->mimeData()); + } + else // allow for previous functionality of dropping text. + { + QTextEdit::dropEvent(event); + } +} QCompleter *ResizingTextEdit::getCompleter() const { return this->completer_; diff --git a/src/widgets/helper/ResizingTextEdit.hpp b/src/widgets/helper/ResizingTextEdit.hpp index ff79cddb4..df6649048 100644 --- a/src/widgets/helper/ResizingTextEdit.hpp +++ b/src/widgets/helper/ResizingTextEdit.hpp @@ -19,6 +19,7 @@ public: pajlada::Signals::Signal keyPressed; pajlada::Signals::NoArgSignal focused; pajlada::Signals::NoArgSignal focusLost; + pajlada::Signals::Signal pastedImage; void setCompleter(QCompleter *c); QCompleter *getCompleter() const; @@ -33,6 +34,9 @@ protected: bool canInsertFromMimeData(const QMimeData *source) const override; void insertFromMimeData(const QMimeData *source) override; + void dragEnterEvent(QDragEnterEvent *event); + void dropEvent(QDropEvent *event); + private: // hadSpace is set to true in case the "textUnderCursor" word was after a // space diff --git a/src/widgets/splits/Split.cpp b/src/widgets/splits/Split.cpp index 209baa028..361b1adee 100644 --- a/src/widgets/splits/Split.cpp +++ b/src/widgets/splits/Split.cpp @@ -1,16 +1,18 @@ #include "widgets/splits/Split.hpp" #include "common/Common.hpp" +#include "common/Env.hpp" #include "common/NetworkRequest.hpp" #include "controllers/accounts/AccountController.hpp" #include "debug/Log.hpp" #include "providers/twitch/EmoteValue.hpp" #include "providers/twitch/TwitchChannel.hpp" -#include "providers/twitch/TwitchMessageBuilder.hpp" #include "providers/twitch/TwitchIrcServer.hpp" +#include "providers/twitch/TwitchMessageBuilder.hpp" #include "singletons/Settings.hpp" #include "singletons/Theme.hpp" #include "singletons/WindowManager.hpp" +#include "util/NuulsUploader.hpp" #include "util/Shortcut.hpp" #include "util/StreamLink.hpp" #include "widgets/Notebook.hpp" @@ -205,6 +207,11 @@ Split::Split(QWidget *parent) [this] { this->focused.invoke(); }); this->input_->ui_.textEdit->focusLost.connect( [this] { this->focusLost.invoke(); }); + this->input_->ui_.textEdit->pastedImage.connect( + [this](const QMimeData *source) { + pasteFromClipboard(source, this->getChannel(), + *this->input_->ui_.textEdit); + }); } Split::~Split() From e0eb4e5a6d2f4f1b533e1032510c764bf29b077f Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Tue, 24 Sep 2019 16:08:12 +0200 Subject: [PATCH 02/27] I'm Pepega too. Added two missing files. --- src/util/NuulsUploader.cpp | 205 +++++++++++++++++++++++++++++++++++++ src/util/NuulsUploader.hpp | 19 ++++ 2 files changed, 224 insertions(+) create mode 100644 src/util/NuulsUploader.cpp create mode 100644 src/util/NuulsUploader.hpp diff --git a/src/util/NuulsUploader.cpp b/src/util/NuulsUploader.cpp new file mode 100644 index 000000000..693ae3f58 --- /dev/null +++ b/src/util/NuulsUploader.cpp @@ -0,0 +1,205 @@ +#include "NuulsUploader.hpp" + +#include "common/Env.hpp" +#include "common/NetworkRequest.hpp" +#include "providers/twitch/TwitchMessageBuilder.hpp" + +#include + +namespace chatterino { +bool isUploading = false; + +std::queue uploadQueue; +void uploadImageToNuuls(QByteArray imageData, ChannelPtr channel, + ResizingTextEdit &textEdit) +{ + uploadImageToNuuls(imageData, channel, textEdit, "png"); +} +void uploadImageToNuuls(TypedBytes imageData, ChannelPtr channel, + ResizingTextEdit &textEdit) +{ + uploadImageToNuuls(imageData.data, channel, textEdit, imageData.type); +} +void uploadImageToNuuls(QByteArray imageData, ChannelPtr channel, + ResizingTextEdit &textEdit, std::string format) +{ + QByteArray dataToSend; + const char *boundary = "thisistheboudaryasd"; + static QUrl url(Env::get().imagePasteSiteUrl); + + dataToSend.insert(0, "--"); + dataToSend.append(boundary); + std::string temp = "\r\n" + "Content-Disposition: form-data; name=\"attachment\"; " + "filename=\"control_v.%\"\r\n" + "Content-Type: image/%\r\n" + "\r\n"; + boost::replace_all(temp, "%", format); + + dataToSend.append(temp.c_str()); + dataToSend.append(imageData); + dataToSend.append("\r\n--"); + dataToSend.append(boundary); + dataToSend.append("\r\n"); + + NetworkRequest(url, NetworkRequestType::Post) + .header("Content-Type", (std::string("multipart/form-data; boundary=") + + std::string(boundary)) + .c_str()) + + .payload(dataToSend) + .onSuccess([&textEdit, channel](NetworkResult result) -> Outcome { + textEdit.insertPlainText(result.getData() + QString(" ")); + // this->input_->ui_.textEdit->insertPlainText(result.getData()); + if (uploadQueue.size()) + { + channel->addMessage(makeSystemMessage( + QString("Your image has been uploaded. %1 left. Please " + "wait until all of them are uploaded. About %2 " + "seconds left.") + .arg(uploadQueue.size()) + .arg(uploadQueue.size() * 3))); + // Argument number 2 is the ETA. + // 2 seconds for the timer that's there not to spam Nuuls' server + // and 1 second of actual uploading. + } + else + { + channel->addMessage(makeSystemMessage( + QString("Your image has been uploaded."))); + } + isUploading = false; + QTimer::singleShot(2000, [channel, &textEdit]() { + if (uploadQueue.size()) + { + uploadImageToNuuls(uploadQueue.front(), channel, textEdit); + uploadQueue.pop(); + } + }); + return Success; + }) + .onError([channel](NetworkResult result) -> bool { + channel->addMessage(makeSystemMessage( + QString("An error happened while uploading your image: %1") + .arg(result.status()))); + isUploading = false; + return true; + }) + .execute(); +} +QString getImageFileFormat(QString path) +{ + static QStringList LIST_OF_IMAGE_FORMATS = {".png", ".jpg", ".jpeg"}; + for (QString i : LIST_OF_IMAGE_FORMATS) + { + if (path.endsWith(i)) + { + return i.replace('.', ""); + } + } + return QString(); +} + +void pasteFromClipboard(const QMimeData *source, ChannelPtr channel, + ResizingTextEdit &outputTextEdit) +{ + /* +static QUrl url("http://localhost:7494/upload?password=xd"); +// default port and password for nuuls' filehost. +*/ + if (isUploading) + { + channel->addMessage(makeSystemMessage( + QString("You are already uploading an image. " + "Please wait until the upload finishes."))); + return; + } + + isUploading = true; + channel->addMessage(makeSystemMessage(QString("Started upload..."))); + + if (source->hasFormat("image/png")) + { + uploadImageToNuuls(source->data("image/png"), channel, outputTextEdit); + } + else if (source->hasFormat("text/uri-list")) + { + QStringList potientialPathsToSend = + QString(source->data("text/uri-list").toStdString().c_str()) + .split("\r\n"); + + for (QString path : potientialPathsToSend) + { + if (path.isEmpty()) + { + break; + } + else + { + if (getImageFileFormat(path) != QString()) + { + channel->addMessage(makeSystemMessage( + QString("Uploading image: %1").arg(path))); + QImage img = QImage(QUrl(path).toLocalFile()); + if (img.isNull()) + { + channel->addMessage(makeSystemMessage( + QString("Couldn't load image :("))); + return; + } + QByteArray imageData; + QBuffer buf(&imageData); + buf.open(QIODevice::WriteOnly); + img.save(&buf, "png"); + + TypedBytes data = {imageData, "png"}; + uploadQueue.push(data); + } + else if (path.endsWith(".gif")) + { + channel->addMessage(makeSystemMessage( + QString("Uploading GIF: %1").arg(path))); + QFile file(QUrl(path).toLocalFile()); + bool isOkay = file.open(QIODevice::ReadOnly); + if (!isOkay) + { + channel->addMessage(makeSystemMessage( + QString("Failed to open file. :("))); + return; + } + TypedBytes data = {file.readAll(), "gif"}; + uploadQueue.push(data); + file.close(); + // file.readAll() => might be a bit big but it /should/ work + } + else + { + channel->addMessage(makeSystemMessage( + QString("Cannot upload file: %1, not an image") + .arg(path))); + } + } + } + if (uploadQueue.size()) + { + uploadImageToNuuls(uploadQueue.front(), channel, outputTextEdit); + uploadQueue.pop(); + } + } + else if (source->hasFormat("image/gif")) + { + TypedBytes data = {source->data("image/gif"), "gif"}; + uploadImageToNuuls(data, channel, outputTextEdit); + } + else + { // not PNG, try loading it into QImage and save it to a PNG. + QImage image = qvariant_cast(source->imageData()); + QByteArray imageData; + QBuffer buf(&imageData); + buf.open(QIODevice::WriteOnly); + image.save(&buf, "png"); + + uploadImageToNuuls(imageData, channel, outputTextEdit); + } +} +} // namespace chatterino diff --git a/src/util/NuulsUploader.hpp b/src/util/NuulsUploader.hpp new file mode 100644 index 000000000..938f94cb0 --- /dev/null +++ b/src/util/NuulsUploader.hpp @@ -0,0 +1,19 @@ +#include "common/Channel.hpp" +#include "widgets/helper/ResizingTextEdit.hpp" + +#include +#include + +namespace chatterino { +struct TypedBytes { + QByteArray data; + std::string type; +}; +void uploadImageToNuuls(QByteArray imageData, ChannelPtr channel, + ResizingTextEdit &textEdit, std::string format); +void uploadImageToNuuls(TypedBytes imageData, ChannelPtr channel, + ResizingTextEdit &textEdit); +QString getImageFileFormat(QString path); +void pasteFromClipboard(const QMimeData *source, ChannelPtr channel, + ResizingTextEdit &outputTextEdit); +} // namespace chatterino From 1a77df16744f8dda0db1a8be9c2065bee8b8a594 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Tue, 24 Sep 2019 18:28:28 +0200 Subject: [PATCH 03/27] Delete overloads for `uploadImageToNuuls()`. Use TypedBytes in the main definition. Use QHttpMultiPart instead of manually creating the request. --- src/util/NuulsUploader.cpp | 55 +++++++++++++++----------------------- src/util/NuulsUploader.hpp | 2 +- 2 files changed, 22 insertions(+), 35 deletions(-) diff --git a/src/util/NuulsUploader.cpp b/src/util/NuulsUploader.cpp index 693ae3f58..c181a6e69 100644 --- a/src/util/NuulsUploader.cpp +++ b/src/util/NuulsUploader.cpp @@ -4,53 +4,39 @@ #include "common/NetworkRequest.hpp" #include "providers/twitch/TwitchMessageBuilder.hpp" -#include +#include namespace chatterino { bool isUploading = false; std::queue uploadQueue; -void uploadImageToNuuls(QByteArray imageData, ChannelPtr channel, - ResizingTextEdit &textEdit) -{ - uploadImageToNuuls(imageData, channel, textEdit, "png"); -} + void uploadImageToNuuls(TypedBytes imageData, ChannelPtr channel, ResizingTextEdit &textEdit) { - uploadImageToNuuls(imageData.data, channel, textEdit, imageData.type); -} -void uploadImageToNuuls(QByteArray imageData, ChannelPtr channel, - ResizingTextEdit &textEdit, std::string format) -{ - QByteArray dataToSend; const char *boundary = "thisistheboudaryasd"; static QUrl url(Env::get().imagePasteSiteUrl); - dataToSend.insert(0, "--"); - dataToSend.append(boundary); - std::string temp = "\r\n" - "Content-Disposition: form-data; name=\"attachment\"; " - "filename=\"control_v.%\"\r\n" - "Content-Type: image/%\r\n" - "\r\n"; - boost::replace_all(temp, "%", format); - - dataToSend.append(temp.c_str()); - dataToSend.append(imageData); - dataToSend.append("\r\n--"); - dataToSend.append(boundary); - dataToSend.append("\r\n"); - + QHttpMultiPart *payload = new QHttpMultiPart(QHttpMultiPart::FormDataType); + QHttpPart part = QHttpPart(); + part.setBody(imageData.data); + part.setHeader(QNetworkRequest::ContentTypeHeader, + QString("image/%1").arg(imageData.type)); + part.setHeader(QNetworkRequest::ContentLengthHeader, + QVariant(imageData.data.length())); + part.setHeader( + QNetworkRequest::ContentDispositionHeader, + QString("form-data; name=\"attachment\"; filename=\"control_v.%1\"")); + payload->setBoundary(boundary); + payload->append(part); NetworkRequest(url, NetworkRequestType::Post) .header("Content-Type", (std::string("multipart/form-data; boundary=") + std::string(boundary)) .c_str()) - .payload(dataToSend) + .multiPart(payload) .onSuccess([&textEdit, channel](NetworkResult result) -> Outcome { textEdit.insertPlainText(result.getData() + QString(" ")); - // this->input_->ui_.textEdit->insertPlainText(result.getData()); if (uploadQueue.size()) { channel->addMessage(makeSystemMessage( @@ -104,9 +90,9 @@ void pasteFromClipboard(const QMimeData *source, ChannelPtr channel, ResizingTextEdit &outputTextEdit) { /* -static QUrl url("http://localhost:7494/upload?password=xd"); -// default port and password for nuuls' filehost. -*/ + http://localhost:7494/upload?password=xd + default port and password for nuuls' filehost. + */ if (isUploading) { channel->addMessage(makeSystemMessage( @@ -120,7 +106,8 @@ static QUrl url("http://localhost:7494/upload?password=xd"); if (source->hasFormat("image/png")) { - uploadImageToNuuls(source->data("image/png"), channel, outputTextEdit); + uploadImageToNuuls({source->data("image/png"), "png"}, channel, + outputTextEdit); } else if (source->hasFormat("text/uri-list")) { @@ -199,7 +186,7 @@ static QUrl url("http://localhost:7494/upload?password=xd"); buf.open(QIODevice::WriteOnly); image.save(&buf, "png"); - uploadImageToNuuls(imageData, channel, outputTextEdit); + uploadImageToNuuls({imageData, "png"}, channel, outputTextEdit); } } } // namespace chatterino diff --git a/src/util/NuulsUploader.hpp b/src/util/NuulsUploader.hpp index 938f94cb0..e735d5b6c 100644 --- a/src/util/NuulsUploader.hpp +++ b/src/util/NuulsUploader.hpp @@ -7,7 +7,7 @@ namespace chatterino { struct TypedBytes { QByteArray data; - std::string type; + QString type; }; void uploadImageToNuuls(QByteArray imageData, ChannelPtr channel, ResizingTextEdit &textEdit, std::string format); From 02dc0a4e1d57a323936ef0ad7d4228e31acb9d27 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Tue, 24 Sep 2019 18:39:15 +0200 Subject: [PATCH 04/27] Reword docs slightly. --- docs/ENV.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/ENV.md b/docs/ENV.md index bddef4687..8c4f9d087 100644 --- a/docs/ENV.md +++ b/docs/ENV.md @@ -20,11 +20,12 @@ Arguments: - `%1` = Emote set ID ### CHATTERINO2_IMAGE_PASTE_SITE_URL -Used to change the URL that Chatterino2 uses when uploading an image by pasting it into the input box. +Used to change the URL that Chatterino2 uses when trying to paste an image into chat. This can be used for hosting the uploaded images yourself. Default value: `https://i.nuuls.com/upload` Arguments: - None Notes: - - The server that's running the web page MUST be compatible with [Nuuls' filehost](https://github.com/nuuls/filehost) + - If you want to host the images yourself. You need [Nuuls' filehost software](https://github.com/nuuls/fiehost) + From f6c780b2e7c802146864f21ba3a51bc5d85c8d67 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Wed, 25 Sep 2019 12:33:34 +0200 Subject: [PATCH 05/27] Smol change in ENV.md. --- docs/ENV.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/ENV.md b/docs/ENV.md index 8c4f9d087..6c83da4a9 100644 --- a/docs/ENV.md +++ b/docs/ENV.md @@ -28,4 +28,5 @@ Arguments: Notes: - If you want to host the images yourself. You need [Nuuls' filehost software](https://github.com/nuuls/fiehost) + - Other image hosting software is currently not supported. From 298c013fa0fc17097e11936a8b4340e8c4261fbe Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Wed, 25 Sep 2019 12:51:17 +0200 Subject: [PATCH 06/27] Rename `Env::imagePasteSiteUrl` to `Env::imageUploaderUrl`. --- src/common/Env.cpp | 2 +- src/common/Env.hpp | 2 +- src/util/NuulsUploader.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/Env.cpp b/src/common/Env.cpp index 046febf53..b30cc652d 100644 --- a/src/common/Env.cpp +++ b/src/common/Env.cpp @@ -28,7 +28,7 @@ Env::Env() , twitchEmoteSetResolverUrl(readStringEnv( "CHATTERINO2_TWITCH_EMOTE_SET_RESOLVER_URL", "https://braize.pajlada.com/chatterino/twitchemotes/set/%1/")) - , imagePasteSiteUrl(readStringEnv("CHATTERINO2_IMAGE_PASTE_SITE_URL", + , imageUploaderUrl(readStringEnv("CHATTERINO2_IMAGE_PASTE_SITE_URL", "https://i.nuuls.com/upload")) { } diff --git a/src/common/Env.hpp b/src/common/Env.hpp index a47dd3aff..07beef859 100644 --- a/src/common/Env.hpp +++ b/src/common/Env.hpp @@ -14,7 +14,7 @@ public: const QString recentMessagesApiUrl; const QString linkResolverUrl; const QString twitchEmoteSetResolverUrl; - const QString imagePasteSiteUrl; + const QString imageUploaderUrl; }; } // namespace chatterino diff --git a/src/util/NuulsUploader.cpp b/src/util/NuulsUploader.cpp index c181a6e69..dfe6c679e 100644 --- a/src/util/NuulsUploader.cpp +++ b/src/util/NuulsUploader.cpp @@ -15,7 +15,7 @@ void uploadImageToNuuls(TypedBytes imageData, ChannelPtr channel, ResizingTextEdit &textEdit) { const char *boundary = "thisistheboudaryasd"; - static QUrl url(Env::get().imagePasteSiteUrl); + static QUrl url(Env::get().imageUploaderUrl); QHttpMultiPart *payload = new QHttpMultiPart(QHttpMultiPart::FormDataType); QHttpPart part = QHttpPart(); From 4e9951371fed2084ad149c9c6fd52b202d313d9a Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Wed, 25 Sep 2019 22:21:26 +0200 Subject: [PATCH 07/27] Applied a bit of foutf's suggestions. Replace uploadQueue.size() with .empty() and swap if/else bodies, move non empty queue check outside of the timer on line 60, move getImageFileFormat to an anonymous namespace, rename pasteFromClipoard to upload(), removed usesless comment, shortened message on line 83, use QMimeData.hasUrls() and QMimeData.urls(), moved GIF format case in upload() more to the top, call original functions in canInsertFromMimeData and dragEnterEvent which are overriden --- src/util/NuulsUploader.cpp | 165 ++++++++++++------------ src/util/NuulsUploader.hpp | 17 ++- src/widgets/helper/ResizingTextEdit.cpp | 11 +- src/widgets/splits/Split.cpp | 3 +- 4 files changed, 99 insertions(+), 97 deletions(-) diff --git a/src/util/NuulsUploader.cpp b/src/util/NuulsUploader.cpp index dfe6c679e..77c476e04 100644 --- a/src/util/NuulsUploader.cpp +++ b/src/util/NuulsUploader.cpp @@ -37,7 +37,12 @@ void uploadImageToNuuls(TypedBytes imageData, ChannelPtr channel, .multiPart(payload) .onSuccess([&textEdit, channel](NetworkResult result) -> Outcome { textEdit.insertPlainText(result.getData() + QString(" ")); - if (uploadQueue.size()) + if (uploadQueue.empty()) + { + channel->addMessage(makeSystemMessage( + QString("Your image has been uploaded."))); + } + else { channel->addMessage(makeSystemMessage( QString("Your image has been uploaded. %1 left. Please " @@ -49,19 +54,14 @@ void uploadImageToNuuls(TypedBytes imageData, ChannelPtr channel, // 2 seconds for the timer that's there not to spam Nuuls' server // and 1 second of actual uploading. } - else - { - channel->addMessage(makeSystemMessage( - QString("Your image has been uploaded."))); - } isUploading = false; - QTimer::singleShot(2000, [channel, &textEdit]() { - if (uploadQueue.size()) - { + if (uploadQueue.size()) + { + QTimer::singleShot(2000, [channel, &textEdit]() { uploadImageToNuuls(uploadQueue.front(), channel, textEdit); uploadQueue.pop(); - } - }); + }); + } return Success; }) .onError([channel](NetworkResult result) -> bool { @@ -73,31 +73,14 @@ void uploadImageToNuuls(TypedBytes imageData, ChannelPtr channel, }) .execute(); } -QString getImageFileFormat(QString path) -{ - static QStringList LIST_OF_IMAGE_FORMATS = {".png", ".jpg", ".jpeg"}; - for (QString i : LIST_OF_IMAGE_FORMATS) - { - if (path.endsWith(i)) - { - return i.replace('.', ""); - } - } - return QString(); -} -void pasteFromClipboard(const QMimeData *source, ChannelPtr channel, - ResizingTextEdit &outputTextEdit) +void upload(const QMimeData *source, ChannelPtr channel, + ResizingTextEdit &outputTextEdit) { - /* - http://localhost:7494/upload?password=xd - default port and password for nuuls' filehost. - */ if (isUploading) { channel->addMessage(makeSystemMessage( - QString("You are already uploading an image. " - "Please wait until the upload finishes."))); + QString("Please wait until the upload finishes."))); return; } @@ -109,62 +92,61 @@ void pasteFromClipboard(const QMimeData *source, ChannelPtr channel, uploadImageToNuuls({source->data("image/png"), "png"}, channel, outputTextEdit); } - else if (source->hasFormat("text/uri-list")) + else if (source->hasFormat("image/jpeg")) { - QStringList potientialPathsToSend = - QString(source->data("text/uri-list").toStdString().c_str()) - .split("\r\n"); - - for (QString path : potientialPathsToSend) + uploadImageToNuuls({source->data("image/jpeg"), "jpeg"}, channel, + outputTextEdit); + } + else if (source->hasFormat("image/gif")) + { + uploadImageToNuuls({source->data("image/gif"), "gif"}, channel, + outputTextEdit); + } + else if (source->hasUrls()) + { + for (QUrl path : source->urls()) { - if (path.isEmpty()) + if (getImageFileFormat(path.toLocalFile()) != QString()) { - break; + channel->addMessage(makeSystemMessage( + QString("Uploading image: %1").arg(path.toLocalFile()))); + QImage img = QImage(path.toLocalFile()); + if (img.isNull()) + { + channel->addMessage( + makeSystemMessage(QString("Couldn't load image :("))); + return; + } + QByteArray imageData; + QBuffer buf(&imageData); + buf.open(QIODevice::WriteOnly); + img.save(&buf, "png"); + + TypedBytes data = {imageData, "png"}; + uploadQueue.push(data); + } + else if (path.toLocalFile().endsWith(".gif")) + { + channel->addMessage(makeSystemMessage( + QString("Uploading GIF: %1").arg(path.toLocalFile()))); + QFile file(path.toLocalFile()); + bool isOkay = file.open(QIODevice::ReadOnly); + if (!isOkay) + { + channel->addMessage( + makeSystemMessage(QString("Failed to open file. :("))); + return; + } + TypedBytes data = {file.readAll(), "gif"}; + uploadQueue.push(data); + file.close(); + // file.readAll() => might be a bit big but it /should/ work } else { - if (getImageFileFormat(path) != QString()) - { - channel->addMessage(makeSystemMessage( - QString("Uploading image: %1").arg(path))); - QImage img = QImage(QUrl(path).toLocalFile()); - if (img.isNull()) - { - channel->addMessage(makeSystemMessage( - QString("Couldn't load image :("))); - return; - } - QByteArray imageData; - QBuffer buf(&imageData); - buf.open(QIODevice::WriteOnly); - img.save(&buf, "png"); - - TypedBytes data = {imageData, "png"}; - uploadQueue.push(data); - } - else if (path.endsWith(".gif")) - { - channel->addMessage(makeSystemMessage( - QString("Uploading GIF: %1").arg(path))); - QFile file(QUrl(path).toLocalFile()); - bool isOkay = file.open(QIODevice::ReadOnly); - if (!isOkay) - { - channel->addMessage(makeSystemMessage( - QString("Failed to open file. :("))); - return; - } - TypedBytes data = {file.readAll(), "gif"}; - uploadQueue.push(data); - file.close(); - // file.readAll() => might be a bit big but it /should/ work - } - else - { - channel->addMessage(makeSystemMessage( - QString("Cannot upload file: %1, not an image") - .arg(path))); - } + channel->addMessage(makeSystemMessage( + QString("Cannot upload file: %1, not an image") + .arg(path.toLocalFile()))); } } if (uploadQueue.size()) @@ -173,11 +155,6 @@ void pasteFromClipboard(const QMimeData *source, ChannelPtr channel, uploadQueue.pop(); } } - else if (source->hasFormat("image/gif")) - { - TypedBytes data = {source->data("image/gif"), "gif"}; - uploadImageToNuuls(data, channel, outputTextEdit); - } else { // not PNG, try loading it into QImage and save it to a PNG. QImage image = qvariant_cast(source->imageData()); @@ -190,3 +167,19 @@ void pasteFromClipboard(const QMimeData *source, ChannelPtr channel, } } } // namespace chatterino + +namespace { + +QString getImageFileFormat(QString path) +{ + static QStringList listOfImageFormats = {".png", ".jpg", ".jpeg"}; + for (const QString &format : listOfImageFormats) + { + if (path.endsWith(format)) + { + return format.mid(1); + } + } + return QString(); +} +} // namespace diff --git a/src/util/NuulsUploader.hpp b/src/util/NuulsUploader.hpp index e735d5b6c..97010bb06 100644 --- a/src/util/NuulsUploader.hpp +++ b/src/util/NuulsUploader.hpp @@ -9,11 +9,14 @@ struct TypedBytes { QByteArray data; QString type; }; -void uploadImageToNuuls(QByteArray imageData, ChannelPtr channel, - ResizingTextEdit &textEdit, std::string format); -void uploadImageToNuuls(TypedBytes imageData, ChannelPtr channel, - ResizingTextEdit &textEdit); -QString getImageFileFormat(QString path); -void pasteFromClipboard(const QMimeData *source, ChannelPtr channel, - ResizingTextEdit &outputTextEdit); +void upload(QByteArray imageData, ChannelPtr channel, + ResizingTextEdit &textEdit, std::string format); +void upload(TypedBytes imageData, ChannelPtr channel, + ResizingTextEdit &textEdit); +void upload(const QMimeData *source, ChannelPtr channel, + ResizingTextEdit &outputTextEdit); } // namespace chatterino + +namespace { +QString getImageFileFormat(QString path); +} diff --git a/src/widgets/helper/ResizingTextEdit.cpp b/src/widgets/helper/ResizingTextEdit.cpp index 4ed3ba0cd..7f4836199 100644 --- a/src/widgets/helper/ResizingTextEdit.cpp +++ b/src/widgets/helper/ResizingTextEdit.cpp @@ -264,7 +264,10 @@ bool ResizingTextEdit::canInsertFromMimeData(const QMimeData *source) const { return true; } - return false; + else + { + return QTextEdit::canInsertFromMimeData(source); + } } void ResizingTextEdit::insertFromMimeData(const QMimeData *source) @@ -285,8 +288,12 @@ void ResizingTextEdit::dragEnterEvent(QDragEnterEvent *event) { event->acceptProposedAction(); } - // QTextEdit doesn't implement dragEnterEvent, so there's nothing to call here. + else + { + QAbstractScrollArea::dragEnterEvent(event); + } } + void ResizingTextEdit::dropEvent(QDropEvent *event) { if (event->mimeData()->hasImage() || event->mimeData()->hasUrls()) diff --git a/src/widgets/splits/Split.cpp b/src/widgets/splits/Split.cpp index 361b1adee..7b066b052 100644 --- a/src/widgets/splits/Split.cpp +++ b/src/widgets/splits/Split.cpp @@ -209,8 +209,7 @@ Split::Split(QWidget *parent) [this] { this->focusLost.invoke(); }); this->input_->ui_.textEdit->pastedImage.connect( [this](const QMimeData *source) { - pasteFromClipboard(source, this->getChannel(), - *this->input_->ui_.textEdit); + upload(source, this->getChannel(), *this->input_->ui_.textEdit); }); } From b55d08df8e1444bbf31bab14fa37dcfeab85e422 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Wed, 25 Sep 2019 22:39:02 +0200 Subject: [PATCH 08/27] Renamed the pastedImage signal to imagePasted. --- src/widgets/helper/ResizingTextEdit.cpp | 4 ++-- src/widgets/helper/ResizingTextEdit.hpp | 2 +- src/widgets/splits/Split.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/widgets/helper/ResizingTextEdit.cpp b/src/widgets/helper/ResizingTextEdit.cpp index 7f4836199..6f0464d10 100644 --- a/src/widgets/helper/ResizingTextEdit.cpp +++ b/src/widgets/helper/ResizingTextEdit.cpp @@ -274,7 +274,7 @@ void ResizingTextEdit::insertFromMimeData(const QMimeData *source) { if (source->hasImage() || source->hasUrls()) { - this->pastedImage.invoke(source); + this->imagePasted.invoke(source); } else { @@ -298,7 +298,7 @@ void ResizingTextEdit::dropEvent(QDropEvent *event) { if (event->mimeData()->hasImage() || event->mimeData()->hasUrls()) { - this->pastedImage.invoke(event->mimeData()); + this->imagePasted.invoke(event->mimeData()); } else // allow for previous functionality of dropping text. { diff --git a/src/widgets/helper/ResizingTextEdit.hpp b/src/widgets/helper/ResizingTextEdit.hpp index df6649048..bc81272d9 100644 --- a/src/widgets/helper/ResizingTextEdit.hpp +++ b/src/widgets/helper/ResizingTextEdit.hpp @@ -19,7 +19,7 @@ public: pajlada::Signals::Signal keyPressed; pajlada::Signals::NoArgSignal focused; pajlada::Signals::NoArgSignal focusLost; - pajlada::Signals::Signal pastedImage; + pajlada::Signals::Signal imagePasted; void setCompleter(QCompleter *c); QCompleter *getCompleter() const; diff --git a/src/widgets/splits/Split.cpp b/src/widgets/splits/Split.cpp index 7b066b052..9867281d4 100644 --- a/src/widgets/splits/Split.cpp +++ b/src/widgets/splits/Split.cpp @@ -207,7 +207,7 @@ Split::Split(QWidget *parent) [this] { this->focused.invoke(); }); this->input_->ui_.textEdit->focusLost.connect( [this] { this->focusLost.invoke(); }); - this->input_->ui_.textEdit->pastedImage.connect( + this->input_->ui_.textEdit->imagePasted.connect( [this](const QMimeData *source) { upload(source, this->getChannel(), *this->input_->ui_.textEdit); }); From 205122ee682f15a9df1308ddbcd6e655aeab03ba Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Thu, 26 Sep 2019 14:24:41 +0200 Subject: [PATCH 09/27] Avoid copying a QUrl in upload() when pasting files from the filesystem. --- src/util/NuulsUploader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/NuulsUploader.cpp b/src/util/NuulsUploader.cpp index 77c476e04..05ae31b49 100644 --- a/src/util/NuulsUploader.cpp +++ b/src/util/NuulsUploader.cpp @@ -104,7 +104,7 @@ void upload(const QMimeData *source, ChannelPtr channel, } else if (source->hasUrls()) { - for (QUrl path : source->urls()) + for (const QUrl &path : source->urls()) { if (getImageFileFormat(path.toLocalFile()) != QString()) { From 970503b7554196fc3081dda5ec961eb9aaf2a412 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Thu, 26 Sep 2019 14:42:30 +0200 Subject: [PATCH 10/27] Apply last suggestion and fix mistake not letting the user upload an image after attempting to upload a file that is not an image. --- src/util/NuulsUploader.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/util/NuulsUploader.cpp b/src/util/NuulsUploader.cpp index 05ae31b49..6fd27efc1 100644 --- a/src/util/NuulsUploader.cpp +++ b/src/util/NuulsUploader.cpp @@ -106,7 +106,7 @@ void upload(const QMimeData *source, ChannelPtr channel, { for (const QUrl &path : source->urls()) { - if (getImageFileFormat(path.toLocalFile()) != QString()) + if (!getImageFileFormat(path.toLocalFile()).isEmpty()) { channel->addMessage(makeSystemMessage( QString("Uploading image: %1").arg(path.toLocalFile()))); @@ -147,6 +147,7 @@ void upload(const QMimeData *source, ChannelPtr channel, channel->addMessage(makeSystemMessage( QString("Cannot upload file: %1, not an image") .arg(path.toLocalFile()))); + isUploading = false; } } if (uploadQueue.size()) From bf434f3ac59f45ea5fcce42d4bac4640bacfa7aa Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Fri, 11 Oct 2019 15:41:33 +0200 Subject: [PATCH 11/27] Fix the stupid things. This includes: - NuulsUploader.cpp - changing upload delay to a #define - moving png conversion code to `boost::optional convertToPng(QImage image)` - in uploadImageToNuuls: moving content type definition to a variable, move things around a bit to eliminate an if - in upload: adding a comment about `source->hasUrls()` and `source->urls()`, change `uploadQueue.size()` to `!uploadQueue.empty()` - ResizingTextEdit.cpp - changing #include order --- src/util/NuulsUploader.cpp | 115 +++++++++++++++--------- src/util/NuulsUploader.hpp | 4 - src/widgets/helper/ResizingTextEdit.cpp | 5 +- 3 files changed, 78 insertions(+), 46 deletions(-) diff --git a/src/util/NuulsUploader.cpp b/src/util/NuulsUploader.cpp index 6fd27efc1..6a6086066 100644 --- a/src/util/NuulsUploader.cpp +++ b/src/util/NuulsUploader.cpp @@ -6,6 +6,41 @@ #include +#define UPLOAD_DELAY 2000 +// Delay between uploads in milliseconds + +namespace { + +QString getImageFileFormat(QString path) +{ + static QStringList listOfImageFormats = {".png", ".jpg", ".jpeg"}; + for (const QString &format : listOfImageFormats) + { + if (path.endsWith(format)) + { + return format.mid(1); + } + } + return QString(); +} + +boost::optional convertToPng(QImage image) +{ + QByteArray imageData; + QBuffer buf(&imageData); + buf.open(QIODevice::WriteOnly); + bool success = image.save(&buf, "png"); + if (success) + { + return boost::optional(imageData); + } + else + { + return boost::optional(boost::none); + } +} +} // namespace + namespace chatterino { bool isUploading = false; @@ -15,6 +50,8 @@ void uploadImageToNuuls(TypedBytes imageData, ChannelPtr channel, ResizingTextEdit &textEdit) { const char *boundary = "thisistheboudaryasd"; + QString contentType = + QString("multipart/form-data; boundary=%1").arg(boundary); static QUrl url(Env::get().imageUploaderUrl); QHttpMultiPart *payload = new QHttpMultiPart(QHttpMultiPart::FormDataType); @@ -26,17 +63,17 @@ void uploadImageToNuuls(TypedBytes imageData, ChannelPtr channel, QVariant(imageData.data.length())); part.setHeader( QNetworkRequest::ContentDispositionHeader, - QString("form-data; name=\"attachment\"; filename=\"control_v.%1\"")); + QString("form-data; name=\"attachment\"; filename=\"control_v.%1\"") + .arg(imageData.type)); payload->setBoundary(boundary); payload->append(part); NetworkRequest(url, NetworkRequestType::Post) - .header("Content-Type", (std::string("multipart/form-data; boundary=") + - std::string(boundary)) - .c_str()) + .header("Content-Type", contentType) .multiPart(payload) .onSuccess([&textEdit, channel](NetworkResult result) -> Outcome { textEdit.insertPlainText(result.getData() + QString(" ")); + isUploading = false; if (uploadQueue.empty()) { channel->addMessage(makeSystemMessage( @@ -48,16 +85,16 @@ void uploadImageToNuuls(TypedBytes imageData, ChannelPtr channel, QString("Your image has been uploaded. %1 left. Please " "wait until all of them are uploaded. About %2 " "seconds left.") - .arg(uploadQueue.size()) - .arg(uploadQueue.size() * 3))); + .arg(uploadQueue.size(), + uploadQueue.size() * + (UPLOAD_DELAY / 1000 + + 1) // convert UPLOAD_DELAY to seconds + ))); // Argument number 2 is the ETA. // 2 seconds for the timer that's there not to spam Nuuls' server // and 1 second of actual uploading. - } - isUploading = false; - if (uploadQueue.size()) - { - QTimer::singleShot(2000, [channel, &textEdit]() { + + QTimer::singleShot(UPLOAD_DELAY, [channel, &textEdit]() { uploadImageToNuuls(uploadQueue.front(), channel, textEdit); uploadQueue.pop(); }); @@ -104,6 +141,8 @@ void upload(const QMimeData *source, ChannelPtr channel, } else if (source->hasUrls()) { + // This path gets chosen when files are copied from a file manager, like explorer.exe, caja. + // Each entry in source->urls() is a QUrl pointng to a file that was copied. for (const QUrl &path : source->urls()) { if (!getImageFileFormat(path.toLocalFile()).isEmpty()) @@ -117,13 +156,20 @@ void upload(const QMimeData *source, ChannelPtr channel, makeSystemMessage(QString("Couldn't load image :("))); return; } - QByteArray imageData; - QBuffer buf(&imageData); - buf.open(QIODevice::WriteOnly); - img.save(&buf, "png"); - TypedBytes data = {imageData, "png"}; - uploadQueue.push(data); + boost::optional imageData = convertToPng(img); + if (imageData) + { + TypedBytes data = {imageData.get(), "png"}; + uploadQueue.push(data); + } + else + { + channel->addMessage(makeSystemMessage( + QString("Cannot upload file: %1, Couldn't convert " + "image to png.") + .arg(path.toLocalFile()))); + } } else if (path.toLocalFile().endsWith(".gif")) { @@ -150,7 +196,7 @@ void upload(const QMimeData *source, ChannelPtr channel, isUploading = false; } } - if (uploadQueue.size()) + if (!uploadQueue.empty()) { uploadImageToNuuls(uploadQueue.front(), channel, outputTextEdit); uploadQueue.pop(); @@ -159,28 +205,17 @@ void upload(const QMimeData *source, ChannelPtr channel, else { // not PNG, try loading it into QImage and save it to a PNG. QImage image = qvariant_cast(source->imageData()); - QByteArray imageData; - QBuffer buf(&imageData); - buf.open(QIODevice::WriteOnly); - image.save(&buf, "png"); - - uploadImageToNuuls({imageData, "png"}, channel, outputTextEdit); + boost::optional imageData = convertToPng(image); + if (imageData) + { + uploadImageToNuuls({imageData.get(), "png"}, channel, + outputTextEdit); + } + else + { + channel->addMessage(makeSystemMessage( + QString("Cannot upload file, failed to convert to png."))); + } } } } // namespace chatterino - -namespace { - -QString getImageFileFormat(QString path) -{ - static QStringList listOfImageFormats = {".png", ".jpg", ".jpeg"}; - for (const QString &format : listOfImageFormats) - { - if (path.endsWith(format)) - { - return format.mid(1); - } - } - return QString(); -} -} // namespace diff --git a/src/util/NuulsUploader.hpp b/src/util/NuulsUploader.hpp index 97010bb06..675d9c0ed 100644 --- a/src/util/NuulsUploader.hpp +++ b/src/util/NuulsUploader.hpp @@ -16,7 +16,3 @@ void upload(TypedBytes imageData, ChannelPtr channel, void upload(const QMimeData *source, ChannelPtr channel, ResizingTextEdit &outputTextEdit); } // namespace chatterino - -namespace { -QString getImageFileFormat(QString path); -} diff --git a/src/widgets/helper/ResizingTextEdit.cpp b/src/widgets/helper/ResizingTextEdit.cpp index 6f0464d10..623caf6de 100644 --- a/src/widgets/helper/ResizingTextEdit.cpp +++ b/src/widgets/helper/ResizingTextEdit.cpp @@ -1,9 +1,10 @@ -#include +#include "widgets/helper/ResizingTextEdit.hpp" #include "common/Common.hpp" #include "common/CompletionModel.hpp" #include "singletons/Settings.hpp" -#include "widgets/helper/ResizingTextEdit.hpp" + +#include namespace chatterino { From 77af5d54cfa7fb1e0667235af6e8e6077e02637e Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Fri, 11 Oct 2019 17:00:26 +0200 Subject: [PATCH 12/27] Change more things. Things changed: - make getImageFileFormat case insensitive - use QTextEdit::dragEnterEvent instead of QAbstractScrollArea::dragEnterEvent, - Make dragEnterEvent() and dropEvent() overrides. --- src/util/NuulsUploader.cpp | 2 +- src/widgets/helper/ResizingTextEdit.cpp | 2 +- src/widgets/helper/ResizingTextEdit.hpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/util/NuulsUploader.cpp b/src/util/NuulsUploader.cpp index 6a6086066..8f201f54f 100644 --- a/src/util/NuulsUploader.cpp +++ b/src/util/NuulsUploader.cpp @@ -16,7 +16,7 @@ QString getImageFileFormat(QString path) static QStringList listOfImageFormats = {".png", ".jpg", ".jpeg"}; for (const QString &format : listOfImageFormats) { - if (path.endsWith(format)) + if (path.endsWith(format, Qt::CaseInsensitive)) { return format.mid(1); } diff --git a/src/widgets/helper/ResizingTextEdit.cpp b/src/widgets/helper/ResizingTextEdit.cpp index 623caf6de..60ccec58d 100644 --- a/src/widgets/helper/ResizingTextEdit.cpp +++ b/src/widgets/helper/ResizingTextEdit.cpp @@ -291,7 +291,7 @@ void ResizingTextEdit::dragEnterEvent(QDragEnterEvent *event) } else { - QAbstractScrollArea::dragEnterEvent(event); + QTextEdit::dragEnterEvent(event); } } diff --git a/src/widgets/helper/ResizingTextEdit.hpp b/src/widgets/helper/ResizingTextEdit.hpp index bc81272d9..018d345f9 100644 --- a/src/widgets/helper/ResizingTextEdit.hpp +++ b/src/widgets/helper/ResizingTextEdit.hpp @@ -34,8 +34,8 @@ protected: bool canInsertFromMimeData(const QMimeData *source) const override; void insertFromMimeData(const QMimeData *source) override; - void dragEnterEvent(QDragEnterEvent *event); - void dropEvent(QDropEvent *event); + void dragEnterEvent(QDragEnterEvent *event) override; + void dropEvent(QDropEvent *event) override; private: // hadSpace is set to true in case the "textUnderCursor" word was after a From e005fe806a320ce0aae417e08a160b1abacee3e8 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sat, 19 Oct 2019 11:41:23 +0200 Subject: [PATCH 13/27] Comment stuff This includes: - Adding two comments - Fixing one spelling mistake. --- src/util/NuulsUploader.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/util/NuulsUploader.cpp b/src/util/NuulsUploader.cpp index 8f201f54f..4f167201a 100644 --- a/src/util/NuulsUploader.cpp +++ b/src/util/NuulsUploader.cpp @@ -42,6 +42,7 @@ boost::optional convertToPng(QImage image) } // namespace namespace chatterino { +// These variables are only used from the main thread. bool isUploading = false; std::queue uploadQueue; @@ -114,6 +115,7 @@ void uploadImageToNuuls(TypedBytes imageData, ChannelPtr channel, void upload(const QMimeData *source, ChannelPtr channel, ResizingTextEdit &outputTextEdit) { + // There's no need to thread proof this function. It is called only from the main thread. if (isUploading) { channel->addMessage(makeSystemMessage( @@ -142,7 +144,7 @@ void upload(const QMimeData *source, ChannelPtr channel, else if (source->hasUrls()) { // This path gets chosen when files are copied from a file manager, like explorer.exe, caja. - // Each entry in source->urls() is a QUrl pointng to a file that was copied. + // Each entry in source->urls() is a QUrl pointing to a file that was copied. for (const QUrl &path : source->urls()) { if (!getImageFileFormat(path.toLocalFile()).isEmpty()) From 736df48788055cfd89d4aad9dd78518e2e87ced5 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Tue, 22 Oct 2019 16:04:36 +0200 Subject: [PATCH 14/27] Smol fix. --- src/util/NuulsUploader.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/util/NuulsUploader.cpp b/src/util/NuulsUploader.cpp index 4f167201a..07b0b4379 100644 --- a/src/util/NuulsUploader.cpp +++ b/src/util/NuulsUploader.cpp @@ -147,11 +147,12 @@ void upload(const QMimeData *source, ChannelPtr channel, // Each entry in source->urls() is a QUrl pointing to a file that was copied. for (const QUrl &path : source->urls()) { - if (!getImageFileFormat(path.toLocalFile()).isEmpty()) + QString localPath = path.toLocalFile(); + if (!getImageFileFormat(localPath).isEmpty()) { channel->addMessage(makeSystemMessage( - QString("Uploading image: %1").arg(path.toLocalFile()))); - QImage img = QImage(path.toLocalFile()); + QString("Uploading image: %1").arg(localPath))); + QImage img = QImage(localPath); if (img.isNull()) { channel->addMessage( @@ -170,14 +171,14 @@ void upload(const QMimeData *source, ChannelPtr channel, channel->addMessage(makeSystemMessage( QString("Cannot upload file: %1, Couldn't convert " "image to png.") - .arg(path.toLocalFile()))); + .arg(localPath))); } } - else if (path.toLocalFile().endsWith(".gif")) + else if (localPath.endsWith(".gif")) { channel->addMessage(makeSystemMessage( - QString("Uploading GIF: %1").arg(path.toLocalFile()))); - QFile file(path.toLocalFile()); + QString("Uploading GIF: %1").arg(localPath))); + QFile file(localPath); bool isOkay = file.open(QIODevice::ReadOnly); if (!isOkay) { @@ -194,7 +195,7 @@ void upload(const QMimeData *source, ChannelPtr channel, { channel->addMessage(makeSystemMessage( QString("Cannot upload file: %1, not an image") - .arg(path.toLocalFile()))); + .arg(localPath))); isUploading = false; } } From 6c261459cfb4584a5f303147d9525b9a0467d4e7 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Tue, 22 Oct 2019 17:21:46 +0200 Subject: [PATCH 15/27] Renamed TypedBytes to RawImageData, type to format. FeelsGoodMan Clap --- src/util/NuulsUploader.cpp | 13 ++++++------- src/util/NuulsUploader.hpp | 6 +++--- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/util/NuulsUploader.cpp b/src/util/NuulsUploader.cpp index 07b0b4379..cbf81beda 100644 --- a/src/util/NuulsUploader.cpp +++ b/src/util/NuulsUploader.cpp @@ -44,10 +44,9 @@ boost::optional convertToPng(QImage image) namespace chatterino { // These variables are only used from the main thread. bool isUploading = false; +std::queue uploadQueue; -std::queue uploadQueue; - -void uploadImageToNuuls(TypedBytes imageData, ChannelPtr channel, +void uploadImageToNuuls(RawImageData imageData, ChannelPtr channel, ResizingTextEdit &textEdit) { const char *boundary = "thisistheboudaryasd"; @@ -59,13 +58,13 @@ void uploadImageToNuuls(TypedBytes imageData, ChannelPtr channel, QHttpPart part = QHttpPart(); part.setBody(imageData.data); part.setHeader(QNetworkRequest::ContentTypeHeader, - QString("image/%1").arg(imageData.type)); + QString("image/%1").arg(imageData.format)); part.setHeader(QNetworkRequest::ContentLengthHeader, QVariant(imageData.data.length())); part.setHeader( QNetworkRequest::ContentDispositionHeader, QString("form-data; name=\"attachment\"; filename=\"control_v.%1\"") - .arg(imageData.type)); + .arg(imageData.format)); payload->setBoundary(boundary); payload->append(part); NetworkRequest(url, NetworkRequestType::Post) @@ -163,7 +162,7 @@ void upload(const QMimeData *source, ChannelPtr channel, boost::optional imageData = convertToPng(img); if (imageData) { - TypedBytes data = {imageData.get(), "png"}; + RawImageData data = {imageData.get(), "png"}; uploadQueue.push(data); } else @@ -186,7 +185,7 @@ void upload(const QMimeData *source, ChannelPtr channel, makeSystemMessage(QString("Failed to open file. :("))); return; } - TypedBytes data = {file.readAll(), "gif"}; + RawImageData data = {file.readAll(), "gif"}; uploadQueue.push(data); file.close(); // file.readAll() => might be a bit big but it /should/ work diff --git a/src/util/NuulsUploader.hpp b/src/util/NuulsUploader.hpp index 675d9c0ed..192de31f6 100644 --- a/src/util/NuulsUploader.hpp +++ b/src/util/NuulsUploader.hpp @@ -5,13 +5,13 @@ #include namespace chatterino { -struct TypedBytes { +struct RawImageData { QByteArray data; - QString type; + QString format; }; void upload(QByteArray imageData, ChannelPtr channel, ResizingTextEdit &textEdit, std::string format); -void upload(TypedBytes imageData, ChannelPtr channel, +void upload(RawImageData imageData, ChannelPtr channel, ResizingTextEdit &textEdit); void upload(const QMimeData *source, ChannelPtr channel, ResizingTextEdit &outputTextEdit); From 86318590bf1f620583228067c3ca8379e1cfa755 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Wed, 1 Jan 2020 21:27:13 +0100 Subject: [PATCH 16/27] Fix formatting. --- src/common/Env.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/Env.cpp b/src/common/Env.cpp index b27121845..35a8ea716 100644 --- a/src/common/Env.cpp +++ b/src/common/Env.cpp @@ -58,7 +58,7 @@ Env::Env() "CHATTERINO2_TWITCH_EMOTE_SET_RESOLVER_URL", "https://braize.pajlada.com/chatterino/twitchemotes/set/%1/")) , imageUploaderUrl(readStringEnv("CHATTERINO2_IMAGE_PASTE_SITE_URL", - "https://i.nuuls.com/upload")) + "https://i.nuuls.com/upload")) , twitchServerHost( readStringEnv("CHATTERINO2_TWITCH_SERVER_HOST", "irc.chat.twitch.tv")) , twitchServerPort(readPortEnv("CHATTERINO2_TWITCH_SERVER_PORT", 6697)) From a86367f4be00ab0bd9fbd4b58ac63a555770f4b2 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Fri, 3 Jan 2020 14:43:05 +0100 Subject: [PATCH 17/27] Fix issues pointed out in reviews :) Fixed PR BabyRage --- docs/ENV.md | 5 +++-- src/util/NuulsUploader.cpp | 17 ++++++++--------- src/util/NuulsUploader.hpp | 1 + src/widgets/helper/ResizingTextEdit.cpp | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/docs/ENV.md b/docs/ENV.md index 3fb19d802..00a680a27 100644 --- a/docs/ENV.md +++ b/docs/ENV.md @@ -27,7 +27,7 @@ Arguments: - None Notes: - - If you want to host the images yourself. You need [Nuuls' filehost software](https://github.com/nuuls/fiehost) + - If you want to host the images yourself. You need [Nuuls' filehost software](https://github.com/nuuls/filehost) - Other image hosting software is currently not supported. ### CHATTERINO2_TWITCH_SERVER_HOST @@ -42,4 +42,5 @@ Default value: `443` ### CHATTERINO2_TWITCH_SERVER_SECURE Bool value used to tell Chatterino whether to try to connect securely (secure irc) to the Twitch chat server. -Default value: `true` \ No newline at end of file +Default value: `true` + diff --git a/src/util/NuulsUploader.cpp b/src/util/NuulsUploader.cpp index cbf81beda..a51d5e877 100644 --- a/src/util/NuulsUploader.cpp +++ b/src/util/NuulsUploader.cpp @@ -11,7 +11,7 @@ namespace { -QString getImageFileFormat(QString path) +QString getImageFileFormat(const QString &path) { static QStringList listOfImageFormats = {".png", ".jpg", ".jpeg"}; for (const QString &format : listOfImageFormats) @@ -49,8 +49,8 @@ std::queue uploadQueue; void uploadImageToNuuls(RawImageData imageData, ChannelPtr channel, ResizingTextEdit &textEdit) { - const char *boundary = "thisistheboudaryasd"; - QString contentType = + const static char *boundary = "thisistheboudaryasd"; + const static QString contentType = QString("multipart/form-data; boundary=%1").arg(boundary); static QUrl url(Env::get().imageUploaderUrl); @@ -86,12 +86,8 @@ void uploadImageToNuuls(RawImageData imageData, ChannelPtr channel, "wait until all of them are uploaded. About %2 " "seconds left.") .arg(uploadQueue.size(), - uploadQueue.size() * - (UPLOAD_DELAY / 1000 + - 1) // convert UPLOAD_DELAY to seconds - ))); - // Argument number 2 is the ETA. - // 2 seconds for the timer that's there not to spam Nuuls' server + uploadQueue.size() * (UPLOAD_DELAY / 1000 + 1)))); + // 2 seconds for the timer that's there not to spam the remote server // and 1 second of actual uploading. QTimer::singleShot(UPLOAD_DELAY, [channel, &textEdit]() { @@ -156,6 +152,7 @@ void upload(const QMimeData *source, ChannelPtr channel, { channel->addMessage( makeSystemMessage(QString("Couldn't load image :("))); + isUploading = false; return; } @@ -183,6 +180,7 @@ void upload(const QMimeData *source, ChannelPtr channel, { channel->addMessage( makeSystemMessage(QString("Failed to open file. :("))); + isUploading = false; return; } RawImageData data = {file.readAll(), "gif"}; @@ -217,6 +215,7 @@ void upload(const QMimeData *source, ChannelPtr channel, { channel->addMessage(makeSystemMessage( QString("Cannot upload file, failed to convert to png."))); + isUploading = false; } } } diff --git a/src/util/NuulsUploader.hpp b/src/util/NuulsUploader.hpp index 192de31f6..0c059367d 100644 --- a/src/util/NuulsUploader.hpp +++ b/src/util/NuulsUploader.hpp @@ -9,6 +9,7 @@ struct RawImageData { QByteArray data; QString format; }; + void upload(QByteArray imageData, ChannelPtr channel, ResizingTextEdit &textEdit, std::string format); void upload(RawImageData imageData, ChannelPtr channel, diff --git a/src/widgets/helper/ResizingTextEdit.cpp b/src/widgets/helper/ResizingTextEdit.cpp index 60ccec58d..d26339130 100644 --- a/src/widgets/helper/ResizingTextEdit.cpp +++ b/src/widgets/helper/ResizingTextEdit.cpp @@ -301,7 +301,7 @@ void ResizingTextEdit::dropEvent(QDropEvent *event) { this->imagePasted.invoke(event->mimeData()); } - else // allow for previous functionality of dropping text. + else { QTextEdit::dropEvent(event); } From 6c0b53a9966a714e417443d47f9055d1eaa9b07e Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Fri, 3 Jan 2020 15:31:39 +0100 Subject: [PATCH 18/27] Fix wrong QSrting::arg() being called by using two calls. --- src/util/NuulsUploader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/NuulsUploader.cpp b/src/util/NuulsUploader.cpp index a51d5e877..35598a37f 100644 --- a/src/util/NuulsUploader.cpp +++ b/src/util/NuulsUploader.cpp @@ -85,8 +85,8 @@ void uploadImageToNuuls(RawImageData imageData, ChannelPtr channel, QString("Your image has been uploaded. %1 left. Please " "wait until all of them are uploaded. About %2 " "seconds left.") - .arg(uploadQueue.size(), - uploadQueue.size() * (UPLOAD_DELAY / 1000 + 1)))); + .arg(uploadQueue.size()) + .arg(uploadQueue.size() * (UPLOAD_DELAY / 1000 + 1)))); // 2 seconds for the timer that's there not to spam the remote server // and 1 second of actual uploading. From 916fbb2551d4be9e26293364a24a39058409bca7 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sat, 8 Feb 2020 15:44:03 +0100 Subject: [PATCH 19/27] Fix a couple things Pajlada pointed out :) Change `CHATTERINO2_IMAGE_PASTE_SITE_URL` to `CHATTERINO2_IMAGE_UPLOADER_URL` Remove newline at the end of `docs/ENV.md` --- docs/ENV.md | 3 +-- src/common/Env.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/ENV.md b/docs/ENV.md index 00a680a27..1615009e6 100644 --- a/docs/ENV.md +++ b/docs/ENV.md @@ -19,7 +19,7 @@ Default value: `https://braize.pajlada.com/chatterino/twitchemotes/set/%1/` Arguments: - `%1` = Emote set ID -### CHATTERINO2_IMAGE_PASTE_SITE_URL +### CHATTERINO2_IMAGE_UPLOADER_URL Used to change the URL that Chatterino2 uses when trying to paste an image into chat. This can be used for hosting the uploaded images yourself. Default value: `https://i.nuuls.com/upload` @@ -43,4 +43,3 @@ Default value: `443` ### CHATTERINO2_TWITCH_SERVER_SECURE Bool value used to tell Chatterino whether to try to connect securely (secure irc) to the Twitch chat server. Default value: `true` - diff --git a/src/common/Env.cpp b/src/common/Env.cpp index 35a8ea716..7921e8dec 100644 --- a/src/common/Env.cpp +++ b/src/common/Env.cpp @@ -57,7 +57,7 @@ Env::Env() , twitchEmoteSetResolverUrl(readStringEnv( "CHATTERINO2_TWITCH_EMOTE_SET_RESOLVER_URL", "https://braize.pajlada.com/chatterino/twitchemotes/set/%1/")) - , imageUploaderUrl(readStringEnv("CHATTERINO2_IMAGE_PASTE_SITE_URL", + , imageUploaderUrl(readStringEnv("CHATTERINO2_IMAGE_UPLOADER_URL", "https://i.nuuls.com/upload")) , twitchServerHost( readStringEnv("CHATTERINO2_TWITCH_SERVER_HOST", "irc.chat.twitch.tv")) From 553e8f2f7bc2727462a08264c9db13ec2cbc62c8 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sat, 8 Feb 2020 15:47:27 +0100 Subject: [PATCH 20/27] Delete useless else block --- src/widgets/helper/ResizingTextEdit.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/widgets/helper/ResizingTextEdit.cpp b/src/widgets/helper/ResizingTextEdit.cpp index d26339130..a26909a64 100644 --- a/src/widgets/helper/ResizingTextEdit.cpp +++ b/src/widgets/helper/ResizingTextEdit.cpp @@ -265,10 +265,7 @@ bool ResizingTextEdit::canInsertFromMimeData(const QMimeData *source) const { return true; } - else - { - return QTextEdit::canInsertFromMimeData(source); - } + return QTextEdit::canInsertFromMimeData(source); } void ResizingTextEdit::insertFromMimeData(const QMimeData *source) From d53bfbfdf2239afd9a1043864dafaa271be00485 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sat, 8 Feb 2020 16:26:32 +0100 Subject: [PATCH 21/27] Fixes and more changes Pajlada requested :) - get rid of `getImageFileFormat`, now uses QMimeDatabase - now uses a `QMutex` to be thread safe, - uploading two things at the same time is now impossible --- src/util/NuulsUploader.cpp | 42 +++++++++++++++----------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/src/util/NuulsUploader.cpp b/src/util/NuulsUploader.cpp index 35598a37f..db1fef06a 100644 --- a/src/util/NuulsUploader.cpp +++ b/src/util/NuulsUploader.cpp @@ -4,26 +4,16 @@ #include "common/NetworkRequest.hpp" #include "providers/twitch/TwitchMessageBuilder.hpp" +#include #include +#include +#include #define UPLOAD_DELAY 2000 // Delay between uploads in milliseconds namespace { -QString getImageFileFormat(const QString &path) -{ - static QStringList listOfImageFormats = {".png", ".jpg", ".jpeg"}; - for (const QString &format : listOfImageFormats) - { - if (path.endsWith(format, Qt::CaseInsensitive)) - { - return format.mid(1); - } - } - return QString(); -} - boost::optional convertToPng(QImage image) { QByteArray imageData; @@ -43,7 +33,7 @@ boost::optional convertToPng(QImage image) namespace chatterino { // These variables are only used from the main thread. -bool isUploading = false; +auto uploadMutex = QMutex(); std::queue uploadQueue; void uploadImageToNuuls(RawImageData imageData, ChannelPtr channel, @@ -73,11 +63,11 @@ void uploadImageToNuuls(RawImageData imageData, ChannelPtr channel, .multiPart(payload) .onSuccess([&textEdit, channel](NetworkResult result) -> Outcome { textEdit.insertPlainText(result.getData() + QString(" ")); - isUploading = false; if (uploadQueue.empty()) { channel->addMessage(makeSystemMessage( QString("Your image has been uploaded."))); + uploadMutex.unlock(); } else { @@ -101,7 +91,7 @@ void uploadImageToNuuls(RawImageData imageData, ChannelPtr channel, channel->addMessage(makeSystemMessage( QString("An error happened while uploading your image: %1") .arg(result.status()))); - isUploading = false; + uploadMutex.unlock(); return true; }) .execute(); @@ -110,15 +100,13 @@ void uploadImageToNuuls(RawImageData imageData, ChannelPtr channel, void upload(const QMimeData *source, ChannelPtr channel, ResizingTextEdit &outputTextEdit) { - // There's no need to thread proof this function. It is called only from the main thread. - if (isUploading) + if (!uploadMutex.tryLock()) { channel->addMessage(makeSystemMessage( QString("Please wait until the upload finishes."))); return; } - isUploading = true; channel->addMessage(makeSystemMessage(QString("Started upload..."))); if (source->hasFormat("image/png")) @@ -138,12 +126,15 @@ void upload(const QMimeData *source, ChannelPtr channel, } else if (source->hasUrls()) { + auto mimeDb = QMimeDatabase(); // This path gets chosen when files are copied from a file manager, like explorer.exe, caja. // Each entry in source->urls() is a QUrl pointing to a file that was copied. for (const QUrl &path : source->urls()) { QString localPath = path.toLocalFile(); - if (!getImageFileFormat(localPath).isEmpty()) + QMimeType mime = mimeDb.mimeTypeForUrl(path); + qDebug() << mime.name(); + if (mime.name().startsWith("image") && !mime.inherits("image/gif")) { channel->addMessage(makeSystemMessage( QString("Uploading image: %1").arg(localPath))); @@ -152,7 +143,7 @@ void upload(const QMimeData *source, ChannelPtr channel, { channel->addMessage( makeSystemMessage(QString("Couldn't load image :("))); - isUploading = false; + uploadMutex.unlock(); return; } @@ -170,7 +161,7 @@ void upload(const QMimeData *source, ChannelPtr channel, .arg(localPath))); } } - else if (localPath.endsWith(".gif")) + else if (mime.inherits("image/gif")) { channel->addMessage(makeSystemMessage( QString("Uploading GIF: %1").arg(localPath))); @@ -180,7 +171,7 @@ void upload(const QMimeData *source, ChannelPtr channel, { channel->addMessage( makeSystemMessage(QString("Failed to open file. :("))); - isUploading = false; + uploadMutex.unlock(); return; } RawImageData data = {file.readAll(), "gif"}; @@ -193,7 +184,8 @@ void upload(const QMimeData *source, ChannelPtr channel, channel->addMessage(makeSystemMessage( QString("Cannot upload file: %1, not an image") .arg(localPath))); - isUploading = false; + uploadMutex.unlock(); + return; } } if (!uploadQueue.empty()) @@ -215,7 +207,7 @@ void upload(const QMimeData *source, ChannelPtr channel, { channel->addMessage(makeSystemMessage( QString("Cannot upload file, failed to convert to png."))); - isUploading = false; + uploadMutex.unlock(); } } } From a929053c4bb2cb5d42d426d47cbae5724c8a590e Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sat, 8 Feb 2020 16:41:01 +0100 Subject: [PATCH 22/27] Dropping images now works on the whole split :) --- src/widgets/helper/ResizingTextEdit.cpp | 24 ------------------------ src/widgets/helper/ResizingTextEdit.hpp | 3 --- src/widgets/splits/Split.cpp | 24 ++++++++++++++++++++++++ src/widgets/splits/Split.hpp | 3 +++ 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/widgets/helper/ResizingTextEdit.cpp b/src/widgets/helper/ResizingTextEdit.cpp index a26909a64..40bc66b02 100644 --- a/src/widgets/helper/ResizingTextEdit.cpp +++ b/src/widgets/helper/ResizingTextEdit.cpp @@ -25,7 +25,6 @@ ResizingTextEdit::ResizingTextEdit() [this] { this->completionInProgress_ = false; }); this->setFocusPolicy(Qt::ClickFocus); - setAcceptDrops(true); } QSize ResizingTextEdit::sizeHint() const @@ -280,29 +279,6 @@ void ResizingTextEdit::insertFromMimeData(const QMimeData *source) } } -void ResizingTextEdit::dragEnterEvent(QDragEnterEvent *event) -{ - if (event->mimeData()->hasImage() || event->mimeData()->hasUrls()) - { - event->acceptProposedAction(); - } - else - { - QTextEdit::dragEnterEvent(event); - } -} - -void ResizingTextEdit::dropEvent(QDropEvent *event) -{ - if (event->mimeData()->hasImage() || event->mimeData()->hasUrls()) - { - this->imagePasted.invoke(event->mimeData()); - } - else - { - QTextEdit::dropEvent(event); - } -} QCompleter *ResizingTextEdit::getCompleter() const { return this->completer_; diff --git a/src/widgets/helper/ResizingTextEdit.hpp b/src/widgets/helper/ResizingTextEdit.hpp index 018d345f9..cd1db3114 100644 --- a/src/widgets/helper/ResizingTextEdit.hpp +++ b/src/widgets/helper/ResizingTextEdit.hpp @@ -34,9 +34,6 @@ protected: bool canInsertFromMimeData(const QMimeData *source) const override; void insertFromMimeData(const QMimeData *source) override; - void dragEnterEvent(QDragEnterEvent *event) override; - void dropEvent(QDropEvent *event) override; - private: // hadSpace is set to true in case the "textUnderCursor" word was after a // space diff --git a/src/widgets/splits/Split.cpp b/src/widgets/splits/Split.cpp index 9983a65cb..66ee22324 100644 --- a/src/widgets/splits/Split.cpp +++ b/src/widgets/splits/Split.cpp @@ -211,6 +211,7 @@ Split::Split(QWidget *parent) [this](const QMimeData *source) { upload(source, this->getChannel(), *this->input_->ui_.textEdit); }); + setAcceptDrops(true); } Split::~Split() @@ -689,6 +690,29 @@ void Split::reloadChannelAndSubscriberEmotes() } } +void Split::dragEnterEvent(QDragEnterEvent *event) +{ + if (event->mimeData()->hasImage() || event->mimeData()->hasUrls()) + { + event->acceptProposedAction(); + } + else + { + BaseWidget::dragEnterEvent(event); + } +} + +void Split::dropEvent(QDropEvent *event) +{ + if (event->mimeData()->hasImage() || event->mimeData()->hasUrls()) + { + this->input_->ui_.textEdit->imagePasted.invoke(event->mimeData()); + } + else + { + BaseWidget::dropEvent(event); + } +} template static Iter select_randomly(Iter start, Iter end, RandomGenerator &g) { diff --git a/src/widgets/splits/Split.hpp b/src/widgets/splits/Split.hpp index edfca352e..56d21053e 100644 --- a/src/widgets/splits/Split.hpp +++ b/src/widgets/splits/Split.hpp @@ -86,6 +86,9 @@ protected: void leaveEvent(QEvent *event) override; void focusInEvent(QFocusEvent *event) override; + void dragEnterEvent(QDragEnterEvent *event) override; + void dropEvent(QDropEvent *event) override; + private: void channelNameUpdated(const QString &newChannelName); void handleModifiers(Qt::KeyboardModifiers modifiers); From dc9acf1bf33e98bb459e518b20863f3e78f1d7b9 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sun, 9 Feb 2020 11:15:39 +0100 Subject: [PATCH 23/27] Sort imports --- src/widgets/splits/Split.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/splits/Split.cpp b/src/widgets/splits/Split.cpp index 0576b7859..63d7cc5ea 100644 --- a/src/widgets/splits/Split.cpp +++ b/src/widgets/splits/Split.cpp @@ -11,8 +11,8 @@ #include "singletons/Settings.hpp" #include "singletons/Theme.hpp" #include "singletons/WindowManager.hpp" -#include "util/NuulsUploader.hpp" #include "util/Clipboard.hpp" +#include "util/NuulsUploader.hpp" #include "util/Shortcut.hpp" #include "util/StreamLink.hpp" #include "widgets/Notebook.hpp" From 979bf5e74a0f6815346eb963d53ee5080f350fd5 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 10 Feb 2020 16:55:59 +0100 Subject: [PATCH 24/27] Add missing return --- src/util/NuulsUploader.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/util/NuulsUploader.cpp b/src/util/NuulsUploader.cpp index db1fef06a..443fbfd1d 100644 --- a/src/util/NuulsUploader.cpp +++ b/src/util/NuulsUploader.cpp @@ -159,6 +159,8 @@ void upload(const QMimeData *source, ChannelPtr channel, QString("Cannot upload file: %1, Couldn't convert " "image to png.") .arg(localPath))); + uploadMutex.unlock(); + return; } } else if (mime.inherits("image/gif")) From 8f076d8075f2caf58c6f9dbcdb7b4a60e990f03e Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sun, 29 Mar 2020 13:47:52 +0200 Subject: [PATCH 25/27] Internal changes Add missing `static` and `const` --- src/util/NuulsUploader.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/util/NuulsUploader.cpp b/src/util/NuulsUploader.cpp index 443fbfd1d..1678ff474 100644 --- a/src/util/NuulsUploader.cpp +++ b/src/util/NuulsUploader.cpp @@ -33,13 +33,13 @@ boost::optional convertToPng(QImage image) namespace chatterino { // These variables are only used from the main thread. -auto uploadMutex = QMutex(); -std::queue uploadQueue; +static auto uploadMutex = QMutex(); +static std::queue uploadQueue; void uploadImageToNuuls(RawImageData imageData, ChannelPtr channel, ResizingTextEdit &textEdit) { - const static char *boundary = "thisistheboudaryasd"; + const static char *const boundary = "thisistheboudaryasd"; const static QString contentType = QString("multipart/form-data; boundary=%1").arg(boundary); static QUrl url(Env::get().imageUploaderUrl); From 2b1c9794b7dc9a9bcc1951de6627ccb6f4b59bca Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sat, 9 May 2020 13:14:41 +0200 Subject: [PATCH 26/27] Add confirmation box for uploads. --- src/singletons/Settings.hpp | 1 + src/widgets/settingspages/GeneralPage.cpp | 3 +++ src/widgets/splits/Split.cpp | 31 ++++++++++++++++++++--- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index 1d56e5787..11927120a 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -291,6 +291,7 @@ public: "/misc/attachExtensionToAnyProcess", false}; BoolSetting hideViewerCountAndDuration = { "/misc/hideViewerCountAndDuration", false}; + BoolSetting askOnImageUpload = {"/misc/askOnImageUpload", true}; /// Debug BoolSetting showUnhandledIrcMessages = {"/debug/showUnhandledIrcMessages", diff --git a/src/widgets/settingspages/GeneralPage.cpp b/src/widgets/settingspages/GeneralPage.cpp index 26f35cdbe..fb8fd1524 100644 --- a/src/widgets/settingspages/GeneralPage.cpp +++ b/src/widgets/settingspages/GeneralPage.cpp @@ -545,6 +545,9 @@ void GeneralPage::initLayout(SettingsLayout &layout) layout.addCheckbox( "Hide viewercount and stream length while hovering the split", s.hideViewerCountAndDuration); + layout.addCheckbox( + "Ask for confirmation when uploading an image to i.nuuls.com", + s.askOnImageUpload); layout.addTitle("Cache"); layout.addDescription( diff --git a/src/widgets/splits/Split.cpp b/src/widgets/splits/Split.cpp index c331f3c23..84048191b 100644 --- a/src/widgets/splits/Split.cpp +++ b/src/widgets/splits/Split.cpp @@ -207,10 +207,33 @@ Split::Split(QWidget *parent) [this] { this->focused.invoke(); }); this->input_->ui_.textEdit->focusLost.connect( [this] { this->focusLost.invoke(); }); - this->input_->ui_.textEdit->imagePasted.connect( - [this](const QMimeData *source) { - upload(source, this->getChannel(), *this->input_->ui_.textEdit); - }); + this->input_->ui_.textEdit->imagePasted.connect([this](const QMimeData + *source) { + if (getSettings()->askOnImageUpload.getValue()) + { + QMessageBox msgBox; + msgBox.setText("Image upload"); + msgBox.setInformativeText( + "You are uploading an image to i.nuuls.com. You won't be able " + "to remove the image from the site. Are you okay with this?"); + msgBox.addButton(QMessageBox::Cancel); + msgBox.addButton(QMessageBox::Yes); + msgBox.addButton("Yes, don't ask again", QMessageBox::YesRole); + + msgBox.setDefaultButton(QMessageBox::Yes); + + auto picked = msgBox.exec(); + if (picked == QMessageBox::Cancel) + { + return; + } + else if (picked == 0) // don't ask again button + { + getSettings()->askOnImageUpload.setValue(false); + } + } + upload(source, this->getChannel(), *this->input_->ui_.textEdit); + }); setAcceptDrops(true); } From 4bfd20f9432585337c4fed4296b02be89b618c1e Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sat, 9 May 2020 13:33:23 +0200 Subject: [PATCH 27/27] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 477bf3f5d..f0c936b25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## Unversioned +- Major: Added image upload functionality to i.nuuls.com. This works by dragging and dropping an image into a split, or pasting an image into the text edit field. (#1332) - Minor: Emotes in the emote popup are now sorted in the same order as the tab completion (#1549) - Bugfix: Fix preview on hover not working when Animated emotes options was disabled (#1546) - Bugfix: FFZ custom mod badges no longer scale with the emote scale options (#1602)