Implement search type tabs for balance and transaction details; enhance UI for better usability and sharing functionality

This commit is contained in:
void-57 2025-08-13 23:17:23 +05:30
parent b303575bb3
commit 4ede245060
3 changed files with 644 additions and 38 deletions

View File

@ -1399,8 +1399,6 @@ body {
text-overflow: ellipsis;
}
/* Transaction Filter and Pagination Controls */
.transaction-controls {
background: var(--surface-color);
@ -1659,7 +1657,6 @@ body {
}
}
.wallet-recovered h3 {
display: flex;
align-items: center;
@ -2480,12 +2477,18 @@ sm-popup::part(popup) {
.balance-header {
display: flex;
flex-direction: column;
flex-direction: row;
align-items: center;
text-align: center;
justify-content: center;
position: relative;
margin-bottom: 1.5rem;
}
.balance-header .share-btn {
position: absolute;
right: 0;
}
.balance-header h3 {
margin: 0 0 0.5rem 0;
color: var(--primary-color);
@ -3246,7 +3249,6 @@ sm-popup::part(popup) {
width: auto;
}
@media (max-width: 768px) {
.modern-popup-container {
max-height: 75vh !important;
@ -4060,3 +4062,207 @@ sm-popup::part(popup) {
margin-bottom: 0;
}
}
/* ===== SEARCH TYPE TABS ===== */
.search-type-tabs {
display: flex;
gap: 0.5rem;
margin-bottom: 1rem;
background: var(--bg-secondary);
border-radius: var(--border-radius);
padding: 0.25rem;
}
.tab-btn {
flex: 1;
padding: 0.75rem 1rem;
border: none;
background: transparent;
color: var(--text-secondary);
font-size: 0.875rem;
font-weight: 500;
border-radius: calc(var(--border-radius) - 0.25rem);
cursor: pointer;
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.tab-btn:hover {
background: var(--surface-color);
color: var(--text-primary);
}
.tab-btn.active {
background: var(--primary-color);
color: white;
box-shadow: 0 2px 4px rgba(var(--primary-rgb), 0.2);
}
.search-section {
transition: all 0.3s ease;
}
/* ===== SHARE BUTTON STYLES ===== */
.share-btn {
background: var(--accent-color);
color: white;
border: none;
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
font-size: 0.875rem;
}
.share-btn:hover {
background: var(--accent-dark);
transform: scale(1.05);
}
.balance-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1rem;
}
.balance-header h3 {
margin: 0;
display: flex;
align-items: center;
gap: 0.5rem;
}
.transaction-details-header {
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: 1.5rem;
padding-bottom: 1rem;
border-bottom: 1px solid var(--border-color);
}
.transaction-details-header .share-btn {
position: absolute;
right: 0;
}
.transaction-details-header h3 {
margin: 0;
display: flex;
align-items: center;
gap: 0.5rem;
color: var(--text-primary);
}
/* ===== TRANSACTION DETAILS CONTENT ===== */
.transaction-details-content {
display: grid;
gap: 1rem;
}
.tx-detail-card {
background: var(--surface-color);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: 1rem;
transition: all 0.2s ease;
}
.tx-detail-card:hover {
border-color: var(--primary-color);
box-shadow: 0 2px 8px rgba(var(--primary-rgb), 0.1);
}
.tx-detail-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.5rem 0;
border-bottom: 1px solid var(--border-color);
}
.tx-detail-row:last-child {
border-bottom: none;
}
.tx-detail-label {
font-weight: 600;
color: var(--text-secondary);
display: flex;
align-items: center;
gap: 0.5rem;
min-width: 120px;
}
.tx-detail-value {
color: var(--text-primary);
font-family: var(--font-mono);
word-break: break-all;
text-align: right;
flex: 1;
margin-left: 1rem;
}
.tx-detail-value.success {
color: var(--success-color);
font-weight: 600;
}
.tx-detail-value.failed {
color: var(--error-color);
font-weight: 600;
}
.tx-detail-value.amount {
font-size: 1.125rem;
font-weight: 700;
color: var(--primary-color);
}
/* ===== MOBILE RESPONSIVE FOR NEW ELEMENTS ===== */
@media (max-width: 768px) {
.search-type-tabs {
flex-direction: column;
gap: 0.25rem;
}
.tab-btn {
justify-content: flex-start;
padding: 0.625rem 1rem;
}
.balance-header,
.transaction-details-header {
flex-direction: column;
align-items: flex-start;
gap: 1rem;
}
.share-btn {
align-self: flex-end;
}
.tx-detail-row {
flex-direction: column;
align-items: flex-start;
gap: 0.5rem;
}
.tx-detail-value {
text-align: left;
margin-left: 0;
}
.tx-detail-label {
min-width: auto;
}
}

View File

@ -92,44 +92,105 @@
<div id="connectPage" class="page">
<div class="page-header">
<h2><i class="fas fa-search-dollar"></i> Check Balance</h2>
<p>
Check the XRP balance and transaction history for any Ripple
address
</p>
<p>Check XRP balance or view transaction details</p>
</div>
<div class="card">
<!-- Search Type Selector -->
<div class="form-group">
<label for="checkAddress">
<i class="fas fa-wallet"></i> XRP Address or Private Key
</label>
<input
type="text"
id="checkAddress"
class="form-input"
placeholder="Enter XRP address (r...), private key (BTC/FLO)"
autocomplete="off"
/>
<div class="input-help">
Enter an XRP address,private key (BTC/FLO) to check XRP balance
<label>Search Type:</label>
<div class="search-type-tabs">
<button
type="button"
class="tab-btn active"
onclick="switchSearchType('balance')"
id="balanceTab"
>
<i class="fas fa-wallet"></i> Balance & History
</button>
<button
type="button"
class="tab-btn"
onclick="switchSearchType('transaction')"
id="transactionTab"
>
<i class="fas fa-receipt"></i> Transaction Details
</button>
</div>
</div>
<button
onclick="checkBalanceAndTransactions()"
class="btn btn-primary btn-block"
<!-- Balance Search -->
<div id="balanceSearch" class="search-section">
<div class="form-group">
<label for="checkAddress">
<i class="fas fa-wallet"></i> XRP Address or Private Key
</label>
<input
type="text"
id="checkAddress"
class="form-input"
placeholder="Enter XRP address (r...), BTC/FLO private key, or XRP seed"
autocomplete="off"
/>
<div class="input-help">
Enter an XRP address, private key (BTC/FLO), or XRP seed to
check balance
</div>
</div>
<button
onclick="checkBalanceAndTransactions()"
class="btn btn-primary btn-block"
>
<i class="fas fa-search-dollar"></i> Check Balance
</button>
</div>
<!-- Transaction Hash Search -->
<div
id="transactionSearch"
class="search-section"
style="display: none"
>
<i class="fas fa-search-dollar"></i> Check Balance
</button>
<div class="form-group">
<label for="checkTransactionHash">
<i class="fas fa-receipt"></i> Transaction Hash
</label>
<input
type="text"
id="checkTransactionHash"
class="form-input"
placeholder="Enter transaction hash (TX ID)"
autocomplete="off"
/>
<div class="input-help">
Enter a transaction hash to view transaction details
</div>
</div>
<button
onclick="checkTransactionDetails()"
class="btn btn-primary btn-block"
>
<i class="fas fa-search"></i> View Transaction
</button>
</div>
</div>
<!-- Balance Info Display -->
<div class="card balance-info" id="balanceInfo" style="display: none">
<div class="balance-header">
<h3><i class="fas fa-coins"></i> Account Balance</h3>
<div class="balance-display">
<span class="balance-amount" id="displayBalance">0 XRP</span>
</div>
<button
onclick="copyBalanceLink()"
class="btn-icon share-btn"
title="Copy Shareable Link"
>
<i class="fas fa-share-alt"></i>
</button>
</div>
<div class="balance-display">
<span class="balance-amount" id="displayBalance">0 XRP</span>
</div>
<div class="account-details">
<div class="detail-row">
@ -148,6 +209,27 @@
</div>
</div>
<!-- Transaction Details Display -->
<div
class="card transaction-details"
id="transactionDetails"
style="display: none"
>
<div class="transaction-details-header">
<h3><i class="fas fa-receipt"></i> Transaction Details</h3>
<button
onclick="copyTransactionLink()"
class="btn-icon share-btn"
title="Copy Shareable Link"
>
<i class="fas fa-share-alt"></i>
</button>
</div>
<div id="transactionDetailsContent">
<!-- Transaction details -->
</div>
</div>
<!-- Transaction Section -->
<div
id="transactionSection"

View File

@ -492,7 +492,7 @@ async function sendXRP() {
const senderKey = senderKeyElement.value;
const destination = destinationElement.value;
const amount = amountElement.value;
// Validation
if (!senderKey) return notify("Please enter your private key", "error");
if (!destination) return notify("Please enter recipient address", "error");
@ -775,7 +775,7 @@ async function confirmSend() {
confirmBtn.innerHTML = originalText;
confirmBtn.disabled = false;
// Don't close popup here - it's handled in success/error cases
// Don't close popup here
window.pendingTransaction = null;
}
}
@ -795,12 +795,12 @@ function displaySearchedAddresses(addresses) {
let container = document.getElementById("searchedAddressesContainer");
if (!container && addresses.length > 0) {
// Create the container after the balance check card
const balanceCard = document.querySelector("#connectPage .card");
// Create the container at the end of the connectPage
const connectPage = document.getElementById("connectPage");
container = document.createElement("div");
container.id = "searchedAddressesContainer";
container.className = "card searched-addresses-card";
balanceCard.parentNode.insertBefore(container, balanceCard.nextSibling);
connectPage.appendChild(container);
}
if (!container) return;
@ -1305,7 +1305,7 @@ async function checkBalanceAndTransactions() {
try {
if (!userInput) {
notify(
"Please enter an XRP address, BTC private key, or FLO private key",
"Please enter an XRP address, XRP seed, BTC private key, or FLO private key",
"error"
);
return;
@ -1343,11 +1343,34 @@ async function checkBalanceAndTransactions() {
);
return;
}
// Detect if input is a private key and convert to XRP address
// Detect if input is a private key/seed and convert to XRP address
else if (!userInput.startsWith("r")) {
try {
// Check if it's an XRP seed first (starts with 's')
if (userInput.startsWith("s") && userInput.length >= 25) {
try {
notify("Detected XRP seed - converting to XRP address...", "info");
const rippleWallet = xrpl.Wallet.fromSeed(userInput);
actualXRPAddress = rippleWallet.address;
sourceInfo = {
type: "XRP Seed",
originalKey: userInput,
originalAddress: actualXRPAddress,
blockchain: "XRP",
};
notify(
`Converted XRP seed to address: ${actualXRPAddress}`,
"success"
);
} catch (seedError) {
throw new Error("Invalid XRP seed format");
}
}
// Check if it's a Bitcoin WIF format (starts with "L" or "K")
if (userInput.startsWith("L") || userInput.startsWith("K")) {
else if (userInput.startsWith("L") || userInput.startsWith("K")) {
notify(
"Detected Bitcoin private key - converting to XRP address...",
"info"
@ -1460,6 +1483,12 @@ async function checkBalanceAndTransactions() {
document.getElementById("checkedAddress").textContent =
actualXRPAddress;
// Update URL for sharing
const currentUrl = new URL(window.location);
currentUrl.searchParams.set("address", actualXRPAddress);
currentUrl.searchParams.delete("tx"); // Remove transaction param if exists
window.history.pushState({}, "", currentUrl);
// Save to IndexedDB with source information
try {
await searchedAddressDB.saveSearchedAddress(
@ -1482,6 +1511,12 @@ async function checkBalanceAndTransactions() {
document.getElementById("checkedAddress").textContent =
actualXRPAddress;
// Update URL for sharing
const currentUrl = new URL(window.location);
currentUrl.searchParams.set("address", actualXRPAddress);
currentUrl.searchParams.delete("tx"); // Remove transaction param if exists
window.history.pushState({}, "", currentUrl);
// Save to IndexedDB with source information
try {
await searchedAddressDB.saveSearchedAddress(
@ -1906,6 +1941,282 @@ function generateFLOFromPrivateKey(privateKey) {
}
}
// Switch between search types
function switchSearchType(type) {
const balanceTab = document.getElementById("balanceTab");
const transactionTab = document.getElementById("transactionTab");
const balanceSearch = document.getElementById("balanceSearch");
const transactionSearch = document.getElementById("transactionSearch");
if (type === "balance") {
balanceTab.classList.add("active");
transactionTab.classList.remove("active");
balanceSearch.style.display = "block";
transactionSearch.style.display = "none";
// Hide transaction details if visible
document.getElementById("transactionDetails").style.display = "none";
} else {
transactionTab.classList.add("active");
balanceTab.classList.remove("active");
transactionSearch.style.display = "block";
balanceSearch.style.display = "none";
// Hide balance info if visible
document.getElementById("balanceInfo").style.display = "none";
document.getElementById("transactionSection").style.display = "none";
}
}
// Check transaction details by hash
async function checkTransactionDetails() {
const hashInput = document.getElementById("checkTransactionHash");
const txHash = hashInput.value.trim();
try {
if (!txHash) {
notify("Please enter a transaction hash", "error");
return;
}
// Show loading state
const checkBtn = document.querySelector(
'[onclick="checkTransactionDetails()"]'
);
const originalText = checkBtn.innerHTML;
checkBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Loading...';
checkBtn.disabled = true;
// Create XRPL client instance
const client = new xrpl.Client("wss://s.altnet.rippletest.net:51233");
try {
await client.connect();
// Get transaction details
const txResponse = await client.request({
command: "tx",
transaction: txHash,
});
if (txResponse.result) {
displayTransactionDetails(txResponse.result);
// Update URL for sharing
const currentUrl = new URL(window.location);
currentUrl.searchParams.set("tx", txHash);
window.history.pushState({}, "", currentUrl);
notify("Transaction details loaded successfully", "success");
} else {
notify("Transaction not found", "error");
}
} catch (error) {
console.error("Transaction lookup error:", error);
if (error.data && error.data.error === "txnNotFound") {
notify(
"Transaction not found. Please check the hash and try again.",
"error"
);
} else {
notify(
"Failed to fetch transaction details: " + error.message,
"error"
);
}
} finally {
await client.disconnect();
}
} catch (error) {
console.error("Error in checkTransactionDetails:", error);
notify("An error occurred while checking transaction details", "error");
} finally {
// Restore button state
const checkBtn = document.querySelector(
'[onclick="checkTransactionDetails()"]'
);
if (checkBtn) {
checkBtn.innerHTML = '<i class="fas fa-search"></i> View Transaction';
checkBtn.disabled = false;
}
}
}
// Display transaction details
function displayTransactionDetails(txData) {
const detailsContainer = document.getElementById("transactionDetailsContent");
// console.log(txData);
// console.log(parseFloat(txData.Amount)/ 1000000);
// console.log(parseFloat(txData.Fee)/ 1000000);
const txType = txData.TransactionType || "Unknown";
const account = txData.Account || "N/A";
const destination = txData.Destination || "N/A";
const amount = txData.Amount
? parseFloat(txData.Amount) / 1000000 + " XRP"
: "N/A";
const fee = txData.Fee ? parseFloat(txData.Fee) / 1000000 + " XRP" : "N/A";
const sequence = txData.Sequence || "N/A";
const ledgerIndex = txData.ledger_index || "N/A";
const hash = txData.hash || "N/A";
const date = txData.date
? new Date((txData.date + 946684800) * 1000).toLocaleString()
: "N/A";
const validated = txData.validated ? "Validated" : "Not Validated";
detailsContainer.innerHTML = `
<div class="tx-detail-card">
<div class="tx-detail-row">
<span class="tx-detail-label">
<i class="fas fa-check-circle"></i>
Status:
</span>
<span class="tx-detail-value success">${validated}</span>
</div>
<div class="tx-detail-row">
<span class="tx-detail-label">
<i class="fas fa-exchange-alt"></i>
Type:
</span>
<span class="tx-detail-value">${txType}</span>
</div>
<div class="tx-detail-row">
<span class="tx-detail-label">
<i class="fas fa-coins"></i>
Amount:
</span>
<span class="tx-detail-value amount">${amount}</span>
</div>
<div class="tx-detail-row">
<span class="tx-detail-label">
<i class="fas fa-receipt"></i>
Fee:
</span>
<span class="tx-detail-value">${fee}</span>
</div>
</div>
<div class="tx-detail-card">
<div class="tx-detail-row">
<span class="tx-detail-label">
<i class="fas fa-user-minus"></i>
From:
</span>
<span class="tx-detail-value">${account}</span>
</div>
${
destination !== "N/A"
? `
<div class="tx-detail-row">
<span class="tx-detail-label">
<i class="fas fa-user-plus"></i>
To:
</span>
<span class="tx-detail-value">${destination}</span>
</div>
`
: ""
}
<div class="tx-detail-row">
<span class="tx-detail-label">
<i class="fas fa-hashtag"></i>
Hash:
</span>
<span class="tx-detail-value">${hash}</span>
</div>
</div>
<div class="tx-detail-card">
<div class="tx-detail-row">
<span class="tx-detail-label">
<i class="fas fa-layer-group"></i>
Ledger:
</span>
<span class="tx-detail-value">${ledgerIndex}</span>
</div>
<div class="tx-detail-row">
<span class="tx-detail-label">
<i class="fas fa-sort-numeric-up"></i>
Sequence:
</span>
<span class="tx-detail-value">${sequence}</span>
</div>
<div class="tx-detail-row">
<span class="tx-detail-label">
<i class="fas fa-clock"></i>
Date:
</span>
<span class="tx-detail-value">${date}</span>
</div>
</div>
`;
document.getElementById("transactionDetails").style.display = "block";
}
// shareable balance link
function copyBalanceLink() {
const address = document.getElementById("checkedAddress").textContent;
if (address && address !== "-") {
const currentUrl = new URL(window.location);
currentUrl.searchParams.set("address", address);
currentUrl.searchParams.delete("tx"); // Remove transaction param if exists
navigator.clipboard
.writeText(currentUrl.toString())
.then(() => {
notify("Balance link copied to clipboard!", "success");
})
.catch(() => {
notify("Failed to copy link", "error");
});
} else {
notify("No address available to share", "error");
}
}
// shareable transaction link
function copyTransactionLink() {
const currentUrl = new URL(window.location);
const txHash = currentUrl.searchParams.get("tx");
if (txHash) {
navigator.clipboard
.writeText(currentUrl.toString())
.then(() => {
notify("Transaction link copied to clipboard!", "success");
})
.catch(() => {
notify("Failed to copy link", "error");
});
} else {
notify("No transaction hash available to share", "error");
}
}
// Handle URL parameters on page load
function handleUrlParameters() {
const urlParams = new URLSearchParams(window.location.search);
const address = urlParams.get("address");
const txHash = urlParams.get("tx");
if (address) {
// Auto-fill address and check balance
document.getElementById("checkAddress").value = address;
switchSearchType("balance");
setTimeout(() => {
checkBalanceAndTransactions();
}, 500);
} else if (txHash) {
// Auto-fill transaction hash and check details
document.getElementById("checkTransactionHash").value = txHash;
switchSearchType("transaction");
setTimeout(() => {
checkTransactionDetails();
}, 500);
}
}
window.sendXRP = sendXRP;
window.retrieveXRPAddress = retrieveXRPAddress;
@ -1915,6 +2226,12 @@ window.confirmSend = confirmSend;
window.closePopup = closePopup;
window.checkBalanceAndTransactions = checkBalanceAndTransactions;
window.switchSearchType = switchSearchType;
window.checkTransactionDetails = checkTransactionDetails;
window.copyBalanceLink = copyBalanceLink;
window.copyTransactionLink = copyTransactionLink;
window.handleUrlParameters = handleUrlParameters;
window.convertWIFtoRippleWallet = convertWIFtoRippleWallet;
// Multi-blockchain function exports
@ -1938,4 +2255,5 @@ window.copyCurrentAddress = copyCurrentAddress;
document.addEventListener("DOMContentLoaded", () => {
initializeInputControls();
handleUrlParameters();
});