From 45cda9b7a567c8f501e42102eae046b39ea92177 Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 6 Jun 2018 15:54:14 +0200 Subject: [PATCH] added follow and ignore functionality to the user popup --- src/widgets/basewindow.cpp | 4 +- src/widgets/helper/channelview.cpp | 6 + src/widgets/helper/rippleeffectbutton.cpp | 4 +- src/widgets/userinfopopup.cpp | 190 +++++++++++++++++++--- src/widgets/userinfopopup.hpp | 8 + 5 files changed, 181 insertions(+), 31 deletions(-) diff --git a/src/widgets/basewindow.cpp b/src/widgets/basewindow.cpp index a5cd03f6d..6ede48551 100644 --- a/src/widgets/basewindow.cpp +++ b/src/widgets/basewindow.cpp @@ -34,7 +34,7 @@ namespace widgets { BaseWindow::BaseWindow(QWidget *parent, Flags _flags) : BaseWidget(parent, - Qt::Window | ((_flags & TopMost) ? Qt::WindowStaysOnTopHint : Qt::WindowFlags(0))) + Qt::Window | ((_flags & TopMost) ? Qt::WindowStaysOnTopHint : Qt::WindowFlags())) , enableCustomFrame(_flags & EnableCustomFrame) , frameless(_flags & Frameless) , flags(_flags) @@ -207,7 +207,7 @@ void BaseWindow::themeRefreshEvent() bool BaseWindow::event(QEvent *event) { - if (event->type() == QEvent::WindowDeactivate) { + if (event->type() == QEvent::WindowDeactivate /*|| event->type() == QEvent::FocusOut*/) { if (this->flags & DeleteOnFocusOut) { this->close(); } diff --git a/src/widgets/helper/channelview.cpp b/src/widgets/helper/channelview.cpp index 643c5a484..f27961a22 100644 --- a/src/widgets/helper/channelview.cpp +++ b/src/widgets/helper/channelview.cpp @@ -1108,6 +1108,8 @@ void ChannelView::handleLinkClick(QMouseEvent *event, const messages::Link &link auto *userPopup = new UserInfoPopup; userPopup->setData(user, this->channel); userPopup->setAttribute(Qt::WA_DeleteOnClose); + userPopup->move(event->globalPos()); + userPopup->setFocus(); userPopup->show(); // this->userPopupWidget.setName(user); @@ -1118,15 +1120,19 @@ void ChannelView::handleLinkClick(QMouseEvent *event, const messages::Link &link qDebug() << "Clicked " << user << "s message"; break; } + case messages::Link::Url: { QDesktopServices::openUrl(QUrl(link.value)); break; } + case messages::Link::UserAction: { QString value = link.value; value.replace("{user}", layout->getMessage()->loginName); this->channel->sendMessage(value); } + + default:; } } diff --git a/src/widgets/helper/rippleeffectbutton.cpp b/src/widgets/helper/rippleeffectbutton.cpp index 61e683cc0..18d91e194 100644 --- a/src/widgets/helper/rippleeffectbutton.cpp +++ b/src/widgets/helper/rippleeffectbutton.cpp @@ -51,8 +51,6 @@ void RippleEffectButton::paintEvent(QPaintEvent *) painter.setRenderHint(QPainter::SmoothPixmapTransform); - this->fancyPaint(painter); - if (!this->pixmap_.isNull()) { QRect rect = this->rect(); int s = int(6 * this->getScale()); @@ -65,6 +63,8 @@ void RippleEffectButton::paintEvent(QPaintEvent *) painter.drawPixmap(rect, this->pixmap_); } + this->fancyPaint(painter); + if (this->borderColor_.isValid()) { painter.setRenderHint(QPainter::Antialiasing, false); painter.setPen(this->borderColor_); diff --git a/src/widgets/userinfopopup.cpp b/src/widgets/userinfopopup.cpp index 972dc31ce..f0fa9dda7 100644 --- a/src/widgets/userinfopopup.cpp +++ b/src/widgets/userinfopopup.cpp @@ -1,6 +1,7 @@ #include "userinfopopup.hpp" #include "application.hpp" +#include "providers/twitch/twitchchannel.hpp" #include "singletons/resourcemanager.hpp" #include "util/layoutcreator.hpp" #include "util/posttothread.hpp" @@ -12,11 +13,16 @@ #include #include +#define TEXT_FOLLOWERS "Followers: " +#define TEXT_VIEWS "Views: " +#define TEXT_CREATED "Created: " + namespace chatterino { namespace widgets { UserInfoPopup::UserInfoPopup() : BaseWindow(nullptr, BaseWindow::Flags(BaseWindow::Frameless | BaseWindow::DeleteOnFocusOut)) + , hack_(new bool) { auto app = getApp(); @@ -41,9 +47,9 @@ UserInfoPopup::UserInfoPopup() auto font = name->font(); font.setBold(true); name->setFont(font); - vbox.emplace("Loading...").assign(&this->ui_.viewCountLabel); - vbox.emplace().assign(&this->ui_.followerCountLabel); - vbox.emplace().assign(&this->ui_.createdDateLabel); + vbox.emplace(TEXT_VIEWS).assign(&this->ui_.viewCountLabel); + vbox.emplace(TEXT_FOLLOWERS).assign(&this->ui_.followerCountLabel); + vbox.emplace(TEXT_CREATED).assign(&this->ui_.createdDateLabel); } } @@ -54,12 +60,9 @@ UserInfoPopup::UserInfoPopup() { user->addStretch(1); - auto ignore = user.emplace("Ignore").assign(&this->ui_.ignore); - // ignore->setEnabled(false); - - auto ignoreHighlights = - user.emplace("Ignore highlights").assign(&this->ui_.ignoreHighlights); - // ignoreHighlights->setEnabled(false); + user.emplace("Follow").assign(&this->ui_.follow); + user.emplace("Ignore").assign(&this->ui_.ignore); + user.emplace("Ignore highlights").assign(&this->ui_.ignoreHighlights); auto mod = user.emplace(this); mod->setPixmap(app->resources->buttons.mod); @@ -69,17 +72,106 @@ UserInfoPopup::UserInfoPopup() unmod->setScaleIndependantSize(30, 30); user->addStretch(1); + + // userstate + this->userStateChanged.connect([this, mod, unmod]() mutable { + providers::twitch::TwitchChannel *twitchChannel = + dynamic_cast(this->channel_.get()); + + if (twitchChannel) { + qDebug() << this->userName_; + + bool isMyself = + QString::compare(getApp()->accounts->twitch.getCurrent()->getUserName(), + this->userName_, Qt::CaseInsensitive) == 0; + + mod->setVisible(twitchChannel->isBroadcaster() && !isMyself); + unmod->setVisible((twitchChannel->isBroadcaster() && !isMyself) || + (twitchChannel->isMod() && isMyself)); + } + }); } - layout.emplace(false); + auto lineMod = layout.emplace(false); // third line auto moderation = layout.emplace().withoutMargin(); { - moderation.emplace(); + auto timeout = moderation.emplace(); + + this->userStateChanged.connect([this, lineMod, timeout]() mutable { + providers::twitch::TwitchChannel *twitchChannel = + dynamic_cast(this->channel_.get()); + + if (twitchChannel) { + lineMod->setVisible(twitchChannel->hasModRights()); + timeout->setVisible(twitchChannel->hasModRights()); + } + }); } this->setStyleSheet("font-size: 11pt;"); + + this->installEvents(); +} + +void UserInfoPopup::installEvents() +{ + std::weak_ptr hack = this->hack_; + + // follow + QObject::connect(this->ui_.follow, &QCheckBox::stateChanged, [this](int) mutable { + auto currentUser = getApp()->accounts->twitch.getCurrent(); + + QUrl requestUrl("https://api.twitch.tv/kraken/users/" + currentUser->getUserId() + + "/follows/channels/" + this->userId_); + + this->ui_.follow->setEnabled(false); + if (this->ui_.follow->isChecked()) { + util::twitch::put(requestUrl, + [this](QJsonObject) { this->ui_.follow->setEnabled(true); }); + } else { + util::twitch::sendDelete(requestUrl, [this] { this->ui_.follow->setEnabled(true); }); + } + }); + + std::shared_ptr ignoreNext = std::make_shared(false); + + // ignore + QObject::connect( + this->ui_.ignore, &QCheckBox::stateChanged, [this, ignoreNext, hack](int) mutable { + if (*ignoreNext) { + *ignoreNext = false; + return; + } + + this->ui_.ignore->setEnabled(false); + + auto currentUser = getApp()->accounts->twitch.getCurrent(); + if (this->ui_.ignore->isChecked()) { + currentUser->ignoreByID(this->userId_, this->userName_, + [=](auto result, const auto &message) mutable { + if (hack.lock()) { + if (result == IgnoreResult_Failed) { + *ignoreNext = true; + this->ui_.ignore->setChecked(false); + } + this->ui_.ignore->setEnabled(true); + } + }); + } else { + currentUser->unignoreByID(this->userId_, this->userName_, + [=](auto result, const auto &message) mutable { + if (hack.lock()) { + if (result == UnignoreResult_Failed) { + *ignoreNext = true; + this->ui_.ignore->setChecked(true); + } + this->ui_.ignore->setEnabled(true); + } + }); + } + }); } void UserInfoPopup::setData(const QString &name, const ChannelPtr &channel) @@ -90,21 +182,59 @@ void UserInfoPopup::setData(const QString &name, const ChannelPtr &channel) this->ui_.nameLabel->setText(name); this->updateUserData(); + + this->userStateChanged.invoke(); } void UserInfoPopup::updateUserData() { - util::twitch::get("https://api.twitch.tv/kraken/channels/" + this->userName_, this, - [=](const QJsonObject &obj) { - this->ui_.followerCountLabel->setText( - "Followers: " + QString::number(obj.value("followers").toInt())); - this->ui_.viewCountLabel->setText( - "Views: " + QString::number(obj.value("views").toInt())); - this->ui_.createdDateLabel->setText( - "Created: " + obj.value("created_at").toString().section("T", 0, 0)); + std::weak_ptr hack = this->hack_; - this->loadAvatar(QUrl(obj.value("logo").toString())); - }); + // get user info + util::twitch::getUserID(this->userName_, this, [this, hack](QString id) { + auto currentUser = getApp()->accounts->twitch.getCurrent(); + + this->userId_ = id; + + // get channel info + util::twitch::get( + "https://api.twitch.tv/kraken/channels/" + id, this, [this](const QJsonObject &obj) { + this->ui_.followerCountLabel->setText( + TEXT_FOLLOWERS + QString::number(obj.value("followers").toInt())); + this->ui_.viewCountLabel->setText(TEXT_VIEWS + + QString::number(obj.value("views").toInt())); + this->ui_.createdDateLabel->setText( + TEXT_CREATED + obj.value("created_at").toString().section("T", 0, 0)); + + this->loadAvatar(QUrl(obj.value("logo").toString())); + }); + + // get follow state + currentUser->checkFollow(id, [this, hack](auto result) { + if (hack.lock()) { + if (result != FollowResult_Failed) { + this->ui_.follow->setEnabled(true); + this->ui_.follow->setChecked(result == FollowResult_Following); + } + } + }); + + // get ignore state + bool isIgnoring = false; + for (const auto &ignoredUser : currentUser->getIgnores()) { + if (id == ignoredUser.id) { + isIgnoring = true; + break; + } + } + + this->ui_.ignore->setEnabled(true); + this->ui_.ignore->setChecked(isIgnoring); + }); + + this->ui_.follow->setEnabled(false); + this->ui_.ignore->setEnabled(false); + this->ui_.ignoreHighlights->setEnabled(false); } void UserInfoPopup::loadAvatar(const QUrl &url) @@ -150,7 +280,8 @@ UserInfoPopup::TimeoutWidget::TimeoutWidget() { auto title = vbox.emplace().withoutMargin(); title->addStretch(1); - title.emplace("unban"); + auto label = title.emplace("unban"); + label->setStyleSheet("color: #BBB"); title->addStretch(1); auto hbox = vbox.emplace().withoutMargin(); @@ -169,7 +300,8 @@ UserInfoPopup::TimeoutWidget::TimeoutWidget() { auto title = vbox.emplace().withoutMargin(); title->addStretch(1); - title.emplace("sec"); + auto label = title.emplace("sec"); + label->setStyleSheet("color: #BBB"); title->addStretch(1); auto hbox = vbox.emplace().withoutMargin(); @@ -188,7 +320,8 @@ UserInfoPopup::TimeoutWidget::TimeoutWidget() { auto title = vbox.emplace().withoutMargin(); title->addStretch(1); - title.emplace("min"); + auto label = title.emplace("min"); + label->setStyleSheet("color: #BBB"); title->addStretch(1); auto hbox = vbox.emplace().withoutMargin(); @@ -219,7 +352,8 @@ UserInfoPopup::TimeoutWidget::TimeoutWidget() { auto title = vbox.emplace().withoutMargin(); title->addStretch(1); - title.emplace("hour"); + auto label = title.emplace("hour"); + label->setStyleSheet("color: #BBB"); title->addStretch(1); auto hbox = vbox.emplace().withoutMargin(); @@ -244,7 +378,8 @@ UserInfoPopup::TimeoutWidget::TimeoutWidget() { auto title = vbox.emplace().withoutMargin(); title->addStretch(1); - title.emplace("week"); + auto label = title.emplace("week"); + label->setStyleSheet("color: #BBB"); title->addStretch(1); auto hbox = vbox.emplace().withoutMargin(); @@ -269,7 +404,8 @@ UserInfoPopup::TimeoutWidget::TimeoutWidget() { auto title = vbox.emplace().withoutMargin(); title->addStretch(1); - title.emplace("ban"); + auto label = title.emplace("ban"); + label->setStyleSheet("color: #BBB"); title->addStretch(1); auto hbox = vbox.emplace().withoutMargin(); diff --git a/src/widgets/userinfopopup.hpp b/src/widgets/userinfopopup.hpp index 4ac2585b6..34ff9efde 100644 --- a/src/widgets/userinfopopup.hpp +++ b/src/widgets/userinfopopup.hpp @@ -13,6 +13,8 @@ namespace widgets { class UserInfoPopup final : public BaseWindow { + Q_OBJECT + public: UserInfoPopup(); @@ -23,13 +25,18 @@ private: bool isBroadcaster_; QString userName_; + QString userId_; ChannelPtr channel_; pajlada::Signals::NoArgSignal userStateChanged; + void installEvents(); + void updateUserData(); void loadAvatar(const QUrl &url); + std::shared_ptr hack_; + struct { RippleEffectButton *avatarButton = nullptr; @@ -38,6 +45,7 @@ private: QLabel *followerCountLabel = nullptr; QLabel *createdDateLabel = nullptr; + QCheckBox *follow = nullptr; QCheckBox *ignore = nullptr; QCheckBox *ignoreHighlights = nullptr; } ui_;