mirror-chatterino2/src/singletons/Toasts.cpp

260 lines
7.9 KiB
C++
Raw Normal View History

2018-08-11 12:47:03 +02:00
#include "Toasts.hpp"
#include "Application.hpp"
2018-08-19 19:02:49 +02:00
#include "common/DownloadManager.hpp"
2018-08-19 15:09:00 +02:00
#include "common/NetworkRequest.hpp"
2018-08-11 12:47:03 +02:00
#include "controllers/notifications/NotificationController.hpp"
#include "providers/twitch/TwitchChannel.hpp"
2018-08-19 15:09:00 +02:00
#include "providers/twitch/TwitchCommon.hpp"
#include "providers/twitch/TwitchIrcServer.hpp"
2018-11-25 15:02:48 +01:00
#include "singletons/Paths.hpp"
#include "util/StreamLink.hpp"
#include "widgets/helper/CommonTexts.hpp"
2018-08-11 12:47:03 +02:00
2018-08-12 18:54:32 +02:00
#ifdef Q_OS_WIN
2018-08-29 19:25:37 +02:00
# include <wintoastlib.h>
2018-08-12 18:54:32 +02:00
#endif
#include <QDesktopServices>
2018-08-19 15:09:00 +02:00
#include <QFileInfo>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QUrl>
#include <cstdlib>
2018-08-11 12:47:03 +02:00
namespace chatterino {
std::map<ToastReaction, QString> Toasts::reactionToString = {
{ToastReaction::OpenInBrowser, OPEN_IN_BROWSER},
{ToastReaction::OpenInPlayer, OPEN_PLAYER_IN_BROWSER},
{ToastReaction::OpenInStreamlink, OPEN_IN_STREAMLINK},
{ToastReaction::DontOpen, DONT_OPEN}};
2018-08-12 18:54:32 +02:00
bool Toasts::isEnabled()
{
2018-09-01 13:01:54 +02:00
#ifdef Q_OS_WIN
2018-08-12 18:54:32 +02:00
return WinToastLib::WinToast::isCompatible() &&
2018-08-29 19:25:37 +02:00
getSettings()->notificationToast;
#else
2018-09-01 13:01:54 +02:00
return false;
#endif
2018-08-12 18:54:32 +02:00
}
QString Toasts::findStringFromReaction(const ToastReaction &reaction)
{
auto iterator = Toasts::reactionToString.find(reaction);
if (iterator != Toasts::reactionToString.end())
{
return iterator->second;
}
else
{
return DONT_OPEN;
}
}
QString Toasts::findStringFromReaction(
const pajlada::Settings::Setting<int> &value)
{
int i = static_cast<int>(value);
return Toasts::findStringFromReaction(static_cast<ToastReaction>(i));
}
2018-08-12 18:54:32 +02:00
void Toasts::sendChannelNotification(const QString &channelName, Platform p)
2018-08-11 12:47:03 +02:00
{
2018-09-30 19:15:17 +02:00
#ifdef Q_OS_WIN
auto sendChannelNotification = [this, channelName, p] {
this->sendWindowsNotification(channelName, p);
};
#else
auto sendChannelNotification = [] {
// Unimplemented for OSX and Linux
};
#endif
2018-08-19 19:02:49 +02:00
// Fetch user profile avatar
2018-10-21 13:43:02 +02:00
if (p == Platform::Twitch)
{
2018-08-31 18:18:05 +02:00
QFileInfo check_file(getPaths()->twitchProfileAvatars + "/twitch/" +
channelName + ".png");
2018-10-21 13:43:02 +02:00
if (check_file.exists() && check_file.isFile())
{
2018-09-30 19:15:17 +02:00
sendChannelNotification();
2018-10-21 13:43:02 +02:00
}
else
{
2018-08-19 19:02:49 +02:00
this->fetchChannelAvatar(
2018-09-30 19:15:17 +02:00
channelName,
[channelName, sendChannelNotification](QString avatarLink) {
2018-08-19 19:02:49 +02:00
DownloadManager *manager = new DownloadManager();
manager->setFile(avatarLink, channelName);
2018-09-30 19:15:17 +02:00
manager->connect(manager,
&DownloadManager::downloadComplete,
sendChannelNotification);
2018-08-19 19:02:49 +02:00
});
}
}
2018-08-11 12:47:03 +02:00
}
#ifdef Q_OS_WIN
class CustomHandler : public WinToastLib::IWinToastHandler
{
2018-08-19 15:09:00 +02:00
private:
QString channelName_;
Platform platform_;
public:
2018-08-19 15:09:00 +02:00
CustomHandler(QString channelName, Platform p)
: channelName_(channelName)
, platform_(p)
{
}
void toastActivated() const
{
2018-08-19 15:09:00 +02:00
QString link;
auto toastReaction =
static_cast<ToastReaction>(getSettings()->openFromToast.getValue());
switch (toastReaction)
{
case ToastReaction::OpenInBrowser:
if (platform_ == Platform::Twitch)
{
link = "http://www.twitch.tv/" + channelName_;
}
QDesktopServices::openUrl(QUrl(link));
break;
case ToastReaction::OpenInPlayer:
if (platform_ == Platform::Twitch)
{
link = "https://player.twitch.tv/?channel=" + channelName_;
}
QDesktopServices::openUrl(QUrl(link));
break;
case ToastReaction::OpenInStreamlink:
{
openStreamlinkForChannel(channelName_);
break;
}
// the fourth and last option is "don't open"
// in this case obviously nothing should happen
}
}
void toastActivated(int actionIndex) const
{
}
void toastFailed() const
{
}
2018-08-19 15:09:00 +02:00
void toastDismissed(WinToastDismissalReason state) const
{
}
};
2018-08-12 18:54:32 +02:00
void Toasts::sendWindowsNotification(const QString &channelName, Platform p)
{
WinToastLib::WinToastTemplate templ = WinToastLib::WinToastTemplate(
2018-08-29 23:39:02 +02:00
WinToastLib::WinToastTemplate::ImageAndText03);
2018-08-19 15:09:00 +02:00
QString str = channelName + " is live!";
std::string utf8_text = str.toUtf8().constData();
std::wstring widestr = std::wstring(utf8_text.begin(), utf8_text.end());
templ.setTextField(widestr, WinToastLib::WinToastTemplate::FirstLine);
if (static_cast<ToastReaction>(getSettings()->openFromToast.getValue()) !=
ToastReaction::DontOpen)
{
QString mode =
Toasts::findStringFromReaction(getSettings()->openFromToast);
mode = mode.toLower();
templ.setTextField(L"Click here to " + mode.toStdWString(),
WinToastLib::WinToastTemplate::SecondLine);
}
2018-08-19 15:09:00 +02:00
QString Path;
2018-10-21 13:43:02 +02:00
if (p == Platform::Twitch)
{
2018-08-29 23:39:02 +02:00
Path = getPaths()->twitchProfileAvatars + "/twitch/" + channelName +
".png";
2018-08-19 15:09:00 +02:00
}
std::string temp_Utf8 = Path.toUtf8().constData();
std::wstring imagePath = std::wstring(temp_Utf8.begin(), temp_Utf8.end());
templ.setImagePath(imagePath);
2018-10-21 13:43:02 +02:00
if (getSettings()->notificationPlaySound)
{
templ.setAudioOption(
WinToastLib::WinToastTemplate::AudioOption::Silent);
}
WinToastLib::WinToast::instance()->setAppName(L"Chatterino2");
int mbstowcs(wchar_t * aumi_version, const char *CHATTERINO_VERSION,
size_t size);
std::string(CHATTERINO_VERSION);
std::wstring aumi_version =
std::wstring(CHATTERINO_VERSION.begin(), CHATTERINO_VERSION.end());
WinToastLib::WinToast::instance()->setAppUserModelId(
WinToastLib::WinToast::configureAUMI(L"", L"Chatterino 2", L"",
aumi_version));
WinToastLib::WinToast::instance()->initialize();
2018-08-19 15:09:00 +02:00
WinToastLib::WinToast::instance()->showToast(
templ, new CustomHandler(channelName, p));
}
2018-08-19 15:09:00 +02:00
#endif
2018-08-19 15:09:00 +02:00
void Toasts::fetchChannelAvatar(const QString channelName,
std::function<void(QString)> successCallback)
{
QString requestUrl("https://api.twitch.tv/kraken/users?login=" +
channelName);
2019-08-20 21:50:36 +02:00
NetworkRequest(requestUrl)
Improvements to Message Search (#1237) * Ran clang-format * Implement user-specific search in message history This functionality was originally requested in #1236. This commit changes the SearchPopup::performSearch method so that only messages from specific users can be shown. In order to filter for a specific user, enter their username with a leading '@' in the search popup. You can also add an additional search phrase which will also be considered in the search. * Naive implementation for "from:" tags Rebase later? * Cleverer (?) version using Predicates Commit adds two POC predicates: one for the author of messages, and one for substring search in messages. Problems/TODOs: * Best way to register new predicates? * Clean up tags (e.g. "from:") or not? * Test combinations of different predicates * Add a predicate to check for links in messages * Remove a dumb TODO * Rewrite SearchPopup::performSearch to be cleaner * Ran clang-format on all files * Remove TODO I missed earlier * Forgot to run clang-format peepoSadDank * Re-use {}-initialization Was accidentally removed when fixing earlier merge conflict. * Does this fix line endings? No diffs are shown locally, hopefully Git doesn't lie to me. * Rename "predicates" directory to "search" Resolving one conversation in the review of #1237. * Use LinkParser in LinkPredicate Resolving a conversation in the review of #1237. * Predicates: Use unique_ptr instead of shared_ptr Resolves a conversation in the review of #1237. * Refactor of SearchPopup and AuthorPredicate Resolving some points from the review in #1237. * Moved parsing of comma-seperated values into AuthorPredicate constructor. * Rewrite SearchPopup::parsePredicates as suggested. * Deleted now redundant methods in SearchPopup. * MessagePredicate::appliesTo now takes a Message& ... instead of a MessagePtr. This resolves a conversation in the review of #1237. * Run clang-format on two files I missed * AuthorPredicate: Check for displayName & loginName Resolving conversation on #1237.
2019-09-09 15:21:49 +02:00
2019-08-20 21:50:36 +02:00
.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();
2018-08-19 15:09:00 +02:00
}
2018-08-11 12:47:03 +02:00
} // namespace chatterino