From 7db45aa7f2e54e98e323d19de8fee0b80378d661 Mon Sep 17 00:00:00 2001 From: Cranken Date: Mon, 11 Sep 2017 22:37:39 +0200 Subject: [PATCH] Adds viewer list + livechecks (#105) * Added check for livestatus + tooltip. * Also added live check on startup. * Added viewerlist + livesearch. * Refactored code to make it compacter. --- src/channel.cpp | 21 ++------ src/channel.hpp | 16 +++--- src/ircmanager.cpp | 3 ++ src/widgets/chatwidget.cpp | 87 ++++++++++++++++++++++++++++++++ src/widgets/chatwidget.hpp | 3 ++ src/widgets/chatwidgetheader.cpp | 51 ++++++++++++++++++- src/widgets/chatwidgetheader.hpp | 3 +- 7 files changed, 156 insertions(+), 28 deletions(-) diff --git a/src/channel.cpp b/src/channel.cpp index 311ecd03c..af979c6a4 100644 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -29,6 +29,7 @@ Channel::Channel(WindowManager &_windowManager, EmoteManager &_emoteManager, , _subLink("https://www.twitch.tv/" + name + "/subscribe?ref=in_chat_subscriber_link") , _channelLink("https://twitch.tv/" + name) , _popoutPlayerLink("https://player.twitch.tv/?channel=" + name) + , isLive(false) // , _loggingChannel(logging::get(_name)) { qDebug() << "Open channel:" << this->name; @@ -62,24 +63,10 @@ const QString &Channel::getPopoutPlayerLink() const return _popoutPlayerLink; } -bool Channel::getIsLive() const +void Channel::setRoomID(std::string id) { - return _isLive; -} - -int Channel::getStreamViewerCount() const -{ - return _streamViewerCount; -} - -const QString &Channel::getStreamStatus() const -{ - return _streamStatus; -} - -const QString &Channel::getStreamGame() const -{ - return _streamGame; + this->roomID = id; + this->roomIDchanged(); } messages::LimitedQueueSnapshot Channel::getMessageSnapshot() diff --git a/src/channel.hpp b/src/channel.hpp index ec3e88766..5d4c8cbce 100644 --- a/src/channel.hpp +++ b/src/channel.hpp @@ -39,10 +39,6 @@ public: const QString &getSubLink() const; const QString &getChannelLink() const; const QString &getPopoutPlayerLink() const; - bool getIsLive() const; - int getStreamViewerCount() const; - const QString &getStreamStatus() const; - const QString &getStreamGame() const; messages::LimitedQueueSnapshot getMessageSnapshot(); // methods @@ -53,6 +49,14 @@ public: std::string roomID; const QString name; + bool isLive; + QString streamViewerCount; + QString streamStatus; + QString streamGame; + QString streamUptime; + + void setRoomID(std::string id); + boost::signals2::signal roomIDchanged; private: // variables @@ -67,10 +71,6 @@ private: QString _channelLink; QString _popoutPlayerLink; - bool _isLive; - int _streamViewerCount; - QString _streamStatus; - QString _streamGame; // std::shared_ptr _loggingChannel; }; diff --git a/src/ircmanager.cpp b/src/ircmanager.cpp index 76240b4e7..ca2d1a972 100644 --- a/src/ircmanager.cpp +++ b/src/ircmanager.cpp @@ -282,6 +282,9 @@ void IrcManager::handleRoomStateMessage(Communi::IrcMessage *message) if (iterator != tags.end()) { std::string roomID = iterator.value().toString().toStdString(); + auto channel = QString(message->toData()).split("#").at(1); + channelManager.getChannel(channel)->setRoomID(roomID); + this->resources.loadChannelData(roomID); } } diff --git a/src/widgets/chatwidget.cpp b/src/widgets/chatwidget.cpp index 469e48ca2..9fc87ca61 100644 --- a/src/widgets/chatwidget.cpp +++ b/src/widgets/chatwidget.cpp @@ -3,12 +3,15 @@ #include "colorscheme.hpp" #include "notebookpage.hpp" #include "settingsmanager.hpp" +#include "util/urlfetch.hpp" #include "widgets/textinputdialog.hpp" #include +#include #include #include #include +#include #include #include #include @@ -91,6 +94,9 @@ std::shared_ptr &ChatWidget::getChannelRef() void ChatWidget::setChannel(std::shared_ptr _newChannel) { this->channel = _newChannel; + this->channel->roomIDchanged.connect([this](){ + this->header.checkLive(); + }); // on new message this->messageAppendedConnection = @@ -244,6 +250,9 @@ void ChatWidget::doCloseSplit() { NotebookPage *page = static_cast(this->parentWidget()); page->removeFromLayout(this); + QTimer* timer = this->header.findChild(); + timer->stop(); + timer->deleteLater(); } void ChatWidget::doChangeChannel() @@ -296,5 +305,83 @@ void ChatWidget::doOpenStreamlink() } } +void ChatWidget::doOpenViewerList() +{ + auto viewerDock = new QDockWidget("Viewer List",this); + viewerDock->setAllowedAreas(Qt::LeftDockWidgetArea); + viewerDock->setFeatures(QDockWidget::DockWidgetVerticalTitleBar | + QDockWidget::DockWidgetClosable | + QDockWidget::DockWidgetFloatable); + viewerDock->setMaximumHeight(this->height()); + viewerDock->resize(0.5*this->width(),this->height()); + + auto multiWidget = new QWidget(viewerDock); + auto dockVbox = new QVBoxLayout(viewerDock); + auto searchBar = new QLineEdit(viewerDock); + + auto chattersList = new QListWidget(); + auto resultList = new QListWidget(); + + static QStringList labels = {"Moderators", "Staff", "Admins", "Global Moderators", "Viewers"}; + static QStringList jsonLabels = {"moderators", "staff", "admins", "global_mods", "viewers"}; + QList labelList; + for(auto &x : labels) + { + auto label = new QListWidgetItem(x); + label->setBackgroundColor(this->colorScheme.ChatHeaderBackground); + labelList.append(label); + } + auto loadingLabel = new QLabel("Loading..."); + + util::twitch::get("https://tmi.twitch.tv/group/user/" + channel->name + "/chatters",[=](QJsonObject obj){ + QJsonObject chattersObj = obj.value("chatters").toObject(); + + loadingLabel->hide(); + for(int i = 0; i < jsonLabels.size(); i++) + { + chattersList->addItem(labelList.at(i)); + foreach (const QJsonValue & v, chattersObj.value(jsonLabels.at(i)).toArray()) + chattersList->addItem(v.toString()); + } + }); + + searchBar->setPlaceholderText("Search User..."); + QObject::connect(searchBar,&QLineEdit::textEdited,this,[=](){ + auto query = searchBar->text(); + if(!query.isEmpty()) + { + auto results = chattersList->findItems(query,Qt::MatchStartsWith); + chattersList->hide(); + resultList->clear(); + for (auto & item : results) + { + if(!labels.contains(item->text())) + resultList->addItem(item->text()); + } + resultList->show(); + } + else + { + resultList->hide(); + chattersList->show(); + } + }); + + QObject::connect(viewerDock,&QDockWidget::topLevelChanged,this,[=](){ + viewerDock->setMinimumWidth(300); + }); + + dockVbox->addWidget(searchBar); + dockVbox->addWidget(loadingLabel); + dockVbox->addWidget(chattersList); + dockVbox->addWidget(resultList); + resultList->hide(); + + multiWidget->setStyleSheet(this->colorScheme.InputStyleSheet); + multiWidget->setLayout(dockVbox); + viewerDock->setWidget(multiWidget); + viewerDock->show(); +} + } // namespace widgets } // namespace chatterino diff --git a/src/widgets/chatwidget.hpp b/src/widgets/chatwidget.hpp index e7112294d..5defc2d6e 100644 --- a/src/widgets/chatwidget.hpp +++ b/src/widgets/chatwidget.hpp @@ -115,6 +115,9 @@ public slots: // Open twitch channel stream through streamlink void doOpenStreamlink(); + + // Open viewer list of the channel + void doOpenViewerList(); }; } // namespace widgets diff --git a/src/widgets/chatwidgetheader.cpp b/src/widgets/chatwidgetheader.cpp index 97964892d..6eb99b048 100644 --- a/src/widgets/chatwidgetheader.cpp +++ b/src/widgets/chatwidgetheader.cpp @@ -2,6 +2,7 @@ #include "colorscheme.hpp" #include "widgets/chatwidget.hpp" #include "widgets/notebookpage.hpp" +#include "util/urlfetch.hpp" #include #include @@ -44,6 +45,7 @@ ChatWidgetHeader::ChatWidgetHeader(ChatWidget *_chatWidget) QKeySequence(tr("Ctrl+W"))); this->leftMenu.addAction("Move split", this, SLOT(menuMoveSplit())); this->leftMenu.addAction("Popup", this->chatWidget, &ChatWidget::doPopup); + this->leftMenu.addAction("Open viewer list", this->chatWidget, &ChatWidget::doOpenViewerList); this->leftMenu.addSeparator(); this->leftMenu.addAction("Change channel", this->chatWidget, &ChatWidget::doChangeChannel, QKeySequence(tr("Ctrl+R"))); @@ -67,16 +69,36 @@ ChatWidgetHeader::ChatWidgetHeader(ChatWidget *_chatWidget) this->rightLabel.setMinimumWidth(this->height()); this->rightLabel.getLabel().setTextFormat(Qt::RichText); this->rightLabel.getLabel().setText("ayy"); + + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, checkLive); + timer->start(60000); } void ChatWidgetHeader::updateChannelText() { const std::string channelName = this->chatWidget->channelName; - if (channelName.empty()) { this->channelNameLabel.setText(""); } else { - this->channelNameLabel.setText(QString::fromStdString(channelName)); + if(this->chatWidget->getChannelRef()->isLive) + { + auto channel = this->chatWidget->getChannelRef(); + this->channelNameLabel.setText(QString::fromStdString(channelName) + " (live)"); + this->setToolTip("" \ + "

" + \ + channel->streamStatus + "

" + \ + channel->streamGame + "
" \ + "Live for " + channel->streamUptime + \ + " with " + channel->streamViewerCount + " viewers" \ + "

" + ); + } + else + { + this->channelNameLabel.setText(QString::fromStdString(channelName)); + this->setToolTip(""); + } } } @@ -173,5 +195,30 @@ void ChatWidgetHeader::menuShowChangelog() { } +void ChatWidgetHeader::checkLive() +{ + auto channel = this->chatWidget->getChannelRef(); + auto id = QString::fromStdString(channel->roomID); + util::twitch::get("https://api.twitch.tv/kraken/streams/" + id,[=](QJsonObject obj){ + if(obj.value("stream").isNull()) + { + channel->isLive = false; + this->updateChannelText(); + } + else + { + channel->isLive = true; + auto stream = obj.value("stream").toObject(); + channel->streamViewerCount = QString::number(stream.value("viewers").toDouble()); + channel->streamGame = stream.value("game").toString(); + channel->streamStatus = stream.value("channel").toObject().value("status").toString(); + QDateTime since = QDateTime::fromString(stream.value("created_at").toString(),Qt::ISODate); + auto diff = since.secsTo(QDateTime::currentDateTime()); + channel->streamUptime = QString::number(diff/3600) + "h " + QString::number(diff % 3600 / 60) + "m"; + this->updateChannelText(); + } + }); +} + } // namespace widgets } // namespace chatterino diff --git a/src/widgets/chatwidgetheader.hpp b/src/widgets/chatwidgetheader.hpp index 8ce3ca740..fe437862e 100644 --- a/src/widgets/chatwidgetheader.hpp +++ b/src/widgets/chatwidgetheader.hpp @@ -27,9 +27,9 @@ class ChatWidgetHeader : public BaseWidget public: explicit ChatWidgetHeader(ChatWidget *_chatWidget); - // Update channel text from chat widget void updateChannelText(); + void checkLive(); protected: virtual void paintEvent(QPaintEvent *) override; @@ -66,6 +66,7 @@ public slots: void menuReloadChannelEmotes(); void menuManualReconnect(); void menuShowChangelog(); + }; } // namespace widgets