From b3a41e5c1a3c395367e421e1216bf2966cefcc42 Mon Sep 17 00:00:00 2001 From: hemirt Date: Tue, 9 Oct 2018 19:43:29 +0200 Subject: [PATCH 01/12] Closed Splits --- chatterino.pro | 6 ++-- src/widgets/Window.cpp | 22 ++++++++++++ src/widgets/splits/ClosedSplits.cpp | 52 +++++++++++++++++++++++++++++ src/widgets/splits/ClosedSplits.hpp | 35 +++++++++++++++++++ src/widgets/splits/Split.cpp | 6 ++++ 5 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 src/widgets/splits/ClosedSplits.cpp create mode 100644 src/widgets/splits/ClosedSplits.hpp diff --git a/chatterino.pro b/chatterino.pro index 131696679..9d7119797 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -261,7 +261,8 @@ SOURCES += \ src/messages/MessageContainer.cpp \ src/debug/Benchmark.cpp \ src/common/UsernameSet.cpp \ - src/widgets/settingspages/AdvancedPage.cpp + src/widgets/settingspages/AdvancedPage.cpp \ + src/widgets/splits/ClosedSplits.cpp HEADERS += \ src/Application.hpp \ @@ -462,7 +463,8 @@ HEADERS += \ src/widgets/helper/Button.hpp \ src/messages/MessageContainer.hpp \ src/common/UsernameSet.hpp \ - src/widgets/settingspages/AdvancedPage.hpp + src/widgets/settingspages/AdvancedPage.hpp \ + src/widgets/splits/ClosedSplits.hpp RESOURCES += \ resources/resources.qrc \ diff --git a/src/widgets/Window.cpp b/src/widgets/Window.cpp index 22a326ebf..3f6ebdd98 100644 --- a/src/widgets/Window.cpp +++ b/src/widgets/Window.cpp @@ -15,8 +15,10 @@ #include "widgets/dialogs/UpdateDialog.hpp" #include "widgets/dialogs/WelcomeDialog.hpp" #include "widgets/helper/EffectLabel.hpp" +#include "widgets/helper/NotebookTab.hpp" #include "widgets/helper/Shortcut.hpp" #include "widgets/helper/TitlebarButton.hpp" +#include "widgets/splits/ClosedSplits.hpp" #include "widgets/splits/Split.hpp" #include "widgets/splits/SplitContainer.hpp" @@ -304,6 +306,26 @@ void Window::addShortcuts() // Close tab createWindowShortcut(this, "CTRL+SHIFT+W", [this] { this->notebook_->removeCurrentPage(); }); + + // Reopen last closed split + createWindowShortcut(this, "CTRL+G", [this] { + if (ClosedSplits::empty()) { + return; + } + ClosedSplits::SplitInfo si = ClosedSplits::pop(); + SplitContainer *splitContainer{nullptr}; + if (si.tab) { + splitContainer = dynamic_cast(si.tab->page); + } + if (!splitContainer) { + splitContainer = this->notebook_->getOrAddSelectedPage(); + } + this->notebook_->select(splitContainer); + Split *split = new Split(splitContainer); + splitContainer->appendSplit(split); + split->setChannel( + getApp()->twitch.server->getOrAddChannel(si.channelName)); + }); } #define UGLYMACROHACK1(s) #s diff --git a/src/widgets/splits/ClosedSplits.cpp b/src/widgets/splits/ClosedSplits.cpp new file mode 100644 index 000000000..2c1d912c9 --- /dev/null +++ b/src/widgets/splits/ClosedSplits.cpp @@ -0,0 +1,52 @@ +#include "ClosedSplits.hpp" + +namespace chatterino { + +std::mutex ClosedSplits::m_; +std::vector ClosedSplits::closedSplits_; + +void ClosedSplits::invalidateTab(NotebookTab *const tab) +{ + std::lock_guard lk(ClosedSplits::m_); + auto it = std::find_if( + ClosedSplits::closedSplits_.begin(), ClosedSplits::closedSplits_.end(), + [tab](const auto &item) -> bool { return item.tab == tab; }); + if (it == ClosedSplits::closedSplits_.end()) { + return; + } + it->tab = nullptr; +} + +void ClosedSplits::push(const SplitInfo &si) +{ + std::lock_guard lk(ClosedSplits::m_); + ClosedSplits::closedSplits_.push_back(si); +} + +void ClosedSplits::push(SplitInfo &&si) +{ + std::lock_guard lk(ClosedSplits::m_); + ClosedSplits::closedSplits_.push_back(std::move(si)); +} + +ClosedSplits::SplitInfo ClosedSplits::pop() +{ + std::lock_guard lk(ClosedSplits::m_); + SplitInfo si = std::move(ClosedSplits::closedSplits_.back()); + ClosedSplits::closedSplits_.pop_back(); + return si; +} + +bool ClosedSplits::empty() +{ + std::lock_guard lk(ClosedSplits::m_); + return ClosedSplits::closedSplits_.empty(); +} + +std::size_t ClosedSplits::size() +{ + std::lock_guard lk(ClosedSplits::m_); + return ClosedSplits::closedSplits_.size(); +} + +} // namespace chatterino diff --git a/src/widgets/splits/ClosedSplits.hpp b/src/widgets/splits/ClosedSplits.hpp new file mode 100644 index 000000000..4d05f22c0 --- /dev/null +++ b/src/widgets/splits/ClosedSplits.hpp @@ -0,0 +1,35 @@ +#ifndef CLOSEDSPLITS_HPP +#define CLOSEDSPLITS_HPP + +#include "common/Channel.hpp" +#include "widgets/helper/NotebookTab.hpp" + +#include +#include +#include + +namespace chatterino { + +class ClosedSplits +{ +public: + struct SplitInfo { + QString channelName; + NotebookTab *tab; // non owning ptr + }; + + static void invalidateTab(NotebookTab *const tab); + static void push(const SplitInfo &si); + static void push(SplitInfo &&si); + static SplitInfo pop(); + static bool empty(); + static std::size_t size(); + +private: + static std::mutex m_; + static std::vector closedSplits_; +}; + +} // namespace chatterino + +#endif // CLOSEDSPLITS_HPP diff --git a/src/widgets/splits/Split.cpp b/src/widgets/splits/Split.cpp index d67652d58..6477a1e79 100644 --- a/src/widgets/splits/Split.cpp +++ b/src/widgets/splits/Split.cpp @@ -19,9 +19,11 @@ #include "widgets/dialogs/UserInfoPopup.hpp" #include "widgets/helper/ChannelView.hpp" #include "widgets/helper/DebugPopup.hpp" +#include "widgets/helper/NotebookTab.hpp" #include "widgets/helper/ResizingTextEdit.hpp" #include "widgets/helper/SearchPopup.hpp" #include "widgets/helper/Shortcut.hpp" +#include "widgets/splits/ClosedSplits.hpp" #include "widgets/splits/SplitContainer.hpp" #include "widgets/splits/SplitHeader.hpp" #include "widgets/splits/SplitInput.hpp" @@ -415,6 +417,10 @@ void Split::deleteFromContainer() { if (this->container_) { this->container_->deleteSplit(this); + auto *tab = this->getContainer()->getTab(); + tab->connect(tab, &QWidget::destroyed, + [tab]() mutable { ClosedSplits::invalidateTab(tab); }); + ClosedSplits::push({this->getChannel()->getName(), tab}); } } From 7a246cd9d8b693e5921b15ec279ac34495340712 Mon Sep 17 00:00:00 2001 From: hemirt Date: Sat, 13 Oct 2018 11:08:57 +0200 Subject: [PATCH 02/12] pragma once --- src/widgets/splits/ClosedSplits.hpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/widgets/splits/ClosedSplits.hpp b/src/widgets/splits/ClosedSplits.hpp index 4d05f22c0..59c9d6594 100644 --- a/src/widgets/splits/ClosedSplits.hpp +++ b/src/widgets/splits/ClosedSplits.hpp @@ -1,5 +1,4 @@ -#ifndef CLOSEDSPLITS_HPP -#define CLOSEDSPLITS_HPP +#pragma once #include "common/Channel.hpp" #include "widgets/helper/NotebookTab.hpp" @@ -31,5 +30,3 @@ private: }; } // namespace chatterino - -#endif // CLOSEDSPLITS_HPP From 71a51d34c740ab100271e693cf1a3fc16c02203d Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 11 Oct 2018 15:56:10 +0300 Subject: [PATCH 03/12] Combined emotes of different tiers in Emote Popup. --- src/widgets/dialogs/EmotePopup.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/widgets/dialogs/EmotePopup.cpp b/src/widgets/dialogs/EmotePopup.cpp index 0f3375121..227493050 100644 --- a/src/widgets/dialogs/EmotePopup.cpp +++ b/src/widgets/dialogs/EmotePopup.cpp @@ -48,19 +48,26 @@ namespace { std::vector> sets, Channel &globalChannel, Channel &subChannel) { - for (const auto &set : sets) { - auto &channel = set->key == "0" ? globalChannel : subChannel; + QMap>> mapOfSets; + for (const auto &set : sets) { // TITLE + auto channelName = set->channelName; auto text = set->key == "0" || set->text.isEmpty() ? "Twitch" : set->text; - channel.addMessage(makeTitleMessage(text)); // EMOTES MessageBuilder builder; builder->flags.set(MessageFlag::Centered); builder->flags.set(MessageFlag::DisableCompactEmotes); + // If value of map is empty, create init pair and add title. + if (mapOfSets.find(channelName) == mapOfSets.end()) { + std::vector b; + b.push_back(makeTitleMessage(text)); + mapOfSets[channelName] = qMakePair(set->key == "0", b); + } + for (const auto &emote : set->emotes) { builder .emplace( @@ -70,7 +77,16 @@ namespace { ->setLink(Link(Link::InsertText, emote.name.string)); } - channel.addMessage(builder.release()); + mapOfSets[channelName].second.push_back(builder.release()); + } + + // Output to channel all created messages, + // That contain title or emotes. + foreach (auto pair, mapOfSets) { + auto &channel = pair.first ? globalChannel : subChannel; + for (auto message : pair.second) { + channel.addMessage(message); + } } } } // namespace From 49449379da1a9fe2a865c139df487e2c4d1ca3b4 Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sat, 13 Oct 2018 14:20:06 +0200 Subject: [PATCH 04/12] Added a Live status indicator to tabs --- src/common/Channel.cpp | 5 ++ src/common/Channel.hpp | 1 + src/common/Common.hpp | 1 - src/providers/twitch/TwitchChannel.cpp | 2 - src/providers/twitch/TwitchChannel.hpp | 2 +- src/singletons/Theme.cpp | 8 --- src/singletons/Theme.hpp | 1 - src/widgets/helper/ChannelView.cpp | 5 +- src/widgets/helper/ChannelView.hpp | 1 + src/widgets/helper/NotebookTab.cpp | 27 ++++++-- src/widgets/helper/NotebookTab.hpp | 3 + src/widgets/splits/Split.cpp | 2 +- src/widgets/splits/SplitContainer.cpp | 88 +++++++++++++++++--------- src/widgets/splits/SplitContainer.hpp | 9 ++- 14 files changed, 104 insertions(+), 51 deletions(-) diff --git a/src/common/Channel.cpp b/src/common/Channel.cpp index d73213954..84b882c4f 100644 --- a/src/common/Channel.cpp +++ b/src/common/Channel.cpp @@ -221,6 +221,11 @@ bool Channel::hasModRights() const return this->isMod() || this->isBroadcaster(); } +bool Channel::isLive() const +{ + return false; +} + std::shared_ptr Channel::getEmpty() { static std::shared_ptr channel(new Channel("", Type::None)); diff --git a/src/common/Channel.hpp b/src/common/Channel.hpp index 12382561f..5d2d96db2 100644 --- a/src/common/Channel.hpp +++ b/src/common/Channel.hpp @@ -69,6 +69,7 @@ public: virtual bool isMod() const; virtual bool isBroadcaster() const; virtual bool hasModRights() const; + virtual bool isLive() const; static std::shared_ptr getEmpty(); diff --git a/src/common/Common.hpp b/src/common/Common.hpp index bea206f78..7f22bf27d 100644 --- a/src/common/Common.hpp +++ b/src/common/Common.hpp @@ -17,7 +17,6 @@ enum class HighlightState { None, Highlighted, NewMessage, - Notification, }; inline QString qS(const std::string &string) diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index f3523ffba..be38dce59 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -413,8 +413,6 @@ void TwitchChannel::setLive(bool newLiveStatus) } auto live = makeSystemMessage(this->getName() + " is live"); this->addMessage(live); - this->tabHighlightRequested.invoke( - HighlightState::Notification); } else { auto offline = makeSystemMessage(this->getName() + " is offline"); diff --git a/src/providers/twitch/TwitchChannel.hpp b/src/providers/twitch/TwitchChannel.hpp index a4a180135..1411e8894 100644 --- a/src/providers/twitch/TwitchChannel.hpp +++ b/src/providers/twitch/TwitchChannel.hpp @@ -66,7 +66,7 @@ public: const QString &subscriptionUrl(); const QString &channelUrl(); const QString &popoutPlayerUrl(); - bool isLive() const; + virtual bool isLive() const override; QString roomId() const; AccessGuard accessRoomModes() const; AccessGuard accessStreamStatus() const; diff --git a/src/singletons/Theme.cpp b/src/singletons/Theme.cpp index 39272c4be..6a4d937a4 100644 --- a/src/singletons/Theme.cpp +++ b/src/singletons/Theme.cpp @@ -105,10 +105,6 @@ void Theme::actuallyUpdate(double hue, double multiplier) QColor("#000"), {QColor("#b4d7ff"), QColor("#b4d7ff"), QColor("#b4d7ff")}, {QColor("#00aeef"), QColor("#00aeef"), QColor("#00aeef")}}; - this->tabs.notified = { - fg, - {QColor("#fff"), QColor("#fff"), QColor("#fff")}, - {QColor("#F824A8"), QColor("#F824A8"), QColor("#F824A8")}}; } else { this->tabs.regular = { QColor("#aaa"), @@ -127,10 +123,6 @@ void Theme::actuallyUpdate(double hue, double multiplier) QColor("#fff"), {QColor("#555555"), QColor("#555555"), QColor("#555555")}, {QColor("#00aeef"), QColor("#00aeef"), QColor("#00aeef")}}; - this->tabs.notified = { - fg, - {QColor("#252525"), QColor("#252525"), QColor("#252525")}, - {QColor("#F824A8"), QColor("#F824A8"), QColor("#F824A8")}}; } this->splits.input.focusedLine = highlighted; diff --git a/src/singletons/Theme.hpp b/src/singletons/Theme.hpp index d301d21af..2267b68f3 100644 --- a/src/singletons/Theme.hpp +++ b/src/singletons/Theme.hpp @@ -50,7 +50,6 @@ public: TabColors newMessage; TabColors highlighted; TabColors selected; - TabColors notified; QColor border; QColor bottomLine; } tabs; diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index 8c2c60217..d00859d5a 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -555,7 +555,10 @@ void ChannelView::setChannel(ChannelPtr newChannel) TwitchChannel *tc = dynamic_cast(newChannel.get()); if (tc != nullptr) { tc->tabHighlightRequested.connect([this](HighlightState state) { - this->tabHighlightRequested.invoke(HighlightState::Notification); + this->tabHighlightRequested.invoke(state); + }); + tc->liveStatusChanged.connect([this]() { + this->liveStatusChanged.invoke(); // }); } } diff --git a/src/widgets/helper/ChannelView.hpp b/src/widgets/helper/ChannelView.hpp index 512439e38..3cd602a18 100644 --- a/src/widgets/helper/ChannelView.hpp +++ b/src/widgets/helper/ChannelView.hpp @@ -61,6 +61,7 @@ public: pajlada::Signals::Signal mouseDown; pajlada::Signals::NoArgSignal selectionChanged; pajlada::Signals::Signal tabHighlightRequested; + pajlada::Signals::NoArgSignal liveStatusChanged; pajlada::Signals::Signal linkClicked; pajlada::Signals::Signal joinToChannel; diff --git a/src/widgets/helper/NotebookTab.cpp b/src/widgets/helper/NotebookTab.cpp index 8875a4ca6..fe05303cb 100644 --- a/src/widgets/helper/NotebookTab.cpp +++ b/src/widgets/helper/NotebookTab.cpp @@ -168,13 +168,20 @@ void NotebookTab::setSelected(bool value) this->update(); } +void NotebookTab::setLive(bool isLive) +{ + if (this->isLive_ != isLive) { + this->isLive_ = isLive; + this->update(); + } +} + void NotebookTab::setHighlightState(HighlightState newHighlightStyle) { if (this->isSelected() || !this->highlightEnabled_) { return; } - if (this->highlightState_ != HighlightState::Highlighted && - this->highlightState_ != HighlightState::Notification) { + if (this->highlightState_ != HighlightState::Highlighted) { this->highlightState_ = newHighlightStyle; this->update(); @@ -252,8 +259,6 @@ void NotebookTab::paintEvent(QPaintEvent *) colors = this->theme->tabs.selected; } else if (this->highlightState_ == HighlightState::Highlighted) { colors = this->theme->tabs.highlighted; - } else if (this->highlightState_ == HighlightState::Notification) { - colors = this->theme->tabs.notified; } else if (this->highlightState_ == HighlightState::NewMessage) { colors = this->theme->tabs.newMessage; } else { @@ -297,6 +302,20 @@ void NotebookTab::paintEvent(QPaintEvent *) ? colors.line.hover : (windowFocused ? colors.line.regular : colors.line.unfocused)); + // draw live indicator + if (this->isLive_) { + painter.setPen(QColor(Qt::GlobalColor::red)); + QBrush b; + b.setColor(QColor(Qt::GlobalColor::red)); + b.setStyle(Qt::SolidPattern); + painter.setBrush(b); + + auto x = this->width() - (6.f * scale); + auto y = 4.f * scale; + auto diameter = 4.f * scale; + painter.drawEllipse(QRectF(x, y, diameter, diameter)); + } + // set the pen color painter.setPen(colors.text); diff --git a/src/widgets/helper/NotebookTab.hpp b/src/widgets/helper/NotebookTab.hpp index 4ec931acc..0e8889611 100644 --- a/src/widgets/helper/NotebookTab.hpp +++ b/src/widgets/helper/NotebookTab.hpp @@ -39,6 +39,7 @@ public: bool isSelected() const; void setSelected(bool value); + void setLive(bool isLive); void setHighlightState(HighlightState style); void setHighlightsEnabled(const bool &newVal); bool hasHighlightsEnabled() const; @@ -88,6 +89,8 @@ private: bool highlightEnabled_ = true; QAction *highlightNewMessagesAction_; + bool isLive_{}; + QMenu menu_; std::vector managedConnections_; diff --git a/src/widgets/splits/Split.cpp b/src/widgets/splits/Split.cpp index d67652d58..34d00a5b5 100644 --- a/src/widgets/splits/Split.cpp +++ b/src/widgets/splits/Split.cpp @@ -290,7 +290,7 @@ void Split::showChangeChannelPopup(const char *dialogTitle, bool empty, if (dialog->hasSeletedChannel()) { this->setChannel(dialog->getSelectedChannel()); if (this->isInContainer()) { - this->container_->refreshTabTitle(); + this->container_->refreshTab(); } } diff --git a/src/widgets/splits/SplitContainer.cpp b/src/widgets/splits/SplitContainer.cpp index a4de3e6d1..61d465c2b 100644 --- a/src/widgets/splits/SplitContainer.cpp +++ b/src/widgets/splits/SplitContainer.cpp @@ -84,7 +84,7 @@ void SplitContainer::setTab(NotebookTab *_tab) this->tab_->page = this; - this->refreshTabTitle(); + this->refreshTab(); } void SplitContainer::hideResizeHandles() @@ -177,7 +177,7 @@ void SplitContainer::addSplit(Split *split) this->unsetCursor(); this->splits_.push_back(split); - this->refreshTabTitle(); + this->refreshTab(); split->getChannelView().tabHighlightRequested.connect( [this](HighlightState state) { @@ -186,6 +186,10 @@ void SplitContainer::addSplit(Split *split) } }); + split->getChannelView().liveStatusChanged.connect([this]() { + this->refreshTabLiveStatus(); // + }); + split->focused.connect([this, split] { this->setSelected(split); }); this->layout(); @@ -228,7 +232,7 @@ SplitContainer::Position SplitContainer::releaseSplit(Split *split) this->splits_.front()->giveFocus(Qt::MouseFocusReason); } - this->refreshTabTitle(); + this->refreshTab(); // fourtf: really bad split->getChannelView().tabHighlightRequested.disconnectAll(); @@ -568,34 +572,10 @@ void SplitContainer::focusInEvent(QFocusEvent *) } } -void SplitContainer::refreshTabTitle() +void SplitContainer::refreshTab() { - if (this->tab_ == nullptr) { - return; - } - - QString newTitle = ""; - bool first = true; - - for (const auto &chatWidget : this->splits_) { - auto channelName = chatWidget->getChannel()->getName(); - if (channelName.isEmpty()) { - continue; - } - - if (!first) { - newTitle += ", "; - } - newTitle += channelName; - - first = false; - } - - if (newTitle.isEmpty()) { - newTitle = "empty"; - } - - this->tab_->setDefaultTitle(newTitle); + this->refreshTabTitle(); + this->refreshTabLiveStatus(); } int SplitContainer::getSplitCount() @@ -677,6 +657,54 @@ void SplitContainer::decodeNodeRecusively(QJsonObject &obj, Node *node) } } +void SplitContainer::refreshTabTitle() +{ + if (this->tab_ == nullptr) { + return; + } + + QString newTitle = ""; + bool first = true; + + for (const auto &chatWidget : this->splits_) { + auto channelName = chatWidget->getChannel()->getName(); + if (channelName.isEmpty()) { + continue; + } + + if (!first) { + newTitle += ", "; + } + newTitle += channelName; + + first = false; + } + + if (newTitle.isEmpty()) { + newTitle = "empty"; + } + + this->tab_->setDefaultTitle(newTitle); +} + +void SplitContainer::refreshTabLiveStatus() +{ + if (this->tab_ == nullptr) { + return; + } + + bool liveStatus = false; + for (const auto &s : this->splits_) { + auto c = s->getChannel(); + if (c->isLive()) { + liveStatus = true; + break; + } + } + + this->tab_->setLive(liveStatus); +} + // // Node // diff --git a/src/widgets/splits/SplitContainer.hpp b/src/widgets/splits/SplitContainer.hpp index 33604ba7a..a29930038 100644 --- a/src/widgets/splits/SplitContainer.hpp +++ b/src/widgets/splits/SplitContainer.hpp @@ -171,7 +171,7 @@ private: public: SplitContainer(Notebook *parent); - Split* appendNewSplit(bool openChannelNameDialog); + Split *appendNewSplit(bool openChannelNameDialog); void appendSplit(Split *split); void insertSplit(Split *split, const Position &position); void insertSplit(Split *split, Direction direction, Split *relativeTo); @@ -186,7 +186,9 @@ public: int getSplitCount(); const std::vector getSplits() const; - void refreshTabTitle(); + + void refreshTab(); + NotebookTab *getTab() const; Node *getBaseNode(); @@ -221,6 +223,9 @@ private: void decodeNodeRecusively(QJsonObject &obj, Node *node); Split *getTopRightSplit(Node &node); + void refreshTabTitle(); + void refreshTabLiveStatus(); + struct DropRegion { QRect rect; std::pair position; From 6413abf88536b14e87027bdfc055f1899ec2809f Mon Sep 17 00:00:00 2001 From: hemirt Date: Sat, 13 Oct 2018 14:34:33 +0200 Subject: [PATCH 05/12] fix visual bug --- src/widgets/Window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/Window.cpp b/src/widgets/Window.cpp index 3f6ebdd98..5b0ce1fc2 100644 --- a/src/widgets/Window.cpp +++ b/src/widgets/Window.cpp @@ -322,9 +322,9 @@ void Window::addShortcuts() } this->notebook_->select(splitContainer); Split *split = new Split(splitContainer); - splitContainer->appendSplit(split); split->setChannel( getApp()->twitch.server->getOrAddChannel(si.channelName)); + splitContainer->appendSplit(split); }); } From 1422201570e5cfc094f5e4fbfe0f0fd13e6bd07b Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sat, 13 Oct 2018 14:45:51 +0200 Subject: [PATCH 06/12] Make "recent_messages" network request safe in case the channel is closed when the request finished --- src/providers/twitch/TwitchChannel.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index be38dce59..072fd721a 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -29,8 +29,7 @@ namespace chatterino { namespace { - auto parseRecentMessages(const QJsonObject &jsonRoot, - TwitchChannel &channel) + auto parseRecentMessages(const QJsonObject &jsonRoot, ChannelPtr channel) { QJsonArray jsonMessages = jsonRoot.value("messages").toArray(); std::vector messages; @@ -46,7 +45,7 @@ namespace { assert(privMsg); MessageParseArgs args; - TwitchMessageBuilder builder(&channel, privMsg, args); + TwitchMessageBuilder builder(channel.get(), privMsg, args); if (!builder.isIgnored()) { messages.push_back(builder.build()); } @@ -546,13 +545,13 @@ void TwitchChannel::loadRecentMessages() // can't be concurrent right now due to SignalVector // request.setExecuteConcurrently(true); - request.onSuccess([that = this](auto result) -> Outcome { - auto messages = parseRecentMessages(result.parseJson(), *that); + request.onSuccess([weak = weakOf(this)](auto result) -> Outcome { + auto shared = weak.lock(); + if (!shared) return Failure; - // postToThread([that, weak = weakOf(that), - // messages = std::move(messages)]() mutable { - that->addMessagesAtStart(messages); - // }); + auto messages = parseRecentMessages(result.parseJson(), shared); + + shared->addMessagesAtStart(messages); return Success; }); From c38bb05bb414538915aa6a0725d03b21ec171150 Mon Sep 17 00:00:00 2001 From: ckath Date: Sat, 13 Oct 2018 14:22:49 +0200 Subject: [PATCH 07/12] correct options text of hiding buttons --- src/widgets/settingspages/LookPage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/widgets/settingspages/LookPage.cpp b/src/widgets/settingspages/LookPage.cpp index 26ff0b619..fbb175c8b 100644 --- a/src/widgets/settingspages/LookPage.cpp +++ b/src/widgets/settingspages/LookPage.cpp @@ -23,8 +23,8 @@ #define THEME_ITEMS "White", "Light", "Dark", "Black" #define TAB_X "Show tab close button" -#define TAB_PREF "Preferences button (ctrl+p to show)" -#define TAB_USER "User button" +#define TAB_PREF "Hide preferences button (ctrl+p to show)" +#define TAB_USER "Hide user button" // clang-format off #define TIMESTAMP_FORMATS "hh:mm a", "h:mm a", "hh:mm:ss a", "h:mm:ss a", "HH:mm", "H:mm", "HH:mm:ss", "H:mm:ss" From 230b2006dca9e28c1eaecac742378606c78a368c Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sat, 13 Oct 2018 15:45:13 +0200 Subject: [PATCH 08/12] Experimental reconnect support --- src/providers/irc/AbstractIrcServer.cpp | 28 +++++++++++++++++++++++++ src/providers/irc/AbstractIrcServer.hpp | 4 ++++ 2 files changed, 32 insertions(+) diff --git a/src/providers/irc/AbstractIrcServer.cpp b/src/providers/irc/AbstractIrcServer.cpp index 25c810a4d..e884dc327 100644 --- a/src/providers/irc/AbstractIrcServer.cpp +++ b/src/providers/irc/AbstractIrcServer.cpp @@ -11,6 +11,10 @@ namespace chatterino { +const int RECONNECT_BASE_INTERVAL = 2000; +// 60 falloff counter means it will try to reconnect at most every 60*2 seconds +const int MAX_FALLOFF_COUNTER = 60; + AbstractIrcServer::AbstractIrcServer() { // Initialize the connections @@ -38,12 +42,29 @@ AbstractIrcServer::AbstractIrcServer() QObject::connect(this->readConnection_.get(), &Communi::IrcConnection::disconnected, [this] { this->onDisconnected(); }); + QObject::connect(this->readConnection_.get(), + &Communi::IrcConnection::socketError, + [this] { this->onSocketError(); }); // listen to reconnect request this->readConnection_->reconnectRequested.connect( [this] { this->connect(); }); // this->writeConnection->reconnectRequested.connect([this] { // this->connect(); }); + this->reconnectTimer_.setInterval(RECONNECT_BASE_INTERVAL); + this->reconnectTimer_.setSingleShot(true); + QObject::connect(&this->reconnectTimer_, &QTimer::timeout, [this] { + this->reconnectTimer_.setInterval(RECONNECT_BASE_INTERVAL * + this->falloffCounter_); + + this->falloffCounter_ = + std::min(MAX_FALLOFF_COUNTER, this->falloffCounter_ + 1); + + if (!this->readConnection_->isConnected()) { + log("Trying to reconnect... {}", this->falloffCounter_); + this->connect(); + } + }); } void AbstractIrcServer::connect() @@ -215,6 +236,8 @@ void AbstractIrcServer::onConnected() chan->addMessage(connected); } + + this->falloffCounter_ = 1; } void AbstractIrcServer::onDisconnected() @@ -235,6 +258,11 @@ void AbstractIrcServer::onDisconnected() } } +void AbstractIrcServer::onSocketError() +{ + this->reconnectTimer_.start(); +} + std::shared_ptr AbstractIrcServer::getCustomChannel( const QString &channelName) { diff --git a/src/providers/irc/AbstractIrcServer.hpp b/src/providers/irc/AbstractIrcServer.hpp index 109f869ef..e605340cd 100644 --- a/src/providers/irc/AbstractIrcServer.hpp +++ b/src/providers/irc/AbstractIrcServer.hpp @@ -54,6 +54,7 @@ protected: virtual void onConnected(); virtual void onDisconnected(); + virtual void onSocketError(); virtual std::shared_ptr getCustomChannel( const QString &channelName); @@ -70,6 +71,9 @@ private: std::unique_ptr writeConnection_ = nullptr; std::unique_ptr readConnection_ = nullptr; + QTimer reconnectTimer_; + int falloffCounter_ = 1; + std::mutex connectionMutex_; // bool autoReconnect_ = false; From dec8283cddde0e9f82e872f0850325fb85bdbb7f Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 14 Oct 2018 01:54:58 +0300 Subject: [PATCH 09/12] Updated README.md. Added one-line command to get source with submodules. --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4e5bc1f41..11e231e0d 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,15 @@ If you still receive an error about `MSVCR120.dll missing`, then you should inst Releases for linux and mac will follow soon™ ## Building -Before building run `git submodule update --init --recursive` to get required submodules. +To get source code with required submodules run: +``` +git clone --recursive https://github.com/fourtf/chatterino2.git +``` +or +``` +git clone https://github.com/fourtf/chatterino2.git +git submodule update --init --recursive +``` [Building on Windows](../master/BUILDING_ON_WINDOWS.md) From bd04aa007478d6ba92da7459102bf8882f095a6d Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 13 Oct 2018 17:29:37 +0300 Subject: [PATCH 10/12] Added more ignored words for opening new split from context menu. --- src/widgets/helper/ChannelView.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index d00859d5a..575d4f359 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -1254,7 +1254,7 @@ void ChannelView::addContextMenuItems( QGuiApplication::clipboard()->setText(copyString); }); - // Join to channel + // Open in new split. if (hoveredElement->getLink().type == Link::Url) { static QRegularExpression twitchChannelRegex( R"(^(?:https?:\/\/)?(?:www\.|go\.)?twitch\.tv\/(?[a-z0-9_]+))", @@ -1262,6 +1262,13 @@ void ChannelView::addContextMenuItems( static QSet ignoredUsernames{ "videos", "settings", + "directory", + "jobs", + "friends", + "inventory", + "payments", + "subscriptions", + "messages", }; auto twitchMatch = From 3e268637f01283379a99298575428b39dd7420cd Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 13 Oct 2018 17:32:01 +0300 Subject: [PATCH 11/12] Improved 'twitchChannelRegex' regexp. --- src/widgets/helper/ChannelView.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index 575d4f359..8f9ff2917 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -1257,7 +1257,7 @@ void ChannelView::addContextMenuItems( // Open in new split. if (hoveredElement->getLink().type == Link::Url) { static QRegularExpression twitchChannelRegex( - R"(^(?:https?:\/\/)?(?:www\.|go\.)?twitch\.tv\/(?[a-z0-9_]+))", + R"(^(?:https?:\/\/)?(?:www\.|go\.)?twitch\.tv\/(?[a-z0-9_]{3,}))", QRegularExpression::CaseInsensitiveOption); static QSet ignoredUsernames{ "videos", From e13d927a4e80f4ec4714c247fcec178e9aa36788 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 13 Oct 2018 17:15:26 +0300 Subject: [PATCH 12/12] Removed unused signal in TwitchChannel. --- src/providers/twitch/TwitchChannel.cpp | 1 - src/providers/twitch/TwitchChannel.hpp | 1 - src/widgets/helper/ChannelView.cpp | 6 +----- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index 072fd721a..a75c1b8f2 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -89,7 +89,6 @@ TwitchChannel::TwitchChannel(const QString &name, { log("[TwitchChannel:{}] Opened", name); - this->tabHighlightRequested.connect([](HighlightState state) {}); this->liveStatusChanged.connect([this]() { if (this->isLive() == 1) { } diff --git a/src/providers/twitch/TwitchChannel.hpp b/src/providers/twitch/TwitchChannel.hpp index 1411e8894..edc6b7af2 100644 --- a/src/providers/twitch/TwitchChannel.hpp +++ b/src/providers/twitch/TwitchChannel.hpp @@ -92,7 +92,6 @@ public: pajlada::Signals::NoArgSignal userStateChanged; pajlada::Signals::NoArgSignal liveStatusChanged; pajlada::Signals::NoArgSignal roomModesChanged; - pajlada::Signals::Signal tabHighlightRequested; protected: void addRecentChatter(const MessagePtr &message) override; diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index 8f9ff2917..d4f9242b9 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -552,11 +552,7 @@ void ChannelView::setChannel(ChannelPtr newChannel) this->queueUpdate(); // Notifications - TwitchChannel *tc = dynamic_cast(newChannel.get()); - if (tc != nullptr) { - tc->tabHighlightRequested.connect([this](HighlightState state) { - this->tabHighlightRequested.invoke(state); - }); + if (auto tc = dynamic_cast(newChannel.get())) { tc->liveStatusChanged.connect([this]() { this->liveStatusChanged.invoke(); // });