mirror of
https://github.com/feather-wallet/feather.git
synced 2025-01-11 05:15:21 +00:00
Plugin Tab placeholder copy of Calc
This commit is contained in:
parent
c2701c00dd
commit
140547b38e
16 changed files with 908 additions and 4 deletions
|
@ -34,6 +34,7 @@ option(WITH_PLUGIN_BOUNTIES "Include Bounties Home plugin" ON)
|
||||||
option(WITH_PLUGIN_REVUO "Include Revuo Home plugin" ON)
|
option(WITH_PLUGIN_REVUO "Include Revuo Home plugin" ON)
|
||||||
option(WITH_PLUGIN_CALC "Include Calc tab plugin" ON)
|
option(WITH_PLUGIN_CALC "Include Calc tab plugin" ON)
|
||||||
option(WITH_PLUGIN_XMRIG "Include XMRig plugin" ON)
|
option(WITH_PLUGIN_XMRIG "Include XMRig plugin" ON)
|
||||||
|
option(WITH_PLUGIN_ATOMIC "Include Atomic plugin" ON)
|
||||||
|
|
||||||
list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/cmake")
|
list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/cmake")
|
||||||
include(CheckCCompilerFlag)
|
include(CheckCCompilerFlag)
|
||||||
|
|
|
@ -225,7 +225,7 @@ if(STATIC)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
target_compile_definitions(feather PRIVATE QT_NO_DEBUG=1)
|
target_compile_definitions(feather PRIVATE QT_NO_DEBUG=0)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_compile_definitions(feather
|
target_compile_definitions(feather
|
||||||
|
|
101
src/plugins/atomic/AtomicConfigDialog.cpp
Normal file
101
src/plugins/atomic/AtomicConfigDialog.cpp
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#include "AtomicConfigDialog.h"
|
||||||
|
#include "ui_AtomicConfigDialog.h"
|
||||||
|
|
||||||
|
#include "utils/AppData.h"
|
||||||
|
#include "utils/config.h"
|
||||||
|
|
||||||
|
AtomicConfigDialog::AtomicConfigDialog(QWidget *parent)
|
||||||
|
: WindowModalDialog(parent)
|
||||||
|
, ui(new Ui::AtomicConfigDialog)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
this->fillListWidgets();
|
||||||
|
|
||||||
|
connect(ui->btn_selectAll, &QPushButton::clicked, this, &AtomicConfigDialog::selectAll);
|
||||||
|
connect(ui->btn_deselectAll, &QPushButton::clicked, this, &AtomicConfigDialog::deselectAll);
|
||||||
|
|
||||||
|
connect(ui->buttonBox, &QDialogButtonBox::accepted, [this]{
|
||||||
|
conf()->set(Config::fiatSymbols, this->checkedFiat());
|
||||||
|
conf()->set(Config::cryptoSymbols, this->checkedCrypto());
|
||||||
|
this->accept();
|
||||||
|
});
|
||||||
|
|
||||||
|
this->adjustSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList AtomicConfigDialog::checkedFiat() {
|
||||||
|
return this->getChecked(ui->list_fiat);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList AtomicConfigDialog::checkedCrypto() {
|
||||||
|
return this->getChecked(ui->list_crypto);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtomicConfigDialog::selectAll() {
|
||||||
|
this->setCheckState(this->getVisibleListWidget(), Qt::Checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtomicConfigDialog::deselectAll() {
|
||||||
|
this->setCheckState(this->getVisibleListWidget(), Qt::Unchecked);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtomicConfigDialog::setCheckState(QListWidget *widget, Qt::CheckState checkState) {
|
||||||
|
QListWidgetItem *item;
|
||||||
|
for (int i=0; i < widget->count(); i++) {
|
||||||
|
item = widget->item(i);
|
||||||
|
item->setCheckState(checkState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList AtomicConfigDialog::getChecked(QListWidget *widget) {
|
||||||
|
QStringList checked;
|
||||||
|
QListWidgetItem *item;
|
||||||
|
for (int i=0; i < widget->count(); i++) {
|
||||||
|
item = widget->item(i);
|
||||||
|
if (item->checkState() == Qt::Checked) {
|
||||||
|
checked.append(item->text());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
QListWidget* AtomicConfigDialog::getVisibleListWidget() {
|
||||||
|
if (ui->tabWidget->currentIndex() == 0) {
|
||||||
|
return ui->list_fiat;
|
||||||
|
} else {
|
||||||
|
return ui->list_crypto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtomicConfigDialog::fillListWidgets() {
|
||||||
|
QStringList cryptoCurrencies = appData()->prices.markets.keys();
|
||||||
|
QStringList fiatCurrencies = appData()->prices.rates.keys();
|
||||||
|
|
||||||
|
QStringList checkedCryptoCurrencies = conf()->get(Config::cryptoSymbols).toStringList();
|
||||||
|
QStringList checkedFiatCurrencies = conf()->get(Config::fiatSymbols).toStringList();
|
||||||
|
|
||||||
|
ui->list_crypto->addItems(cryptoCurrencies);
|
||||||
|
ui->list_fiat->addItems(fiatCurrencies);
|
||||||
|
|
||||||
|
auto setChecked = [](QListWidget *widget, const QStringList &checked){
|
||||||
|
QListWidgetItem *item;
|
||||||
|
for (int i=0; i < widget->count(); i++) {
|
||||||
|
item = widget->item(i);
|
||||||
|
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||||
|
if (checked.contains(item->text())) {
|
||||||
|
item->setCheckState(Qt::Checked);
|
||||||
|
} else {
|
||||||
|
item->setCheckState(Qt::Unchecked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
setChecked(ui->list_crypto, checkedCryptoCurrencies);
|
||||||
|
setChecked(ui->list_fiat, checkedFiatCurrencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
AtomicConfigDialog::~AtomicConfigDialog() = default;
|
41
src/plugins/atomic/AtomicConfigDialog.h
Normal file
41
src/plugins/atomic/AtomicConfigDialog.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#ifndef FEATHER_ATOMICCONFIGDIALOG_H
|
||||||
|
#define FEATHER_ATOMICCONFIGDIALOG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QListWidget>
|
||||||
|
|
||||||
|
#include "components.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class AtomicConfigDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
class AtomicConfigDialog : public WindowModalDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit AtomicConfigDialog(QWidget *parent = nullptr);
|
||||||
|
~AtomicConfigDialog() override;
|
||||||
|
|
||||||
|
QStringList checkedFiat();
|
||||||
|
QStringList checkedCrypto();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void selectAll();
|
||||||
|
void deselectAll();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setCheckState(QListWidget *widget, Qt::CheckState checkState);
|
||||||
|
QStringList getChecked(QListWidget *widget);
|
||||||
|
void fillListWidgets();
|
||||||
|
QListWidget* getVisibleListWidget();
|
||||||
|
|
||||||
|
QScopedPointer<Ui::AtomicConfigDialog> ui;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //FEATHER_ATOMICCONFIGDIALOG_H
|
153
src/plugins/atomic/AtomicConfigDialog.ui
Normal file
153
src/plugins/atomic/AtomicConfigDialog.ui
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>AtomicConfigDialog</class>
|
||||||
|
<widget class="QDialog" name="AtomicConfigDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>509</width>
|
||||||
|
<height>574</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Atomic config</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QTabWidget" name="tabWidget">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="tab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Fiat</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
|
<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="QListWidget" name="list_fiat"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="tab_2">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Crypto</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
|
<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="QListWidget" name="list_crypto"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="line">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_selectAll">
|
||||||
|
<property name="text">
|
||||||
|
<string>Select all</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_deselectAll">
|
||||||
|
<property name="text">
|
||||||
|
<string>Deselect all</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>AtomicConfigDialog</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>AtomicConfigDialog</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
70
src/plugins/atomic/AtomicPlugin.cpp
Normal file
70
src/plugins/atomic/AtomicPlugin.cpp
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#include "AtomicPlugin.h"
|
||||||
|
#include "AtomicConfigDialog.h"
|
||||||
|
|
||||||
|
#include "plugins/PluginRegistry.h"
|
||||||
|
|
||||||
|
AtomicPlugin::AtomicPlugin()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtomicPlugin::initialize(Wallet *wallet, QObject *parent) {
|
||||||
|
this->setParent(parent);
|
||||||
|
m_tab = new AtomicWidget(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AtomicPlugin::id() {
|
||||||
|
return "atomic";
|
||||||
|
}
|
||||||
|
|
||||||
|
int AtomicPlugin::idx() const {
|
||||||
|
return 61;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AtomicPlugin::parent() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AtomicPlugin::displayName() {
|
||||||
|
return "Atomic";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AtomicPlugin::description() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AtomicPlugin::icon() {
|
||||||
|
return "gnome-calc.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList AtomicPlugin::socketData() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Plugin::PluginType AtomicPlugin::type() {
|
||||||
|
return Plugin::PluginType::TAB;
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget* AtomicPlugin::tab() {
|
||||||
|
return m_tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AtomicPlugin::configurable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDialog* AtomicPlugin::configDialog(QWidget *parent) {
|
||||||
|
return new AtomicConfigDialog{parent};
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtomicPlugin::skinChanged() {
|
||||||
|
m_tab->skinChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool AtomicPlugin::registered = [] {
|
||||||
|
PluginRegistry::registerPlugin(AtomicPlugin::create());
|
||||||
|
PluginRegistry::getInstance().registerPluginCreator(&AtomicPlugin::create);
|
||||||
|
return true;
|
||||||
|
}();
|
41
src/plugins/atomic/AtomicPlugin.h
Normal file
41
src/plugins/atomic/AtomicPlugin.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#ifndef ATOMICPLUGIN_H
|
||||||
|
#define ATOMICPLUGIN_H
|
||||||
|
|
||||||
|
#include "plugins/Plugin.h"
|
||||||
|
#include "AtomicWidget.h"
|
||||||
|
|
||||||
|
class AtomicPlugin : public Plugin {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit AtomicPlugin();
|
||||||
|
|
||||||
|
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;
|
||||||
|
bool configurable() override;
|
||||||
|
QDialog* configDialog(QWidget *parent) override;
|
||||||
|
|
||||||
|
void initialize(Wallet *wallet, QObject *parent) override;
|
||||||
|
|
||||||
|
static AtomicPlugin* create() { return new AtomicPlugin(); }
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void skinChanged() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
AtomicWidget* m_tab = nullptr;
|
||||||
|
static const bool registered;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //ATOMICPLUGIN_H
|
170
src/plugins/atomic/AtomicWidget.cpp
Normal file
170
src/plugins/atomic/AtomicWidget.cpp
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#include "AtomicWidget.h"
|
||||||
|
#include "ui_AtomicWidget.h"
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
#include "AtomicConfigDialog.h"
|
||||||
|
#include "utils/AppData.h"
|
||||||
|
#include "utils/ColorScheme.h"
|
||||||
|
#include "utils/config.h"
|
||||||
|
#include "utils/WebsocketNotifier.h"
|
||||||
|
|
||||||
|
AtomicWidget::AtomicWidget(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, ui(new Ui::AtomicWidget)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
ui->imageExchange->setBackgroundRole(QPalette::Base);
|
||||||
|
ui->imageExchange->setAssets(":/assets/images/exchange.png", ":/assets/images/exchange_white.png");
|
||||||
|
ui->imageExchange->setScaledContents(true);
|
||||||
|
ui->imageExchange->setFixedSize(26, 26);
|
||||||
|
|
||||||
|
// validator/locale for input
|
||||||
|
QString amount_rx = R"(^\d{0,8}[\.]\d{0,12}$)";
|
||||||
|
QRegularExpression rx;
|
||||||
|
rx.setPattern(amount_rx);
|
||||||
|
QValidator *validator = new QRegularExpressionValidator(rx, this);
|
||||||
|
ui->lineFrom->setValidator(validator);
|
||||||
|
ui->lineTo->setValidator(validator);
|
||||||
|
|
||||||
|
connect(&appData()->prices, &Prices::fiatPricesUpdated, this, &AtomicWidget::onPricesReceived);
|
||||||
|
connect(&appData()->prices, &Prices::cryptoPricesUpdated, this, &AtomicWidget::onPricesReceived);
|
||||||
|
|
||||||
|
connect(ui->lineFrom, &QLineEdit::textEdited, this, [this]{this->convert(false);});
|
||||||
|
connect(ui->lineTo, &QLineEdit::textEdited, this, [this]{this->convert(true);});
|
||||||
|
|
||||||
|
connect(ui->comboAtomicFrom, QOverload<int>::of(&QComboBox::currentIndexChanged), [this]{this->convert(false);});
|
||||||
|
connect(ui->comboAtomicTo, QOverload<int>::of(&QComboBox::currentIndexChanged), [this]{this->convert(false);});
|
||||||
|
|
||||||
|
connect(ui->btn_configure, &QPushButton::clicked, this, &AtomicWidget::showAtomicConfigureDialog);
|
||||||
|
|
||||||
|
QTimer::singleShot(1, [this]{
|
||||||
|
this->skinChanged();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_statusTimer.start(5000);
|
||||||
|
connect(&m_statusTimer, &QTimer::timeout, this, &AtomicWidget::updateStatus);
|
||||||
|
QPixmap warningIcon = QPixmap(":/assets/images/warning.png");
|
||||||
|
ui->icon_warning->setPixmap(warningIcon.scaledToWidth(32, Qt::SmoothTransformation));
|
||||||
|
|
||||||
|
this->updateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtomicWidget::convert(bool reverse) {
|
||||||
|
if (!m_comboBoxInit)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto lineFrom = reverse ? ui->lineTo : ui->lineFrom;
|
||||||
|
auto lineTo = reverse ? ui->lineFrom : ui->lineTo;
|
||||||
|
|
||||||
|
auto comboFrom = reverse ? ui->comboAtomicTo : ui->comboAtomicFrom;
|
||||||
|
auto comboTo = reverse ? ui->comboAtomicFrom : ui->comboAtomicTo;
|
||||||
|
|
||||||
|
QString symbolFrom = comboFrom->itemText(comboFrom->currentIndex());
|
||||||
|
QString symbolTo = comboTo->itemText(comboTo->currentIndex());
|
||||||
|
|
||||||
|
if (symbolFrom == symbolTo) {
|
||||||
|
lineTo->setText(lineFrom->text());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString amountStr = lineFrom->text();
|
||||||
|
double amount = amountStr.toDouble();
|
||||||
|
double result = appData()->prices.convert(symbolFrom, symbolTo, amount);
|
||||||
|
|
||||||
|
int precision = 10;
|
||||||
|
if (appData()->prices.rates.contains(symbolTo))
|
||||||
|
precision = 2;
|
||||||
|
|
||||||
|
lineTo->setText(QString::number(result, 'f', precision));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtomicWidget::onPricesReceived() {
|
||||||
|
if (m_comboBoxInit)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QList<QString> cryptoKeys = appData()->prices.markets.keys();
|
||||||
|
QList<QString> fiatKeys = appData()->prices.rates.keys();
|
||||||
|
if (cryptoKeys.empty() || fiatKeys.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
ui->btn_configure->setEnabled(true);
|
||||||
|
this->initComboBox();
|
||||||
|
m_comboBoxInit = true;
|
||||||
|
this->updateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtomicWidget::initComboBox() {
|
||||||
|
QList<QString> cryptoKeys = appData()->prices.markets.keys();
|
||||||
|
QList<QString> fiatKeys = appData()->prices.rates.keys();
|
||||||
|
|
||||||
|
QStringList enabledCrypto = conf()->get(Config::cryptoSymbols).toStringList();
|
||||||
|
QStringList filteredCryptoKeys;
|
||||||
|
for (const auto& symbol : cryptoKeys) {
|
||||||
|
if (enabledCrypto.contains(symbol)) {
|
||||||
|
filteredCryptoKeys.append(symbol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList enabledFiat = conf()->get(Config::fiatSymbols).toStringList();
|
||||||
|
auto preferredFiat = conf()->get(Config::preferredFiatCurrency).toString();
|
||||||
|
if (!enabledFiat.contains(preferredFiat) && fiatKeys.contains(preferredFiat)) {
|
||||||
|
enabledFiat.append(preferredFiat);
|
||||||
|
conf()->set(Config::fiatSymbols, enabledFiat);
|
||||||
|
}
|
||||||
|
QStringList filteredFiatKeys;
|
||||||
|
for (const auto &symbol : fiatKeys) {
|
||||||
|
if (enabledFiat.contains(symbol)) {
|
||||||
|
filteredFiatKeys.append(symbol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->setupComboBox(ui->comboAtomicFrom, filteredCryptoKeys, filteredFiatKeys);
|
||||||
|
this->setupComboBox(ui->comboAtomicTo, filteredCryptoKeys, filteredFiatKeys);
|
||||||
|
|
||||||
|
ui->comboAtomicFrom->setCurrentIndex(ui->comboAtomicFrom->findText("XMR"));
|
||||||
|
|
||||||
|
if (!preferredFiat.isEmpty()) {
|
||||||
|
ui->comboAtomicTo->setCurrentIndex(ui->comboAtomicTo->findText(preferredFiat));
|
||||||
|
} else {
|
||||||
|
ui->comboAtomicTo->setCurrentIndex(ui->comboAtomicTo->findText("USD"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtomicWidget::skinChanged() {
|
||||||
|
ui->imageExchange->setMode(ColorScheme::hasDarkBackground(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtomicWidget::showAtomicConfigureDialog() {
|
||||||
|
AtomicConfigDialog dialog{this};
|
||||||
|
|
||||||
|
if (dialog.exec() == QDialog::Accepted) {
|
||||||
|
this->initComboBox();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtomicWidget::setupComboBox(QComboBox *comboBox, const QStringList &crypto, const QStringList &fiat) {
|
||||||
|
comboBox->clear();
|
||||||
|
comboBox->addItems(crypto);
|
||||||
|
comboBox->insertSeparator(comboBox->count());
|
||||||
|
comboBox->addItems(fiat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtomicWidget::updateStatus() {
|
||||||
|
if (!m_comboBoxInit) {
|
||||||
|
ui->label_warning->setText("Waiting on exchange data.");
|
||||||
|
ui->frame_warning->show();
|
||||||
|
}
|
||||||
|
else if (websocketNotifier()->stale(10)) {
|
||||||
|
ui->label_warning->setText("No new exchange rates received for over 10 minutes.");
|
||||||
|
ui->frame_warning->show();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ui->frame_warning->hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AtomicWidget::~AtomicWidget() = default;
|
41
src/plugins/atomic/AtomicWidget.h
Normal file
41
src/plugins/atomic/AtomicWidget.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#ifndef FEATHER_ATOMICWIDGET_H
|
||||||
|
#define FEATHER_ATOMICWIDGET_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class AtomicWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
class AtomicWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit AtomicWidget(QWidget *parent = nullptr);
|
||||||
|
~AtomicWidget() override;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void skinChanged();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void initComboBox();
|
||||||
|
void showAtomicConfigureDialog();
|
||||||
|
void onPricesReceived();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void convert(bool reverse);
|
||||||
|
void setupComboBox(QComboBox *comboBox, const QStringList &crypto, const QStringList &fiat);
|
||||||
|
void updateStatus();
|
||||||
|
|
||||||
|
QScopedPointer<Ui::AtomicWidget> ui;
|
||||||
|
bool m_comboBoxInit = false;
|
||||||
|
QTimer m_statusTimer;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FEATHER_ATOMICWIDGET_H
|
187
src/plugins/atomic/AtomicWidget.ui
Normal file
187
src/plugins/atomic/AtomicWidget.ui
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>AtomicWidget</class>
|
||||||
|
<widget class="QWidget" name="AtomicWidget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>800</width>
|
||||||
|
<height>366</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>MainWindow</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame_warning">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="icon_warning">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>icon</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Maximum</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>10</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_warning">
|
||||||
|
<property name="text">
|
||||||
|
<string>Warning text</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
|
<property name="horizontalSpacing">
|
||||||
|
<number>18</number>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="lineFrom">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="frame">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>From...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="comboAtomicFrom"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="DoublePixmapLabel" name="imageExchange">
|
||||||
|
<property name="text">
|
||||||
|
<string>exchange image</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="lineTo">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>To...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="comboAtomicTo"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_configure">
|
||||||
|
<property name="text">
|
||||||
|
<string>Configure</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>DoublePixmapLabel</class>
|
||||||
|
<extends>QLabel</extends>
|
||||||
|
<header>components.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
<slots>
|
||||||
|
<slot>fromChanged(QString)</slot>
|
||||||
|
<slot>toChanged(QString)</slot>
|
||||||
|
<slot>toComboChanged(QString)</slot>
|
||||||
|
</slots>
|
||||||
|
</ui>
|
21
src/plugins/atomic/AtomicWindow.cpp
Normal file
21
src/plugins/atomic/AtomicWindow.cpp
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#include "AtomicWindow.h"
|
||||||
|
#include "ui_AtomicWindow.h"
|
||||||
|
|
||||||
|
#include "utils/AppData.h"
|
||||||
|
#include "utils/Icons.h"
|
||||||
|
|
||||||
|
AtomicWindow::AtomicWindow(QWidget *parent)
|
||||||
|
: QMainWindow(parent)
|
||||||
|
, ui(new Ui::AtomicWindow)
|
||||||
|
{
|
||||||
|
Qt::WindowFlags flags = this->windowFlags();
|
||||||
|
this->setWindowFlags(flags|Qt::WindowStaysOnTopHint); // on top
|
||||||
|
|
||||||
|
ui->setupUi(this);
|
||||||
|
this->setWindowIcon(icons()->icon("gnome-Atomic.png"));
|
||||||
|
}
|
||||||
|
|
||||||
|
AtomicWindow::~AtomicWindow() = default;
|
26
src/plugins/atomic/AtomicWindow.h
Normal file
26
src/plugins/atomic/AtomicWindow.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#ifndef FEATHER_ATOMICWINDOW_H
|
||||||
|
#define FEATHER_ATOMICWINDOW_H
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class AtomicWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
class AtomicWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit AtomicWindow(QWidget *parent = nullptr);
|
||||||
|
~AtomicWindow() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QScopedPointer<Ui::AtomicWindow> ui;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FEATHER_ATOMICWINDOW_H
|
||||||
|
|
49
src/plugins/atomic/AtomicWindow.ui
Normal file
49
src/plugins/atomic/AtomicWindow.ui
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>AtomicWindow</class>
|
||||||
|
<widget class="QMainWindow" name="AtomicWindow">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>520</width>
|
||||||
|
<height>108</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Atomic Swap</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralwidget">
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>9</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>9</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>9</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>9</number>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="AtomicWidget" name="atomicWidget" native="true"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>AtomicWidget</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>plugins/atomic/AtomicWidget.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
<slots>
|
||||||
|
<slot>stayOnTop(int)</slot>
|
||||||
|
</slots>
|
||||||
|
</ui>
|
|
@ -43,7 +43,7 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
|
||||||
{Config::useOnionNodes,{QS("useOnionNodes"), false}},
|
{Config::useOnionNodes,{QS("useOnionNodes"), false}},
|
||||||
|
|
||||||
// Tabs
|
// Tabs
|
||||||
{Config::enabledTabs, {QS("enabledTabs"), QStringList{"Home", "History", "Send", "Receive", "Calc"}}},
|
{Config::enabledTabs, {QS("enabledTabs"), QStringList{"Home", "History", "Send", "Receive", "Calc", "Atomic"}}},
|
||||||
{Config::showSearchbar,{QS("showSearchbar"), true}},
|
{Config::showSearchbar,{QS("showSearchbar"), true}},
|
||||||
|
|
||||||
// History
|
// History
|
||||||
|
@ -129,7 +129,7 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
|
||||||
{Config::useLocalTor, {QS("useLocalTor"), false}},
|
{Config::useLocalTor, {QS("useLocalTor"), false}},
|
||||||
{Config::initSyncThreshold, {QS("initSyncThreshold"), 360}},
|
{Config::initSyncThreshold, {QS("initSyncThreshold"), 360}},
|
||||||
|
|
||||||
{Config::enabledPlugins, {QS("enabledPlugins"), QStringList{"tickers", "crowdfunding", "bounties", "revuo", "calc", "xmrig"}}},
|
{Config::enabledPlugins, {QS("enabledPlugins"), QStringList{"tickers", "crowdfunding", "bounties", "revuo", "calc", "xmrig", "atomic"}}},
|
||||||
{Config::restartRequired, {QS("restartRequired"), false}},
|
{Config::restartRequired, {QS("restartRequired"), false}},
|
||||||
|
|
||||||
{Config::tickers, {QS("tickers"), QStringList{"XMR", "BTC", "XMR/BTC"}}},
|
{Config::tickers, {QS("tickers"), QStringList{"XMR", "BTC", "XMR/BTC"}}},
|
||||||
|
|
|
@ -40,6 +40,8 @@ WalletWizard::WalletWizard(QWidget *parent)
|
||||||
auto networkWebsocketPage = new PageNetworkWebsocket(this);
|
auto networkWebsocketPage = new PageNetworkWebsocket(this);
|
||||||
auto menuPage = new PageMenu(&m_wizardFields, m_walletKeysFilesModel, this);
|
auto menuPage = new PageMenu(&m_wizardFields, m_walletKeysFilesModel, this);
|
||||||
auto openWalletPage = new PageOpenWallet(m_walletKeysFilesModel, this);
|
auto openWalletPage = new PageOpenWallet(m_walletKeysFilesModel, this);
|
||||||
|
|
||||||
|
|
||||||
auto createWallet = new PageWalletFile(&m_wizardFields , this);
|
auto createWallet = new PageWalletFile(&m_wizardFields , this);
|
||||||
auto createWalletSeed = new PageWalletSeed(&m_wizardFields, this);
|
auto createWalletSeed = new PageWalletSeed(&m_wizardFields, this);
|
||||||
auto walletSetPasswordPage = new PageSetPassword(&m_wizardFields, this);
|
auto walletSetPasswordPage = new PageSetPassword(&m_wizardFields, this);
|
||||||
|
|
|
@ -84,7 +84,8 @@ public:
|
||||||
Page_HardwareDevice,
|
Page_HardwareDevice,
|
||||||
Page_NetworkProxy,
|
Page_NetworkProxy,
|
||||||
Page_NetworkWebsocket,
|
Page_NetworkWebsocket,
|
||||||
Page_Plugins
|
Page_Plugins,
|
||||||
|
Page_Atomic
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit WalletWizard(QWidget *parent = nullptr);
|
explicit WalletWizard(QWidget *parent = nullptr);
|
||||||
|
|
Loading…
Reference in a new issue