diff --git a/src/accountmanager.cpp b/src/accountmanager.cpp index 256287ec4..5a70a5653 100644 --- a/src/accountmanager.cpp +++ b/src/accountmanager.cpp @@ -67,9 +67,24 @@ twitch::TwitchUser &AccountManager::getTwitchUser() return this->getTwitchAnon(); } + std::string currentUser = pajlada::Settings::Setting::get("/accounts/current"); + + QString currentUsername = QString::fromStdString(currentUser); + + for (auto &user : this->twitchUsers) { + if (user.getUserName() == currentUsername) { + return user; + } + } + return this->twitchUsers.front(); } +void AccountManager::setCurrentTwitchUser(const QString &username) +{ + pajlada::Settings::Setting::set("/accounts/current", username.toStdString()); +} + std::vector AccountManager::getTwitchUsers() { std::lock_guard lock(this->twitchUsersMutex); diff --git a/src/accountmanager.hpp b/src/accountmanager.hpp index a6b89bd0e..5e2858e64 100644 --- a/src/accountmanager.hpp +++ b/src/accountmanager.hpp @@ -29,6 +29,8 @@ public: // Remove twitch user with the given username bool removeTwitchUser(const QString &userName); + void setCurrentTwitchUser(const QString &username); + // Add twitch user to the list of available twitch users void addTwitchUser(const twitch::TwitchUser &user); diff --git a/src/ircmanager.cpp b/src/ircmanager.cpp index 74fc0f32b..76240b4e7 100644 --- a/src/ircmanager.cpp +++ b/src/ircmanager.cpp @@ -31,8 +31,13 @@ IrcManager::IrcManager(ChannelManager &_channelManager, Resources &_resources, , resources(_resources) , emoteManager(_emoteManager) , windowManager(_windowManager) - , _account(AccountManager::getInstance().getTwitchUser()) + , _account(AccountManager::getInstance().getTwitchAnon()) + , currentUser("/accounts/current") { + this->currentUser.getValueChangedSignal().connect([](const auto &newUsername) { + // TODO: Implement + qDebug() << "Current user changed, fetch new credentials and reconnect"; + }); } const twitch::TwitchUser &IrcManager::getUser() const diff --git a/src/ircmanager.hpp b/src/ircmanager.hpp index c3153e59d..f24810cad 100644 --- a/src/ircmanager.hpp +++ b/src/ircmanager.hpp @@ -58,6 +58,8 @@ private: // variables twitch::TwitchUser _account; + pajlada::Settings::Setting currentUser; + std::shared_ptr writeConnection = nullptr; std::shared_ptr readConnection = nullptr; diff --git a/src/settingsmanager.cpp b/src/settingsmanager.cpp index b54a1114a..11aaf71a6 100644 --- a/src/settingsmanager.cpp +++ b/src/settingsmanager.cpp @@ -15,7 +15,6 @@ SettingsManager::SettingsManager() , showTimestampSeconds("/appearance/messages/showTimestampSeconds", true) , showBadges("/appearance/messages/showBadges", true) , streamlinkPath("/behaviour/streamlinkPath", "") - , selectedUser(_settingsItems, "selectedUser", "") , emoteScale(_settingsItems, "emoteScale", 1.0) , mouseScrollMultiplier(_settingsItems, "mouseScrollMultiplier", 1.0) , scaleEmotesByLineHeight(_settingsItems, "scaleEmotesByLineHeight", false) diff --git a/src/settingsmanager.hpp b/src/settingsmanager.hpp index b1613432f..f11596d35 100644 --- a/src/settingsmanager.hpp +++ b/src/settingsmanager.hpp @@ -46,7 +46,6 @@ public: pajlada::Settings::Setting streamlinkPath; // Settings - Setting selectedUser; Setting emoteScale; Setting mouseScrollMultiplier; Setting scaleEmotesByLineHeight; diff --git a/src/twitch/twitchmessagebuilder.cpp b/src/twitch/twitchmessagebuilder.cpp index 22b0a656d..acd9c479f 100644 --- a/src/twitch/twitchmessagebuilder.cpp +++ b/src/twitch/twitchmessagebuilder.cpp @@ -365,8 +365,11 @@ void TwitchMessageBuilder::parseHighlights() { static auto player = new QMediaPlayer; SettingsManager &settings = SettingsManager::getInstance(); + static pajlada::Settings::Setting currentUser("/accounts/currentUser"); - if (this->ircMessage->nick() == settings.selectedUser.get()) { + QString currentUsername = QString::fromStdString(currentUser.getValue()); + + if (this->ircMessage->nick() == currentUsername) { // Do nothing. Highlights cannot be triggered by yourself return; } @@ -393,9 +396,8 @@ void TwitchMessageBuilder::parseHighlights() // TODO: This vector should only be rebuilt upon highlights being changed std::vector activeHighlights; - if (settings.enableHighlightsSelf.get() && settings.selectedUser.get().size() > 0) { - activeHighlights.emplace_back(settings.selectedUser.get(), - settings.enableHighlightSound.get(), + if (settings.enableHighlightsSelf.get() && currentUsername.size() > 0) { + activeHighlights.emplace_back(currentUsername, settings.enableHighlightSound.get(), settings.enableHighlightTaskbar.get()); } const auto &highlightProperties = settings.highlightProperties.get(); diff --git a/src/util/urlfetch.hpp b/src/util/urlfetch.hpp index d68e5e1c9..e86ed4074 100644 --- a/src/util/urlfetch.hpp +++ b/src/util/urlfetch.hpp @@ -1,5 +1,7 @@ #pragma once +#include "credentials.hpp" + #include #include #include @@ -16,6 +18,67 @@ namespace chatterino { namespace util { +namespace twitch { + +static void get(QString url, std::function successCallback) +{ + auto manager = new QNetworkAccessManager(); + + QUrl requestUrl(url); + QNetworkRequest request(requestUrl); + + request.setRawHeader("Client-ID", getDefaultClientID()); + request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json"); + + QNetworkReply *reply = manager->get(request); + + QObject::connect(reply, &QNetworkReply::finished, [=] { + if (reply->error() == QNetworkReply::NetworkError::NoError) { + QByteArray data = reply->readAll(); + QJsonDocument jsonDoc(QJsonDocument::fromJson(data)); + + if (!jsonDoc.isNull()) { + QJsonObject rootNode = jsonDoc.object(); + + successCallback(rootNode); + } + } + + reply->deleteLater(); + manager->deleteLater(); + }); +} + +static void getUserID(QString username, std::function successCallback) +{ + get("https://api.twitch.tv/kraken/users?login=" + username, [=](const QJsonObject &root) { + if (!root.value("users").isArray()) { + qDebug() << "API Error while getting user id, users is not an array"; + return; + } + + auto users = root.value("users").toArray(); + if (users.size() != 1) { + qDebug() << "API Error while getting user id, users array size is not 1"; + return; + } + if (!users[0].isObject()) { + qDebug() << "API Error while getting user id, first user is not an object"; + return; + } + auto firstUser = users[0].toObject(); + auto id = firstUser.value("_id"); + if (!id.isString()) { + qDebug() + << "API Error: while getting user id, first user object `_id` key is not a string"; + return; + } + successCallback(id.toString()); + }); +} + +} // namespace twitch + static void urlFetch(const QString &url, std::function successCallback, QNetworkAccessManager *manager = nullptr) { diff --git a/src/widgets/chatwidgetview.cpp b/src/widgets/chatwidgetview.cpp index 5b93df0dd..70f1dc2c9 100644 --- a/src/widgets/chatwidgetview.cpp +++ b/src/widgets/chatwidgetview.cpp @@ -279,10 +279,9 @@ void ChatWidgetView::updateMessageBuffer(messages::MessageRef *messageRef, QPixm // this->selectionMax.messageIndex >= messageIndex) { // painter.fillRect(buffer->rect(), QColor(24, 55, 25)); //} else { - painter.fillRect(buffer->rect(), - (messageRef->getMessage()->getCanHighlightTab()) - ? this->colorScheme.ChatBackgroundHighlighted - : this->colorScheme.ChatBackground); + painter.fillRect(buffer->rect(), (messageRef->getMessage()->getCanHighlightTab()) + ? this->colorScheme.ChatBackgroundHighlighted + : this->colorScheme.ChatBackground); //} // draw messages diff --git a/src/widgets/logindialog.cpp b/src/widgets/logindialog.cpp index ebc63b356..5d8df8b75 100644 --- a/src/widgets/logindialog.cpp +++ b/src/widgets/logindialog.cpp @@ -1,4 +1,5 @@ #include "widgets/logindialog.hpp" +#include "util/urlfetch.hpp" #include #include @@ -9,9 +10,9 @@ namespace chatterino { namespace widgets { -LoginWidget::LoginWidget() +BasicLoginWidget::BasicLoginWidget() { - this->setLayout(&this->ui.mainLayout); + this->setLayout(&this->ui.layout); this->ui.loginButton.setText("Log in (Opens in browser)"); this->ui.pasteCodeButton.setText("Paste code"); @@ -19,22 +20,11 @@ LoginWidget::LoginWidget() this->ui.horizontalLayout.addWidget(&this->ui.loginButton); this->ui.horizontalLayout.addWidget(&this->ui.pasteCodeButton); - this->ui.verticalLayout.addLayout(&this->ui.horizontalLayout); - - this->ui.mainLayout.addLayout(&this->ui.verticalLayout); - - this->ui.buttonBox.setStandardButtons(QDialogButtonBox::Close); - - this->ui.mainLayout.addWidget(&this->ui.buttonBox); - - connect(&this->ui.buttonBox, &QDialogButtonBox::rejected, [this]() { - this->close(); // - }); + this->ui.layout.addLayout(&this->ui.horizontalLayout); connect(&this->ui.loginButton, &QPushButton::clicked, []() { printf("open login in browser\n"); QDesktopServices::openUrl(QUrl("https://pajlada.se/chatterino/#chatterino")); - }); connect(&this->ui.pasteCodeButton, &QPushButton::clicked, []() { @@ -81,5 +71,108 @@ LoginWidget::LoginWidget() }); } +AdvancedLoginWidget::AdvancedLoginWidget() +{ + this->setLayout(&this->ui.layout); + + this->ui.instructionsLabel.setText("1. Fill in your username\n2. Fill in your user ID or press " + "the 'Get user ID from username' button\n3. Fill in your " + "Client ID\n4. Fill in your OAuth Token\n5. Press Add User"); + this->ui.instructionsLabel.setWordWrap(true); + + this->ui.layout.addWidget(&this->ui.instructionsLabel); + this->ui.layout.addLayout(&this->ui.formLayout); + this->ui.layout.addLayout(&this->ui.buttonUpperRow.layout); + this->ui.layout.addLayout(&this->ui.buttonLowerRow.layout); + + this->refreshButtons(); + + /// Form + this->ui.formLayout.addRow("Username", &this->ui.usernameInput); + this->ui.formLayout.addRow("User ID", &this->ui.userIDInput); + this->ui.formLayout.addRow("Client ID", &this->ui.clientIDInput); + this->ui.formLayout.addRow("Oauth token", &this->ui.oauthTokenInput); + + this->ui.oauthTokenInput.setEchoMode(QLineEdit::Password); + + connect(&this->ui.userIDInput, &QLineEdit::textChanged, [=]() { this->refreshButtons(); }); + connect(&this->ui.usernameInput, &QLineEdit::textChanged, [=]() { this->refreshButtons(); }); + connect(&this->ui.clientIDInput, &QLineEdit::textChanged, [=]() { this->refreshButtons(); }); + connect(&this->ui.oauthTokenInput, &QLineEdit::textChanged, [=]() { this->refreshButtons(); }); + + /// Upper button row + + this->ui.buttonUpperRow.addUserButton.setText("Add user"); + this->ui.buttonUpperRow.clearFieldsButton.setText("Clear fields"); + + this->ui.buttonUpperRow.layout.addWidget(&this->ui.buttonUpperRow.addUserButton); + this->ui.buttonUpperRow.layout.addWidget(&this->ui.buttonUpperRow.clearFieldsButton); + + connect(&this->ui.buttonUpperRow.clearFieldsButton, &QPushButton::clicked, [=]() { + this->ui.userIDInput.clear(); + this->ui.usernameInput.clear(); + this->ui.clientIDInput.clear(); + this->ui.oauthTokenInput.clear(); + }); + + connect(&this->ui.buttonUpperRow.addUserButton, &QPushButton::clicked, [=]() { + std::string userID = this->ui.userIDInput.text().toStdString(); + std::string username = this->ui.usernameInput.text().toStdString(); + std::string clientID = this->ui.clientIDInput.text().toStdString(); + std::string oauthToken = this->ui.oauthTokenInput.text().toStdString(); + + qDebug() << "Success! mr"; + pajlada::Settings::Setting::set("/accounts/uid" + userID + "/username", + username); + pajlada::Settings::Setting::set("/accounts/uid" + userID + "/userID", userID); + pajlada::Settings::Setting::set("/accounts/uid" + userID + "/clientID", + clientID); + pajlada::Settings::Setting::set("/accounts/uid" + userID + "/oauthToken", + oauthToken); + }); + + /// Lower button row + this->ui.buttonLowerRow.fillInUserIDButton.setText("Get user ID from username"); + + this->ui.buttonLowerRow.layout.addWidget(&this->ui.buttonLowerRow.fillInUserIDButton); + + connect(&this->ui.buttonLowerRow.fillInUserIDButton, &QPushButton::clicked, [=]() { + util::twitch::getUserID(this->ui.usernameInput.text(), [=](const QString &userID) { + this->ui.userIDInput.setText(userID); // + }); + }); +} + +void AdvancedLoginWidget::refreshButtons() +{ + this->ui.buttonLowerRow.fillInUserIDButton.setEnabled(!this->ui.usernameInput.text().isEmpty()); + + if (this->ui.userIDInput.text().isEmpty() || this->ui.usernameInput.text().isEmpty() || + this->ui.clientIDInput.text().isEmpty() || this->ui.oauthTokenInput.text().isEmpty()) { + this->ui.buttonUpperRow.addUserButton.setEnabled(false); + } else { + this->ui.buttonUpperRow.addUserButton.setEnabled(true); + } +} + +LoginWidget::LoginWidget() +{ + this->setLayout(&this->ui.mainLayout); + + this->ui.mainLayout.addWidget(&this->ui.tabWidget); + + this->ui.tabWidget.addTab(&this->ui.basic, "Basic"); + + this->ui.tabWidget.addTab(&this->ui.advanced, "Advanced"); + + this->ui.buttonBox.setStandardButtons(QDialogButtonBox::Close); + + connect(&this->ui.buttonBox, &QDialogButtonBox::rejected, [this]() { + this->close(); // + }); + + this->ui.mainLayout.addWidget(&this->ui.buttonBox); +} + } // namespace widgets } // namespace chatterino diff --git a/src/widgets/logindialog.hpp b/src/widgets/logindialog.hpp index 0f864bd18..b6a17e627 100644 --- a/src/widgets/logindialog.hpp +++ b/src/widgets/logindialog.hpp @@ -8,14 +8,65 @@ #include #include #include +#include #include #include +#include +#include #include +#include #include namespace chatterino { namespace widgets { +class BasicLoginWidget : public QWidget +{ +public: + BasicLoginWidget(); + + struct { + QVBoxLayout layout; + QHBoxLayout horizontalLayout; + QPushButton loginButton; + QPushButton pasteCodeButton; + } ui; +}; + +class AdvancedLoginWidget : public QWidget +{ +public: + AdvancedLoginWidget(); + + void refreshButtons(); + + struct { + QVBoxLayout layout; + + QLabel instructionsLabel; + + QFormLayout formLayout; + + QLineEdit userIDInput; + QLineEdit usernameInput; + QLineEdit clientIDInput; + QLineEdit oauthTokenInput; + + struct { + QHBoxLayout layout; + + QPushButton addUserButton; + QPushButton clearFieldsButton; + } buttonUpperRow; + + struct { + QHBoxLayout layout; + + QPushButton fillInUserIDButton; + } buttonLowerRow; + } ui; +}; + class LoginWidget : public QDialog { public: @@ -25,11 +76,13 @@ private: struct { QVBoxLayout mainLayout; - QVBoxLayout verticalLayout; - QHBoxLayout horizontalLayout; - QPushButton loginButton; - QPushButton pasteCodeButton; + QTabWidget tabWidget; + QDialogButtonBox buttonBox; + + BasicLoginWidget basic; + + AdvancedLoginWidget advanced; } ui; }; diff --git a/src/widgets/scrollbar.cpp b/src/widgets/scrollbar.cpp index 7463d1ba5..e5fdf6ec7 100644 --- a/src/widgets/scrollbar.cpp +++ b/src/widgets/scrollbar.cpp @@ -82,7 +82,7 @@ void ScrollBar::scrollToBottom() bool ScrollBar::isAtBottom() const { - return ((this->getMaximum() - this->getLargeChange()) - this->getCurrentValue()) <= 0.00001; + return ((this->getMaximum() - this->getLargeChange()) - this->getCurrentValue()) <= 1; } void ScrollBar::setMaximum(qreal value) diff --git a/src/widgets/settingsdialog.cpp b/src/widgets/settingsdialog.cpp index df20d408a..e9dae1c27 100644 --- a/src/widgets/settingsdialog.cpp +++ b/src/widgets/settingsdialog.cpp @@ -108,21 +108,22 @@ void SettingsDialog::addTabs() listWidget->addItem(user.getUserName()); } - if (listWidget->count()) { - int itemIndex = 0; - for (; itemIndex < listWidget->count(); ++itemIndex) { - if (listWidget->item(itemIndex)->text().compare(settings.selectedUser.get(), - Qt::CaseInsensitive)) { - ++itemIndex; + if (listWidget->count() > 0) { + const auto ¤tUser = AccountManager::getInstance().getTwitchUser(); + QString currentUsername = currentUser.getUserName(); + for (int i = 0; i < listWidget->count(); ++i) { + QString itemText = listWidget->item(i)->text(); + if (itemText.compare(currentUsername, Qt::CaseInsensitive) == 0) { + listWidget->setCurrentRow(i); break; } } - listWidget->setCurrentRow(itemIndex); } QObject::connect(listWidget, &QListWidget::clicked, this, [&, listWidget] { if (!listWidget->selectedItems().isEmpty()) { - settings.selectedUser.set(listWidget->currentItem()->text()); + AccountManager::getInstance().setCurrentTwitchUser( + listWidget->currentItem()->text()); } });