2026-06-18 20:19:13 +03:00

166 lines
6.0 KiB
QML

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Logos.Controls
import Logos.Theme
import "components"
Rectangle {
id: root
color: Theme.palette.background
// Typed replica of the backend; non-null once the plugin is registered,
// `ready` tracks the live connection.
readonly property var backend: logos.module("lez_explorer_ui")
property bool ready: false
// Self-managed navigation history (browser-style back/forward). Each entry
// is a descriptor { type, param?, query? }.
property var history: []
property int histIndex: -1
// --- Navigation API (pages call these via the injected `explorer`) ---
function sameDescriptor(a, b) {
if (!a || !b)
return false;
if (a.type !== b.type)
return false;
if (a.type === "home")
return true;
if (a.type === "search")
return a.query === b.query;
return a.param === b.param;
}
function navigateTo(desc) {
// Navigating to the page we're already on is a no-op — don't grow the
// history (so e.g. Home-while-home doesn't wake the Back button).
var current = root.histIndex >= 0 ? root.history[root.histIndex] : null;
if (root.sameDescriptor(current, desc))
return;
var h = root.history.slice(0, root.histIndex + 1);
h.push(desc);
root.history = h;
root.histIndex = h.length - 1;
root.render();
}
function navigateBlock(id) { root.navigateTo({ type: "block", param: id }); }
function navigateTx(hash) { root.navigateTo({ type: "tx", param: hash }); }
function navigateAccount(id) { root.navigateTo({ type: "account", param: id }); }
function navigateSettings() { root.navigateTo({ type: "settings" }); }
function showSearchResults(results, query) {
root.navigateTo({ type: "search", param: results, query: query });
}
function goHome() { root.navigateTo({ type: "home" }); }
function goBack() { if (root.histIndex > 0) { root.histIndex--; root.render(); } }
function goForward() { if (root.histIndex < root.history.length - 1) { root.histIndex++; root.render(); } }
function render() {
if (root.histIndex < 0)
return;
var d = root.history[root.histIndex];
var props = { explorer: root };
var url = "pages/HomePage.qml";
if (d.type === "block") {
url = "pages/BlockPage.qml";
props.blockId = d.param;
} else if (d.type === "tx") {
url = "pages/TransactionPage.qml";
props.txHash = d.param;
} else if (d.type === "account") {
url = "pages/AccountPage.qml";
props.accountId = d.param;
} else if (d.type === "search") {
url = "pages/SearchResultsPage.qml";
props.results = d.param;
props.query = d.query || "";
} else if (d.type === "settings") {
url = "pages/SettingsPage.qml";
}
pageLoader.setSource(Qt.resolvedUrl(url), props);
navBar.canGoBack = root.histIndex > 0;
navBar.canGoForward = root.histIndex < root.history.length - 1;
}
Connections {
target: logos
function onViewModuleReadyChanged(moduleName, isReady) {
if (moduleName === "lez_explorer_ui")
root.ready = isReady && root.backend !== null;
}
}
Component.onCompleted: {
root.ready = root.backend !== null && logos.isViewModuleReady("lez_explorer_ui");
root.goHome();
}
ColumnLayout {
anchors.fill: parent
anchors.margins: Theme.spacing.large
spacing: Theme.spacing.medium
NavigationBar {
id: navBar
Layout.fillWidth: true
onBackClicked: root.goBack()
onForwardClicked: root.goForward()
onHomeClicked: root.goHome()
onSettingsClicked: root.navigateSettings()
}
SearchBar {
Layout.fillWidth: true
onSearchRequested: function (query) {
if (!root.backend || query.trim().length === 0)
return;
logos.watch(root.backend.search(query), function (results) {
var blocks = results && results.blocks ? results.blocks : [];
var txs = results && results.transactions ? results.transactions : [];
var accts = results && results.accounts ? results.accounts : [];
var total = blocks.length + txs.length + accts.length;
if (total === 1) {
if (blocks.length === 1)
root.navigateBlock(blocks[0].blockId);
else if (txs.length === 1)
root.navigateTx(txs[0].hash);
else
root.navigateAccount(accts[0].accountId);
} else {
root.showSearchResults(results, query);
}
}, function (err) {
root.showSearchResults({ blocks: [], transactions: [], accounts: [] }, query);
});
}
}
Loader {
id: pageLoader
Layout.fillWidth: true
Layout.fillHeight: true
}
}
// Readiness overlay — covers the view until the backend connects.
Rectangle {
anchors.fill: parent
visible: !root.ready
color: Theme.palette.background
ColumnLayout {
anchors.centerIn: parent
spacing: Theme.spacing.medium
BusyIndicator {
running: !root.ready
Layout.alignment: Qt.AlignHCenter
}
LogosText {
text: "Connecting to indexer backend…"
color: Theme.palette.textSecondary
font.pixelSize: Theme.typography.secondaryText
Layout.alignment: Qt.AlignHCenter
}
}
}
}