diff --git a/browser_ext/background.js b/browser_ext/background.js index d0e0680f3..85846c7fe 100644 --- a/browser_ext/background.js +++ b/browser_ext/background.js @@ -83,6 +83,6 @@ function selectChannel(channelName) { let port = getPort(); if (port) { - port.postMessage({action: "select", channelName: channelName}); + port.postMessage({action: "select", type: "twitch", name: channelName}); } } diff --git a/src/channel.hpp b/src/channel.hpp index d349eb4c2..0342138f3 100644 --- a/src/channel.hpp +++ b/src/channel.hpp @@ -24,6 +24,7 @@ class Channel : public std::enable_shared_from_this public: enum Type { None, + Direct, Twitch, TwitchWhispers, TwitchWatching, @@ -71,4 +72,55 @@ private: using ChannelPtr = std::shared_ptr; +class IndirectChannel +{ + struct Data { + ChannelPtr channel; + Channel::Type type; + pajlada::Signals::NoArgSignal changed; + + Data() = delete; + Data(ChannelPtr _channel, Channel::Type _type) + : channel(_channel) + , type(_type) + { + } + }; + + std::shared_ptr data; + +public: + IndirectChannel(ChannelPtr channel, Channel::Type type = Channel::None) + : data(new Data(channel, type)) + { + } + + ChannelPtr get() + { + return data->channel; + } + + void update(ChannelPtr ptr) + { + assert(this->data->type != Channel::Direct); + + this->data->channel = ptr; + this->data->changed.invoke(); + } + + pajlada::Signals::NoArgSignal &getChannelChanged() + { + return this->data->changed; + } + + Channel::Type getType() + { + if (this->data->type == Channel::Direct) { + return this->get()->getType(); + } else { + return this->data->type; + } + } +}; + } // namespace chatterino diff --git a/src/providers/twitch/twitchserver.cpp b/src/providers/twitch/twitchserver.cpp index 581a7bbb3..fb808af6f 100644 --- a/src/providers/twitch/twitchserver.cpp +++ b/src/providers/twitch/twitchserver.cpp @@ -19,7 +19,7 @@ namespace twitch { TwitchServer::TwitchServer() : whispersChannel(new Channel("/whispers", Channel::TwitchWhispers)) , mentionsChannel(new Channel("/mentions", Channel::TwitchMentions)) - , watchingChannel(new Channel("/watching", Channel::TwitchWatching)) + , watchingChannel(Channel::getEmpty(), Channel::TwitchWatching) { AccountManager::getInstance().Twitch.userChanged.connect([this]() { // util::postToThread([this] { this->connect(); }); diff --git a/src/providers/twitch/twitchserver.hpp b/src/providers/twitch/twitchserver.hpp index 052c56ea0..d29ed847a 100644 --- a/src/providers/twitch/twitchserver.hpp +++ b/src/providers/twitch/twitchserver.hpp @@ -22,7 +22,7 @@ public: const ChannelPtr whispersChannel; const ChannelPtr mentionsChannel; - const ChannelPtr watchingChannel; + IndirectChannel watchingChannel; protected: void initializeConnection(Communi::IrcConnection *connection, bool isRead, diff --git a/src/singletons/nativemessagingmanager.cpp b/src/singletons/nativemessagingmanager.cpp index b6b4768a7..f827e3060 100644 --- a/src/singletons/nativemessagingmanager.cpp +++ b/src/singletons/nativemessagingmanager.cpp @@ -1,6 +1,8 @@ #include "nativemessagingmanager.hpp" +#include "providers/twitch/twitchserver.hpp" #include "singletons/pathmanager.hpp" +#include "util/posttothread.hpp" #include #include @@ -8,11 +10,9 @@ #include #include -#ifdef BOOSTLIBS #include namespace ipc = boost::interprocess; -#endif #ifdef Q_OS_WIN #include @@ -81,7 +81,6 @@ void NativeMessagingManager::registerHost() void NativeMessagingManager::openGuiMessageQueue() { -#ifdef BOOSTLIBS static ReceiverThread thread; if (thread.isRunning()) { @@ -89,12 +88,10 @@ void NativeMessagingManager::openGuiMessageQueue() } thread.start(); -#endif } void NativeMessagingManager::sendToGuiProcess(const QByteArray &array) { -#ifdef BOOSTLIBS ipc::message_queue messageQueue(ipc::open_or_create, "chatterino_gui", 100, MESSAGE_SIZE); try { @@ -102,12 +99,10 @@ void NativeMessagingManager::sendToGuiProcess(const QByteArray &array) } catch (ipc::interprocess_exception &ex) { qDebug() << "send to gui process:" << ex.what(); } -#endif } void NativeMessagingManager::ReceiverThread::run() { -#ifdef BOOSTLIBS ipc::message_queue::remove("chatterino_gui"); ipc::message_queue messageQueue(ipc::open_or_create, "chatterino_gui", 100, MESSAGE_SIZE); @@ -120,19 +115,43 @@ void NativeMessagingManager::ReceiverThread::run() messageQueue.receive(buf, MESSAGE_SIZE, retSize, priority); - QJsonDocument document = QJsonDocument::fromRawData(buf, retSize); + QJsonDocument document = QJsonDocument::fromJson(QByteArray(buf, retSize)); this->handleMessage(document.object()); } catch (ipc::interprocess_exception &ex) { qDebug() << "received from gui process:" << ex.what(); } } -#endif } void NativeMessagingManager::ReceiverThread::handleMessage(const QJsonObject &root) { - // TODO: add code xD + QString action = root.value("action").toString(); + + if (action.isNull()) { + qDebug() << "NM action was null"; + return; + } + + if (action == "select") { + QString _type = root.value("type").toString(); + QString name = root.value("name").toString(); + + if (_type.isNull() || name.isNull()) { + qDebug() << "NM type or name missing"; + return; + } + + if (_type == "twitch") { + util::postToThread([name] { + auto &ts = providers::twitch::TwitchServer::getInstance(); + + ts.watchingChannel.update(ts.addChannel(name)); + }); + } else { + qDebug() << "NM unknown channel type"; + } + } } } // namespace singletons diff --git a/src/singletons/windowmanager.cpp b/src/singletons/windowmanager.cpp index 1c610c997..e91d5d43b 100644 --- a/src/singletons/windowmanager.cpp +++ b/src/singletons/windowmanager.cpp @@ -270,7 +270,7 @@ void WindowManager::save() for (widgets::Split *cell : cells) { QJsonObject cell_obj; - cell_obj.insert("channelName", cell->getChannel()->name); + cell_obj.insert("channelName", cell->getChannel().get()->name); cells_arr.append(cell_obj); } diff --git a/src/widgets/helper/channelview.cpp b/src/widgets/helper/channelview.cpp index 8f80e7a00..5e89545bd 100644 --- a/src/widgets/helper/channelview.cpp +++ b/src/widgets/helper/channelview.cpp @@ -341,6 +341,7 @@ void ChannelView::setChannel(ChannelPtr newChannel) if (this->channel) { this->detachChannel(); } + this->messages.clear(); // on new message @@ -445,15 +446,10 @@ void ChannelView::setChannel(ChannelPtr newChannel) void ChannelView::detachChannel() { - // on message added - if (this->messageAppendedConnection.isConnected()) { - this->messageAppendedConnection.disconnect(); - } - - // on message removed - if (this->messageRemovedConnection.isConnected()) { - this->messageRemovedConnection.disconnect(); - } + messageAppendedConnection.disconnect(); + messageAddedAtStartConnection.disconnect(); + messageRemovedConnection.disconnect(); + messageReplacedConnection.disconnect(); } void ChannelView::pause(int msecTimeout) diff --git a/src/widgets/helper/channelview.hpp b/src/widgets/helper/channelview.hpp index a86a64659..dff02c039 100644 --- a/src/widgets/helper/channelview.hpp +++ b/src/widgets/helper/channelview.hpp @@ -129,6 +129,7 @@ private: pajlada::Signals::Connection layoutConnection; std::vector managedConnections; + std::vector channelConnections; std::unordered_set> messagesOnScreen; diff --git a/src/widgets/helper/splitheader.cpp b/src/widgets/helper/splitheader.cpp index 28927d88b..677a4dbee 100644 --- a/src/widgets/helper/splitheader.cpp +++ b/src/widgets/helper/splitheader.cpp @@ -154,14 +154,14 @@ void SplitHeader::scaleChangedEvent(float scale) void SplitHeader::updateChannelText() { - const QString channelName = this->split->getChannel()->name; + auto channel = this->split->getChannel(); + + const QString channelName = channel->name; if (channelName.isEmpty()) { this->titleLabel->setText(""); return; } - auto channel = this->split->getChannel(); - TwitchChannel *twitchChannel = dynamic_cast(channel.get()); if (twitchChannel != nullptr) { diff --git a/src/widgets/helper/splitinput.cpp b/src/widgets/helper/splitinput.cpp index 79cf1e9c2..3569a0d4f 100644 --- a/src/widgets/helper/splitinput.cpp +++ b/src/widgets/helper/splitinput.cpp @@ -21,7 +21,7 @@ SplitInput::SplitInput(Split *_chatWidget) { this->initLayout(); - auto completer = new QCompleter(&this->chatWidget->getChannel()->completionModel); + auto completer = new QCompleter(&this->chatWidget->getChannel().get()->completionModel); this->ui.textEdit->setCompleter(completer); this->chatWidget->channelChanged.connect([this] { diff --git a/src/widgets/selectchanneldialog.cpp b/src/widgets/selectchanneldialog.cpp index fe9f320c3..25517d7e6 100644 --- a/src/widgets/selectchanneldialog.cpp +++ b/src/widgets/selectchanneldialog.cpp @@ -138,8 +138,10 @@ void SelectChannelDialog::ok() this->close(); } -void SelectChannelDialog::setSelectedChannel(ChannelPtr channel) +void SelectChannelDialog::setSelectedChannel(IndirectChannel _channel) { + auto channel = _channel.get(); + assert(channel); this->selectedChannel = channel; @@ -171,7 +173,7 @@ void SelectChannelDialog::setSelectedChannel(ChannelPtr channel) this->_hasSelectedChannel = false; } -ChannelPtr SelectChannelDialog::getSelectedChannel() const +IndirectChannel SelectChannelDialog::getSelectedChannel() const { if (!this->_hasSelectedChannel) { return this->selectedChannel; diff --git a/src/widgets/selectchanneldialog.hpp b/src/widgets/selectchanneldialog.hpp index 96b78dec0..a13576e3f 100644 --- a/src/widgets/selectchanneldialog.hpp +++ b/src/widgets/selectchanneldialog.hpp @@ -17,8 +17,8 @@ class SelectChannelDialog : public BaseWindow public: SelectChannelDialog(); - void setSelectedChannel(ChannelPtr selectedChannel); - ChannelPtr getSelectedChannel() const; + void setSelectedChannel(IndirectChannel selectedChannel); + IndirectChannel getSelectedChannel() const; bool hasSeletedChannel() const; pajlada::Signals::NoArgSignal closed; diff --git a/src/widgets/split.cpp b/src/widgets/split.cpp index 96b1061a4..e4117f673 100644 --- a/src/widgets/split.cpp +++ b/src/widgets/split.cpp @@ -119,28 +119,39 @@ Split::~Split() { this->usermodeChangedConnection.disconnect(); this->channelIDChangedConnection.disconnect(); + this->indirectChannelChangedConnection.disconnect(); } -ChannelPtr Split::getChannel() const +IndirectChannel Split::getIndirectChannel() { return this->channel; } -void Split::setChannel(ChannelPtr _newChannel) +ChannelPtr Split::getChannel() { - this->view.setChannel(_newChannel); + return this->channel.get(); +} + +void Split::setChannel(IndirectChannel newChannel) +{ + this->view.setChannel(newChannel.get()); this->usermodeChangedConnection.disconnect(); + this->indirectChannelChangedConnection.disconnect(); - this->channel = _newChannel; + this->channel = newChannel; - TwitchChannel *tc = dynamic_cast(_newChannel.get()); + TwitchChannel *tc = dynamic_cast(newChannel.get().get()); if (tc != nullptr) { this->usermodeChangedConnection = tc->userStateChanged.connect([this] { this->header.updateModerationModeIcon(); }); } + this->indirectChannelChangedConnection = newChannel.getChannelChanged().connect([this] { // + QTimer::singleShot(0, [this] { this->setChannel(this->channel); }); + }); + this->header.updateModerationModeIcon(); this->header.updateChannelText(); @@ -316,7 +327,7 @@ void Split::doClearChat() void Split::doOpenChannel() { - ChannelPtr _channel = this->channel; + ChannelPtr _channel = this->getChannel(); TwitchChannel *tc = dynamic_cast(_channel.get()); if (tc != nullptr) { @@ -326,7 +337,7 @@ void Split::doOpenChannel() void Split::doOpenPopupPlayer() { - ChannelPtr _channel = this->channel; + ChannelPtr _channel = this->getChannel(); TwitchChannel *tc = dynamic_cast(_channel.get()); if (tc != nullptr) { @@ -337,7 +348,7 @@ void Split::doOpenPopupPlayer() void Split::doOpenStreamlink() { try { - streamlink::Start(this->channel->name); + streamlink::Start(this->getChannel()->name); } catch (const streamlink::Exception &ex) { debug::Log("Error in doOpenStreamlink: {}", ex.what()); } @@ -353,7 +364,7 @@ void Split::doOpenViewerList() this->height() - this->header.height() - this->input.height()); viewerDock->move(0, this->header.height()); - auto accountPopup = new AccountPopupWidget(this->channel); + auto accountPopup = new AccountPopupWidget(this->getChannel()); accountPopup->setAttribute(Qt::WA_DeleteOnClose); auto multiWidget = new QWidget(viewerDock); auto dockVbox = new QVBoxLayout(viewerDock); @@ -372,8 +383,8 @@ void Split::doOpenViewerList() } auto loadingLabel = new QLabel("Loading..."); - util::twitch::get("https://tmi.twitch.tv/group/user/" + channel->name + "/chatters", this, - [=](QJsonObject obj) { + util::twitch::get("https://tmi.twitch.tv/group/user/" + this->getChannel()->name + "/chatters", + this, [=](QJsonObject obj) { QJsonObject chattersObj = obj.value("chatters").toObject(); loadingLabel->hide(); diff --git a/src/widgets/split.hpp b/src/widgets/split.hpp index f5394117f..871b7169a 100644 --- a/src/widgets/split.hpp +++ b/src/widgets/split.hpp @@ -53,8 +53,9 @@ public: return this->view; } - ChannelPtr getChannel() const; - void setChannel(ChannelPtr newChannel); + IndirectChannel getIndirectChannel(); + ChannelPtr getChannel(); + void setChannel(IndirectChannel newChannel); void setFlexSizeX(double x); double getFlexSizeX(); @@ -83,7 +84,7 @@ protected: private: SplitContainer &parentPage; - ChannelPtr channel; + IndirectChannel channel; QVBoxLayout vbox; SplitHeader header; @@ -96,6 +97,7 @@ private: pajlada::Signals::Connection channelIDChangedConnection; pajlada::Signals::Connection usermodeChangedConnection; + pajlada::Signals::Connection indirectChannelChangedConnection; void doOpenAccountPopupWidget(AccountPopupWidget *widget, QString user); void channelNameUpdated(const QString &newChannelName); void handleModifiers(QEvent *event, Qt::KeyboardModifiers modifiers);