diff --git a/external/lwsf b/external/lwsf index cf83295..ec98d76 160000 --- a/external/lwsf +++ b/external/lwsf @@ -1 +1 @@ -Subproject commit cf83295348064bae2476cb9fb2e682b387f83a82 +Subproject commit ec98d76b697c02d0eedce50d00e46a361e699222 diff --git a/src/views/history.cpp b/src/views/history.cpp index 8d968d3..0b37b29 100644 --- a/src/views/history.cpp +++ b/src/views/history.cpp @@ -196,8 +196,9 @@ namespace lwcli { namespace view const std::shared_ptr wallet_; ftxui::Component table_; ftxui::Component overlay_; - ftxui::Element title_; ftxui::Element table_cached_; //!< Does not redraw when child is enabled + const std::string title1_; + const std::string title2_; std::unordered_map row_map_; const std::uint32_t account_; @@ -209,27 +210,27 @@ namespace lwcli { namespace view return table_; } + ftxui::Element get_title() const + { + // UI can modify label at any time + return ftxui::text(title1_ + wallet_->getSubaddressLabel(account_, 0) + title2_); + } + public: explicit history_(std::shared_ptr&& wallet, std::uint32_t account) : ftxui::ComponentBase(), wallet_(std::move(wallet)), table_(), overlay_(nullptr), - title_(nullptr), table_cached_(nullptr), + title1_(_("Account #") + std::to_string(account) + " / "), + title2_(" / " + wallet_->address(account, 0).substr(0, 20) + "..."), row_map_(), account_(account) { if (!wallet_) throw std::invalid_argument{"lwcli::view::history given nullptr"}; - - { - std::string address = _("Account #") + std::to_string(account_); - address.append(" / ").append(wallet_->getSubaddressLabel(account_, 0)).append(" / "); - address.append(wallet_->address(account_, 0).substr(0, 20)).append("..."); - title_ = ftxui::text(std::move(address)); - } - + // perform transalation lookup once table_ = component::table( {_("Date"), _("Amount"), _("Payment ID"), _("Label"), _("Desription"), _("Block"), _("Fee"), _("Hash")}, @@ -341,7 +342,7 @@ namespace lwcli { namespace view payment_id.substr(0, 16) + extended_payment_id, std::move(label), tx->description(), - std::to_string(tx->blockHeight()), + tx->isPending() ? "Pending" : tx->isFailed() ? "Failed" : std::to_string(tx->blockHeight()), lwsf::displayAmount(tx->fee()), tx->hash().substr(0, 16) + "..." }); @@ -360,7 +361,7 @@ namespace lwcli { namespace view { auto table = table_->Render(); // compute balance first in callback table_cached_ = ftxui::vbox({ - title_, + get_title(), ftxui::text(_("Balance: ") + lwsf::displayAmount(wallet_->balance(account_))), std::move(table) | ftxui::vscroll_indicator | ftxui::yframe | ftxui::center | ftxui::flex }); diff --git a/src/views/send.cpp b/src/views/send.cpp index 4f3e765..7ff1914 100644 --- a/src/views/send.cpp +++ b/src/views/send.cpp @@ -231,18 +231,21 @@ namespace lwcli { namespace view const std::shared_ptr wm_; const std::shared_ptr wal_; const ftxui::Element title_; + const std::vector priority_names_; std::vector> dests_; std::vector dests_ui_; + unsigned animation_; + int priority_; const ftxui::Decorator min_amount_size; ftxui::Component overlay_; ftxui::Component buttons_; + const ftxui::Component priority_menu_; ftxui::Element error_; ftxui::Component ui_; ftxui::Element cached_; std::future, bool>> oa_; std::future, dest_group, std::string>> tx_; const std::uint32_t account_; - unsigned animation_; bool closing_; bool Focusable() const override final { return true; } @@ -253,24 +256,34 @@ namespace lwcli { namespace view return ui_; } + static ftxui::MenuOption toggle(const int focused) + { + auto options = ftxui::MenuOption::Toggle(); + options.focused_entry = focused; + return options; + } + public: explicit send_(std::shared_ptr&& wm, std::shared_ptr&& wal, const std::uint32_t account) : ftxui::ComponentBase(), wm_(std::move(wm)), wal_(std::move(wal)), title_(ftxui::text(_("Send from account #") + std::to_string(account) + " (" + lwsf::displayAmount(wal_->unlockedBalance(account)) + " XMR available)")), + priority_names_({_("Auto"), _("Unimportant"), _("Normal"), _("Elevated"), _("Priority")}), dests_(), dests_ui_(), + animation_(0), + priority_(2), min_amount_size(ftxui::size(ftxui::WIDTH, ftxui::GREATER_THAN, 5)), overlay_(), buttons_(), + priority_menu_(ftxui::Menu(&priority_names_, &priority_, toggle(priority_))), error_(), ui_(), cached_(), oa_(), tx_(), account_(account), - animation_(0), closing_(false) { buttons_ = ftxui::Container::Horizontal({ @@ -312,8 +325,9 @@ namespace lwcli { namespace view void update_ui() { ftxui::Components ui; - ui.reserve(dests_ui_.size() + 1); + ui.reserve(dests_ui_.size() + 2); ui.push_back(buttons_); + ui.push_back(priority_menu_); for (const auto& e : dests_ui_) ui.push_back(ftxui::Container::Horizontal({std::get<0>(e), std::get<1>(e), std::get<2>(e), std::get<3>(e)})); @@ -341,13 +355,13 @@ namespace lwcli { namespace view for (const auto& dest : dests_) { - const std::uint64_t amount = lwsf::amountFromString(dest->first); - if (!amount) + const std::optional amount = lwsf::amountFromString(dest->first); + if (!amount || *amount == 0) { error_ = ftxui::text("Invalid amount"); return; } - dests.second.push_back(amount); + dests.second.push_back(*amount); if (!lwsf::addressValid(dest->second, wal_->nettype())) { @@ -372,7 +386,7 @@ namespace lwcli { namespace view dests.first.push_back(dest->second); } - const auto tx_construct = [] (std::shared_ptr wal, dest_group dests, const std::uint32_t account) + const auto tx_construct = [] (std::shared_ptr wal, dest_group dests, const std::uint32_t account, const int priority) -> std::tuple, dest_group, std::string> { const auto dispose = [wal] (Monero::PendingTransaction* ptr) @@ -382,7 +396,7 @@ namespace lwcli { namespace view }; std::shared_ptr tx{ - wal->createTransactionMultDest(dests.first, {}, dests.second, 0 /*mixin_count*/, Monero::PendingTransaction::Priority_Default, account), + wal->createTransactionMultDest(dests.first, {}, dests.second, 0 /*mixin_count*/, Monero::PendingTransaction::Priority(priority), account), dispose }; if (!tx) @@ -392,7 +406,7 @@ namespace lwcli { namespace view return {nullptr, {}, tx->errorString()}; }; - tx_ = std::async(std::launch::async, tx_construct, wal_, std::move(dests), account_); + tx_ = std::async(std::launch::async, tx_construct, wal_, std::move(dests), account_, priority_); } bool OnEvent(ftxui::Event event) override final @@ -444,10 +458,10 @@ namespace lwcli { namespace view error_ = ftxui::text(_("OpenAlias record is invalid for ") + std::get<1>(oa)->second); else if (!closing_) { - std::get<1>(oa)->second = std::move(std::get<0>(oa)); + std::get<1>(oa)->second = std::move(std::get<0>(oa)); if (!std::get<2>(oa)) error_ = ftxui::text(_("dnssec verification failure for ") + std::get<1>(oa)->second); - else + else try_construct(); } @@ -482,7 +496,7 @@ namespace lwcli { namespace view if (!overlay_) { ftxui::Elements rows; - rows.reserve(3); // dests_ui_.size() + 2); + rows.reserve(5); if (!animate) rows.push_back(buttons_->Render() | ftxui::hcenter); @@ -492,6 +506,13 @@ namespace lwcli { namespace view else rows.push_back(ftxui::separator()); + if (!animate) + { + rows.push_back(priority_menu_->Render() | ftxui::hcenter); + //rows.push_back(ftxui::hbox({ftxui::filler(), priority_menu_->Render(), ftxui::filler()})); + rows.push_back(ftxui::separator()); + } + if (!closing_) { std::vector> grid; diff --git a/src/views/wallet.cpp b/src/views/wallet.cpp index f935784..ec1214a 100644 --- a/src/views/wallet.cpp +++ b/src/views/wallet.cpp @@ -62,6 +62,7 @@ namespace lwcli { namespace view { const std::shared_ptr wal = state->wal; return ftxui::Container::Horizontal({ + ftxui::Button("[c]lose", [] () { throw event::close{}; }, ascii()), ftxui::Button("[s]end", [state] () { state->overlay = send(state->wm, state->wal, state->selected_account); }, ascii()), ftxui::Button("[a]ccounts", [state] () { state->overlay = accounts(state->wal, &state->selected_account); }, ascii()), ftxui::Button("[r]efresh", [wal] () { wal->refreshAsync(); }, ascii()), @@ -158,7 +159,9 @@ namespace lwcli { namespace view return history_->OnEvent(std::move(event)); else if (!ui_->OnEvent(event)) { - if (event == ftxui::Event::s || event == ftxui::Event::S) + if (event == ftxui::Event::c || event == ftxui::Event::C) + throw event::close{}; + else if (event == ftxui::Event::s || event == ftxui::Event::S) state_.overlay = send(state_.wm, state_.wal, state_.selected_account); else if (event == ftxui::Event::a || event == ftxui::Event::a) state_.overlay = accounts(state_.wal, &state_.selected_account);