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:
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user