diff --git a/src/qml/Base58.js b/src/qml/Base58.js new file mode 100644 index 0000000..9477e1e --- /dev/null +++ b/src/qml/Base58.js @@ -0,0 +1,71 @@ +.pragma library + +const ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" + +// Encode a hex string to base58. +function encode(hexStr) { + if (!hexStr || hexStr.length === 0) return "" + const clean = hexStr.toLowerCase().replace(/^0x/, "") + if (clean.length === 0 || clean.length % 2 !== 0) return hexStr + + const bytes = [] + for (let i = 0; i < clean.length; i += 2) + bytes.push(parseInt(clean.substr(i, 2), 16)) + + let leadingZeros = 0 + while (leadingZeros < bytes.length && bytes[leadingZeros] === 0) + leadingZeros++ + + // digits[i] holds base-58 digits, least-significant first. + // For each input byte, multiply existing digits by 256 and add the byte. + const digits = [] + for (let i = 0; i < bytes.length; i++) { + let carry = bytes[i] + for (let j = 0; j < digits.length; j++) { + carry += digits[j] * 256 + digits[j] = carry % 58 + carry = Math.floor(carry / 58) + } + while (carry > 0) { + digits.push(carry % 58) + carry = Math.floor(carry / 58) + } + } + + let result = "1".repeat(leadingZeros) + for (let i = digits.length - 1; i >= 0; i--) + result += ALPHABET[digits[i]] + return result +} + +// Decode a base58 string to hex. +function decode(b58Str) { + if (!b58Str || b58Str.length === 0) return "" + + let leadingZeros = 0 + while (leadingZeros < b58Str.length && b58Str[leadingZeros] === "1") + leadingZeros++ + + // digits[i] holds base-256 (byte) digits, least-significant first. + // For each input char, multiply existing digits by 58 and add the char value. + const digits = [] + for (let i = 0; i < b58Str.length; i++) { + const idx = ALPHABET.indexOf(b58Str[i]) + if (idx < 0) return "" + let carry = idx + for (let j = 0; j < digits.length; j++) { + carry += digits[j] * 58 + digits[j] = carry % 256 + carry = Math.floor(carry / 256) + } + while (carry > 0) { + digits.push(carry % 256) + carry = Math.floor(carry / 256) + } + } + + let hex = "00".repeat(leadingZeros) + for (let i = digits.length - 1; i >= 0; i--) + hex += (digits[i] < 16 ? "0" : "") + digits[i].toString(16) + return hex +} diff --git a/src/qml/controls/AccountComboBox.qml b/src/qml/controls/AccountComboBox.qml index 0dafffe..ab213df 100644 --- a/src/qml/controls/AccountComboBox.qml +++ b/src/qml/controls/AccountComboBox.qml @@ -4,6 +4,7 @@ import QtQuick.Layouts import Logos.Theme import Logos.Controls +import "../Base58.js" as Base58 ComboBox { id: root @@ -45,7 +46,7 @@ ComboBox { selectByMouse: true font.pixelSize: Theme.typography.secondaryText color: Theme.palette.text - text: root.displayText + text: root.currentValue ? ("Account " + Base58.encode(root.currentValue).substring(0, 6)) : root.displayText verticalAlignment: Text.AlignVCenter clip: true } diff --git a/src/qml/controls/AccountDelegate.qml b/src/qml/controls/AccountDelegate.qml index 676d8b6..f45f739 100644 --- a/src/qml/controls/AccountDelegate.qml +++ b/src/qml/controls/AccountDelegate.qml @@ -4,6 +4,7 @@ import QtQuick.Layouts import Logos.Theme import Logos.Controls +import "../Base58.js" as Base58 ItemDelegate { id: root @@ -68,7 +69,7 @@ ItemDelegate { id: addressLabel Layout.fillWidth: true verticalAlignment: Text.AlignVCenter - text: model.address ?? "" + text: Base58.encode(model.address ?? "") font.pixelSize: Theme.typography.secondaryText color: Theme.palette.textMuted elide: Text.ElideMiddle @@ -76,7 +77,7 @@ ItemDelegate { LogosCopyButton { Layout.preferredHeight: 40 Layout.preferredWidth: 40 - onCopyText: root.copyRequested(model.address) + onCopyText: root.copyRequested(Base58.encode(model.address ?? "")) visible: addressLabel.text icon.color: Theme.palette.textMuted } diff --git a/src/qml/views/AccountsPanel.qml b/src/qml/views/AccountsPanel.qml index 957bccb..797c5e1 100644 --- a/src/qml/views/AccountsPanel.qml +++ b/src/qml/views/AccountsPanel.qml @@ -84,6 +84,22 @@ Rectangle { from: 0 to: root.currentBlockHeight value: root.lastSyncedBlock + + contentItem: Item { + Rectangle { + width: parent.width * (root.currentBlockHeight > 0 + ? root.lastSyncedBlock / root.currentBlockHeight : 0) + height: parent.height + radius: height / 2 + color: Theme.palette.overlayOrange + } + } + + background: Rectangle { + implicitHeight: 6 + radius: height / 2 + color: Theme.palette.backgroundElevated + } } } diff --git a/src/qml/views/TransferPanel.qml b/src/qml/views/TransferPanel.qml index 77474da..0a1b05c 100644 --- a/src/qml/views/TransferPanel.qml +++ b/src/qml/views/TransferPanel.qml @@ -6,6 +6,7 @@ import Logos.Theme import Logos.Controls import "../controls" +import "../Base58.js" as Base58 Rectangle { id: root @@ -181,10 +182,11 @@ Rectangle { onClicked: { var fromId = fromFilterCount > 0 && fromCombo.currentIndex >= 0 ? (fromCombo.currentValue ?? "") - : manualFromField.text.trim() + : Base58.decode(manualFromField.text.trim()) + var rawTo = toField.text.trim() var toAddress = (d.toComboPermanent || (d.useOwnedAccountForTo && toCombo.currentIndex >= 0)) ? (toCombo.currentValue ?? "") - : toField.text.trim() + : (rawTo.startsWith("{") ? rawTo : Base58.decode(rawTo)) var amount = amountField.text.trim() if (fromId.length > 0 && toAddress.length > 0 && amount.length > 0) { if (d.isPublicTab)