Advanced channel filters (#1748)

Adds custom channel filters complete with their own mini-language. Filters can be created in settings, and applied by clicking the three dots to open the Split menu and selecting "Set filters".
This commit is contained in:
dnsge
2020-10-18 15:16:56 +02:00
committed by Rasmus Karlsson
parent 812cbdf4f9
commit 4199a01b96
41 changed files with 2189 additions and 43 deletions
+138 -31
View File
@@ -112,6 +112,7 @@ namespace {
ChannelView::ChannelView(BaseWidget *parent)
: BaseWidget(parent)
, sourceChannel_(nullptr)
, underlyingChannel_(nullptr)
, scrollBar_(new Scrollbar(this))
{
this->setMouseTracking(true);
@@ -557,7 +558,12 @@ ChannelPtr ChannelView::channel()
return this->channel_;
}
void ChannelView::setChannel(ChannelPtr channel)
bool ChannelView::showScrollbarHighlights() const
{
return this->channel_->getType() != Channel::Type::TwitchMentions;
}
void ChannelView::setChannel(ChannelPtr underlyingChannel)
{
/// Clear connections from the last channel
this->channelConnections_.clear();
@@ -565,31 +571,88 @@ void ChannelView::setChannel(ChannelPtr channel)
this->clearMessages();
this->scrollBar_->clearHighlights();
/// make copy of channel and expose
this->channel_ = std::make_unique<Channel>(underlyingChannel->getName(),
underlyingChannel->getType());
//
// Proxy channel connections
// Use a proxy channel to keep filtered messages past the time they are removed from their origin channel
//
this->channelConnections_.push_back(
underlyingChannel->messageAppended.connect(
[this](MessagePtr &message,
boost::optional<MessageFlags> overridingFlags) {
if (this->shouldIncludeMessage(message))
{
// When the message was received in the underlyingChannel,
// logging will be handled. Prevent duplications.
if (overridingFlags)
{
overridingFlags.get().set(MessageFlag::DoNotLog);
}
else
{
overridingFlags = MessageFlags(MessageFlag::DoNotLog);
}
this->channel_->addMessage(message, overridingFlags);
}
}));
this->channelConnections_.push_back(
underlyingChannel->messagesAddedAtStart.connect(
[this](std::vector<MessagePtr> &messages) {
std::vector<MessagePtr> filtered;
std::copy_if( //
messages.begin(), messages.end(),
std::back_inserter(filtered), [this](MessagePtr msg) {
return this->shouldIncludeMessage(msg);
});
if (!filtered.empty())
this->channel_->addMessagesAtStart(filtered);
}));
this->channelConnections_.push_back(
underlyingChannel->messageReplaced.connect(
[this](size_t index, MessagePtr replacement) {
if (this->shouldIncludeMessage(replacement))
this->channel_->replaceMessage(index, replacement);
}));
//
// Standard channel connections
//
// on new message
this->channelConnections_.push_back(channel->messageAppended.connect(
this->channelConnections_.push_back(this->channel_->messageAppended.connect(
[this](MessagePtr &message,
boost::optional<MessageFlags> overridingFlags) {
this->messageAppended(message, overridingFlags);
}));
this->channelConnections_.push_back(channel->messagesAddedAtStart.connect(
[this](std::vector<MessagePtr> &messages) {
this->messageAddedAtStart(messages);
}));
this->channelConnections_.push_back(
this->channel_->messagesAddedAtStart.connect(
[this](std::vector<MessagePtr> &messages) {
this->messageAddedAtStart(messages);
}));
// on message removed
this->channelConnections_.push_back(
channel->messageRemovedFromStart.connect([this](MessagePtr &message) {
this->messageRemoveFromStart(message);
}));
this->channel_->messageRemovedFromStart.connect(
[this](MessagePtr &message) {
this->messageRemoveFromStart(message);
}));
// on message replaced
this->channelConnections_.push_back(channel->messageReplaced.connect(
this->channelConnections_.push_back(this->channel_->messageReplaced.connect(
[this](size_t index, MessagePtr replacement) {
this->messageReplaced(index, replacement);
}));
auto snapshot = channel->getMessageSnapshot();
auto snapshot = underlyingChannel->getMessageSnapshot();
for (size_t i = 0; i < snapshot.size(); i++)
{
@@ -604,22 +667,26 @@ void ChannelView::setChannel(ChannelPtr channel)
this->lastMessageHasAlternateBackground_ =
!this->lastMessageHasAlternateBackground_;
if (channel->shouldIgnoreHighlights())
if (underlyingChannel->shouldIgnoreHighlights())
{
messageLayout->flags.set(MessageLayoutFlag::IgnoreHighlights);
}
this->messages_.pushBack(MessageLayoutPtr(messageLayout), deleted);
this->scrollBar_->addHighlight(snapshot[i]->getScrollBarHighlight());
if (this->showScrollbarHighlights())
{
this->scrollBar_->addHighlight(
snapshot[i]->getScrollBarHighlight());
}
}
this->channel_ = channel;
this->underlyingChannel_ = underlyingChannel;
this->queueLayout();
this->queueUpdate();
// Notifications
if (auto tc = dynamic_cast<TwitchChannel *>(channel.get()))
if (auto tc = dynamic_cast<TwitchChannel *>(underlyingChannel.get()))
{
this->connections_.push_back(tc->liveStatusChanged.connect([this]() {
this->liveStatusChanged.invoke(); //
@@ -627,6 +694,41 @@ void ChannelView::setChannel(ChannelPtr channel)
}
}
void ChannelView::setFilters(const QList<QUuid> &ids)
{
this->channelFilters_ = std::make_shared<FilterSet>(ids);
}
const QList<QUuid> ChannelView::getFilterIds() const
{
if (!this->channelFilters_)
{
return QList<QUuid>();
}
return this->channelFilters_->filterIds();
}
FilterSetPtr ChannelView::getFilterSet() const
{
return this->channelFilters_;
}
bool ChannelView::shouldIncludeMessage(const MessagePtr &m) const
{
if (this->channelFilters_)
{
if (getSettings()->excludeUserMessagesFromFilter &&
getApp()->accounts->twitch.getCurrent()->getUserName().compare(
m->loginName, Qt::CaseInsensitive) == 0)
return true;
return this->channelFilters_->filter(m);
}
return true;
}
ChannelPtr ChannelView::sourceChannel() const
{
return this->sourceChannel_;
@@ -695,7 +797,7 @@ void ChannelView::messageAppended(MessagePtr &message,
}
}
if (this->channel_->getType() != Channel::Type::TwitchMentions)
if (this->showScrollbarHighlights())
{
this->scrollBar_->addHighlight(message->getScrollBarHighlight());
}
@@ -712,7 +814,8 @@ void ChannelView::messageAddedAtStart(std::vector<MessagePtr> &messages)
/// Create message layouts
for (size_t i = 0; i < messages.size(); i++)
{
auto layout = new MessageLayout(messages.at(i));
auto message = messages.at(i);
auto layout = new MessageLayout(message);
// alternate color
if (!this->lastMessageHasAlternateBackgroundReverse_)
@@ -732,15 +835,17 @@ void ChannelView::messageAddedAtStart(std::vector<MessagePtr> &messages)
this->scrollBar_->offset(qreal(messages.size()));
}
/// Add highlights
std::vector<ScrollbarHighlight> highlights;
highlights.reserve(messages.size());
for (size_t i = 0; i < messages.size(); i++)
if (this->showScrollbarHighlights())
{
highlights.push_back(messages.at(i)->getScrollBarHighlight());
}
std::vector<ScrollbarHighlight> highlights;
highlights.reserve(messages.size());
for (const auto &message : messages)
{
highlights.push_back(message->getScrollBarHighlight());
}
this->scrollBar_->addHighlightsAtStart(highlights);
this->scrollBar_->addHighlightsAtStart(highlights);
}
this->messageWasAdded_ = true;
this->queueLayout();
@@ -855,7 +960,7 @@ MessageElementFlags ChannelView::getFlags() const
{
flags.set(MessageElementFlag::ModeratorTools);
}
if (this->channel_ == app->twitch.server->mentionsChannel)
if (this->underlyingChannel_ == app->twitch.server->mentionsChannel)
{
flags.set(MessageElementFlag::ChannelName);
flags.unset(MessageElementFlag::ChannelPointReward);
@@ -906,7 +1011,8 @@ void ChannelView::drawMessages(QPainter &painter)
bool windowFocused = this->window() == QApplication::activeWindow();
auto app = getApp();
bool isMentions = this->channel_ == app->twitch.server->mentionsChannel;
bool isMentions =
this->underlyingChannel_ == app->twitch.server->mentionsChannel;
for (size_t i = start; i < messagesSnapshot.size(); ++i)
{
@@ -1830,8 +1936,9 @@ void ChannelView::hideEvent(QHideEvent *)
void ChannelView::showUserInfoPopup(const QString &userName)
{
auto *userPopup = new UserInfoPopup(getSettings()->autoCloseUserPopup);
userPopup->setData(userName, this->hasSourceChannel() ? this->sourceChannel_
: this->channel_);
userPopup->setData(userName, this->hasSourceChannel()
? this->sourceChannel_
: this->underlyingChannel_);
QPoint offset(int(150 * this->scale()), int(70 * this->scale()));
userPopup->move(QCursor::pos() - offset);
userPopup->show();
@@ -1871,9 +1978,9 @@ void ChannelView::handleLinkClick(QMouseEvent *event, const Link &link,
.replace("{msg-id}", layout->getMessage()->id)
.replace("{message}", layout->getMessage()->messageText);
value =
getApp()->commands->execCommand(value, this->channel_, false);
this->channel_->sendMessage(value);
value = getApp()->commands->execCommand(
value, this->underlyingChannel_, false);
this->underlyingChannel_->sendMessage(value);
}
break;