Home: bounties.monero.social

This commit is contained in:
tobtoht 2022-06-23 19:48:30 +02:00
parent 3b3fcfc42c
commit 3c7f1800c2
No known key found for this signature in database
GPG key ID: 1CADD27F41F45C3C
13 changed files with 405 additions and 4 deletions

View 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();
}

View file

@ -215,6 +215,7 @@ private:
void updateWidgetIcons();
bool verifyPassword();
void patchStylesheetMac();
void fillSendTab(const QString &address, const QString &description);
void userActivity();
void checkUserActivity();

View file

@ -123,6 +123,28 @@
</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>
@ -899,6 +921,12 @@
<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"/>

114
src/model/BountiesModel.cpp Normal file
View file

@ -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);
}

43
src/model/BountiesModel.h Normal file
View file

@ -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

View 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);
}

View 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);

View 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"}}},

View file

@ -83,6 +83,7 @@ public:
blockExplorer,
redditFrontend,
localMoneroFrontend,
bountiesFrontend,
fiatSymbols,
cryptoSymbols,

View file

@ -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;

View file

@ -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

View file

@ -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>

23
src/widgets/Bounty.h Normal file
View file

@ -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