mirror of
https://codeberg.org/wownero/lwcli-wow.git
synced 2026-10-17 05:51:26 +02:00
Add CLI flags, updated UI loading routine, and moved files around
This commit is contained in:
@@ -26,8 +26,8 @@
|
||||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
set(lwcli-views_sources history.cpp keys.cpp manager.cpp wallet.cpp)
|
||||
set(lwscli-views_headers history.h keys.h manager.h wallet.h)
|
||||
set(lwcli-views_sources history.cpp keys.cpp manager.cpp settings.cpp wallet.cpp)
|
||||
set(lwscli-views_headers history.h keys.h manager.h settings.h wallet.h)
|
||||
|
||||
add_library(lwcli-views ${lwcli-views_sources} ${lwcli-views_headers})
|
||||
target_link_libraries(lwcli-views PRIVATE component dom lwcli-decorate lwsf-api)
|
||||
|
||||
+57
-35
@@ -31,6 +31,8 @@
|
||||
#include <ftxui/component/event.hpp>
|
||||
#include <ftxui/dom/table.hpp>
|
||||
#include <lws_frontend.h>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "decorate/overlay.h"
|
||||
#include "events.h"
|
||||
@@ -130,13 +132,13 @@ namespace lwcli { namespace view
|
||||
virtual ftxui::Element OnRender() override final
|
||||
{
|
||||
std::vector<ftxui::Elements> grid{
|
||||
{ftxui::text("Description: "), note_input_->Render()},
|
||||
{ftxui::text(_("Description: ")), note_input_->Render()},
|
||||
timestamp_,
|
||||
payment_id_,
|
||||
{ftxui::text("Confirmations: "), ftxui::text(std::to_string(info_->confirmations()))},
|
||||
{ftxui::text(_("Confirmations: ")), ftxui::text(std::to_string(info_->confirmations()))},
|
||||
amount_,
|
||||
fee_,
|
||||
{ftxui::text("Block Height: "), ftxui::text(std::to_string(info_->blockHeight()))},
|
||||
{ftxui::text(_("Block Height: ")), ftxui::text(std::to_string(info_->blockHeight()))},
|
||||
minors_,
|
||||
coinbase_
|
||||
};
|
||||
@@ -148,7 +150,7 @@ namespace lwcli { namespace view
|
||||
std::string{_(" to Unknown Address")} : _(" to ") + transfer.address;
|
||||
std::string text;
|
||||
if (base_size == grid.size())
|
||||
text = "Transfers: ";
|
||||
text = _("Transfers: ");
|
||||
grid.push_back({
|
||||
ftxui::text(std::move(text)),
|
||||
ftxui::text(print_money(transfer.amount) + address)
|
||||
@@ -156,15 +158,17 @@ namespace lwcli { namespace view
|
||||
}
|
||||
|
||||
ftxui::Elements vertical;
|
||||
vertical.reserve(3);
|
||||
vertical.reserve(4);
|
||||
|
||||
vertical.push_back(ftxui::hcenter(ftxui::hbox(cancel_->Render(), ok_->Render())));
|
||||
if (info_->isFailed())
|
||||
vertical.push_back(ftxui::inverted(ftxui::hcenter(ftxui::text("FAILED"))));
|
||||
vertical.push_back(ftxui::inverted(ftxui::hcenter(ftxui::text(_("FAILED")))));
|
||||
else if (info_->isPending())
|
||||
vertical.push_back(ftxui::inverted(ftxui::hcenter(ftxui::text("PENDING"))));
|
||||
vertical.push_back(ftxui::inverted(ftxui::hcenter(ftxui::text(_("PENDING")))));
|
||||
else
|
||||
vertical.push_back(ftxui::separator());
|
||||
|
||||
vertical.push_back(ftxui::gridbox(std::move(grid)));
|
||||
vertical.push_back(ftxui::hcenter(ftxui::hbox(cancel_->Render(), ok_->Render())));
|
||||
|
||||
return ftxui::window(title_, ftxui::vbox(std::move(vertical)));
|
||||
}
|
||||
@@ -231,8 +235,8 @@ namespace lwcli { namespace view
|
||||
}, ftxui::ButtonOption::Ascii());
|
||||
|
||||
container_ = ftxui::Container::Vertical({
|
||||
note_input_,
|
||||
ftxui::Container::Horizontal({cancel_, ok_})
|
||||
ftxui::Container::Horizontal({cancel_, ok_}),
|
||||
note_input_
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -245,7 +249,7 @@ namespace lwcli { namespace view
|
||||
ftxui::Element table_; //!< Does not redraw when child is enabled
|
||||
std::vector<std::vector<std::string>> base_rows;
|
||||
std::ptrdiff_t highlighted_;
|
||||
std::size_t row_count_;
|
||||
std::unordered_map<std::size_t, int> row_map_;
|
||||
const std::uint32_t account_;
|
||||
|
||||
static constexpr std::size_t min_row() noexcept { return 2; };
|
||||
@@ -261,7 +265,7 @@ namespace lwcli { namespace view
|
||||
table_(nullptr),
|
||||
base_rows(),
|
||||
highlighted_(min_row() - 1),
|
||||
row_count_(0),
|
||||
row_map_(),
|
||||
account_(account)
|
||||
{
|
||||
Monero::Subaddress* subaddress = wallet_->subaddress();
|
||||
@@ -294,17 +298,19 @@ namespace lwcli { namespace view
|
||||
}
|
||||
else if (event == ftxui::Event::CtrlQ)
|
||||
throw event::close{};
|
||||
else if (event == ftxui::Event::ArrowDown && highlighted_ + 1 < row_count_)
|
||||
else if (event.is_character())
|
||||
return false;
|
||||
else if (event == ftxui::Event::ArrowDown && highlighted_ < row_map_.size() + min_row() - 1)
|
||||
++highlighted_;
|
||||
else if (event == ftxui::Event::ArrowUp && min_row() <= highlighted_)
|
||||
--highlighted_;
|
||||
else if (event == ftxui::Event::PageDown)
|
||||
highlighted_ = std::min(std::ptrdiff_t(row_count_), highlighted_ + 10);
|
||||
highlighted_ = std::min(std::ptrdiff_t(row_map_.size() + min_row()), highlighted_ + 10);
|
||||
else if (event == ftxui::Event::PageUp)
|
||||
highlighted_ = std::max(std::ptrdiff_t(min_row()), highlighted_ - 10);
|
||||
else if (event == ftxui::Event::Return)
|
||||
{
|
||||
overlay_ = ftxui::Make<tx_details>(wallet_->history(), row_count_ - highlighted_ - 1);
|
||||
overlay_ = ftxui::Make<tx_details>(wallet_->history(), row_map_.at(highlighted_ - min_row()));
|
||||
Add(overlay_);
|
||||
}
|
||||
}
|
||||
@@ -315,7 +321,7 @@ namespace lwcli { namespace view
|
||||
overlay_->Detach();
|
||||
overlay_.reset();
|
||||
}
|
||||
return min_row() <= highlighted_ && highlighted_ < row_count_;
|
||||
return min_row() <= highlighted_ && highlighted_ - min_row() < row_map_.size();
|
||||
}
|
||||
|
||||
ftxui::Element OnRender() override final
|
||||
@@ -324,8 +330,8 @@ namespace lwcli { namespace view
|
||||
{
|
||||
if (highlighted_ < min_row())
|
||||
highlighted_ = min_row();
|
||||
else if (row_count_ <= highlighted_)
|
||||
highlighted_ = row_count_ - 1;
|
||||
else if (row_map_.size() <= highlighted_ - min_row())
|
||||
highlighted_ = row_map_.size() - 1 + min_row();
|
||||
}
|
||||
|
||||
/* Do not redraw table when showing tx. the
|
||||
@@ -336,32 +342,46 @@ namespace lwcli { namespace view
|
||||
if (!tx_history)
|
||||
throw std::runtime_error{"unexpected history nullptr"};
|
||||
|
||||
std::map<std::pair<std::uint64_t, std::string>, std::pair<const Monero::TransactionInfo*, std::size_t>, std::greater<>> ordered;
|
||||
tx_history->refresh();
|
||||
std::vector<std::vector<std::string>> rows = base_rows;
|
||||
|
||||
std::uint64_t balance = 0;
|
||||
|
||||
const auto history = tx_history->getAll();
|
||||
for (auto it = history.rbegin(); it != history.rend(); ++it)
|
||||
for (std::size_t i = 0; i < history.size(); ++i)
|
||||
{
|
||||
const Monero::TransactionInfo* tx = *it;
|
||||
const Monero::TransactionInfo* tx = history[i];
|
||||
if (!tx)
|
||||
throw std::runtime_error{"unexpected tx_info nullptr"};
|
||||
|
||||
if (tx->subaddrAccount() != account_)
|
||||
continue;
|
||||
if (tx->subaddrAccount() == account_)
|
||||
ordered.try_emplace({tx->blockHeight(), tx->hash()}, tx, i);
|
||||
}
|
||||
|
||||
std::uint64_t balance = 0;
|
||||
std::vector<std::vector<std::string>> rows;
|
||||
rows.reserve(ordered.size() + 2);
|
||||
rows = base_rows;
|
||||
|
||||
std::size_t i = 0;
|
||||
row_map_.clear();
|
||||
for (const auto& entry : ordered)
|
||||
{
|
||||
const Monero::TransactionInfo* tx = entry.second.first;
|
||||
row_map_.emplace(i, entry.second.second);
|
||||
|
||||
const std::uint64_t amount = tx->amount();
|
||||
const int direction = tx->direction();
|
||||
const std::uint64_t fee = tx->fee();
|
||||
|
||||
if (direction == Monero::TransactionInfo::Direction_Out)
|
||||
|
||||
if (!tx->isFailed())
|
||||
{
|
||||
balance -= amount;
|
||||
balance -= fee;
|
||||
if (direction == Monero::TransactionInfo::Direction_Out)
|
||||
{
|
||||
balance -= amount;
|
||||
balance -= fee;
|
||||
}
|
||||
else
|
||||
balance += amount;
|
||||
}
|
||||
else
|
||||
balance += amount;
|
||||
|
||||
std::tm expanded{};
|
||||
const std::time_t timestamp = tx->timestamp();
|
||||
@@ -386,15 +406,16 @@ namespace lwcli { namespace view
|
||||
print_money(tx->fee()),
|
||||
tx->hash().substr(0, 16)
|
||||
});
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
row_count_ = rows.size();
|
||||
ftxui::Table table{std::move(rows)};
|
||||
table.SelectRow(0).Decorate(ftxui::bold);
|
||||
table.SelectRow(0).SeparatorVertical(ftxui::LIGHT);
|
||||
table.SelectRow(0).Border(ftxui::LIGHT);
|
||||
table.SelectColumn(3).DecorateCells(ftxui::xflex_grow);
|
||||
if (min_row() <= highlighted_ && highlighted_ < row_count_)
|
||||
//table.SelectColumn(3).DecorateCells(ftxui::xflex_grow);
|
||||
if (min_row() <= highlighted_ && highlighted_ - min_row() < row_map_.size())
|
||||
{
|
||||
auto row = table.SelectRow(highlighted_);
|
||||
row.Decorate(ftxui::inverted);
|
||||
@@ -407,7 +428,8 @@ namespace lwcli { namespace view
|
||||
table_ = ftxui::vbox({
|
||||
title_,
|
||||
ftxui::text(_("Balance: ") + print_money(balance)),
|
||||
ftxui::yframe(ftxui::vscroll_indicator(table.Render()))
|
||||
table.Render() | ftxui::vscroll_indicator | ftxui::yframe | ftxui::center | ftxui::flex
|
||||
//ftxui::flex(ftxui::yframe(ftxui::vscroll_indicator(table.Render())))
|
||||
});
|
||||
return table_;
|
||||
}
|
||||
|
||||
+110
-69
@@ -58,11 +58,21 @@ namespace lwcli { namespace view
|
||||
}
|
||||
};
|
||||
|
||||
ftxui::Component password(std::string* pass)
|
||||
std::string get_home()
|
||||
{
|
||||
const char* home = std::getenv("HOME");
|
||||
if (home)
|
||||
return home;
|
||||
return {};
|
||||
}
|
||||
|
||||
ftxui::Component password(std::string* pass, std::function<void()> on_enter = nullptr)
|
||||
{
|
||||
auto opt = ftxui::InputOption::Default();
|
||||
opt.password = true;
|
||||
opt.multiline = false;
|
||||
if (on_enter)
|
||||
opt.on_enter = std::move(on_enter);
|
||||
return ftxui::Input(pass, std::move(opt));
|
||||
}
|
||||
|
||||
@@ -94,13 +104,9 @@ namespace lwcli { namespace view
|
||||
std::string file;
|
||||
std::string password;
|
||||
|
||||
wallet_base()
|
||||
: file(), password()
|
||||
{
|
||||
const char* home = std::getenv("HOME");
|
||||
if (home)
|
||||
file = home;
|
||||
}
|
||||
wallet_base(std::string default_file)
|
||||
: file(std::move(default_file)), password()
|
||||
{}
|
||||
};
|
||||
|
||||
struct new_wallet : wallet_base
|
||||
@@ -111,8 +117,8 @@ namespace lwcli { namespace view
|
||||
std::string proxy;
|
||||
bool ssl;
|
||||
|
||||
new_wallet()
|
||||
: wallet_base(), confirm(), language(config::default_language), server(config::server::default_url), proxy(), ssl(false)
|
||||
new_wallet(std::string default_file)
|
||||
: wallet_base(std::move(default_file)), confirm(), language(config::default_language), server(config::server::default_url), proxy(), ssl(false)
|
||||
{}
|
||||
|
||||
void setup(Monero::Wallet& wal)
|
||||
@@ -136,7 +142,7 @@ namespace lwcli { namespace view
|
||||
{}
|
||||
};
|
||||
|
||||
std::optional<std::uint64_t> from_string(const std::string& str) noexcept
|
||||
std::optional<std::uint64_t> from_string(const std::string_view str) noexcept
|
||||
{
|
||||
std::uint64_t height = 0;
|
||||
const auto end = str.data() + str.size();
|
||||
@@ -159,57 +165,63 @@ namespace lwcli { namespace view
|
||||
|
||||
if (!wal.init(wal.getCacheAttribute(std::string{config::server::url}), 0, "", "", *ssl, true, wal.getCacheAttribute(std::string{config::server::proxy})))
|
||||
{
|
||||
*error = wal.errorString();
|
||||
*error = "Failure to initialize" + wal.errorString();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::pair<std::vector<std::pair<ftxui::Element, ftxui::Component>>, ftxui::Component> get_load_options(start_state* state)
|
||||
using option_set = std::pair<std::vector<std::pair<ftxui::Element, ftxui::Component>>, ftxui::Component>;
|
||||
|
||||
option_set get_load_options(std::string default_file, start_state* state)
|
||||
{
|
||||
struct options
|
||||
{
|
||||
start_state* state;
|
||||
wallet_base config;
|
||||
|
||||
options(start_state* state)
|
||||
: state(state), config()
|
||||
options(std::string default_file, start_state* state)
|
||||
: state(state), config(std::move(default_file))
|
||||
{}
|
||||
};
|
||||
auto enclosed = std::make_shared<options>(state);
|
||||
auto load = ftxui::Button("Load", [enclosed] () {
|
||||
auto enclosed = std::make_shared<options>(std::move(default_file), state);
|
||||
const auto load_action = [enclosed] () {
|
||||
auto prepped = prep_wallet(
|
||||
enclosed->state->wm,
|
||||
enclosed->state->wm->openWallet(enclosed->config.file, enclosed->config.password, Monero::TESTNET),
|
||||
enclosed->state->wm->openWallet(enclosed->config.file, enclosed->config.password, config::network),
|
||||
&enclosed->state->error
|
||||
);
|
||||
if (prepped)
|
||||
{
|
||||
enclosed->config.password.clear();
|
||||
init_wallet(*prepped, &enclosed->state->error);
|
||||
enclosed->state->wal = prepped;
|
||||
if (init_wallet(*prepped, &enclosed->state->error))
|
||||
{
|
||||
enclosed->config.password.clear();
|
||||
prepped->startRefresh();
|
||||
enclosed->state->wal = prepped;
|
||||
}
|
||||
}
|
||||
}, ftxui::ButtonOption::Ascii());
|
||||
};
|
||||
|
||||
return {
|
||||
{
|
||||
{ftxui::text(_("Filename: ")), last_input(&enclosed->config.file)},
|
||||
{ftxui::text(_("Password: ")), password(&enclosed->config.password)}
|
||||
{ftxui::text(_("Password: ")), password(&enclosed->config.password, load_action)}
|
||||
},
|
||||
load
|
||||
ftxui::Button(_("Load"), load_action, ftxui::ButtonOption::Ascii())
|
||||
};
|
||||
}
|
||||
std::pair<std::vector<std::pair<ftxui::Element, ftxui::Component>>, ftxui::Component> get_create_options(start_state* state)
|
||||
option_set get_create_options(std::string default_file, start_state* state)
|
||||
{
|
||||
struct options
|
||||
{
|
||||
start_state* state;
|
||||
new_wallet config;
|
||||
|
||||
options(start_state* state)
|
||||
: state(state), config()
|
||||
options(std::string default_file, start_state* state)
|
||||
: state(state), config(std::move(default_file))
|
||||
{}
|
||||
};
|
||||
auto enclosed = std::make_shared<options>(state);
|
||||
auto enclosed = std::make_shared<options>(std::move(default_file), state);
|
||||
auto create = ftxui::Button("Create", [enclosed] () {
|
||||
if (!enclosed->config.file.empty())
|
||||
{
|
||||
@@ -223,19 +235,21 @@ namespace lwcli { namespace view
|
||||
{
|
||||
auto prepped = prep_wallet(
|
||||
enclosed->state->wm,
|
||||
enclosed->state->wm->createWallet(enclosed->config.file, enclosed->config.password, enclosed->config.language, Monero::TESTNET),
|
||||
enclosed->state->wm->createWallet(enclosed->config.file, enclosed->config.password, enclosed->config.language, config::network),
|
||||
&enclosed->state->error
|
||||
);
|
||||
if (prepped)
|
||||
{
|
||||
if (prepped->store({}))
|
||||
{
|
||||
enclosed->config.password.clear();
|
||||
enclosed->config.confirm.clear();
|
||||
enclosed->config.setup(*prepped);
|
||||
init_wallet(*prepped, &enclosed->state->error);
|
||||
enclosed->state->overlay = view::keys(prepped, true /* show warning */);
|
||||
enclosed->state->wal = prepped;
|
||||
if (init_wallet(*prepped, &enclosed->state->error))
|
||||
{
|
||||
enclosed->config.password.clear();
|
||||
enclosed->config.confirm.clear();
|
||||
enclosed->state->overlay = view::keys(prepped, true /* show warning */);
|
||||
enclosed->state->wal = prepped;
|
||||
}
|
||||
}
|
||||
else
|
||||
enclosed->state->error = "Unable to create file: " + prepped->errorString();
|
||||
@@ -253,14 +267,14 @@ namespace lwcli { namespace view
|
||||
{ftxui::text(_("Password: ")), password(&enclosed->config.password)},
|
||||
{ftxui::text(_("Confirm: ")), password(&enclosed->config.confirm)},
|
||||
{ftxui::text(_("Language: ")), last_input(&enclosed->config.language)},
|
||||
{ftxui::text(_("LWS Server: ")), last_input(&enclosed->config.server)},
|
||||
{ftxui::text(_("API Server: ")), last_input(&enclosed->config.server)},
|
||||
{ftxui::text(_("Proxy: ")), last_input(&enclosed->config.proxy)},
|
||||
{ftxui::text(_("Server Options: ")), ftxui::Checkbox(_("SSL"), &enclosed->config.ssl)}
|
||||
{ftxui::text(_("Options: ")), ftxui::Checkbox(_("TLS/SSL Cert Check"), &enclosed->config.ssl)}
|
||||
},
|
||||
create
|
||||
};
|
||||
}
|
||||
std::pair<std::vector<std::pair<ftxui::Element, ftxui::Component>>, ftxui::Component> get_seed_options(start_state* state)
|
||||
option_set get_seed_options(std::string default_file, start_state* state)
|
||||
{
|
||||
struct options
|
||||
{
|
||||
@@ -269,11 +283,11 @@ namespace lwcli { namespace view
|
||||
std::string height;
|
||||
new_wallet config;
|
||||
|
||||
options(start_state* state)
|
||||
: state(state), mnemonic(), height("0"), config()
|
||||
options(std::string default_file, start_state* state)
|
||||
: state(state), mnemonic(), height("0"), config(std::move(default_file))
|
||||
{}
|
||||
};
|
||||
auto enclosed = std::make_shared<options>(state);
|
||||
auto enclosed = std::make_shared<options>(std::move(default_file), state);
|
||||
auto recover = ftxui::Button("Recover", [enclosed] () {
|
||||
const auto height = from_string(enclosed->height);
|
||||
if (!height)
|
||||
@@ -294,18 +308,22 @@ namespace lwcli { namespace view
|
||||
{
|
||||
auto prepped = prep_wallet(
|
||||
enclosed->state->wm,
|
||||
enclosed->state->wm->recoveryWallet(enclosed->config.file, enclosed->config.password, enclosed->mnemonic, Monero::TESTNET, *height),
|
||||
enclosed->state->wm->recoveryWallet(enclosed->config.file, enclosed->config.password, enclosed->mnemonic, config::network, *height),
|
||||
&enclosed->state->error
|
||||
);
|
||||
if (prepped)
|
||||
{
|
||||
if (prepped->store({}))
|
||||
{
|
||||
enclosed->config.password.clear();
|
||||
enclosed->config.confirm.clear();
|
||||
enclosed->config.setup(*prepped);
|
||||
init_wallet(*prepped, &enclosed->state->error);
|
||||
enclosed->state->wal = prepped;
|
||||
if (init_wallet(*prepped, &enclosed->state->error))
|
||||
{
|
||||
enclosed->mnemonic.clear();
|
||||
enclosed->config.password.clear();
|
||||
enclosed->config.confirm.clear();
|
||||
prepped->rescanBlockchainAsync();
|
||||
enclosed->state->wal = prepped;
|
||||
}
|
||||
}
|
||||
else
|
||||
enclosed->state->error = "Unable to create file: " + prepped->errorString();
|
||||
@@ -324,15 +342,15 @@ namespace lwcli { namespace view
|
||||
{ftxui::text(_("Confirm: ")), password(&enclosed->config.confirm)},
|
||||
{ftxui::text(_("Mnemonic: ")), last_input(&enclosed->mnemonic)},
|
||||
{ftxui::text(_("Height: ")), last_input(&enclosed->height)},
|
||||
{ftxui::text(_("LWS Server: ")), last_input(&enclosed->config.server)},
|
||||
{ftxui::text(_("API Server: ")), last_input(&enclosed->config.server)},
|
||||
{ftxui::text(_("Proxy: ")), last_input(&enclosed->config.proxy)},
|
||||
{ftxui::text(_("Server Options: ")), ftxui::Checkbox(_("SSL"), &enclosed->config.ssl)}
|
||||
{ftxui::text(_("Options: ")), ftxui::Checkbox(_("TLS/SSL Cert Check"), &enclosed->config.ssl)}
|
||||
},
|
||||
recover
|
||||
};
|
||||
}
|
||||
|
||||
std::pair<std::vector<std::pair<ftxui::Element, ftxui::Component>>, ftxui::Component> get_key_options(start_state* state)
|
||||
option_set get_key_options(std::string default_file, start_state* state)
|
||||
{
|
||||
struct options
|
||||
{
|
||||
@@ -341,12 +359,12 @@ namespace lwcli { namespace view
|
||||
std::string height;
|
||||
new_wallet config;
|
||||
|
||||
options(start_state* state)
|
||||
: state(state), spend_key(), height("0"), config()
|
||||
options(std::string default_file, start_state* state)
|
||||
: state(state), spend_key(), height("0"), config(std::move(default_file))
|
||||
{}
|
||||
};
|
||||
auto enclosed = std::make_shared<options>(state);
|
||||
auto recover = ftxui::Button("Recover (Broken)", [enclosed] () {
|
||||
auto enclosed = std::make_shared<options>(std::move(default_file), state);
|
||||
auto recover = ftxui::Button(_("Recover (Broken)"), [enclosed] () {
|
||||
const auto height = from_string(enclosed->height);
|
||||
if (!height)
|
||||
{
|
||||
@@ -370,7 +388,7 @@ namespace lwcli { namespace view
|
||||
enclosed->config.file,
|
||||
enclosed->config.password,
|
||||
enclosed->config.language,
|
||||
Monero::TESTNET,
|
||||
config::network,
|
||||
*height,
|
||||
"address",
|
||||
"view_key",
|
||||
@@ -382,11 +400,15 @@ namespace lwcli { namespace view
|
||||
{
|
||||
if (prepped->store({}))
|
||||
{
|
||||
enclosed->config.password.clear();
|
||||
enclosed->config.confirm.clear();
|
||||
enclosed->config.setup(*prepped);
|
||||
init_wallet(*prepped, &enclosed->state->error);
|
||||
enclosed->state->wal = prepped;
|
||||
if (init_wallet(*prepped, &enclosed->state->error))
|
||||
{
|
||||
enclosed->spend_key.clear();
|
||||
enclosed->config.password.clear();
|
||||
enclosed->config.confirm.clear();
|
||||
prepped->rescanBlockchainAsync();
|
||||
enclosed->state->wal = prepped;
|
||||
}
|
||||
}
|
||||
else
|
||||
enclosed->state->error = "Unable to create file: " + prepped->errorString();
|
||||
@@ -406,9 +428,9 @@ namespace lwcli { namespace view
|
||||
{ftxui::text(_("Spend key: ")), last_input(&enclosed->spend_key)},
|
||||
{ftxui::text(_("Height: ")), last_input(&enclosed->height)},
|
||||
{ftxui::text(_("Language: ")), last_input(&enclosed->config.language)},
|
||||
{ftxui::text(_("LWS Server: ")), last_input(&enclosed->config.server)},
|
||||
{ftxui::text(_("API Server: ")), last_input(&enclosed->config.server)},
|
||||
{ftxui::text(_("Proxy: ")), last_input(&enclosed->config.proxy)},
|
||||
{ftxui::text(_("Connection Options: ")), ftxui::Checkbox(_("SSL"), &enclosed->config.ssl)}
|
||||
{ftxui::text(_("Options: ")), ftxui::Checkbox(_("TLS/SSL Cert Check"), &enclosed->config.ssl)}
|
||||
},
|
||||
recover
|
||||
};
|
||||
@@ -421,6 +443,7 @@ namespace lwcli { namespace view
|
||||
const ftxui::Element help_;
|
||||
const ftxui::Element disclaimer_;
|
||||
start_state state_;
|
||||
const std::string default_file_;
|
||||
const std::vector<std::string> options_;
|
||||
int active_;
|
||||
int selected_;
|
||||
@@ -437,16 +460,24 @@ namespace lwcli { namespace view
|
||||
return ui_;
|
||||
}
|
||||
|
||||
static int get_selected(const std::string& file)
|
||||
{
|
||||
// default to load file if exists, and create if does not exist
|
||||
std::error_code ec{};
|
||||
return file.empty() || std::filesystem::exists(file, ec) ? 0 : 1;
|
||||
}
|
||||
|
||||
public:
|
||||
explicit start(std::shared_ptr<Monero::WalletManager>&& wm, std::shared_ptr<Monero::Wallet>* out)
|
||||
explicit start(std::shared_ptr<Monero::WalletManager>&& wm, std::string&& file, std::shared_ptr<Monero::Wallet>* out)
|
||||
: out_(out),
|
||||
title_(ftxui::text("wmcli")),
|
||||
help_(decorate::banner(ftxui::text(_("Ctrl-Q to close active window, Ctrl-C close app immediately")))),
|
||||
disclaimer_(decorate::banner(ftxui::text(_("Beware of mouse events in Tmux/Screen")))),
|
||||
state_(std::move(wm)),
|
||||
default_file_(std::move(file)),
|
||||
options_({_("Load Wallet"), _("Create Wallet"), _("Recover from Seed"), _("Recover from Keys")}),
|
||||
active_(-1),
|
||||
selected_(0),
|
||||
selected_(get_selected(default_file_)),
|
||||
mode_(ftxui::Dropdown(&options_, &selected_)),
|
||||
completion_(),
|
||||
stack_(),
|
||||
@@ -459,21 +490,22 @@ namespace lwcli { namespace view
|
||||
{
|
||||
if (active_ != selected_)
|
||||
{
|
||||
const bool first_load = active_ == -1;
|
||||
active_ = selected_;
|
||||
switch(active_)
|
||||
{
|
||||
case 0:
|
||||
default:
|
||||
std::tie(stack_, completion_) = get_load_options(&state_);
|
||||
std::tie(stack_, completion_) = get_load_options((default_file_.empty() ? get_home() : default_file_), &state_);
|
||||
break;
|
||||
case 1:
|
||||
std::tie(stack_, completion_) = get_create_options(&state_);
|
||||
std::tie(stack_, completion_) = get_create_options((default_file_.empty() ? get_home() : default_file_), &state_);
|
||||
break;
|
||||
case 2:
|
||||
std::tie(stack_, completion_) = get_seed_options(&state_);
|
||||
std::tie(stack_, completion_) = get_seed_options((default_file_.empty() ? get_home() : default_file_), &state_);
|
||||
break;
|
||||
case 3:
|
||||
std::tie(stack_, completion_) = get_key_options(&state_);
|
||||
std::tie(stack_, completion_) = get_key_options((default_file_.empty() ? get_home() : default_file_), &state_);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -485,6 +517,15 @@ namespace lwcli { namespace view
|
||||
ui.push_back(completion_);
|
||||
|
||||
ui_ = ftxui::Container::Vertical(std::move(ui));
|
||||
|
||||
// skip directly to password on init if default file given
|
||||
if (first_load)
|
||||
{
|
||||
if (!default_file_.empty())
|
||||
stack_.at(1).second->TakeFocus(); // jump to password field (load or create wallet)
|
||||
else
|
||||
stack_.at(0).second->TakeFocus(); // jump tp file (load wallet)
|
||||
}
|
||||
}
|
||||
|
||||
// Delay showing wallet if options required overlay
|
||||
@@ -566,10 +607,10 @@ namespace lwcli { namespace view
|
||||
}
|
||||
|
||||
public:
|
||||
explicit manager_(std::shared_ptr<Monero::WalletManager>&& wm)
|
||||
explicit manager_(std::shared_ptr<Monero::WalletManager>&& wm, std::string&& file)
|
||||
: ftxui::ComponentBase(),
|
||||
data_(nullptr),
|
||||
start_(std::make_shared<start>(std::move(wm), &data_)),
|
||||
start_(std::make_shared<start>(std::move(wm), std::move(file), &data_)),
|
||||
wallet_(nullptr)
|
||||
{}
|
||||
|
||||
@@ -602,10 +643,10 @@ namespace lwcli { namespace view
|
||||
};
|
||||
}
|
||||
|
||||
ftxui::Component manager(std::shared_ptr<Monero::WalletManager> wm)
|
||||
ftxui::Component manager(std::shared_ptr<Monero::WalletManager> wm, std::string&& file)
|
||||
{
|
||||
if (!wm)
|
||||
throw std::runtime_error{"lwcli::view::manaager given nullptr"};
|
||||
return std::make_shared<manager_>(std::move(wm));
|
||||
return std::make_shared<manager_>(std::move(wm), std::move(file));
|
||||
}
|
||||
}} // lwcli // view
|
||||
|
||||
+2
-1
@@ -29,10 +29,11 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <ftxui/component/component_base.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace Monero { class WalletManager; }
|
||||
namespace lwcli { namespace view
|
||||
{
|
||||
ftxui::Component manager(std::shared_ptr<Monero::WalletManager> wm);
|
||||
ftxui::Component manager(std::shared_ptr<Monero::WalletManager> wm, std::string&& file);
|
||||
}} // lwscli // view
|
||||
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
// Copyright (c) 2025, Cifro Codes LLC
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "wallet.h"
|
||||
|
||||
#include <charconv>
|
||||
#include <ftxui/component/component.hpp>
|
||||
#include <ftxui/component/event.hpp>
|
||||
#include <ftxui/dom/table.hpp>
|
||||
#include <lws_frontend.h>
|
||||
|
||||
#include "decorate/overlay.h"
|
||||
#include "events.h"
|
||||
#include "lwcli_config.h"
|
||||
#include "translate.h"
|
||||
#include "views/history.h"
|
||||
|
||||
|
||||
namespace lwcli { namespace view
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::optional<std::uint64_t> from_string(const std::string_view str) noexcept
|
||||
{
|
||||
std::uint64_t out = 0;
|
||||
const auto end = str.data() + str.size();
|
||||
auto [ptr, ec] = std::from_chars(str.data(), end, out);
|
||||
if (bool(ec) || ptr != end)
|
||||
return std::nullopt;
|
||||
return out;
|
||||
}
|
||||
|
||||
bool set_proxy(Monero::Wallet& wal, const std::string& proxy)
|
||||
{
|
||||
wal.setProxy(proxy);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool set_url(Monero::Wallet& wal, const std::string& url)
|
||||
{
|
||||
const bool is_ssl = bool(from_string(wal.getCacheAttribute(std::string{config::server::ssl})));
|
||||
wal.init(url, 0, "", "", is_ssl, true, wal.getCacheAttribute(std::string{config::server::proxy}));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool set_refresh(Monero::Wallet& wal, const std::string& interval)
|
||||
{
|
||||
const auto number = from_string(interval);
|
||||
if (!number)
|
||||
return false;
|
||||
wal.setAutoRefreshInterval(std::chrono::milliseconds{std::chrono::seconds{*number}}.count());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool set_ssl(Monero::Wallet& wal, const std::string& ssl)
|
||||
{
|
||||
const auto is_ssl = from_string(ssl);
|
||||
if (!is_ssl)
|
||||
return false;
|
||||
|
||||
// no way to forcibly select ssl without another init call. hopefully doesn't break things
|
||||
wal.init(wal.getCacheAttribute(std::string{config::server::url}), 0, "", "", bool(*is_ssl), true, wal.getCacheAttribute(std::string{config::server::proxy}));
|
||||
return true;
|
||||
}
|
||||
|
||||
struct option
|
||||
{
|
||||
using updater = bool(Monero::Wallet&, const std::string&);
|
||||
const std::string_view path;
|
||||
const std::string_view description;
|
||||
updater* const update;
|
||||
};
|
||||
|
||||
const std::array<option, 4> options{{
|
||||
{config::server::proxy, _("Proxy"), set_proxy},
|
||||
{config::server::refresh_interval, _("Refresh Interval (seconds)"), set_refresh},
|
||||
{config::server::ssl, _("TLS/SSL Cert Check"), set_ssl},
|
||||
{config::server::url, _("API Server"), set_url}
|
||||
}};
|
||||
|
||||
ftxui::Component last_input(std::string* str)
|
||||
{
|
||||
auto opt = ftxui::InputOption::Default();
|
||||
opt.cursor_position = str->size();
|
||||
opt.multiline = false;
|
||||
return ftxui::Input(str, std::move(opt));
|
||||
}
|
||||
|
||||
ftxui::ButtonOption ascii() { return ftxui::ButtonOption::Ascii(); }
|
||||
|
||||
struct option_state
|
||||
{
|
||||
ftxui::Element description;
|
||||
std::string original;
|
||||
std::string value;
|
||||
ftxui::Component ui;
|
||||
};
|
||||
|
||||
struct configuration
|
||||
{
|
||||
std::array<option_state, options.size()> states;
|
||||
|
||||
configuration(const Monero::Wallet& wal)
|
||||
: states()
|
||||
{
|
||||
for (std::size_t i = 0; i < options.size(); ++i)
|
||||
{
|
||||
states[i].description = ftxui::text(std::string{options[i].description} + ": ");
|
||||
states[i].original = wal.getCacheAttribute(std::string{options[i].path});
|
||||
states[i].value = states[i].original;
|
||||
states[i].ui = last_input(&states[i].value);
|
||||
}
|
||||
}
|
||||
|
||||
void store(Monero::Wallet& wal, std::string& error)
|
||||
{
|
||||
for (std::size_t i = 0; i < options.size(); ++i)
|
||||
{
|
||||
if (states[i].original != states[i].value)
|
||||
{
|
||||
if (!options[i].update || options[i].update(wal, states[i].value))
|
||||
wal.setCacheAttribute(std::string{options[i].path}, states[i].value);
|
||||
else
|
||||
{
|
||||
error = std::string{options[i].description} + " is invalid";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class settings_ final : public ftxui::ComponentBase
|
||||
{
|
||||
const std::shared_ptr<Monero::Wallet> wal_;
|
||||
const ftxui::Element title_;
|
||||
const std::unique_ptr<configuration> config_;
|
||||
std::string error_;
|
||||
ftxui::Component buttons_;
|
||||
ftxui::Component ui_;
|
||||
|
||||
bool Focusable() const override final { return true; }
|
||||
ftxui::Component ActiveChild() override final { return ui_; }
|
||||
|
||||
public:
|
||||
explicit settings_(std::shared_ptr<Monero::Wallet>&& wal)
|
||||
: ftxui::ComponentBase(),
|
||||
wal_(std::move(wal)),
|
||||
title_(ftxui::text(_("Settings"))),
|
||||
config_(std::make_unique<configuration>(*wal_)),
|
||||
error_(),
|
||||
buttons_(),
|
||||
ui_()
|
||||
{
|
||||
buttons_ = ftxui::Container::Horizontal({
|
||||
ftxui::Button(_("Cancel"), [] () { throw event::close{}; }, ascii()),
|
||||
ftxui::Button(_("Save"), [this] () {
|
||||
error_.clear();
|
||||
config_->store(*wal_, error_);
|
||||
if (error_.empty())
|
||||
throw event::close{};
|
||||
}, ascii())
|
||||
});
|
||||
|
||||
ftxui::Components ui;
|
||||
ui.reserve(config_->states.size() + 1);
|
||||
ui.push_back(buttons_);
|
||||
for (const auto& opt : config_->states)
|
||||
ui.push_back(opt.ui);
|
||||
|
||||
ui_ = ftxui::Container::Vertical(std::move(ui));
|
||||
Add(ui_);
|
||||
}
|
||||
|
||||
bool OnEvent(ftxui::Event event) override final
|
||||
{
|
||||
if (event == ftxui::Event::CtrlQ)
|
||||
throw event::close{};
|
||||
ui_->OnEvent(std::move(event));
|
||||
return true;
|
||||
}
|
||||
|
||||
ftxui::Element OnRender() override final
|
||||
{
|
||||
const auto min_size = ftxui::size(ftxui::WIDTH, ftxui::GREATER_THAN, 5);
|
||||
std::vector<ftxui::Elements> grid;
|
||||
grid.reserve(config_->states.size());
|
||||
for (const auto& opt : config_->states)
|
||||
grid.push_back({opt.description, min_size(opt.ui->Render())});
|
||||
|
||||
ftxui::Element highlighted;
|
||||
if (error_.empty())
|
||||
highlighted = ftxui::separator();
|
||||
else
|
||||
highlighted = ftxui::inverted(ftxui::text(error_));
|
||||
|
||||
return ftxui::window(title_, ftxui::vbox({
|
||||
ftxui::hcenter(buttons_->Render()),
|
||||
highlighted,
|
||||
ftxui::gridbox(std::move(grid))
|
||||
}));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ftxui::Component settings(std::shared_ptr<Monero::Wallet> wal)
|
||||
{
|
||||
return std::make_shared<settings_>(std::move(wal));
|
||||
}
|
||||
|
||||
}} // lwcli //
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2025, Cifro Codes LLC
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#pragma once
|
||||
|
||||
#include <ftxui/component/component_base.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace Monero { class Wallet; }
|
||||
namespace lwcli { namespace view
|
||||
{
|
||||
ftxui::Component settings(std::shared_ptr<Monero::Wallet> wallet);
|
||||
}} // lwscli // view
|
||||
|
||||
+16
-199
@@ -39,198 +39,14 @@
|
||||
#include "lwcli_config.h"
|
||||
#include "translate.h"
|
||||
#include "views/history.h"
|
||||
#include "views/settings.h"
|
||||
|
||||
namespace lwcli { namespace view
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::optional<std::uint64_t> from_string(const std::string_view str) noexcept
|
||||
{
|
||||
std::uint64_t out = 0;
|
||||
const auto end = str.data() + str.size();
|
||||
auto [ptr, ec] = std::from_chars(str.data(), end, out);
|
||||
if (bool(ec) || ptr != end)
|
||||
return std::nullopt;
|
||||
return out;
|
||||
}
|
||||
|
||||
bool set_proxy(Monero::Wallet& wal, const std::string& proxy)
|
||||
{
|
||||
wal.setProxy(proxy);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool set_url(Monero::Wallet& wal, const std::string& url)
|
||||
{
|
||||
const bool is_ssl = bool(from_string(wal.getCacheAttribute(std::string{config::server::ssl})));
|
||||
wal.init(url, 0, "", "", is_ssl, true, wal.getCacheAttribute(std::string{config::server::proxy}));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool set_refresh(Monero::Wallet& wal, const std::string& interval)
|
||||
{
|
||||
const auto number = from_string(interval);
|
||||
if (!number)
|
||||
return false;
|
||||
wal.setAutoRefreshInterval(std::chrono::milliseconds{std::chrono::seconds{*number}}.count());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool set_ssl(Monero::Wallet& wal, const std::string& ssl)
|
||||
{
|
||||
const auto is_ssl = from_string(ssl);
|
||||
if (!is_ssl)
|
||||
return false;
|
||||
|
||||
// no way to forcibly select ssl without another init call. hopefully doesn't break things
|
||||
wal.init(wal.getCacheAttribute(std::string{config::server::url}), 0, "", "", bool(*is_ssl), true, wal.getCacheAttribute(std::string{config::server::proxy}));
|
||||
return true;
|
||||
}
|
||||
|
||||
struct option
|
||||
{
|
||||
using updater = bool(Monero::Wallet&, const std::string&);
|
||||
const std::string_view path;
|
||||
const std::string_view description;
|
||||
updater* const update;
|
||||
};
|
||||
|
||||
const std::array<option, 4> options{{
|
||||
{config::server::proxy, _("LWS Proxy"), set_proxy},
|
||||
{config::server::refresh_interval, _("LWS Refresh Interval (seconds)"), set_refresh},
|
||||
{config::server::ssl, _("LWS SSL Verify"), set_ssl},
|
||||
{config::server::url, _("LWS URL"), set_url}
|
||||
}};
|
||||
|
||||
ftxui::Component last_input(std::string* str)
|
||||
{
|
||||
auto opt = ftxui::InputOption::Default();
|
||||
opt.cursor_position = str->size();
|
||||
opt.multiline = false;
|
||||
return ftxui::Input(str, std::move(opt));
|
||||
}
|
||||
|
||||
ftxui::ButtonOption ascii() { return ftxui::ButtonOption::Ascii(); }
|
||||
|
||||
struct option_state
|
||||
{
|
||||
ftxui::Element description;
|
||||
std::string original;
|
||||
std::string value;
|
||||
ftxui::Component ui;
|
||||
};
|
||||
|
||||
struct configuration
|
||||
{
|
||||
std::array<option_state, options.size()> states;
|
||||
|
||||
configuration(const Monero::Wallet& wal)
|
||||
: states()
|
||||
{
|
||||
for (std::size_t i = 0; i < options.size(); ++i)
|
||||
{
|
||||
states[i].description = ftxui::text(std::string{options[i].description} + ": ");
|
||||
states[i].original = wal.getCacheAttribute(std::string{options[i].path});
|
||||
states[i].value = states[i].original;
|
||||
states[i].ui = last_input(&states[i].value);
|
||||
}
|
||||
}
|
||||
|
||||
void store(Monero::Wallet& wal, std::string& error)
|
||||
{
|
||||
for (std::size_t i = 0; i < options.size(); ++i)
|
||||
{
|
||||
if (states[i].original != states[i].value)
|
||||
{
|
||||
if (!options[i].update || options[i].update(wal, states[i].value))
|
||||
wal.setCacheAttribute(std::string{options[i].path}, states[i].value);
|
||||
else
|
||||
{
|
||||
error = std::string{options[i].description} + " is invalid";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class settings_ final : public ftxui::ComponentBase
|
||||
{
|
||||
const std::shared_ptr<Monero::Wallet> wal_;
|
||||
const ftxui::Element title_;
|
||||
const std::unique_ptr<configuration> config_;
|
||||
std::string error_;
|
||||
ftxui::Component buttons_;
|
||||
ftxui::Component ui_;
|
||||
|
||||
bool Focusable() const override final { return true; }
|
||||
ftxui::Component ActiveChild() override final { return ui_; }
|
||||
|
||||
public:
|
||||
explicit settings_(std::shared_ptr<Monero::Wallet>&& wal)
|
||||
: ftxui::ComponentBase(),
|
||||
wal_(std::move(wal)),
|
||||
title_(ftxui::text(_("Settings"))),
|
||||
config_(std::make_unique<configuration>(*wal_)),
|
||||
error_(),
|
||||
buttons_(),
|
||||
ui_()
|
||||
{
|
||||
buttons_ = ftxui::Container::Horizontal({
|
||||
ftxui::Button("Cancel", [] () { throw event::close{}; }, ascii()),
|
||||
ftxui::Button("Save", [this] () {
|
||||
error_.clear();
|
||||
config_->store(*wal_, error_);
|
||||
if (error_.empty())
|
||||
throw event::close{};
|
||||
}, ascii())
|
||||
});
|
||||
|
||||
ftxui::Components ui;
|
||||
ui.reserve(config_->states.size() + 1);
|
||||
ui.push_back(buttons_);
|
||||
for (const auto& opt : config_->states)
|
||||
ui.push_back(opt.ui);
|
||||
|
||||
ui_ = ftxui::Container::Vertical(std::move(ui));
|
||||
Add(ui_);
|
||||
}
|
||||
|
||||
bool OnEvent(ftxui::Event event) override final
|
||||
{
|
||||
if (event == ftxui::Event::CtrlQ)
|
||||
throw event::close{};
|
||||
ui_->OnEvent(std::move(event));
|
||||
return true;
|
||||
}
|
||||
|
||||
ftxui::Element OnRender() override final
|
||||
{
|
||||
const auto min_size = ftxui::size(ftxui::WIDTH, ftxui::GREATER_THAN, 5);
|
||||
std::vector<ftxui::Elements> grid;
|
||||
grid.reserve(config_->states.size());
|
||||
for (const auto& opt : config_->states)
|
||||
grid.push_back({opt.description, min_size(opt.ui->Render())});
|
||||
|
||||
ftxui::Element highlighted;
|
||||
if (error_.empty())
|
||||
highlighted = ftxui::separator();
|
||||
else
|
||||
highlighted = ftxui::inverted(ftxui::text(error_));
|
||||
|
||||
return ftxui::window(title_, ftxui::vbox({
|
||||
ftxui::hcenter(buttons_->Render()),
|
||||
highlighted,
|
||||
ftxui::gridbox(std::move(grid))
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
ftxui::Component settings(std::shared_ptr<Monero::Wallet> wal)
|
||||
{
|
||||
return std::make_shared<settings_>(std::move(wal));
|
||||
}
|
||||
|
||||
struct wallet_state
|
||||
{
|
||||
const std::shared_ptr<Monero::Wallet> wal;
|
||||
@@ -308,21 +124,22 @@ namespace lwcli { namespace view
|
||||
state_.overlay->OnEvent(std::move(event));
|
||||
else if (event == ftxui::Event::CtrlQ)
|
||||
throw event::close{};
|
||||
else if (event == ftxui::Event::s || event == ftxui::Event::S)
|
||||
; // state_.overlay = send(state_.wal);
|
||||
else if (event == ftxui::Event::b || event == ftxui::Event::B)
|
||||
; // state_.overlay = book(state_.wal);
|
||||
else if (event == ftxui::Event::a || event == ftxui::Event::a)
|
||||
; // overlay = accounts(state_.wal, &active_account_);
|
||||
else if (event == ftxui::Event::r || event == ftxui::Event::R)
|
||||
; //state_.wal->refreshAsync();
|
||||
else if (event == ftxui::Event::e || event == ftxui::Event::E)
|
||||
state_.overlay = settings(state_.wal);
|
||||
else
|
||||
ui_->OnEvent(std::move(event));
|
||||
else if (!ui_->OnEvent(event))
|
||||
{
|
||||
if (event == ftxui::Event::s || event == ftxui::Event::S)
|
||||
; // state_.overlay = send(state_.wal);
|
||||
else if (event == ftxui::Event::b || event == ftxui::Event::B)
|
||||
; // state_.overlay = book(state_.wal);
|
||||
else if (event == ftxui::Event::a || event == ftxui::Event::a)
|
||||
; // overlay = accounts(state_.wal, &active_account_);
|
||||
else if (event == ftxui::Event::r || event == ftxui::Event::R)
|
||||
state_.wal->refreshAsync();
|
||||
else if (event == ftxui::Event::e || event == ftxui::Event::E)
|
||||
state_.overlay = settings(state_.wal);
|
||||
}
|
||||
|
||||
// if (!has_overlay && state_.overlay)
|
||||
// Add(state_.overlay);
|
||||
if (!has_overlay && state_.overlay)
|
||||
Add(state_.overlay);
|
||||
|
||||
update_account();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user