Mechanical, behavior-preserving consolidations from the audit. Rendering extractions were kept byte-equivalent; sites that couldn't be made identical were left as-is (noted below). Shared helpers: - util::truncateMiddle(s,maxLen) / (s,front,back) in text_format.h — replaces 6 file-local middle-ellipsis truncators + several inline substr sites (send/receive/transactions/balance_recent_tx/explorer + app.cpp + 3 dialogs). Carries the maxLen<=3 guard, fixing the latent unsigned-underflow copies. - material::LoadingDots() in draw_helpers.h — one animated-ellipsis source for 8 copy-pasted spinner sites (identical GetTime()*3 phase preserved). - Reuse the existing FormatHashrate() in explorer_tab + peers_tab (dropped two inline hashrate ladders). - material::DrawButtonGlassOverlay() — the glass-fill/rim/tactile overlay block shared by TactileButton / TactileSmallButton / schema TactileButton. - material::CollapsibleHeader() — the invisible-button + label + chevron idiom (3 of 5 settings_page sites; RPC/Debug headers skipped — non-identical). - material::GlassCardScope (RAII) — the ChannelsSplit/Indent/DrawGlassPanel/ ChannelsMerge card scaffold (5 settings_page cards; About/send/receive skipped — different padding / logo interleaving). - material::DialogWarningHeader()/DialogConfirmFooter() — warning header + 50/50 Cancel/danger footer across the confirm dialogs; button height moved from a hardcoded 40px into ui.toml (components.overlay-dialog.confirm-btn-height). - File-local enterLowSpec()/exitLowSpec() collapse the 3 low-spec snapshot copies. i18n: wrapped hardcoded English in the shared balance render paths and the whole Lite lifecycle/security section in TR(), with English defaults added to loadBuiltinEnglish() (res/lang/*.json left for the translation tooling — TR falls back to English, so output is unchanged). Full-node + Lite build clean; ctest 1/1; hygiene clean. These are rendering changes — the Settings page (cards/headers/confirm dialogs), all tactile buttons, and the Lite settings section warrant a screenshot check. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
81 lines
2.9 KiB
C++
81 lines
2.9 KiB
C++
// DragonX Wallet - ImGui Edition
|
|
// Copyright 2024-2026 The Hush Developers
|
|
// Released under the GPLv3
|
|
|
|
#include "text_format.h"
|
|
#include "i18n.h"
|
|
|
|
#include <cctype>
|
|
#include <cstdio>
|
|
#include <ctime>
|
|
|
|
namespace dragonx {
|
|
namespace util {
|
|
|
|
namespace {
|
|
std::int64_t secondsAgo(std::int64_t timestamp)
|
|
{
|
|
std::int64_t now = static_cast<std::int64_t>(std::time(nullptr));
|
|
std::int64_t diff = now - timestamp;
|
|
return diff < 0 ? 0 : diff;
|
|
}
|
|
} // namespace
|
|
|
|
std::string formatTimeAgo(std::int64_t timestamp)
|
|
{
|
|
if (timestamp <= 0) return {};
|
|
const std::int64_t diff = secondsAgo(timestamp);
|
|
char buf[48];
|
|
if (diff < 60) std::snprintf(buf, sizeof(buf), tr("time_seconds_ago"), static_cast<int>(diff));
|
|
else if (diff < 3600) std::snprintf(buf, sizeof(buf), tr("time_minutes_ago"), static_cast<int>(diff / 60));
|
|
else if (diff < 86400) std::snprintf(buf, sizeof(buf), tr("time_hours_ago"), static_cast<int>(diff / 3600));
|
|
else std::snprintf(buf, sizeof(buf), tr("time_days_ago"), static_cast<int>(diff / 86400));
|
|
return buf;
|
|
}
|
|
|
|
std::string formatTimeAgoShort(std::int64_t timestamp)
|
|
{
|
|
if (timestamp <= 0) return {};
|
|
const std::int64_t diff = secondsAgo(timestamp);
|
|
char buf[32];
|
|
if (diff < 60) std::snprintf(buf, sizeof(buf), "%llds ago", static_cast<long long>(diff));
|
|
else if (diff < 3600) std::snprintf(buf, sizeof(buf), "%lldm ago", static_cast<long long>(diff / 60));
|
|
else if (diff < 86400) std::snprintf(buf, sizeof(buf), "%lldh ago", static_cast<long long>(diff / 3600));
|
|
else std::snprintf(buf, sizeof(buf), "%lldd ago", static_cast<long long>(diff / 86400));
|
|
return buf;
|
|
}
|
|
|
|
std::string truncateMiddle(const std::string& s, int maxLen)
|
|
{
|
|
// Guard maxLen <= 3 before the unsigned (maxLen - 3): otherwise it wraps and the
|
|
// trailing substr(length - half) throws std::out_of_range on short inputs.
|
|
if (maxLen <= 3 || s.length() <= static_cast<std::size_t>(maxLen)) return s;
|
|
int half = (maxLen - 3) / 2;
|
|
return s.substr(0, half) + "..." + s.substr(s.length() - half);
|
|
}
|
|
|
|
std::string truncateMiddle(const std::string& s, int front, int back)
|
|
{
|
|
if (s.length() <= static_cast<std::size_t>(front + back + 3)) return s;
|
|
return s.substr(0, front) + "..." + s.substr(s.length() - back);
|
|
}
|
|
|
|
bool containsIgnoreCase(std::string_view haystack, std::string_view needle)
|
|
{
|
|
if (needle.empty()) return true;
|
|
if (needle.size() > haystack.size()) return false;
|
|
const auto low = [](char c) { return static_cast<char>(std::tolower(static_cast<unsigned char>(c))); };
|
|
const std::size_t last = haystack.size() - needle.size();
|
|
for (std::size_t i = 0; i <= last; ++i) {
|
|
std::size_t j = 0;
|
|
for (; j < needle.size(); ++j) {
|
|
if (low(haystack[i + j]) != low(needle[j])) break;
|
|
}
|
|
if (j == needle.size()) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // namespace util
|
|
} // namespace dragonx
|