fix: Windows identity, async address creation, mining UI, and chart artifacts

Windows identity:
- Add VERSIONINFO resource (.rc) with ObsidianDragon file description
- Embed application manifest for DPI awareness and shell identity
- Patch libwinpthread/libpthread to remove competing VERSIONINFO
- Set AppUserModelID and HWND property store to override Task Manager cache
- Link patched pthread libs to eliminate "POSIX WinThreads" description

Address creation (+New button):
- Move z_getnewaddress/getnewaddress off UI thread to async worker
- Inject new address into state immediately for instant UI selection
- Trigger background refresh for balance updates

Mining tab:
- Add pool mining dropdown with saved URLs/workers and bookmarks
- Add solo mining log panel from daemon output with chart/log toggle
- Fix toggle button cursor (render after InputTextMultiline)
- Auto-restart miner on pool config change
- Migrate default pool URL to include stratum port

Transactions:
- Sort pending (0-conf) transactions to top of history
- Fall back to timereceived when timestamp is missing

Shutdown:
- Replace blocking sleep_for calls with 100ms polling loops
- Check shutting_down_ flag throughout daemon restart/bootstrap flows
- Reduce daemon stop timeout from 30s to 10s

Other:
- Fix market chart fill artifact (single concave polygon vs per-segment quads)
- Add bootstrap checksum verification state display
- Rename daemon client identifier to ObsidianDragon
This commit is contained in:
dan_s
2026-03-05 22:43:27 -06:00
parent 4b16a2a2c4
commit 653a90de62
20 changed files with 842 additions and 116 deletions

View File

@@ -493,6 +493,7 @@ void App::refreshData()
if (tx.contains("category")) info.type = tx["category"].get<std::string>();
if (tx.contains("amount")) info.amount = tx["amount"].get<double>();
if (tx.contains("time")) info.timestamp = tx["time"].get<int64_t>();
else if (tx.contains("timereceived")) info.timestamp = tx["timereceived"].get<int64_t>();
if (tx.contains("confirmations")) info.confirmations = tx["confirmations"].get<int>();
if (tx.contains("address")) info.address = tx["address"].get<std::string>();
knownTxids.insert(info.txid);
@@ -1408,25 +1409,61 @@ void App::clearBans()
void App::createNewZAddress(std::function<void(const std::string&)> callback)
{
if (!state_.connected || !rpc_) return;
rpc_->z_getNewAddress([this, callback](const json& result) {
std::string addr = result.get<std::string>();
addresses_dirty_ = true;
refreshAddresses();
if (callback) callback(addr);
if (!state_.connected || !rpc_ || !worker_) return;
worker_->post([this, callback]() -> rpc::RPCWorker::MainCb {
std::string addr;
try {
json result = rpc_->call("z_getnewaddress");
addr = result.get<std::string>();
} catch (const std::exception& e) {
DEBUG_LOGF("z_getnewaddress error: %s\n", e.what());
}
return [this, callback, addr]() {
if (!addr.empty()) {
// Inject immediately so UI can select the address next frame
AddressInfo info;
info.address = addr;
info.type = "shielded";
info.balance = 0.0;
state_.z_addresses.push_back(info);
address_list_dirty_ = true;
// Also trigger full refresh to get proper balances
addresses_dirty_ = true;
refreshAddresses();
}
if (callback) callback(addr);
};
});
}
void App::createNewTAddress(std::function<void(const std::string&)> callback)
{
if (!state_.connected || !rpc_) return;
rpc_->getNewAddress([this, callback](const json& result) {
std::string addr = result.get<std::string>();
addresses_dirty_ = true;
refreshAddresses();
if (callback) callback(addr);
if (!state_.connected || !rpc_ || !worker_) return;
worker_->post([this, callback]() -> rpc::RPCWorker::MainCb {
std::string addr;
try {
json result = rpc_->call("getnewaddress");
addr = result.get<std::string>();
} catch (const std::exception& e) {
DEBUG_LOGF("getnewaddress error: %s\n", e.what());
}
return [this, callback, addr]() {
if (!addr.empty()) {
// Inject immediately so UI can select the address next frame
AddressInfo info;
info.address = addr;
info.type = "transparent";
info.balance = 0.0;
state_.t_addresses.push_back(info);
address_list_dirty_ = true;
// Also trigger full refresh to get proper balances
addresses_dirty_ = true;
refreshAddresses();
}
if (callback) callback(addr);
};
});
}