From: tobtoht Date: Wed, 12 Nov 2025 12:35:11 +0000 (+0100) Subject: Revert "plugins: remove Crowdfunding and Bounties" X-Git-Url: https://git.nutra.tk/v1?a=commitdiff_plain;h=4ad4709fb5deb636de4ccd3be28dae73ac028ac3;p=gamesguru%2Ffeather.git Revert "plugins: remove Crowdfunding and Bounties" partial revert, keep CCS for now --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f7f5bdb..dd2087ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ option(TOR_INSTALLED "Is Tor installed on the filesystem?" OFF) # Plugins option(WITH_PLUGIN_HOME "Include Home tab plugin" ON) option(WITH_PLUGIN_TICKERS "Include Tickers Home plugin" ON) +option(WITH_PLUGIN_CROWDFUNDING "Include Crowdfunding Home plugin" ON) option(WITH_PLUGIN_REVUO "Include Revuo Home plugin" ON) option(WITH_PLUGIN_CALC "Include Calc tab plugin" ON) diff --git a/src/plugins/crowdfunding/CCSEntry.h b/src/plugins/crowdfunding/CCSEntry.h new file mode 100644 index 00000000..49c8f63b --- /dev/null +++ b/src/plugins/crowdfunding/CCSEntry.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: The Monero Project + +#ifndef FEATHER_CCSENTRY_H +#define FEATHER_CCSENTRY_H + +#include + +struct CCSEntry { + CCSEntry()= default;; + + QString title; + QString date; + QString address; + QString author; + QString state; + QString url; + QString organizer; + QString currency; + double target_amount = 0; + double raised_amount = 0; + double percentage_funded = 0; + int contributions = 0; +}; + +#endif //FEATHER_CCSENTRY_H diff --git a/src/plugins/crowdfunding/CCSModel.cpp b/src/plugins/crowdfunding/CCSModel.cpp new file mode 100644 index 00000000..baaaf2ea --- /dev/null +++ b/src/plugins/crowdfunding/CCSModel.cpp @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: The Monero Project + +#include "CCSModel.h" + +CCSModel::CCSModel(QObject *parent) + : QAbstractTableModel(parent) +{ + +} + +void CCSModel::clear() { + beginResetModel(); + + m_entries.clear(); + + endResetModel(); +} + +void CCSModel::updateEntries(const QList>& entries) { + beginResetModel(); + + m_entries.clear(); + for (const auto& entry : entries) { + m_entries.push_back(entry); + } + + endResetModel(); +} + +int CCSModel::rowCount(const QModelIndex &parent) const{ + if (parent.isValid()) { + return 0; + } + return m_entries.count(); +} + +int CCSModel::columnCount(const QModelIndex &parent) const +{ + if (parent.isValid()) { + return 0; + } + return ModelColumn::COUNT; +} + +QVariant CCSModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() < 0 || index.row() >= m_entries.count()) + return QVariant(); + + QSharedPointer entry = m_entries.at(index.row()); + + if(role == Qt::DisplayRole) { + switch(index.column()) { + case Title: + return entry->title; + case Organizer: + return entry->organizer; + case Author: + return QString("%1 ").arg(entry->author); + case Progress: + return QString("%1/%2 %3").arg(QString::number(entry->raised_amount), QString::number(entry->target_amount), entry->currency); + default: + return QVariant(); + } + } + return QVariant(); +} + +QVariant CCSModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) { + return QVariant(); + } + if (orientation == Qt::Horizontal) + { + switch(section) { + case Title: + return QString("Proposal"); + case Organizer: + return QString("Organizer "); + case Author: + return QString("Author"); + case Progress: + return QString("Progress"); + default: + return QVariant(); + } + } + return QVariant(); +} + +QSharedPointer CCSModel::entry(int row) { + if (row < 0 || row >= m_entries.size()) { + qCritical("%s: no reddit post for index %d", __FUNCTION__, row); + return QSharedPointer(); + } + + return m_entries.at(row); +} \ No newline at end of file diff --git a/src/plugins/crowdfunding/CCSModel.h b/src/plugins/crowdfunding/CCSModel.h new file mode 100644 index 00000000..e310878f --- /dev/null +++ b/src/plugins/crowdfunding/CCSModel.h @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: The Monero Project + +#ifndef FEATHER_CCSMODEL_H +#define FEATHER_CCSMODEL_H + +#include +#include + +#include "CCSEntry.h" + +class CCSModel : public QAbstractTableModel +{ +Q_OBJECT + +public: + enum ModelColumn + { + Title = 0, + Organizer, + Author, + Progress, + COUNT + }; + + explicit CCSModel(QObject *parent); + + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + + void clear(); + void updateEntries(const QList>& entries); + + QSharedPointer entry(int row); + +private: + QList> m_entries; +}; + + +#endif //FEATHER_CCSMODEL_H diff --git a/src/plugins/crowdfunding/CCSProgressDelegate.cpp b/src/plugins/crowdfunding/CCSProgressDelegate.cpp new file mode 100644 index 00000000..c915d24d --- /dev/null +++ b/src/plugins/crowdfunding/CCSProgressDelegate.cpp @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: The Monero Project + +#include "CCSProgressDelegate.h" + +#include + +CCSProgressDelegate::CCSProgressDelegate(CCSModel *model, QWidget *parent) + : QStyledItemDelegate(parent) + , m_model(model) +{ +} + +void CCSProgressDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const { + + if (index.column() != CCSModel::Progress) { + QStyledItemDelegate::paint(painter, option, index); + return; + } + + QStyleOptionProgressBar progressBarOption; + progressBarOption.state = QStyle::State_Enabled; + progressBarOption.direction = QApplication::layoutDirection(); + progressBarOption.rect = option.rect; + progressBarOption.fontMetrics = QApplication::fontMetrics(); + progressBarOption.minimum = 0; + progressBarOption.maximum = 100; + progressBarOption.textAlignment = Qt::AlignCenter; + progressBarOption.textVisible = true; + + QSharedPointer entry = m_model->entry(index.row()); + auto target = QString("%1/%2 XMR").arg(entry->raised_amount).arg(entry->target_amount); + auto progress = (int)entry->percentage_funded; + progressBarOption.progress = progress < 0 ? 0 : progress; + progressBarOption.text = target; + + QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter); // Draw the progress bar onto the view. +} \ No newline at end of file diff --git a/src/plugins/crowdfunding/CCSProgressDelegate.h b/src/plugins/crowdfunding/CCSProgressDelegate.h new file mode 100644 index 00000000..4c0f5cb9 --- /dev/null +++ b/src/plugins/crowdfunding/CCSProgressDelegate.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: The Monero Project + +#ifndef FEATHER_CSSPROGRESSDELEGATE_H +#define FEATHER_CSSPROGRESSDELEGATE_H + +#include + +#include "CCSModel.h" + +class CCSProgressDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + explicit CCSProgressDelegate(CCSModel *model, QWidget *parent = nullptr); + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + +private: + CCSModel *m_model; +}; + + +#endif //FEATHER_CSSPROGRESSDELEGATE_H diff --git a/src/plugins/crowdfunding/CCSWidget.cpp b/src/plugins/crowdfunding/CCSWidget.cpp new file mode 100644 index 00000000..92d8e944 --- /dev/null +++ b/src/plugins/crowdfunding/CCSWidget.cpp @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: The Monero Project + +#include "CCSWidget.h" +#include "ui_CCSWidget.h" + +#include +#include + +#include "CCSProgressDelegate.h" +#include "utils/Utils.h" +#include "utils/WebsocketNotifier.h" + +CCSWidget::CCSWidget(QWidget *parent) + : QWidget(parent) + , ui(new Ui::CSSWidget) + , m_model(new CCSModel(this)) + , m_contextMenu(new QMenu(this)) +{ + ui->setupUi(this); + ui->treeView->setModel(m_model); + + m_contextMenu->addAction("View proposal", this, &CCSWidget::linkClicked); + m_contextMenu->addAction("Donate", this, &CCSWidget::donateClicked); + + connect(ui->treeView, &QHeaderView::customContextMenuRequested, this, &CCSWidget::showContextMenu); + connect(ui->treeView, &QTreeView::doubleClicked, this, &CCSWidget::linkClicked); + + ui->treeView->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + ui->treeView->header()->setSectionResizeMode(CCSModel::Title, QHeaderView::Stretch); + + connect(websocketNotifier(), &WebsocketNotifier::dataReceived, this, [this](const QString& type, const QJsonValue& json) { + if (type == "ccs") { + QJsonArray ccs_data = json.toArray(); + QList> l; + + for (const auto& entry: ccs_data) { + auto obj = entry.toObject(); + auto c = QSharedPointer(new CCSEntry()); + + if (obj.value("state").toString() != "FUNDING-REQUIRED") + continue; + + c->state = obj.value("state").toString(); + c->address = obj.value("address").toString(); + c->author = obj.value("author").toString(); + c->date = obj.value("date").toString(); + c->title = obj.value("title").toString(); + c->target_amount = obj.value("target_amount").toDouble(); + c->raised_amount = obj.value("raised_amount").toDouble(); + c->percentage_funded = obj.value("percentage_funded").toDouble(); + c->contributions = obj.value("contributions").toInt(); + c->organizer = obj.value("organizer").toString(); + c->currency = obj.value("currency").toString(); + + QString urlpath = obj.value("urlpath").toString(); + if (c->organizer == "CCS") { + c->url = QString("https://ccs.getmonero.org/%1").arg(urlpath); + } + else if (c->organizer == "MAGIC") { + c->url = QString("https://donate.magicgrants.org/%1").arg(urlpath); + } + else { + continue; + } + + l.append(c); + } + + m_model->updateEntries(l); + } + }); +} + +void CCSWidget::linkClicked() { + QModelIndex index = ui->treeView->currentIndex(); + auto entry = m_model->entry(index.row()); + + if (entry) { + Utils::externalLinkWarning(this, entry->url); + } +} + +void CCSWidget::donateClicked() { + QModelIndex index = ui->treeView->currentIndex(); + auto entry = m_model->entry(index.row()); + + if (entry) { + emit fillSendTab(entry->address, QString("Donation to %1: %2").arg(entry->organizer, entry->title)); + } +} + +void CCSWidget::showContextMenu(const QPoint &pos) { + QModelIndex index = ui->treeView->indexAt(pos); + if (!index.isValid()) { + return; + } + + m_contextMenu->exec(ui->treeView->viewport()->mapToGlobal(pos)); +} + +CCSWidget::~CCSWidget() = default; \ No newline at end of file diff --git a/src/plugins/crowdfunding/CCSWidget.h b/src/plugins/crowdfunding/CCSWidget.h new file mode 100644 index 00000000..5a763da8 --- /dev/null +++ b/src/plugins/crowdfunding/CCSWidget.h @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: The Monero Project + +#ifndef FEATHER_CSSWIDGET_H +#define FEATHER_CSSWIDGET_H + +#include +#include +#include +#include +#include + +#include "CCSModel.h" +#include "CCSEntry.h" + +namespace Ui { + class CSSWidget; +} + +class CCSWidget : public QWidget +{ +Q_OBJECT + +public: + explicit CCSWidget(QWidget *parent = nullptr); + ~CCSWidget(); + +signals: + void fillSendTab(const QString &address, const QString &description); + +public slots: + void donateClicked(); + +private slots: + void linkClicked(); + +private: + void showContextMenu(const QPoint &pos); + + QScopedPointer ui; + CCSModel *m_model; + QMenu *m_contextMenu; +}; + +#endif // FEATHER_CSSWIDGET_H diff --git a/src/plugins/crowdfunding/CCSWidget.ui b/src/plugins/crowdfunding/CCSWidget.ui new file mode 100644 index 00000000..51163e36 --- /dev/null +++ b/src/plugins/crowdfunding/CCSWidget.ui @@ -0,0 +1,56 @@ + + + CSSWidget + + + + 0 + 0 + 893 + 396 + + + + Form + + + + 4 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::CustomContextMenu + + + true + + + false + + + false + + + + + + + + + donateClicked() + linkClicked() + + diff --git a/src/plugins/crowdfunding/CrowdfundingPlugin.cpp b/src/plugins/crowdfunding/CrowdfundingPlugin.cpp new file mode 100644 index 00000000..8cebc63d --- /dev/null +++ b/src/plugins/crowdfunding/CrowdfundingPlugin.cpp @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: The Monero Project + +#include "CrowdfundingPlugin.h" + +#include "plugins/PluginRegistry.h" +#include "CCSWidget.h" + +CrowdfundingPlugin::CrowdfundingPlugin() +{ +} + +void CrowdfundingPlugin::initialize(Wallet *wallet, QObject *parent) { + this->setParent(parent); + m_tab = new CCSWidget(nullptr); + connect(m_tab, &CCSWidget::fillSendTab, this, &Plugin::fillSendTab); +} + +QString CrowdfundingPlugin::id() { + return "crowdfunding"; +} + +int CrowdfundingPlugin::idx() const { + return 10; +} + +QString CrowdfundingPlugin::parent() { + return "home"; +} + +QString CrowdfundingPlugin::displayName() { + return "Crowdfunding"; +} + +QString CrowdfundingPlugin::description() { + return {}; +} + +QString CrowdfundingPlugin::icon() { + return {}; +} + +QStringList CrowdfundingPlugin::socketData() { + return {"ccs"}; +} + +Plugin::PluginType CrowdfundingPlugin::type() { + return Plugin::PluginType::TAB; +} + +QWidget* CrowdfundingPlugin::tab() { + return m_tab; +} + +const bool CrowdfundingPlugin::registered = [] { + PluginRegistry::registerPlugin(CrowdfundingPlugin::create()); + PluginRegistry::getInstance().registerPluginCreator(&CrowdfundingPlugin::create); + return true; +}(); diff --git a/src/plugins/crowdfunding/CrowdfundingPlugin.h b/src/plugins/crowdfunding/CrowdfundingPlugin.h new file mode 100644 index 00000000..9bd4736c --- /dev/null +++ b/src/plugins/crowdfunding/CrowdfundingPlugin.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: The Monero Project + +#ifndef CROWDFUNDINGPLUGIN_H +#define CROWDFUNDINGPLUGIN_H + +#include "plugins/Plugin.h" +#include "CCSWidget.h" + +class CrowdfundingPlugin : public Plugin { + Q_OBJECT + +public: + explicit CrowdfundingPlugin(); + + QString id() override; + int idx() const override; + QString parent() override; + QString displayName() override; + QString description() override; + QString icon() override; + QStringList socketData() override; + PluginType type() override; + QWidget* tab() override; + + void initialize(Wallet *wallet, QObject *parent) override; + + static CrowdfundingPlugin* create() { return new CrowdfundingPlugin(); } + +private: + CCSWidget* m_tab = nullptr; + static const bool registered; +}; + +#endif //CROWDFUNDINGPLUGIN_H diff --git a/src/utils/config.cpp b/src/utils/config.cpp index dfae745b..b5baa886 100644 --- a/src/utils/config.cpp +++ b/src/utils/config.cpp @@ -27,7 +27,7 @@ static const QHash configStrings = { {Config::warnOnKiImport,{QS("warnOnKiImport"), true}}, {Config::logLevel,{QS("logLevel"), 0}}, - {Config::homeWidget,{QS("homeWidget"), "revuo"}}, + {Config::homeWidget,{QS("homeWidget"), "ccs"}}, {Config::donateBeg,{QS("donateBeg"), 1}}, {Config::showHistorySyncNotice, {QS("showHistorySyncNotice"), true}}, @@ -123,7 +123,7 @@ static const QHash configStrings = { {Config::useLocalTor, {QS("useLocalTor"), false}}, {Config::initSyncThreshold, {QS("initSyncThreshold"), 360}}, - {Config::enabledPlugins, {QS("enabledPlugins"), QStringList{"tickers", "revuo", "calc"}}}, + {Config::enabledPlugins, {QS("enabledPlugins"), QStringList{"tickers", "crowdfunding", "revuo", "calc"}}}, {Config::restartRequired, {QS("restartRequired"), false}}, {Config::tickers, {QS("tickers"), QStringList{"XMR", "BTC", "XMR/BTC"}}},