From 65fab779c5a447efa367c70879cd29e9a57b1960 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 23 Sep 2019 19:36:52 +0200 Subject: [PATCH 001/153] 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 002/153] 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 003/153] 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 004/153] 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 005/153] 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 006/153] 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 007/153] 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 008/153] 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 009/153] 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 010/153] 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 011/153] 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 012/153] 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 013/153] 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 014/153] 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 015/153] 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 016/153] 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 017/153] 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 018/153] 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 019/153] 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 020/153] 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 021/153] 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 022/153] 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 023/153] 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 024/153] 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 6229b2f43478daa478de3e12ee441d4f34b8ab86 Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 16 Feb 2020 14:24:11 +0100 Subject: [PATCH 025/153] sync gif emotes of same length --- src/messages/Image.cpp | 19 +++++++++++++------ src/messages/Image.hpp | 1 + src/singletons/Emotes.hpp | 2 -- src/singletons/helper/GifTimer.cpp | 1 + src/singletons/helper/GifTimer.hpp | 7 +++++++ 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/messages/Image.cpp b/src/messages/Image.cpp index b42223012..39a30545f 100644 --- a/src/messages/Image.cpp +++ b/src/messages/Image.cpp @@ -41,6 +41,14 @@ namespace detail { getApp()->emotes->gifTimer.signal.connect( [this] { this->advance(); }); } + + auto totalLength = std::accumulate( + this->items_.begin(), this->items_.end(), 0UL, + [](auto init, auto &&frame) { return init + frame.duration; }); + + this->durationOffset_ = std::min( + int(getApp()->emotes->gifTimer.position() % totalLength), 60000); + this->processOffset(); } Frames::~Frames() @@ -58,17 +66,16 @@ namespace detail { void Frames::advance() { - this->durationOffset_ += GIF_FRAME_LENGTH; + this->durationOffset_ += gifFrameLength; + this->processOffset(); + } + void Frames::processOffset() + { while (true) { this->index_ %= this->items_.size(); - // TODO: Figure out what this was supposed to achieve - // if (this->index_ >= this->items_.size()) { - // this->index_ = this->index_; - // } - if (this->durationOffset_ > this->items_[this->index_].duration) { this->durationOffset_ -= this->items_[this->index_].duration; diff --git a/src/messages/Image.hpp b/src/messages/Image.hpp index f114d1f0f..193ba5787 100644 --- a/src/messages/Image.hpp +++ b/src/messages/Image.hpp @@ -35,6 +35,7 @@ namespace detail { boost::optional first() const; private: + void processOffset(); QVector> items_; int index_{0}; int durationOffset_{0}; diff --git a/src/singletons/Emotes.hpp b/src/singletons/Emotes.hpp index fc475d752..be7fdc480 100644 --- a/src/singletons/Emotes.hpp +++ b/src/singletons/Emotes.hpp @@ -2,8 +2,6 @@ #include "common/Singleton.hpp" -#define GIF_FRAME_LENGTH 33 - #include "providers/bttv/BttvEmotes.hpp" #include "providers/emoji/Emojis.hpp" #include "providers/ffz/FfzEmotes.hpp" diff --git a/src/singletons/helper/GifTimer.cpp b/src/singletons/helper/GifTimer.cpp index 28e1b77ed..2edf7bacd 100644 --- a/src/singletons/helper/GifTimer.cpp +++ b/src/singletons/helper/GifTimer.cpp @@ -22,6 +22,7 @@ void GIFTimer::initialize() qApp->activeWindow() == nullptr) return; + this->position_ += gifFrameLength; this->signal.invoke(); getApp()->windows->repaintGifEmotes(); }); diff --git a/src/singletons/helper/GifTimer.hpp b/src/singletons/helper/GifTimer.hpp index b44fcfb7f..da47149b8 100644 --- a/src/singletons/helper/GifTimer.hpp +++ b/src/singletons/helper/GifTimer.hpp @@ -5,15 +5,22 @@ namespace chatterino { +constexpr long unsigned gifFrameLength = 33; + class GIFTimer { public: void initialize(); pajlada::Signals::NoArgSignal signal; + long unsigned position() + { + return this->position_; + } private: QTimer timer; + long unsigned position_{}; }; } // namespace chatterino From d0839ac36ee3b6b2492c54f344d8ca31d2bea814 Mon Sep 17 00:00:00 2001 From: Leon Richardt Date: Thu, 20 Feb 2020 00:10:10 +0100 Subject: [PATCH 026/153] Make pre-defined highlight columns unselectable Fixes #1539. --- src/controllers/highlights/HighlightModel.cpp | 12 ++++++------ src/util/StandardItemHelper.hpp | 14 ++++++++++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/controllers/highlights/HighlightModel.cpp b/src/controllers/highlights/HighlightModel.cpp index 05f37bf78..ef134c12d 100644 --- a/src/controllers/highlights/HighlightModel.cpp +++ b/src/controllers/highlights/HighlightModel.cpp @@ -63,10 +63,10 @@ void HighlightModel::afterInit() usernameRow[Column::CaseSensitive]->setFlags(0); QUrl selfSound = QUrl(getSettings()->selfHighlightSoundUrl.getValue()); - setFilePathItem(usernameRow[Column::SoundPath], selfSound); + setFilePathItem(usernameRow[Column::SoundPath], selfSound, false); auto selfColor = ColorProvider::instance().color(ColorType::SelfHighlight); - setColorItem(usernameRow[Column::Color], *selfColor); + setColorItem(usernameRow[Column::Color], *selfColor, false); this->insertCustomRow(usernameRow, 0); @@ -86,10 +86,10 @@ void HighlightModel::afterInit() QUrl whisperSound = QUrl(getSettings()->whisperHighlightSoundUrl.getValue()); - setFilePathItem(whisperRow[Column::SoundPath], whisperSound); + setFilePathItem(whisperRow[Column::SoundPath], whisperSound, false); auto whisperColor = ColorProvider::instance().color(ColorType::Whisper); - setColorItem(whisperRow[Column::Color], *whisperColor); + setColorItem(whisperRow[Column::Color], *whisperColor, false); this->insertCustomRow(whisperRow, 1); @@ -107,10 +107,10 @@ void HighlightModel::afterInit() subRow[Column::CaseSensitive]->setFlags(0); QUrl subSound = QUrl(getSettings()->subHighlightSoundUrl.getValue()); - setFilePathItem(subRow[Column::SoundPath], subSound); + setFilePathItem(subRow[Column::SoundPath], subSound, false); auto subColor = ColorProvider::instance().color(ColorType::Subscription); - setColorItem(subRow[Column::Color], *subColor); + setColorItem(subRow[Column::Color], *subColor, false); this->insertCustomRow(subRow, 2); } diff --git a/src/util/StandardItemHelper.hpp b/src/util/StandardItemHelper.hpp index 4b7ba52b9..47e4db14b 100644 --- a/src/util/StandardItemHelper.hpp +++ b/src/util/StandardItemHelper.hpp @@ -22,17 +22,23 @@ static void setStringItem(QStandardItem *item, const QString &value, (editable ? (Qt::ItemIsEditable) : 0))); } -static void setFilePathItem(QStandardItem *item, const QUrl &value) +static void setFilePathItem(QStandardItem *item, const QUrl &value, + bool selectable = true) { item->setData(value, Qt::UserRole); item->setData(value.fileName(), Qt::DisplayRole); - item->setFlags(Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable)); + item->setFlags( + Qt::ItemFlags(Qt::ItemIsEnabled | + (selectable ? Qt::ItemIsSelectable : Qt::NoItemFlags))); } -static void setColorItem(QStandardItem *item, const QColor &value) +static void setColorItem(QStandardItem *item, const QColor &value, + bool selectable = true) { item->setData(value, Qt::DecorationRole); - item->setFlags(Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable)); + item->setFlags( + Qt::ItemFlags(Qt::ItemIsEnabled | + (selectable ? Qt::ItemIsSelectable : Qt::NoItemFlags))); } static QStandardItem *emptyItem() From d6c3c996050d14aaca2b927982578ee2ae775a3c Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 20 Feb 2020 23:16:46 +0100 Subject: [PATCH 027/153] list settings can be reordered now --- src/common/SignalVectorModel.hpp | 64 +++++++++++++++++++++++- src/widgets/helper/EditableModelView.cpp | 24 ++++++--- 2 files changed, 79 insertions(+), 9 deletions(-) diff --git a/src/common/SignalVectorModel.hpp b/src/common/SignalVectorModel.hpp index adea78843..bacb6c4df 100644 --- a/src/common/SignalVectorModel.hpp +++ b/src/common/SignalVectorModel.hpp @@ -205,7 +205,9 @@ public: assert(row >= 0 && row < this->rows_.size() && column >= 0 && column < this->columnCount_); - return this->rows_[row].items[column]->flags(); + return this->rows_[row].items[column]->flags() | + Qt::ItemFlag::ItemIsDragEnabled | + Qt::ItemFlag::ItemIsDropEnabled; } QStandardItem *getItem(int row, int column) @@ -239,6 +241,66 @@ public: return true; } + QStringList mimeTypes() const override + { + return {"chatterino_row_id"}; + } + + QMimeData *mimeData(const QModelIndexList &list) const + { + if (list.length() == 1) + { + return nullptr; + } + + // Check if all indices are in the same row -> single row selected + for (auto &&x : list) + { + if (x.row() != list.first().row()) + return nullptr; + } + + auto data = new QMimeData; + data->setData("chatterino_row_id", QByteArray::number(list[0].row())); + return data; + } + + bool dropMimeData(const QMimeData *data, Qt::DropAction action, int /*row*/, + int /*column*/, const QModelIndex &parent) override + { + if (data->hasFormat("chatterino_row_id") && + action & (Qt::DropAction::MoveAction | Qt::DropAction::CopyAction)) + { + int from = data->data("chatterino_row_id").toInt(); + int to = parent.row(); + + if (from < 0 || from > this->vector_->getVector().size() || + to < 0 || to > this->vector_->getVector().size()) + { + return false; + } + + if (from != to) + { + auto item = this->vector_->getVector()[from]; + this->vector_->removeItem(from); + this->vector_->insertItem(item, to); + } + + // We return false since we remove items ourselves. + return false; + } + + return false; + } + + Qt::DropActions supportedDropActions() const override + { + return this->vector_->isSorted() + ? Qt::DropActions() + : Qt::DropAction::CopyAction | Qt::DropAction::MoveAction; + } + protected: virtual void afterInit() { diff --git a/src/widgets/helper/EditableModelView.cpp b/src/widgets/helper/EditableModelView.cpp index ff6f92143..62f1e4dcc 100644 --- a/src/widgets/helper/EditableModelView.cpp +++ b/src/widgets/helper/EditableModelView.cpp @@ -15,9 +15,12 @@ EditableModelView::EditableModelView(QAbstractTableModel *model) { this->model_->setParent(this); this->tableView_->setModel(model); - this->tableView_->setSelectionMode(QAbstractItemView::ExtendedSelection); + this->tableView_->setSelectionMode(QAbstractItemView::SingleSelection); this->tableView_->setSelectionBehavior(QAbstractItemView::SelectRows); - this->tableView_->verticalHeader()->hide(); + this->tableView_->setDragDropMode(QTableView::DragDropMode::InternalMove); + this->tableView_->setDragDropOverwriteMode(false); + this->tableView_->setDefaultDropAction(Qt::DropAction::MoveAction); + this->tableView_->verticalHeader()->setVisible(false); // create layout QVBoxLayout *vbox = new QVBoxLayout(this); @@ -38,12 +41,17 @@ EditableModelView::EditableModelView(QAbstractTableModel *model) QPushButton *remove = new QPushButton("Remove"); buttons->addWidget(remove); QObject::connect(remove, &QPushButton::clicked, [this] { - QModelIndexList list; - while ((list = this->getTableView()->selectionModel()->selectedRows(0)) - .length() > 0) - { - model_->removeRow(list[0].row()); - } + auto selected = this->getTableView()->selectionModel()->selectedRows(0); + + // Remove rows backwards so indices don't shift. + std::vector rows; + for (auto &&index : selected) + rows.push_back(index.row()); + + std::sort(rows.begin(), rows.end(), std::greater{}); + + for (auto &&row : rows) + model_->removeRow(row); }); buttons->addStretch(); From 4a69095d5f8cd906bdcb927331d68204c721943c Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 20 Feb 2020 23:37:46 +0100 Subject: [PATCH 028/153] fix --- src/common/SignalVectorModel.hpp | 4 +--- src/controllers/commands/CommandModel.cpp | 10 ++++------ src/util/StandardItemHelper.hpp | 23 +++++++++++++++-------- src/widgets/settingspages/CommandPage.cpp | 1 + 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/common/SignalVectorModel.hpp b/src/common/SignalVectorModel.hpp index bacb6c4df..bb7cd7c05 100644 --- a/src/common/SignalVectorModel.hpp +++ b/src/common/SignalVectorModel.hpp @@ -205,9 +205,7 @@ public: assert(row >= 0 && row < this->rows_.size() && column >= 0 && column < this->columnCount_); - return this->rows_[row].items[column]->flags() | - Qt::ItemFlag::ItemIsDragEnabled | - Qt::ItemFlag::ItemIsDropEnabled; + return this->rows_[row].items[column]->flags(); } QStandardItem *getItem(int row, int column) diff --git a/src/controllers/commands/CommandModel.cpp b/src/controllers/commands/CommandModel.cpp index 4a55907d0..d12a81c69 100644 --- a/src/controllers/commands/CommandModel.cpp +++ b/src/controllers/commands/CommandModel.cpp @@ -1,5 +1,7 @@ #include "CommandModel.hpp" +#include "util/StandardItemHelper.hpp" + namespace chatterino { // commandmodel @@ -20,12 +22,8 @@ Command CommandModel::getItemFromRow(std::vector &row, void CommandModel::getRowFromItem(const Command &item, std::vector &row) { - row[0]->setData(item.name, Qt::DisplayRole); - row[0]->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | - Qt::ItemIsEditable); - row[1]->setData(item.func, Qt::DisplayRole); - row[1]->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | - Qt::ItemIsEditable); + setStringItem(row[0], item.name); + setStringItem(row[1], item.func); } } // namespace chatterino diff --git a/src/util/StandardItemHelper.hpp b/src/util/StandardItemHelper.hpp index 47e4db14b..f9caeb0a6 100644 --- a/src/util/StandardItemHelper.hpp +++ b/src/util/StandardItemHelper.hpp @@ -4,12 +4,20 @@ namespace chatterino { +static auto defaultItemFlags(bool selectable) +{ + return Qt::ItemIsEnabled | + (selectable ? Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | + Qt::ItemIsDropEnabled + : Qt::ItemFlag()); +} + static void setBoolItem(QStandardItem *item, bool value, bool userCheckable = true, bool selectable = true) { - item->setFlags(Qt::ItemFlags( - Qt::ItemIsEnabled | (selectable ? Qt::ItemIsSelectable : 0) | - (userCheckable ? Qt::ItemIsUserCheckable : 0))); + item->setFlags( + Qt::ItemFlags(defaultItemFlags(selectable) | + (userCheckable ? Qt::ItemIsUserCheckable : 0))); item->setCheckState(value ? Qt::Checked : Qt::Unchecked); } @@ -17,8 +25,7 @@ static void setStringItem(QStandardItem *item, const QString &value, bool editable = true, bool selectable = true) { item->setData(value, Qt::EditRole); - item->setFlags(Qt::ItemFlags(Qt::ItemIsEnabled | - (selectable ? Qt::ItemIsSelectable : 0) | + item->setFlags(Qt::ItemFlags(defaultItemFlags(selectable) | (editable ? (Qt::ItemIsEditable) : 0))); } @@ -28,7 +35,7 @@ static void setFilePathItem(QStandardItem *item, const QUrl &value, item->setData(value, Qt::UserRole); item->setData(value.fileName(), Qt::DisplayRole); item->setFlags( - Qt::ItemFlags(Qt::ItemIsEnabled | + Qt::ItemFlags(defaultItemFlags(selectable) | (selectable ? Qt::ItemIsSelectable : Qt::NoItemFlags))); } @@ -37,14 +44,14 @@ static void setColorItem(QStandardItem *item, const QColor &value, { item->setData(value, Qt::DecorationRole); item->setFlags( - Qt::ItemFlags(Qt::ItemIsEnabled | + Qt::ItemFlags(defaultItemFlags(selectable) | (selectable ? Qt::ItemIsSelectable : Qt::NoItemFlags))); } static QStandardItem *emptyItem() { auto *item = new QStandardItem(); - item->setFlags(Qt::ItemFlags(0)); + item->setFlags(Qt::ItemFlags()); return item; } diff --git a/src/widgets/settingspages/CommandPage.cpp b/src/widgets/settingspages/CommandPage.cpp index 6df83105d..c3470c4b1 100644 --- a/src/widgets/settingspages/CommandPage.cpp +++ b/src/widgets/settingspages/CommandPage.cpp @@ -51,6 +51,7 @@ CommandPage::CommandPage() Command{"/command", "I made a new command HeyGuys"}); }); + // TODO: asyncronously check path if (QFile(c1settingsPath()).exists()) { auto button = new QPushButton("Import commands from Chatterino 1"); From 0bb112a3c2132dc5829b3c0dba4c44e41a00d611 Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 20 Feb 2020 23:47:25 +0100 Subject: [PATCH 029/153] improved performance of opening the settings dialog --- src/widgets/dialogs/SettingsDialog.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index 73ad5f756..e9769e3fd 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -28,13 +28,15 @@ SettingsDialog::SettingsDialog() { this->setWindowTitle("Chatterino Settings"); + this->themeChangedEvent(); + this->scaleChangedEvent(this->scale()); + this->initUi(); this->addTabs(); - this->scaleChangedEvent(this->scale()); - this->overrideBackgroundColor_ = QColor("#111111"); - this->themeChangedEvent(); + this->scaleChangedEvent( + this->scale()); // execute twice to fix performance + width of item this->resize(815, 600); } @@ -273,7 +275,8 @@ void SettingsDialog::scaleChangedEvent(float newDpi) this->setStyleSheet(styleSheet); - this->ui_.tabContainerContainer->setFixedWidth(int(150 * newDpi)); + if (this->ui_.tabContainerContainer) + this->ui_.tabContainerContainer->setFixedWidth(int(150 * newDpi)); } void SettingsDialog::themeChangedEvent() From fbbf34878f4ba7027936364831b922b9bb85249c Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 21 Feb 2020 00:46:19 +0100 Subject: [PATCH 030/153] getSettingsPage -> page --- src/widgets/dialogs/SettingsDialog.cpp | 6 +++--- src/widgets/helper/SettingsDialogTab.cpp | 2 +- src/widgets/helper/SettingsDialogTab.hpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index e9769e3fd..414aaa4c3 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -199,7 +199,7 @@ void SettingsDialog::addTab(SettingsPage *page, Qt::Alignment alignment) void SettingsDialog::selectTab(SettingsDialogTab *tab, bool byUser) { - this->ui_.pageStack->setCurrentWidget(tab->getSettingsPage()); + this->ui_.pageStack->setCurrentWidget(tab->page()); if (this->selectedTab_ != nullptr) { @@ -256,7 +256,7 @@ void SettingsDialog::refresh() for (auto *tab : this->tabs_) { - tab->getSettingsPage()->onShow(); + tab->page()->onShow(); } } @@ -304,7 +304,7 @@ void SettingsDialog::onCancelClicked() { for (auto &tab : this->tabs_) { - tab->getSettingsPage()->cancel(); + tab->page()->cancel(); } getSettings()->restoreSnapshot(); diff --git a/src/widgets/helper/SettingsDialogTab.cpp b/src/widgets/helper/SettingsDialogTab.cpp index 8089bf803..b74cf60da 100644 --- a/src/widgets/helper/SettingsDialogTab.cpp +++ b/src/widgets/helper/SettingsDialogTab.cpp @@ -34,7 +34,7 @@ void SettingsDialogTab::setSelected(bool _selected) emit selectedChanged(selected_); } -SettingsPage *SettingsDialogTab::getSettingsPage() +SettingsPage *SettingsDialogTab::page() { return this->page_; } diff --git a/src/widgets/helper/SettingsDialogTab.hpp b/src/widgets/helper/SettingsDialogTab.hpp index dfb765b16..3ee7609b3 100644 --- a/src/widgets/helper/SettingsDialogTab.hpp +++ b/src/widgets/helper/SettingsDialogTab.hpp @@ -20,7 +20,7 @@ public: QString imageFileName); void setSelected(bool selected_); - SettingsPage *getSettingsPage(); + SettingsPage *page(); signals: void selectedChanged(bool); From 39d40d6db6522a5550693a158be50d8f0a06fc67 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 21 Feb 2020 01:17:22 +0100 Subject: [PATCH 031/153] some settings refactorings --- src/widgets/dialogs/SettingsDialog.cpp | 41 +++++++++++++++++------- src/widgets/dialogs/SettingsDialog.hpp | 9 +++--- src/widgets/helper/SettingsDialogTab.cpp | 9 +++++- src/widgets/helper/SettingsDialogTab.hpp | 10 +++++- 4 files changed, 52 insertions(+), 17 deletions(-) diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index 414aaa4c3..3da4429a8 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -4,7 +4,6 @@ #include "singletons/Resources.hpp" #include "util/LayoutCreator.hpp" #include "widgets/helper/Button.hpp" -#include "widgets/helper/SettingsDialogTab.hpp" #include "widgets/settingspages/AboutPage.hpp" #include "widgets/settingspages/AccountsPage.hpp" #include "widgets/settingspages/CommandPage.hpp" @@ -21,7 +20,7 @@ namespace chatterino { -SettingsDialog *SettingsDialog::handle = nullptr; +SettingsDialog *SettingsDialog::instance_ = nullptr; SettingsDialog::SettingsDialog() : BaseWindow(BaseWindow::DisableCustomScaling) @@ -105,10 +104,10 @@ void SettingsDialog::initUi() void SettingsDialog::filterElements(const QString &text) { // filter elements and hide pages - for (auto &&page : this->pages_) + for (auto &&tab : this->tabs_) { // filterElements returns true if anything on the page matches the search query - page->tab()->setVisible(page->filterElements(text)); + tab->setVisible(tab->page()->filterElements(text)); } // find next visible page @@ -146,9 +145,9 @@ void SettingsDialog::filterElements(const QString &text) } } -SettingsDialog *SettingsDialog::getHandle() +SettingsDialog *SettingsDialog::instance() { - return SettingsDialog::handle; + return SettingsDialog::instance_; } void SettingsDialog::addTabs() @@ -173,7 +172,7 @@ void SettingsDialog::addTabs() this->ui_.tabContainer->addSpacing(16); this->addTab(new KeyboardSettingsPage); - this->addTab(this->ui_.moderationPage = new ModerationPage); + this->addTab(new ModerationPage); this->addTab(new NotificationPage); this->addTab(new ExternalToolsPage); @@ -189,7 +188,6 @@ void SettingsDialog::addTab(SettingsPage *page, Qt::Alignment alignment) this->ui_.pageStack->addWidget(page); this->ui_.tabContainer->addWidget(tab, 0, alignment); this->tabs_.push_back(tab); - this->pages_.push_back(page); if (this->tabs_.size() == 1) { @@ -217,6 +215,21 @@ void SettingsDialog::selectTab(SettingsDialogTab *tab, bool byUser) } } +void SettingsDialog::selectTab(SettingsTabId id) +{ + this->selectTab(this->tab(id)); +} + +SettingsDialogTab *SettingsDialog::tab(SettingsTabId id) +{ + for (auto &&tab : this->tabs_) + if (tab->id() == id) + return tab; + + assert(false); + return nullptr; +} + void SettingsDialog::selectPage(SettingsPage *page) { assert(page); @@ -233,12 +246,18 @@ void SettingsDialog::showDialog(SettingsDialogPreference preferredTab) switch (preferredTab) { case SettingsDialogPreference::Accounts: - instance->selectTab(instance->tabs_.at(1)); + instance->selectTab(SettingsTabId::Accounts); break; case SettingsDialogPreference::ModerationActions: - instance->selectPage(instance->ui_.moderationPage); - instance->ui_.moderationPage->selectModerationActions(); + if (auto tab = instance->tab(SettingsTabId::Moderation)) + { + instance->selectTab(tab); + if (auto page = dynamic_cast(tab->page())) + { + page->selectModerationActions(); + } + } break; default:; diff --git a/src/widgets/dialogs/SettingsDialog.hpp b/src/widgets/dialogs/SettingsDialog.hpp index 5a064b667..7495352d4 100644 --- a/src/widgets/dialogs/SettingsDialog.hpp +++ b/src/widgets/dialogs/SettingsDialog.hpp @@ -7,6 +7,7 @@ #include #include #include +#include "widgets/helper/SettingsDialogTab.hpp" class QLineEdit; @@ -32,7 +33,7 @@ class SettingsDialog : public BaseWindow public: SettingsDialog(); - static SettingsDialog *getHandle(); // may be NULL + static SettingsDialog *instance(); // may be NULL static void showDialog(SettingsDialogPreference preferredTab = SettingsDialogPreference::NoPreference); @@ -42,7 +43,7 @@ protected: virtual void showEvent(QShowEvent *) override; private: - static SettingsDialog *handle; + static SettingsDialog *instance_; void refresh(); @@ -51,6 +52,8 @@ private: void addTab(SettingsPage *page, Qt::Alignment alignment = Qt::AlignTop); void selectTab(SettingsDialogTab *tab, bool byUser = true); void selectPage(SettingsPage *page); + void selectTab(SettingsTabId id); + SettingsDialogTab *tab(SettingsTabId id); void filterElements(const QString &query); void onOkClicked(); @@ -62,11 +65,9 @@ private: QStackedLayout *pageStack{}; QPushButton *okButton{}; QPushButton *cancelButton{}; - ModerationPage *moderationPage{}; QLineEdit *search{}; } ui_; std::vector tabs_; - std::vector pages_; SettingsDialogTab *selectedTab_{}; SettingsDialogTab *lastSelectedByUser_{}; diff --git a/src/widgets/helper/SettingsDialogTab.cpp b/src/widgets/helper/SettingsDialogTab.cpp index b74cf60da..a5bc7145f 100644 --- a/src/widgets/helper/SettingsDialogTab.cpp +++ b/src/widgets/helper/SettingsDialogTab.cpp @@ -8,10 +8,12 @@ namespace chatterino { SettingsDialogTab::SettingsDialogTab(SettingsDialog *_dialog, - SettingsPage *_page, QString imageFileName) + SettingsPage *_page, QString imageFileName, + SettingsTabId id) : BaseWidget(_dialog) , dialog_(_dialog) , page_(_page) + , id_(id) { this->ui_.labelText = page_->getName(); this->ui_.icon.addFile(imageFileName); @@ -72,4 +74,9 @@ void SettingsDialogTab::mousePressEvent(QMouseEvent *event) this->setFocus(); } +SettingsTabId SettingsDialogTab::id() const +{ + return id_; +} + } // namespace chatterino diff --git a/src/widgets/helper/SettingsDialogTab.hpp b/src/widgets/helper/SettingsDialogTab.hpp index 3ee7609b3..29b994785 100644 --- a/src/widgets/helper/SettingsDialogTab.hpp +++ b/src/widgets/helper/SettingsDialogTab.hpp @@ -11,16 +11,23 @@ namespace chatterino { class SettingsPage; class SettingsDialog; +enum SettingsTabId { + None, + Accounts, + Moderation, +}; + class SettingsDialogTab : public BaseWidget { Q_OBJECT public: SettingsDialogTab(SettingsDialog *dialog_, SettingsPage *page_, - QString imageFileName); + QString imageFileName, SettingsTabId id = {}); void setSelected(bool selected_); SettingsPage *page(); + SettingsTabId id() const; signals: void selectedChanged(bool); @@ -37,6 +44,7 @@ private: // Parent settings dialog SettingsDialog *dialog_; SettingsPage *page_; + SettingsTabId id_; bool selected_ = false; }; From 78ca0cb84fb36ee038bd5b0fa2c2186b11f138f5 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 21 Feb 2020 01:22:50 +0100 Subject: [PATCH 032/153] moved settings tab icons to SettingsDialog --- src/widgets/dialogs/SettingsDialog.cpp | 25 ++++++++++--------- src/widgets/dialogs/SettingsDialog.hpp | 5 ++-- src/widgets/settingspages/AboutPage.cpp | 2 +- src/widgets/settingspages/AccountsPage.cpp | 2 +- src/widgets/settingspages/CommandPage.cpp | 2 +- .../settingspages/ExternalToolsPage.cpp | 2 +- src/widgets/settingspages/GeneralPage.cpp | 2 +- .../settingspages/HighlightingPage.cpp | 2 +- src/widgets/settingspages/IgnoresPage.cpp | 2 +- .../settingspages/KeyboardSettingsPage.cpp | 2 +- src/widgets/settingspages/ModerationPage.cpp | 2 +- .../settingspages/NotificationPage.cpp | 2 +- src/widgets/settingspages/SettingsPage.cpp | 8 +----- src/widgets/settingspages/SettingsPage.hpp | 6 +---- 14 files changed, 28 insertions(+), 36 deletions(-) diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index 3da4429a8..8deaa830c 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -157,32 +157,33 @@ void SettingsDialog::addTabs() this->ui_.tabContainer->setContentsMargins(0, 20, 0, 20); - this->addTab(new GeneralPage); + this->addTab(new GeneralPage, ":/settings/about.svg"); this->ui_.tabContainer->addSpacing(16); - this->addTab(new AccountsPage); + this->addTab(new AccountsPage, ":/settings/accounts.svg"); this->ui_.tabContainer->addSpacing(16); - this->addTab(new CommandPage); - this->addTab(new HighlightingPage); - this->addTab(new IgnoresPage); + this->addTab(new CommandPage, ":/settings/commands.svg"); + this->addTab(new HighlightingPage, ":/settings/notifications.svg"); + this->addTab(new IgnoresPage, ":/settings/ignore.svg"); this->ui_.tabContainer->addSpacing(16); - this->addTab(new KeyboardSettingsPage); - this->addTab(new ModerationPage); - this->addTab(new NotificationPage); - this->addTab(new ExternalToolsPage); + this->addTab(new KeyboardSettingsPage, ":/settings/keybinds.svg"); + this->addTab(new ModerationPage, ":/settings/moderation.svg"); + this->addTab(new NotificationPage, ":/settings/notification2.svg"); + this->addTab(new ExternalToolsPage, ":/settings/externaltools.svg"); this->ui_.tabContainer->addStretch(1); - this->addTab(new AboutPage, Qt::AlignBottom); + this->addTab(new AboutPage, ":/settings/about.svg", Qt::AlignBottom); } -void SettingsDialog::addTab(SettingsPage *page, Qt::Alignment alignment) +void SettingsDialog::addTab(SettingsPage *page, const QString &iconPath, + Qt::Alignment alignment) { - auto tab = new SettingsDialogTab(this, page, page->getIconResource()); + auto tab = new SettingsDialogTab(this, page, iconPath); page->setTab(tab); this->ui_.pageStack->addWidget(page); diff --git a/src/widgets/dialogs/SettingsDialog.hpp b/src/widgets/dialogs/SettingsDialog.hpp index 7495352d4..6e282d130 100644 --- a/src/widgets/dialogs/SettingsDialog.hpp +++ b/src/widgets/dialogs/SettingsDialog.hpp @@ -49,8 +49,9 @@ private: void initUi(); void addTabs(); - void addTab(SettingsPage *page, Qt::Alignment alignment = Qt::AlignTop); - void selectTab(SettingsDialogTab *tab, bool byUser = true); + void addTab(SettingsPage *page, const QString &iconPath, + Qt::Alignment alignment = Qt::AlignTop); + void selectTab(SettingsDialogTab *tab, const bool byUser = true); void selectPage(SettingsPage *page); void selectTab(SettingsTabId id); SettingsDialogTab *tab(SettingsTabId id); diff --git a/src/widgets/settingspages/AboutPage.cpp b/src/widgets/settingspages/AboutPage.cpp index 5a7dcf534..1371bbee6 100644 --- a/src/widgets/settingspages/AboutPage.cpp +++ b/src/widgets/settingspages/AboutPage.cpp @@ -18,7 +18,7 @@ namespace chatterino { AboutPage::AboutPage() - : SettingsPage("About", ":/settings/about.svg") + : SettingsPage("About") { LayoutCreator layoutCreator(this); diff --git a/src/widgets/settingspages/AccountsPage.cpp b/src/widgets/settingspages/AccountsPage.cpp index 944085539..1969e3fc7 100644 --- a/src/widgets/settingspages/AccountsPage.cpp +++ b/src/widgets/settingspages/AccountsPage.cpp @@ -17,7 +17,7 @@ namespace chatterino { AccountsPage::AccountsPage() - : SettingsPage("Accounts", ":/settings/accounts.svg") + : SettingsPage("Accounts") { auto *app = getApp(); diff --git a/src/widgets/settingspages/CommandPage.cpp b/src/widgets/settingspages/CommandPage.cpp index c3470c4b1..d565a10f3 100644 --- a/src/widgets/settingspages/CommandPage.cpp +++ b/src/widgets/settingspages/CommandPage.cpp @@ -33,7 +33,7 @@ namespace { } // namespace CommandPage::CommandPage() - : SettingsPage("Commands", ":/settings/commands.svg") + : SettingsPage("Commands") { auto app = getApp(); diff --git a/src/widgets/settingspages/ExternalToolsPage.cpp b/src/widgets/settingspages/ExternalToolsPage.cpp index 57a8a496e..f7aad15b5 100644 --- a/src/widgets/settingspages/ExternalToolsPage.cpp +++ b/src/widgets/settingspages/ExternalToolsPage.cpp @@ -12,7 +12,7 @@ namespace chatterino { ExternalToolsPage::ExternalToolsPage() - : SettingsPage("External tools", ":/settings/externaltools.svg") + : SettingsPage("External tools") { LayoutCreator layoutCreator(this); auto layout = layoutCreator.setLayoutType(); diff --git a/src/widgets/settingspages/GeneralPage.cpp b/src/widgets/settingspages/GeneralPage.cpp index 68d5206bd..38b3b4e35 100644 --- a/src/widgets/settingspages/GeneralPage.cpp +++ b/src/widgets/settingspages/GeneralPage.cpp @@ -237,7 +237,7 @@ bool SettingsLayout::filterElements(const QString &query) } GeneralPage::GeneralPage() - : SettingsPage("General", ":/settings/about.svg") + : SettingsPage("General") { auto y = new QVBoxLayout; auto scroll = new QScrollArea; diff --git a/src/widgets/settingspages/HighlightingPage.cpp b/src/widgets/settingspages/HighlightingPage.cpp index 7113ee337..05a71bd75 100644 --- a/src/widgets/settingspages/HighlightingPage.cpp +++ b/src/widgets/settingspages/HighlightingPage.cpp @@ -29,7 +29,7 @@ namespace chatterino { HighlightingPage::HighlightingPage() - : SettingsPage("Highlights", ":/settings/notifications.svg") + : SettingsPage("Highlights") { auto app = getApp(); LayoutCreator layoutCreator(this); diff --git a/src/widgets/settingspages/IgnoresPage.cpp b/src/widgets/settingspages/IgnoresPage.cpp index f1bed2842..af36ecad2 100644 --- a/src/widgets/settingspages/IgnoresPage.cpp +++ b/src/widgets/settingspages/IgnoresPage.cpp @@ -29,7 +29,7 @@ static void addUsersTab(IgnoresPage &page, LayoutCreator box, QStringListModel &model); IgnoresPage::IgnoresPage() - : SettingsPage("Ignores", ":/settings/ignore.svg") + : SettingsPage("Ignores") { LayoutCreator layoutCreator(this); auto layout = layoutCreator.setLayoutType(); diff --git a/src/widgets/settingspages/KeyboardSettingsPage.cpp b/src/widgets/settingspages/KeyboardSettingsPage.cpp index ee2502f27..0dd557a75 100644 --- a/src/widgets/settingspages/KeyboardSettingsPage.cpp +++ b/src/widgets/settingspages/KeyboardSettingsPage.cpp @@ -8,7 +8,7 @@ namespace chatterino { KeyboardSettingsPage::KeyboardSettingsPage() - : SettingsPage("Keybindings", ":/settings/keybinds.svg") + : SettingsPage("Keybindings") { auto layout = LayoutCreator(this).setLayoutType(); diff --git a/src/widgets/settingspages/ModerationPage.cpp b/src/widgets/settingspages/ModerationPage.cpp index 3d2050baa..88d8ac9eb 100644 --- a/src/widgets/settingspages/ModerationPage.cpp +++ b/src/widgets/settingspages/ModerationPage.cpp @@ -73,7 +73,7 @@ QString fetchLogDirectorySize() } ModerationPage::ModerationPage() - : SettingsPage("Moderation", ":/settings/moderation.svg") + : SettingsPage("Moderation") { auto app = getApp(); LayoutCreator layoutCreator(this); diff --git a/src/widgets/settingspages/NotificationPage.cpp b/src/widgets/settingspages/NotificationPage.cpp index 06acf44d9..968c34f54 100644 --- a/src/widgets/settingspages/NotificationPage.cpp +++ b/src/widgets/settingspages/NotificationPage.cpp @@ -21,7 +21,7 @@ namespace chatterino { NotificationPage::NotificationPage() - : SettingsPage("Notifications", ":/settings/notification2.svg") + : SettingsPage("Notifications") { LayoutCreator layoutCreator(this); auto layout = layoutCreator.emplace().withoutMargin(); diff --git a/src/widgets/settingspages/SettingsPage.cpp b/src/widgets/settingspages/SettingsPage.cpp index cbcc90191..40f6e049b 100644 --- a/src/widgets/settingspages/SettingsPage.cpp +++ b/src/widgets/settingspages/SettingsPage.cpp @@ -64,9 +64,8 @@ bool filterItemsRec(QObject *object, const QString &query) return any; } -SettingsPage::SettingsPage(const QString &name, const QString &iconResource) +SettingsPage::SettingsPage(const QString &name) : name_(name) - , iconResource_(iconResource) { } @@ -81,11 +80,6 @@ const QString &SettingsPage::getName() return this->name_; } -const QString &SettingsPage::getIconResource() -{ - return this->iconResource_; -} - SettingsDialogTab *SettingsPage::tab() const { return this->tab_; diff --git a/src/widgets/settingspages/SettingsPage.hpp b/src/widgets/settingspages/SettingsPage.hpp index 0e92311f8..eeb8b9609 100644 --- a/src/widgets/settingspages/SettingsPage.hpp +++ b/src/widgets/settingspages/SettingsPage.hpp @@ -47,10 +47,9 @@ class SettingsPage : public QFrame Q_OBJECT public: - SettingsPage(const QString &name, const QString &iconResource); + SettingsPage(const QString &name); const QString &getName(); - const QString &getIconResource(); virtual bool filterElements(const QString &query); @@ -73,10 +72,7 @@ public: protected: QString name_; - QString iconResource_; - SettingsDialogTab *tab_; - pajlada::Signals::NoArgSignal onCancel_; std::vector managedConnections_; }; From 70e5bd1bfd6ba12c285969550e6c24a3e13537b9 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 21 Feb 2020 01:59:58 +0100 Subject: [PATCH 033/153] lazily initialize settings pages --- src/widgets/dialogs/SettingsDialog.cpp | 61 +++++++++---------- src/widgets/dialogs/SettingsDialog.hpp | 5 +- src/widgets/helper/SettingsDialogTab.cpp | 18 +++++- src/widgets/helper/SettingsDialogTab.hpp | 15 +++-- src/widgets/settingspages/AboutPage.cpp | 1 - src/widgets/settingspages/AccountsPage.cpp | 1 - src/widgets/settingspages/CommandPage.cpp | 1 - .../settingspages/ExternalToolsPage.cpp | 1 - src/widgets/settingspages/GeneralPage.cpp | 5 +- .../settingspages/HighlightingPage.cpp | 1 - src/widgets/settingspages/IgnoresPage.cpp | 1 - .../settingspages/KeyboardSettingsPage.cpp | 1 - src/widgets/settingspages/ModerationPage.cpp | 1 - .../settingspages/NotificationPage.cpp | 1 - src/widgets/settingspages/SettingsPage.cpp | 11 +--- src/widgets/settingspages/SettingsPage.hpp | 5 +- 16 files changed, 63 insertions(+), 66 deletions(-) diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index 8deaa830c..a3dbf3455 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -107,7 +107,8 @@ void SettingsDialog::filterElements(const QString &text) for (auto &&tab : this->tabs_) { // filterElements returns true if anything on the page matches the search query - tab->setVisible(tab->page()->filterElements(text)); + tab->setVisible(tab->name().contains(text, Qt::CaseInsensitive) || + tab->page()->filterElements(text)); } // find next visible page @@ -157,36 +158,30 @@ void SettingsDialog::addTabs() this->ui_.tabContainer->setContentsMargins(0, 20, 0, 20); - this->addTab(new GeneralPage, ":/settings/about.svg"); - + // clang-format off + this->addTab([]{return new GeneralPage;}, "General", ":/settings/about.svg"); this->ui_.tabContainer->addSpacing(16); - - this->addTab(new AccountsPage, ":/settings/accounts.svg"); - + this->addTab([]{return new AccountsPage;}, "Accounts", ":/settings/accounts.svg"); this->ui_.tabContainer->addSpacing(16); - - this->addTab(new CommandPage, ":/settings/commands.svg"); - this->addTab(new HighlightingPage, ":/settings/notifications.svg"); - this->addTab(new IgnoresPage, ":/settings/ignore.svg"); - + this->addTab([]{return new CommandPage;}, "Commands", ":/settings/commands.svg"); + this->addTab([]{return new HighlightingPage;}, "Highlights", ":/settings/notifications.svg"); + this->addTab([]{return new IgnoresPage;}, "Ignores", ":/settings/ignore.svg"); this->ui_.tabContainer->addSpacing(16); - - this->addTab(new KeyboardSettingsPage, ":/settings/keybinds.svg"); - this->addTab(new ModerationPage, ":/settings/moderation.svg"); - this->addTab(new NotificationPage, ":/settings/notification2.svg"); - this->addTab(new ExternalToolsPage, ":/settings/externaltools.svg"); - + this->addTab([]{return new KeyboardSettingsPage;}, "Keybindings", ":/settings/keybinds.svg"); + this->addTab([]{return new ModerationPage;}, "Moderation", ":/settings/moderation.svg"); + this->addTab([]{return new NotificationPage;}, "Notifications", ":/settings/notification2.svg"); + this->addTab([]{return new ExternalToolsPage;}, "External tools", ":/settings/externaltools.svg"); this->ui_.tabContainer->addStretch(1); - this->addTab(new AboutPage, ":/settings/about.svg", Qt::AlignBottom); + this->addTab([]{return new AboutPage;}, "About", ":/settings/about.svg", Qt::AlignBottom); + // clang-format on } -void SettingsDialog::addTab(SettingsPage *page, const QString &iconPath, +void SettingsDialog::addTab(std::function page, + const QString &name, const QString &iconPath, Qt::Alignment alignment) { - auto tab = new SettingsDialogTab(this, page, iconPath); - page->setTab(tab); + auto tab = new SettingsDialogTab(this, std::move(page), name, iconPath); - this->ui_.pageStack->addWidget(page); this->ui_.tabContainer->addWidget(tab, 0, alignment); this->tabs_.push_back(tab); @@ -198,6 +193,15 @@ void SettingsDialog::addTab(SettingsPage *page, const QString &iconPath, void SettingsDialog::selectTab(SettingsDialogTab *tab, bool byUser) { + // add page if it's not been added yet + [&] { + for (int i = 0; i < this->ui_.pageStack->count(); i++) + if (this->ui_.pageStack->itemAt(i)->widget() == tab->page()) + return; + + this->ui_.pageStack->addWidget(tab->page()); + }(); + this->ui_.pageStack->setCurrentWidget(tab->page()); if (this->selectedTab_ != nullptr) @@ -231,18 +235,13 @@ SettingsDialogTab *SettingsDialog::tab(SettingsTabId id) return nullptr; } -void SettingsDialog::selectPage(SettingsPage *page) -{ - assert(page); - assert(page->tab()); - - this->selectTab(page->tab()); -} - void SettingsDialog::showDialog(SettingsDialogPreference preferredTab) { static SettingsDialog *instance = new SettingsDialog(); - instance->refresh(); + static bool hasShownBefore = false; + if (hasShownBefore) + instance->refresh(); + hasShownBefore = true; switch (preferredTab) { diff --git a/src/widgets/dialogs/SettingsDialog.hpp b/src/widgets/dialogs/SettingsDialog.hpp index 6e282d130..84cdb98de 100644 --- a/src/widgets/dialogs/SettingsDialog.hpp +++ b/src/widgets/dialogs/SettingsDialog.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "widgets/helper/SettingsDialogTab.hpp" @@ -49,10 +50,10 @@ private: void initUi(); void addTabs(); - void addTab(SettingsPage *page, const QString &iconPath, + void addTab(std::function page, const QString &name, + const QString &iconPath, Qt::Alignment alignment = Qt::AlignTop); void selectTab(SettingsDialogTab *tab, const bool byUser = true); - void selectPage(SettingsPage *page); void selectTab(SettingsTabId id); SettingsDialogTab *tab(SettingsTabId id); void filterElements(const QString &query); diff --git a/src/widgets/helper/SettingsDialogTab.cpp b/src/widgets/helper/SettingsDialogTab.cpp index a5bc7145f..5ad366ab1 100644 --- a/src/widgets/helper/SettingsDialogTab.cpp +++ b/src/widgets/helper/SettingsDialogTab.cpp @@ -8,14 +8,16 @@ namespace chatterino { SettingsDialogTab::SettingsDialogTab(SettingsDialog *_dialog, - SettingsPage *_page, QString imageFileName, + std::function _lazyPage, + const QString &name, QString imageFileName, SettingsTabId id) : BaseWidget(_dialog) , dialog_(_dialog) - , page_(_page) + , lazyPage_(std::move(_lazyPage)) , id_(id) + , name_(name) { - this->ui_.labelText = page_->getName(); + this->ui_.labelText = name; this->ui_.icon.addFile(imageFileName); this->setCursor(QCursor(Qt::PointingHandCursor)); @@ -38,6 +40,11 @@ void SettingsDialogTab::setSelected(bool _selected) SettingsPage *SettingsDialogTab::page() { + if (this->page_) + return this->page_; + + this->page_ = this->lazyPage_(); + this->page_->setTab(this); return this->page_; } @@ -74,6 +81,11 @@ void SettingsDialogTab::mousePressEvent(QMouseEvent *event) this->setFocus(); } +const QString &SettingsDialogTab::name() const +{ + return name_; +} + SettingsTabId SettingsDialogTab::id() const { return id_; diff --git a/src/widgets/helper/SettingsDialogTab.hpp b/src/widgets/helper/SettingsDialogTab.hpp index 29b994785..49dc9ff23 100644 --- a/src/widgets/helper/SettingsDialogTab.hpp +++ b/src/widgets/helper/SettingsDialogTab.hpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace chatterino { @@ -22,13 +23,17 @@ class SettingsDialogTab : public BaseWidget Q_OBJECT public: - SettingsDialogTab(SettingsDialog *dialog_, SettingsPage *page_, - QString imageFileName, SettingsTabId id = {}); + SettingsDialogTab(SettingsDialog *dialog_, + std::function page_, + const QString &name, QString imageFileName, + SettingsTabId id = {}); void setSelected(bool selected_); SettingsPage *page(); SettingsTabId id() const; + const QString &name() const; + signals: void selectedChanged(bool); @@ -42,9 +47,11 @@ private: } ui_; // Parent settings dialog - SettingsDialog *dialog_; - SettingsPage *page_; + SettingsDialog *dialog_{}; + SettingsPage *page_{}; + std::function lazyPage_; SettingsTabId id_; + QString name_; bool selected_ = false; }; diff --git a/src/widgets/settingspages/AboutPage.cpp b/src/widgets/settingspages/AboutPage.cpp index 1371bbee6..4a51feda0 100644 --- a/src/widgets/settingspages/AboutPage.cpp +++ b/src/widgets/settingspages/AboutPage.cpp @@ -18,7 +18,6 @@ namespace chatterino { AboutPage::AboutPage() - : SettingsPage("About") { LayoutCreator layoutCreator(this); diff --git a/src/widgets/settingspages/AccountsPage.cpp b/src/widgets/settingspages/AccountsPage.cpp index 1969e3fc7..555738771 100644 --- a/src/widgets/settingspages/AccountsPage.cpp +++ b/src/widgets/settingspages/AccountsPage.cpp @@ -17,7 +17,6 @@ namespace chatterino { AccountsPage::AccountsPage() - : SettingsPage("Accounts") { auto *app = getApp(); diff --git a/src/widgets/settingspages/CommandPage.cpp b/src/widgets/settingspages/CommandPage.cpp index d565a10f3..99b83ec90 100644 --- a/src/widgets/settingspages/CommandPage.cpp +++ b/src/widgets/settingspages/CommandPage.cpp @@ -33,7 +33,6 @@ namespace { } // namespace CommandPage::CommandPage() - : SettingsPage("Commands") { auto app = getApp(); diff --git a/src/widgets/settingspages/ExternalToolsPage.cpp b/src/widgets/settingspages/ExternalToolsPage.cpp index f7aad15b5..648af25ba 100644 --- a/src/widgets/settingspages/ExternalToolsPage.cpp +++ b/src/widgets/settingspages/ExternalToolsPage.cpp @@ -12,7 +12,6 @@ namespace chatterino { ExternalToolsPage::ExternalToolsPage() - : SettingsPage("External tools") { LayoutCreator layoutCreator(this); auto layout = layoutCreator.setLayoutType(); diff --git a/src/widgets/settingspages/GeneralPage.cpp b/src/widgets/settingspages/GeneralPage.cpp index 38b3b4e35..bbc972a73 100644 --- a/src/widgets/settingspages/GeneralPage.cpp +++ b/src/widgets/settingspages/GeneralPage.cpp @@ -237,7 +237,6 @@ bool SettingsLayout::filterElements(const QString &query) } GeneralPage::GeneralPage() - : SettingsPage("General") { auto y = new QVBoxLayout; auto scroll = new QScrollArea; @@ -263,9 +262,7 @@ GeneralPage::GeneralPage() bool GeneralPage::filterElements(const QString &query) { if (this->settingsLayout_) - return this->settingsLayout_->filterElements(query) || - this->name_.contains(query, Qt::CaseInsensitive) || - query.isEmpty(); + return this->settingsLayout_->filterElements(query) || query.isEmpty(); else return false; } diff --git a/src/widgets/settingspages/HighlightingPage.cpp b/src/widgets/settingspages/HighlightingPage.cpp index 05a71bd75..2612454be 100644 --- a/src/widgets/settingspages/HighlightingPage.cpp +++ b/src/widgets/settingspages/HighlightingPage.cpp @@ -29,7 +29,6 @@ namespace chatterino { HighlightingPage::HighlightingPage() - : SettingsPage("Highlights") { auto app = getApp(); LayoutCreator layoutCreator(this); diff --git a/src/widgets/settingspages/IgnoresPage.cpp b/src/widgets/settingspages/IgnoresPage.cpp index af36ecad2..9cc657880 100644 --- a/src/widgets/settingspages/IgnoresPage.cpp +++ b/src/widgets/settingspages/IgnoresPage.cpp @@ -29,7 +29,6 @@ static void addUsersTab(IgnoresPage &page, LayoutCreator box, QStringListModel &model); IgnoresPage::IgnoresPage() - : SettingsPage("Ignores") { LayoutCreator layoutCreator(this); auto layout = layoutCreator.setLayoutType(); diff --git a/src/widgets/settingspages/KeyboardSettingsPage.cpp b/src/widgets/settingspages/KeyboardSettingsPage.cpp index 0dd557a75..d09bb97d1 100644 --- a/src/widgets/settingspages/KeyboardSettingsPage.cpp +++ b/src/widgets/settingspages/KeyboardSettingsPage.cpp @@ -8,7 +8,6 @@ namespace chatterino { KeyboardSettingsPage::KeyboardSettingsPage() - : SettingsPage("Keybindings") { auto layout = LayoutCreator(this).setLayoutType(); diff --git a/src/widgets/settingspages/ModerationPage.cpp b/src/widgets/settingspages/ModerationPage.cpp index 88d8ac9eb..479d0a7cb 100644 --- a/src/widgets/settingspages/ModerationPage.cpp +++ b/src/widgets/settingspages/ModerationPage.cpp @@ -73,7 +73,6 @@ QString fetchLogDirectorySize() } ModerationPage::ModerationPage() - : SettingsPage("Moderation") { auto app = getApp(); LayoutCreator layoutCreator(this); diff --git a/src/widgets/settingspages/NotificationPage.cpp b/src/widgets/settingspages/NotificationPage.cpp index 968c34f54..27c1da4b5 100644 --- a/src/widgets/settingspages/NotificationPage.cpp +++ b/src/widgets/settingspages/NotificationPage.cpp @@ -21,7 +21,6 @@ namespace chatterino { NotificationPage::NotificationPage() - : SettingsPage("Notifications") { LayoutCreator layoutCreator(this); auto layout = layoutCreator.emplace().withoutMargin(); diff --git a/src/widgets/settingspages/SettingsPage.cpp b/src/widgets/settingspages/SettingsPage.cpp index 40f6e049b..78b9d9387 100644 --- a/src/widgets/settingspages/SettingsPage.cpp +++ b/src/widgets/settingspages/SettingsPage.cpp @@ -64,20 +64,13 @@ bool filterItemsRec(QObject *object, const QString &query) return any; } -SettingsPage::SettingsPage(const QString &name) - : name_(name) +SettingsPage::SettingsPage() { } bool SettingsPage::filterElements(const QString &query) { - return filterItemsRec(this, query) || query.isEmpty() || - this->name_.contains(query, Qt::CaseInsensitive); -} - -const QString &SettingsPage::getName() -{ - return this->name_; + return filterItemsRec(this, query) || query.isEmpty(); } SettingsDialogTab *SettingsPage::tab() const diff --git a/src/widgets/settingspages/SettingsPage.hpp b/src/widgets/settingspages/SettingsPage.hpp index eeb8b9609..d3a9ee111 100644 --- a/src/widgets/settingspages/SettingsPage.hpp +++ b/src/widgets/settingspages/SettingsPage.hpp @@ -47,9 +47,7 @@ class SettingsPage : public QFrame Q_OBJECT public: - SettingsPage(const QString &name); - - const QString &getName(); + SettingsPage(); virtual bool filterElements(const QString &query); @@ -71,7 +69,6 @@ public: } protected: - QString name_; SettingsDialogTab *tab_; pajlada::Signals::NoArgSignal onCancel_; std::vector managedConnections_; From 927ee09221203d78800914ca5dd4f672ed71361c Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 21 Feb 2020 02:07:34 +0100 Subject: [PATCH 034/153] reordered resize call --- src/widgets/dialogs/SettingsDialog.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index a3dbf3455..a15b214d1 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -26,18 +26,15 @@ SettingsDialog::SettingsDialog() : BaseWindow(BaseWindow::DisableCustomScaling) { this->setWindowTitle("Chatterino Settings"); - + this->resize(815, 600); this->themeChangedEvent(); this->scaleChangedEvent(this->scale()); this->initUi(); this->addTabs(); - this->overrideBackgroundColor_ = QColor("#111111"); this->scaleChangedEvent( this->scale()); // execute twice to fix performance + width of item - - this->resize(815, 600); } void SettingsDialog::initUi() From a2d21510acf7cd15dd8be8a8cadfc8c08411f4a8 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 21 Feb 2020 02:13:31 +0100 Subject: [PATCH 035/153] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 201b0be80..1e69784f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,3 +3,4 @@ ## Unversioned - 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) +- Settings open faster From 5a2633ef5cf6c1df1e72bd01e6d0a1039ba2932d Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 21 Feb 2020 02:16:17 +0100 Subject: [PATCH 036/153] fixed bug --- src/widgets/dialogs/SettingsDialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index a15b214d1..e33c260ed 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -104,8 +104,8 @@ void SettingsDialog::filterElements(const QString &text) for (auto &&tab : this->tabs_) { // filterElements returns true if anything on the page matches the search query - tab->setVisible(tab->name().contains(text, Qt::CaseInsensitive) || - tab->page()->filterElements(text)); + tab->setVisible(tab->page()->filterElements(text) || + tab->name().contains(text, Qt::CaseInsensitive)); } // find next visible page From 03cf6e81ffb7fc871968c4a72c0f8067a607bd41 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 21 Feb 2020 03:01:48 +0100 Subject: [PATCH 037/153] fixed spaces and margins in general settings --- resources/qss/settings.qss | 1 - src/widgets/dialogs/SettingsDialog.cpp | 19 +++++++++---------- src/widgets/settingspages/GeneralPage.cpp | 12 ++++++++---- src/widgets/settingspages/GeneralPage.hpp | 6 ++++++ 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/resources/qss/settings.qss b/resources/qss/settings.qss index 590dff5f5..a3172fd65 100644 --- a/resources/qss/settings.qss +++ b/resources/qss/settings.qss @@ -39,7 +39,6 @@ chatterino--TitleLabel { font-family: "Segoe UI light"; font-size: 24px; color: #4FC3F7; - margin-top: 16px; } chatterino--DescriptionLabel { diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index e33c260ed..5fc797805 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -33,8 +33,7 @@ SettingsDialog::SettingsDialog() this->initUi(); this->addTabs(); this->overrideBackgroundColor_ = QColor("#111111"); - this->scaleChangedEvent( - this->scale()); // execute twice to fix performance + width of item + this->scaleChangedEvent(this->scale()); // execute twice to width of item } void SettingsDialog::initUi() @@ -67,13 +66,9 @@ void SettingsDialog::initUi() .assign(&this->ui_.tabContainer); // right side (pages) - auto right = - centerBox.emplace().withoutMargin().withoutSpacing(); - { - right.emplace() - .assign(&this->ui_.pageStack) - .withoutMargin(); - } + centerBox.emplace() + .assign(&this->ui_.pageStack) + .withoutMargin(); this->ui_.pageStack->setMargin(0); @@ -155,6 +150,8 @@ void SettingsDialog::addTabs() this->ui_.tabContainer->setContentsMargins(0, 20, 0, 20); + // Constructors are wrapped in std::function to remove some strain from first time loading. + // clang-format off this->addTab([]{return new GeneralPage;}, "General", ":/settings/about.svg"); this->ui_.tabContainer->addSpacing(16); @@ -268,8 +265,10 @@ void SettingsDialog::showDialog(SettingsDialogPreference preferredTab) void SettingsDialog::refresh() { + // Resets the cancel button. getSettings()->saveSnapshot(); + // Updates tabs. for (auto *tab : this->tabs_) { tab->page()->onShow(); @@ -300,7 +299,7 @@ void SettingsDialog::themeChangedEvent() BaseWindow::themeChangedEvent(); QPalette palette; - palette.setColor(QPalette::Background, QColor("#111")); + palette.setColor(QPalette::Window, QColor("#111")); this->setPalette(palette); } diff --git a/src/widgets/settingspages/GeneralPage.cpp b/src/widgets/settingspages/GeneralPage.cpp index bbc972a73..086b7a6ad 100644 --- a/src/widgets/settingspages/GeneralPage.cpp +++ b/src/widgets/settingspages/GeneralPage.cpp @@ -74,14 +74,16 @@ namespace { TitleLabel *SettingsLayout::addTitle(const QString &title) { - auto label = new TitleLabel(title + ":"); + // space + if (!this->groups_.empty()) + this->addWidget(this->groups_.back().space = new Space); - if (this->count() == 0) - label->setStyleSheet("margin-top: 0"); + // title + auto label = new TitleLabel(title + ":"); this->addWidget(label); // groups - this->groups_.push_back(Group{title, label, {}}); + this->groups_.push_back(Group{title, label, nullptr, {}}); return label; } @@ -228,6 +230,8 @@ bool SettingsLayout::filterElements(const QString &query) } } + if (group.space) + group.space->setVisible(groupAny); group.title->setVisible(groupAny); any |= groupAny; } diff --git a/src/widgets/settingspages/GeneralPage.hpp b/src/widgets/settingspages/GeneralPage.hpp index 76e3dc46a..d0dc0e4ed 100644 --- a/src/widgets/settingspages/GeneralPage.hpp +++ b/src/widgets/settingspages/GeneralPage.hpp @@ -17,6 +17,11 @@ class QComboBox; namespace chatterino { +class Space : public QLabel +{ + Q_OBJECT +}; + class TitleLabel : public QLabel { Q_OBJECT @@ -162,6 +167,7 @@ private: struct Group { QString name; QWidget *title{}; + Space *space{}; std::vector widgets; }; From 2b5c6ffe338eb4229d612206cce4d185a0828bab Mon Sep 17 00:00:00 2001 From: pajlada Date: Sat, 22 Feb 2020 06:05:36 -0500 Subject: [PATCH 038/153] Remove chatroom code (#1561) People who had chatrooms still open should notice no difference at all, since it will just try to join a channel with the chatroom name. Documentation for the closing/deprecation of twitch chat rooms: https://discuss.dev.twitch.tv/t/rooms-are-closing-how-it-affects-the-api-and-irc/22996 https://dev.twitch.tv/docs/change-log (see 2020-01-10 entry) --- CHANGELOG.md | 1 + chatterino.pro | 2 - src/providers/twitch/ChatroomChannel.cpp | 67 ------------------------ src/providers/twitch/ChatroomChannel.hpp | 28 ---------- src/providers/twitch/TwitchIrcServer.cpp | 30 +---------- 5 files changed, 3 insertions(+), 125 deletions(-) delete mode 100644 src/providers/twitch/ChatroomChannel.cpp delete mode 100644 src/providers/twitch/ChatroomChannel.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e69784f5..a76cf906f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,3 +4,4 @@ - 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) - Settings open faster +- Dev: Fully remove Twitch Chatroom support diff --git a/chatterino.pro b/chatterino.pro index 6cf3d8128..029508395 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -178,7 +178,6 @@ SOURCES += \ src/providers/irc/IrcConnection2.cpp \ src/providers/irc/IrcServer.cpp \ src/providers/LinkResolver.cpp \ - src/providers/twitch/ChatroomChannel.cpp \ src/providers/twitch/IrcMessageHandler.cpp \ src/providers/twitch/PartialTwitchUser.cpp \ src/providers/twitch/PubsubActions.cpp \ @@ -382,7 +381,6 @@ HEADERS += \ src/providers/irc/IrcConnection2.hpp \ src/providers/irc/IrcServer.hpp \ src/providers/LinkResolver.hpp \ - src/providers/twitch/ChatroomChannel.hpp \ src/providers/twitch/EmoteValue.hpp \ src/providers/twitch/IrcMessageHandler.hpp \ src/providers/twitch/PartialTwitchUser.hpp \ diff --git a/src/providers/twitch/ChatroomChannel.cpp b/src/providers/twitch/ChatroomChannel.cpp deleted file mode 100644 index 5f393e427..000000000 --- a/src/providers/twitch/ChatroomChannel.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "ChatroomChannel.hpp" - -#include -#include "TwitchApi.hpp" -#include "common/Common.hpp" -#include "messages/Emote.hpp" -#include "providers/bttv/BttvEmotes.hpp" -#include "providers/bttv/LoadBttvChannelEmote.hpp" -#include "singletons/Emotes.hpp" - -namespace chatterino { - -ChatroomChannel::ChatroomChannel(const QString &channelName, - TwitchBadges &globalTwitchBadges, - BttvEmotes &globalBttv, FfzEmotes &globalFfz) - : TwitchChannel(channelName, globalTwitchBadges, globalBttv, globalFfz) -{ - auto listRef = channelName.splitRef(":"); - if (listRef.size() > 2) - { - this->chatroomOwnerId = listRef[1].toString(); - } -} - -void ChatroomChannel::refreshBTTVChannelEmotes() -{ - if (this->chatroomOwnerId.isEmpty()) - { - return; - } - TwitchApi::findUserName( - this->chatroomOwnerId, - [this, weak = weakOf(this)](QString username) { - BttvEmotes::loadChannel(username, [this, weak](auto &&emoteMap) { - if (auto shared = weak.lock()) - this->bttvEmotes_.set( - std::make_shared(std::move(emoteMap))); - }); - if (auto shared = weak.lock()) - { - this->chatroomOwnerName = username; - } - }); -} -void ChatroomChannel::refreshFFZChannelEmotes() -{ - if (this->chatroomOwnerId.isEmpty()) - { - return; - } - FfzEmotes::loadChannel( - this->chatroomOwnerId, - [this](auto &&emoteMap) { - this->ffzEmotes_.set( - std::make_shared(std::move(emoteMap))); - }, - [this](auto &&modBadge) { - this->ffzCustomModBadge_.set(std::move(modBadge)); - }); -} - -const QString &ChatroomChannel::getDisplayName() const -{ - return this->chatroomOwnerName; -} - -} // namespace chatterino diff --git a/src/providers/twitch/ChatroomChannel.hpp b/src/providers/twitch/ChatroomChannel.hpp deleted file mode 100644 index 2ed249141..000000000 --- a/src/providers/twitch/ChatroomChannel.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "TwitchChannel.hpp" - -#include -#include - -namespace chatterino { - -class ChatroomChannel : public TwitchChannel -{ -protected: - explicit ChatroomChannel(const QString &channelName, - TwitchBadges &globalTwitchBadges, - BttvEmotes &globalBttv, FfzEmotes &globalFfz); - virtual void refreshBTTVChannelEmotes() override; - virtual void refreshFFZChannelEmotes() override; - virtual const QString &getDisplayName() const override; - - QString chatroomOwnerId; - QString chatroomOwnerName; - - friend class TwitchIrcServer; - friend class TwitchMessageBuilder; - friend class IrcMessageHandler; -}; - -} // namespace chatterino diff --git a/src/providers/twitch/TwitchIrcServer.cpp b/src/providers/twitch/TwitchIrcServer.cpp index 45f50cecd..d13850b46 100644 --- a/src/providers/twitch/TwitchIrcServer.cpp +++ b/src/providers/twitch/TwitchIrcServer.cpp @@ -10,7 +10,6 @@ #include "controllers/highlights/HighlightController.hpp" #include "messages/Message.hpp" #include "messages/MessageBuilder.hpp" -#include "providers/twitch/ChatroomChannel.hpp" #include "providers/twitch/IrcMessageHandler.hpp" #include "providers/twitch/PubsubClient.hpp" #include "providers/twitch/TwitchAccount.hpp" @@ -24,21 +23,6 @@ using namespace std::chrono_literals; namespace chatterino { -namespace { - bool isChatroom(const QString &channel) - { - if (channel.left(10) == "chatrooms:") - { - auto reflist = channel.splitRef(':'); - if (reflist.size() == 3) - { - return true; - } - } - return false; - } -} // namespace - TwitchIrcServer::TwitchIrcServer() : whispersChannel(new Channel("/whispers", Channel::Type::TwitchWhispers)) , mentionsChannel(new Channel("/mentions", Channel::Type::TwitchMentions)) @@ -100,18 +84,8 @@ void TwitchIrcServer::initializeConnection(IrcConnection *connection, std::shared_ptr TwitchIrcServer::createChannel( const QString &channelName) { - std::shared_ptr channel; - if (isChatroom(channelName)) - { - channel = std::static_pointer_cast( - std::shared_ptr(new ChatroomChannel( - channelName, this->twitchBadges, this->bttv, this->ffz))); - } - else - { - channel = std::shared_ptr(new TwitchChannel( - channelName, this->twitchBadges, this->bttv, this->ffz)); - } + auto channel = std::shared_ptr(new TwitchChannel( + channelName, this->twitchBadges, this->bttv, this->ffz)); channel->initialize(); channel->sendMessageSignal.connect( From e1838154ff29a1dd8527c5cc93ab7d30e5e588fd Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 17:10:49 +0100 Subject: [PATCH 039/153] refactored SignalVector --- src/common/SignalVector.hpp | 337 ++++++++---------- .../accounts/AccountController.cpp | 4 +- .../highlights/HighlightController.cpp | 2 +- src/providers/twitch/TwitchMessageBuilder.cpp | 13 +- 4 files changed, 161 insertions(+), 195 deletions(-) diff --git a/src/common/SignalVector.hpp b/src/common/SignalVector.hpp index 2995cbba4..95006011c 100644 --- a/src/common/SignalVector.hpp +++ b/src/common/SignalVector.hpp @@ -4,243 +4,210 @@ #include #include #include -#include #include #include "debug/AssertInGuiThread.hpp" namespace chatterino { -template -struct SignalVectorItemArgs { - const TVectorItem &item; +template +struct SignalVectorItemEvent { + const T &item; int index; void *caller; }; -template -class ReadOnlySignalVector : boost::noncopyable +template +class SignalVector : boost::noncopyable { - using VecIt = typename std::vector::iterator; - public: - struct Iterator - : public std::iterator { - Iterator(VecIt &&it, std::shared_mutex &mutex) - : it_(std::move(it)) - , lock_(mutex) - , mutex_(mutex) - { - } + pajlada::Signals::Signal> itemInserted; + pajlada::Signals::Signal> itemRemoved; + pajlada::Signals::NoArgSignal delayedItemsChanged; - Iterator(const Iterator &other) - : it_(other.it_) - , lock_(other.mutex_) - , mutex_(other.mutex_) - { - } - - Iterator &operator=(const Iterator &other) - { - this->lock_ = std::shared_lock(other.mutex_.get()); - this->mutex_ = other.mutex_; - - return *this; - } - - TVectorItem &operator*() - { - return it_.operator*(); - } - - Iterator &operator++() - { - ++this->it_; - return *this; - } - - bool operator==(const Iterator &other) - { - return this->it_ == other.it_; - } - - bool operator!=(const Iterator &other) - { - return this->it_ != other.it_; - } - - auto operator-(const Iterator &other) - { - return this->it_ - other.it_; - } - - private: - VecIt it_; - std::shared_lock lock_; - std::reference_wrapper mutex_; - }; - - ReadOnlySignalVector() + SignalVector() + : readOnly_(new std::vector()) { QObject::connect(&this->itemsChangedTimer_, &QTimer::timeout, [this] { this->delayedItemsChanged.invoke(); }); this->itemsChangedTimer_.setInterval(100); this->itemsChangedTimer_.setSingleShot(true); } - virtual ~ReadOnlySignalVector() = default; - pajlada::Signals::Signal> itemInserted; - pajlada::Signals::Signal> itemRemoved; - pajlada::Signals::NoArgSignal delayedItemsChanged; - - Iterator begin() const + SignalVector(std::function &&compare) + : SignalVector() { - return Iterator( - const_cast &>(this->vector_).begin(), - this->mutex_); + itemCompare_ = std::move(compare); } - Iterator end() const + virtual bool isSorted() const { - return Iterator( - const_cast &>(this->vector_).end(), - this->mutex_); + return bool(this->itemCompare_); } - bool empty() const + /// A read-only version of the vector which can be used concurrently. + std::shared_ptr> readOnly() { - std::shared_lock lock(this->mutex_); - - return this->vector_.empty(); + return this->readOnly_; } - const std::vector &getVector() const + /// This may only be called from the GUI thread. + /// + /// @param item + /// Item to be inserted. + /// @param proposedIndex + /// Index to insert at. `-1` will append at the end. + /// Will be ignored if the vector is sorted. + /// @param caller + /// Caller id which will be passed in the itemInserted and itemRemoved + /// signals. + int insert(const T &item, int index = -1, void *caller = nullptr) { assertInGuiThread(); - return this->vector_; + if (this->isSorted()) + { + auto it = std::lower_bound(this->items_.begin(), this->items_.end(), + item, this->itemCompare_); + index = it - this->items_.begin(); + this->items_.insert(it, item); + } + else + { + if (index == -1) + index = this->items_.size(); + else + assert(index >= 0 && index <= this->items_.size()); + + this->items_.insert(this->items_.begin() + index, item); + } + + SignalVectorItemEvent args{item, index, caller}; + this->itemInserted.invoke(args); + this->itemsChanged_(); + + return index; } - std::vector cloneVector() const + /// This may only be called from the GUI thread. + /// + /// @param item + /// Item to be appended. + /// @param caller + /// Caller id which will be passed in the itemInserted and itemRemoved + /// signals. + int append(const T &item, void *caller = nullptr) { - std::shared_lock lock(this->mutex_); - - return this->vector_; + return this->insertItem(item, -1, caller); } - void invokeDelayedItemsChanged() + void removeAt(int index, void *caller = nullptr) + { + assertInGuiThread(); + assert(index >= 0 && index < int(this->items_.size())); + + T item = this->items_[index]; + this->items_.erase(this->items_.begin() + index); + + SignalVectorItemEvent args{item, index, caller}; + this->itemRemoved.invoke(args); + + this->itemsChanged_(); + } + + // compatability + [[deprecated]] int insertItem(const T &item, int proposedIndex = -1, + void *caller = nullptr) + { + return this->insert(item, proposedIndex, caller); + } + + [[deprecated]] int appendItem(const T &item, void *caller = nullptr) + { + return this->append(item, caller); + } + + [[deprecated]] void removeItem(int index, void *caller = nullptr) + { + this->removeAt(index, caller); + } + + [[deprecated]] const std::vector &getVector() const { assertInGuiThread(); + return this->items_; + } + + [[deprecated]] std::vector cloneVector() + { + return *this->readOnly(); + } + + // mirror vector functions + auto begin() const + { + assertInGuiThread(); + return this->items_.begin(); + } + + auto end() const + { + assertInGuiThread(); + return this->items_.end(); + } + + decltype(auto) operator[](size_t index) + { + assertInGuiThread(); + return this->items[index]; + } + + auto empty() + { + assertInGuiThread(); + return this->items_.empty(); + } + +private: + void itemsChanged_() + { + // emit delayed event if (!this->itemsChangedTimer_.isActive()) { this->itemsChangedTimer_.start(); } + + // update concurrent version + this->readOnly_ = std::make_shared>(this->items_); } - virtual bool isSorted() const = 0; - -protected: - std::vector vector_; + std::vector items_; + std::shared_ptr> readOnly_; QTimer itemsChangedTimer_; - mutable std::shared_mutex mutex_; + std::function itemCompare_; }; -template -class BaseSignalVector : public ReadOnlySignalVector +// compatability +template +using SignalVectorItemArgs = SignalVectorItemEvent; + +template +using ReadOnlySignalVector = SignalVector; + +template +using BaseSignalVector = SignalVector; + +template +using UnsortedSignalVector = SignalVector; + +template +class SortedSignalVector : public SignalVector { public: - // returns the actual index of the inserted item - virtual int insertItem(const TVectorItem &item, int proposedIndex = -1, - void *caller = nullptr) = 0; - - void removeItem(int index, void *caller = nullptr) + SortedSignalVector() + : SignalVector(Compare{}) { - assertInGuiThread(); - std::unique_lock lock(this->mutex_); - - assert(index >= 0 && index < int(this->vector_.size())); - - TVectorItem item = this->vector_[index]; - - this->vector_.erase(this->vector_.begin() + index); - lock.unlock(); // manual unlock - - SignalVectorItemArgs args{item, index, caller}; - this->itemRemoved.invoke(args); - - this->invokeDelayedItemsChanged(); - } - - int appendItem(const TVectorItem &item, void *caller = nullptr) - { - return this->insertItem(item, -1, caller); - } -}; - -template -class UnsortedSignalVector : public BaseSignalVector -{ -public: - virtual int insertItem(const TVectorItem &item, int index = -1, - void *caller = nullptr) override - { - assertInGuiThread(); - - { - std::unique_lock lock(this->mutex_); - if (index == -1) - { - index = this->vector_.size(); - } - else - { - assert(index >= 0 && index <= this->vector_.size()); - } - - this->vector_.insert(this->vector_.begin() + index, item); - } - - SignalVectorItemArgs args{item, index, caller}; - this->itemInserted.invoke(args); - this->invokeDelayedItemsChanged(); - return index; - } - - virtual bool isSorted() const override - { - return false; - } -}; - -template -class SortedSignalVector : public BaseSignalVector -{ -public: - virtual int insertItem(const TVectorItem &item, int = -1, - void *caller = nullptr) override - { - assertInGuiThread(); - int index = -1; - - { - std::unique_lock lock(this->mutex_); - - auto it = std::lower_bound(this->vector_.begin(), - this->vector_.end(), item, Compare{}); - index = it - this->vector_.begin(); - this->vector_.insert(it, item); - } - - SignalVectorItemArgs args{item, index, caller}; - this->itemInserted.invoke(args); - this->invokeDelayedItemsChanged(); - return index; - } - - virtual bool isSorted() const override - { - return true; } }; diff --git a/src/controllers/accounts/AccountController.cpp b/src/controllers/accounts/AccountController.cpp index cdaa53285..bd32401a8 100644 --- a/src/controllers/accounts/AccountController.cpp +++ b/src/controllers/accounts/AccountController.cpp @@ -30,10 +30,10 @@ AccountController::AccountController() case ProviderId::Twitch: { if (args.caller != this) { - auto accs = this->twitch.accounts.cloneVector(); + auto &&accs = this->twitch.accounts; auto it = std::find(accs.begin(), accs.end(), args.item); assert(it != accs.end()); - this->twitch.accounts.removeItem(it - accs.begin(), this); + this->twitch.accounts.removeAt(it - accs.begin(), this); } } break; diff --git a/src/controllers/highlights/HighlightController.cpp b/src/controllers/highlights/HighlightController.cpp index d030f77b8..a59218f39 100644 --- a/src/controllers/highlights/HighlightController.cpp +++ b/src/controllers/highlights/HighlightController.cpp @@ -87,7 +87,7 @@ HighlightBlacklistModel *HighlightController::createBlacklistModel( bool HighlightController::blacklistContains(const QString &username) { - for (const auto &blacklistedUser : this->blacklistedUsers) + for (const auto &blacklistedUser : *this->blacklistedUsers.readOnly()) { if (blacklistedUser.isMatch(username)) { diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 0d4680472..2e1d18b4c 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -170,7 +170,8 @@ bool TwitchMessageBuilder::isIgnored() const auto app = getApp(); // TODO(pajlada): Do we need to check if the phrase is valid first? - for (const auto &phrase : app->ignores->phrases) + auto phrases = app->ignores->phrases.readOnly(); + for (const auto &phrase : *phrases) { if (phrase.isBlock() && phrase.isMatch(this->originalMessage_)) { @@ -765,7 +766,7 @@ void TwitchMessageBuilder::runIgnoreReplaces( std::vector> &twitchEmotes) { auto app = getApp(); - const auto &phrases = app->ignores->phrases; + auto phrases = app->ignores->phrases.readOnly(); auto removeEmotesInRange = [](int pos, int len, std::vector> @@ -828,7 +829,7 @@ void TwitchMessageBuilder::runIgnoreReplaces( } }; - for (const auto &phrase : phrases) + for (const auto &phrase : *phrases) { if (phrase.isBlock()) { @@ -1056,11 +1057,9 @@ void TwitchMessageBuilder::parseHighlights() */ } - std::vector userHighlights = - app->highlights->highlightedUsers.cloneVector(); - // Highlight because of sender - for (const HighlightPhrase &userHighlight : userHighlights) + auto userHighlights = app->highlights->highlightedUsers.readOnly(); + for (const HighlightPhrase &userHighlight : *userHighlights) { if (!userHighlight.isMatch(this->ircMessage->nick())) { From 4a5dc80bc6cd27194f7f9e8a0c508cdc5ba106cf Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 17:45:59 +0100 Subject: [PATCH 040/153] some more refactoring --- src/common/SignalVector.hpp | 39 ++++++++-------- src/common/SignalVectorModel.hpp | 8 ++-- .../accounts/AccountController.cpp | 2 +- .../commands/CommandController.cpp | 2 +- .../highlights/HighlightController.cpp | 46 ++++++++----------- .../highlights/HighlightController.hpp | 7 --- src/controllers/ignores/IgnoreController.cpp | 2 +- .../moderationactions/ModerationActions.cpp | 2 +- .../notifications/NotificationController.cpp | 12 ++--- src/controllers/pings/PingController.cpp | 6 +-- src/widgets/dialogs/SelectChannelDialog.cpp | 7 ++- src/widgets/dialogs/UserInfoPopup.cpp | 4 +- 12 files changed, 62 insertions(+), 75 deletions(-) diff --git a/src/common/SignalVector.hpp b/src/common/SignalVector.hpp index 95006011c..bf8cbfc51 100644 --- a/src/common/SignalVector.hpp +++ b/src/common/SignalVector.hpp @@ -115,30 +115,33 @@ public: this->itemsChanged_(); } - // compatability - [[deprecated]] int insertItem(const T &item, int proposedIndex = -1, - void *caller = nullptr) - { - return this->insert(item, proposedIndex, caller); - } - - [[deprecated]] int appendItem(const T &item, void *caller = nullptr) - { - return this->append(item, caller); - } - - [[deprecated]] void removeItem(int index, void *caller = nullptr) - { - this->removeAt(index, caller); - } - - [[deprecated]] const std::vector &getVector() const + const std::vector &raw() const { assertInGuiThread(); return this->items_; } + // compatability + [[deprecated("use insert")]] int insertItem(const T &item, + int proposedIndex = -1, + void *caller = nullptr) + { + return this->insert(item, proposedIndex, caller); + } + + [[deprecated("use append")]] int appendItem(const T &item, + void *caller = nullptr) + { + return this->append(item, caller); + } + + [[deprecated("use removeAt")]] void removeItem(int index, + void *caller = nullptr) + { + this->removeAt(index, caller); + } + [[deprecated]] std::vector cloneVector() { return *this->readOnly(); diff --git a/src/common/SignalVectorModel.hpp b/src/common/SignalVectorModel.hpp index bb7cd7c05..4fe39eddf 100644 --- a/src/common/SignalVectorModel.hpp +++ b/src/common/SignalVectorModel.hpp @@ -52,7 +52,7 @@ public: }; int i = 0; - for (const TVectorItem &item : vec->getVector()) + for (const TVectorItem &item : vec->raw()) { SignalVectorItemArgs args{item, i++, 0}; @@ -272,15 +272,15 @@ public: int from = data->data("chatterino_row_id").toInt(); int to = parent.row(); - if (from < 0 || from > this->vector_->getVector().size() || - to < 0 || to > this->vector_->getVector().size()) + if (from < 0 || from > this->vector_->raw().size() || + to < 0 || to > this->vector_->raw().size()) { return false; } if (from != to) { - auto item = this->vector_->getVector()[from]; + auto item = this->vector_->raw()[from]; this->vector_->removeItem(from); this->vector_->insertItem(item, to); } diff --git a/src/controllers/accounts/AccountController.cpp b/src/controllers/accounts/AccountController.cpp index bd32401a8..db65e9440 100644 --- a/src/controllers/accounts/AccountController.cpp +++ b/src/controllers/accounts/AccountController.cpp @@ -16,7 +16,7 @@ AccountController::AccountController() this->twitch.accounts.itemRemoved.connect([this](const auto &args) { if (args.caller != this) { - auto &accs = this->twitch.accounts.getVector(); + auto &accs = this->twitch.accounts.raw(); auto it = std::find(accs.begin(), accs.end(), args.item); assert(it != accs.end()); diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index a6150cfa9..75464bbb7 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -212,7 +212,7 @@ void CommandController::initialize(Settings &, Paths &paths) // Update the setting when the vector of commands has been updated (most // likely from the settings dialog) this->items_.delayedItemsChanged.connect([this] { // - this->commandsSetting_->setValue(this->items_.getVector()); + this->commandsSetting_->setValue(this->items_.raw()); }); // Load commands from commands.json diff --git a/src/controllers/highlights/HighlightController.cpp b/src/controllers/highlights/HighlightController.cpp index a59218f39..60df26ff1 100644 --- a/src/controllers/highlights/HighlightController.cpp +++ b/src/controllers/highlights/HighlightController.cpp @@ -12,38 +12,30 @@ HighlightController::HighlightController() { } +template +inline void persist(SignalVector &vec, const std::string &name) +{ + auto setting = std::make_unique>>(name); + + for (auto &&item : setting->getValue()) + vec.append(item); + + vec.delayedItemsChanged.connect([setting = setting.get(), vec = &vec] { + setting->setValue(vec->raw()); + }); + + // TODO + setting.release(); +} + void HighlightController::initialize(Settings &settings, Paths &paths) { assert(!this->initialized_); this->initialized_ = true; - for (const HighlightPhrase &phrase : this->highlightsSetting_.getValue()) - { - this->phrases.appendItem(phrase); - } - - this->phrases.delayedItemsChanged.connect([this] { // - this->highlightsSetting_.setValue(this->phrases.getVector()); - }); - - for (const HighlightBlacklistUser &blacklistedUser : - this->blacklistSetting_.getValue()) - { - this->blacklistedUsers.appendItem(blacklistedUser); - } - - this->blacklistedUsers.delayedItemsChanged.connect([this] { - this->blacklistSetting_.setValue(this->blacklistedUsers.getVector()); - }); - - for (const HighlightPhrase &user : this->userSetting_.getValue()) - { - this->highlightedUsers.appendItem(user); - } - - this->highlightedUsers.delayedItemsChanged.connect([this] { // - this->userSetting_.setValue(this->highlightedUsers.getVector()); - }); + persist(this->phrases, "/highlighting/highlights"); + persist(this->blacklistedUsers, "/highlighting/blacklist"); + persist(this->highlightedUsers, "/highlighting/users"); } HighlightModel *HighlightController::createModel(QObject *parent) diff --git a/src/controllers/highlights/HighlightController.hpp b/src/controllers/highlights/HighlightController.hpp index faa4683c1..2f61b1a49 100644 --- a/src/controllers/highlights/HighlightController.hpp +++ b/src/controllers/highlights/HighlightController.hpp @@ -40,13 +40,6 @@ public: private: bool initialized_ = false; - - ChatterinoSetting> highlightsSetting_ = { - "/highlighting/highlights"}; - ChatterinoSetting> blacklistSetting_ = { - "/highlighting/blacklist"}; - ChatterinoSetting> userSetting_ = { - "/highlighting/users"}; }; } // namespace chatterino diff --git a/src/controllers/ignores/IgnoreController.cpp b/src/controllers/ignores/IgnoreController.cpp index 5548ee5d4..b1028be0a 100644 --- a/src/controllers/ignores/IgnoreController.cpp +++ b/src/controllers/ignores/IgnoreController.cpp @@ -18,7 +18,7 @@ void IgnoreController::initialize(Settings &, Paths &) } this->phrases.delayedItemsChanged.connect([this] { // - this->ignoresSetting_.setValue(this->phrases.getVector()); + this->ignoresSetting_.setValue(this->phrases.raw()); }); } diff --git a/src/controllers/moderationactions/ModerationActions.cpp b/src/controllers/moderationactions/ModerationActions.cpp index 0104c7de1..cb68174be 100644 --- a/src/controllers/moderationactions/ModerationActions.cpp +++ b/src/controllers/moderationactions/ModerationActions.cpp @@ -27,7 +27,7 @@ void ModerationActions::initialize(Settings &settings, Paths &paths) } this->items.delayedItemsChanged.connect([this] { // - this->setting_->setValue(this->items.getVector()); + this->setting_->setValue(this->items.raw()); }); } diff --git a/src/controllers/notifications/NotificationController.cpp b/src/controllers/notifications/NotificationController.cpp index b9777c802..ec2f231c6 100644 --- a/src/controllers/notifications/NotificationController.cpp +++ b/src/controllers/notifications/NotificationController.cpp @@ -31,7 +31,7 @@ void NotificationController::initialize(Settings &settings, Paths &paths) this->channelMap[Platform::Twitch].delayedItemsChanged.connect([this] { // this->twitchSetting_.setValue( - this->channelMap[Platform::Twitch].getVector()); + this->channelMap[Platform::Twitch].raw()); }); /* for (const QString &channelName : this->mixerSetting_.getValue()) { @@ -88,9 +88,9 @@ void NotificationController::removeChannelNotification( const QString &channelName, Platform p) { for (std::vector::size_type i = 0; - i != channelMap[p].getVector().size(); i++) + i != channelMap[p].raw().size(); i++) { - if (channelMap[p].getVector()[i].toLower() == channelName.toLower()) + if (channelMap[p].raw()[i].toLower() == channelName.toLower()) { channelMap[p].removeItem(i); i--; @@ -128,14 +128,14 @@ NotificationModel *NotificationController::createModel(QObject *parent, void NotificationController::fetchFakeChannels() { for (std::vector::size_type i = 0; - i != channelMap[Platform::Twitch].getVector().size(); i++) + i != channelMap[Platform::Twitch].raw().size(); i++) { auto chan = getApp()->twitch.server->getChannelOrEmpty( - channelMap[Platform::Twitch].getVector()[i]); + channelMap[Platform::Twitch].raw()[i]); if (chan->isEmpty()) { getFakeTwitchChannelLiveStatus( - channelMap[Platform::Twitch].getVector()[i]); + channelMap[Platform::Twitch].raw()[i]); } } } diff --git a/src/controllers/pings/PingController.cpp b/src/controllers/pings/PingController.cpp index b61d6e795..67bd3ad66 100644 --- a/src/controllers/pings/PingController.cpp +++ b/src/controllers/pings/PingController.cpp @@ -12,7 +12,7 @@ void PingController::initialize(Settings &settings, Paths &paths) } this->channelVector.delayedItemsChanged.connect([this] { // - this->pingSetting_.setValue(this->channelVector.getVector()); + this->pingSetting_.setValue(this->channelVector.raw()); }); } @@ -43,9 +43,9 @@ void PingController::muteChannel(const QString &channelName) void PingController::unmuteChannel(const QString &channelName) { for (std::vector::size_type i = 0; - i != channelVector.getVector().size(); i++) + i != channelVector.raw().size(); i++) { - if (channelVector.getVector()[i].toLower() == channelName.toLower()) + if (channelVector.raw()[i].toLower() == channelName.toLower()) { channelVector.removeItem(i); i--; diff --git a/src/widgets/dialogs/SelectChannelDialog.cpp b/src/widgets/dialogs/SelectChannelDialog.cpp index 25d61ff01..c8e21a18b 100644 --- a/src/widgets/dialogs/SelectChannelDialog.cpp +++ b/src/widgets/dialogs/SelectChannelDialog.cpp @@ -160,13 +160,12 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent) view->getTableView(), &QTableView::doubleClicked, [](const QModelIndex &index) { auto editor = new IrcConnectionEditor( - Irc::instance() - .connections.getVector()[size_t(index.row())]); + Irc::instance().connections.raw()[size_t(index.row())]); if (editor->exec() == QDialog::Accepted) { auto data = editor->data(); - auto &&conns = Irc::instance().connections.getVector(); + auto &&conns = Irc::instance().connections.raw(); int i = 0; for (auto &&conn : conns) { @@ -348,7 +347,7 @@ IndirectChannel SelectChannelDialog::getSelectedChannel() const ->currentIndex() .row(); - auto &&vector = Irc::instance().connections.getVector(); + auto &&vector = Irc::instance().connections.raw(); if (row >= 0 && row < int(vector.size())) { diff --git a/src/widgets/dialogs/UserInfoPopup.cpp b/src/widgets/dialogs/UserInfoPopup.cpp index e530a5bff..bb75a61fb 100644 --- a/src/widgets/dialogs/UserInfoPopup.cpp +++ b/src/widgets/dialogs/UserInfoPopup.cpp @@ -343,7 +343,7 @@ void UserInfoPopup::installEvents() else { const auto &vector = - getApp()->highlights->blacklistedUsers.getVector(); + getApp()->highlights->blacklistedUsers.raw(); for (int i = 0; i < vector.size(); i++) { @@ -456,7 +456,7 @@ void UserInfoPopup::updateUserData() // get ignoreHighlights state bool isIgnoringHighlights = false; - const auto &vector = getApp()->highlights->blacklistedUsers.getVector(); + const auto &vector = getApp()->highlights->blacklistedUsers.raw(); for (int i = 0; i < vector.size(); i++) { if (this->userName_ == vector[i].getPattern()) From 843e2ad99480a389169d03e7219ee130996d443e Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 19:31:43 +0100 Subject: [PATCH 041/153] refactored HighlightController --- src/common/SignalVectorModel.hpp | 13 +++-- .../accounts/AccountController.cpp | 2 +- .../commands/CommandController.cpp | 4 +- .../commands/CommandController.hpp | 2 - .../highlights/HighlightBlacklistModel.hpp | 4 +- .../highlights/HighlightController.cpp | 52 ++----------------- .../highlights/HighlightController.hpp | 19 ------- src/controllers/highlights/HighlightModel.hpp | 6 +-- .../highlights/UserHighlightModel.hpp | 3 +- src/controllers/ignores/IgnoreController.cpp | 2 +- .../moderationactions/ModerationActions.cpp | 2 +- .../notifications/NotificationController.cpp | 2 +- src/controllers/pings/PingController.cpp | 2 +- .../taggedusers/TaggedUsersController.cpp | 2 +- src/providers/irc/Irc2.cpp | 2 +- src/providers/twitch/IrcMessageHandler.cpp | 1 - .../settingspages/HighlightingPage.cpp | 16 +++--- 17 files changed, 34 insertions(+), 100 deletions(-) diff --git a/src/common/SignalVectorModel.hpp b/src/common/SignalVectorModel.hpp index 4fe39eddf..31129769b 100644 --- a/src/common/SignalVectorModel.hpp +++ b/src/common/SignalVectorModel.hpp @@ -25,7 +25,7 @@ public: } } - void init(BaseSignalVector *vec) + void initialize(BaseSignalVector *vec) { this->vector_ = vec; @@ -89,6 +89,13 @@ public: this->afterInit(); } + SignalVectorModel *initialized( + BaseSignalVector *vec) + { + this->initialize(vec); + return this; + } + virtual ~SignalVectorModel() { for (Row &row : this->rows_) @@ -272,8 +279,8 @@ public: int from = data->data("chatterino_row_id").toInt(); int to = parent.row(); - if (from < 0 || from > this->vector_->raw().size() || - to < 0 || to > this->vector_->raw().size()) + if (from < 0 || from > this->vector_->raw().size() || to < 0 || + to > this->vector_->raw().size()) { return false; } diff --git a/src/controllers/accounts/AccountController.cpp b/src/controllers/accounts/AccountController.cpp index db65e9440..6d272cf0c 100644 --- a/src/controllers/accounts/AccountController.cpp +++ b/src/controllers/accounts/AccountController.cpp @@ -50,7 +50,7 @@ AccountModel *AccountController::createModel(QObject *parent) { AccountModel *model = new AccountModel(parent); - model->init(&this->accounts_); + model->initialize(&this->accounts_); return model; } diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 75464bbb7..418612be7 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -234,7 +234,7 @@ void CommandController::save() CommandModel *CommandController::createModel(QObject *parent) { CommandModel *model = new CommandModel(parent); - model->init(&this->items_); + model->initialize(&this->items_); return model; } @@ -245,8 +245,6 @@ QString CommandController::execCommand(const QString &textNoEmoji, QString text = getApp()->emotes->emojis.replaceShortCodes(textNoEmoji); QStringList words = text.split(' ', QString::SkipEmptyParts); - std::lock_guard lock(this->mutex_); - if (words.length() == 0) { return text; diff --git a/src/controllers/commands/CommandController.hpp b/src/controllers/commands/CommandController.hpp index 938fe0f39..5435a60b9 100644 --- a/src/controllers/commands/CommandController.hpp +++ b/src/controllers/commands/CommandController.hpp @@ -39,8 +39,6 @@ private: QMap commandsMap_; int maxSpaces_ = 0; - std::mutex mutex_; - std::shared_ptr sm_; // Because the setting manager is not initialized until the initialize // function is called (and not in the constructor), we have to diff --git a/src/controllers/highlights/HighlightBlacklistModel.hpp b/src/controllers/highlights/HighlightBlacklistModel.hpp index c3420b066..d4acc474d 100644 --- a/src/controllers/highlights/HighlightBlacklistModel.hpp +++ b/src/controllers/highlights/HighlightBlacklistModel.hpp @@ -11,9 +11,9 @@ class HighlightController; class HighlightBlacklistModel : public SignalVectorModel { +public: explicit HighlightBlacklistModel(QObject *parent); -public: enum Column { Pattern = 0, UseRegex = 1, @@ -28,8 +28,6 @@ protected: // turns a row in the model into a vector item virtual void getRowFromItem(const HighlightBlacklistUser &item, std::vector &row) override; - - friend class HighlightController; }; } // namespace chatterino diff --git a/src/controllers/highlights/HighlightController.cpp b/src/controllers/highlights/HighlightController.cpp index 60df26ff1..f341f3495 100644 --- a/src/controllers/highlights/HighlightController.cpp +++ b/src/controllers/highlights/HighlightController.cpp @@ -1,17 +1,10 @@ #include "HighlightController.hpp" #include "Application.hpp" -#include "controllers/highlights/HighlightBlacklistModel.hpp" -#include "controllers/highlights/HighlightModel.hpp" -#include "controllers/highlights/UserHighlightModel.hpp" -#include "widgets/dialogs/NotificationPopup.hpp" +#include "common/ChatterinoSetting.hpp" namespace chatterino { -HighlightController::HighlightController() -{ -} - template inline void persist(SignalVector &vec, const std::string &name) { @@ -24,7 +17,7 @@ inline void persist(SignalVector &vec, const std::string &name) setting->setValue(vec->raw()); }); - // TODO + // TODO: Delete when appropriate. setting.release(); } @@ -38,65 +31,26 @@ void HighlightController::initialize(Settings &settings, Paths &paths) persist(this->highlightedUsers, "/highlighting/users"); } -HighlightModel *HighlightController::createModel(QObject *parent) -{ - HighlightModel *model = new HighlightModel(parent); - model->init(&this->phrases); - - return model; -} - -UserHighlightModel *HighlightController::createUserModel(QObject *parent) -{ - auto *model = new UserHighlightModel(parent); - model->init(&this->highlightedUsers); - - return model; -} - bool HighlightController::isHighlightedUser(const QString &username) { - const auto &userItems = this->highlightedUsers; - for (const auto &highlightedUser : userItems) + for (const auto &highlightedUser : this->highlightedUsers) { if (highlightedUser.isMatch(username)) - { return true; - } } return false; } -HighlightBlacklistModel *HighlightController::createBlacklistModel( - QObject *parent) -{ - auto *model = new HighlightBlacklistModel(parent); - model->init(&this->blacklistedUsers); - - return model; -} - bool HighlightController::blacklistContains(const QString &username) { for (const auto &blacklistedUser : *this->blacklistedUsers.readOnly()) { if (blacklistedUser.isMatch(username)) - { return true; - } } return false; } -void HighlightController::addHighlight(const MessagePtr &msg) -{ - // static NotificationPopup popup; - - // popup.updatePosition(); - // popup.addMessage(msg); - // popup.show(); -} - } // namespace chatterino diff --git a/src/controllers/highlights/HighlightController.hpp b/src/controllers/highlights/HighlightController.hpp index 2f61b1a49..5f746c8bf 100644 --- a/src/controllers/highlights/HighlightController.hpp +++ b/src/controllers/highlights/HighlightController.hpp @@ -1,6 +1,5 @@ #pragma once -#include "common/ChatterinoSetting.hpp" #include "common/SignalVector.hpp" #include "common/Singleton.hpp" #include "controllers/highlights/HighlightBlacklistUser.hpp" @@ -8,36 +7,18 @@ namespace chatterino { -struct Message; -using MessagePtr = std::shared_ptr; - -class Settings; -class Paths; - -class UserHighlightModel; -class HighlightModel; -class HighlightBlacklistModel; - class HighlightController final : public Singleton { public: - HighlightController(); - virtual void initialize(Settings &settings, Paths &paths) override; UnsortedSignalVector phrases; UnsortedSignalVector blacklistedUsers; UnsortedSignalVector highlightedUsers; - HighlightModel *createModel(QObject *parent); - HighlightBlacklistModel *createBlacklistModel(QObject *parent); - UserHighlightModel *createUserModel(QObject *parent); - bool isHighlightedUser(const QString &username); bool blacklistContains(const QString &username); - void addHighlight(const MessagePtr &msg); - private: bool initialized_ = false; }; diff --git a/src/controllers/highlights/HighlightModel.hpp b/src/controllers/highlights/HighlightModel.hpp index 13b85bca1..aa37b4ffe 100644 --- a/src/controllers/highlights/HighlightModel.hpp +++ b/src/controllers/highlights/HighlightModel.hpp @@ -7,13 +7,11 @@ namespace chatterino { -class HighlightController; - class HighlightModel : public SignalVectorModel { +public: explicit HighlightModel(QObject *parent); -public: // Used here, in HighlightingPage and in UserHighlightModel enum Column { Pattern = 0, @@ -40,8 +38,6 @@ protected: virtual void customRowSetData(const std::vector &row, int column, const QVariant &value, int role, int rowIndex) override; - - friend class HighlightController; }; } // namespace chatterino diff --git a/src/controllers/highlights/UserHighlightModel.hpp b/src/controllers/highlights/UserHighlightModel.hpp index dcc42a950..de4332433 100644 --- a/src/controllers/highlights/UserHighlightModel.hpp +++ b/src/controllers/highlights/UserHighlightModel.hpp @@ -11,6 +11,7 @@ class HighlightController; class UserHighlightModel : public SignalVectorModel { +public: explicit UserHighlightModel(QObject *parent); protected: @@ -21,8 +22,6 @@ protected: virtual void getRowFromItem(const HighlightPhrase &item, std::vector &row) override; - - friend class HighlightController; }; } // namespace chatterino diff --git a/src/controllers/ignores/IgnoreController.cpp b/src/controllers/ignores/IgnoreController.cpp index b1028be0a..28b511a36 100644 --- a/src/controllers/ignores/IgnoreController.cpp +++ b/src/controllers/ignores/IgnoreController.cpp @@ -25,7 +25,7 @@ void IgnoreController::initialize(Settings &, Paths &) IgnoreModel *IgnoreController::createModel(QObject *parent) { IgnoreModel *model = new IgnoreModel(parent); - model->init(&this->phrases); + model->initialize(&this->phrases); return model; } diff --git a/src/controllers/moderationactions/ModerationActions.cpp b/src/controllers/moderationactions/ModerationActions.cpp index cb68174be..d13a71eab 100644 --- a/src/controllers/moderationactions/ModerationActions.cpp +++ b/src/controllers/moderationactions/ModerationActions.cpp @@ -34,7 +34,7 @@ void ModerationActions::initialize(Settings &settings, Paths &paths) ModerationActionModel *ModerationActions::createModel(QObject *parent) { ModerationActionModel *model = new ModerationActionModel(parent); - model->init(&this->items); + model->initialize(&this->items); return model; } diff --git a/src/controllers/notifications/NotificationController.cpp b/src/controllers/notifications/NotificationController.cpp index ec2f231c6..59fed3b1f 100644 --- a/src/controllers/notifications/NotificationController.cpp +++ b/src/controllers/notifications/NotificationController.cpp @@ -121,7 +121,7 @@ NotificationModel *NotificationController::createModel(QObject *parent, Platform p) { NotificationModel *model = new NotificationModel(parent); - model->init(&this->channelMap[p]); + model->initialize(&this->channelMap[p]); return model; } diff --git a/src/controllers/pings/PingController.cpp b/src/controllers/pings/PingController.cpp index 67bd3ad66..36f4b08ad 100644 --- a/src/controllers/pings/PingController.cpp +++ b/src/controllers/pings/PingController.cpp @@ -19,7 +19,7 @@ void PingController::initialize(Settings &settings, Paths &paths) PingModel *PingController::createModel(QObject *parent) { PingModel *model = new PingModel(parent); - model->init(&this->channelVector); + model->initialize(&this->channelVector); return model; } diff --git a/src/controllers/taggedusers/TaggedUsersController.cpp b/src/controllers/taggedusers/TaggedUsersController.cpp index 2aa82a6c3..095bd779d 100644 --- a/src/controllers/taggedusers/TaggedUsersController.cpp +++ b/src/controllers/taggedusers/TaggedUsersController.cpp @@ -11,7 +11,7 @@ TaggedUsersController::TaggedUsersController() TaggedUsersModel *TaggedUsersController::createModel(QObject *parent) { TaggedUsersModel *model = new TaggedUsersModel(parent); - model->init(&this->users); + model->initialize(&this->users); return model; } diff --git a/src/providers/irc/Irc2.cpp b/src/providers/irc/Irc2.cpp index 0a8f2ed60..ee59e94ce 100644 --- a/src/providers/irc/Irc2.cpp +++ b/src/providers/irc/Irc2.cpp @@ -143,7 +143,7 @@ Irc::Irc() QAbstractTableModel *Irc::newConnectionModel(QObject *parent) { auto model = new Model(parent); - model->init(&this->connections); + model->initialize(&this->connections); return model; } diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index 7a456ecaf..5b022bc1e 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -241,7 +241,6 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message, if (highlighted) { server.mentionsChannel->addMessage(msg); - getApp()->highlights->addHighlight(msg); } } diff --git a/src/widgets/settingspages/HighlightingPage.cpp b/src/widgets/settingspages/HighlightingPage.cpp index 2612454be..9b8d1af94 100644 --- a/src/widgets/settingspages/HighlightingPage.cpp +++ b/src/widgets/settingspages/HighlightingPage.cpp @@ -49,12 +49,12 @@ HighlightingPage::HighlightingPage() "Play notification sounds and highlight messages based on " "certain patterns."); - EditableModelView *view = + auto view = highlights .emplace( - app->highlights->createModel(nullptr)) + (new HighlightModel(nullptr)) + ->initialized(&app->highlights->phrases)) .getElement(); - view->addRegexHelpLink(); view->setTitles({"Pattern", "Flash\ntaskbar", "Play\nsound", "Enable\nregex", "Case-\nsensitive", @@ -94,7 +94,9 @@ HighlightingPage::HighlightingPage() EditableModelView *view = pingUsers .emplace( - app->highlights->createUserModel(nullptr)) + (new UserHighlightModel(nullptr)) + ->initialized( + &app->highlights->highlightedUsers)) .getElement(); view->addRegexHelpLink(); @@ -140,7 +142,9 @@ HighlightingPage::HighlightingPage() EditableModelView *view = disabledUsers .emplace( - app->highlights->createBlacklistModel(nullptr)) + (new HighlightBlacklistModel(nullptr)) + ->initialized( + &app->highlights->blacklistedUsers)) .getElement(); view->addRegexHelpLink(); @@ -205,7 +209,7 @@ HighlightingPage::HighlightingPage() // ---- misc this->disabledUsersChangedTimer_.setSingleShot(true); -} +} // namespace chatterino void HighlightingPage::tableCellClicked(const QModelIndex &clicked, EditableModelView *view) From e2c493f3697854ffacfb6517417442cb817ffbf7 Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 19:37:02 +0100 Subject: [PATCH 042/153] removed compat aliases for SignalVector --- src/common/SignalVector.hpp | 12 ------------ src/common/SignalVectorModel.hpp | 11 +++++------ src/controllers/commands/CommandController.hpp | 2 +- src/controllers/highlights/HighlightController.hpp | 6 +++--- src/controllers/ignores/IgnoreController.hpp | 2 +- .../moderationactions/ModerationActions.hpp | 2 +- .../notifications/NotificationController.hpp | 4 ++-- src/controllers/pings/PingController.hpp | 2 +- src/providers/irc/Irc2.hpp | 2 +- 9 files changed, 15 insertions(+), 28 deletions(-) diff --git a/src/common/SignalVector.hpp b/src/common/SignalVector.hpp index bf8cbfc51..7181067fb 100644 --- a/src/common/SignalVector.hpp +++ b/src/common/SignalVector.hpp @@ -192,18 +192,6 @@ private: }; // compatability -template -using SignalVectorItemArgs = SignalVectorItemEvent; - -template -using ReadOnlySignalVector = SignalVector; - -template -using BaseSignalVector = SignalVector; - -template -using UnsortedSignalVector = SignalVector; - template class SortedSignalVector : public SignalVector { diff --git a/src/common/SignalVectorModel.hpp b/src/common/SignalVectorModel.hpp index 31129769b..a13bd97f0 100644 --- a/src/common/SignalVectorModel.hpp +++ b/src/common/SignalVectorModel.hpp @@ -25,11 +25,11 @@ public: } } - void initialize(BaseSignalVector *vec) + void initialize(SignalVector *vec) { this->vector_ = vec; - auto insert = [this](const SignalVectorItemArgs &args) { + auto insert = [this](const SignalVectorItemEvent &args) { if (args.caller == this) { return; @@ -54,7 +54,7 @@ public: int i = 0; for (const TVectorItem &item : vec->raw()) { - SignalVectorItemArgs args{item, i++, 0}; + SignalVectorItemEvent args{item, i++, 0}; insert(args); } @@ -89,8 +89,7 @@ public: this->afterInit(); } - SignalVectorModel *initialized( - BaseSignalVector *vec) + SignalVectorModel *initialized(SignalVector *vec) { this->initialize(vec); return this; @@ -393,7 +392,7 @@ protected: private: std::vector> headerData_; - BaseSignalVector *vector_; + SignalVector *vector_; std::vector rows_; int columnCount_; diff --git a/src/controllers/commands/CommandController.hpp b/src/controllers/commands/CommandController.hpp index 5435a60b9..ebc57d8cb 100644 --- a/src/controllers/commands/CommandController.hpp +++ b/src/controllers/commands/CommandController.hpp @@ -22,7 +22,7 @@ class CommandModel; class CommandController final : public Singleton { public: - UnsortedSignalVector items_; + SignalVector items_; QString execCommand(const QString &text, std::shared_ptr channel, bool dryRun); diff --git a/src/controllers/highlights/HighlightController.hpp b/src/controllers/highlights/HighlightController.hpp index 5f746c8bf..6741e1dbd 100644 --- a/src/controllers/highlights/HighlightController.hpp +++ b/src/controllers/highlights/HighlightController.hpp @@ -12,9 +12,9 @@ class HighlightController final : public Singleton public: virtual void initialize(Settings &settings, Paths &paths) override; - UnsortedSignalVector phrases; - UnsortedSignalVector blacklistedUsers; - UnsortedSignalVector highlightedUsers; + SignalVector phrases; + SignalVector blacklistedUsers; + SignalVector highlightedUsers; bool isHighlightedUser(const QString &username); bool blacklistContains(const QString &username); diff --git a/src/controllers/ignores/IgnoreController.hpp b/src/controllers/ignores/IgnoreController.hpp index 1279f4a88..e3330c33a 100644 --- a/src/controllers/ignores/IgnoreController.hpp +++ b/src/controllers/ignores/IgnoreController.hpp @@ -19,7 +19,7 @@ class IgnoreController final : public Singleton public: virtual void initialize(Settings &settings, Paths &paths) override; - UnsortedSignalVector phrases; + SignalVector phrases; IgnoreModel *createModel(QObject *parent); diff --git a/src/controllers/moderationactions/ModerationActions.hpp b/src/controllers/moderationactions/ModerationActions.hpp index e1bf1b802..7388019ba 100644 --- a/src/controllers/moderationactions/ModerationActions.hpp +++ b/src/controllers/moderationactions/ModerationActions.hpp @@ -20,7 +20,7 @@ public: virtual void initialize(Settings &settings, Paths &paths) override; - UnsortedSignalVector items; + SignalVector items; ModerationActionModel *createModel(QObject *parent); diff --git a/src/controllers/notifications/NotificationController.hpp b/src/controllers/notifications/NotificationController.hpp index 228348add..a9c048efe 100644 --- a/src/controllers/notifications/NotificationController.hpp +++ b/src/controllers/notifications/NotificationController.hpp @@ -30,9 +30,9 @@ public: void playSound(); - UnsortedSignalVector getVector(Platform p); + SignalVector getVector(Platform p); - std::map> channelMap; + std::map> channelMap; NotificationModel *createModel(QObject *parent, Platform p); diff --git a/src/controllers/pings/PingController.hpp b/src/controllers/pings/PingController.hpp index 80805e856..b0b5542b4 100644 --- a/src/controllers/pings/PingController.hpp +++ b/src/controllers/pings/PingController.hpp @@ -28,7 +28,7 @@ public: private: bool initialized_ = false; - UnsortedSignalVector channelVector; + SignalVector channelVector; ChatterinoSetting> pingSetting_ = {"/pings/muted"}; }; diff --git a/src/providers/irc/Irc2.hpp b/src/providers/irc/Irc2.hpp index de484924a..0cdc60c77 100644 --- a/src/providers/irc/Irc2.hpp +++ b/src/providers/irc/Irc2.hpp @@ -41,7 +41,7 @@ public: static inline void *const noEraseCredentialCaller = reinterpret_cast(1); - UnsortedSignalVector connections; + SignalVector connections; QAbstractTableModel *newConnectionModel(QObject *parent); ChannelPtr getOrAddChannel(int serverId, QString name); From 2ebe07bace950c72425da147d9926141fc9048bc Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 19:44:13 +0100 Subject: [PATCH 043/153] removed compat functions in SignalVector --- src/common/SignalVector.hpp | 22 +------------------ src/common/SignalVectorModel.hpp | 12 +++++----- .../accounts/AccountController.cpp | 5 ++--- .../commands/CommandController.cpp | 2 +- src/controllers/ignores/IgnoreController.cpp | 2 +- .../moderationactions/ModerationActions.cpp | 2 +- .../notifications/NotificationController.cpp | 13 +++++------ src/controllers/pings/PingController.cpp | 6 ++--- src/providers/irc/Irc2.cpp | 2 +- src/providers/twitch/TwitchAccountManager.cpp | 2 +- src/widgets/dialogs/SelectChannelDialog.cpp | 6 ++--- src/widgets/dialogs/UserInfoPopup.cpp | 4 ++-- src/widgets/settingspages/CommandPage.cpp | 4 ++-- .../settingspages/HighlightingPage.cpp | 6 ++--- src/widgets/settingspages/IgnoresPage.cpp | 2 +- src/widgets/settingspages/ModerationPage.cpp | 2 +- .../settingspages/NotificationPage.cpp | 2 +- 17 files changed, 36 insertions(+), 58 deletions(-) diff --git a/src/common/SignalVector.hpp b/src/common/SignalVector.hpp index 7181067fb..074614d88 100644 --- a/src/common/SignalVector.hpp +++ b/src/common/SignalVector.hpp @@ -98,7 +98,7 @@ public: /// signals. int append(const T &item, void *caller = nullptr) { - return this->insertItem(item, -1, caller); + return this->insert(item, -1, caller); } void removeAt(int index, void *caller = nullptr) @@ -122,26 +122,6 @@ public: return this->items_; } - // compatability - [[deprecated("use insert")]] int insertItem(const T &item, - int proposedIndex = -1, - void *caller = nullptr) - { - return this->insert(item, proposedIndex, caller); - } - - [[deprecated("use append")]] int appendItem(const T &item, - void *caller = nullptr) - { - return this->append(item, caller); - } - - [[deprecated("use removeAt")]] void removeItem(int index, - void *caller = nullptr) - { - this->removeAt(index, caller); - } - [[deprecated]] std::vector cloneVector() { return *this->readOnly(); diff --git a/src/common/SignalVectorModel.hpp b/src/common/SignalVectorModel.hpp index a13bd97f0..76bc14b22 100644 --- a/src/common/SignalVectorModel.hpp +++ b/src/common/SignalVectorModel.hpp @@ -153,12 +153,12 @@ public: else { int vecRow = this->getVectorIndexFromModelIndex(row); - this->vector_->removeItem(vecRow, this); + this->vector_->removeAt(vecRow, this); assert(this->rows_[row].original); TVectorItem item = this->getItemFromRow( this->rows_[row].items, this->rows_[row].original.get()); - this->vector_->insertItem(item, vecRow, this); + this->vector_->insert(item, vecRow, this); } return true; @@ -225,7 +225,7 @@ public: void deleteRow(int row) { int signalVectorRow = this->getVectorIndexFromModelIndex(row); - this->vector_->removeItem(signalVectorRow); + this->vector_->removeAt(signalVectorRow); } bool removeRows(int row, int count, const QModelIndex &parent) override @@ -240,7 +240,7 @@ public: assert(row >= 0 && row < this->rows_.size()); int signalVectorRow = this->getVectorIndexFromModelIndex(row); - this->vector_->removeItem(signalVectorRow); + this->vector_->removeAt(signalVectorRow); return true; } @@ -287,8 +287,8 @@ public: if (from != to) { auto item = this->vector_->raw()[from]; - this->vector_->removeItem(from); - this->vector_->insertItem(item, to); + this->vector_->removeAt(from); + this->vector_->insert(item, to); } // We return false since we remove items ourselves. diff --git a/src/controllers/accounts/AccountController.cpp b/src/controllers/accounts/AccountController.cpp index 6d272cf0c..d44e00d5b 100644 --- a/src/controllers/accounts/AccountController.cpp +++ b/src/controllers/accounts/AccountController.cpp @@ -9,8 +9,7 @@ namespace chatterino { AccountController::AccountController() { this->twitch.accounts.itemInserted.connect([this](const auto &args) { - this->accounts_.insertItem( - std::dynamic_pointer_cast(args.item)); + this->accounts_.insert(std::dynamic_pointer_cast(args.item)); }); this->twitch.accounts.itemRemoved.connect([this](const auto &args) { @@ -20,7 +19,7 @@ AccountController::AccountController() auto it = std::find(accs.begin(), accs.end(), args.item); assert(it != accs.end()); - this->accounts_.removeItem(it - accs.begin(), this); + this->accounts_.removeAt(it - accs.begin(), this); } }); diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 418612be7..92d89e5c2 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -222,7 +222,7 @@ void CommandController::initialize(Settings &, Paths &paths) // of commands) for (const auto &command : this->commandsSetting_->getValue()) { - this->items_.appendItem(command); + this->items_.append(command); } } diff --git a/src/controllers/ignores/IgnoreController.cpp b/src/controllers/ignores/IgnoreController.cpp index 28b511a36..0080af65c 100644 --- a/src/controllers/ignores/IgnoreController.cpp +++ b/src/controllers/ignores/IgnoreController.cpp @@ -14,7 +14,7 @@ void IgnoreController::initialize(Settings &, Paths &) for (const IgnorePhrase &phrase : this->ignoresSetting_.getValue()) { - this->phrases.appendItem(phrase); + this->phrases.append(phrase); } this->phrases.delayedItemsChanged.connect([this] { // diff --git a/src/controllers/moderationactions/ModerationActions.cpp b/src/controllers/moderationactions/ModerationActions.cpp index d13a71eab..5d2ed9dc1 100644 --- a/src/controllers/moderationactions/ModerationActions.cpp +++ b/src/controllers/moderationactions/ModerationActions.cpp @@ -23,7 +23,7 @@ void ModerationActions::initialize(Settings &settings, Paths &paths) for (auto &val : this->setting_->getValue()) { - this->items.insertItem(val); + this->items.insert(val); } this->items.delayedItemsChanged.connect([this] { // diff --git a/src/controllers/notifications/NotificationController.cpp b/src/controllers/notifications/NotificationController.cpp index 59fed3b1f..72d7b0124 100644 --- a/src/controllers/notifications/NotificationController.cpp +++ b/src/controllers/notifications/NotificationController.cpp @@ -26,12 +26,11 @@ void NotificationController::initialize(Settings &settings, Paths &paths) this->initialized_ = true; for (const QString &channelName : this->twitchSetting_.getValue()) { - this->channelMap[Platform::Twitch].appendItem(channelName); + this->channelMap[Platform::Twitch].append(channelName); } this->channelMap[Platform::Twitch].delayedItemsChanged.connect([this] { // - this->twitchSetting_.setValue( - this->channelMap[Platform::Twitch].raw()); + this->twitchSetting_.setValue(this->channelMap[Platform::Twitch].raw()); }); /* for (const QString &channelName : this->mixerSetting_.getValue()) { @@ -81,18 +80,18 @@ bool NotificationController::isChannelNotified(const QString &channelName, void NotificationController::addChannelNotification(const QString &channelName, Platform p) { - channelMap[p].appendItem(channelName); + channelMap[p].append(channelName); } void NotificationController::removeChannelNotification( const QString &channelName, Platform p) { - for (std::vector::size_type i = 0; - i != channelMap[p].raw().size(); i++) + for (std::vector::size_type i = 0; i != channelMap[p].raw().size(); + i++) { if (channelMap[p].raw()[i].toLower() == channelName.toLower()) { - channelMap[p].removeItem(i); + channelMap[p].removeAt(i); i--; } } diff --git a/src/controllers/pings/PingController.cpp b/src/controllers/pings/PingController.cpp index 36f4b08ad..db245acc2 100644 --- a/src/controllers/pings/PingController.cpp +++ b/src/controllers/pings/PingController.cpp @@ -8,7 +8,7 @@ void PingController::initialize(Settings &settings, Paths &paths) this->initialized_ = true; for (const QString &channelName : this->pingSetting_.getValue()) { - this->channelVector.appendItem(channelName); + this->channelVector.append(channelName); } this->channelVector.delayedItemsChanged.connect([this] { // @@ -37,7 +37,7 @@ bool PingController::isMuted(const QString &channelName) void PingController::muteChannel(const QString &channelName) { - channelVector.appendItem(channelName); + channelVector.append(channelName); } void PingController::unmuteChannel(const QString &channelName) @@ -47,7 +47,7 @@ void PingController::unmuteChannel(const QString &channelName) { if (channelVector.raw()[i].toLower() == channelName.toLower()) { - channelVector.removeItem(i); + channelVector.removeAt(i); i--; } } diff --git a/src/providers/irc/Irc2.cpp b/src/providers/irc/Irc2.cpp index ee59e94ce..2f685be63 100644 --- a/src/providers/irc/Irc2.cpp +++ b/src/providers/irc/Irc2.cpp @@ -252,7 +252,7 @@ void Irc::load() { ids.insert(data.id); - this->connections.appendItem(data); + this->connections.append(data); } } } diff --git a/src/providers/twitch/TwitchAccountManager.cpp b/src/providers/twitch/TwitchAccountManager.cpp index b772f2911..5369e40e4 100644 --- a/src/providers/twitch/TwitchAccountManager.cpp +++ b/src/providers/twitch/TwitchAccountManager.cpp @@ -221,7 +221,7 @@ TwitchAccountManager::AddUserResponse TwitchAccountManager::addUser( // std::lock_guard lock(this->mutex); - this->accounts.insertItem(newUser); + this->accounts.insert(newUser); return AddUserResponse::UserAdded; } diff --git a/src/widgets/dialogs/SelectChannelDialog.cpp b/src/widgets/dialogs/SelectChannelDialog.cpp index c8e21a18b..a17c7022a 100644 --- a/src/widgets/dialogs/SelectChannelDialog.cpp +++ b/src/widgets/dialogs/SelectChannelDialog.cpp @@ -152,7 +152,7 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent) auto editor = new IrcConnectionEditor(unique); if (editor->exec() == QDialog::Accepted) { - Irc::instance().connections.appendItem(editor->data()); + Irc::instance().connections.append(editor->data()); } }); @@ -171,9 +171,9 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent) { if (conn.id == data.id) { - Irc::instance().connections.removeItem( + Irc::instance().connections.removeAt( i, Irc::noEraseCredentialCaller); - Irc::instance().connections.insertItem(data, i); + Irc::instance().connections.insert(data, i); } i++; } diff --git a/src/widgets/dialogs/UserInfoPopup.cpp b/src/widgets/dialogs/UserInfoPopup.cpp index bb75a61fb..e54a4373a 100644 --- a/src/widgets/dialogs/UserInfoPopup.cpp +++ b/src/widgets/dialogs/UserInfoPopup.cpp @@ -336,7 +336,7 @@ void UserInfoPopup::installEvents() if (checked) { - getApp()->highlights->blacklistedUsers.insertItem( + getApp()->highlights->blacklistedUsers.insert( HighlightBlacklistUser{this->userName_, false}); this->ui_.ignoreHighlights->setEnabled(true); } @@ -349,7 +349,7 @@ void UserInfoPopup::installEvents() { if (this->userName_ == vector[i].getPattern()) { - getApp()->highlights->blacklistedUsers.removeItem(i); + getApp()->highlights->blacklistedUsers.removeAt(i); i--; } } diff --git a/src/widgets/settingspages/CommandPage.cpp b/src/widgets/settingspages/CommandPage.cpp index 99b83ec90..7dc72c55f 100644 --- a/src/widgets/settingspages/CommandPage.cpp +++ b/src/widgets/settingspages/CommandPage.cpp @@ -46,7 +46,7 @@ CommandPage::CommandPage() view->setTitles({"Trigger", "Command"}); view->getTableView()->horizontalHeader()->setStretchLastSection(true); view->addButtonPressed.connect([] { - getApp()->commands->items_.appendItem( + getApp()->commands->items_.append( Command{"/command", "I made a new command HeyGuys"}); }); @@ -65,7 +65,7 @@ CommandPage::CommandPage() { if (int index = line.indexOf(' '); index != -1) { - getApp()->commands->items_.insertItem( + getApp()->commands->items_.insert( Command(line.mid(0, index), line.mid(index + 1))); } } diff --git a/src/widgets/settingspages/HighlightingPage.cpp b/src/widgets/settingspages/HighlightingPage.cpp index 9b8d1af94..fb2d7da01 100644 --- a/src/widgets/settingspages/HighlightingPage.cpp +++ b/src/widgets/settingspages/HighlightingPage.cpp @@ -72,7 +72,7 @@ HighlightingPage::HighlightingPage() }); view->addButtonPressed.connect([] { - getApp()->highlights->phrases.appendItem(HighlightPhrase{ + getApp()->highlights->phrases.append(HighlightPhrase{ "my phrase", true, false, false, false, "", *ColorProvider::instance().color( ColorType::SelfHighlight)}); @@ -120,7 +120,7 @@ HighlightingPage::HighlightingPage() }); view->addButtonPressed.connect([] { - getApp()->highlights->highlightedUsers.appendItem( + getApp()->highlights->highlightedUsers.append( HighlightPhrase{"highlighted user", true, false, false, false, "", *ColorProvider::instance().color( @@ -162,7 +162,7 @@ HighlightingPage::HighlightingPage() }); view->addButtonPressed.connect([] { - getApp()->highlights->blacklistedUsers.appendItem( + getApp()->highlights->blacklistedUsers.append( HighlightBlacklistUser{"blacklisted user", false}); }); } diff --git a/src/widgets/settingspages/IgnoresPage.cpp b/src/widgets/settingspages/IgnoresPage.cpp index 9cc657880..773fdea33 100644 --- a/src/widgets/settingspages/IgnoresPage.cpp +++ b/src/widgets/settingspages/IgnoresPage.cpp @@ -60,7 +60,7 @@ void addPhrasesTab(LayoutCreator layout) }); view->addButtonPressed.connect([] { - getApp()->ignores->phrases.appendItem( + getApp()->ignores->phrases.append( IgnorePhrase{"my pattern", false, false, getSettings()->ignoredPhraseReplace.getValue(), true}); }); diff --git a/src/widgets/settingspages/ModerationPage.cpp b/src/widgets/settingspages/ModerationPage.cpp index 479d0a7cb..87e3a5e5d 100644 --- a/src/widgets/settingspages/ModerationPage.cpp +++ b/src/widgets/settingspages/ModerationPage.cpp @@ -184,7 +184,7 @@ ModerationPage::ModerationPage() 0, QHeaderView::Stretch); view->addButtonPressed.connect([] { - getApp()->moderationActions->items.appendItem( + getApp()->moderationActions->items.append( ModerationAction("/timeout {user} 300")); }); diff --git a/src/widgets/settingspages/NotificationPage.cpp b/src/widgets/settingspages/NotificationPage.cpp index 27c1da4b5..b14af27fc 100644 --- a/src/widgets/settingspages/NotificationPage.cpp +++ b/src/widgets/settingspages/NotificationPage.cpp @@ -100,7 +100,7 @@ NotificationPage::NotificationPage() view->addButtonPressed.connect([] { getApp() ->notifications->channelMap[Platform::Twitch] - .appendItem("channel"); + .append("channel"); }); } /* From 7e49371ba16f3fd0707a582684a0e17879a2e8a3 Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 19:44:37 +0100 Subject: [PATCH 044/153] fixed formatting --- src/controllers/pings/PingController.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/pings/PingController.cpp b/src/controllers/pings/PingController.cpp index db245acc2..268dfe18a 100644 --- a/src/controllers/pings/PingController.cpp +++ b/src/controllers/pings/PingController.cpp @@ -42,8 +42,8 @@ void PingController::muteChannel(const QString &channelName) void PingController::unmuteChannel(const QString &channelName) { - for (std::vector::size_type i = 0; - i != channelVector.raw().size(); i++) + for (std::vector::size_type i = 0; i != channelVector.raw().size(); + i++) { if (channelVector.raw()[i].toLower() == channelName.toLower()) { From 89389608e9ce40a3d585d03cc361788fb63192a9 Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 19:49:45 +0100 Subject: [PATCH 045/153] removed compat class for SignalVector --- src/common/SignalVector.hpp | 11 ----------- src/controllers/accounts/AccountController.cpp | 1 + src/controllers/accounts/AccountController.hpp | 3 +-- src/controllers/taggedusers/TaggedUsersController.cpp | 1 + src/controllers/taggedusers/TaggedUsersController.hpp | 2 +- src/providers/twitch/TwitchAccountManager.cpp | 3 ++- src/providers/twitch/TwitchAccountManager.hpp | 4 +--- 7 files changed, 7 insertions(+), 18 deletions(-) diff --git a/src/common/SignalVector.hpp b/src/common/SignalVector.hpp index 074614d88..33bd8254c 100644 --- a/src/common/SignalVector.hpp +++ b/src/common/SignalVector.hpp @@ -171,15 +171,4 @@ private: std::function itemCompare_; }; -// compatability -template -class SortedSignalVector : public SignalVector -{ -public: - SortedSignalVector() - : SignalVector(Compare{}) - { - } -}; - } // namespace chatterino diff --git a/src/controllers/accounts/AccountController.cpp b/src/controllers/accounts/AccountController.cpp index d44e00d5b..2692b05a9 100644 --- a/src/controllers/accounts/AccountController.cpp +++ b/src/controllers/accounts/AccountController.cpp @@ -7,6 +7,7 @@ namespace chatterino { AccountController::AccountController() + : accounts_(SharedPtrElementLess{}) { this->twitch.accounts.itemInserted.connect([this](const auto &args) { this->accounts_.insert(std::dynamic_pointer_cast(args.item)); diff --git a/src/controllers/accounts/AccountController.hpp b/src/controllers/accounts/AccountController.hpp index e297b9e4d..e1ef3c70a 100644 --- a/src/controllers/accounts/AccountController.hpp +++ b/src/controllers/accounts/AccountController.hpp @@ -27,8 +27,7 @@ public: TwitchAccountManager twitch; private: - SortedSignalVector, SharedPtrElementLess> - accounts_; + SignalVector> accounts_; }; } // namespace chatterino diff --git a/src/controllers/taggedusers/TaggedUsersController.cpp b/src/controllers/taggedusers/TaggedUsersController.cpp index 095bd779d..a42265fd4 100644 --- a/src/controllers/taggedusers/TaggedUsersController.cpp +++ b/src/controllers/taggedusers/TaggedUsersController.cpp @@ -5,6 +5,7 @@ namespace chatterino { TaggedUsersController::TaggedUsersController() + : users(std::less{}) { } diff --git a/src/controllers/taggedusers/TaggedUsersController.hpp b/src/controllers/taggedusers/TaggedUsersController.hpp index db9ff2d14..ea917195a 100644 --- a/src/controllers/taggedusers/TaggedUsersController.hpp +++ b/src/controllers/taggedusers/TaggedUsersController.hpp @@ -14,7 +14,7 @@ class TaggedUsersController final : public Singleton public: TaggedUsersController(); - SortedSignalVector> users; + SignalVector users; TaggedUsersModel *createModel(QObject *parent = nullptr); }; diff --git a/src/providers/twitch/TwitchAccountManager.cpp b/src/providers/twitch/TwitchAccountManager.cpp index 5369e40e4..c3b2ee69c 100644 --- a/src/providers/twitch/TwitchAccountManager.cpp +++ b/src/providers/twitch/TwitchAccountManager.cpp @@ -7,7 +7,8 @@ namespace chatterino { TwitchAccountManager::TwitchAccountManager() - : anonymousUser_(new TwitchAccount(ANONYMOUS_USERNAME, "", "", "")) + : accounts(SharedPtrElementLess{}) + , anonymousUser_(new TwitchAccount(ANONYMOUS_USERNAME, "", "", "")) { this->currentUserChanged.connect([this] { auto currentUser = this->getCurrent(); diff --git a/src/providers/twitch/TwitchAccountManager.hpp b/src/providers/twitch/TwitchAccountManager.hpp index c9ce0215e..5956fdf00 100644 --- a/src/providers/twitch/TwitchAccountManager.hpp +++ b/src/providers/twitch/TwitchAccountManager.hpp @@ -51,9 +51,7 @@ public: pajlada::Signals::NoArgSignal currentUserChanged; pajlada::Signals::NoArgSignal userListUpdated; - SortedSignalVector, - SharedPtrElementLess> - accounts; + SignalVector> accounts; private: enum class AddUserResponse { From 8ae9abb2506f46ff1a20fab52b681e21c3301471 Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 20:01:35 +0100 Subject: [PATCH 046/153] refactored IgnoredController --- chatterino.pro | 1 + .../highlights/HighlightController.cpp | 18 +------------ src/controllers/ignores/IgnoreController.cpp | 18 ++----------- src/controllers/ignores/IgnoreController.hpp | 5 ---- src/controllers/ignores/IgnoreModel.hpp | 5 +--- src/util/PersistSignalVector.hpp | 25 +++++++++++++++++++ src/widgets/settingspages/IgnoresPage.cpp | 4 ++- 7 files changed, 33 insertions(+), 43 deletions(-) create mode 100644 src/util/PersistSignalVector.hpp diff --git a/chatterino.pro b/chatterino.pro index 029508395..78a226811 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -434,6 +434,7 @@ HEADERS += \ src/util/LayoutCreator.hpp \ src/util/LayoutHelper.hpp \ src/util/Overloaded.hpp \ + src/util/PersistSignalVector.hpp \ src/util/PostToThread.hpp \ src/util/QObjectRef.hpp \ src/util/QStringHash.hpp \ diff --git a/src/controllers/highlights/HighlightController.cpp b/src/controllers/highlights/HighlightController.cpp index f341f3495..d81ea74e7 100644 --- a/src/controllers/highlights/HighlightController.cpp +++ b/src/controllers/highlights/HighlightController.cpp @@ -1,26 +1,10 @@ #include "HighlightController.hpp" #include "Application.hpp" -#include "common/ChatterinoSetting.hpp" +#include "util/PersistSignalVector.hpp" namespace chatterino { -template -inline void persist(SignalVector &vec, const std::string &name) -{ - auto setting = std::make_unique>>(name); - - for (auto &&item : setting->getValue()) - vec.append(item); - - vec.delayedItemsChanged.connect([setting = setting.get(), vec = &vec] { - setting->setValue(vec->raw()); - }); - - // TODO: Delete when appropriate. - setting.release(); -} - void HighlightController::initialize(Settings &settings, Paths &paths) { assert(!this->initialized_); diff --git a/src/controllers/ignores/IgnoreController.cpp b/src/controllers/ignores/IgnoreController.cpp index 0080af65c..3ce1d562c 100644 --- a/src/controllers/ignores/IgnoreController.cpp +++ b/src/controllers/ignores/IgnoreController.cpp @@ -2,6 +2,7 @@ #include "Application.hpp" #include "controllers/ignores/IgnoreModel.hpp" +#include "util/PersistSignalVector.hpp" #include @@ -12,22 +13,7 @@ void IgnoreController::initialize(Settings &, Paths &) assert(!this->initialized_); this->initialized_ = true; - for (const IgnorePhrase &phrase : this->ignoresSetting_.getValue()) - { - this->phrases.append(phrase); - } - - this->phrases.delayedItemsChanged.connect([this] { // - this->ignoresSetting_.setValue(this->phrases.raw()); - }); -} - -IgnoreModel *IgnoreController::createModel(QObject *parent) -{ - IgnoreModel *model = new IgnoreModel(parent); - model->initialize(&this->phrases); - - return model; + persist(this->phrases, "/ignore/phrases"); } } // namespace chatterino diff --git a/src/controllers/ignores/IgnoreController.hpp b/src/controllers/ignores/IgnoreController.hpp index e3330c33a..979647a0a 100644 --- a/src/controllers/ignores/IgnoreController.hpp +++ b/src/controllers/ignores/IgnoreController.hpp @@ -21,13 +21,8 @@ public: SignalVector phrases; - IgnoreModel *createModel(QObject *parent); - private: bool initialized_ = false; - - ChatterinoSetting> ignoresSetting_ = { - "/ignore/phrases"}; }; } // namespace chatterino diff --git a/src/controllers/ignores/IgnoreModel.hpp b/src/controllers/ignores/IgnoreModel.hpp index 1b9a5099e..473b61b93 100644 --- a/src/controllers/ignores/IgnoreModel.hpp +++ b/src/controllers/ignores/IgnoreModel.hpp @@ -7,10 +7,9 @@ namespace chatterino { -class IgnoreController; - class IgnoreModel : public SignalVectorModel { +public: explicit IgnoreModel(QObject *parent); protected: @@ -21,8 +20,6 @@ protected: // turns a row in the model into a vector item virtual void getRowFromItem(const IgnorePhrase &item, std::vector &row) override; - - friend class IgnoreController; }; } // namespace chatterino diff --git a/src/util/PersistSignalVector.hpp b/src/util/PersistSignalVector.hpp new file mode 100644 index 000000000..b1dde7786 --- /dev/null +++ b/src/util/PersistSignalVector.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include "common/ChatterinoSetting.hpp" +#include "common/SignalVector.hpp" + +namespace chatterino { + +template +inline void persist(SignalVector &vec, const std::string &name) +{ + auto setting = std::make_unique>>(name); + + for (auto &&item : setting->getValue()) + vec.append(item); + + vec.delayedItemsChanged.connect([setting = setting.get(), vec = &vec] { + setting->setValue(vec->raw()); + }); + + // TODO: Delete when appropriate. + setting.release(); +} + +} // namespace chatterino diff --git a/src/widgets/settingspages/IgnoresPage.cpp b/src/widgets/settingspages/IgnoresPage.cpp index 773fdea33..c16b1858f 100644 --- a/src/widgets/settingspages/IgnoresPage.cpp +++ b/src/widgets/settingspages/IgnoresPage.cpp @@ -44,7 +44,9 @@ void addPhrasesTab(LayoutCreator layout) layout.emplace("Ignore messages based certain patterns."); EditableModelView *view = layout - .emplace(getApp()->ignores->createModel(nullptr)) + .emplace( + (new IgnoreModel(nullptr)) + ->initialized(&getApp()->ignores->phrases)) .getElement(); view->setTitles( {"Pattern", "Regex", "Case Sensitive", "Block", "Replacement"}); From df91ea44c4be7c6d229038b7a2777fe2eb15e03c Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 20:10:47 +0100 Subject: [PATCH 047/153] refactored ModeratinActions --- .../ModerationActionModel.hpp | 4 --- .../moderationactions/ModerationActions.cpp | 27 ++----------------- .../moderationactions/ModerationActions.hpp | 11 +------- src/widgets/settingspages/ModerationPage.cpp | 3 ++- 4 files changed, 5 insertions(+), 40 deletions(-) diff --git a/src/controllers/moderationactions/ModerationActionModel.hpp b/src/controllers/moderationactions/ModerationActionModel.hpp index e9f372361..e13b3b27b 100644 --- a/src/controllers/moderationactions/ModerationActionModel.hpp +++ b/src/controllers/moderationactions/ModerationActionModel.hpp @@ -7,8 +7,6 @@ namespace chatterino { -class ModerationActions; - class ModerationActionModel : public SignalVectorModel { public: @@ -25,8 +23,6 @@ protected: std::vector &row) override; friend class HighlightController; - - friend class ModerationActions; }; } // namespace chatterino diff --git a/src/controllers/moderationactions/ModerationActions.cpp b/src/controllers/moderationactions/ModerationActions.cpp index 5d2ed9dc1..6215c81b8 100644 --- a/src/controllers/moderationactions/ModerationActions.cpp +++ b/src/controllers/moderationactions/ModerationActions.cpp @@ -2,41 +2,18 @@ #include "Application.hpp" #include "controllers/moderationactions/ModerationActionModel.hpp" -#include "singletons/Settings.hpp" +#include "util/PersistSignalVector.hpp" #include namespace chatterino { -ModerationActions::ModerationActions() -{ -} - void ModerationActions::initialize(Settings &settings, Paths &paths) { assert(!this->initialized_); this->initialized_ = true; - this->setting_ = - std::make_unique>>( - "/moderation/actions"); - - for (auto &val : this->setting_->getValue()) - { - this->items.insert(val); - } - - this->items.delayedItemsChanged.connect([this] { // - this->setting_->setValue(this->items.raw()); - }); -} - -ModerationActionModel *ModerationActions::createModel(QObject *parent) -{ - ModerationActionModel *model = new ModerationActionModel(parent); - model->initialize(&this->items); - - return model; + persist(this->items, "/moderation/actions"); } } // namespace chatterino diff --git a/src/controllers/moderationactions/ModerationActions.hpp b/src/controllers/moderationactions/ModerationActions.hpp index 7388019ba..8e6a4ded8 100644 --- a/src/controllers/moderationactions/ModerationActions.hpp +++ b/src/controllers/moderationactions/ModerationActions.hpp @@ -1,9 +1,7 @@ #pragma once -#include "common/Singleton.hpp" - -#include "common/ChatterinoSetting.hpp" #include "common/SignalVector.hpp" +#include "common/Singleton.hpp" #include "controllers/moderationactions/ModerationAction.hpp" namespace chatterino { @@ -11,21 +9,14 @@ namespace chatterino { class Settings; class Paths; -class ModerationActionModel; - class ModerationActions final : public Singleton { public: - ModerationActions(); - virtual void initialize(Settings &settings, Paths &paths) override; SignalVector items; - ModerationActionModel *createModel(QObject *parent); - private: - std::unique_ptr>> setting_; bool initialized_ = false; }; diff --git a/src/widgets/settingspages/ModerationPage.cpp b/src/widgets/settingspages/ModerationPage.cpp index 87e3a5e5d..8b1754d86 100644 --- a/src/widgets/settingspages/ModerationPage.cpp +++ b/src/widgets/settingspages/ModerationPage.cpp @@ -174,7 +174,8 @@ ModerationPage::ModerationPage() EditableModelView *view = modMode .emplace( - app->moderationActions->createModel(nullptr)) + (new ModerationActionModel(nullptr)) + ->initialized(&app->moderationActions->items)) .getElement(); view->setTitles({"Actions"}); From d0a81f3fe7f4c2709ebf2d7a6ef752bc90a42531 Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 20:34:00 +0100 Subject: [PATCH 048/153] refactored TaggedUsers and Ping --- chatterino.pro | 8 +-- src/Application.cpp | 4 +- src/Application.hpp | 4 +- .../pings/MutedChannelController.cpp | 57 +++++++++++++++ .../pings/MutedChannelController.hpp | 32 +++++++++ src/controllers/pings/MutedChannelModel.cpp | 28 ++++++++ .../{PingModel.hpp => MutedChannelModel.hpp} | 8 +-- src/controllers/pings/PingController.cpp | 70 ------------------- src/controllers/pings/PingController.hpp | 36 ---------- src/controllers/pings/PingModel.cpp | 28 -------- .../taggedusers/TaggedUsersController.cpp | 8 --- .../taggedusers/TaggedUsersController.hpp | 2 - src/providers/twitch/TwitchMessageBuilder.cpp | 2 +- src/widgets/splits/SplitHeader.cpp | 5 +- 14 files changed, 131 insertions(+), 161 deletions(-) create mode 100644 src/controllers/pings/MutedChannelController.cpp create mode 100644 src/controllers/pings/MutedChannelController.hpp create mode 100644 src/controllers/pings/MutedChannelModel.cpp rename src/controllers/pings/{PingModel.hpp => MutedChannelModel.hpp} (79%) delete mode 100644 src/controllers/pings/PingController.cpp delete mode 100644 src/controllers/pings/PingController.hpp delete mode 100644 src/controllers/pings/PingModel.cpp diff --git a/chatterino.pro b/chatterino.pro index 78a226811..f663b52cf 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -142,8 +142,8 @@ SOURCES += \ src/controllers/moderationactions/ModerationActions.cpp \ src/controllers/notifications/NotificationController.cpp \ src/controllers/notifications/NotificationModel.cpp \ - src/controllers/pings/PingController.cpp \ - src/controllers/pings/PingModel.cpp \ + src/controllers/pings/MutedChannelController.cpp \ + src/controllers/pings/MutedChannelModel.cpp \ src/controllers/taggedusers/TaggedUser.cpp \ src/controllers/taggedusers/TaggedUsersController.cpp \ src/controllers/taggedusers/TaggedUsersModel.cpp \ @@ -338,8 +338,8 @@ HEADERS += \ src/controllers/moderationactions/ModerationActions.hpp \ src/controllers/notifications/NotificationController.hpp \ src/controllers/notifications/NotificationModel.hpp \ - src/controllers/pings/PingController.hpp \ - src/controllers/pings/PingModel.hpp \ + src/controllers/pings/MutedChannelController.hpp \ + src/controllers/pings/MutedChannelModel.hpp \ src/controllers/taggedusers/TaggedUser.hpp \ src/controllers/taggedusers/TaggedUsersController.hpp \ src/controllers/taggedusers/TaggedUsersModel.hpp \ diff --git a/src/Application.cpp b/src/Application.cpp index fe0aab349..da891ab89 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -9,7 +9,7 @@ #include "controllers/ignores/IgnoreController.hpp" #include "controllers/moderationactions/ModerationActions.hpp" #include "controllers/notifications/NotificationController.hpp" -#include "controllers/pings/PingController.hpp" +#include "controllers/pings/MutedChannelController.hpp" #include "controllers/taggedusers/TaggedUsersController.hpp" #include "messages/MessageBuilder.hpp" #include "providers/bttv/BttvEmotes.hpp" @@ -56,7 +56,7 @@ Application::Application(Settings &_settings, Paths &_paths) , commands(&this->emplace()) , highlights(&this->emplace()) , notifications(&this->emplace()) - , pings(&this->emplace()) + , pings(&this->emplace()) , ignores(&this->emplace()) , taggedUsers(&this->emplace()) , moderationActions(&this->emplace()) diff --git a/src/Application.hpp b/src/Application.hpp index fac8ec0f6..87489fdff 100644 --- a/src/Application.hpp +++ b/src/Application.hpp @@ -18,7 +18,7 @@ class TaggedUsersController; class AccountController; class ModerationActions; class NotificationController; -class PingController; +class MutedChannelController; class Theme; class WindowManager; @@ -60,7 +60,7 @@ public: CommandController *const commands{}; HighlightController *const highlights{}; NotificationController *const notifications{}; - PingController *const pings{}; + MutedChannelController *const pings{}; IgnoreController *const ignores{}; TaggedUsersController *const taggedUsers{}; ModerationActions *const moderationActions{}; diff --git a/src/controllers/pings/MutedChannelController.cpp b/src/controllers/pings/MutedChannelController.cpp new file mode 100644 index 000000000..809097ab0 --- /dev/null +++ b/src/controllers/pings/MutedChannelController.cpp @@ -0,0 +1,57 @@ +#include "controllers/pings/MutedChannelController.hpp" +#include "controllers/pings/MutedChannelModel.hpp" +#include "util/PersistSignalVector.hpp" + +namespace chatterino { + +void MutedChannelController::initialize(Settings &settings, Paths &paths) +{ + this->initialized_ = true; + + persist(this->channels, "/pings/muted"); +} + +bool MutedChannelController::isMuted(const QString &channelName) +{ + for (const auto &channel : this->channels) + { + if (channelName.toLower() == channel.toLower()) + { + return true; + } + } + return false; +} + +void MutedChannelController::mute(const QString &channelName) +{ + channels.append(channelName); +} + +void MutedChannelController::unmute(const QString &channelName) +{ + for (std::vector::size_type i = 0; i != channels.raw().size(); i++) + { + if (channels.raw()[i].toLower() == channelName.toLower()) + { + channels.removeAt(i); + i--; + } + } +} + +bool MutedChannelController::toggleMuted(const QString &channelName) +{ + if (this->isMuted(channelName)) + { + unmute(channelName); + return false; + } + else + { + mute(channelName); + return true; + } +} + +} // namespace chatterino diff --git a/src/controllers/pings/MutedChannelController.hpp b/src/controllers/pings/MutedChannelController.hpp new file mode 100644 index 000000000..ff4a6f050 --- /dev/null +++ b/src/controllers/pings/MutedChannelController.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include "common/SignalVector.hpp" +#include "common/Singleton.hpp" +#include "singletons/Settings.hpp" + +namespace chatterino { + +class Settings; +class Paths; + +class MutedChannelModel; + +class MutedChannelController final : public Singleton, private QObject +{ +public: + virtual void initialize(Settings &settings, Paths &paths) override; + + bool isMuted(const QString &channelName); + bool toggleMuted(const QString &channelName); + +private: + void mute(const QString &channelName); + void unmute(const QString &channelName); + bool initialized_ = false; + + SignalVector channels; +}; + +} // namespace chatterino diff --git a/src/controllers/pings/MutedChannelModel.cpp b/src/controllers/pings/MutedChannelModel.cpp new file mode 100644 index 000000000..fc1473506 --- /dev/null +++ b/src/controllers/pings/MutedChannelModel.cpp @@ -0,0 +1,28 @@ +#include "MutedChannelModel.hpp" + +#include "Application.hpp" +#include "singletons/Settings.hpp" +#include "util/StandardItemHelper.hpp" + +namespace chatterino { + +MutedChannelModel::MutedChannelModel(QObject *parent) + : SignalVectorModel(1, parent) +{ +} + +// turn a vector item into a model row +QString MutedChannelModel::getItemFromRow(std::vector &row, + const QString &original) +{ + return QString(row[0]->data(Qt::DisplayRole).toString()); +} + +// turn a model +void MutedChannelModel::getRowFromItem(const QString &item, + std::vector &row) +{ + setStringItem(row[0], item); +} + +} // namespace chatterino diff --git a/src/controllers/pings/PingModel.hpp b/src/controllers/pings/MutedChannelModel.hpp similarity index 79% rename from src/controllers/pings/PingModel.hpp rename to src/controllers/pings/MutedChannelModel.hpp index 137be5e0c..c53b3177c 100644 --- a/src/controllers/pings/PingModel.hpp +++ b/src/controllers/pings/MutedChannelModel.hpp @@ -7,11 +7,11 @@ namespace chatterino { -class PingController; +class MutedChannelController; -class PingModel : public SignalVectorModel +class MutedChannelModel : public SignalVectorModel { - explicit PingModel(QObject *parent); + explicit MutedChannelModel(QObject *parent); protected: // turn a vector item into a model row @@ -21,8 +21,6 @@ protected: // turns a row in the model into a vector item virtual void getRowFromItem(const QString &item, std::vector &row) override; - - friend class PingController; }; } // namespace chatterino diff --git a/src/controllers/pings/PingController.cpp b/src/controllers/pings/PingController.cpp deleted file mode 100644 index 268dfe18a..000000000 --- a/src/controllers/pings/PingController.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "controllers/pings/PingController.hpp" -#include "controllers/pings/PingModel.hpp" - -namespace chatterino { - -void PingController::initialize(Settings &settings, Paths &paths) -{ - this->initialized_ = true; - for (const QString &channelName : this->pingSetting_.getValue()) - { - this->channelVector.append(channelName); - } - - this->channelVector.delayedItemsChanged.connect([this] { // - this->pingSetting_.setValue(this->channelVector.raw()); - }); -} - -PingModel *PingController::createModel(QObject *parent) -{ - PingModel *model = new PingModel(parent); - model->initialize(&this->channelVector); - return model; -} - -bool PingController::isMuted(const QString &channelName) -{ - for (const auto &channel : this->channelVector) - { - if (channelName.toLower() == channel.toLower()) - { - return true; - } - } - return false; -} - -void PingController::muteChannel(const QString &channelName) -{ - channelVector.append(channelName); -} - -void PingController::unmuteChannel(const QString &channelName) -{ - for (std::vector::size_type i = 0; i != channelVector.raw().size(); - i++) - { - if (channelVector.raw()[i].toLower() == channelName.toLower()) - { - channelVector.removeAt(i); - i--; - } - } -} - -bool PingController::toggleMuteChannel(const QString &channelName) -{ - if (this->isMuted(channelName)) - { - unmuteChannel(channelName); - return false; - } - else - { - muteChannel(channelName); - return true; - } -} - -} // namespace chatterino diff --git a/src/controllers/pings/PingController.hpp b/src/controllers/pings/PingController.hpp deleted file mode 100644 index b0b5542b4..000000000 --- a/src/controllers/pings/PingController.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include - -#include "common/SignalVector.hpp" -#include "common/Singleton.hpp" -#include "singletons/Settings.hpp" - -namespace chatterino { - -class Settings; -class Paths; - -class PingModel; - -class PingController final : public Singleton, private QObject -{ -public: - virtual void initialize(Settings &settings, Paths &paths) override; - - bool isMuted(const QString &channelName); - void muteChannel(const QString &channelName); - void unmuteChannel(const QString &channelName); - bool toggleMuteChannel(const QString &channelName); - - PingModel *createModel(QObject *parent); - -private: - bool initialized_ = false; - - SignalVector channelVector; - - ChatterinoSetting> pingSetting_ = {"/pings/muted"}; -}; - -} // namespace chatterino diff --git a/src/controllers/pings/PingModel.cpp b/src/controllers/pings/PingModel.cpp deleted file mode 100644 index 28098a209..000000000 --- a/src/controllers/pings/PingModel.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "PingModel.hpp" - -#include "Application.hpp" -#include "singletons/Settings.hpp" -#include "util/StandardItemHelper.hpp" - -namespace chatterino { - -PingModel::PingModel(QObject *parent) - : SignalVectorModel(1, parent) -{ -} - -// turn a vector item into a model row -QString PingModel::getItemFromRow(std::vector &row, - const QString &original) -{ - return QString(row[0]->data(Qt::DisplayRole).toString()); -} - -// turn a model -void PingModel::getRowFromItem(const QString &item, - std::vector &row) -{ - setStringItem(row[0], item); -} - -} // namespace chatterino diff --git a/src/controllers/taggedusers/TaggedUsersController.cpp b/src/controllers/taggedusers/TaggedUsersController.cpp index a42265fd4..a81b100c3 100644 --- a/src/controllers/taggedusers/TaggedUsersController.cpp +++ b/src/controllers/taggedusers/TaggedUsersController.cpp @@ -9,12 +9,4 @@ TaggedUsersController::TaggedUsersController() { } -TaggedUsersModel *TaggedUsersController::createModel(QObject *parent) -{ - TaggedUsersModel *model = new TaggedUsersModel(parent); - model->initialize(&this->users); - - return model; -} - } // namespace chatterino diff --git a/src/controllers/taggedusers/TaggedUsersController.hpp b/src/controllers/taggedusers/TaggedUsersController.hpp index ea917195a..dd9c4d897 100644 --- a/src/controllers/taggedusers/TaggedUsersController.hpp +++ b/src/controllers/taggedusers/TaggedUsersController.hpp @@ -15,8 +15,6 @@ public: TaggedUsersController(); SignalVector users; - - TaggedUsersModel *createModel(QObject *parent = nullptr); }; } // namespace chatterino diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 2e1d18b4c..09e5f675b 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -4,7 +4,7 @@ #include "controllers/accounts/AccountController.hpp" #include "controllers/highlights/HighlightController.hpp" #include "controllers/ignores/IgnoreController.hpp" -#include "controllers/pings/PingController.hpp" +#include "controllers/pings/MutedChannelController.hpp" #include "messages/Message.hpp" #include "providers/chatterino/ChatterinoBadges.hpp" #include "providers/twitch/TwitchBadges.hpp" diff --git a/src/widgets/splits/SplitHeader.cpp b/src/widgets/splits/SplitHeader.cpp index 79153b44c..84a389aae 100644 --- a/src/widgets/splits/SplitHeader.cpp +++ b/src/widgets/splits/SplitHeader.cpp @@ -4,7 +4,7 @@ #include "controllers/accounts/AccountController.hpp" #include "controllers/moderationactions/ModerationActions.hpp" #include "controllers/notifications/NotificationController.hpp" -#include "controllers/pings/PingController.hpp" +#include "controllers/pings/MutedChannelController.hpp" #include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/TwitchIrcServer.hpp" #include "singletons/Resources.hpp" @@ -343,8 +343,7 @@ std::unique_ptr SplitHeader::createMainMenu() this->split_->getChannel()->getName())); }); action->connect(action, &QAction::triggered, this, [this]() { - getApp()->pings->toggleMuteChannel( - this->split_->getChannel()->getName()); + getApp()->pings->toggleMuted(this->split_->getChannel()->getName()); }); moreMenu->addAction(action); From f8a98501514e4d7dde415e6c3940037c64a309a7 Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 21:18:40 +0100 Subject: [PATCH 049/153] absorbed HighlightController into Application --- chatterino.pro | 2 - src/Application.cpp | 40 +++++++++++++++++-- src/Application.hpp | 14 ++++++- .../highlights/HighlightController.cpp | 40 ------------------- .../highlights/HighlightController.hpp | 26 ------------ src/providers/colors/ColorProvider.cpp | 5 +-- src/providers/twitch/IrcMessageHandler.cpp | 1 - src/providers/twitch/TwitchIrcServer.cpp | 1 - src/providers/twitch/TwitchMessageBuilder.cpp | 7 ++-- src/widgets/dialogs/UserInfoPopup.cpp | 15 ++++--- .../settingspages/HighlightingPage.cpp | 22 +++++----- 11 files changed, 70 insertions(+), 103 deletions(-) delete mode 100644 src/controllers/highlights/HighlightController.cpp delete mode 100644 src/controllers/highlights/HighlightController.hpp diff --git a/chatterino.pro b/chatterino.pro index f663b52cf..d19b4fb83 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -131,7 +131,6 @@ SOURCES += \ src/controllers/commands/CommandController.cpp \ src/controllers/commands/CommandModel.cpp \ src/controllers/highlights/HighlightBlacklistModel.cpp \ - src/controllers/highlights/HighlightController.cpp \ src/controllers/highlights/HighlightModel.cpp \ src/controllers/highlights/HighlightPhrase.cpp \ src/controllers/highlights/UserHighlightModel.cpp \ @@ -326,7 +325,6 @@ HEADERS += \ src/controllers/commands/CommandModel.hpp \ src/controllers/highlights/HighlightBlacklistModel.hpp \ src/controllers/highlights/HighlightBlacklistUser.hpp \ - src/controllers/highlights/HighlightController.hpp \ src/controllers/highlights/HighlightModel.hpp \ src/controllers/highlights/HighlightPhrase.hpp \ src/controllers/highlights/UserHighlightModel.hpp \ diff --git a/src/Application.cpp b/src/Application.cpp index da891ab89..4e0ae212e 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -5,7 +5,8 @@ #include "common/Args.hpp" #include "controllers/accounts/AccountController.hpp" #include "controllers/commands/CommandController.hpp" -#include "controllers/highlights/HighlightController.hpp" +#include "controllers/highlights/HighlightBlacklistUser.hpp" +#include "controllers/highlights/HighlightPhrase.hpp" #include "controllers/ignores/IgnoreController.hpp" #include "controllers/moderationactions/ModerationActions.hpp" #include "controllers/notifications/NotificationController.hpp" @@ -30,6 +31,7 @@ #include "singletons/Updates.hpp" #include "singletons/WindowManager.hpp" #include "util/IsBigEndian.hpp" +#include "util/PersistSignalVector.hpp" #include "util/PostToThread.hpp" #include "widgets/Notebook.hpp" #include "widgets/Window.hpp" @@ -46,7 +48,11 @@ Application *Application::instance = nullptr; // to each other Application::Application(Settings &_settings, Paths &_paths) - : themes(&this->emplace()) + : highlightedMessages(*new SignalVector()) + , highlightedUsers(*new SignalVector()) + , blacklistedUsers(*new SignalVector()) + + , themes(&this->emplace()) , fonts(&this->emplace()) , emotes(&this->emplace()) , windows(&this->emplace()) @@ -54,7 +60,6 @@ Application::Application(Settings &_settings, Paths &_paths) , accounts(&this->emplace()) , commands(&this->emplace()) - , highlights(&this->emplace()) , notifications(&this->emplace()) , pings(&this->emplace()) , ignores(&this->emplace()) @@ -63,10 +68,13 @@ Application::Application(Settings &_settings, Paths &_paths) , twitch2(&this->emplace()) , chatterinoBadges(&this->emplace()) , logging(&this->emplace()) - { this->instance = this; + persist(this->highlightedMessages, "/highlighting/highlights"); + persist(this->blacklistedUsers, "/highlighting/blacklist"); + persist(this->highlightedUsers, "/highlighting/users"); + this->fonts->fontChanged.connect( [this]() { this->windows->layoutChannelViews(); }); @@ -321,4 +329,28 @@ Application *getApp() return Application::instance; } +bool Application::isHighlightedUser(const QString &username) +{ + for (const auto &highlightedUser : this->highlightedUsers) + { + if (highlightedUser.isMatch(username)) + return true; + } + + return false; +} + +bool Application::isBlacklistedUser(const QString &username) +{ + auto items = this->blacklistedUsers.readOnly(); + + for (const auto &blacklistedUser : *items) + { + if (blacklistedUser.isMatch(username)) + return true; + } + + return false; +} + } // namespace chatterino diff --git a/src/Application.hpp b/src/Application.hpp index 87489fdff..966e21ef5 100644 --- a/src/Application.hpp +++ b/src/Application.hpp @@ -3,6 +3,7 @@ #include #include +#include "common/SignalVector.hpp" #include "common/Singleton.hpp" #include "singletons/NativeMessaging.hpp" @@ -31,6 +32,9 @@ class Fonts; class Toasts; class ChatterinoBadges; +class HighlightPhrase; +class HighlightBlacklistUser; + class Application { std::vector> singletons_; @@ -50,6 +54,15 @@ public: friend void test(); + // clang-format off + SignalVector &highlightedMessages; + SignalVector &highlightedUsers; + SignalVector &blacklistedUsers; + // clang-format on + + bool isHighlightedUser(const QString &username); + bool isBlacklistedUser(const QString &username); + Theme *const themes{}; Fonts *const fonts{}; Emotes *const emotes{}; @@ -58,7 +71,6 @@ public: AccountController *const accounts{}; CommandController *const commands{}; - HighlightController *const highlights{}; NotificationController *const notifications{}; MutedChannelController *const pings{}; IgnoreController *const ignores{}; diff --git a/src/controllers/highlights/HighlightController.cpp b/src/controllers/highlights/HighlightController.cpp deleted file mode 100644 index d81ea74e7..000000000 --- a/src/controllers/highlights/HighlightController.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "HighlightController.hpp" - -#include "Application.hpp" -#include "util/PersistSignalVector.hpp" - -namespace chatterino { - -void HighlightController::initialize(Settings &settings, Paths &paths) -{ - assert(!this->initialized_); - this->initialized_ = true; - - persist(this->phrases, "/highlighting/highlights"); - persist(this->blacklistedUsers, "/highlighting/blacklist"); - persist(this->highlightedUsers, "/highlighting/users"); -} - -bool HighlightController::isHighlightedUser(const QString &username) -{ - for (const auto &highlightedUser : this->highlightedUsers) - { - if (highlightedUser.isMatch(username)) - return true; - } - - return false; -} - -bool HighlightController::blacklistContains(const QString &username) -{ - for (const auto &blacklistedUser : *this->blacklistedUsers.readOnly()) - { - if (blacklistedUser.isMatch(username)) - return true; - } - - return false; -} - -} // namespace chatterino diff --git a/src/controllers/highlights/HighlightController.hpp b/src/controllers/highlights/HighlightController.hpp deleted file mode 100644 index 6741e1dbd..000000000 --- a/src/controllers/highlights/HighlightController.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "common/SignalVector.hpp" -#include "common/Singleton.hpp" -#include "controllers/highlights/HighlightBlacklistUser.hpp" -#include "controllers/highlights/HighlightPhrase.hpp" - -namespace chatterino { - -class HighlightController final : public Singleton -{ -public: - virtual void initialize(Settings &settings, Paths &paths) override; - - SignalVector phrases; - SignalVector blacklistedUsers; - SignalVector highlightedUsers; - - bool isHighlightedUser(const QString &username); - bool blacklistContains(const QString &username); - -private: - bool initialized_ = false; -}; - -} // namespace chatterino diff --git a/src/providers/colors/ColorProvider.cpp b/src/providers/colors/ColorProvider.cpp index 492689aa0..ce61a69f6 100644 --- a/src/providers/colors/ColorProvider.cpp +++ b/src/providers/colors/ColorProvider.cpp @@ -1,6 +1,5 @@ #include "providers/colors/ColorProvider.hpp" -#include "controllers/highlights/HighlightController.hpp" #include "singletons/Theme.hpp" namespace chatterino { @@ -38,12 +37,12 @@ QSet ColorProvider::recentColors() const * Currently, only colors used in highlight phrases are considered. This * may change at any point in the future. */ - for (auto phrase : getApp()->highlights->phrases) + for (auto phrase : getApp()->highlightedMessages) { retVal.insert(*phrase.getColor()); } - for (auto userHl : getApp()->highlights->highlightedUsers) + for (auto userHl : getApp()->highlightedUsers) { retVal.insert(*userHl.getColor()); } diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index 5b022bc1e..60d5a5c5a 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -2,7 +2,6 @@ #include "Application.hpp" #include "controllers/accounts/AccountController.hpp" -#include "controllers/highlights/HighlightController.hpp" #include "messages/LimitedQueue.hpp" #include "messages/Message.hpp" #include "providers/twitch/TwitchAccountManager.hpp" diff --git a/src/providers/twitch/TwitchIrcServer.cpp b/src/providers/twitch/TwitchIrcServer.cpp index d13850b46..7ed568c8f 100644 --- a/src/providers/twitch/TwitchIrcServer.cpp +++ b/src/providers/twitch/TwitchIrcServer.cpp @@ -7,7 +7,6 @@ #include "common/Common.hpp" #include "common/Env.hpp" #include "controllers/accounts/AccountController.hpp" -#include "controllers/highlights/HighlightController.hpp" #include "messages/Message.hpp" #include "messages/MessageBuilder.hpp" #include "providers/twitch/IrcMessageHandler.hpp" diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 09e5f675b..8902512d1 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -2,7 +2,6 @@ #include "Application.hpp" #include "controllers/accounts/AccountController.hpp" -#include "controllers/highlights/HighlightController.hpp" #include "controllers/ignores/IgnoreController.hpp" #include "controllers/pings/MutedChannelController.hpp" #include "messages/Message.hpp" @@ -1018,7 +1017,7 @@ void TwitchMessageBuilder::parseHighlights() QString currentUsername = currentUser->getUserName(); - if (app->highlights->blacklistContains(this->ircMessage->nick())) + if (app->isBlacklistedUser(this->ircMessage->nick())) { // Do nothing. We ignore highlights from this user. return; @@ -1058,7 +1057,7 @@ void TwitchMessageBuilder::parseHighlights() } // Highlight because of sender - auto userHighlights = app->highlights->highlightedUsers.readOnly(); + auto userHighlights = app->highlightedUsers.readOnly(); for (const HighlightPhrase &userHighlight : *userHighlights) { if (!userHighlight.isMatch(this->ircMessage->nick())) @@ -1110,7 +1109,7 @@ void TwitchMessageBuilder::parseHighlights() // TODO: This vector should only be rebuilt upon highlights being changed // fourtf: should be implemented in the HighlightsController std::vector activeHighlights = - app->highlights->phrases.cloneVector(); + app->highlightedMessages.cloneVector(); if (getSettings()->enableSelfHighlight && currentUsername.size() > 0) { diff --git a/src/widgets/dialogs/UserInfoPopup.cpp b/src/widgets/dialogs/UserInfoPopup.cpp index e54a4373a..d60414488 100644 --- a/src/widgets/dialogs/UserInfoPopup.cpp +++ b/src/widgets/dialogs/UserInfoPopup.cpp @@ -4,7 +4,7 @@ #include "common/Channel.hpp" #include "common/NetworkRequest.hpp" #include "controllers/accounts/AccountController.hpp" -#include "controllers/highlights/HighlightController.hpp" +#include "controllers/highlights/HighlightBlacklistUser.hpp" #include "providers/twitch/PartialTwitchUser.hpp" #include "providers/twitch/TwitchChannel.hpp" #include "singletons/Resources.hpp" @@ -336,24 +336,23 @@ void UserInfoPopup::installEvents() if (checked) { - getApp()->highlights->blacklistedUsers.insert( + getApp()->blacklistedUsers.insert( HighlightBlacklistUser{this->userName_, false}); this->ui_.ignoreHighlights->setEnabled(true); } else { - const auto &vector = - getApp()->highlights->blacklistedUsers.raw(); + const auto &vector = getApp()->blacklistedUsers.raw(); for (int i = 0; i < vector.size(); i++) { if (this->userName_ == vector[i].getPattern()) { - getApp()->highlights->blacklistedUsers.removeAt(i); + getApp()->blacklistedUsers.removeAt(i); i--; } } - if (getApp()->highlights->blacklistContains(this->userName_)) + if (getApp()->isBlacklistedUser(this->userName_)) { this->ui_.ignoreHighlights->setToolTip( "Name matched by regex"); @@ -456,7 +455,7 @@ void UserInfoPopup::updateUserData() // get ignoreHighlights state bool isIgnoringHighlights = false; - const auto &vector = getApp()->highlights->blacklistedUsers.raw(); + const auto &vector = getApp()->blacklistedUsers.raw(); for (int i = 0; i < vector.size(); i++) { if (this->userName_ == vector[i].getPattern()) @@ -465,7 +464,7 @@ void UserInfoPopup::updateUserData() break; } } - if (getApp()->highlights->blacklistContains(this->userName_) && + if (getApp()->isBlacklistedUser(this->userName_) && !isIgnoringHighlights) { this->ui_.ignoreHighlights->setToolTip("Name matched by regex"); diff --git a/src/widgets/settingspages/HighlightingPage.cpp b/src/widgets/settingspages/HighlightingPage.cpp index fb2d7da01..5013bc30e 100644 --- a/src/widgets/settingspages/HighlightingPage.cpp +++ b/src/widgets/settingspages/HighlightingPage.cpp @@ -2,7 +2,6 @@ #include "Application.hpp" #include "controllers/highlights/HighlightBlacklistModel.hpp" -#include "controllers/highlights/HighlightController.hpp" #include "controllers/highlights/HighlightModel.hpp" #include "controllers/highlights/UserHighlightModel.hpp" #include "singletons/Settings.hpp" @@ -53,7 +52,7 @@ HighlightingPage::HighlightingPage() highlights .emplace( (new HighlightModel(nullptr)) - ->initialized(&app->highlights->phrases)) + ->initialized(&app->highlightedMessages)) .getElement(); view->addRegexHelpLink(); view->setTitles({"Pattern", "Flash\ntaskbar", "Play\nsound", @@ -72,7 +71,7 @@ HighlightingPage::HighlightingPage() }); view->addButtonPressed.connect([] { - getApp()->highlights->phrases.append(HighlightPhrase{ + getApp()->highlightedMessages.append(HighlightPhrase{ "my phrase", true, false, false, false, "", *ColorProvider::instance().color( ColorType::SelfHighlight)}); @@ -95,8 +94,7 @@ HighlightingPage::HighlightingPage() pingUsers .emplace( (new UserHighlightModel(nullptr)) - ->initialized( - &app->highlights->highlightedUsers)) + ->initialized(&app->highlightedUsers)) .getElement(); view->addRegexHelpLink(); @@ -120,11 +118,10 @@ HighlightingPage::HighlightingPage() }); view->addButtonPressed.connect([] { - getApp()->highlights->highlightedUsers.append( - HighlightPhrase{"highlighted user", true, false, false, - false, "", - *ColorProvider::instance().color( - ColorType::SelfHighlight)}); + getApp()->highlightedUsers.append(HighlightPhrase{ + "highlighted user", true, false, false, false, "", + *ColorProvider::instance().color( + ColorType::SelfHighlight)}); }); QObject::connect(view->getTableView(), &QTableView::clicked, @@ -143,8 +140,7 @@ HighlightingPage::HighlightingPage() disabledUsers .emplace( (new HighlightBlacklistModel(nullptr)) - ->initialized( - &app->highlights->blacklistedUsers)) + ->initialized(&app->blacklistedUsers)) .getElement(); view->addRegexHelpLink(); @@ -162,7 +158,7 @@ HighlightingPage::HighlightingPage() }); view->addButtonPressed.connect([] { - getApp()->highlights->blacklistedUsers.append( + getApp()->blacklistedUsers.append( HighlightBlacklistUser{"blacklisted user", false}); }); } From 5ad427bd61ab5921311e9783558b28a2a208e83f Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 22:15:13 +0100 Subject: [PATCH 050/153] moved some Settings from Application to Settings --- src/Application.cpp | 38 +------------- src/Application.hpp | 13 ----- src/controllers/ignores/IgnoreController.cpp | 2 - src/controllers/ignores/IgnoreController.hpp | 2 - src/providers/colors/ColorProvider.cpp | 4 +- src/providers/twitch/TwitchMessageBuilder.cpp | 11 ++-- src/singletons/Settings.cpp | 52 +++++++++++++++++++ src/singletons/Settings.hpp | 27 +++++++++- src/widgets/dialogs/UserInfoPopup.cpp | 13 ++--- .../settingspages/HighlightingPage.cpp | 13 ++--- src/widgets/settingspages/IgnoresPage.cpp | 4 +- 11 files changed, 102 insertions(+), 77 deletions(-) diff --git a/src/Application.cpp b/src/Application.cpp index 4e0ae212e..62f8f392d 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -5,8 +5,6 @@ #include "common/Args.hpp" #include "controllers/accounts/AccountController.hpp" #include "controllers/commands/CommandController.hpp" -#include "controllers/highlights/HighlightBlacklistUser.hpp" -#include "controllers/highlights/HighlightPhrase.hpp" #include "controllers/ignores/IgnoreController.hpp" #include "controllers/moderationactions/ModerationActions.hpp" #include "controllers/notifications/NotificationController.hpp" @@ -31,7 +29,6 @@ #include "singletons/Updates.hpp" #include "singletons/WindowManager.hpp" #include "util/IsBigEndian.hpp" -#include "util/PersistSignalVector.hpp" #include "util/PostToThread.hpp" #include "widgets/Notebook.hpp" #include "widgets/Window.hpp" @@ -48,11 +45,7 @@ Application *Application::instance = nullptr; // to each other Application::Application(Settings &_settings, Paths &_paths) - : highlightedMessages(*new SignalVector()) - , highlightedUsers(*new SignalVector()) - , blacklistedUsers(*new SignalVector()) - - , themes(&this->emplace()) + : themes(&this->emplace()) , fonts(&this->emplace()) , emotes(&this->emplace()) , windows(&this->emplace()) @@ -62,7 +55,6 @@ Application::Application(Settings &_settings, Paths &_paths) , commands(&this->emplace()) , notifications(&this->emplace()) , pings(&this->emplace()) - , ignores(&this->emplace()) , taggedUsers(&this->emplace()) , moderationActions(&this->emplace()) , twitch2(&this->emplace()) @@ -71,10 +63,6 @@ Application::Application(Settings &_settings, Paths &_paths) { this->instance = this; - persist(this->highlightedMessages, "/highlighting/highlights"); - persist(this->blacklistedUsers, "/highlighting/blacklist"); - persist(this->highlightedUsers, "/highlighting/users"); - this->fonts->fontChanged.connect( [this]() { this->windows->layoutChannelViews(); }); @@ -329,28 +317,4 @@ Application *getApp() return Application::instance; } -bool Application::isHighlightedUser(const QString &username) -{ - for (const auto &highlightedUser : this->highlightedUsers) - { - if (highlightedUser.isMatch(username)) - return true; - } - - return false; -} - -bool Application::isBlacklistedUser(const QString &username) -{ - auto items = this->blacklistedUsers.readOnly(); - - for (const auto &blacklistedUser : *items) - { - if (blacklistedUser.isMatch(username)) - return true; - } - - return false; -} - } // namespace chatterino diff --git a/src/Application.hpp b/src/Application.hpp index 966e21ef5..fa04a34de 100644 --- a/src/Application.hpp +++ b/src/Application.hpp @@ -32,9 +32,6 @@ class Fonts; class Toasts; class ChatterinoBadges; -class HighlightPhrase; -class HighlightBlacklistUser; - class Application { std::vector> singletons_; @@ -54,15 +51,6 @@ public: friend void test(); - // clang-format off - SignalVector &highlightedMessages; - SignalVector &highlightedUsers; - SignalVector &blacklistedUsers; - // clang-format on - - bool isHighlightedUser(const QString &username); - bool isBlacklistedUser(const QString &username); - Theme *const themes{}; Fonts *const fonts{}; Emotes *const emotes{}; @@ -73,7 +61,6 @@ public: CommandController *const commands{}; NotificationController *const notifications{}; MutedChannelController *const pings{}; - IgnoreController *const ignores{}; TaggedUsersController *const taggedUsers{}; ModerationActions *const moderationActions{}; TwitchIrcServer *const twitch2{}; diff --git a/src/controllers/ignores/IgnoreController.cpp b/src/controllers/ignores/IgnoreController.cpp index 3ce1d562c..0415032e1 100644 --- a/src/controllers/ignores/IgnoreController.cpp +++ b/src/controllers/ignores/IgnoreController.cpp @@ -12,8 +12,6 @@ void IgnoreController::initialize(Settings &, Paths &) { assert(!this->initialized_); this->initialized_ = true; - - persist(this->phrases, "/ignore/phrases"); } } // namespace chatterino diff --git a/src/controllers/ignores/IgnoreController.hpp b/src/controllers/ignores/IgnoreController.hpp index 979647a0a..c0b11e829 100644 --- a/src/controllers/ignores/IgnoreController.hpp +++ b/src/controllers/ignores/IgnoreController.hpp @@ -19,8 +19,6 @@ class IgnoreController final : public Singleton public: virtual void initialize(Settings &settings, Paths &paths) override; - SignalVector phrases; - private: bool initialized_ = false; }; diff --git a/src/providers/colors/ColorProvider.cpp b/src/providers/colors/ColorProvider.cpp index ce61a69f6..e1be8d6f6 100644 --- a/src/providers/colors/ColorProvider.cpp +++ b/src/providers/colors/ColorProvider.cpp @@ -37,12 +37,12 @@ QSet ColorProvider::recentColors() const * Currently, only colors used in highlight phrases are considered. This * may change at any point in the future. */ - for (auto phrase : getApp()->highlightedMessages) + for (auto phrase : getSettings()->highlightedMessages) { retVal.insert(*phrase.getColor()); } - for (auto userHl : getApp()->highlightedUsers) + for (auto userHl : getSettings()->highlightedUsers) { retVal.insert(*userHl.getColor()); } diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 8902512d1..730bf3ca3 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -169,7 +169,7 @@ bool TwitchMessageBuilder::isIgnored() const auto app = getApp(); // TODO(pajlada): Do we need to check if the phrase is valid first? - auto phrases = app->ignores->phrases.readOnly(); + auto phrases = getCSettings().ignoredMessages.readOnly(); for (const auto &phrase : *phrases) { if (phrase.isBlock() && phrase.isMatch(this->originalMessage_)) @@ -764,8 +764,7 @@ void TwitchMessageBuilder::appendUsername() void TwitchMessageBuilder::runIgnoreReplaces( std::vector> &twitchEmotes) { - auto app = getApp(); - auto phrases = app->ignores->phrases.readOnly(); + auto phrases = getCSettings().ignoredMessages.readOnly(); auto removeEmotesInRange = [](int pos, int len, std::vector> @@ -1017,7 +1016,7 @@ void TwitchMessageBuilder::parseHighlights() QString currentUsername = currentUser->getUserName(); - if (app->isBlacklistedUser(this->ircMessage->nick())) + if (getCSettings().isBlacklistedUser(this->ircMessage->nick())) { // Do nothing. We ignore highlights from this user. return; @@ -1057,7 +1056,7 @@ void TwitchMessageBuilder::parseHighlights() } // Highlight because of sender - auto userHighlights = app->highlightedUsers.readOnly(); + auto userHighlights = getCSettings().highlightedUsers.readOnly(); for (const HighlightPhrase &userHighlight : *userHighlights) { if (!userHighlight.isMatch(this->ircMessage->nick())) @@ -1109,7 +1108,7 @@ void TwitchMessageBuilder::parseHighlights() // TODO: This vector should only be rebuilt upon highlights being changed // fourtf: should be implemented in the HighlightsController std::vector activeHighlights = - app->highlightedMessages.cloneVector(); + getSettings()->highlightedMessages.cloneVector(); if (getSettings()->enableSelfHighlight && currentUsername.size() > 0) { diff --git a/src/singletons/Settings.cpp b/src/singletons/Settings.cpp index 30ba15ec3..0e42db012 100644 --- a/src/singletons/Settings.cpp +++ b/src/singletons/Settings.cpp @@ -1,19 +1,71 @@ #include "singletons/Settings.hpp" #include "Application.hpp" +#include "controllers/highlights/HighlightBlacklistUser.hpp" +#include "controllers/highlights/HighlightPhrase.hpp" +#include "controllers/ignores/IgnorePhrase.hpp" #include "singletons/Paths.hpp" #include "singletons/Resources.hpp" #include "singletons/WindowManager.hpp" +#include "util/PersistSignalVector.hpp" #include "util/WindowsHelper.hpp" namespace chatterino { +ConcurrentSettings *concurrentInstance_{}; + +ConcurrentSettings::ConcurrentSettings() + : highlightedMessages(*new SignalVector()) + , highlightedUsers(*new SignalVector()) + , blacklistedUsers(*new SignalVector()) + , ignoredMessages(*new SignalVector()) +{ +} + +bool ConcurrentSettings::isHighlightedUser(const QString &username) +{ + for (const auto &highlightedUser : this->highlightedUsers) + { + if (highlightedUser.isMatch(username)) + return true; + } + + return false; +} + +bool ConcurrentSettings::isBlacklistedUser(const QString &username) +{ + auto items = this->blacklistedUsers.readOnly(); + + for (const auto &blacklistedUser : *items) + { + if (blacklistedUser.isMatch(username)) + return true; + } + + return false; +} + +ConcurrentSettings &getCSettings() +{ + // `concurrentInstance_` gets assigned in Settings ctor. + assert(concurrentInstance_); + + return *concurrentInstance_; +} + Settings *Settings::instance_ = nullptr; Settings::Settings(const QString &settingsDirectory) : ABSettings(settingsDirectory) { instance_ = this; + concurrentInstance_ = this; + + persist(this->highlightedMessages, "/highlighting/highlights"); + persist(this->blacklistedUsers, "/highlighting/blacklist"); + persist(this->highlightedUsers, "/highlighting/users"); + persist(this->ignoredMessages, "/ignore/phrases"); #ifdef USEWINSDK this->autorun = isRegisteredForStartup(); diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index f0d1679d4..cc2e4e3c5 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -5,13 +5,38 @@ #include "BaseSettings.hpp" #include "common/Channel.hpp" +#include "common/SignalVector.hpp" #include "controllers/highlights/HighlightPhrase.hpp" #include "controllers/moderationactions/ModerationAction.hpp" #include "singletons/Toasts.hpp" namespace chatterino { -class Settings : public ABSettings +class HighlightPhrase; +class HighlightBlacklistUser; +class IgnorePhrase; + +// Settings which are availlable for reading on all threads. +class ConcurrentSettings +{ +public: + ConcurrentSettings(); + + // clang-format off + SignalVector &highlightedMessages; + SignalVector &highlightedUsers; + SignalVector &blacklistedUsers; + SignalVector &ignoredMessages; + // clang-format on + + bool isHighlightedUser(const QString &username); + bool isBlacklistedUser(const QString &username); +}; + +ConcurrentSettings &getCSettings(); + +// These settings are still accessed concurrently in the code but it is bad practice. +class Settings : public ABSettings, public ConcurrentSettings { static Settings *instance_; diff --git a/src/widgets/dialogs/UserInfoPopup.cpp b/src/widgets/dialogs/UserInfoPopup.cpp index d60414488..3b8307024 100644 --- a/src/widgets/dialogs/UserInfoPopup.cpp +++ b/src/widgets/dialogs/UserInfoPopup.cpp @@ -8,6 +8,7 @@ #include "providers/twitch/PartialTwitchUser.hpp" #include "providers/twitch/TwitchChannel.hpp" #include "singletons/Resources.hpp" +#include "singletons/Settings.hpp" #include "util/LayoutCreator.hpp" #include "util/PostToThread.hpp" #include "widgets/Label.hpp" @@ -336,23 +337,23 @@ void UserInfoPopup::installEvents() if (checked) { - getApp()->blacklistedUsers.insert( + getSettings()->blacklistedUsers.insert( HighlightBlacklistUser{this->userName_, false}); this->ui_.ignoreHighlights->setEnabled(true); } else { - const auto &vector = getApp()->blacklistedUsers.raw(); + const auto &vector = getSettings()->blacklistedUsers.raw(); for (int i = 0; i < vector.size(); i++) { if (this->userName_ == vector[i].getPattern()) { - getApp()->blacklistedUsers.removeAt(i); + getSettings()->blacklistedUsers.removeAt(i); i--; } } - if (getApp()->isBlacklistedUser(this->userName_)) + if (getSettings()->isBlacklistedUser(this->userName_)) { this->ui_.ignoreHighlights->setToolTip( "Name matched by regex"); @@ -455,7 +456,7 @@ void UserInfoPopup::updateUserData() // get ignoreHighlights state bool isIgnoringHighlights = false; - const auto &vector = getApp()->blacklistedUsers.raw(); + const auto &vector = getSettings()->blacklistedUsers.raw(); for (int i = 0; i < vector.size(); i++) { if (this->userName_ == vector[i].getPattern()) @@ -464,7 +465,7 @@ void UserInfoPopup::updateUserData() break; } } - if (getApp()->isBlacklistedUser(this->userName_) && + if (getSettings()->isBlacklistedUser(this->userName_) && !isIgnoringHighlights) { this->ui_.ignoreHighlights->setToolTip("Name matched by regex"); diff --git a/src/widgets/settingspages/HighlightingPage.cpp b/src/widgets/settingspages/HighlightingPage.cpp index 5013bc30e..2ba16f62e 100644 --- a/src/widgets/settingspages/HighlightingPage.cpp +++ b/src/widgets/settingspages/HighlightingPage.cpp @@ -52,7 +52,8 @@ HighlightingPage::HighlightingPage() highlights .emplace( (new HighlightModel(nullptr)) - ->initialized(&app->highlightedMessages)) + ->initialized( + &getSettings()->highlightedMessages)) .getElement(); view->addRegexHelpLink(); view->setTitles({"Pattern", "Flash\ntaskbar", "Play\nsound", @@ -71,7 +72,7 @@ HighlightingPage::HighlightingPage() }); view->addButtonPressed.connect([] { - getApp()->highlightedMessages.append(HighlightPhrase{ + getSettings()->highlightedMessages.append(HighlightPhrase{ "my phrase", true, false, false, false, "", *ColorProvider::instance().color( ColorType::SelfHighlight)}); @@ -94,7 +95,7 @@ HighlightingPage::HighlightingPage() pingUsers .emplace( (new UserHighlightModel(nullptr)) - ->initialized(&app->highlightedUsers)) + ->initialized(&getSettings()->highlightedUsers)) .getElement(); view->addRegexHelpLink(); @@ -118,7 +119,7 @@ HighlightingPage::HighlightingPage() }); view->addButtonPressed.connect([] { - getApp()->highlightedUsers.append(HighlightPhrase{ + getSettings()->highlightedUsers.append(HighlightPhrase{ "highlighted user", true, false, false, false, "", *ColorProvider::instance().color( ColorType::SelfHighlight)}); @@ -140,7 +141,7 @@ HighlightingPage::HighlightingPage() disabledUsers .emplace( (new HighlightBlacklistModel(nullptr)) - ->initialized(&app->blacklistedUsers)) + ->initialized(&getSettings()->blacklistedUsers)) .getElement(); view->addRegexHelpLink(); @@ -158,7 +159,7 @@ HighlightingPage::HighlightingPage() }); view->addButtonPressed.connect([] { - getApp()->blacklistedUsers.append( + getSettings()->blacklistedUsers.append( HighlightBlacklistUser{"blacklisted user", false}); }); } diff --git a/src/widgets/settingspages/IgnoresPage.cpp b/src/widgets/settingspages/IgnoresPage.cpp index c16b1858f..0e3fe2ad8 100644 --- a/src/widgets/settingspages/IgnoresPage.cpp +++ b/src/widgets/settingspages/IgnoresPage.cpp @@ -46,7 +46,7 @@ void addPhrasesTab(LayoutCreator layout) layout .emplace( (new IgnoreModel(nullptr)) - ->initialized(&getApp()->ignores->phrases)) + ->initialized(&getSettings()->ignoredMessages)) .getElement(); view->setTitles( {"Pattern", "Regex", "Case Sensitive", "Block", "Replacement"}); @@ -62,7 +62,7 @@ void addPhrasesTab(LayoutCreator layout) }); view->addButtonPressed.connect([] { - getApp()->ignores->phrases.append( + getSettings()->ignoredMessages.append( IgnorePhrase{"my pattern", false, false, getSettings()->ignoredPhraseReplace.getValue(), true}); }); From e537277fa80692ba28e542681dcfb476086cd06e Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 23:07:28 +0100 Subject: [PATCH 051/153] did some more things and stuff --- chatterino.pro | 4 -- src/Application.cpp | 11 +--- src/Application.hpp | 8 --- .../moderationactions/ModerationAction.cpp | 14 ++++- .../moderationactions/ModerationAction.hpp | 3 +- .../moderationactions/ModerationActions.cpp | 19 ------ .../moderationactions/ModerationActions.hpp | 23 ------- .../pings/MutedChannelController.cpp | 45 -------------- .../pings/MutedChannelController.hpp | 5 -- .../taggedusers/TaggedUsersController.cpp | 12 ---- .../taggedusers/TaggedUsersController.hpp | 20 ------- src/messages/MessageElement.cpp | 5 +- src/providers/twitch/TwitchMessageBuilder.cpp | 2 +- src/singletons/Settings.cpp | 60 +++++++++++++++++-- src/singletons/Settings.hpp | 21 +++++-- src/widgets/settingspages/ModerationPage.cpp | 6 +- src/widgets/splits/SplitHeader.cpp | 14 ++--- 17 files changed, 98 insertions(+), 174 deletions(-) delete mode 100644 src/controllers/moderationactions/ModerationActions.cpp delete mode 100644 src/controllers/moderationactions/ModerationActions.hpp delete mode 100644 src/controllers/taggedusers/TaggedUsersController.cpp delete mode 100644 src/controllers/taggedusers/TaggedUsersController.hpp diff --git a/chatterino.pro b/chatterino.pro index d19b4fb83..755bc589d 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -138,13 +138,11 @@ SOURCES += \ src/controllers/ignores/IgnoreModel.cpp \ src/controllers/moderationactions/ModerationAction.cpp \ src/controllers/moderationactions/ModerationActionModel.cpp \ - src/controllers/moderationactions/ModerationActions.cpp \ src/controllers/notifications/NotificationController.cpp \ src/controllers/notifications/NotificationModel.cpp \ src/controllers/pings/MutedChannelController.cpp \ src/controllers/pings/MutedChannelModel.cpp \ src/controllers/taggedusers/TaggedUser.cpp \ - src/controllers/taggedusers/TaggedUsersController.cpp \ src/controllers/taggedusers/TaggedUsersModel.cpp \ src/debug/Benchmark.cpp \ src/main.cpp \ @@ -333,13 +331,11 @@ HEADERS += \ src/controllers/ignores/IgnorePhrase.hpp \ src/controllers/moderationactions/ModerationAction.hpp \ src/controllers/moderationactions/ModerationActionModel.hpp \ - src/controllers/moderationactions/ModerationActions.hpp \ src/controllers/notifications/NotificationController.hpp \ src/controllers/notifications/NotificationModel.hpp \ src/controllers/pings/MutedChannelController.hpp \ src/controllers/pings/MutedChannelModel.hpp \ src/controllers/taggedusers/TaggedUser.hpp \ - src/controllers/taggedusers/TaggedUsersController.hpp \ src/controllers/taggedusers/TaggedUsersModel.hpp \ src/debug/AssertInGuiThread.hpp \ src/debug/Benchmark.hpp \ diff --git a/src/Application.cpp b/src/Application.cpp index 62f8f392d..8f9425011 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -6,10 +6,7 @@ #include "controllers/accounts/AccountController.hpp" #include "controllers/commands/CommandController.hpp" #include "controllers/ignores/IgnoreController.hpp" -#include "controllers/moderationactions/ModerationActions.hpp" #include "controllers/notifications/NotificationController.hpp" -#include "controllers/pings/MutedChannelController.hpp" -#include "controllers/taggedusers/TaggedUsersController.hpp" #include "messages/MessageBuilder.hpp" #include "providers/bttv/BttvEmotes.hpp" #include "providers/chatterino/ChatterinoBadges.hpp" @@ -54,9 +51,6 @@ Application::Application(Settings &_settings, Paths &_paths) , accounts(&this->emplace()) , commands(&this->emplace()) , notifications(&this->emplace()) - , pings(&this->emplace()) - , taggedUsers(&this->emplace()) - , moderationActions(&this->emplace()) , twitch2(&this->emplace()) , chatterinoBadges(&this->emplace()) , logging(&this->emplace()) @@ -111,9 +105,6 @@ void Application::initialize(Settings &settings, Paths &paths) this->initNm(paths); this->initPubsub(); - - this->moderationActions->items.delayedItemsChanged.connect( - [this] { this->windows->forceLayoutChannelViews(); }); } int Application::run(QApplication &qtApp) @@ -126,6 +117,8 @@ int Application::run(QApplication &qtApp) getSettings()->betaUpdates.connect( [] { Updates::instance().checkForUpdates(); }, false); + getSettings()->moderationActions.delayedItemsChanged.connect( + [this] { this->windows->forceLayoutChannelViews(); }); return qtApp.exec(); } diff --git a/src/Application.hpp b/src/Application.hpp index fa04a34de..0c0e54bcb 100644 --- a/src/Application.hpp +++ b/src/Application.hpp @@ -13,13 +13,8 @@ class TwitchIrcServer; class PubSub; class CommandController; -class HighlightController; -class IgnoreController; -class TaggedUsersController; class AccountController; -class ModerationActions; class NotificationController; -class MutedChannelController; class Theme; class WindowManager; @@ -60,9 +55,6 @@ public: AccountController *const accounts{}; CommandController *const commands{}; NotificationController *const notifications{}; - MutedChannelController *const pings{}; - TaggedUsersController *const taggedUsers{}; - ModerationActions *const moderationActions{}; TwitchIrcServer *const twitch2{}; ChatterinoBadges *const chatterinoBadges{}; diff --git a/src/controllers/moderationactions/ModerationAction.cpp b/src/controllers/moderationactions/ModerationAction.cpp index dce01904c..d94cb234d 100644 --- a/src/controllers/moderationactions/ModerationAction.cpp +++ b/src/controllers/moderationactions/ModerationAction.cpp @@ -71,11 +71,11 @@ ModerationAction::ModerationAction(const QString &action) } else if (action.startsWith("/ban ")) { - this->image_ = Image::fromPixmap(getResources().buttons.ban); + this->imageToLoad_ = 1; } else if (action.startsWith("/delete ")) { - this->image_ = Image::fromPixmap(getResources().buttons.trashCan); + this->imageToLoad_ = 2; } else { @@ -100,6 +100,16 @@ bool ModerationAction::isImage() const const boost::optional &ModerationAction::getImage() const { + assertInGuiThread(); + + if (this->imageToLoad_ != 0) + { + if (this->imageToLoad_ == 1) + this->image_ = Image::fromPixmap(getResources().buttons.ban); + else if (this->imageToLoad_ == 2) + this->image_ = Image::fromPixmap(getResources().buttons.trashCan); + } + return this->image_; } diff --git a/src/controllers/moderationactions/ModerationAction.hpp b/src/controllers/moderationactions/ModerationAction.hpp index eed7579a5..3b4532d91 100644 --- a/src/controllers/moderationactions/ModerationAction.hpp +++ b/src/controllers/moderationactions/ModerationAction.hpp @@ -25,10 +25,11 @@ public: const QString &getAction() const; private: - boost::optional image_; + mutable boost::optional image_; QString line1_; QString line2_; QString action_; + int imageToLoad_{}; }; } // namespace chatterino diff --git a/src/controllers/moderationactions/ModerationActions.cpp b/src/controllers/moderationactions/ModerationActions.cpp deleted file mode 100644 index 6215c81b8..000000000 --- a/src/controllers/moderationactions/ModerationActions.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "ModerationActions.hpp" - -#include "Application.hpp" -#include "controllers/moderationactions/ModerationActionModel.hpp" -#include "util/PersistSignalVector.hpp" - -#include - -namespace chatterino { - -void ModerationActions::initialize(Settings &settings, Paths &paths) -{ - assert(!this->initialized_); - this->initialized_ = true; - - persist(this->items, "/moderation/actions"); -} - -} // namespace chatterino diff --git a/src/controllers/moderationactions/ModerationActions.hpp b/src/controllers/moderationactions/ModerationActions.hpp deleted file mode 100644 index 8e6a4ded8..000000000 --- a/src/controllers/moderationactions/ModerationActions.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "common/SignalVector.hpp" -#include "common/Singleton.hpp" -#include "controllers/moderationactions/ModerationAction.hpp" - -namespace chatterino { - -class Settings; -class Paths; - -class ModerationActions final : public Singleton -{ -public: - virtual void initialize(Settings &settings, Paths &paths) override; - - SignalVector items; - -private: - bool initialized_ = false; -}; - -} // namespace chatterino diff --git a/src/controllers/pings/MutedChannelController.cpp b/src/controllers/pings/MutedChannelController.cpp index 809097ab0..62555fd5e 100644 --- a/src/controllers/pings/MutedChannelController.cpp +++ b/src/controllers/pings/MutedChannelController.cpp @@ -7,51 +7,6 @@ namespace chatterino { void MutedChannelController::initialize(Settings &settings, Paths &paths) { this->initialized_ = true; - - persist(this->channels, "/pings/muted"); -} - -bool MutedChannelController::isMuted(const QString &channelName) -{ - for (const auto &channel : this->channels) - { - if (channelName.toLower() == channel.toLower()) - { - return true; - } - } - return false; -} - -void MutedChannelController::mute(const QString &channelName) -{ - channels.append(channelName); -} - -void MutedChannelController::unmute(const QString &channelName) -{ - for (std::vector::size_type i = 0; i != channels.raw().size(); i++) - { - if (channels.raw()[i].toLower() == channelName.toLower()) - { - channels.removeAt(i); - i--; - } - } -} - -bool MutedChannelController::toggleMuted(const QString &channelName) -{ - if (this->isMuted(channelName)) - { - unmute(channelName); - return false; - } - else - { - mute(channelName); - return true; - } } } // namespace chatterino diff --git a/src/controllers/pings/MutedChannelController.hpp b/src/controllers/pings/MutedChannelController.hpp index ff4a6f050..ea681fdc9 100644 --- a/src/controllers/pings/MutedChannelController.hpp +++ b/src/controllers/pings/MutedChannelController.hpp @@ -18,12 +18,7 @@ class MutedChannelController final : public Singleton, private QObject public: virtual void initialize(Settings &settings, Paths &paths) override; - bool isMuted(const QString &channelName); - bool toggleMuted(const QString &channelName); - private: - void mute(const QString &channelName); - void unmute(const QString &channelName); bool initialized_ = false; SignalVector channels; diff --git a/src/controllers/taggedusers/TaggedUsersController.cpp b/src/controllers/taggedusers/TaggedUsersController.cpp deleted file mode 100644 index a81b100c3..000000000 --- a/src/controllers/taggedusers/TaggedUsersController.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "TaggedUsersController.hpp" - -#include "controllers/taggedusers/TaggedUsersModel.hpp" - -namespace chatterino { - -TaggedUsersController::TaggedUsersController() - : users(std::less{}) -{ -} - -} // namespace chatterino diff --git a/src/controllers/taggedusers/TaggedUsersController.hpp b/src/controllers/taggedusers/TaggedUsersController.hpp deleted file mode 100644 index dd9c4d897..000000000 --- a/src/controllers/taggedusers/TaggedUsersController.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "common/Singleton.hpp" - -#include "common/SignalVector.hpp" -#include "controllers/taggedusers/TaggedUser.hpp" - -namespace chatterino { - -class TaggedUsersModel; - -class TaggedUsersController final : public Singleton -{ -public: - TaggedUsersController(); - - SignalVector users; -}; - -} // namespace chatterino diff --git a/src/messages/MessageElement.cpp b/src/messages/MessageElement.cpp index 0defed597..3849b8b43 100644 --- a/src/messages/MessageElement.cpp +++ b/src/messages/MessageElement.cpp @@ -1,7 +1,6 @@ #include "messages/MessageElement.hpp" #include "Application.hpp" -#include "controllers/moderationactions/ModerationActions.hpp" #include "debug/Benchmark.hpp" #include "messages/Emote.hpp" #include "messages/layouts/MessageLayoutContainer.hpp" @@ -374,8 +373,8 @@ void TwitchModerationElement::addToContainer(MessageLayoutContainer &container, { QSize size(int(container.getScale() * 16), int(container.getScale() * 16)); - - for (const auto &action : getApp()->moderationActions->items) + auto actions = getCSettings().moderationActions.readOnly(); + for (const auto &action : *actions) { if (auto image = action.getImage()) { diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 730bf3ca3..76c42d662 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -238,7 +238,7 @@ void TwitchMessageBuilder::triggerHighlights() return; } - if (getApp()->pings->isMuted(this->channel->getName())) + if (getCSettings().isMutedChannel(this->channel->getName())) { // Do nothing. Pings are muted in this channel. return; diff --git a/src/singletons/Settings.cpp b/src/singletons/Settings.cpp index 0e42db012..95a121d64 100644 --- a/src/singletons/Settings.cpp +++ b/src/singletons/Settings.cpp @@ -4,6 +4,7 @@ #include "controllers/highlights/HighlightBlacklistUser.hpp" #include "controllers/highlights/HighlightPhrase.hpp" #include "controllers/ignores/IgnorePhrase.hpp" +#include "controllers/pings/MutedChannelController.hpp" #include "singletons/Paths.hpp" #include "singletons/Resources.hpp" #include "singletons/WindowManager.hpp" @@ -15,11 +16,21 @@ namespace chatterino { ConcurrentSettings *concurrentInstance_{}; ConcurrentSettings::ConcurrentSettings() + // NOTE: these do not get deleted : highlightedMessages(*new SignalVector()) , highlightedUsers(*new SignalVector()) , blacklistedUsers(*new SignalVector()) , ignoredMessages(*new SignalVector()) + , mutedChannels(*new SignalVector()) + , moderationActions(*new SignalVector) { + persist(this->highlightedMessages, "/highlighting/highlights"); + persist(this->blacklistedUsers, "/highlighting/blacklist"); + persist(this->highlightedUsers, "/highlighting/users"); + persist(this->ignoredMessages, "/ignore/phrases"); + persist(this->mutedChannels, "/pings/muted"); + // tagged users? + persist(this->moderationActions, "/moderation/actions"); } bool ConcurrentSettings::isHighlightedUser(const QString &username) @@ -46,6 +57,50 @@ bool ConcurrentSettings::isBlacklistedUser(const QString &username) return false; } +bool ConcurrentSettings::isMutedChannel(const QString &channelName) +{ + for (const auto &channel : this->mutedChannels) + { + if (channelName.toLower() == channel.toLower()) + { + return true; + } + } + return false; +} + +void ConcurrentSettings::mute(const QString &channelName) +{ + mutedChannels.append(channelName); +} + +void ConcurrentSettings::unmute(const QString &channelName) +{ + for (std::vector::size_type i = 0; i != mutedChannels.raw().size(); + i++) + { + if (mutedChannels.raw()[i].toLower() == channelName.toLower()) + { + mutedChannels.removeAt(i); + i--; + } + } +} + +bool ConcurrentSettings::toggleMutedChannel(const QString &channelName) +{ + if (this->isMutedChannel(channelName)) + { + unmute(channelName); + return false; + } + else + { + mute(channelName); + return true; + } +} + ConcurrentSettings &getCSettings() { // `concurrentInstance_` gets assigned in Settings ctor. @@ -62,11 +117,6 @@ Settings::Settings(const QString &settingsDirectory) instance_ = this; concurrentInstance_ = this; - persist(this->highlightedMessages, "/highlighting/highlights"); - persist(this->blacklistedUsers, "/highlighting/blacklist"); - persist(this->highlightedUsers, "/highlighting/users"); - persist(this->ignoredMessages, "/ignore/phrases"); - #ifdef USEWINSDK this->autorun = isRegisteredForStartup(); this->autorun.connect( diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index cc2e4e3c5..cb5613efc 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -15,26 +15,35 @@ namespace chatterino { class HighlightPhrase; class HighlightBlacklistUser; class IgnorePhrase; +class TaggedUser; -// Settings which are availlable for reading on all threads. +/// Settings which are availlable for reading on all threads. class ConcurrentSettings { public: ConcurrentSettings(); - // clang-format off - SignalVector &highlightedMessages; - SignalVector &highlightedUsers; + SignalVector &highlightedMessages; + SignalVector &highlightedUsers; SignalVector &blacklistedUsers; - SignalVector &ignoredMessages; - // clang-format on + SignalVector &ignoredMessages; + SignalVector &mutedChannels; + //SignalVector &taggedUsers; + SignalVector &moderationActions; bool isHighlightedUser(const QString &username); bool isBlacklistedUser(const QString &username); + bool isMutedChannel(const QString &channelName); + bool toggleMutedChannel(const QString &channelName); + +private: + void mute(const QString &channelName); + void unmute(const QString &channelName); }; ConcurrentSettings &getCSettings(); +/// Settings which are availlable for reading and writing on the gui thread. // These settings are still accessed concurrently in the code but it is bad practice. class Settings : public ABSettings, public ConcurrentSettings { diff --git a/src/widgets/settingspages/ModerationPage.cpp b/src/widgets/settingspages/ModerationPage.cpp index 8b1754d86..55f282c7a 100644 --- a/src/widgets/settingspages/ModerationPage.cpp +++ b/src/widgets/settingspages/ModerationPage.cpp @@ -2,8 +2,6 @@ #include "Application.hpp" #include "controllers/moderationactions/ModerationActionModel.hpp" -#include "controllers/moderationactions/ModerationActions.hpp" -#include "controllers/taggedusers/TaggedUsersController.hpp" #include "controllers/taggedusers/TaggedUsersModel.hpp" #include "singletons/Logging.hpp" #include "singletons/Paths.hpp" @@ -175,7 +173,7 @@ ModerationPage::ModerationPage() modMode .emplace( (new ModerationActionModel(nullptr)) - ->initialized(&app->moderationActions->items)) + ->initialized(&getSettings()->moderationActions)) .getElement(); view->setTitles({"Actions"}); @@ -185,7 +183,7 @@ ModerationPage::ModerationPage() 0, QHeaderView::Stretch); view->addButtonPressed.connect([] { - getApp()->moderationActions->items.append( + getSettings()->moderationActions.append( ModerationAction("/timeout {user} 300")); }); diff --git a/src/widgets/splits/SplitHeader.cpp b/src/widgets/splits/SplitHeader.cpp index 84a389aae..49a474e35 100644 --- a/src/widgets/splits/SplitHeader.cpp +++ b/src/widgets/splits/SplitHeader.cpp @@ -2,7 +2,6 @@ #include "Application.hpp" #include "controllers/accounts/AccountController.hpp" -#include "controllers/moderationactions/ModerationActions.hpp" #include "controllers/notifications/NotificationController.hpp" #include "controllers/pings/MutedChannelController.hpp" #include "providers/twitch/TwitchChannel.hpp" @@ -189,7 +188,7 @@ void SplitHeader::initializeLayout() switch (button) { case Qt::LeftButton: - if (getApp()->moderationActions->items.empty()) + if (getSettings()->moderationActions.empty()) { getApp()->windows->showSettingsDialog( SettingsDialogPreference:: @@ -233,9 +232,9 @@ void SplitHeader::initializeLayout() }); // update moderation button when items changed - this->managedConnect(getApp()->moderationActions->items.delayedItemsChanged, + this->managedConnect(getSettings()->moderationActions.delayedItemsChanged, [this] { - if (getApp()->moderationActions->items.empty()) + if (getSettings()->moderationActions.empty()) { if (this->split_->getModerationMode()) this->split_->setModerationMode(true); @@ -339,11 +338,12 @@ std::unique_ptr SplitHeader::createMainMenu() action->setCheckable(true); QObject::connect(moreMenu, &QMenu::aboutToShow, this, [action, this]() { - action->setChecked(getApp()->pings->isMuted( + action->setChecked(getSettings()->isMutedChannel( this->split_->getChannel()->getName())); }); action->connect(action, &QAction::triggered, this, [this]() { - getApp()->pings->toggleMuted(this->split_->getChannel()->getName()); + getSettings()->toggleMutedChannel( + this->split_->getChannel()->getName()); }); moreMenu->addAction(action); @@ -567,7 +567,7 @@ void SplitHeader::updateChannelText() void SplitHeader::updateModerationModeIcon() { auto moderationMode = this->split_->getModerationMode() && - !getApp()->moderationActions->items.empty(); + !getSettings()->moderationActions.empty(); this->moderationButton_->setPixmap( moderationMode ? getResources().buttons.modModeEnabled From ba5d8427675bfb962f6cdf5b972c2f7120ff4cd3 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 28 Feb 2020 19:04:25 +0100 Subject: [PATCH 052/153] removed MutedChannelController --- chatterino.pro | 3 --- src/controllers/ignores/IgnoreController.cpp | 17 ------------ src/controllers/ignores/IgnoreController.hpp | 19 ------------- .../pings/MutedChannelController.cpp | 12 --------- .../pings/MutedChannelController.hpp | 27 ------------------- src/providers/twitch/TwitchMessageBuilder.cpp | 2 +- src/singletons/Settings.cpp | 1 - src/widgets/splits/SplitHeader.cpp | 1 - 8 files changed, 1 insertion(+), 81 deletions(-) delete mode 100644 src/controllers/ignores/IgnoreController.cpp delete mode 100644 src/controllers/pings/MutedChannelController.cpp delete mode 100644 src/controllers/pings/MutedChannelController.hpp diff --git a/chatterino.pro b/chatterino.pro index 755bc589d..41327803c 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -134,13 +134,11 @@ SOURCES += \ src/controllers/highlights/HighlightModel.cpp \ src/controllers/highlights/HighlightPhrase.cpp \ src/controllers/highlights/UserHighlightModel.cpp \ - src/controllers/ignores/IgnoreController.cpp \ src/controllers/ignores/IgnoreModel.cpp \ src/controllers/moderationactions/ModerationAction.cpp \ src/controllers/moderationactions/ModerationActionModel.cpp \ src/controllers/notifications/NotificationController.cpp \ src/controllers/notifications/NotificationModel.cpp \ - src/controllers/pings/MutedChannelController.cpp \ src/controllers/pings/MutedChannelModel.cpp \ src/controllers/taggedusers/TaggedUser.cpp \ src/controllers/taggedusers/TaggedUsersModel.cpp \ @@ -333,7 +331,6 @@ HEADERS += \ src/controllers/moderationactions/ModerationActionModel.hpp \ src/controllers/notifications/NotificationController.hpp \ src/controllers/notifications/NotificationModel.hpp \ - src/controllers/pings/MutedChannelController.hpp \ src/controllers/pings/MutedChannelModel.hpp \ src/controllers/taggedusers/TaggedUser.hpp \ src/controllers/taggedusers/TaggedUsersModel.hpp \ diff --git a/src/controllers/ignores/IgnoreController.cpp b/src/controllers/ignores/IgnoreController.cpp deleted file mode 100644 index 0415032e1..000000000 --- a/src/controllers/ignores/IgnoreController.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "controllers/ignores/IgnoreController.hpp" - -#include "Application.hpp" -#include "controllers/ignores/IgnoreModel.hpp" -#include "util/PersistSignalVector.hpp" - -#include - -namespace chatterino { - -void IgnoreController::initialize(Settings &, Paths &) -{ - assert(!this->initialized_); - this->initialized_ = true; -} - -} // namespace chatterino diff --git a/src/controllers/ignores/IgnoreController.hpp b/src/controllers/ignores/IgnoreController.hpp index c0b11e829..fed12f12c 100644 --- a/src/controllers/ignores/IgnoreController.hpp +++ b/src/controllers/ignores/IgnoreController.hpp @@ -1,26 +1,7 @@ #pragma once -#include "common/ChatterinoSetting.hpp" -#include "common/SignalVector.hpp" -#include "common/Singleton.hpp" -#include "controllers/ignores/IgnorePhrase.hpp" - namespace chatterino { -class Settings; -class Paths; - -class IgnoreModel; - enum class ShowIgnoredUsersMessages { Never, IfModerator, IfBroadcaster }; -class IgnoreController final : public Singleton -{ -public: - virtual void initialize(Settings &settings, Paths &paths) override; - -private: - bool initialized_ = false; -}; - } // namespace chatterino diff --git a/src/controllers/pings/MutedChannelController.cpp b/src/controllers/pings/MutedChannelController.cpp deleted file mode 100644 index 62555fd5e..000000000 --- a/src/controllers/pings/MutedChannelController.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "controllers/pings/MutedChannelController.hpp" -#include "controllers/pings/MutedChannelModel.hpp" -#include "util/PersistSignalVector.hpp" - -namespace chatterino { - -void MutedChannelController::initialize(Settings &settings, Paths &paths) -{ - this->initialized_ = true; -} - -} // namespace chatterino diff --git a/src/controllers/pings/MutedChannelController.hpp b/src/controllers/pings/MutedChannelController.hpp deleted file mode 100644 index ea681fdc9..000000000 --- a/src/controllers/pings/MutedChannelController.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include - -#include "common/SignalVector.hpp" -#include "common/Singleton.hpp" -#include "singletons/Settings.hpp" - -namespace chatterino { - -class Settings; -class Paths; - -class MutedChannelModel; - -class MutedChannelController final : public Singleton, private QObject -{ -public: - virtual void initialize(Settings &settings, Paths &paths) override; - -private: - bool initialized_ = false; - - SignalVector channels; -}; - -} // namespace chatterino diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 76c42d662..a5ecaae42 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -3,7 +3,7 @@ #include "Application.hpp" #include "controllers/accounts/AccountController.hpp" #include "controllers/ignores/IgnoreController.hpp" -#include "controllers/pings/MutedChannelController.hpp" +#include "controllers/ignores/IgnorePhrase.hpp" #include "messages/Message.hpp" #include "providers/chatterino/ChatterinoBadges.hpp" #include "providers/twitch/TwitchBadges.hpp" diff --git a/src/singletons/Settings.cpp b/src/singletons/Settings.cpp index 95a121d64..60e38e554 100644 --- a/src/singletons/Settings.cpp +++ b/src/singletons/Settings.cpp @@ -4,7 +4,6 @@ #include "controllers/highlights/HighlightBlacklistUser.hpp" #include "controllers/highlights/HighlightPhrase.hpp" #include "controllers/ignores/IgnorePhrase.hpp" -#include "controllers/pings/MutedChannelController.hpp" #include "singletons/Paths.hpp" #include "singletons/Resources.hpp" #include "singletons/WindowManager.hpp" diff --git a/src/widgets/splits/SplitHeader.cpp b/src/widgets/splits/SplitHeader.cpp index 49a474e35..fb8c024b2 100644 --- a/src/widgets/splits/SplitHeader.cpp +++ b/src/widgets/splits/SplitHeader.cpp @@ -3,7 +3,6 @@ #include "Application.hpp" #include "controllers/accounts/AccountController.hpp" #include "controllers/notifications/NotificationController.hpp" -#include "controllers/pings/MutedChannelController.hpp" #include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/TwitchIrcServer.hpp" #include "singletons/Resources.hpp" From b887f1c90ee2401ded810a082e388dbf6513733c Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 28 Feb 2020 19:05:50 +0100 Subject: [PATCH 053/153] fix crash --- src/widgets/dialogs/SettingsDialog.cpp | 17 +++++++++++------ src/widgets/dialogs/SettingsDialog.hpp | 2 +- src/widgets/helper/SettingsDialogTab.hpp | 2 +- src/widgets/settingspages/GeneralPage.cpp | 3 ++- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index 5fc797805..a9ca54324 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -155,26 +155,26 @@ void SettingsDialog::addTabs() // clang-format off this->addTab([]{return new GeneralPage;}, "General", ":/settings/about.svg"); this->ui_.tabContainer->addSpacing(16); - this->addTab([]{return new AccountsPage;}, "Accounts", ":/settings/accounts.svg"); + this->addTab([]{return new AccountsPage;}, "Accounts", ":/settings/accounts.svg", SettingsTabId::Accounts); this->ui_.tabContainer->addSpacing(16); this->addTab([]{return new CommandPage;}, "Commands", ":/settings/commands.svg"); this->addTab([]{return new HighlightingPage;}, "Highlights", ":/settings/notifications.svg"); this->addTab([]{return new IgnoresPage;}, "Ignores", ":/settings/ignore.svg"); this->ui_.tabContainer->addSpacing(16); this->addTab([]{return new KeyboardSettingsPage;}, "Keybindings", ":/settings/keybinds.svg"); - this->addTab([]{return new ModerationPage;}, "Moderation", ":/settings/moderation.svg"); + this->addTab([]{return new ModerationPage;}, "Moderation", ":/settings/moderation.svg", SettingsTabId::Moderation); this->addTab([]{return new NotificationPage;}, "Notifications", ":/settings/notification2.svg"); this->addTab([]{return new ExternalToolsPage;}, "External tools", ":/settings/externaltools.svg"); this->ui_.tabContainer->addStretch(1); - this->addTab([]{return new AboutPage;}, "About", ":/settings/about.svg", Qt::AlignBottom); + this->addTab([]{return new AboutPage;}, "About", ":/settings/about.svg", SettingsTabId(), Qt::AlignBottom); // clang-format on } void SettingsDialog::addTab(std::function page, const QString &name, const QString &iconPath, - Qt::Alignment alignment) + SettingsTabId id, Qt::Alignment alignment) { - auto tab = new SettingsDialogTab(this, std::move(page), name, iconPath); + auto tab = new SettingsDialogTab(this, std::move(page), name, iconPath, id); this->ui_.tabContainer->addWidget(tab, 0, alignment); this->tabs_.push_back(tab); @@ -216,7 +216,12 @@ void SettingsDialog::selectTab(SettingsDialogTab *tab, bool byUser) void SettingsDialog::selectTab(SettingsTabId id) { - this->selectTab(this->tab(id)); + auto t = this->tab(id); + assert(t); + if (!t) + return; + + this->selectTab(t); } SettingsDialogTab *SettingsDialog::tab(SettingsTabId id) diff --git a/src/widgets/dialogs/SettingsDialog.hpp b/src/widgets/dialogs/SettingsDialog.hpp index 84cdb98de..5dbef8b60 100644 --- a/src/widgets/dialogs/SettingsDialog.hpp +++ b/src/widgets/dialogs/SettingsDialog.hpp @@ -51,7 +51,7 @@ private: void initUi(); void addTabs(); void addTab(std::function page, const QString &name, - const QString &iconPath, + const QString &iconPath, SettingsTabId id = {}, Qt::Alignment alignment = Qt::AlignTop); void selectTab(SettingsDialogTab *tab, const bool byUser = true); void selectTab(SettingsTabId id); diff --git a/src/widgets/helper/SettingsDialogTab.hpp b/src/widgets/helper/SettingsDialogTab.hpp index 49dc9ff23..97c17f93b 100644 --- a/src/widgets/helper/SettingsDialogTab.hpp +++ b/src/widgets/helper/SettingsDialogTab.hpp @@ -26,7 +26,7 @@ public: SettingsDialogTab(SettingsDialog *dialog_, std::function page_, const QString &name, QString imageFileName, - SettingsTabId id = {}); + SettingsTabId id); void setSelected(bool selected_); SettingsPage *page(); diff --git a/src/widgets/settingspages/GeneralPage.cpp b/src/widgets/settingspages/GeneralPage.cpp index 086b7a6ad..d891d0859 100644 --- a/src/widgets/settingspages/GeneralPage.cpp +++ b/src/widgets/settingspages/GeneralPage.cpp @@ -328,7 +328,8 @@ void GeneralPage::initLayout(SettingsLayout &layout) layout.addCheckbox("Smooth scrolling on new messages", s.enableSmoothScrollingNewMessages); layout.addDropdown( - "Pause on hover", {"Disabled", "0.5s", "1s", "2s", "5s", "Indefinite"}, + "Pause after hover", + {"Disabled", "0.5s", "1s", "2s", "5s", "Indefinite"}, s.pauseOnHoverDuration, [](auto val) { if (val < -0.5f) From d83b597c75efb68aa7864219550b101bb737e074 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 28 Feb 2020 19:12:22 +0100 Subject: [PATCH 054/153] removed unused function in SettingsDialog --- src/widgets/dialogs/SettingsDialog.cpp | 7 ------- src/widgets/dialogs/SettingsDialog.hpp | 5 +---- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index a9ca54324..7effdb892 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -20,8 +20,6 @@ namespace chatterino { -SettingsDialog *SettingsDialog::instance_ = nullptr; - SettingsDialog::SettingsDialog() : BaseWindow(BaseWindow::DisableCustomScaling) { @@ -138,11 +136,6 @@ void SettingsDialog::filterElements(const QString &text) } } -SettingsDialog *SettingsDialog::instance() -{ - return SettingsDialog::instance_; -} - void SettingsDialog::addTabs() { this->ui_.tabContainer->setMargin(0); diff --git a/src/widgets/dialogs/SettingsDialog.hpp b/src/widgets/dialogs/SettingsDialog.hpp index 5dbef8b60..0d08f7c9f 100644 --- a/src/widgets/dialogs/SettingsDialog.hpp +++ b/src/widgets/dialogs/SettingsDialog.hpp @@ -34,7 +34,6 @@ class SettingsDialog : public BaseWindow public: SettingsDialog(); - static SettingsDialog *instance(); // may be NULL static void showDialog(SettingsDialogPreference preferredTab = SettingsDialogPreference::NoPreference); @@ -44,18 +43,16 @@ protected: virtual void showEvent(QShowEvent *) override; private: - static SettingsDialog *instance_; - void refresh(); void initUi(); + SettingsDialogTab *tab(SettingsTabId id); void addTabs(); void addTab(std::function page, const QString &name, const QString &iconPath, SettingsTabId id = {}, Qt::Alignment alignment = Qt::AlignTop); void selectTab(SettingsDialogTab *tab, const bool byUser = true); void selectTab(SettingsTabId id); - SettingsDialogTab *tab(SettingsTabId id); void filterElements(const QString &query); void onOkClicked(); From 269d2ee67f40dca53696f90ba40cb9f6d7396d16 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 28 Feb 2020 19:48:04 +0100 Subject: [PATCH 055/153] improved user and settings icon --- src/widgets/helper/Button.cpp | 1 - src/widgets/helper/NotebookButton.cpp | 20 +++++++++----------- src/widgets/helper/TitlebarButton.cpp | 2 -- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/widgets/helper/Button.cpp b/src/widgets/helper/Button.cpp index fe00b951d..cba385700 100644 --- a/src/widgets/helper/Button.cpp +++ b/src/widgets/helper/Button.cpp @@ -154,7 +154,6 @@ void Button::fancyPaint(QPainter &painter) return; } - painter.setRenderHint(QPainter::HighQualityAntialiasing); painter.setRenderHint(QPainter::Antialiasing); QColor c; diff --git a/src/widgets/helper/NotebookButton.cpp b/src/widgets/helper/NotebookButton.cpp index 3b76adcaa..57f99c2e7 100644 --- a/src/widgets/helper/NotebookButton.cpp +++ b/src/widgets/helper/NotebookButton.cpp @@ -91,7 +91,6 @@ void NotebookButton::paintEvent(QPaintEvent *event) case User: { painter.setRenderHint(QPainter::Antialiasing); - painter.setRenderHint(QPainter::HighQualityAntialiasing); auto a = w / 8; QPainterPath path; @@ -99,19 +98,18 @@ void NotebookButton::paintEvent(QPaintEvent *event) path.arcMoveTo(a, 4 * a, 6 * a, 6 * a, 0); path.arcTo(a, 4 * a, 6 * a, 6 * a, 0, 180); + QPainterPath remove; + remove.addEllipse(2 * a, 1 * a, 4 * a, 4 * a); + path = path.subtracted(remove); + + path.addEllipse(2.5 * a, 1.5 * a, 3 * a + 1, 3 * a); + painter.fillPath(path, foreground); - - painter.setBrush(background); - painter.drawEllipse(2 * a, 1 * a, 4 * a, 4 * a); - - painter.setBrush(foreground); - painter.drawEllipse(2.5 * a, 1.5 * a, 3 * a + 1, 3 * a); } break; case Settings: { painter.setRenderHint(QPainter::Antialiasing); - painter.setRenderHint(QPainter::HighQualityAntialiasing); auto a = w / 8; QPainterPath path; @@ -126,10 +124,10 @@ void NotebookButton::paintEvent(QPaintEvent *event) i * (360 / 8.0) + (360 / 32.0), (360 / 32.0)); } - painter.fillPath(path, foreground); + QPainterPath remove; + remove.addEllipse(3 * a, 3 * a, 2 * a, 2 * a); - painter.setBrush(background); - painter.drawEllipse(3 * a, 3 * a, 2 * a, 2 * a); + painter.fillPath(path.subtracted(remove), foreground); } break; diff --git a/src/widgets/helper/TitlebarButton.cpp b/src/widgets/helper/TitlebarButton.cpp index d7fe28577..b6eeb8789 100644 --- a/src/widgets/helper/TitlebarButton.cpp +++ b/src/widgets/helper/TitlebarButton.cpp @@ -67,7 +67,6 @@ void TitleBarButton::paintEvent(QPaintEvent *event) color = "#999"; painter.setRenderHint(QPainter::Antialiasing); - painter.setRenderHint(QPainter::HighQualityAntialiasing); auto a = xD / 3; QPainterPath path; @@ -92,7 +91,6 @@ void TitleBarButton::paintEvent(QPaintEvent *event) case TitleBarButtonStyle::Settings: { color = "#999"; painter.setRenderHint(QPainter::Antialiasing); - painter.setRenderHint(QPainter::HighQualityAntialiasing); painter.save(); painter.translate(3, 3); From 1e1a56bb87ce051e7eb2b7e0539a6d914d292023 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 28 Feb 2020 19:52:15 +0100 Subject: [PATCH 056/153] fixed position of user icon head --- src/widgets/helper/NotebookButton.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/helper/NotebookButton.cpp b/src/widgets/helper/NotebookButton.cpp index 57f99c2e7..cfdf7d03f 100644 --- a/src/widgets/helper/NotebookButton.cpp +++ b/src/widgets/helper/NotebookButton.cpp @@ -102,7 +102,7 @@ void NotebookButton::paintEvent(QPaintEvent *event) remove.addEllipse(2 * a, 1 * a, 4 * a, 4 * a); path = path.subtracted(remove); - path.addEllipse(2.5 * a, 1.5 * a, 3 * a + 1, 3 * a); + path.addEllipse(2.5 * a, 1.5 * a, 3 * a, 3 * a); painter.fillPath(path, foreground); } From a889d8d186f8b28892e133aafb06438865a48893 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 28 Feb 2020 23:33:27 +0100 Subject: [PATCH 057/153] Disabled travis CI for now --- .travis.yml => _.travis.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .travis.yml => _.travis.yml (100%) diff --git a/.travis.yml b/_.travis.yml similarity index 100% rename from .travis.yml rename to _.travis.yml From 82354327e5f7dd075b2aec98c9dbdad9cd7390df Mon Sep 17 00:00:00 2001 From: mmb L Date: Sat, 29 Feb 2020 18:52:49 +0800 Subject: [PATCH 058/153] Add option for username tab completion only with an @ prefix (#1573) * add an additional check for username completion without an @ prefix * Changed setting name * default userCompletionOnlyWithAt to false --- src/common/CompletionModel.cpp | 2 +- src/singletons/Settings.hpp | 2 ++ src/widgets/settingspages/GeneralPage.cpp | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/common/CompletionModel.cpp b/src/common/CompletionModel.cpp index 6cb3bc800..f3d368da8 100644 --- a/src/common/CompletionModel.cpp +++ b/src/common/CompletionModel.cpp @@ -127,7 +127,7 @@ void CompletionModel::refresh(const QString &prefix, bool isFirstWord) TaggedString::Type::Username); } } - else + else if (!getSettings()->userCompletionOnlyWithAt) { for (const auto &name : usernames->subrange(Prefix(usernamePrefix))) diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index cb5613efc..e6d975043 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -134,6 +134,8 @@ public: "/behaviour/autocompletion/smallStreamerLimit", 1000}; BoolSetting prefixOnlyEmoteCompletion = { "/behaviour/autocompletion/prefixOnlyCompletion", true}; + BoolSetting userCompletionOnlyWithAt = { + "/behaviour/autocompletion/userCompletionOnlyWithAt", false}; FloatSetting pauseOnHoverDuration = {"/behaviour/pauseOnHoverDuration", 0}; EnumSetting pauseChatModifier = { diff --git a/src/widgets/settingspages/GeneralPage.cpp b/src/widgets/settingspages/GeneralPage.cpp index d891d0859..d0cf636eb 100644 --- a/src/widgets/settingspages/GeneralPage.cpp +++ b/src/widgets/settingspages/GeneralPage.cpp @@ -525,6 +525,8 @@ void GeneralPage::initLayout(SettingsLayout &layout) layout.addCheckbox( "Only search for emote autocompletion at the start of emote names", s.prefixOnlyEmoteCompletion); + layout.addCheckbox("Only search for username autocompletion with an @", + s.userCompletionOnlyWithAt); layout.addCheckbox("Show twitch whispers inline", s.inlineWhispers); layout.addCheckbox("Highlight received inline whispers", From 26d3078defe74333a22eb9e7552a8b37d8595396 Mon Sep 17 00:00:00 2001 From: fourtf Date: Sat, 29 Feb 2020 13:56:14 +0100 Subject: [PATCH 059/153] removed unused variables --- src/widgets/helper/ChannelView.hpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/widgets/helper/ChannelView.hpp b/src/widgets/helper/ChannelView.hpp index 308681624..6ea7659e3 100644 --- a/src/widgets/helper/ChannelView.hpp +++ b/src/widgets/helper/ChannelView.hpp @@ -197,13 +197,6 @@ private: LimitedQueue messages_; - pajlada::Signals::Connection messageAppendedConnection_; - pajlada::Signals::Connection messageAddedAtStartConnection_; - pajlada::Signals::Connection messageRemovedConnection_; - pajlada::Signals::Connection messageReplacedConnection_; - pajlada::Signals::Connection repaintGifsConnection_; - pajlada::Signals::Connection layoutConnection_; - std::vector connections_; std::vector channelConnections_; From 20612a1381c690611a8b99a1d3f4fa095b5f0387 Mon Sep 17 00:00:00 2001 From: pajlada Date: Sun, 1 Mar 2020 06:05:08 -0500 Subject: [PATCH 060/153] Fix repeated signal connects on reconnect in irc server (#1576) --- src/providers/irc/AbstractIrcServer.cpp | 22 ++++++ src/providers/irc/AbstractIrcServer.hpp | 13 ++++ src/providers/irc/IrcServer.cpp | 94 +++++++++++++----------- src/providers/irc/IrcServer.hpp | 2 + src/providers/twitch/TwitchIrcServer.cpp | 2 + 5 files changed, 92 insertions(+), 41 deletions(-) diff --git a/src/providers/irc/AbstractIrcServer.cpp b/src/providers/irc/AbstractIrcServer.cpp index 0a3019e5c..2618465a5 100644 --- a/src/providers/irc/AbstractIrcServer.cpp +++ b/src/providers/irc/AbstractIrcServer.cpp @@ -71,8 +71,30 @@ AbstractIrcServer::AbstractIrcServer() }); } +void AbstractIrcServer::initializeIrc() +{ + assert(!this->initialized_); + + if (this->hasSeparateWriteConnection()) + { + this->initializeConnectionSignals(this->writeConnection_.get(), + ConnectionType::Write); + this->initializeConnectionSignals(this->readConnection_.get(), + ConnectionType::Read); + } + else + { + this->initializeConnectionSignals(this->readConnection_.get(), + ConnectionType::Both); + } + + this->initialized_ = true; +} + void AbstractIrcServer::connect() { + assert(this->initialized_); + this->disconnect(); if (this->hasSeparateWriteConnection()) diff --git a/src/providers/irc/AbstractIrcServer.hpp b/src/providers/irc/AbstractIrcServer.hpp index 20d782490..1c865ef52 100644 --- a/src/providers/irc/AbstractIrcServer.hpp +++ b/src/providers/irc/AbstractIrcServer.hpp @@ -21,6 +21,10 @@ public: virtual ~AbstractIrcServer() = default; + // initializeIrc must be called from the derived class + // this allows us to initialize the abstract irc server based on the derived class's parameters + void initializeIrc(); + // connection void connect(); void disconnect(); @@ -45,8 +49,15 @@ public: protected: AbstractIrcServer(); + // initializeConnectionSignals is called on a connection once in its lifetime. + // it can be used to connect signals to your class + virtual void initializeConnectionSignals(IrcConnection *connection, + ConnectionType type){}; + + // initializeConnection is called every time before we try to connect to the irc server virtual void initializeConnection(IrcConnection *connection, ConnectionType type) = 0; + virtual std::shared_ptr createChannel( const QString &channelName) = 0; @@ -83,6 +94,8 @@ private: // bool autoReconnect_ = false; pajlada::Signals::SignalHolder connections_; + + bool initialized_{false}; }; } // namespace chatterino diff --git a/src/providers/irc/IrcServer.cpp b/src/providers/irc/IrcServer.cpp index dd7505ff9..1b3aea5fe 100644 --- a/src/providers/irc/IrcServer.cpp +++ b/src/providers/irc/IrcServer.cpp @@ -15,6 +15,8 @@ namespace chatterino { IrcServer::IrcServer(const IrcServerData &data) : data_(new IrcServerData(data)) { + this->initializeIrc(); + this->connect(); } @@ -51,6 +53,57 @@ const QString &IrcServer::nick() return this->data_->nick.isEmpty() ? this->data_->user : this->data_->nick; } +void IrcServer::initializeConnectionSignals(IrcConnection *connection, + ConnectionType type) +{ + assert(type == Both); + + QObject::connect( + connection, &Communi::IrcConnection::socketError, this, + [this](QAbstractSocket::SocketError error) { + static int index = + QAbstractSocket::staticMetaObject.indexOfEnumerator( + "SocketError"); + + std::lock_guard lock(this->channelMutex); + + for (auto &&weak : this->channels) + { + if (auto shared = weak.lock()) + { + shared->addMessage(makeSystemMessage( + QStringLiteral("Socket error: ") + + QAbstractSocket::staticMetaObject.enumerator(index) + .valueToKey(error))); + } + } + }); + + QObject::connect(connection, &Communi::IrcConnection::nickNameRequired, + this, [](const QString &reserved, QString *result) { + *result = reserved + (std::rand() % 100); + }); + + QObject::connect(connection, &Communi::IrcConnection::noticeMessageReceived, + this, [this](Communi::IrcNoticeMessage *message) { + MessageBuilder builder; + + builder.emplace(); + builder.emplace( + message->nick(), MessageElementFlag::Username); + builder.emplace( + "-> you:", MessageElementFlag::Username); + builder.emplace(message->content(), + MessageElementFlag::Text); + + auto msg = builder.release(); + + for (auto &&weak : this->channels) + if (auto shared = weak.lock()) + shared->addMessage(msg); + }); +} + void IrcServer::initializeConnection(IrcConnection *connection, ConnectionType type) { @@ -90,47 +143,6 @@ void IrcServer::initializeConnection(IrcConnection *connection, this->open(Both); } } - - QObject::connect( - connection, &Communi::IrcConnection::socketError, this, - [this](QAbstractSocket::SocketError error) { - static int index = - QAbstractSocket::staticMetaObject.indexOfEnumerator( - "SocketError"); - - std::lock_guard lock(this->channelMutex); - - for (auto &&weak : this->channels) - if (auto shared = weak.lock()) - shared->addMessage(makeSystemMessage( - QStringLiteral("Socket error: ") + - QAbstractSocket::staticMetaObject.enumerator(index) - .valueToKey(error))); - }); - - QObject::connect(connection, &Communi::IrcConnection::nickNameRequired, - this, [](const QString &reserved, QString *result) { - *result = reserved + (std::rand() % 100); - }); - - QObject::connect(connection, &Communi::IrcConnection::noticeMessageReceived, - this, [this](Communi::IrcNoticeMessage *message) { - MessageBuilder builder; - - builder.emplace(); - builder.emplace( - message->nick(), MessageElementFlag::Username); - builder.emplace( - "-> you:", MessageElementFlag::Username); - builder.emplace(message->content(), - MessageElementFlag::Text); - - auto msg = builder.release(); - - for (auto &&weak : this->channels) - if (auto shared = weak.lock()) - shared->addMessage(msg); - }); } std::shared_ptr IrcServer::createChannel(const QString &channelName) diff --git a/src/providers/irc/IrcServer.hpp b/src/providers/irc/IrcServer.hpp index 0b5a0aad5..6c1251fa8 100644 --- a/src/providers/irc/IrcServer.hpp +++ b/src/providers/irc/IrcServer.hpp @@ -21,6 +21,8 @@ public: // AbstractIrcServer interface protected: + void initializeConnectionSignals(IrcConnection *connection, + ConnectionType type) override; void initializeConnection(IrcConnection *connection, ConnectionType type) override; std::shared_ptr createChannel(const QString &channelName) override; diff --git a/src/providers/twitch/TwitchIrcServer.cpp b/src/providers/twitch/TwitchIrcServer.cpp index 7ed568c8f..0f1d4884d 100644 --- a/src/providers/twitch/TwitchIrcServer.cpp +++ b/src/providers/twitch/TwitchIrcServer.cpp @@ -27,6 +27,8 @@ TwitchIrcServer::TwitchIrcServer() , mentionsChannel(new Channel("/mentions", Channel::Type::TwitchMentions)) , watchingChannel(Channel::getEmpty(), Channel::Type::TwitchWatching) { + this->initializeIrc(); + this->pubsub = new PubSub; // getSettings()->twitchSeperateWriteConnection.connect([this](auto, auto) { From be745862fd6d1a3bcd7abffc34fa9176e256d7af Mon Sep 17 00:00:00 2001 From: Daniel Pasch Date: Sun, 1 Mar 2020 12:20:59 +0100 Subject: [PATCH 061/153] Update contributors list (gempir) (#1583) --- resources/contributors.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/contributors.txt b/resources/contributors.txt index ea0644a7c..cf94931e8 100644 --- a/resources/contributors.txt +++ b/resources/contributors.txt @@ -29,6 +29,7 @@ TranRed | https://github.com/TranRed | | Contributor RAnders00 | https://github.com/RAnders00 | | Contributor YungLPR | https://github.com/leon-richardt | | Contributor Mm2PL | https://github.com/mm2pl | | Contributor +gempir | https://github.com/gempir | | Contributor # If you are a contributor add yourself above this line Defman21 | https://github.com/Defman21 | | Documentation From 2e39dd4d9bc19f46f60d3e1f8224efe26f6fe500 Mon Sep 17 00:00:00 2001 From: pajlada Date: Sun, 1 Mar 2020 08:15:43 -0500 Subject: [PATCH 062/153] macos ci: Replace create-dmg with builddmg (#1587) --- .CI/CreateDMG.sh | 20 ++-- .CI/dmg-settings.py | 247 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 257 insertions(+), 10 deletions(-) create mode 100644 .CI/dmg-settings.py diff --git a/.CI/CreateDMG.sh b/.CI/CreateDMG.sh index 60a8f5341..8ad191e2d 100755 --- a/.CI/CreateDMG.sh +++ b/.CI/CreateDMG.sh @@ -1,13 +1,13 @@ #!/bin/sh echo "Running MACDEPLOYQT" -/usr/local/opt/qt/bin/macdeployqt chatterino.app -dmg -echo "Creating APP folder" -mkdir app -echo "Running hdiutil attach on the built DMG" -hdiutil attach chatterino.dmg -echo "Copying chatterino.app into the app folder" -cp -r /Volumes/chatterino/chatterino.app app/ -echo "Creating DMG with create-dmg" -create-dmg --volname Chatterino2 --volicon ../resources/chatterino.icns --icon-size 50 --app-drop-link 0 0 --format UDBZ chatterino-osx.dmg app/ -echo "DONE" +/usr/local/opt/qt/bin/macdeployqt chatterino.app +echo "Creating python3 virtual environment" +python3 -m venv venv +echo "Entering python3 virtual environment" +. venv/bin/activate +echo "Installing dmgbuild" +python3 -m pip install dmgbuild +echo "Running dmgbuild.." +dmgbuild --settings ./../.CI/dmg-settings.py -D app=./chatterino.app Chatterino2 chatterino-osx.dmg +echo "Done!" diff --git a/.CI/dmg-settings.py b/.CI/dmg-settings.py new file mode 100644 index 000000000..6b068fa1b --- /dev/null +++ b/.CI/dmg-settings.py @@ -0,0 +1,247 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import biplist +import os.path + +# +# Example settings file for dmgbuild +# + +# Use like this: dmgbuild -s settings.py "Test Volume" test.dmg + +# You can actually use this file for your own application (not just TextEdit) +# by doing e.g. +# +# dmgbuild -s settings.py -D app=/path/to/My.app "My Application" MyApp.dmg + +# .. Useful stuff .............................................................. + +application = defines.get('app', '/Applications/TextEdit.app') +appname = os.path.basename(application) + +def icon_from_app(app_path): + plist_path = os.path.join(app_path, 'Contents', 'Info.plist') + plist = biplist.readPlist(plist_path) + icon_name = plist['CFBundleIconFile'] + icon_root,icon_ext = os.path.splitext(icon_name) + if not icon_ext: + icon_ext = '.icns' + icon_name = icon_root + icon_ext + return os.path.join(app_path, 'Contents', 'Resources', icon_name) + +# .. Basics .................................................................... + +# Uncomment to override the output filename +# filename = 'test.dmg' + +# Uncomment to override the output volume name +# volume_name = 'Test' + +# Volume format (see hdiutil create -help) +format = defines.get('format', 'UDBZ') + +# Volume size +size = defines.get('size', None) + +# Files to include +files = [ application ] + +# Symlinks to create +symlinks = { 'Applications': '/Applications' } + +# Volume icon +# +# You can either define icon, in which case that icon file will be copied to the +# image, *or* you can define badge_icon, in which case the icon file you specify +# will be used to badge the system's Removable Disk icon +# +#icon = '/path/to/icon.icns' +badge_icon = icon_from_app(application) + +# Where to put the icons +icon_locations = { + appname: (140, 120), + 'Applications': (500, 120) + } + +# .. Window configuration ...................................................... + +# Background +# +# This is a STRING containing any of the following: +# +# #3344ff - web-style RGB color +# #34f - web-style RGB color, short form (#34f == #3344ff) +# rgb(1,0,0) - RGB color, each value is between 0 and 1 +# hsl(120,1,.5) - HSL (hue saturation lightness) color +# hwb(300,0,0) - HWB (hue whiteness blackness) color +# cmyk(0,1,0,0) - CMYK color +# goldenrod - X11/SVG named color +# builtin-arrow - A simple built-in background with a blue arrow +# /foo/bar/baz.png - The path to an image file +# +# The hue component in hsl() and hwb() may include a unit; it defaults to +# degrees ('deg'), but also supports radians ('rad') and gradians ('grad' +# or 'gon'). +# +# Other color components may be expressed either in the range 0 to 1, or +# as percentages (e.g. 60% is equivalent to 0.6). +background = 'builtin-arrow' + +show_status_bar = False +show_tab_view = False +show_toolbar = False +show_pathbar = False +show_sidebar = False +sidebar_width = 180 + +# Window position in ((x, y), (w, h)) format +window_rect = ((100, 100), (640, 280)) + +# Select the default view; must be one of +# +# 'icon-view' +# 'list-view' +# 'column-view' +# 'coverflow' +# +default_view = 'icon-view' + +# General view configuration +show_icon_preview = False + +# Set these to True to force inclusion of icon/list view settings (otherwise +# we only include settings for the default view) +include_icon_view_settings = 'auto' +include_list_view_settings = 'auto' + +# .. Icon view configuration ................................................... + +arrange_by = None +grid_offset = (0, 0) +grid_spacing = 100 +scroll_position = (0, 0) +label_pos = 'bottom' # or 'right' +text_size = 16 +icon_size = 128 + +# .. List view configuration ................................................... + +# Column names are as follows: +# +# name +# date-modified +# date-created +# date-added +# date-last-opened +# size +# kind +# label +# version +# comments +# +list_icon_size = 16 +list_text_size = 12 +list_scroll_position = (0, 0) +list_sort_by = 'name' +list_use_relative_dates = True +list_calculate_all_sizes = False, +list_columns = ('name', 'date-modified', 'size', 'kind', 'date-added') +list_column_widths = { + 'name': 300, + 'date-modified': 181, + 'date-created': 181, + 'date-added': 181, + 'date-last-opened': 181, + 'size': 97, + 'kind': 115, + 'label': 100, + 'version': 75, + 'comments': 300, + } +list_column_sort_directions = { + 'name': 'ascending', + 'date-modified': 'descending', + 'date-created': 'descending', + 'date-added': 'descending', + 'date-last-opened': 'descending', + 'size': 'descending', + 'kind': 'ascending', + 'label': 'ascending', + 'version': 'ascending', + 'comments': 'ascending', + } + +# .. License configuration ..................................................... + +# Text in the license configuration is stored in the resources, which means +# it gets stored in a legacy Mac encoding according to the language. dmgbuild +# will *try* to convert Unicode strings to the appropriate encoding, *but* +# you should be aware that Python doesn't support all of the necessary encodings; +# in many cases you will need to encode the text yourself and use byte strings +# instead here. + +# Recognized language names are: +# +# af_ZA, ar, be_BY, bg_BG, bn, bo, br, ca_ES, cs_CZ, cy, da_DK, de_AT, de_CH, +# de_DE, dz_BT, el_CY, el_GR, en_AU, en_CA, en_GB, en_IE, en_SG, en_US, eo, +# es_419, es_ES, et_EE, fa_IR, fi_FI, fo_FO, fr_001, fr_BE, fr_CA, fr_CH, +# fr_FR, ga-Latg_IE, ga_IE, gd, grc, gu_IN, gv, he_IL, hi_IN, hr_HR, hu_HU, +# hy_AM, is_IS, it_CH, it_IT, iu_CA, ja_JP, ka_GE, kl, ko_KR, lt_LT, lv_LV, +# mk_MK, mr_IN, mt_MT, nb_NO, ne_NP, nl_BE, nl_NL, nn_NO, pa, pl_PL, pt_BR, +# pt_PT, ro_RO, ru_RU, se, sk_SK, sl_SI, sr_RS, sv_SE, th_TH, to_TO, tr_TR, +# uk_UA, ur_IN, ur_PK, uz_UZ, vi_VN, zh_CN, zh_TW + +# license = { +# 'default-language': 'en_US', +# 'licenses': { +# # For each language, the text of the license. This can be plain text, +# # RTF (in which case it must start "{\rtf1"), or a path to a file +# # containing the license text. If you're using RTF, +# # watch out for Python escaping (or read it from a file). +# 'English': b'''{\\rtf1\\ansi\\ansicpg1252\\cocoartf1504\\cocoasubrtf820 +# {\\fonttbl\\f0\\fnil\\fcharset0 Helvetica-Bold;\\f1\\fnil\\fcharset0 Helvetica;} +# {\\colortbl;\\red255\\green255\\blue255;\\red0\\green0\\blue0;} +# {\\*\\expandedcolortbl;;\\cssrgb\\c0\\c0\\c0;} +# \\paperw11905\\paperh16837\\margl1133\\margr1133\\margb1133\\margt1133 +# \\deftab720 +# \\pard\\pardeftab720\\sa160\\partightenfactor0 + +# \\f0\\b\\fs60 \\cf2 \\expnd0\\expndtw0\\kerning0 +# \\up0 \\nosupersub \\ulnone \\outl0\\strokewidth0 \\strokec2 Test License\\ +# \\pard\\pardeftab720\\sa160\\partightenfactor0 + +# \\fs36 \\cf2 \\strokec2 What is this?\\ +# \\pard\\pardeftab720\\sa160\\partightenfactor0 + +# \\f1\\b0\\fs22 \\cf2 \\strokec2 This is the English license. It says what you are allowed to do with this software.\\ +# \\ +# }''', +# }, +# 'buttons': { +# # For each language, text for the buttons on the licensing window. +# # +# # Default buttons and text are built-in for the following languages: +# # +# # English (en_US), German (de_DE), Spanish (es_ES), French (fr_FR), +# # Italian (it_IT), Japanese (ja_JP), Dutch (nl_NL), Swedish (sv_SE), +# # Brazilian Portuguese (pt_BR), Simplified Chinese (zh_CN), +# # Traditional Chinese (zh_TW), Danish (da_DK), Finnish (fi_FI), +# # Korean (ko_KR), Norwegian (nb_NO) +# # +# # You don't need to specify them for those languages; if you fail to +# # specify them for some other language, English will be used instead. + +# 'en_US': ( +# b'English', +# b'Agree', +# b'Disagree', +# b'Print', +# b'Save', +# b'If you agree with the terms of this license, press "Agree" to ' +# b'install the software. If you do not agree, press "Disagree".' +# ), +# }, +# } + From 9a8b85e338ba4fe3b2bb1eee81f3f6f05387959f Mon Sep 17 00:00:00 2001 From: pajlada Date: Sat, 14 Mar 2020 07:13:57 -0400 Subject: [PATCH 063/153] Twitch API: v5 to Helix migration (#1560) There's a document in src/providers/twitch/api which describes how we interact with the Twitch API. Keeping this up to date might be a healthy way for us to ensure we keep using the right APIs for the right job. --- chatterino.pro | 8 +- .../commands/CommandController.cpp | 36 +- .../notifications/NotificationController.cpp | 106 +++-- .../notifications/NotificationController.hpp | 1 + src/main.cpp | 5 + src/providers/twitch/PartialTwitchUser.cpp | 85 ---- src/providers/twitch/PartialTwitchUser.hpp | 30 -- src/providers/twitch/TwitchAccount.cpp | 52 ++- src/providers/twitch/TwitchAccountManager.cpp | 4 + src/providers/twitch/TwitchApi.cpp | 85 ---- src/providers/twitch/TwitchApi.hpp | 19 - src/providers/twitch/TwitchChannel.cpp | 174 ++++----- src/providers/twitch/TwitchChannel.hpp | 8 +- .../twitch/TwitchParseCheerEmotes.cpp | 1 + src/providers/twitch/api/Helix.cpp | 368 ++++++++++++++++++ src/providers/twitch/api/Helix.hpp | 198 ++++++++++ src/providers/twitch/api/Kraken.cpp | 104 +++++ src/providers/twitch/api/Kraken.hpp | 61 +++ src/providers/twitch/api/README.md | 125 ++++++ src/singletons/Toasts.cpp | 59 +-- src/singletons/Toasts.hpp | 4 - src/widgets/dialogs/LoginDialog.cpp | 21 - src/widgets/dialogs/LoginDialog.hpp | 6 - src/widgets/dialogs/LogsPopup.cpp | 1 - src/widgets/dialogs/UserInfoPopup.cpp | 87 ++--- 25 files changed, 1076 insertions(+), 572 deletions(-) delete mode 100644 src/providers/twitch/PartialTwitchUser.cpp delete mode 100644 src/providers/twitch/PartialTwitchUser.hpp delete mode 100644 src/providers/twitch/TwitchApi.cpp delete mode 100644 src/providers/twitch/TwitchApi.hpp create mode 100644 src/providers/twitch/api/Helix.cpp create mode 100644 src/providers/twitch/api/Helix.hpp create mode 100644 src/providers/twitch/api/Kraken.cpp create mode 100644 src/providers/twitch/api/Kraken.hpp create mode 100644 src/providers/twitch/api/README.md diff --git a/chatterino.pro b/chatterino.pro index 41327803c..6ec03fee5 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -173,14 +173,14 @@ SOURCES += \ src/providers/irc/IrcConnection2.cpp \ src/providers/irc/IrcServer.cpp \ src/providers/LinkResolver.cpp \ + src/providers/twitch/api/Helix.cpp \ + src/providers/twitch/api/Kraken.cpp \ src/providers/twitch/IrcMessageHandler.cpp \ - src/providers/twitch/PartialTwitchUser.cpp \ src/providers/twitch/PubsubActions.cpp \ src/providers/twitch/PubsubClient.cpp \ src/providers/twitch/PubsubHelpers.cpp \ src/providers/twitch/TwitchAccount.cpp \ src/providers/twitch/TwitchAccountManager.cpp \ - src/providers/twitch/TwitchApi.cpp \ src/providers/twitch/TwitchBadge.cpp \ src/providers/twitch/TwitchBadges.cpp \ src/providers/twitch/TwitchChannel.cpp \ @@ -372,15 +372,15 @@ HEADERS += \ src/providers/irc/IrcConnection2.hpp \ src/providers/irc/IrcServer.hpp \ src/providers/LinkResolver.hpp \ + src/providers/twitch/api/Helix.hpp \ + src/providers/twitch/api/Kraken.hpp \ src/providers/twitch/EmoteValue.hpp \ src/providers/twitch/IrcMessageHandler.hpp \ - src/providers/twitch/PartialTwitchUser.hpp \ src/providers/twitch/PubsubActions.hpp \ src/providers/twitch/PubsubClient.hpp \ src/providers/twitch/PubsubHelpers.hpp \ src/providers/twitch/TwitchAccount.hpp \ src/providers/twitch/TwitchAccountManager.hpp \ - src/providers/twitch/TwitchApi.hpp \ src/providers/twitch/TwitchBadge.hpp \ src/providers/twitch/TwitchBadges.hpp \ src/providers/twitch/TwitchChannel.hpp \ diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 92d89e5c2..fa8a08e95 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -8,9 +8,9 @@ #include "messages/Message.hpp" #include "messages/MessageBuilder.hpp" #include "messages/MessageElement.hpp" -#include "providers/twitch/TwitchApi.hpp" #include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/TwitchIrcServer.hpp" +#include "providers/twitch/api/Helix.hpp" #include "singletons/Emotes.hpp" #include "singletons/Paths.hpp" #include "singletons/Settings.hpp" @@ -364,18 +364,17 @@ QString CommandController::execCommand(const QString &textNoEmoji, return ""; } - TwitchApi::findUserId( - target, [user, channel, target](QString userId) { - if (userId.isEmpty()) - { - channel->addMessage(makeSystemMessage( - "User " + target + " could not be followed!")); - return; - } - user->followUser(userId, [channel, target]() { + getHelix()->getUserByName( + target, + [user, channel, target](const auto &targetUser) { + user->followUser(targetUser.id, [channel, target]() { channel->addMessage(makeSystemMessage( "You successfully followed " + target)); }); + }, + [channel, target] { + channel->addMessage(makeSystemMessage( + "User " + target + " could not be followed!")); }); return ""; @@ -400,18 +399,17 @@ QString CommandController::execCommand(const QString &textNoEmoji, return ""; } - TwitchApi::findUserId( - target, [user, channel, target](QString userId) { - if (userId.isEmpty()) - { - channel->addMessage(makeSystemMessage( - "User " + target + " could not be followed!")); - return; - } - user->unfollowUser(userId, [channel, target]() { + getHelix()->getUserByName( + target, + [user, channel, target](const auto &targetUser) { + user->unfollowUser(targetUser.id, [channel, target]() { channel->addMessage(makeSystemMessage( "You successfully unfollowed " + target)); }); + }, + [channel, target] { + channel->addMessage(makeSystemMessage( + "User " + target + " could not be followed!")); }); return ""; diff --git a/src/controllers/notifications/NotificationController.cpp b/src/controllers/notifications/NotificationController.cpp index 72d7b0124..2578558df 100644 --- a/src/controllers/notifications/NotificationController.cpp +++ b/src/controllers/notifications/NotificationController.cpp @@ -4,8 +4,8 @@ #include "common/NetworkRequest.hpp" #include "common/Outcome.hpp" #include "controllers/notifications/NotificationModel.hpp" -#include "providers/twitch/TwitchApi.hpp" #include "providers/twitch/TwitchIrcServer.hpp" +#include "providers/twitch/api/Helix.hpp" #include "singletons/Toasts.hpp" #include "singletons/WindowManager.hpp" #include "widgets/Window.hpp" @@ -142,68 +142,52 @@ void NotificationController::fetchFakeChannels() void NotificationController::getFakeTwitchChannelLiveStatus( const QString &channelName) { - TwitchApi::findUserId(channelName, [channelName, this](QString roomID) { - if (roomID.isEmpty()) - { + getHelix()->getStreamByName( + channelName, + [channelName, this](bool live, const auto &stream) { + qDebug() << "[TwitchChannel" << channelName + << "] Refreshing live status"; + + if (!live) + { + // Stream is offline + this->removeFakeChannel(channelName); + return; + } + + // Stream is online + auto i = std::find(fakeTwitchChannels.begin(), + fakeTwitchChannels.end(), channelName); + + if (i != fakeTwitchChannels.end()) + { + // We have already pushed the live state of this stream + // Could not find stream in fake twitch channels! + return; + } + + if (Toasts::isEnabled()) + { + getApp()->toasts->sendChannelNotification(channelName, + Platform::Twitch); + } + if (getSettings()->notificationPlaySound) + { + getApp()->notifications->playSound(); + } + if (getSettings()->notificationFlashTaskbar) + { + getApp()->windows->sendAlert(); + } + + // Indicate that we have pushed notifications for this stream + fakeTwitchChannels.push_back(channelName); + }, + [channelName, this] { qDebug() << "[TwitchChannel" << channelName << "] Refreshing live status (Missing ID)"; - removeFakeChannel(channelName); - return; - } - qDebug() << "[TwitchChannel" << channelName - << "] Refreshing live status"; - - QString url("https://api.twitch.tv/kraken/streams/" + roomID); - NetworkRequest::twitchRequest(url) - .onSuccess([this, channelName](auto result) -> Outcome { - rapidjson::Document document = result.parseRapidJson(); - if (!document.IsObject()) - { - qDebug() << "[TwitchChannel:refreshLiveStatus] root is not " - "an object"; - return Failure; - } - - if (!document.HasMember("stream")) - { - qDebug() << "[TwitchChannel:refreshLiveStatus] Missing " - "stream in root"; - return Failure; - } - - const auto &stream = document["stream"]; - - if (!stream.IsObject()) - { - // Stream is offline (stream is most likely null) - // removeFakeChannel(channelName); - return Failure; - } - // Stream is live - auto i = std::find(fakeTwitchChannels.begin(), - fakeTwitchChannels.end(), channelName); - - if (!(i != fakeTwitchChannels.end())) - { - fakeTwitchChannels.push_back(channelName); - if (Toasts::isEnabled()) - { - getApp()->toasts->sendChannelNotification( - channelName, Platform::Twitch); - } - if (getSettings()->notificationPlaySound) - { - getApp()->notifications->playSound(); - } - if (getSettings()->notificationFlashTaskbar) - { - getApp()->windows->sendAlert(); - } - } - return Success; - }) - .execute(); - }); + this->removeFakeChannel(channelName); + }); } void NotificationController::removeFakeChannel(const QString channelName) diff --git a/src/controllers/notifications/NotificationController.hpp b/src/controllers/notifications/NotificationController.hpp index a9c048efe..e8d0c4ef7 100644 --- a/src/controllers/notifications/NotificationController.hpp +++ b/src/controllers/notifications/NotificationController.hpp @@ -43,6 +43,7 @@ private: void removeFakeChannel(const QString channelName); void getFakeTwitchChannelLiveStatus(const QString &channelName); + // fakeTwitchChannels is a list of streams who are live that we have already sent out a notification for std::vector fakeTwitchChannels; QTimer *liveStatusTimer_; diff --git a/src/main.cpp b/src/main.cpp index 463465894..dfb42904b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,6 +9,8 @@ #include "common/Args.hpp" #include "common/Modes.hpp" #include "common/Version.hpp" +#include "providers/twitch/api/Helix.hpp" +#include "providers/twitch/api/Kraken.hpp" #include "singletons/Paths.hpp" #include "singletons/Settings.hpp" #include "util/IncognitoBrowser.hpp" @@ -36,6 +38,9 @@ int main(int argc, char **argv) } else { + Helix::initialize(); + Kraken::initialize(); + Paths *paths{}; try diff --git a/src/providers/twitch/PartialTwitchUser.cpp b/src/providers/twitch/PartialTwitchUser.cpp deleted file mode 100644 index 5407fbada..000000000 --- a/src/providers/twitch/PartialTwitchUser.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "providers/twitch/PartialTwitchUser.hpp" - -#include "common/Common.hpp" -#include "common/NetworkRequest.hpp" -#include "providers/twitch/TwitchCommon.hpp" - -#include -#include - -namespace chatterino { - -PartialTwitchUser PartialTwitchUser::byName(const QString &username) -{ - PartialTwitchUser user; - user.username_ = username; - - return user; -} - -PartialTwitchUser PartialTwitchUser::byId(const QString &id) -{ - PartialTwitchUser user; - user.id_ = id; - - return user; -} - -void PartialTwitchUser::getId(std::function successCallback, - const QObject *caller) -{ - getId( - successCallback, [] {}, caller); -} -void PartialTwitchUser::getId(std::function successCallback, - std::function failureCallback, - const QObject *caller) -{ - assert(!this->username_.isEmpty()); - - NetworkRequest("https://api.twitch.tv/kraken/users?login=" + - this->username_) - .caller(caller) - .authorizeTwitchV5(getDefaultClientID()) - .onSuccess([successCallback, failureCallback](auto result) -> Outcome { - auto root = result.parseJson(); - if (!root.value("users").isArray()) - { - qDebug() - << "API Error while getting user id, users is not an array"; - failureCallback(); - return Failure; - } - - auto users = root.value("users").toArray(); - if (users.size() != 1) - { - qDebug() << "API Error while getting user id, users array size " - "is not 1"; - failureCallback(); - return Failure; - } - if (!users[0].isObject()) - { - qDebug() << "API Error while getting user id, first user is " - "not an object"; - failureCallback(); - return Failure; - } - auto firstUser = users[0].toObject(); - auto id = firstUser.value("_id"); - if (!id.isString()) - { - qDebug() << "API Error: while getting user id, first user " - "object `_id` key is not a string"; - failureCallback(); - return Failure; - } - successCallback(id.toString()); - - return Success; - }) - .execute(); -} - -} // namespace chatterino diff --git a/src/providers/twitch/PartialTwitchUser.hpp b/src/providers/twitch/PartialTwitchUser.hpp deleted file mode 100644 index 6537bb279..000000000 --- a/src/providers/twitch/PartialTwitchUser.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include -#include - -#include - -namespace chatterino { - -// Experimental class to test a method of calling APIs on twitch users -class PartialTwitchUser -{ - PartialTwitchUser() = default; - - QString username_; - QString id_; - -public: - static PartialTwitchUser byName(const QString &username); - static PartialTwitchUser byId(const QString &id); - - void getId(std::function successCallback, - const QObject *caller = nullptr); - - void getId(std::function successCallback, - std::function failureCallback, - const QObject *caller = nullptr); -}; - -} // namespace chatterino diff --git a/src/providers/twitch/TwitchAccount.cpp b/src/providers/twitch/TwitchAccount.cpp index 13b4d4092..1b0f2c0ff 100644 --- a/src/providers/twitch/TwitchAccount.cpp +++ b/src/providers/twitch/TwitchAccount.cpp @@ -6,8 +6,8 @@ #include "common/Env.hpp" #include "common/NetworkRequest.hpp" #include "common/Outcome.hpp" -#include "providers/twitch/PartialTwitchUser.hpp" #include "providers/twitch/TwitchCommon.hpp" +#include "providers/twitch/api/Helix.hpp" #include "singletons/Emotes.hpp" #include "util/RapidjsonHelpers.hpp" @@ -151,12 +151,14 @@ void TwitchAccount::ignore( const QString &targetName, std::function onFinished) { - const auto onIdFetched = [this, targetName, - onFinished](QString targetUserId) { - this->ignoreByID(targetUserId, targetName, onFinished); // + const auto onUserFetched = [this, targetName, + onFinished](const auto &user) { + this->ignoreByID(user.id, targetName, onFinished); // }; - PartialTwitchUser::byName(targetName).getId(onIdFetched); + const auto onUserFetchFailed = [] {}; + + getHelix()->getUserByName(targetName, onUserFetched, onUserFetchFailed); } void TwitchAccount::ignoreByID( @@ -226,12 +228,14 @@ void TwitchAccount::unignore( const QString &targetName, std::function onFinished) { - const auto onIdFetched = [this, targetName, - onFinished](QString targetUserId) { - this->unignoreByID(targetUserId, targetName, onFinished); // + const auto onUserFetched = [this, targetName, + onFinished](const auto &user) { + this->unignoreByID(user.id, targetName, onFinished); // }; - PartialTwitchUser::byName(targetName).getId(onIdFetched); + const auto onUserFetchFailed = [] {}; + + getHelix()->getUserByName(targetName, onUserFetched, onUserFetchFailed); } void TwitchAccount::unignoreByID( @@ -270,28 +274,18 @@ void TwitchAccount::unignoreByID( void TwitchAccount::checkFollow(const QString targetUserID, std::function onFinished) { - QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() + - "/follows/channels/" + targetUserID); + const auto onResponse = [onFinished](bool following, const auto &record) { + if (!following) + { + onFinished(FollowResult_NotFollowing); + return; + } - NetworkRequest(url) + onFinished(FollowResult_Following); + }; - .authorizeTwitchV5(this->getOAuthClient(), this->getOAuthToken()) - .onError([=](NetworkResult result) { - if (result.status() == 203) - { - onFinished(FollowResult_NotFollowing); - } - else - { - onFinished(FollowResult_Failed); - } - }) - .onSuccess([=](auto result) -> Outcome { - auto document = result.parseRapidJson(); - onFinished(FollowResult_Following); - return Success; - }) - .execute(); + getHelix()->getUserFollow(this->getUserId(), targetUserID, onResponse, + [] {}); } void TwitchAccount::followUser(const QString userID, diff --git a/src/providers/twitch/TwitchAccountManager.cpp b/src/providers/twitch/TwitchAccountManager.cpp index c3b2ee69c..3813e9822 100644 --- a/src/providers/twitch/TwitchAccountManager.cpp +++ b/src/providers/twitch/TwitchAccountManager.cpp @@ -3,6 +3,8 @@ #include "common/Common.hpp" #include "providers/twitch/TwitchAccount.hpp" #include "providers/twitch/TwitchCommon.hpp" +#include "providers/twitch/api/Helix.hpp" +#include "providers/twitch/api/Kraken.hpp" namespace chatterino { @@ -141,6 +143,8 @@ void TwitchAccountManager::load() if (user) { qDebug() << "Twitch user updated to" << newUsername; + getHelix()->update(user->getOAuthClient(), user->getOAuthToken()); + getKraken()->update(user->getOAuthClient(), user->getOAuthToken()); this->currentUser_ = user; } else diff --git a/src/providers/twitch/TwitchApi.cpp b/src/providers/twitch/TwitchApi.cpp deleted file mode 100644 index 6c3b6b6ee..000000000 --- a/src/providers/twitch/TwitchApi.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "providers/twitch/TwitchApi.hpp" - -#include "common/Common.hpp" -#include "common/NetworkRequest.hpp" -#include "providers/twitch/TwitchCommon.hpp" - -#include -#include - -namespace chatterino { - -void TwitchApi::findUserId(const QString user, - std::function successCallback) -{ - QString requestUrl("https://api.twitch.tv/kraken/users?login=" + user); - - NetworkRequest(requestUrl) - - .authorizeTwitchV5(getDefaultClientID()) - .timeout(30000) - .onSuccess([successCallback](auto result) mutable -> Outcome { - auto root = result.parseJson(); - if (!root.value("users").isArray()) - { - qDebug() - << "API Error while getting user id, users is not an array"; - successCallback(""); - return Failure; - } - auto users = root.value("users").toArray(); - if (users.size() != 1) - { - qDebug() << "API Error while getting user id, users array size " - "is not 1"; - successCallback(""); - return Failure; - } - if (!users[0].isObject()) - { - qDebug() << "API Error while getting user id, first user is " - "not an object"; - successCallback(""); - return Failure; - } - auto firstUser = users[0].toObject(); - auto id = firstUser.value("_id"); - if (!id.isString()) - { - qDebug() << "API Error: while getting user id, first user " - "object `_id` key is not a string"; - successCallback(""); - return Failure; - } - successCallback(id.toString()); - return Success; - }) - .execute(); -} - -void TwitchApi::findUserName(const QString userid, - std::function successCallback) -{ - QString requestUrl("https://api.twitch.tv/kraken/users/" + userid); - - NetworkRequest(requestUrl) - - .authorizeTwitchV5(getDefaultClientID()) - .timeout(30000) - .onSuccess([successCallback](auto result) mutable -> Outcome { - auto root = result.parseJson(); - auto name = root.value("name"); - if (!name.isString()) - { - qDebug() << "API Error: while getting user name, `name` is not " - "a string"; - successCallback(""); - return Failure; - } - successCallback(name.toString()); - return Success; - }) - .execute(); -} - -} // namespace chatterino diff --git a/src/providers/twitch/TwitchApi.hpp b/src/providers/twitch/TwitchApi.hpp deleted file mode 100644 index 8b1b85c13..000000000 --- a/src/providers/twitch/TwitchApi.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include - -namespace chatterino { - -class TwitchApi -{ -public: - static void findUserId(const QString user, - std::function callback); - static void findUserName(const QString userid, - std::function callback); - -private: -}; - -} // namespace chatterino diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index d305f0c43..05dac0123 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -14,6 +14,8 @@ #include "providers/twitch/TwitchCommon.hpp" #include "providers/twitch/TwitchMessageBuilder.hpp" #include "providers/twitch/TwitchParseCheerEmotes.hpp" +#include "providers/twitch/api/Helix.hpp" +#include "providers/twitch/api/Kraken.hpp" #include "singletons/Emotes.hpp" #include "singletons/Settings.hpp" #include "singletons/Toasts.hpp" @@ -21,6 +23,7 @@ #include "util/PostToThread.hpp" #include "widgets/Window.hpp" +#include #include #include #include @@ -454,35 +457,25 @@ void TwitchChannel::refreshTitle() } this->titleRefreshedTime_ = QTime::currentTime(); - QString url("https://api.twitch.tv/kraken/channels/" + roomID); - NetworkRequest::twitchRequest(url) - .onSuccess( - [this, weak = weakOf(this)](auto result) -> Outcome { - ChannelPtr shared = weak.lock(); - if (!shared) - return Failure; + const auto onSuccess = [this, + weak = weakOf(this)](const auto &channel) { + ChannelPtr shared = weak.lock(); + if (!shared) + { + return; + } - const auto document = result.parseRapidJson(); + { + auto status = this->streamStatus_.access(); + status->title = channel.status; + } - auto statusIt = document.FindMember("status"); + this->liveStatusChanged.invoke(); + }; - if (statusIt == document.MemberEnd()) - { - return Failure; - } + const auto onFailure = [] {}; - { - auto status = this->streamStatus_.access(); - if (!rj::getSafe(statusIt->value, status->title)) - { - return Failure; - } - } - - this->liveStatusChanged.invoke(); - return Success; - }) - .execute(); + getKraken()->getChannel(roomID, onSuccess, onFailure); } void TwitchChannel::refreshLiveStatus() @@ -497,106 +490,73 @@ void TwitchChannel::refreshLiveStatus() return; } - QString url("https://api.twitch.tv/kraken/streams/" + roomID); + getHelix()->getStreamById( + roomID, + [this, weak = weakOf(this)](bool live, const auto &stream) { + ChannelPtr shared = weak.lock(); + if (!shared) + { + return; + } - // auto request = makeGetStreamRequest(roomID, QThread::currentThread()); - NetworkRequest::twitchRequest(url) - - .onSuccess( - [this, weak = weakOf(this)](auto result) -> Outcome { - ChannelPtr shared = weak.lock(); - if (!shared) - return Failure; - - return this->parseLiveStatus(result.parseRapidJson()); - }) - .execute(); + this->parseLiveStatus(live, stream); + }, + [] { + // failure + }); } -Outcome TwitchChannel::parseLiveStatus(const rapidjson::Document &document) +void TwitchChannel::parseLiveStatus(bool live, const HelixStream &stream) { - if (!document.IsObject()) + if (!live) { - qDebug() << "[TwitchChannel:refreshLiveStatus] root is not an object"; - return Failure; - } - - if (!document.HasMember("stream")) - { - qDebug() << "[TwitchChannel:refreshLiveStatus] Missing stream in root"; - return Failure; - } - - const auto &stream = document["stream"]; - - if (!stream.IsObject()) - { - // Stream is offline (stream is most likely null) this->setLive(false); - return Failure; + return; } - if (!stream.HasMember("viewers") || !stream.HasMember("game") || - !stream.HasMember("channel") || !stream.HasMember("created_at")) - { - qDebug() - << "[TwitchChannel:refreshLiveStatus] Missing members in stream"; - this->setLive(false); - return Failure; - } - - const rapidjson::Value &streamChannel = stream["channel"]; - - if (!streamChannel.IsObject() || !streamChannel.HasMember("status")) - { - qDebug() << "[TwitchChannel:refreshLiveStatus] Missing member " - "\"status\" in channel"; - return Failure; - } - - // Stream is live - { auto status = this->streamStatus_.access(); - status->viewerCount = stream["viewers"].GetUint(); - status->game = stream["game"].GetString(); - status->title = streamChannel["status"].GetString(); - QDateTime since = QDateTime::fromString( - stream["created_at"].GetString(), Qt::ISODate); + status->viewerCount = stream.viewerCount; + if (status->gameId != stream.gameId) + { + status->gameId = stream.gameId; + + // Resolve game ID to game name + getHelix()->getGameById( + stream.gameId, + [this, weak = weakOf(this)](const auto &game) { + ChannelPtr shared = weak.lock(); + if (!shared) + { + return; + } + + { + auto status = this->streamStatus_.access(); + status->game = game.name; + } + + this->liveStatusChanged.invoke(); + }, + [] { + // failure + }); + } + status->game = stream.gameId; + status->title = stream.title; + QDateTime since = QDateTime::fromString(stream.startedAt, Qt::ISODate); auto diff = since.secsTo(QDateTime::currentDateTime()); status->uptime = QString::number(diff / 3600) + "h " + QString::number(diff % 3600 / 60) + "m"; status->rerun = false; - if (stream.HasMember("stream_type")) - { - status->streamType = stream["stream_type"].GetString(); - } - else - { - status->streamType = QString(); - } - - if (stream.HasMember("broadcast_platform")) - { - const auto &broadcastPlatformValue = stream["broadcast_platform"]; - - if (broadcastPlatformValue.IsString()) - { - const char *broadcastPlatform = - stream["broadcast_platform"].GetString(); - if (strcmp(broadcastPlatform, "rerun") == 0) - { - status->rerun = true; - } - } - } + status->streamType = stream.type; } - setLive(true); + + this->setLive(true); + // Signal all listeners that the stream status has been updated this->liveStatusChanged.invoke(); - - return Success; } void TwitchChannel::loadRecentMessages() diff --git a/src/providers/twitch/TwitchChannel.hpp b/src/providers/twitch/TwitchChannel.hpp index 026c90717..fc40ad325 100644 --- a/src/providers/twitch/TwitchChannel.hpp +++ b/src/providers/twitch/TwitchChannel.hpp @@ -8,14 +8,15 @@ #include "common/UniqueAccess.hpp" #include "common/UsernameSet.hpp" #include "providers/twitch/TwitchEmotes.hpp" +#include "providers/twitch/api/Helix.hpp" -#include #include #include #include #include -#include #include + +#include #include namespace chatterino { @@ -43,6 +44,7 @@ public: unsigned viewerCount = 0; QString title; QString game; + QString gameId; QString uptime; QString streamType; }; @@ -120,7 +122,7 @@ protected: private: // Methods void refreshLiveStatus(); - Outcome parseLiveStatus(const rapidjson::Document &document); + void parseLiveStatus(bool live, const HelixStream &stream); void refreshPubsub(); void refreshChatters(); void refreshBadges(); diff --git a/src/providers/twitch/TwitchParseCheerEmotes.cpp b/src/providers/twitch/TwitchParseCheerEmotes.cpp index 5258baf9a..9667ffd2a 100644 --- a/src/providers/twitch/TwitchParseCheerEmotes.cpp +++ b/src/providers/twitch/TwitchParseCheerEmotes.cpp @@ -278,6 +278,7 @@ namespace { return true; } + } // namespace // Look through the results of diff --git a/src/providers/twitch/api/Helix.cpp b/src/providers/twitch/api/Helix.cpp new file mode 100644 index 000000000..974b62128 --- /dev/null +++ b/src/providers/twitch/api/Helix.cpp @@ -0,0 +1,368 @@ +#include "providers/twitch/api/Helix.hpp" + +#include "common/Outcome.hpp" + +namespace chatterino { + +static Helix *instance = nullptr; + +void Helix::fetchUsers(QStringList userIds, QStringList userLogins, + ResultCallback> successCallback, + HelixFailureCallback failureCallback) +{ + QUrlQuery urlQuery; + + for (const auto &id : userIds) + { + urlQuery.addQueryItem("id", id); + } + + for (const auto &login : userLogins) + { + urlQuery.addQueryItem("login", login); + } + + // TODO: set on success and on error + this->makeRequest("users", urlQuery) + .onSuccess([successCallback, failureCallback](auto result) -> Outcome { + auto root = result.parseJson(); + auto data = root.value("data"); + + if (!data.isArray()) + { + failureCallback(); + return Failure; + } + + std::vector users; + + for (const auto &jsonUser : data.toArray()) + { + users.emplace_back(jsonUser.toObject()); + } + + successCallback(users); + + return Success; + }) + .onError([failureCallback](auto result) { + // TODO: make better xd + failureCallback(); + }) + .execute(); +} + +void Helix::getUserByName(QString userId, + ResultCallback successCallback, + HelixFailureCallback failureCallback) +{ + QStringList userIds; + QStringList userLogins{userId}; + + this->fetchUsers( + userIds, userLogins, + [successCallback, + failureCallback](const std::vector &users) { + if (users.empty()) + { + failureCallback(); + return; + } + successCallback(users[0]); + }, + failureCallback); +} + +void Helix::getUserById(QString userId, + ResultCallback successCallback, + HelixFailureCallback failureCallback) +{ + QStringList userIds{userId}; + QStringList userLogins; + + this->fetchUsers( + userIds, userLogins, + [successCallback, failureCallback](const auto &users) { + if (users.empty()) + { + failureCallback(); + return; + } + successCallback(users[0]); + }, + failureCallback); +} + +void Helix::fetchUsersFollows( + QString fromId, QString toId, + ResultCallback successCallback, + HelixFailureCallback failureCallback) +{ + assert(!fromId.isEmpty() || !toId.isEmpty()); + + QUrlQuery urlQuery; + + if (!fromId.isEmpty()) + { + urlQuery.addQueryItem("from_id", fromId); + } + + if (!toId.isEmpty()) + { + urlQuery.addQueryItem("to_id", toId); + } + + // TODO: set on success and on error + this->makeRequest("users/follows", urlQuery) + .onSuccess([successCallback, failureCallback](auto result) -> Outcome { + auto root = result.parseJson(); + if (root.empty()) + { + failureCallback(); + return Failure; + } + successCallback(HelixUsersFollowsResponse(root)); + return Success; + }) + .onError([failureCallback](auto result) { + // TODO: make better xd + failureCallback(); + }) + .execute(); +} + +void Helix::getUserFollowers( + QString userId, ResultCallback successCallback, + HelixFailureCallback failureCallback) +{ + this->fetchUsersFollows("", userId, successCallback, failureCallback); +} + +void Helix::getUserFollow( + QString userId, QString targetId, + ResultCallback successCallback, + HelixFailureCallback failureCallback) +{ + this->fetchUsersFollows( + userId, targetId, + [successCallback](const auto &response) { + if (response.data.empty()) + { + successCallback(false, HelixUsersFollowsRecord()); + return; + } + + successCallback(true, response.data[0]); + }, + failureCallback); +} + +void Helix::fetchStreams( + QStringList userIds, QStringList userLogins, + ResultCallback> successCallback, + HelixFailureCallback failureCallback) +{ + QUrlQuery urlQuery; + + for (const auto &id : userIds) + { + urlQuery.addQueryItem("user_id", id); + } + + for (const auto &login : userLogins) + { + urlQuery.addQueryItem("user_login", login); + } + + // TODO: set on success and on error + this->makeRequest("streams", urlQuery) + .onSuccess([successCallback, failureCallback](auto result) -> Outcome { + auto root = result.parseJson(); + auto data = root.value("data"); + + if (!data.isArray()) + { + failureCallback(); + return Failure; + } + + std::vector streams; + + for (const auto &jsonStream : data.toArray()) + { + streams.emplace_back(jsonStream.toObject()); + } + + successCallback(streams); + + return Success; + }) + .onError([failureCallback](auto result) { + // TODO: make better xd + failureCallback(); + }) + .execute(); +} + +void Helix::getStreamById(QString userId, + ResultCallback successCallback, + HelixFailureCallback failureCallback) +{ + QStringList userIds{userId}; + QStringList userLogins; + + this->fetchStreams( + userIds, userLogins, + [successCallback, failureCallback](const auto &streams) { + if (streams.empty()) + { + successCallback(false, HelixStream()); + return; + } + successCallback(true, streams[0]); + }, + failureCallback); +} + +void Helix::getStreamByName(QString userName, + ResultCallback successCallback, + HelixFailureCallback failureCallback) +{ + QStringList userIds; + QStringList userLogins{userName}; + + this->fetchStreams( + userIds, userLogins, + [successCallback, failureCallback](const auto &streams) { + if (streams.empty()) + { + successCallback(false, HelixStream()); + return; + } + successCallback(true, streams[0]); + }, + failureCallback); +} + +/// + +void Helix::fetchGames(QStringList gameIds, QStringList gameNames, + ResultCallback> successCallback, + HelixFailureCallback failureCallback) +{ + assert((gameIds.length() + gameNames.length()) > 0); + + QUrlQuery urlQuery; + + for (const auto &id : gameIds) + { + urlQuery.addQueryItem("id", id); + } + + for (const auto &login : gameNames) + { + urlQuery.addQueryItem("name", login); + } + + // TODO: set on success and on error + this->makeRequest("games", urlQuery) + .onSuccess([successCallback, failureCallback](auto result) -> Outcome { + auto root = result.parseJson(); + auto data = root.value("data"); + + if (!data.isArray()) + { + failureCallback(); + return Failure; + } + + std::vector games; + + for (const auto &jsonStream : data.toArray()) + { + games.emplace_back(jsonStream.toObject()); + } + + successCallback(games); + + return Success; + }) + .onError([failureCallback](auto result) { + // TODO: make better xd + failureCallback(); + }) + .execute(); +} + +void Helix::getGameById(QString gameId, + ResultCallback successCallback, + HelixFailureCallback failureCallback) +{ + QStringList gameIds{gameId}; + QStringList gameNames; + + this->fetchGames( + gameIds, gameNames, + [successCallback, failureCallback](const auto &games) { + if (games.empty()) + { + failureCallback(); + return; + } + successCallback(games[0]); + }, + failureCallback); +} + +NetworkRequest Helix::makeRequest(QString url, QUrlQuery urlQuery) +{ + assert(!url.startsWith("/")); + + if (this->clientId.isEmpty()) + { + qDebug() + << "Helix::makeRequest called without a client ID set BabyRage"; + // return boost::none; + } + + if (this->oauthToken.isEmpty()) + { + qDebug() + << "Helix::makeRequest called without an oauth token set BabyRage"; + // return boost::none; + } + + const QString baseUrl("https://api.twitch.tv/helix/"); + + QUrl fullUrl(baseUrl + url); + + fullUrl.setQuery(urlQuery); + + return NetworkRequest(fullUrl) + .timeout(5 * 1000) + .header("Accept", "application/json") + .header("Client-ID", this->clientId) + .header("Authorization", "Bearer " + this->oauthToken); +} + +void Helix::update(QString clientId, QString oauthToken) +{ + this->clientId = clientId; + this->oauthToken = oauthToken; +} + +void Helix::initialize() +{ + assert(instance == nullptr); + + instance = new Helix(); +} + +Helix *getHelix() +{ + assert(instance != nullptr); + + return instance; +} + +} // namespace chatterino diff --git a/src/providers/twitch/api/Helix.hpp b/src/providers/twitch/api/Helix.hpp new file mode 100644 index 000000000..bfe54b699 --- /dev/null +++ b/src/providers/twitch/api/Helix.hpp @@ -0,0 +1,198 @@ +#pragma once + +#include "common/NetworkRequest.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace chatterino { + +using HelixFailureCallback = std::function; +template +using ResultCallback = std::function; + +struct HelixUser { + QString id; + QString login; + QString displayName; + QString description; + QString profileImageUrl; + int viewCount; + + explicit HelixUser(QJsonObject jsonObject) + : id(jsonObject.value("id").toString()) + , login(jsonObject.value("login").toString()) + , displayName(jsonObject.value("display_name").toString()) + , description(jsonObject.value("description").toString()) + , profileImageUrl(jsonObject.value("profile_image_url").toString()) + , viewCount(jsonObject.value("view_count").toInt()) + { + } +}; + +struct HelixUsersFollowsRecord { + QString fromId; + QString fromName; + QString toId; + QString toName; + QString followedAt; // date time object + + HelixUsersFollowsRecord() + : fromId("") + , fromName("") + , toId("") + , toName("") + , followedAt("") + { + } + + explicit HelixUsersFollowsRecord(QJsonObject jsonObject) + : fromId(jsonObject.value("from_id").toString()) + , fromName(jsonObject.value("from_name").toString()) + , toId(jsonObject.value("to_id").toString()) + , toName(jsonObject.value("to_name").toString()) + , followedAt(jsonObject.value("followed_at").toString()) + { + } +}; + +struct HelixUsersFollowsResponse { + int total; + std::vector data; + explicit HelixUsersFollowsResponse(QJsonObject jsonObject) + : total(jsonObject.value("total").toInt()) + { + const auto &jsonData = jsonObject.value("data").toArray(); + std::transform(jsonData.begin(), jsonData.end(), + std::back_inserter(this->data), + [](const QJsonValue &record) { + return HelixUsersFollowsRecord(record.toObject()); + }); + } +}; + +struct HelixStream { + QString id; // stream id + QString userId; + QString userName; + QString gameId; + QString type; + QString title; + int viewerCount; + QString startedAt; + QString language; + QString thumbnailUrl; + + HelixStream() + : id("") + , userId("") + , userName("") + , gameId("") + , type("") + , title("") + , viewerCount() + , startedAt("") + , language("") + , thumbnailUrl("") + { + } + + explicit HelixStream(QJsonObject jsonObject) + : id(jsonObject.value("id").toString()) + , userId(jsonObject.value("user_id").toString()) + , userName(jsonObject.value("user_name").toString()) + , gameId(jsonObject.value("game_id").toString()) + , type(jsonObject.value("type").toString()) + , title(jsonObject.value("title").toString()) + , viewerCount(jsonObject.value("viewer_count").toInt()) + , startedAt(jsonObject.value("started_at").toString()) + , language(jsonObject.value("language").toString()) + , thumbnailUrl(jsonObject.value("thumbnail_url").toString()) + { + } +}; + +struct HelixGame { + QString id; // stream id + QString name; + QString boxArtUrl; + + explicit HelixGame(QJsonObject jsonObject) + : id(jsonObject.value("id").toString()) + , name(jsonObject.value("name").toString()) + , boxArtUrl(jsonObject.value("box_art_url").toString()) + { + } +}; + +class Helix final : boost::noncopyable +{ +public: + // https://dev.twitch.tv/docs/api/reference#get-users + void fetchUsers(QStringList userIds, QStringList userLogins, + ResultCallback> successCallback, + HelixFailureCallback failureCallback); + void getUserByName(QString userName, + ResultCallback successCallback, + HelixFailureCallback failureCallback); + void getUserById(QString userId, ResultCallback successCallback, + HelixFailureCallback failureCallback); + + // https://dev.twitch.tv/docs/api/reference#get-users-follows + void fetchUsersFollows( + QString fromId, QString toId, + ResultCallback successCallback, + HelixFailureCallback failureCallback); + + void getUserFollowers( + QString userId, + ResultCallback successCallback, + HelixFailureCallback failureCallback); + + void getUserFollow( + QString userId, QString targetId, + ResultCallback successCallback, + HelixFailureCallback failureCallback); + + // https://dev.twitch.tv/docs/api/reference#get-streams + void fetchStreams(QStringList userIds, QStringList userLogins, + ResultCallback> successCallback, + HelixFailureCallback failureCallback); + + void getStreamById(QString userId, + ResultCallback successCallback, + HelixFailureCallback failureCallback); + + void getStreamByName(QString userName, + ResultCallback successCallback, + HelixFailureCallback failureCallback); + + // https://dev.twitch.tv/docs/api/reference#get-games + void fetchGames(QStringList gameIds, QStringList gameNames, + ResultCallback> successCallback, + HelixFailureCallback failureCallback); + + void getGameById(QString gameId, ResultCallback successCallback, + HelixFailureCallback failureCallback); + + void update(QString clientId, QString oauthToken); + + static void initialize(); + +private: + NetworkRequest makeRequest(QString url, QUrlQuery urlQuery); + + QString clientId; + QString oauthToken; +}; + +Helix *getHelix(); + +} // namespace chatterino diff --git a/src/providers/twitch/api/Kraken.cpp b/src/providers/twitch/api/Kraken.cpp new file mode 100644 index 000000000..be7e98b36 --- /dev/null +++ b/src/providers/twitch/api/Kraken.cpp @@ -0,0 +1,104 @@ +#include "providers/twitch/api/Kraken.hpp" + +#include "common/Outcome.hpp" +#include "providers/twitch/TwitchCommon.hpp" + +namespace chatterino { + +static Kraken *instance = nullptr; + +void Kraken::getChannel(QString userId, + ResultCallback successCallback, + KrakenFailureCallback failureCallback) +{ + assert(!userId.isEmpty()); + + this->makeRequest("channels/" + userId, {}) + .onSuccess([successCallback, failureCallback](auto result) -> Outcome { + auto root = result.parseJson(); + + successCallback(root); + + return Success; + }) + .onError([failureCallback](auto result) { + // TODO: make better xd + failureCallback(); + }) + .execute(); +} + +void Kraken::getUser(QString userId, ResultCallback successCallback, + KrakenFailureCallback failureCallback) +{ + assert(!userId.isEmpty()); + + this->makeRequest("users/" + userId, {}) + .onSuccess([successCallback, failureCallback](auto result) -> Outcome { + auto root = result.parseJson(); + + successCallback(root); + + return Success; + }) + .onError([failureCallback](auto result) { + // TODO: make better xd + failureCallback(); + }) + .execute(); +} + +NetworkRequest Kraken::makeRequest(QString url, QUrlQuery urlQuery) +{ + assert(!url.startsWith("/")); + + if (this->clientId.isEmpty()) + { + qDebug() + << "Kraken::makeRequest called without a client ID set BabyRage"; + } + + const QString baseUrl("https://api.twitch.tv/kraken/"); + + QUrl fullUrl(baseUrl + url); + + fullUrl.setQuery(urlQuery); + + if (!this->oauthToken.isEmpty()) + { + return NetworkRequest(fullUrl) + .timeout(5 * 1000) + .header("Accept", "application/vnd.twitchtv.v5+json") + .header("Client-ID", this->clientId) + .header("Authorization", "OAuth " + this->oauthToken); + } + + return NetworkRequest(fullUrl) + .timeout(5 * 1000) + .header("Accept", "application/vnd.twitchtv.v5+json") + .header("Client-ID", this->clientId); +} + +void Kraken::update(QString clientId, QString oauthToken) +{ + this->clientId = clientId; + this->oauthToken = oauthToken; +} + +void Kraken::initialize() +{ + assert(instance == nullptr); + + instance = new Kraken(); + + getKraken()->update(getDefaultClientID(), ""); +} + +Kraken *getKraken() +{ + assert(instance != nullptr); + + return instance; +} + +} // namespace chatterino diff --git a/src/providers/twitch/api/Kraken.hpp b/src/providers/twitch/api/Kraken.hpp new file mode 100644 index 000000000..047173d6e --- /dev/null +++ b/src/providers/twitch/api/Kraken.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include "common/NetworkRequest.hpp" + +#include +#include +#include +#include + +#include + +namespace chatterino { + +using KrakenFailureCallback = std::function; +template +using ResultCallback = std::function; + +struct KrakenChannel { + const QString status; + + KrakenChannel(QJsonObject jsonObject) + : status(jsonObject.value("status").toString()) + { + } +}; + +struct KrakenUser { + const QString createdAt; + + KrakenUser(QJsonObject jsonObject) + : createdAt(jsonObject.value("created_at").toString()) + { + } +}; + +class Kraken final : boost::noncopyable +{ +public: + // https://dev.twitch.tv/docs/v5/reference/users#follow-channel + void getChannel(QString userId, + ResultCallback resultCallback, + KrakenFailureCallback failureCallback); + + // https://dev.twitch.tv/docs/v5/reference/users#get-user-by-id + void getUser(QString userId, ResultCallback resultCallback, + KrakenFailureCallback failureCallback); + + void update(QString clientId, QString oauthToken); + + static void initialize(); + +private: + NetworkRequest makeRequest(QString url, QUrlQuery urlQuery); + + QString clientId; + QString oauthToken; +}; + +Kraken *getKraken(); + +} // namespace chatterino diff --git a/src/providers/twitch/api/README.md b/src/providers/twitch/api/README.md new file mode 100644 index 000000000..a95ac6bcc --- /dev/null +++ b/src/providers/twitch/api/README.md @@ -0,0 +1,125 @@ +# Twitch API +this folder describes what sort of API requests we do, what permissions are required for the requests etc + +## Kraken (V5) +We use a bunch of Kraken (V5) in Chatterino2. + +### Get User +URL: https://dev.twitch.tv/docs/v5/reference/users#get-user-by-id + +Migration path: **Unknown** + + * We implement this in `providers/twitch/api/Kraken.cpp getUser` + Used in: + * `UserInfoPopup` to get the "created at" date of a user + +### Get Channel +URL: https://dev.twitch.tv/docs/v5/reference/channels#get-channel + +Migration path: **Unknown** + + * We implement this in `providers/twitch/api/Kraken.cpp getChannel` + Used in: + * `TwitchChannel::refreshTitle` to check the current stream title/game of offline channels + +### Follow Channel +URL: https://dev.twitch.tv/docs/v5/reference/users#follow-channel +Requires `user_follows_edit` scope + +Migration path: **Unknown** + + * We implement this API in `providers/twitch/TwitchAccount.cpp followUser` + +### Unfollow Channel +URL: https://dev.twitch.tv/docs/v5/reference/users#unfollow-channel +Requires `user_follows_edit` scope + +Migration path: **Unknown** + + * We implement this API in `providers/twitch/TwitchAccount.cpp unfollowUser` + + +### Get Cheermotes +URL: https://dev.twitch.tv/docs/v5/reference/bits#get-cheermotes + +Migration path: **Not checked** + + * We implement this API in `providers/twitch/TwitchChannel.cpp` to resolve a chats available cheer emotes. This helps us parse incoming messages like `pajaCheer1000` + +### Get User Block List +URL: https://dev.twitch.tv/docs/v5/reference/users#get-user-block-list + +Migration path: **Unknown** + + * We use this in `providers/twitch/TwitchAccount.cpp loadIgnores` + +### Block User +URL: https://dev.twitch.tv/docs/v5/reference/users#block-user +Requires `user_blocks_edit` scope + +Migration path: **Unknown** + + * We use this in `providers/twitch/TwitchAccount.cpp ignoreByID` + +### Unblock User +URL: https://dev.twitch.tv/docs/v5/reference/users#unblock-user +Requires `user_blocks_edit` scope + +Migration path: **Unknown** + + * We use this in `providers/twitch/TwitchAccount.cpp unignoreByID` + +### Get User Emotes +URL: https://dev.twitch.tv/docs/v5/reference/users#get-user-emotes +Requires `user_subscriptions` scope + +Migration path: **Unknown** + + * We use this in `providers/twitch/TwitchAccount.cpp loadEmotes` to figure out which emotes a user is allowed to use! + +### AUTOMOD APPROVE +**Unofficial** documentation: https://discuss.dev.twitch.tv/t/allowing-others-aka-bots-to-use-twitchbot-reject/8508/2 + + * We use this in `providers/twitch/TwitchAccount.cpp autoModAllow` to approve an automod deny/allow question + +### AUTOMOD DENY +**Unofficial** documentation: https://discuss.dev.twitch.tv/t/allowing-others-aka-bots-to-use-twitchbot-reject/8508/2 + + * We use this in `providers/twitch/TwitchAccount.cpp autoModDeny` to deny an automod deny/allow question + +## Helix +Full Helix API reference: https://dev.twitch.tv/docs/api/reference + +### Get Users +URL: https://dev.twitch.tv/docs/api/reference#get-users + + * We implement this in `providers/twitch/api/Helix.cpp fetchUsers`. + Used in: + * `UserInfoPopup` to get ID and viewcount of username we clicked + * `CommandController` to power any commands that need to get a user ID + * `Toasts` to get the profile picture of a streamer who just went live + * `TwitchAccount` ignore and unignore features to translate user name to user ID + +### Get Users Follows +URL: https://dev.twitch.tv/docs/api/reference#get-users-follows + + * We implement this in `providers/twitch/api/Helix.cpp fetchUsersFollows` + Used in: + * `UserInfoPopup` to get number of followers a user has + +### Get Streams +URL: https://dev.twitch.tv/docs/api/reference#get-streams + + * We implement this in `providers/twitch/api/Helix.cpp fetchStreams` + Used in: + * `TwitchChannel` to get live status, game, title, and viewer count of a channel + * `NotificationController` to provide notifications for channels you might not have open in Chatterino, but are still interested in getting notifications for + +## TMI +The TMI api is undocumented. + +### Get Chatters +**Undocumented** + + * We use this in `widgets/splits/Split.cpp showViewerList` + * We use this in `providers/twitch/TwitchChannel.cpp refreshChatters` diff --git a/src/singletons/Toasts.cpp b/src/singletons/Toasts.cpp index def5df00f..9316a8791 100644 --- a/src/singletons/Toasts.cpp +++ b/src/singletons/Toasts.cpp @@ -7,6 +7,7 @@ #include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/TwitchCommon.hpp" #include "providers/twitch/TwitchIrcServer.hpp" +#include "providers/twitch/api/Helix.hpp" #include "singletons/Paths.hpp" #include "util/StreamLink.hpp" #include "widgets/helper/CommonTexts.hpp" @@ -85,14 +86,17 @@ void Toasts::sendChannelNotification(const QString &channelName, Platform p) } else { - this->fetchChannelAvatar( + getHelix()->getUserByName( channelName, - [channelName, sendChannelNotification](QString avatarLink) { + [channelName, sendChannelNotification](const auto &user) { DownloadManager *manager = new DownloadManager(); - manager->setFile(avatarLink, channelName); + manager->setFile(user.profileImageUrl, channelName); manager->connect(manager, &DownloadManager::downloadComplete, sendChannelNotification); + }, + [] { + // on failure }); } } @@ -206,53 +210,4 @@ void Toasts::sendWindowsNotification(const QString &channelName, Platform p) #endif -void Toasts::fetchChannelAvatar(const QString channelName, - std::function successCallback) -{ - QString requestUrl("https://api.twitch.tv/kraken/users?login=" + - channelName); - - NetworkRequest(requestUrl) - - .authorizeTwitchV5(getDefaultClientID()) - .timeout(30000) - .onSuccess([successCallback](auto result) mutable -> Outcome { - auto root = result.parseJson(); - if (!root.value("users").isArray()) - { - // log("API Error while getting user id, users is not an array"); - successCallback(""); - return Failure; - } - auto users = root.value("users").toArray(); - if (users.size() != 1) - { - // log("API Error while getting user id, users array size is not - // 1"); - successCallback(""); - return Failure; - } - if (!users[0].isObject()) - { - // log("API Error while getting user id, first user is not an - // object"); - successCallback(""); - return Failure; - } - auto firstUser = users[0].toObject(); - auto avatar = firstUser.value("logo"); - if (!avatar.isString()) - { - // log("API Error: while getting user avatar, first user object " - // "`avatar` key " - // "is not a " - // "string"); - successCallback(""); - return Failure; - } - successCallback(avatar.toString()); - return Success; - }) - .execute(); -} } // namespace chatterino diff --git a/src/singletons/Toasts.hpp b/src/singletons/Toasts.hpp index 43c6d1da8..5ef792c77 100644 --- a/src/singletons/Toasts.hpp +++ b/src/singletons/Toasts.hpp @@ -29,9 +29,5 @@ private: #ifdef Q_OS_WIN void sendWindowsNotification(const QString &channelName, Platform p); #endif - - static void fetchChannelAvatar( - const QString channelName, - std::function successCallback); }; } // namespace chatterino diff --git a/src/widgets/dialogs/LoginDialog.cpp b/src/widgets/dialogs/LoginDialog.cpp index f96c86342..3666ea657 100644 --- a/src/widgets/dialogs/LoginDialog.cpp +++ b/src/widgets/dialogs/LoginDialog.cpp @@ -4,7 +4,6 @@ #include "common/Common.hpp" #include "common/NetworkRequest.hpp" #include "controllers/accounts/AccountController.hpp" -#include "providers/twitch/PartialTwitchUser.hpp" #include "util/Helpers.hpp" #ifdef USEWINSDK @@ -159,7 +158,6 @@ AdvancedLoginWidget::AdvancedLoginWidget() this->ui_.layout.addWidget(&this->ui_.instructionsLabel); this->ui_.layout.addLayout(&this->ui_.formLayout); this->ui_.layout.addLayout(&this->ui_.buttonUpperRow.layout); - this->ui_.layout.addLayout(&this->ui_.buttonLowerRow.layout); this->refreshButtons(); @@ -207,29 +205,10 @@ AdvancedLoginWidget::AdvancedLoginWidget() LogInWithCredentials(userID, username, clientID, oauthToken); }); - - /// Lower button row - this->ui_.buttonLowerRow.fillInUserIDButton.setText( - "Get user ID from username"); - - this->ui_.buttonLowerRow.layout.addWidget( - &this->ui_.buttonLowerRow.fillInUserIDButton); - - connect(&this->ui_.buttonLowerRow.fillInUserIDButton, &QPushButton::clicked, - [=]() { - const auto onIdFetched = [=](const QString &userID) { - this->ui_.userIDInput.setText(userID); // - }; - PartialTwitchUser::byName(this->ui_.usernameInput.text()) - .getId(onIdFetched, this); - }); } void AdvancedLoginWidget::refreshButtons() { - this->ui_.buttonLowerRow.fillInUserIDButton.setEnabled( - !this->ui_.usernameInput.text().isEmpty()); - if (this->ui_.userIDInput.text().isEmpty() || this->ui_.usernameInput.text().isEmpty() || this->ui_.clientIDInput.text().isEmpty() || diff --git a/src/widgets/dialogs/LoginDialog.hpp b/src/widgets/dialogs/LoginDialog.hpp index a6819e3af..796b98cac 100644 --- a/src/widgets/dialogs/LoginDialog.hpp +++ b/src/widgets/dialogs/LoginDialog.hpp @@ -57,12 +57,6 @@ public: QPushButton addUserButton; QPushButton clearFieldsButton; } buttonUpperRow; - - struct { - QHBoxLayout layout; - - QPushButton fillInUserIDButton; - } buttonLowerRow; } ui_; }; diff --git a/src/widgets/dialogs/LogsPopup.cpp b/src/widgets/dialogs/LogsPopup.cpp index 18e1f1a04..0afd6b9b4 100644 --- a/src/widgets/dialogs/LogsPopup.cpp +++ b/src/widgets/dialogs/LogsPopup.cpp @@ -4,7 +4,6 @@ #include "common/Channel.hpp" #include "common/NetworkRequest.hpp" #include "messages/Message.hpp" -#include "providers/twitch/PartialTwitchUser.hpp" #include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/TwitchMessageBuilder.hpp" #include "util/PostToThread.hpp" diff --git a/src/widgets/dialogs/UserInfoPopup.cpp b/src/widgets/dialogs/UserInfoPopup.cpp index 3b8307024..a4d0f2220 100644 --- a/src/widgets/dialogs/UserInfoPopup.cpp +++ b/src/widgets/dialogs/UserInfoPopup.cpp @@ -5,8 +5,9 @@ #include "common/NetworkRequest.hpp" #include "controllers/accounts/AccountController.hpp" #include "controllers/highlights/HighlightBlacklistUser.hpp" -#include "providers/twitch/PartialTwitchUser.hpp" #include "providers/twitch/TwitchChannel.hpp" +#include "providers/twitch/api/Helix.hpp" +#include "providers/twitch/api/Kraken.hpp" #include "singletons/Resources.hpp" #include "singletons/Settings.hpp" #include "util/LayoutCreator.hpp" @@ -22,9 +23,9 @@ #include #include -#define TEXT_FOLLOWERS "Followers: " -#define TEXT_VIEWS "Views: " -#define TEXT_CREATED "Created: " +const QString TEXT_VIEWS("Views: %1"); +const QString TEXT_FOLLOWERS("Followers: %1"); +const QString TEXT_CREATED("Created: %1"); #define TEXT_USER_ID "ID: " #define TEXT_UNAVAILABLE "(not available)" @@ -92,10 +93,11 @@ UserInfoPopup::UserInfoPopup() this->ui_.userIDLabel->setPalette(palette); } - vbox.emplace