From e359b7fa005970b5898ef4ce7237d19ca46e64f0 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Tue, 30 Apr 2024 19:01:42 +0200 Subject: [PATCH] Add Twitch connection tab --- data/locale/en-US.ini | 12 ++ plugins/twitch/CMakeLists.txt | 4 +- plugins/twitch/token.cpp | 5 + plugins/twitch/token.hpp | 2 + plugins/twitch/twitch-tab.cpp | 221 ++++++++++++++++++++++++++++++++++ plugins/twitch/twitch-tab.hpp | 20 +++ 6 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 plugins/twitch/twitch-tab.cpp create mode 100644 plugins/twitch/twitch-tab.hpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 28779ac8..7bd2551f 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -116,6 +116,18 @@ AdvSceneSwitcher.websocketConnectionTab.protocol.no="No" AdvSceneSwitcher.websocketConnectionTab.removeSingleConnectionPopup.text="Are you sure you want to remove \"%1\"?" AdvSceneSwitcher.websocketConnectionTab.removeMultipleConnectionsPopup.text="Are you sure you want to remove %1 connections?" +; Twitch Connections Tab +AdvSceneSwitcher.twitchConnectionTab.title="Twitch Connections" +AdvSceneSwitcher.twitchConnectionTab.help="Twitch connections can be used to use Twitch events as triggers to execute actions or perform actions on the linked Twitch account.\n\nClick on the highlighted plus symbol to add a new connection." +AdvSceneSwitcher.twitchConnectionTab.twitchConnectionAddButton.tooltip.tooltip="Add new Twitch connection" +AdvSceneSwitcher.twitchConnectionTab.twitchConnectionRemoveButton.tooltip="Remove selected Twitch connections" +AdvSceneSwitcher.twitchConnectionTab.name.header="Name" +AdvSceneSwitcher.twitchConnectionTab.isValid.header="Is valid?" +AdvSceneSwitcher.twitchConnectionTab.permissionCount.header="Number of permissions" +AdvSceneSwitcher.twitchConnectionTab.yes="Yes" +AdvSceneSwitcher.twitchConnectionTab.no="No" +AdvSceneSwitcher.twitchConnectionTab.removeSingleConnectionPopup.text="Are you sure you want to remove \"%1\"?" +AdvSceneSwitcher.twitchConnectionTab.removeMultipleConnectionsPopup.text="Are you sure you want to remove %1 Twitch connections?" ; Macro Tab AdvSceneSwitcher.macroTab.title="Macro" diff --git a/plugins/twitch/CMakeLists.txt b/plugins/twitch/CMakeLists.txt index b64a919b..fdd42ce0 100644 --- a/plugins/twitch/CMakeLists.txt +++ b/plugins/twitch/CMakeLists.txt @@ -55,7 +55,9 @@ target_sources( token.cpp token.hpp twitch-helpers.cpp - twitch-helpers.hpp) + twitch-helpers.hpp + twitch-tab.cpp + twitch-tab.hpp) setup_advss_plugin(${PROJECT_NAME}) set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "") diff --git a/plugins/twitch/token.cpp b/plugins/twitch/token.cpp index f11c32ba..a9b726ed 100644 --- a/plugins/twitch/token.cpp +++ b/plugins/twitch/token.cpp @@ -351,6 +351,11 @@ bool TokenIsValid(const std::weak_ptr &token_) return token->IsValid(); } +std::deque> &GetTwitchTokens() +{ + return twitchTokens; +} + static bool ConnectionNameAvailable(const QString &name) { return !GetTwitchTokenByName(name); diff --git a/plugins/twitch/token.hpp b/plugins/twitch/token.hpp index 5def02c5..99f33a23 100644 --- a/plugins/twitch/token.hpp +++ b/plugins/twitch/token.hpp @@ -50,6 +50,7 @@ public: std::string GetUserID() const { return _userID; } std::shared_ptr GetEventSub(); bool IsValid(bool forceUpdate = false) const; + size_t PermissionCount() const { return _tokenOptions.size(); } private: std::string _token; @@ -152,6 +153,7 @@ std::weak_ptr GetWeakTwitchTokenByName(const std::string &name); std::weak_ptr GetWeakTwitchTokenByQString(const QString &name); std::string GetWeakTwitchTokenName(std::weak_ptr); bool TokenIsValid(const std::weak_ptr &token); +std::deque> &GetTwitchTokens(); } // namespace advss diff --git a/plugins/twitch/twitch-tab.cpp b/plugins/twitch/twitch-tab.cpp new file mode 100644 index 00000000..a1020829 --- /dev/null +++ b/plugins/twitch/twitch-tab.cpp @@ -0,0 +1,221 @@ +#include "twitch-tab.hpp" +#include "obs-module-helper.hpp" +#include "sync-helpers.hpp" +#include "tab-helpers.hpp" +#include "token.hpp" +#include "ui-helpers.hpp" + +#include + +namespace advss { + +static bool registerTab(); +static void setupTab(QTabWidget *); +static bool registerTabDone = registerTab(); + +static TwitchConnectionsTable *tabWidget = nullptr; + +static bool registerTab() +{ + AddSetupTabCallback("twitchConnectionTab", + TwitchConnectionsTable::Create, setupTab); + return true; +} + +static void setTabVisible(QTabWidget *tabWidget, bool visible) +{ + SetTabVisibleByName( + tabWidget, visible, + obs_module_text("AdvSceneSwitcher.twitchConnectionTab.title")); +} + +TwitchConnectionsTable *TwitchConnectionsTable::Create() +{ + tabWidget = new TwitchConnectionsTable(); + return tabWidget; +} + +void TwitchConnectionsTable::Add() +{ + auto newToken = std::make_shared(); + auto accepted = + TwitchTokenSettingsDialog::AskForSettings(this, *newToken); + if (!accepted) { + return; + } + + { + auto lock = LockContext(); + auto &tokens = GetTwitchTokens(); + tokens.emplace_back(newToken); + } + + TwitchConnectionSignalManager::Instance()->Add( + QString::fromStdString(newToken->Name())); +} + +void TwitchConnectionsTable::Remove() +{ + auto selectedRows = + tabWidget->Table()->selectionModel()->selectedRows(); + if (selectedRows.empty()) { + return; + } + + QStringList tokenNames; + for (auto row : selectedRows) { + auto cell = tabWidget->Table()->item(row.row(), 0); + if (!cell) { + continue; + } + + tokenNames << cell->text(); + } + + int tokenNameCount = tokenNames.size(); + if (tokenNameCount == 1) { + QString deleteWarning = obs_module_text( + "AdvSceneSwitcher.twitchConnectionTab.removeSingleConnectionPopup.text"); + if (!DisplayMessage(deleteWarning.arg(tokenNames.at(0)), + true)) { + return; + } + } else { + QString deleteWarning = obs_module_text( + "AdvSceneSwitcher.twitchConnectionTab.removeMultipleConnectionsPopup.text"); + if (!DisplayMessage(deleteWarning.arg(tokenNameCount), true)) { + return; + } + } + + { + auto lock = LockContext(); + RemoveItemsByName(GetTwitchTokens(), tokenNames); + } + + for (const auto &name : tokenNames) { + TwitchConnectionSignalManager::Instance()->Remove(name); + } +} + +static QStringList getCellLabels(TwitchToken *token, bool addName = true) +{ + assert(token); + + auto result = QStringList(); + if (addName) { + result << QString::fromStdString(token->Name()); + } + result << QString::fromStdString(obs_module_text( + token->IsValid() + ? "AdvSceneSwitcher.twitchConnectionTab.yes" + : "AdvSceneSwitcher.twitchConnectionTab.no")) + << QString::number(token->PermissionCount()); + return result; +} + +static void openSettingsDialog() +{ + auto selectedRows = + tabWidget->Table()->selectionModel()->selectedRows(); + if (selectedRows.empty()) { + return; + } + + auto cell = tabWidget->Table()->item(selectedRows.last().row(), 0); + if (!cell) { + return; + } + + auto weakToken = GetWeakTwitchTokenByQString(cell->text()); + auto token = weakToken.lock(); + if (!token) { + return; + } + + TwitchTokenSettingsDialog::AskForSettings(tabWidget->Table(), + *token.get()); +} + +static const QStringList headers = + QStringList() + << obs_module_text("AdvSceneSwitcher.twitchConnectionTab.name.header") + << obs_module_text( + "AdvSceneSwitcher.twitchConnectionTab.isValid.header") + << obs_module_text( + "AdvSceneSwitcher.twitchConnectionTab.permissionCount.header"); + +TwitchConnectionsTable::TwitchConnectionsTable(QTabWidget *parent) + : ResourceTable( + parent, + obs_module_text("AdvSceneSwitcher.twitchConnectionTab.help"), + obs_module_text( + "AdvSceneSwitcher.twitchConnectionTab.twitchConnectionAddButton.tooltip"), + obs_module_text( + "AdvSceneSwitcher.twitchConnectionTab.twitchConnectionRemoveButton.tooltip"), + headers, openSettingsDialog) +{ + for (const auto &token : GetTwitchTokens()) { + auto t = std::static_pointer_cast(token); + AddItemTableRow(Table(), getCellLabels(t.get())); + } + + SetHelpVisible(GetTwitchTokens().empty()); +} + +static void updateConnectionStatus(QTableWidget *table) +{ + for (int row = 0; row < table->rowCount(); row++) { + auto item = table->item(row, 0); + if (!item) { + continue; + } + + auto weakToken = GetWeakTwitchTokenByQString(item->text()); + auto token = weakToken.lock(); + if (!token) { + continue; + } + + UpdateItemTableRow(table, row, + getCellLabels(token.get(), false)); + } +} + +static void setupTab(QTabWidget *tab) +{ + if (GetTwitchTokens().empty()) { + setTabVisible(tab, false); + } + + QWidget::connect( + TwitchConnectionSignalManager::Instance(), + &TwitchConnectionSignalManager::Add, + [tab](const QString &name) { + AddItemTableRow( + tabWidget->Table(), + getCellLabels(GetWeakTwitchTokenByQString(name) + .lock() + .get())); + tabWidget->SetHelpVisible(false); + tabWidget->HighlightAddButton(false); + setTabVisible(tab, true); + }); + QWidget::connect(TwitchConnectionSignalManager::Instance(), + &TwitchConnectionSignalManager::Remove, + [](const QString &name) { + RemoveItemTableRow(tabWidget->Table(), name); + if (tabWidget->Table()->rowCount() == 0) { + tabWidget->SetHelpVisible(true); + tabWidget->HighlightAddButton(true); + } + }); + + auto timer = new QTimer(tabWidget); + timer->setInterval(1000); + QWidget::connect(timer, &QTimer::timeout, + []() { updateConnectionStatus(tabWidget->Table()); }); + timer->start(); +} + +} // namespace advss diff --git a/plugins/twitch/twitch-tab.hpp b/plugins/twitch/twitch-tab.hpp new file mode 100644 index 00000000..706b109f --- /dev/null +++ b/plugins/twitch/twitch-tab.hpp @@ -0,0 +1,20 @@ +#pragma once +#include "resource-table.hpp" + +namespace advss { + +class TwitchConnectionsTable final : public ResourceTable { + Q_OBJECT + +public: + static TwitchConnectionsTable *Create(); + +private slots: + void Add(); + void Remove(); + +private: + TwitchConnectionsTable(QTabWidget *parent = nullptr); +}; + +} // namespace advss