Compare commits
2
Commits
a792a1ec0c
...
408393a547
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
408393a547 | ||
|
|
a80be443d1 |
@@ -302,6 +302,9 @@ public:
|
|||||||
wallet::LiteWalletController* liteWallet() { return lite_wallet_.get(); }
|
wallet::LiteWalletController* liteWallet() { return lite_wallet_.get(); }
|
||||||
// HushChat service (identity + in-memory message store); the Chat tab reads its store.
|
// HushChat service (identity + in-memory message store); the Chat tab reads its store.
|
||||||
chat::ChatService& chatService() { return chat_service_; }
|
chat::ChatService& chatService() { return chat_service_; }
|
||||||
|
// Public forwarder for the UI: the wallet z-address others use to reach this user in chat. Call on
|
||||||
|
// click (not per-frame) — the underlying chatReplyZaddr() may persist a freshly chosen address.
|
||||||
|
std::string chatMyAddress() { return chatReplyZaddr(); }
|
||||||
// Why the Chat tab has no identity yet, so it can show the right prompt instead of always
|
// Why the Chat tab has no identity yet, so it can show the right prompt instead of always
|
||||||
// "unlock your wallet". Locked = encrypted & locked (seed unreadable); Preparing = unlocked and
|
// "unlock your wallet". Locked = encrypted & locked (seed unreadable); Preparing = unlocked and
|
||||||
// deriving the identity (fetch in flight, or waiting on the node / address list); Unavailable =
|
// deriving the identity (fetch in flight, or waiting on the node / address list); Unavailable =
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "history_scan_policy.h"
|
#include "history_scan_policy.h"
|
||||||
#include "rpc/rpc_busy_gate.h"
|
#include "rpc/rpc_busy_gate.h"
|
||||||
#include "../util/logger.h"
|
#include "../util/logger.h"
|
||||||
|
#include "../util/i18n.h" // util::tr for the price-fetch-failed default
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
@@ -1995,7 +1996,7 @@ void NetworkRefreshService::applyPriceRefreshFailure(WalletState& state,
|
|||||||
{
|
{
|
||||||
state.market.price_loading = false;
|
state.market.price_loading = false;
|
||||||
state.market.price_error = errorMessage.empty()
|
state.market.price_error = errorMessage.empty()
|
||||||
? std::string("Price fetch failed")
|
? std::string(util::tr("market_price_fetch_failed"))
|
||||||
: errorMessage;
|
: errorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -916,6 +916,24 @@ void RenderChatTab(App* app)
|
|||||||
if (ImGui::SmallButton(bl.c_str())) s_show_blocked = true;
|
if (ImGui::SmallButton(bl.c_str())) s_show_blocked = true;
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
}
|
}
|
||||||
|
// "Copy my chat address" — the address others use to reach this user. The conversation header
|
||||||
|
// only copies a PEER's address, so without this there's no way to hand out your own. Copies on
|
||||||
|
// click only (chatMyAddress may persist a freshly chosen address).
|
||||||
|
{
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, material::OnSurfaceMedium());
|
||||||
|
// Text-only: SmallButton uses the body font, which has no Material icon glyph (would show "?").
|
||||||
|
if (ImGui::SmallButton(TR("chat_copy_my_address"))) {
|
||||||
|
std::string myAddr = app->chatMyAddress();
|
||||||
|
if (myAddr.empty()) {
|
||||||
|
Notifications::instance().warning(TR("chat_no_my_address"));
|
||||||
|
} else {
|
||||||
|
ImGui::SetClipboardText(myAddr.c_str());
|
||||||
|
Notifications::instance().info(TR("address_copied"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("chat_my_address_tip"));
|
||||||
|
}
|
||||||
const std::string search = s_search;
|
const std::string search = s_search;
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
if (convs.empty()) {
|
if (convs.empty()) {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <chrono>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
namespace dragonx {
|
namespace dragonx {
|
||||||
@@ -1496,7 +1497,18 @@ static void mktDrawPriceHero(const MktCtx& cx)
|
|||||||
// ---- HERO PRICE (large, prominent) ----
|
// ---- HERO PRICE (large, prominent) ----
|
||||||
ImFont* h3 = Type().h3();
|
ImFont* h3 = Type().h3();
|
||||||
std::string priceStr = FormatPrice(heroPrice);
|
std::string priceStr = FormatPrice(heroPrice);
|
||||||
ImU32 priceCol = Success();
|
// Stale-price cue: the aggregate price is kept on a failed/old refresh (it's never cleared), so a
|
||||||
|
// green number can be hours out of date. Flag it amber when the last refresh failed or the
|
||||||
|
// aggregate is >=5 min old. Only when the hero IS the aggregate (an exchange pair has its own feed).
|
||||||
|
const bool haveFetch = market.last_fetch_time.time_since_epoch().count() != 0;
|
||||||
|
const long ageMin = haveFetch
|
||||||
|
? (long)std::chrono::duration_cast<std::chrono::minutes>(
|
||||||
|
std::chrono::steady_clock::now() - market.last_fetch_time).count()
|
||||||
|
: 0;
|
||||||
|
const bool usingAggregate = (heroPrice == market.price_usd);
|
||||||
|
const bool priceStale = usingAggregate && haveFetch &&
|
||||||
|
(!market.price_error.empty() || ageMin >= 5);
|
||||||
|
ImU32 priceCol = priceStale ? Warning() : Success();
|
||||||
DrawTextShadow(dl, h3, h3->LegacySize, ImVec2(cx0, cy), priceCol, priceStr.c_str());
|
DrawTextShadow(dl, h3, h3->LegacySize, ImVec2(cx0, cy), priceCol, priceStr.c_str());
|
||||||
|
|
||||||
// Ticker label after price
|
// Ticker label after price
|
||||||
@@ -1519,6 +1531,29 @@ static void mktDrawPriceHero(const MktCtx& cx)
|
|||||||
dl->AddRectFilled(bMin, bMax, badgeBg, 4.0f * dp);
|
dl->AddRectFilled(bMin, bMax, badgeBg, 4.0f * dp);
|
||||||
dl->AddText(capFont, capFont->LegacySize, ImVec2(bMin.x + badgePadH, bMin.y + badgePadV), chgCol, buf);
|
dl->AddText(capFont, capFont->LegacySize, ImVec2(bMin.x + badgePadH, bMin.y + badgePadV), chgCol, buf);
|
||||||
|
|
||||||
|
// Stale "as of Nh ago" caption + hover tooltip, right of the change badge (cursor saved/restored
|
||||||
|
// so the custom draw-list layout below is undisturbed).
|
||||||
|
if (priceStale) {
|
||||||
|
char age[64];
|
||||||
|
if (ageMin >= 60) snprintf(age, sizeof(age), TR("market_price_stale_hour"), (int)(ageMin / 60));
|
||||||
|
else snprintf(age, sizeof(age), TR("market_price_stale_min"), (int)std::max(1L, ageMin));
|
||||||
|
ImVec2 asz = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, age);
|
||||||
|
float ax = bMax.x + Layout::spacingMd();
|
||||||
|
float limit = cardMax.x - pad - 260.0f * dp; // reserve the trade pill + chart toggles
|
||||||
|
if (ax + asz.x < limit) {
|
||||||
|
dl->AddText(capFont, capFont->LegacySize, ImVec2(ax, bMin.y + badgePadV), Warning(), age);
|
||||||
|
ImVec2 sv = ImGui::GetCursorScreenPos();
|
||||||
|
ImGui::SetCursorScreenPos(ImVec2(ax, bMin.y));
|
||||||
|
ImGui::InvisibleButton("##PriceStale", ImVec2(asz.x, bMax.y - bMin.y));
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
std::string tip = market.price_error.empty()
|
||||||
|
? std::string(TR("market_price_fetch_failed")) : market.price_error;
|
||||||
|
material::Tooltip(TR("market_price_stale_tip"), tip.c_str());
|
||||||
|
}
|
||||||
|
ImGui::SetCursorScreenPos(sv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// (Divider is drawn in the chart block, below the interval buttons.)
|
// (Divider is drawn in the chart block, below the interval buttons.)
|
||||||
|
|
||||||
// ---- TRADE BUTTON (top-right of card) ----
|
// ---- TRADE BUTTON (top-right of card) ----
|
||||||
|
|||||||
@@ -263,6 +263,9 @@ void I18n::loadBuiltinEnglish()
|
|||||||
strings_["chat_jump_latest"] = "Latest";
|
strings_["chat_jump_latest"] = "Latest";
|
||||||
strings_["chat_empty_title"] = "No conversations yet";
|
strings_["chat_empty_title"] = "No conversations yet";
|
||||||
strings_["chat_empty_start"] = "Start one with \"New conversation\".";
|
strings_["chat_empty_start"] = "Start one with \"New conversation\".";
|
||||||
|
strings_["chat_copy_my_address"] = "Copy my chat address";
|
||||||
|
strings_["chat_my_address_tip"] = "Share this address so others can message you";
|
||||||
|
strings_["chat_no_my_address"] = "No chat address yet";
|
||||||
strings_["chat_search"] = "Search conversations";
|
strings_["chat_search"] = "Search conversations";
|
||||||
strings_["chat_no_matches"] = "No conversations match your search.";
|
strings_["chat_no_matches"] = "No conversations match your search.";
|
||||||
strings_["chat_export"] = "Export chat\xE2\x80\xA6";
|
strings_["chat_export"] = "Export chat\xE2\x80\xA6";
|
||||||
@@ -2222,6 +2225,10 @@ void I18n::loadBuiltinEnglish()
|
|||||||
strings_["market_24h_change"] = "24h";
|
strings_["market_24h_change"] = "24h";
|
||||||
strings_["market_price_loading"] = "Loading price data...";
|
strings_["market_price_loading"] = "Loading price data...";
|
||||||
strings_["market_price_unavailable"] = "Price data unavailable";
|
strings_["market_price_unavailable"] = "Price data unavailable";
|
||||||
|
strings_["market_price_fetch_failed"] = "Price fetch failed";
|
||||||
|
strings_["market_price_stale_min"] = "as of %d min ago";
|
||||||
|
strings_["market_price_stale_hour"] = "as of %dh ago";
|
||||||
|
strings_["market_price_stale_tip"] = "Showing the last known price. The latest refresh failed: %s";
|
||||||
strings_["market_refresh_price"] = "Refresh price data";
|
strings_["market_refresh_price"] = "Refresh price data";
|
||||||
strings_["market_trade_on"] = "Trade on %s";
|
strings_["market_trade_on"] = "Trade on %s";
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user