]> Nutra Git (v1) - gamesguru/feather.git/commitdiff
Home: bounties.monero.social
authortobtoht <thotbot@protonmail.com>
Thu, 23 Jun 2022 17:48:30 +0000 (19:48 +0200)
committertobtoht <thotbot@protonmail.com>
Thu, 23 Jun 2022 17:48:30 +0000 (19:48 +0200)
13 files changed:
src/MainWindow.cpp
src/MainWindow.h
src/MainWindow.ui
src/model/BountiesModel.cpp [new file with mode: 0644]
src/model/BountiesModel.h [new file with mode: 0644]
src/utils/WebsocketNotifier.cpp
src/utils/WebsocketNotifier.h
src/utils/config.cpp
src/utils/config.h
src/widgets/BountiesWidget.cpp [new file with mode: 0644]
src/widgets/BountiesWidget.h [new file with mode: 0644]
src/widgets/BountiesWidget.ui [new file with mode: 0644]
src/widgets/Bounty.h [new file with mode: 0644]

index a477f3887a1cf4f2d34793ec5e0baaf781753932..302b0bef2df933ca08994b20cc95635b19983fc4 100644 (file)
@@ -62,6 +62,7 @@ MainWindow::MainWindow(WindowManager *windowManager, Wallet *wallet, QWidget *pa
 
     // Websocket notifier
     connect(websocketNotifier(), &WebsocketNotifier::CCSReceived, ui->ccsWidget->model(), &CCSModel::updateEntries);
+    connect(websocketNotifier(), &WebsocketNotifier::BountyReceived, ui->bountiesWidget->model(), &BountiesModel::updateBounties);
     connect(websocketNotifier(), &WebsocketNotifier::RedditReceived, ui->redditWidget->model(), &RedditModel::updatePosts);
     connect(websocketNotifier(), &WebsocketNotifier::RevuoReceived, ui->revuoWidget, &RevuoWidget::updateItems);
     connect(websocketNotifier(), &WebsocketNotifier::UpdatesReceived, this, &MainWindow::onUpdatesAvailable);
@@ -373,6 +374,7 @@ void MainWindow::initHome() {
     ui->fiatTickerLayout->addWidget(m_balanceTickerWidget);
 
     connect(ui->ccsWidget, &CCSWidget::selected, this, &MainWindow::showSendScreen);
+    connect(ui->bountiesWidget, &BountiesWidget::donate, this, &MainWindow::fillSendTab);
     connect(ui->redditWidget, &RedditWidget::setStatusText, this, &MainWindow::setStatusText);
     connect(ui->revuoWidget, &RevuoWidget::donate, [this](const QString &address, const QString &description){
         m_sendWidget->fill(address, description);
@@ -935,6 +937,11 @@ void MainWindow::showSendTab() {
     ui->tabWidget->setCurrentIndex(Tabs::SEND);
 }
 
+void MainWindow::fillSendTab(const QString &address, const QString &description) {
+    m_sendWidget->fill(address, description);
+    ui->tabWidget->setCurrentIndex(Tabs::SEND);
+}
+
 void MainWindow::showCalcWindow() {
     m_windowCalc->show();
 }
index cdfe795bcd5024a913d04db022611a8fa929fdf5..b8fdfa2f682b5f1b3078b38d3607720273360dee 100644 (file)
@@ -215,6 +215,7 @@ private:
     void updateWidgetIcons();
     bool verifyPassword();
     void patchStylesheetMac();
+    void fillSendTab(const QString &address, const QString &description);
     void userActivity();
     void checkUserActivity();
 
index 035666ec394961c75dd76fb9790b0197108a470d..513a251f7551f22bc8cef008b8d8732a52af977c 100644 (file)
             </item>
            </layout>
           </widget>
+          <widget class="QWidget" name="tab_4">
+           <attribute name="title">
+            <string>Bounties</string>
+           </attribute>
+           <layout class="QVBoxLayout" name="verticalLayout_10">
+            <property name="leftMargin">
+             <number>0</number>
+            </property>
+            <property name="topMargin">
+             <number>0</number>
+            </property>
+            <property name="rightMargin">
+             <number>0</number>
+            </property>
+            <property name="bottomMargin">
+             <number>0</number>
+            </property>
+            <item>
+             <widget class="BountiesWidget" name="bountiesWidget" native="true"/>
+            </item>
+           </layout>
+          </widget>
           <widget class="QWidget" name="tab_2">
            <attribute name="title">
             <string>/r/Monero</string>
    <header>widgets/RevuoWidget.h</header>
    <container>1</container>
   </customwidget>
+  <customwidget>
+   <class>BountiesWidget</class>
+   <extends>QWidget</extends>
+   <header>widgets/BountiesWidget.h</header>
+   <container>1</container>
+  </customwidget>
  </customwidgets>
  <resources>
   <include location="assets.qrc"/>
diff --git a/src/model/BountiesModel.cpp b/src/model/BountiesModel.cpp
new file mode 100644 (file)
index 0000000..8f999bf
--- /dev/null
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: BSD-3-Clause
+// Copyright (c) 2020-2021, The Monero Project.
+
+#include "BountiesModel.h"
+
+BountiesModel::BountiesModel(QObject *parent)
+        : QAbstractTableModel(parent)
+{
+
+}
+
+void BountiesModel::clear() {
+    beginResetModel();
+
+    m_bounties.clear();
+
+    endResetModel();
+}
+
+void BountiesModel::updateBounties(const QList<QSharedPointer<BountyEntry>> &posts) {
+    beginResetModel();
+
+    m_bounties.clear();
+    for (const auto& post : posts) {
+        m_bounties.push_back(post);
+    }
+
+    endResetModel();
+}
+
+int BountiesModel::rowCount(const QModelIndex &parent) const{
+    if (parent.isValid()) {
+        return 0;
+    }
+    return m_bounties.count();
+}
+
+int BountiesModel::columnCount(const QModelIndex &parent) const
+{
+    if (parent.isValid()) {
+        return 0;
+    }
+    return ModelColumn::COUNT;
+}
+
+QVariant BountiesModel::data(const QModelIndex &index, int role) const
+{
+    if (!index.isValid() || index.row() < 0 || index.row() >= m_bounties.count())
+        return {};
+
+    QSharedPointer<BountyEntry> post = m_bounties.at(index.row());
+
+    if(role == Qt::DisplayRole) {
+        switch(index.column()) {
+            case Votes:
+                return QString::number(post->votes);
+            case Title:
+                return post->title;
+            case Status:
+                return post->status;
+            case Bounty: {
+                if (post->bountyAmount > 0) {
+                    return QString("%1 XMR").arg(QString::number(post->bountyAmount, 'f', 5));
+                }
+                return "None";
+            }
+            default:
+                return {};
+        }
+    }
+    else if (role == Qt::TextAlignmentRole) {
+        switch(index.column()) {
+            case Votes:
+            case Status:
+            case Bounty:
+                return Qt::AlignRight;
+            default:
+                return {};
+        }
+    }
+    return {};
+}
+
+QVariant BountiesModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+    if (role != Qt::DisplayRole) {
+        return QVariant();
+    }
+    if (orientation == Qt::Horizontal)
+    {
+        switch(section) {
+            case Votes:
+                return QString("🡅");
+            case Title:
+                return QString("Title");
+            case Status:
+                return QString("Status");
+            case Bounty:
+                return QString("Bounty");
+            default:
+                return QVariant();
+        }
+    }
+    return QVariant();
+}
+
+QSharedPointer<BountyEntry> BountiesModel::post(int row) {
+    if (row < 0 || row >= m_bounties.size()) {
+        qCritical("%s: no reddit post for index %d", __FUNCTION__, row);
+        return QSharedPointer<BountyEntry>();
+    }
+
+    return m_bounties.at(row);
+}
\ No newline at end of file
diff --git a/src/model/BountiesModel.h b/src/model/BountiesModel.h
new file mode 100644 (file)
index 0000000..21aecb7
--- /dev/null
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: BSD-3-Clause
+// Copyright (c) 2020-2021, The Monero Project.
+
+#ifndef FEATHER_BOUNTIESMODEL_H
+#define FEATHER_BOUNTIESMODEL_H
+
+#include <QAbstractTableModel>
+#include <QSharedPointer>
+
+#include "widgets/Bounty.h"
+
+class BountiesModel : public QAbstractTableModel
+{
+    Q_OBJECT
+
+public:
+    enum ModelColumn
+    {
+        Title = 0,
+        Votes,
+        Status,
+        Bounty,
+        COUNT
+    };
+
+    explicit BountiesModel(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 updateBounties(const QList<QSharedPointer<BountyEntry>>& posts);
+
+    QSharedPointer<BountyEntry> post(int row);
+
+private:
+    QList<QSharedPointer<BountyEntry>> m_bounties;
+};
+
+
+#endif //FEATHER_BOUNTIESMODEL_H
index 5ba833ed6ad99ecdcfd3f373901751fad272c7d4..6414a70af3ef24eeb999dddbec7cf44c85aeedd4 100644 (file)
@@ -55,6 +55,11 @@ void WebsocketNotifier::onWSMessage(const QJsonObject &msg) {
         this->onWSCCS(ccs_data);
     }
 
+    else if(cmd == "bounties") {
+        auto data = msg.value("data").toArray();
+        this->onWSBounties(data);
+    }
+
     else if(cmd == "txFiatHistory") {
         auto txFiatHistory_data = msg.value("data").toObject();
         emit TxFiatHistoryReceived(txFiatHistory_data);
@@ -151,10 +156,6 @@ void WebsocketNotifier::onWSReddit(const QJsonArray& reddit_data) {
 void WebsocketNotifier::onWSCCS(const QJsonArray &ccs_data) {
     QList<QSharedPointer<CCSEntry>> l;
 
-    QStringList fonts = {"state", "address", "author", "date",
-                         "title", "target_amount", "raised_amount",
-                         "percentage_funded", "contributions"};
-
     for (auto &&entry: ccs_data) {
         auto obj = entry.toObject();
         auto c = QSharedPointer<CCSEntry>(new CCSEntry());
@@ -201,6 +202,24 @@ void WebsocketNotifier::onWSRevuo(const QJsonArray &revuo_data) {
     emit RevuoReceived(l);
 }
 
+void WebsocketNotifier::onWSBounties(const QJsonArray &bounties_data) {
+    QList<QSharedPointer<BountyEntry>> l;
+
+    for (const auto& entry : bounties_data) {
+        QJsonObject obj = entry.toObject();
+        auto bounty = new BountyEntry(obj.value("votes").toInt(),
+                                      obj.value("title").toString(),
+                                      obj.value("amount").toDouble(),
+                                      obj.value("link").toString(),
+                                      obj.value("address").toString(),
+                                      obj.value("status").toString());
+        QSharedPointer<BountyEntry> b = QSharedPointer<BountyEntry>(bounty);
+        l.append(b);
+    }
+
+    emit BountyReceived(l);
+}
+
 void WebsocketNotifier::onWSUpdates(const QJsonObject &updates) {
     emit UpdatesReceived(updates);
 }
index a0fff9994e639b876c427c1d99601aaa26a7c1ea..deeef78a438554338128e02389cf54a639e4eb36 100644 (file)
@@ -11,6 +11,7 @@
 #include "networktype.h"
 #include "nodes.h"
 #include "prices.h"
+#include "widgets/Bounty.h"
 #include "widgets/RedditPost.h"
 #include "widgets/CCSEntry.h"
 #include "widgets/RevuoItem.h"
@@ -37,6 +38,7 @@ signals:
     void FiatRatesReceived(const QJsonObject &fiat_rates);
     void RedditReceived(QList<QSharedPointer<RedditPost>> L);
     void CCSReceived(QList<QSharedPointer<CCSEntry>> L);
+    void BountyReceived(QList<QSharedPointer<BountyEntry>> L);
     void RevuoReceived(QList<QSharedPointer<RevuoItem>> L);
     void TxFiatHistoryReceived(const QJsonObject &data);
     void UpdatesReceived(const QJsonObject &updates);
@@ -51,6 +53,7 @@ private slots:
     void onWSNodes(const QJsonArray &nodes);
     void onWSReddit(const QJsonArray &reddit_data);
     void onWSCCS(const QJsonArray &ccs_data);
+    void onWSBounties(const QJsonArray &bounties_data);
     void onWSRevuo(const QJsonArray &revuo_data);
     void onWSUpdates(const QJsonObject &updates);
     void onWSXMRigDownloads(const QJsonObject &downloads);
index 426f96bfe92510476c037db1aeb7a4aa8a46e0f2..ed44c821dc40ebcb377a2f3bf9a0aac651f2c10e 100644 (file)
@@ -79,6 +79,7 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
         {Config::blockExplorer,{QS("blockExplorer"), "exploremonero.com"}},
         {Config::redditFrontend, {QS("redditFrontend"), "old.reddit.com"}},
         {Config::localMoneroFrontend, {QS("localMoneroFrontend"), "https://localmonero.co"}},
+        {Config::bountiesFrontend, {QS("bountiesFrontend"), "https://bounties.monero.social"}},
 
         {Config::fiatSymbols, {QS("fiatSymbols"), QStringList{"USD", "EUR", "GBP", "CAD", "AUD", "RUB"}}},
         {Config::cryptoSymbols, {QS("cryptoSymbols"), QStringList{"BTC", "ETH", "LTC", "XMR", "ZEC"}}},
index cc3ca7ff777985064c005d169099b951dea563ef..e85cf5fe49f0babd821c7a07eddfeec0988b9e73 100644 (file)
@@ -83,6 +83,7 @@ public:
         blockExplorer,
         redditFrontend,
         localMoneroFrontend,
+        bountiesFrontend,
 
         fiatSymbols,
         cryptoSymbols,
diff --git a/src/widgets/BountiesWidget.cpp b/src/widgets/BountiesWidget.cpp
new file mode 100644 (file)
index 0000000..152afd0
--- /dev/null
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: BSD-3-Clause
+// Copyright (c) 2020-2021, The Monero Project.
+
+#include "BountiesWidget.h"
+#include "ui_BountiesWidget.h"
+
+#include <QDesktopServices>
+#include <QStandardItemModel>
+#include <QTableWidget>
+
+#include "model/BountiesModel.h"
+#include "utils/Utils.h"
+#include "utils/config.h"
+
+BountiesWidget::BountiesWidget(QWidget *parent)
+        : QWidget(parent)
+        , ui(new Ui::BountiesWidget)
+        , m_model(new BountiesModel(this))
+        , m_contextMenu(new QMenu(this))
+{
+    ui->setupUi(this);
+    ui->tableView->setModel(m_model);
+    this->setupTable();
+
+    m_contextMenu->addAction("View Bounty", this, &BountiesWidget::linkClicked);
+    m_contextMenu->addAction("Donate", this, &BountiesWidget::donateClicked);
+    connect(ui->tableView, &QHeaderView::customContextMenuRequested, this, &BountiesWidget::showContextMenu);
+
+    connect(ui->tableView, &QTableView::doubleClicked, this, &BountiesWidget::linkClicked);
+
+    ui->tableView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
+}
+
+BountiesModel * BountiesWidget::model() {
+    return m_model;
+}
+
+void BountiesWidget::linkClicked() {
+    QModelIndex index = ui->tableView->currentIndex();
+    auto post = m_model->post(index.row());
+
+    if (post)
+        Utils::externalLinkWarning(this, this->getLink(post->link));
+}
+
+void BountiesWidget::donateClicked() {
+    QModelIndex index = ui->tableView->currentIndex();
+    auto bounty = m_model->post(index.row());
+
+    if (bounty) {
+        emit donate(bounty->donationAddress, QString("Bounty: %1").arg(bounty->title));
+    }
+}
+
+void BountiesWidget::setupTable() {
+    ui->tableView->verticalHeader()->setVisible(false);
+    ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
+
+    ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
+    ui->tableView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
+}
+
+void BountiesWidget::showContextMenu(const QPoint &pos) {
+    QModelIndex index = ui->tableView->indexAt(pos);
+    if (!index.isValid()) {
+        return;
+    }
+
+    m_contextMenu->exec(ui->tableView->viewport()->mapToGlobal(pos));
+}
+
+QString BountiesWidget::getLink(const QString &permaLink) {
+    QString frontend = config()->get(Config::bountiesFrontend).toString();
+    return QString("%1/%2").arg(frontend, permaLink);
+}
+
+BountiesWidget::~BountiesWidget() = default;
\ No newline at end of file
diff --git a/src/widgets/BountiesWidget.h b/src/widgets/BountiesWidget.h
new file mode 100644 (file)
index 0000000..548e1c2
--- /dev/null
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: BSD-3-Clause
+// Copyright (c) 2020-2021, The Monero Project.
+
+#ifndef FEATHER_BOUNTIESWIDGET_H
+#define FEATHER_BOUNTIESWIDGET_H
+
+#include <QItemSelection>
+#include <QMenu>
+#include <QWidget>
+
+#include "model/BountiesModel.h"
+
+namespace Ui {
+    class BountiesWidget;
+}
+
+class BountiesWidget : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit BountiesWidget(QWidget *parent = nullptr);
+    ~BountiesWidget() override;
+    BountiesModel* model();
+
+public slots:
+    void linkClicked();
+
+signals:
+    void setStatusText(const QString &msg, bool override, int timeout);
+    void donate(const QString &address, const QString &description);
+
+private:
+    void setupTable();
+    void showContextMenu(const QPoint &pos);
+    void donateClicked();
+    QString getLink(const QString &permaLink);
+
+    QScopedPointer<Ui::BountiesWidget> ui;
+    BountiesModel *m_model;
+    QMenu *m_contextMenu;
+};
+
+#endif //FEATHER_BOUNTIESWIDGET_H
diff --git a/src/widgets/BountiesWidget.ui b/src/widgets/BountiesWidget.ui
new file mode 100644 (file)
index 0000000..d4e985a
--- /dev/null
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>BountiesWidget</class>
+ <widget class="QWidget" name="BountiesWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>566</width>
+    <height>372</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <property name="leftMargin">
+    <number>0</number>
+   </property>
+   <property name="topMargin">
+    <number>0</number>
+   </property>
+   <property name="rightMargin">
+    <number>0</number>
+   </property>
+   <property name="bottomMargin">
+    <number>0</number>
+   </property>
+   <item>
+    <widget class="QTableView" name="tableView">
+     <property name="contextMenuPolicy">
+      <enum>Qt::CustomContextMenu</enum>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/widgets/Bounty.h b/src/widgets/Bounty.h
new file mode 100644 (file)
index 0000000..7dfd9ed
--- /dev/null
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: BSD-3-Clause
+// Copyright (c) 2020-2021, The Monero Project.
+
+#ifndef FEATHER_BOUNTY_H
+#define FEATHER_BOUNTY_H
+
+#include <QString>
+#include <utility>
+
+struct BountyEntry {
+    BountyEntry(int votes, QString title, double bountyAmount, QString link, QString donationAddress, QString status)
+        : votes(votes), title(std::move(title)), bountyAmount(bountyAmount), link(std::move(link)),
+        donationAddress(std::move(donationAddress)), status(std::move(status)){};
+
+    int votes;
+    QString title;
+    double bountyAmount;
+    QString link;
+    QString donationAddress;
+    QString status;
+};
+
+#endif //FEATHER_BOUNTY_H