Add a fuzzy mode to the explorer search: a non-numeric, non-full-hash query now filters the list to cached blocks whose hash (or height text) contains the query substring, live as you type. Backed by a new ExplorerBlockCache::searchBlocks() (SQLite LIKE with escaped wildcards), memoized per query so it doesn't hit the DB every frame. Exact queries still navigate precisely: a block height re-anchors the list, and a full 64-char hash is resolved via RPC. Row clicks still open the detail modal. Empty results show "No matching cached blocks". Note: fuzzy matching covers cached (browsed/prefetched) blocks only — the daemon has no partial-hash index — while exact height/hash lookups reach any block. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
75 lines
2.3 KiB
C++
75 lines
2.3 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <map>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
struct sqlite3;
|
|
|
|
namespace dragonx {
|
|
namespace ui {
|
|
|
|
struct ExplorerBlockSummary {
|
|
int height = 0;
|
|
std::string hash;
|
|
int tx_count = 0;
|
|
int size = 0;
|
|
std::int64_t time = 0;
|
|
double difficulty = 0.0;
|
|
};
|
|
|
|
class ExplorerBlockCache {
|
|
public:
|
|
struct SavedTipValidation {
|
|
bool needed = false;
|
|
int height = 0;
|
|
std::string expectedHash;
|
|
};
|
|
|
|
ExplorerBlockCache();
|
|
ExplorerBlockCache(std::string databasePath, std::string legacyJsonPath);
|
|
~ExplorerBlockCache();
|
|
|
|
ExplorerBlockCache(const ExplorerBlockCache&) = delete;
|
|
ExplorerBlockCache& operator=(const ExplorerBlockCache&) = delete;
|
|
|
|
static std::string defaultDatabasePath();
|
|
static std::string defaultLegacyJsonPath();
|
|
|
|
bool ensureOpen();
|
|
bool isOpen() const { return db_ != nullptr; }
|
|
const std::string& databasePath() const { return database_path_; }
|
|
|
|
std::map<int, ExplorerBlockSummary> loadRange(int minHeight, int maxHeight);
|
|
// Fuzzy search over cached blocks: matches when the query is a substring of the height (as text)
|
|
// or the block hash (case-insensitive). Returns newest-first, capped at `limit`.
|
|
std::vector<ExplorerBlockSummary> searchBlocks(const std::string& query, int limit);
|
|
bool storeBlock(const ExplorerBlockSummary& block);
|
|
int cachedBlockCount();
|
|
void clearBlocks();
|
|
|
|
SavedTipValidation prepareValidation(int currentHeight, const std::string& currentBestHash);
|
|
void applySavedTipValidation(const SavedTipValidation& validation,
|
|
const std::string& actualHash,
|
|
int currentHeight,
|
|
const std::string& currentBestHash);
|
|
void updateTip(int height, const std::string& hash);
|
|
|
|
private:
|
|
bool exec(const char* sql);
|
|
std::string getMetaValue(const std::string& key);
|
|
int getMetaInt(const std::string& key, int fallback);
|
|
void setMetaValue(const std::string& key, const std::string& value);
|
|
bool createSchema();
|
|
void migrateLegacyJsonIfNeeded();
|
|
void close();
|
|
|
|
sqlite3* db_ = nullptr;
|
|
std::string database_path_;
|
|
std::string legacy_json_path_;
|
|
};
|
|
|
|
} // namespace ui
|
|
} // namespace dragonx
|