Added support for Twitch's Chat Replies (#3722)

Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
This commit is contained in:
Daniel Sage
2022-07-31 06:45:25 -04:00
committed by GitHub
parent a280089693
commit 20c974fdab
53 changed files with 2022 additions and 310 deletions
+146 -5
View File
@@ -44,6 +44,7 @@
#include "widgets/Scrollbar.hpp"
#include "widgets/TooltipWidget.hpp"
#include "widgets/Window.hpp"
#include "widgets/dialogs/ReplyThreadPopup.hpp"
#include "widgets/dialogs/SettingsDialog.hpp"
#include "widgets/dialogs/UserInfoPopup.hpp"
#include "widgets/helper/EffectLabel.hpp"
@@ -119,9 +120,11 @@ namespace {
}
} // namespace
ChannelView::ChannelView(BaseWidget *parent)
ChannelView::ChannelView(BaseWidget *parent, Split *split, Context context)
: BaseWidget(parent)
, scrollBar_(new Scrollbar(this))
, split_(split)
, context_(context)
{
this->setMouseTracking(true);
@@ -160,7 +163,7 @@ ChannelView::ChannelView(BaseWidget *parent)
// and tabbing to it from another widget. I don't currently know
// of any place where you can, or where it would make sense,
// to tab to a ChannelVieChannelView
this->setFocusPolicy(Qt::FocusPolicy::StrongFocus);
this->setFocusPolicy(Qt::FocusPolicy::ClickFocus);
}
void ChannelView::initializeLayout()
@@ -1019,6 +1022,17 @@ MessageElementFlags ChannelView::getFlags() const
if (this->sourceChannel_ == app->twitch->mentionsChannel)
flags.set(MessageElementFlag::ChannelName);
if (this->context_ == Context::ReplyThread)
{
// Don't show inline replies within the ReplyThreadPopup
flags.unset(MessageElementFlag::RepliedMessage);
}
if (!this->canReplyToMessages())
{
flags.unset(MessageElementFlag::ReplyButton);
}
return flags;
}
@@ -1983,10 +1997,27 @@ void ChannelView::addMessageContextMenuItems(
menu.addAction("Copy full message", [layout] {
QString copyString;
layout->addSelectionText(copyString);
layout->addSelectionText(copyString, 0, INT_MAX,
CopyMode::EverythingButReplies);
crossPlatformCopy(copyString);
});
// Only display reply option where it makes sense
if (this->canReplyToMessages() && layout->isReplyable())
{
const auto &messagePtr = layout->getMessagePtr();
menu.addAction("Reply to message", [this, &messagePtr] {
this->setInputReply(messagePtr);
});
if (messagePtr->replyThread != nullptr)
{
menu.addAction("View thread", [this, &messagePtr] {
this->showReplyThreadPopup(messagePtr);
});
}
}
}
void ChannelView::addTwitchLinkContextMenuItems(
@@ -2224,8 +2255,8 @@ void ChannelView::showUserInfoPopup(const QString &userName,
{
auto *userCardParent =
static_cast<QWidget *>(&(getApp()->windows->getMainWindow()));
auto *userPopup =
new UserInfoPopup(getSettings()->autoCloseUserPopup, userCardParent);
auto *userPopup = new UserInfoPopup(getSettings()->autoCloseUserPopup,
userCardParent, this->split_);
auto contextChannel =
getApp()->twitch->getChannelOrEmpty(alternativePopoutChannel);
@@ -2351,6 +2382,14 @@ void ChannelView::handleLinkClick(QMouseEvent *event, const Link &link,
this->underlyingChannel_.get()->reconnect();
}
break;
case Link::ReplyToMessage: {
this->setInputReply(layout->getMessagePtr());
}
break;
case Link::ViewThread: {
this->showReplyThreadPopup(layout->getMessagePtr());
}
break;
default:;
}
@@ -2473,4 +2512,106 @@ void ChannelView::scrollUpdateRequested()
this->scrollBar_->offset(multiplier * offset);
}
void ChannelView::setInputReply(const MessagePtr &message)
{
if (message == nullptr)
{
return;
}
std::shared_ptr<MessageThread> thread;
if (message->replyThread == nullptr)
{
auto getThread = [&](TwitchChannel *tc) {
auto threadIt = tc->threads().find(message->id);
if (threadIt != tc->threads().end() && !threadIt->second.expired())
{
return threadIt->second.lock();
}
else
{
auto thread = std::make_shared<MessageThread>(message);
tc->addReplyThread(thread);
return thread;
}
};
if (auto tc =
dynamic_cast<TwitchChannel *>(this->underlyingChannel_.get()))
{
thread = getThread(tc);
}
else if (auto tc = dynamic_cast<TwitchChannel *>(this->channel_.get()))
{
thread = getThread(tc);
}
else
{
qCWarning(chatterinoCommon) << "Failed to create new reply thread";
// Unable to create new reply thread.
// TODO(dnsge): Should probably notify user?
return;
}
}
else
{
thread = message->replyThread;
}
this->split_->setInputReply(thread);
}
void ChannelView::showReplyThreadPopup(const MessagePtr &message)
{
if (message == nullptr || message->replyThread == nullptr)
{
return;
}
auto popupParent =
static_cast<QWidget *>(&(getApp()->windows->getMainWindow()));
auto popup = new ReplyThreadPopup(getSettings()->autoCloseThreadPopup,
popupParent, this->split_);
popup->setThread(message->replyThread);
QPoint offset(int(150 * this->scale()), int(70 * this->scale()));
popup->move(QCursor::pos() - offset);
popup->show();
popup->giveFocus(Qt::MouseFocusReason);
}
ChannelView::Context ChannelView::getContext() const
{
return this->context_;
}
bool ChannelView::canReplyToMessages() const
{
if (this->context_ == ChannelView::Context::ReplyThread ||
this->context_ == ChannelView::Context::Search)
{
return false;
}
if (this->channel_ == nullptr)
{
return false;
}
if (!this->channel_->isTwitchChannel())
{
return false;
}
if (this->channel_->getType() == Channel::Type::TwitchWhispers ||
this->channel_->getType() == Channel::Type::TwitchLive)
{
return false;
}
return true;
}
} // namespace chatterino