Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into chatterino7
Browse files Browse the repository at this point in the history
  • Loading branch information
Nerixyz committed Apr 16, 2024
2 parents db4456f + c391ff9 commit c1b8963
Show file tree
Hide file tree
Showing 20 changed files with 311 additions and 117 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/check-formatting.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
run: sudo apt-get -y install dos2unix

- name: Check formatting
uses: DoozyX/clang-format-lint-action@v0.16.2
uses: DoozyX/clang-format-lint-action@v0.17
with:
source: "./src ./tests/src ./benchmarks/src ./mocks/include"
extensions: "hpp,cpp"
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

## Unversioned

- Minor: Added context menu action to toggle visibility of offline tabs. (#5318)
- Minor: Report sub duration for more multi-month gift cases. (#5319)
- Minor: Improved error reporting for the automatic streamer mode detection on Linux and macOS. (#5321)
- Bugfix: Fixed a crash that could occur on Wayland when using the image uploader. (#5314)
- Bugfix: Fixed split tooltip getting stuck in some cases. (#5309)
- Bugfix: Fixed the version string not showing up as expected in Finder on macOS. (#5311)
- Bugfix: Fixed links having `http://` added to the beginning in certain cases. (#5323)

## 2.5.0-beta.1

- Major: Twitch follower emotes can now be correctly tabbed in other channels when you are subscribed to the channel the emote is from. (#4922)
Expand Down
2 changes: 0 additions & 2 deletions cmake/MacOSXBundleInfo.plist.in
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
<string>English</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleGetInfoString</key>
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>CFBundleIdentifier</key>
Expand Down
2 changes: 1 addition & 1 deletion lib/settings
Binary file added resources/avatars/nealxm.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions resources/contributors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ crazysmc | https://github.com/crazysmc | :/avatars/crazysmc.png
SputNikPlop | https://github.com/SputNikPlop |
fraxx | https://github.com/fraxxio | :/avatars/fraxx.png
KleberPF | https://github.com/KleberPF |
nealxm | https://github.com/nealxm | :/avatars/nealxm.png

# If you are a contributor add yourself above this line

Expand Down
1 change: 0 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1010,7 +1010,6 @@ if (APPLE AND BUILD_APP)
PROPERTIES
MACOSX_BUNDLE_BUNDLE_NAME "Chatterino"
MACOSX_BUNDLE_GUI_IDENTIFIER "com.chatterino"
MACOSX_BUNDLE_INFO_STRING "Chat client for Twitch"
MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_VERSION}"
MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}"
MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION}"
Expand Down
2 changes: 1 addition & 1 deletion src/messages/MessageBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@ void MessageBuilder::addLink(const ParsedLink &parsedLink)
auto textColor = MessageColor(MessageColor::Link);
auto *el = this->emplace<LinkElement>(
LinkElement::Parsed{.lowercase = lowercaseLinkString,
.original = matchedLink},
.original = origLink},
MessageElementFlag::Text, textColor);
el->setLink({Link::Url, matchedLink});
getIApp()->getLinkResolver()->resolve(el->linkInfo());
Expand Down
50 changes: 40 additions & 10 deletions src/providers/twitch/IrcMessageHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -518,8 +518,7 @@ std::vector<MessagePtr> parseUserNoticeMessage(Channel *channel,
{
messageText = "Announcement";
}
else if (msgType == "subgift" &&
ANONYMOUS_GIFTER_ID == tags.value("user-id").toString())
else if (msgType == "subgift")
{
if (auto monthsIt = tags.find("msg-param-gift-months");
monthsIt != tags.end())
Expand All @@ -528,13 +527,29 @@ std::vector<MessagePtr> parseUserNoticeMessage(Channel *channel,
if (months > 1)
{
auto plan = tags.value("msg-param-sub-plan").toString();
QString name =
ANONYMOUS_GIFTER_ID == tags.value("user-id").toString()
? "An anonymous user"
: tags.value("display-name").toString();
messageText =
QString("An anonymous user gifted %1 months of a Tier "
"%2 sub to %3!")
.arg(QString::number(months),
QString("%1 gifted %2 months of a Tier %3 sub to %4!")
.arg(name, QString::number(months),
plan.isEmpty() ? '1' : plan.at(0),
tags.value("msg-param-recipient-display-name")
.toString());

if (auto countIt = tags.find("msg-param-sender-count");
countIt != tags.end())
{
int count = countIt.value().toInt();
if (count > months)
{
messageText +=
QString(
" They've gifted %1 months in the channel.")
.arg(QString::number(count));
}
}
}
}
}
Expand Down Expand Up @@ -1032,8 +1047,7 @@ void IrcMessageHandler::handleUserNoticeMessage(Communi::IrcMessage *message,
{
messageText = "Announcement";
}
else if (msgType == "subgift" &&
ANONYMOUS_GIFTER_ID == tags.value("user-id").toString())
else if (msgType == "subgift")
{
if (auto monthsIt = tags.find("msg-param-gift-months");
monthsIt != tags.end())
Expand All @@ -1042,13 +1056,29 @@ void IrcMessageHandler::handleUserNoticeMessage(Communi::IrcMessage *message,
if (months > 1)
{
auto plan = tags.value("msg-param-sub-plan").toString();
QString name =
ANONYMOUS_GIFTER_ID == tags.value("user-id").toString()
? "An anonymous user"
: tags.value("display-name").toString();
messageText =
QString("An anonymous user gifted %1 months of a Tier "
"%2 sub to %3!")
.arg(QString::number(months),
QString("%1 gifted %2 months of a Tier %3 sub to %4!")
.arg(name, QString::number(months),
plan.isEmpty() ? '1' : plan.at(0),
tags.value("msg-param-recipient-display-name")
.toString());

if (auto countIt = tags.find("msg-param-sender-count");
countIt != tags.end())
{
int count = countIt.value().toInt();
if (count > months)
{
messageText +=
QString(
" They've gifted %1 months in the channel.")
.arg(QString::number(count));
}
}
}
}
}
Expand Down
145 changes: 84 additions & 61 deletions src/singletons/ImageUploader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "common/network/NetworkRequest.hpp"
#include "common/network/NetworkResult.hpp"
#include "common/QLogging.hpp"
#include "debug/Benchmark.hpp"
#include "messages/MessageBuilder.hpp"
#include "providers/twitch/TwitchMessageBuilder.hpp"
#include "singletons/Paths.hpp"
Expand All @@ -21,6 +22,8 @@
#include <QPointer>
#include <QSaveFile>

#include <utility>

#define UPLOAD_DELAY 2000
// Delay between uploads in milliseconds

Expand Down Expand Up @@ -195,6 +198,11 @@ void ImageUploader::handleFailedUpload(const NetworkResult &result,
}

channel->addMessage(makeSystemMessage(errorMessage));
// NOTE: We abort any future uploads on failure. Should this be handled differently?
while (!this->uploadQueue_.empty())
{
this->uploadQueue_.pop();
}
this->uploadMutex_.unlock();
}

Expand Down Expand Up @@ -248,22 +256,20 @@ void ImageUploader::handleSuccessfulUpload(const NetworkResult &result,
this->logToFile(originalFilePath, link, deletionLink, channel);
}

void ImageUploader::upload(const QMimeData *source, ChannelPtr channel,
QPointer<ResizingTextEdit> outputTextEdit)
std::pair<std::queue<RawImageData>, QString> ImageUploader::getImages(
const QMimeData *source) const
{
if (!this->uploadMutex_.tryLock())
{
channel->addMessage(makeSystemMessage(
QString("Please wait until the upload finishes.")));
return;
}
BenchmarkGuard benchmarkGuard("ImageUploader::getImages");

channel->addMessage(makeSystemMessage(QString("Started upload...")));
auto tryUploadFromUrls = [&]() -> bool {
auto tryUploadFromUrls =
[&]() -> std::pair<std::queue<RawImageData>, QString> {
if (!source->hasUrls())
{
return false;
return {{}, {}};
}

std::queue<RawImageData> images;

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.
Expand All @@ -273,101 +279,118 @@ void ImageUploader::upload(const QMimeData *source, ChannelPtr channel,
QMimeType mime = mimeDb.mimeTypeForUrl(path);
if (mime.name().startsWith("image") && !mime.inherits("image/gif"))
{
channel->addMessage(makeSystemMessage(
QString("Uploading image: %1").arg(localPath)));
QImage img = QImage(localPath);
if (img.isNull())
{
channel->addMessage(
makeSystemMessage(QString("Couldn't load image :(")));
return false;
return {{}, "Couldn't load image :("};
}

auto imageData = convertToPng(img);
if (imageData)
{
RawImageData data = {*imageData, "png", localPath};
this->uploadQueue_.push(data);
}
else
if (!imageData)
{
channel->addMessage(makeSystemMessage(
return {
{},
QString("Cannot upload file: %1. Couldn't convert "
"image to png.")
.arg(localPath)));
return false;
.arg(localPath),
};
}
images.push({*imageData, "png", localPath});
}
else if (mime.inherits("image/gif"))
{
channel->addMessage(makeSystemMessage(
QString("Uploading GIF: %1").arg(localPath)));
QFile file(localPath);
bool isOkay = file.open(QIODevice::ReadOnly);
if (!isOkay)
{
channel->addMessage(
makeSystemMessage(QString("Failed to open file. :(")));
return false;
return {{}, "Failed to open file :("};
}
// file.readAll() => might be a bit big but it /should/ work
RawImageData data = {file.readAll(), "gif", localPath};
this->uploadQueue_.push(data);
images.push({file.readAll(), "gif", localPath});
file.close();
}
}
if (!this->uploadQueue_.empty())
{
this->sendImageUploadRequest(this->uploadQueue_.front(), channel,
outputTextEdit);
this->uploadQueue_.pop();
return true;
}
return false;

return {images, {}};
};

auto tryUploadDirectly = [&]() -> bool {
auto tryUploadDirectly =
[&]() -> std::pair<std::queue<RawImageData>, QString> {
std::queue<RawImageData> images;

if (source->hasFormat("image/png"))
{
// the path to file is not present every time, thus the filePath is empty
this->sendImageUploadRequest({source->data("image/png"), "png", ""},
channel, outputTextEdit);
return true;
images.push({source->data("image/png"), "png", ""});
return {images, {}};
}

if (source->hasFormat("image/jpeg"))
{
this->sendImageUploadRequest(
{source->data("image/jpeg"), "jpeg", ""}, channel,
outputTextEdit);
return true;
images.push({source->data("image/jpeg"), "jpeg", ""});
return {images, {}};
}

if (source->hasFormat("image/gif"))
{
this->sendImageUploadRequest({source->data("image/gif"), "gif", ""},
channel, outputTextEdit);
return true;
images.push({source->data("image/gif"), "gif", ""});
return {images, {}};
}

// not PNG, try loading it into QImage and save it to a PNG.
auto image = qvariant_cast<QImage>(source->imageData());
auto imageData = convertToPng(image);
if (imageData)
{
sendImageUploadRequest({*imageData, "png", ""}, channel,
outputTextEdit);
return true;
images.push({*imageData, "png", ""});
return {images, {}};
}

// No direct upload happenned
channel->addMessage(makeSystemMessage(
QString("Cannot upload file, failed to convert to png.")));
return false;
return {{}, "Cannot upload file, failed to convert to png."};
};

if (!tryUploadFromUrls() && !tryUploadDirectly())
const auto [urlImageData, urlError] = tryUploadFromUrls();

if (!urlImageData.empty())
{
channel->addMessage(
makeSystemMessage(QString("Cannot upload file from clipboard.")));
this->uploadMutex_.unlock();
return {urlImageData, {}};
}

const auto [directImageData, directError] = tryUploadDirectly();
if (!directImageData.empty())
{
return {directImageData, {}};
}

return {
{},
// TODO: verify that this looks ok xd
urlError + directError,
};
}

void ImageUploader::upload(std::queue<RawImageData> images, ChannelPtr channel,
QPointer<ResizingTextEdit> outputTextEdit)
{
BenchmarkGuard benchmarkGuard("upload");
if (!this->uploadMutex_.tryLock())
{
channel->addMessage(makeSystemMessage(
QString("Please wait until the upload finishes.")));
return;
}

assert(!images.empty());
assert(this->uploadQueue_.empty());

std::swap(this->uploadQueue_, images);

channel->addMessage(makeSystemMessage("Started upload..."));

this->sendImageUploadRequest(this->uploadQueue_.front(), std::move(channel),
std::move(outputTextEdit));
this->uploadQueue_.pop();
}

} // namespace chatterino
10 changes: 9 additions & 1 deletion src/singletons/ImageUploader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,16 @@ struct RawImageData {
class ImageUploader final : public Singleton
{
public:
/**
* Tries to get the image(s) from the given QMimeData
*
* If no images were found, the second value in the pair will contain an error message
*/
std::pair<std::queue<RawImageData>, QString> getImages(
const QMimeData *source) const;

void save() override;
void upload(const QMimeData *source, ChannelPtr channel,
void upload(std::queue<RawImageData> images, ChannelPtr channel,
QPointer<ResizingTextEdit> outputTextEdit);

private:
Expand Down
Loading

0 comments on commit c1b8963

Please sign in to comment.