370 lines
11 KiB
JavaScript
370 lines
11 KiB
JavaScript
|
|
function setRecentTransactionsForWalletState(state) {
|
|||
|
|
recentTransactionsCacheReadId += 1;
|
|||
|
|
latestTransactionsSyncId += 1;
|
|||
|
|
clearRecentTransactionsRows();
|
|||
|
|
stopLatestDashboardTransactionsPolling();
|
|||
|
|
|
|||
|
|
if (state === "registered") {
|
|||
|
|
setRecentTransactionsState("");
|
|||
|
|
resetLatestDashboardTransactionsMemory();
|
|||
|
|
updateLatestDashboardTransactionsPolling();
|
|||
|
|
startBackgroundTransactionSync();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
stopBackgroundTransactionSync();
|
|||
|
|
resetLatestDashboardTransactionsMemory();
|
|||
|
|
if (state === "checking") {
|
|||
|
|
setRecentTransactionsState("Checking address registration before loading transactions.");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (state === "unregistered") {
|
|||
|
|
setRecentTransactionsState("Address registration required before transactions can appear.");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
setRecentTransactionsState("Load a wallet to view recent transactions.");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function resetBalances(message = "Load wallet", statusMessage = "Balances refresh while dashboard is open.") {
|
|||
|
|
activeWalletBalances = [];
|
|||
|
|
if (typeof refreshTransferAssetOptions === "function") {
|
|||
|
|
refreshTransferAssetOptions();
|
|||
|
|
}
|
|||
|
|
if (typeof refreshActiveTransactionFormAfterWalletData === "function") {
|
|||
|
|
refreshActiveTransactionFormAfterWalletData();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (baseBalanceLabel) {
|
|||
|
|
baseBalanceLabel.textContent = `${activeBaseCoin()} Balance`;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (clcBalance) {
|
|||
|
|
clcBalance.textContent = message;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (tokenBalances) {
|
|||
|
|
tokenBalances.innerHTML = `
|
|||
|
|
<div class="token-title">Token Balances</div>
|
|||
|
|
<div class="empty-balance">No wallet loaded.</div>
|
|||
|
|
`;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
dashboardLastBalanceStatus = "";
|
|||
|
|
setBalanceStatus(statusMessage);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function refreshDashboardBalances({ force = false, quiet = false } = {}) {
|
|||
|
|
if (!walletLoaded || !registrationRegistered || currentView !== "dashboard" || balanceRefreshInFlight) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const invoke = window.__TAURI__?.core?.invoke;
|
|||
|
|
const broadcastNode = activeBroadcastNode();
|
|||
|
|
if (!invoke || !broadcastNode) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
balanceRefreshInFlight = true;
|
|||
|
|
const requestId = ++balanceRefreshRequestId;
|
|||
|
|
if (force && !quiet) {
|
|||
|
|
setBalanceStatus("Refreshing balances...");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const balances = await invoke("get_active_wallet_balances", {
|
|||
|
|
broadcastNode
|
|||
|
|
});
|
|||
|
|
if (requestId !== balanceRefreshRequestId || !walletLoaded) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
renderBalances(balances);
|
|||
|
|
dashboardLastBalanceStatus = `Updated ${new Date().toLocaleTimeString()}`;
|
|||
|
|
setBalanceStatus(dashboardLastBalanceStatus, "success");
|
|||
|
|
return true;
|
|||
|
|
} catch (error) {
|
|||
|
|
if (requestId !== balanceRefreshRequestId || !walletLoaded) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
setBalanceStatus(String(error), "error");
|
|||
|
|
return false;
|
|||
|
|
} finally {
|
|||
|
|
if (requestId === balanceRefreshRequestId) {
|
|||
|
|
balanceRefreshInFlight = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function startBalanceRefresh({ immediate = true } = {}) {
|
|||
|
|
if (balanceRefreshTimer || !walletLoaded || !registrationRegistered || currentView !== "dashboard") {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (immediate) {
|
|||
|
|
refreshDashboardBalances({ force: true });
|
|||
|
|
}
|
|||
|
|
balanceRefreshTimer = setInterval(() => {
|
|||
|
|
refreshDashboardBalances();
|
|||
|
|
}, BALANCE_REFRESH_MS);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function stopBalanceRefresh() {
|
|||
|
|
if (balanceRefreshTimer) {
|
|||
|
|
clearInterval(balanceRefreshTimer);
|
|||
|
|
balanceRefreshTimer = null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function updateBalanceRefreshState() {
|
|||
|
|
if (walletLoaded && registrationRegistered && currentView === "dashboard") {
|
|||
|
|
startBalanceRefresh();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
stopBalanceRefresh();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function restoreLatestDashboardTransactionsFromMemory() {
|
|||
|
|
if (!latestDashboardTransactionRecords.size || !latestDashboardTxids.length) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const latestCache = { transactions: Object.fromEntries(latestDashboardTransactionRecords) };
|
|||
|
|
updateTransactionCacheChainStatus(latestCache);
|
|||
|
|
latestDashboardTransactionRecords = new Map(Object.entries(latestCache.transactions));
|
|||
|
|
renderRecentTransactions(normalizeRecentTransactionRecords(
|
|||
|
|
transactionRecordsForTxids(latestCache, latestDashboardTxids),
|
|||
|
|
{ preserveOrder: true }
|
|||
|
|
));
|
|||
|
|
setRecentTransactionsState("");
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function startRegisteredDashboardLoadSequence({ resetTransactions = false, quiet = false } = {}) {
|
|||
|
|
const loadId = ++dashboardRegisteredLoadId;
|
|||
|
|
stopLatestDashboardTransactionsPolling();
|
|||
|
|
stopBalanceRefresh();
|
|||
|
|
|
|||
|
|
if (resetTransactions) {
|
|||
|
|
resetLatestDashboardTransactionsMemory();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const restoredTransactions = restoreLatestDashboardTransactionsFromMemory();
|
|||
|
|
if (!restoredTransactions && !quiet) {
|
|||
|
|
setRecentTransactionsForWalletState("checking");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!walletLoaded || !registrationRegistered || currentView !== "dashboard") {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (dashboardLastBalanceStatus) {
|
|||
|
|
setBalanceStatus(dashboardLastBalanceStatus, "success");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
await refreshDashboardBalances({ force: true, quiet: quiet || Boolean(dashboardLastBalanceStatus) });
|
|||
|
|
if (
|
|||
|
|
loadId !== dashboardRegisteredLoadId ||
|
|||
|
|
!walletLoaded ||
|
|||
|
|
!registrationRegistered ||
|
|||
|
|
currentView !== "dashboard"
|
|||
|
|
) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
startBalanceRefresh({ immediate: false });
|
|||
|
|
if (!restoredTransactions) {
|
|||
|
|
setRecentTransactionsForWalletState("registered");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
updateLatestDashboardTransactionsPolling();
|
|||
|
|
startBackgroundTransactionSync();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function startNetworkInfoRefresh() {
|
|||
|
|
if (networkInfoRefreshTimer || !walletLoaded || currentView !== "dashboard") {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
refreshNetworkInfo({ target: "dashboard" });
|
|||
|
|
networkInfoRefreshTimer = setInterval(() => {
|
|||
|
|
refreshNetworkInfo({ target: "dashboard" });
|
|||
|
|
}, NETWORK_INFO_REFRESH_MS);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function stopNetworkInfoRefresh() {
|
|||
|
|
if (networkInfoRefreshTimer) {
|
|||
|
|
clearInterval(networkInfoRefreshTimer);
|
|||
|
|
networkInfoRefreshTimer = null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function updateNetworkInfoRefreshState() {
|
|||
|
|
if (walletLoaded && currentView === "dashboard") {
|
|||
|
|
startNetworkInfoRefresh();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
stopNetworkInfoRefresh();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function startNodeInfoRefresh() {
|
|||
|
|
if (nodeInfoRefreshTimer || !walletLoaded) {
|
|||
|
|
renderNodeInfo(null);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
refreshNetworkInfo({ target: "node", force: true });
|
|||
|
|
nodeInfoRefreshTimer = setInterval(() => {
|
|||
|
|
refreshNetworkInfo({ target: "node", force: true });
|
|||
|
|
}, NODE_INFO_REFRESH_MS);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function stopNodeInfoRefresh() {
|
|||
|
|
if (nodeInfoRefreshTimer) {
|
|||
|
|
clearInterval(nodeInfoRefreshTimer);
|
|||
|
|
nodeInfoRefreshTimer = null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function activeBroadcastNode() {
|
|||
|
|
return broadcastNodeButton?.value || broadcastNodeButton?.dataset.broadcastNode || broadcastNodeButton?.textContent?.trim() || "";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function setRegistrationState(state) {
|
|||
|
|
const states = ["registered", "unregistered", "checking"];
|
|||
|
|
registrationBadge?.classList.remove(...states);
|
|||
|
|
registrationAction?.classList.remove(...states);
|
|||
|
|
registrationNotice?.classList.remove(...states);
|
|||
|
|
|
|||
|
|
if (state === "registered") {
|
|||
|
|
registrationRegistered = true;
|
|||
|
|
registrationBadge && (registrationBadge.textContent = "Registered");
|
|||
|
|
registrationBadge?.classList.add("registered");
|
|||
|
|
if (registrationNotice) {
|
|||
|
|
registrationNotice.textContent = "Address registered. This wallet can receive coins and tokens.";
|
|||
|
|
registrationNotice.classList.add("registered");
|
|||
|
|
}
|
|||
|
|
if (registrationAction) {
|
|||
|
|
registrationAction.textContent = "Address Registered";
|
|||
|
|
registrationAction.disabled = true;
|
|||
|
|
registrationAction.classList.add("registered");
|
|||
|
|
}
|
|||
|
|
startRegisteredDashboardLoadSequence();
|
|||
|
|
updateHistoryRefreshState();
|
|||
|
|
updateNetworkInfoRefreshState();
|
|||
|
|
refreshReceivePage();
|
|||
|
|
if (typeof refreshActiveTransactionFormAfterWalletData === "function") {
|
|||
|
|
refreshActiveTransactionFormAfterWalletData();
|
|||
|
|
}
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (state === "checking") {
|
|||
|
|
registrationBadge && (registrationBadge.textContent = "Checking");
|
|||
|
|
registrationBadge?.classList.add("checking");
|
|||
|
|
if (registrationNotice) {
|
|||
|
|
registrationNotice.textContent = "Checking whether this address can receive coins and tokens.";
|
|||
|
|
registrationNotice.classList.add("checking");
|
|||
|
|
}
|
|||
|
|
if (registrationAction) {
|
|||
|
|
registrationAction.textContent = "Checking...";
|
|||
|
|
registrationAction.disabled = true;
|
|||
|
|
registrationAction.classList.add("checking");
|
|||
|
|
}
|
|||
|
|
dashboardRegisteredLoadId += 1;
|
|||
|
|
stopBalanceRefresh();
|
|||
|
|
setRecentTransactionsForWalletState("checking");
|
|||
|
|
updateHistoryRefreshState();
|
|||
|
|
refreshReceivePage();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
registrationRegistered = false;
|
|||
|
|
registrationBadge && (registrationBadge.textContent = "Unregistered");
|
|||
|
|
registrationBadge?.classList.add("unregistered");
|
|||
|
|
if (registrationNotice) {
|
|||
|
|
registrationNotice.textContent = "Address must be registered before this wallet can receive coins or tokens.";
|
|||
|
|
registrationNotice.classList.add("unregistered");
|
|||
|
|
}
|
|||
|
|
if (registrationAction) {
|
|||
|
|
registrationAction.textContent = "Register Address";
|
|||
|
|
registrationAction.disabled = false;
|
|||
|
|
registrationAction.classList.add("unregistered");
|
|||
|
|
}
|
|||
|
|
dashboardRegisteredLoadId += 1;
|
|||
|
|
stopBalanceRefresh();
|
|||
|
|
setRecentTransactionsForWalletState("unregistered");
|
|||
|
|
updateHistoryRefreshState();
|
|||
|
|
updateNetworkInfoRefreshState();
|
|||
|
|
refreshReceivePage();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function checkWalletRegistrationOnce() {
|
|||
|
|
if (!walletLoaded || registrationRegistered || registrationCheckInFlight) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const invoke = window.__TAURI__?.core?.invoke;
|
|||
|
|
const broadcastNode = activeBroadcastNode();
|
|||
|
|
if (!invoke || !broadcastNode) {
|
|||
|
|
setRegistrationState("unregistered");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
registrationCheckInFlight = true;
|
|||
|
|
const requestId = ++registrationRequestId;
|
|||
|
|
setRegistrationState("checking");
|
|||
|
|
try {
|
|||
|
|
const status = await invoke("check_wallet_registration", { broadcastNode });
|
|||
|
|
if (requestId !== registrationRequestId || !walletLoaded) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
setRegistrationState(status?.registered ? "registered" : "unregistered");
|
|||
|
|
} catch (_) {
|
|||
|
|
if (requestId !== registrationRequestId || !walletLoaded) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
setRegistrationState("unregistered");
|
|||
|
|
} finally {
|
|||
|
|
if (requestId === registrationRequestId) {
|
|||
|
|
registrationCheckInFlight = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function registerActiveWallet() {
|
|||
|
|
if (!walletLoaded || registrationRegistered || registrationCheckInFlight) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const invoke = window.__TAURI__?.core?.invoke;
|
|||
|
|
const broadcastNode = activeBroadcastNode();
|
|||
|
|
if (!invoke || !broadcastNode) {
|
|||
|
|
setRegistrationState("unregistered");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
registrationCheckInFlight = true;
|
|||
|
|
const requestId = ++registrationRequestId;
|
|||
|
|
setRegistrationState("checking");
|
|||
|
|
try {
|
|||
|
|
const status = await invoke("register_active_wallet", { broadcastNode });
|
|||
|
|
if (requestId !== registrationRequestId || !walletLoaded) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
setRegistrationState(status?.registered ? "registered" : "unregistered");
|
|||
|
|
} catch (_) {
|
|||
|
|
if (requestId !== registrationRequestId || !walletLoaded) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
setRegistrationState("unregistered");
|
|||
|
|
} finally {
|
|||
|
|
if (requestId === registrationRequestId) {
|
|||
|
|
registrationCheckInFlight = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|