diff --git a/CHANGELOG.md b/CHANGELOG.md index a8bc08e45..f1e236653 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,14 @@ ## Unversioned +- Major: New split for channels going live! /live. (#1797) - Minor: Hosting messages are now clickable. (#2655) - Minor: Messages held by automod are now shown to the user. (#2626) - Bugfix: Strip newlines from stream titles to prevent text going off of split header (#2755) - Bugfix: Automod messages now work properly again. (#2682) - Bugfix: `Login expired` message no longer highlights all tabs. (#2735) - Bugfix: Fix a deadlock that would occur during user badge loading. (#1704, #2756) +- Bugfix: Tabbing in `Select a channel to open` is now consistent. (#1797) ## 2.3.1 diff --git a/src/common/Channel.hpp b/src/common/Channel.hpp index d95079f0e..6b83c90e6 100644 --- a/src/common/Channel.hpp +++ b/src/common/Channel.hpp @@ -36,6 +36,7 @@ public: TwitchWhispers, TwitchWatching, TwitchMentions, + TwitchLive, TwitchEnd, Irc, Misc diff --git a/src/controllers/notifications/NotificationController.cpp b/src/controllers/notifications/NotificationController.cpp index 5afe585ac..a8f870394 100644 --- a/src/controllers/notifications/NotificationController.cpp +++ b/src/controllers/notifications/NotificationController.cpp @@ -6,6 +6,7 @@ #include "common/QLogging.hpp" #include "controllers/notifications/NotificationModel.hpp" #include "providers/twitch/TwitchIrcServer.hpp" +#include "providers/twitch/TwitchMessageBuilder.hpp" #include "providers/twitch/api/Helix.hpp" #include "singletons/Toasts.hpp" #include "singletons/WindowManager.hpp" @@ -185,6 +186,9 @@ void NotificationController::getFakeTwitchChannelLiveStatus( { getApp()->windows->sendAlert(); } + MessageBuilder builder; + TwitchMessageBuilder::liveMessage(channelName, &builder); + getApp()->twitch2->liveChannel->addMessage(builder.release()); // Indicate that we have pushed notifications for this stream fakeTwitchChannels.push_back(channelName); diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index 9ce7d606d..de9392cc3 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -563,10 +563,25 @@ void TwitchChannel::setLive(bool newLiveStatus) getApp()->windows->sendAlert(); } } + // Channel live message MessageBuilder builder; TwitchMessageBuilder::liveSystemMessage(this->getDisplayName(), &builder); this->addMessage(builder.release()); + + // Message in /live channel + MessageBuilder builder2; + TwitchMessageBuilder::liveMessage(this->getDisplayName(), + &builder2); + getApp()->twitch2->liveChannel->addMessage(builder2.release()); + + // Notify on all channels with a ping sound + if (getSettings()->notificationOnAnyChannel && + !(isInStreamerMode() && + getSettings()->streamerModeSuppressLiveNotifications)) + { + getApp()->notifications->playSound(); + } } else { diff --git a/src/providers/twitch/TwitchIrcServer.cpp b/src/providers/twitch/TwitchIrcServer.cpp index 148b5c3ae..fdefd32d1 100644 --- a/src/providers/twitch/TwitchIrcServer.cpp +++ b/src/providers/twitch/TwitchIrcServer.cpp @@ -28,6 +28,7 @@ TwitchIrcServer::TwitchIrcServer() : whispersChannel(new Channel("/whispers", Channel::Type::TwitchWhispers)) , mentionsChannel(new Channel("/mentions", Channel::Type::TwitchMentions)) , watchingChannel(Channel::getEmpty(), Channel::Type::TwitchWatching) + , liveChannel(new Channel("/live", Channel::Type::TwitchLive)) { this->initializeIrc(); @@ -257,6 +258,11 @@ std::shared_ptr TwitchIrcServer::getCustomChannel( return this->mentionsChannel; } + if (channelName == "/live") + { + return this->liveChannel; + } + if (channelName == "$$$") { static auto channel = @@ -289,6 +295,7 @@ void TwitchIrcServer::forEachChannelAndSpecialChannels( func(this->whispersChannel); func(this->mentionsChannel); + func(this->liveChannel); } std::shared_ptr TwitchIrcServer::getChannelOrEmptyByID( diff --git a/src/providers/twitch/TwitchIrcServer.hpp b/src/providers/twitch/TwitchIrcServer.hpp index 17203c272..8b76f0903 100644 --- a/src/providers/twitch/TwitchIrcServer.hpp +++ b/src/providers/twitch/TwitchIrcServer.hpp @@ -35,6 +35,7 @@ public: const ChannelPtr whispersChannel; const ChannelPtr mentionsChannel; + const ChannelPtr liveChannel; IndirectChannel watchingChannel; PubSub *pubsub; diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index a433e4de0..1c0116597 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -1302,6 +1302,18 @@ void TwitchMessageBuilder::appendChannelPointRewardMessage( builder->message().flags.set(MessageFlag::RedeemedChannelPointReward); } +void TwitchMessageBuilder::liveMessage(const QString &channelName, + MessageBuilder *builder) +{ + builder->emplace(); + builder + ->emplace(channelName, MessageElementFlag::Username, + MessageColor::Text, FontStyle::ChatMediumBold) + ->setLink({Link::UserInfo, channelName}); + builder->emplace("is live!", MessageElementFlag::Text, + MessageColor::Text); +} + void TwitchMessageBuilder::liveSystemMessage(const QString &channelName, MessageBuilder *builder) { diff --git a/src/providers/twitch/TwitchMessageBuilder.hpp b/src/providers/twitch/TwitchMessageBuilder.hpp index 01bc16b17..ee780c4a6 100644 --- a/src/providers/twitch/TwitchMessageBuilder.hpp +++ b/src/providers/twitch/TwitchMessageBuilder.hpp @@ -52,6 +52,12 @@ public: static void appendChannelPointRewardMessage( const ChannelPointReward &reward, MessageBuilder *builder); + + // Message in the /live chat for channel going live + static void liveMessage(const QString &channelName, + MessageBuilder *builder); + + // Messages in normal chat for channel stuff static void liveSystemMessage(const QString &channelName, MessageBuilder *builder); static void offlineSystemMessage(const QString &channelName, diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index 687238f74..f7498e043 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -300,6 +300,8 @@ public: false}; QStringSetting notificationPathSound = {"/notifications/highlightSoundPath", "qrc:/sounds/ping3.wav"}; + BoolSetting notificationOnAnyChannel = {"/notifications/onAnyChannel", + false}; BoolSetting notificationToast = {"/notifications/enableToast", false}; IntSetting openFromToast = {"/notifications/openFromToast", diff --git a/src/singletons/WindowManager.cpp b/src/singletons/WindowManager.cpp index 8492ca0cc..af66ffcad 100644 --- a/src/singletons/WindowManager.cpp +++ b/src/singletons/WindowManager.cpp @@ -559,6 +559,10 @@ void WindowManager::encodeChannel(IndirectChannel channel, QJsonObject &obj) obj.insert("type", "whispers"); } break; + case Channel::Type::TwitchLive: { + obj.insert("type", "live"); + } + break; case Channel::Type::Irc: { if (auto ircChannel = dynamic_cast(channel.get().get())) @@ -608,6 +612,10 @@ IndirectChannel WindowManager::decodeChannel(const SplitDescriptor &descriptor) { return app->twitch.server->whispersChannel; } + else if (descriptor.type_ == "live") + { + return app->twitch.server->liveChannel; + } else if (descriptor.type_ == "irc") { return Irc::instance().getOrAddChannel(descriptor.server_, diff --git a/src/widgets/dialogs/SelectChannelDialog.cpp b/src/widgets/dialogs/SelectChannelDialog.cpp index 2755c3c0d..03376f16a 100644 --- a/src/widgets/dialogs/SelectChannelDialog.cpp +++ b/src/widgets/dialogs/SelectChannelDialog.cpp @@ -119,17 +119,31 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent) watching_lbl->setVisible(enabled); }); + // live_btn + auto live_btn = + vbox.emplace("Live").assign(&this->ui_.twitch.live); + auto live_lbl = + vbox.emplace("Shows when channels go live.").hidden(); + + live_lbl->setWordWrap(true); + live_btn->installEventFilter(&this->tabFilter_); + + QObject::connect(live_btn.getElement(), &QRadioButton::toggled, + [=](bool enabled) mutable { + live_lbl->setVisible(enabled); + }); + vbox->addStretch(1); // tabbing order - QWidget::setTabOrder(watching_btn.getElement(), - channel_btn.getElement()); + QWidget::setTabOrder(live_btn.getElement(), channel_btn.getElement()); QWidget::setTabOrder(channel_btn.getElement(), whispers_btn.getElement()); QWidget::setTabOrder(whispers_btn.getElement(), mentions_btn.getElement()); QWidget::setTabOrder(mentions_btn.getElement(), watching_btn.getElement()); + QWidget::setTabOrder(watching_btn.getElement(), live_btn.getElement()); // tab auto tab = notebook->addPage(obj.getElement()); @@ -294,6 +308,11 @@ void SelectChannelDialog::setSelectedChannel(IndirectChannel _channel) this->ui_.twitch.whispers->setFocus(); } break; + case Channel::Type::TwitchLive: { + this->ui_.notebook->selectIndex(TAB_TWITCH); + this->ui_.twitch.live->setFocus(); + } + break; case Channel::Type::Irc: { this->ui_.notebook->selectIndex(TAB_IRC); this->ui_.irc.channel->setText(_channel.get()->getName()); @@ -357,6 +376,10 @@ IndirectChannel SelectChannelDialog::getSelectedChannel() const { return app->twitch.server->whispersChannel; } + else if (this->ui_.twitch.live->isChecked()) + { + return app->twitch.server->liveChannel; + } } break; case TAB_IRC: { @@ -417,15 +440,22 @@ bool SelectChannelDialog::EventFilter::eventFilter(QObject *watched, event_key->key() == Qt::Key_Down) && event_key->modifiers() == Qt::NoModifier) { + // Tab has been pressed, focus next entry in list + if (widget == this->dialog->ui_.twitch.channelName) { + // Special case for when current selection is the "Channel" entry's edit box since the Edit box actually has the focus this->dialog->ui_.twitch.whispers->setFocus(); return true; } - else + else if (widget == this->dialog->ui_.twitch.live) { - widget->nextInFocusChain()->setFocus(); + // Special case for when current selection is "Live" (the last entry in the list), next wrap is Channel, but we need to select its edit box + this->dialog->ui_.twitch.channel->setFocus(); + return true; } + + widget->nextInFocusChain()->setFocus(); return true; } else if (((event_key->key() == Qt::Key_Tab || @@ -434,14 +464,12 @@ bool SelectChannelDialog::EventFilter::eventFilter(QObject *watched, ((event_key->key() == Qt::Key_Up) && event_key->modifiers() == Qt::NoModifier)) { + // Shift+Tab has been pressed, focus previous entry in list + if (widget == this->dialog->ui_.twitch.channelName) { - this->dialog->ui_.twitch.watching->setFocus(); - return true; - } - else if (widget == this->dialog->ui_.twitch.whispers) - { - this->dialog->ui_.twitch.channel->setFocus(); + // Special case for when current selection is the "Channel" entry's edit box since the Edit box actually has the focus + this->dialog->ui_.twitch.live->setFocus(); return true; } diff --git a/src/widgets/dialogs/SelectChannelDialog.hpp b/src/widgets/dialogs/SelectChannelDialog.hpp index 2e4e4ed19..3d7e7346c 100644 --- a/src/widgets/dialogs/SelectChannelDialog.hpp +++ b/src/widgets/dialogs/SelectChannelDialog.hpp @@ -49,6 +49,7 @@ private: QRadioButton *whispers; QRadioButton *mentions; QRadioButton *watching; + QRadioButton *live; } twitch; struct { QLineEdit *channel; diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index a1d69a5f0..9c416cc58 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -994,7 +994,8 @@ MessageElementFlags ChannelView::getFlags() const { flags.set(MessageElementFlag::ModeratorTools); } - if (this->underlyingChannel_ == app->twitch.server->mentionsChannel) + if (this->underlyingChannel_ == app->twitch.server->mentionsChannel || + this->underlyingChannel_ == app->twitch.server->liveChannel) { flags.set(MessageElementFlag::ChannelName); flags.unset(MessageElementFlag::ChannelPointReward); diff --git a/src/widgets/settingspages/NotificationPage.cpp b/src/widgets/settingspages/NotificationPage.cpp index 4f4ea161b..fb52c88eb 100644 --- a/src/widgets/settingspages/NotificationPage.cpp +++ b/src/widgets/settingspages/NotificationPage.cpp @@ -35,8 +35,12 @@ NotificationPage::NotificationPage() settings.append(this->createCheckBox( "Flash taskbar", getSettings()->notificationFlashTaskbar)); + settings.append( + this->createCheckBox("Play sound for selected channels", + getSettings()->notificationPlaySound)); settings.append(this->createCheckBox( - "Play sound", getSettings()->notificationPlaySound)); + "Play sound for any channel going live", + getSettings()->notificationOnAnyChannel)); #ifdef Q_OS_WIN settings.append(this->createCheckBox( "Show notification", getSettings()->notificationToast));