added watching channel

This commit is contained in:
fourtf 2018-04-20 19:54:45 +02:00
parent e17a7cc222
commit a16a2b0579
14 changed files with 128 additions and 45 deletions

View file

@ -83,6 +83,6 @@ function selectChannel(channelName) {
let port = getPort(); let port = getPort();
if (port) { if (port) {
port.postMessage({action: "select", channelName: channelName}); port.postMessage({action: "select", type: "twitch", name: channelName});
} }
} }

View file

@ -24,6 +24,7 @@ class Channel : public std::enable_shared_from_this<Channel>
public: public:
enum Type { enum Type {
None, None,
Direct,
Twitch, Twitch,
TwitchWhispers, TwitchWhispers,
TwitchWatching, TwitchWatching,
@ -71,4 +72,55 @@ private:
using ChannelPtr = std::shared_ptr<Channel>; using ChannelPtr = std::shared_ptr<Channel>;
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> 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 } // namespace chatterino

View file

@ -19,7 +19,7 @@ namespace twitch {
TwitchServer::TwitchServer() TwitchServer::TwitchServer()
: whispersChannel(new Channel("/whispers", Channel::TwitchWhispers)) : whispersChannel(new Channel("/whispers", Channel::TwitchWhispers))
, mentionsChannel(new Channel("/mentions", Channel::TwitchMentions)) , mentionsChannel(new Channel("/mentions", Channel::TwitchMentions))
, watchingChannel(new Channel("/watching", Channel::TwitchWatching)) , watchingChannel(Channel::getEmpty(), Channel::TwitchWatching)
{ {
AccountManager::getInstance().Twitch.userChanged.connect([this]() { // AccountManager::getInstance().Twitch.userChanged.connect([this]() { //
util::postToThread([this] { this->connect(); }); util::postToThread([this] { this->connect(); });

View file

@ -22,7 +22,7 @@ public:
const ChannelPtr whispersChannel; const ChannelPtr whispersChannel;
const ChannelPtr mentionsChannel; const ChannelPtr mentionsChannel;
const ChannelPtr watchingChannel; IndirectChannel watchingChannel;
protected: protected:
void initializeConnection(Communi::IrcConnection *connection, bool isRead, void initializeConnection(Communi::IrcConnection *connection, bool isRead,

View file

@ -1,6 +1,8 @@
#include "nativemessagingmanager.hpp" #include "nativemessagingmanager.hpp"
#include "providers/twitch/twitchserver.hpp"
#include "singletons/pathmanager.hpp" #include "singletons/pathmanager.hpp"
#include "util/posttothread.hpp"
#include <QCoreApplication> #include <QCoreApplication>
#include <QFile> #include <QFile>
@ -8,11 +10,9 @@
#include <QJsonObject> #include <QJsonObject>
#include <QJsonValue> #include <QJsonValue>
#ifdef BOOSTLIBS
#include <boost/interprocess/ipc/message_queue.hpp> #include <boost/interprocess/ipc/message_queue.hpp>
namespace ipc = boost::interprocess; namespace ipc = boost::interprocess;
#endif
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include <QProcess> #include <QProcess>
@ -81,7 +81,6 @@ void NativeMessagingManager::registerHost()
void NativeMessagingManager::openGuiMessageQueue() void NativeMessagingManager::openGuiMessageQueue()
{ {
#ifdef BOOSTLIBS
static ReceiverThread thread; static ReceiverThread thread;
if (thread.isRunning()) { if (thread.isRunning()) {
@ -89,12 +88,10 @@ void NativeMessagingManager::openGuiMessageQueue()
} }
thread.start(); thread.start();
#endif
} }
void NativeMessagingManager::sendToGuiProcess(const QByteArray &array) void NativeMessagingManager::sendToGuiProcess(const QByteArray &array)
{ {
#ifdef BOOSTLIBS
ipc::message_queue messageQueue(ipc::open_or_create, "chatterino_gui", 100, MESSAGE_SIZE); ipc::message_queue messageQueue(ipc::open_or_create, "chatterino_gui", 100, MESSAGE_SIZE);
try { try {
@ -102,12 +99,10 @@ void NativeMessagingManager::sendToGuiProcess(const QByteArray &array)
} catch (ipc::interprocess_exception &ex) { } catch (ipc::interprocess_exception &ex) {
qDebug() << "send to gui process:" << ex.what(); qDebug() << "send to gui process:" << ex.what();
} }
#endif
} }
void NativeMessagingManager::ReceiverThread::run() void NativeMessagingManager::ReceiverThread::run()
{ {
#ifdef BOOSTLIBS
ipc::message_queue::remove("chatterino_gui"); ipc::message_queue::remove("chatterino_gui");
ipc::message_queue messageQueue(ipc::open_or_create, "chatterino_gui", 100, MESSAGE_SIZE); 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); messageQueue.receive(buf, MESSAGE_SIZE, retSize, priority);
QJsonDocument document = QJsonDocument::fromRawData(buf, retSize); QJsonDocument document = QJsonDocument::fromJson(QByteArray(buf, retSize));
this->handleMessage(document.object()); this->handleMessage(document.object());
} catch (ipc::interprocess_exception &ex) { } catch (ipc::interprocess_exception &ex) {
qDebug() << "received from gui process:" << ex.what(); qDebug() << "received from gui process:" << ex.what();
} }
} }
#endif
} }
void NativeMessagingManager::ReceiverThread::handleMessage(const QJsonObject &root) 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 } // namespace singletons

View file

@ -270,7 +270,7 @@ void WindowManager::save()
for (widgets::Split *cell : cells) { for (widgets::Split *cell : cells) {
QJsonObject cell_obj; QJsonObject cell_obj;
cell_obj.insert("channelName", cell->getChannel()->name); cell_obj.insert("channelName", cell->getChannel().get()->name);
cells_arr.append(cell_obj); cells_arr.append(cell_obj);
} }

View file

@ -341,6 +341,7 @@ void ChannelView::setChannel(ChannelPtr newChannel)
if (this->channel) { if (this->channel) {
this->detachChannel(); this->detachChannel();
} }
this->messages.clear(); this->messages.clear();
// on new message // on new message
@ -445,15 +446,10 @@ void ChannelView::setChannel(ChannelPtr newChannel)
void ChannelView::detachChannel() void ChannelView::detachChannel()
{ {
// on message added messageAppendedConnection.disconnect();
if (this->messageAppendedConnection.isConnected()) { messageAddedAtStartConnection.disconnect();
this->messageAppendedConnection.disconnect(); messageRemovedConnection.disconnect();
} messageReplacedConnection.disconnect();
// on message removed
if (this->messageRemovedConnection.isConnected()) {
this->messageRemovedConnection.disconnect();
}
} }
void ChannelView::pause(int msecTimeout) void ChannelView::pause(int msecTimeout)

View file

@ -129,6 +129,7 @@ private:
pajlada::Signals::Connection layoutConnection; pajlada::Signals::Connection layoutConnection;
std::vector<pajlada::Signals::ScopedConnection> managedConnections; std::vector<pajlada::Signals::ScopedConnection> managedConnections;
std::vector<pajlada::Signals::ScopedConnection> channelConnections;
std::unordered_set<std::shared_ptr<messages::MessageLayout>> messagesOnScreen; std::unordered_set<std::shared_ptr<messages::MessageLayout>> messagesOnScreen;

View file

@ -154,14 +154,14 @@ void SplitHeader::scaleChangedEvent(float scale)
void SplitHeader::updateChannelText() void SplitHeader::updateChannelText()
{ {
const QString channelName = this->split->getChannel()->name; auto channel = this->split->getChannel();
const QString channelName = channel->name;
if (channelName.isEmpty()) { if (channelName.isEmpty()) {
this->titleLabel->setText("<no channel>"); this->titleLabel->setText("<no channel>");
return; return;
} }
auto channel = this->split->getChannel();
TwitchChannel *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get()); TwitchChannel *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
if (twitchChannel != nullptr) { if (twitchChannel != nullptr) {

View file

@ -21,7 +21,7 @@ SplitInput::SplitInput(Split *_chatWidget)
{ {
this->initLayout(); 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->ui.textEdit->setCompleter(completer);
this->chatWidget->channelChanged.connect([this] { this->chatWidget->channelChanged.connect([this] {

View file

@ -138,8 +138,10 @@ void SelectChannelDialog::ok()
this->close(); this->close();
} }
void SelectChannelDialog::setSelectedChannel(ChannelPtr channel) void SelectChannelDialog::setSelectedChannel(IndirectChannel _channel)
{ {
auto channel = _channel.get();
assert(channel); assert(channel);
this->selectedChannel = channel; this->selectedChannel = channel;
@ -171,7 +173,7 @@ void SelectChannelDialog::setSelectedChannel(ChannelPtr channel)
this->_hasSelectedChannel = false; this->_hasSelectedChannel = false;
} }
ChannelPtr SelectChannelDialog::getSelectedChannel() const IndirectChannel SelectChannelDialog::getSelectedChannel() const
{ {
if (!this->_hasSelectedChannel) { if (!this->_hasSelectedChannel) {
return this->selectedChannel; return this->selectedChannel;

View file

@ -17,8 +17,8 @@ class SelectChannelDialog : public BaseWindow
public: public:
SelectChannelDialog(); SelectChannelDialog();
void setSelectedChannel(ChannelPtr selectedChannel); void setSelectedChannel(IndirectChannel selectedChannel);
ChannelPtr getSelectedChannel() const; IndirectChannel getSelectedChannel() const;
bool hasSeletedChannel() const; bool hasSeletedChannel() const;
pajlada::Signals::NoArgSignal closed; pajlada::Signals::NoArgSignal closed;

View file

@ -119,28 +119,39 @@ Split::~Split()
{ {
this->usermodeChangedConnection.disconnect(); this->usermodeChangedConnection.disconnect();
this->channelIDChangedConnection.disconnect(); this->channelIDChangedConnection.disconnect();
this->indirectChannelChangedConnection.disconnect();
} }
ChannelPtr Split::getChannel() const IndirectChannel Split::getIndirectChannel()
{ {
return this->channel; 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->usermodeChangedConnection.disconnect();
this->indirectChannelChangedConnection.disconnect();
this->channel = _newChannel; this->channel = newChannel;
TwitchChannel *tc = dynamic_cast<TwitchChannel *>(_newChannel.get()); TwitchChannel *tc = dynamic_cast<TwitchChannel *>(newChannel.get().get());
if (tc != nullptr) { if (tc != nullptr) {
this->usermodeChangedConnection = this->usermodeChangedConnection =
tc->userStateChanged.connect([this] { this->header.updateModerationModeIcon(); }); 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.updateModerationModeIcon();
this->header.updateChannelText(); this->header.updateChannelText();
@ -316,7 +327,7 @@ void Split::doClearChat()
void Split::doOpenChannel() void Split::doOpenChannel()
{ {
ChannelPtr _channel = this->channel; ChannelPtr _channel = this->getChannel();
TwitchChannel *tc = dynamic_cast<TwitchChannel *>(_channel.get()); TwitchChannel *tc = dynamic_cast<TwitchChannel *>(_channel.get());
if (tc != nullptr) { if (tc != nullptr) {
@ -326,7 +337,7 @@ void Split::doOpenChannel()
void Split::doOpenPopupPlayer() void Split::doOpenPopupPlayer()
{ {
ChannelPtr _channel = this->channel; ChannelPtr _channel = this->getChannel();
TwitchChannel *tc = dynamic_cast<TwitchChannel *>(_channel.get()); TwitchChannel *tc = dynamic_cast<TwitchChannel *>(_channel.get());
if (tc != nullptr) { if (tc != nullptr) {
@ -337,7 +348,7 @@ void Split::doOpenPopupPlayer()
void Split::doOpenStreamlink() void Split::doOpenStreamlink()
{ {
try { try {
streamlink::Start(this->channel->name); streamlink::Start(this->getChannel()->name);
} catch (const streamlink::Exception &ex) { } catch (const streamlink::Exception &ex) {
debug::Log("Error in doOpenStreamlink: {}", ex.what()); debug::Log("Error in doOpenStreamlink: {}", ex.what());
} }
@ -353,7 +364,7 @@ void Split::doOpenViewerList()
this->height() - this->header.height() - this->input.height()); this->height() - this->header.height() - this->input.height());
viewerDock->move(0, this->header.height()); viewerDock->move(0, this->header.height());
auto accountPopup = new AccountPopupWidget(this->channel); auto accountPopup = new AccountPopupWidget(this->getChannel());
accountPopup->setAttribute(Qt::WA_DeleteOnClose); accountPopup->setAttribute(Qt::WA_DeleteOnClose);
auto multiWidget = new QWidget(viewerDock); auto multiWidget = new QWidget(viewerDock);
auto dockVbox = new QVBoxLayout(viewerDock); auto dockVbox = new QVBoxLayout(viewerDock);
@ -372,8 +383,8 @@ void Split::doOpenViewerList()
} }
auto loadingLabel = new QLabel("Loading..."); auto loadingLabel = new QLabel("Loading...");
util::twitch::get("https://tmi.twitch.tv/group/user/" + channel->name + "/chatters", this, util::twitch::get("https://tmi.twitch.tv/group/user/" + this->getChannel()->name + "/chatters",
[=](QJsonObject obj) { this, [=](QJsonObject obj) {
QJsonObject chattersObj = obj.value("chatters").toObject(); QJsonObject chattersObj = obj.value("chatters").toObject();
loadingLabel->hide(); loadingLabel->hide();

View file

@ -53,8 +53,9 @@ public:
return this->view; return this->view;
} }
ChannelPtr getChannel() const; IndirectChannel getIndirectChannel();
void setChannel(ChannelPtr newChannel); ChannelPtr getChannel();
void setChannel(IndirectChannel newChannel);
void setFlexSizeX(double x); void setFlexSizeX(double x);
double getFlexSizeX(); double getFlexSizeX();
@ -83,7 +84,7 @@ protected:
private: private:
SplitContainer &parentPage; SplitContainer &parentPage;
ChannelPtr channel; IndirectChannel channel;
QVBoxLayout vbox; QVBoxLayout vbox;
SplitHeader header; SplitHeader header;
@ -96,6 +97,7 @@ private:
pajlada::Signals::Connection channelIDChangedConnection; pajlada::Signals::Connection channelIDChangedConnection;
pajlada::Signals::Connection usermodeChangedConnection; pajlada::Signals::Connection usermodeChangedConnection;
pajlada::Signals::Connection indirectChannelChangedConnection;
void doOpenAccountPopupWidget(AccountPopupWidget *widget, QString user); void doOpenAccountPopupWidget(AccountPopupWidget *widget, QString user);
void channelNameUpdated(const QString &newChannelName); void channelNameUpdated(const QString &newChannelName);
void handleModifiers(QEvent *event, Qt::KeyboardModifiers modifiers); void handleModifiers(QEvent *event, Qt::KeyboardModifiers modifiers);