feat(wallet): humanize shared wallet experience

Consolidate wallet account decoding and portfolio handling around the shared Rust IDL decoder. Simplify AMM wallet integration, remove obsolete caches and network plumbing, and cover account selection and live flows.
This commit is contained in:
Ricardo Guilherme Schmidt
2026-08-21 17:38:45 -03:00
parent 62d133e909
commit 8c3e6fccfe
64 changed files with 6268 additions and 27501 deletions
@@ -9,12 +9,21 @@ Popup {
property string cancelText: qsTr("Cancel")
property string confirmText: qsTr("Confirm")
property bool busy: false
property string busyText: qsTr("Submitting…")
property bool activityBusy: false
property string activityText: qsTr("Updating…")
property bool showInlineBusyIndicator: true
property var snapshot: ({})
property Component summary: null
property bool confirmationPending: false
property bool confirmEnabled: true
property bool roundedCancelButton: false
property bool closeWhenSettled: true
readonly property bool actionPending: root.busy || root.activityBusy
signal canceled
signal confirmed(var snapshot)
signal summaryEdited(var snapshot)
modal: true
dim: true
@@ -40,7 +49,14 @@ Popup {
root.snapshot = root.cloneSnapshot(nextSnapshot)
root.confirmationPending = false
root.open()
cancelButton.forceActiveFocus()
Qt.callLater(function() {
if (cancelButtonLoader.item)
cancelButtonLoader.item.forceActiveFocus()
})
}
function updateSnapshot(nextSnapshot) {
root.snapshot = root.cloneSnapshot(nextSnapshot)
}
function cancel() {
@@ -52,7 +68,7 @@ Popup {
}
function confirm() {
if (root.busy)
if (root.actionPending || !root.confirmEnabled)
return
root.confirmationPending = true
root.confirmed(root.snapshot)
@@ -62,10 +78,21 @@ Popup {
}
}
Connections {
target: summaryLoader.item
ignoreUnknownSignals: true
function onSnapshotEdited(snapshot) {
root.updateSnapshot(snapshot)
root.summaryEdited(root.snapshot)
}
}
onBusyChanged: {
if (!root.busy && root.confirmationPending) {
root.confirmationPending = false
root.close()
if (root.closeWhenSettled)
root.close()
}
}
@@ -117,26 +144,37 @@ Popup {
}
}
BusyIndicator {
Item {
id: inlineBusyIndicator
property bool active: root.showInlineBusyIndicator && root.actionPending
Layout.alignment: Qt.AlignHCenter
visible: root.busy
running: root.busy
Accessible.name: qsTr("Submitting transaction")
Layout.preferredWidth: active ? busySpinner.implicitWidth : 0
Layout.preferredHeight: active ? busySpinner.implicitHeight : 0
implicitWidth: busySpinner.implicitWidth
implicitHeight: busySpinner.implicitHeight
visible: active
BusyIndicator {
id: busySpinner
anchors.centerIn: parent
running: inlineBusyIndicator.active
Accessible.name: root.busy ? root.busyText : root.activityText
}
}
RowLayout {
Layout.fillWidth: true
spacing: 10
Button {
id: cancelButton
objectName: "transactionCancelButton"
Loader {
id: cancelButtonLoader
objectName: "transactionCancelButtonLoader"
Layout.fillWidth: true
implicitHeight: 44
text: root.cancelText
enabled: !root.busy
Accessible.name: text
onClicked: root.cancel()
Layout.preferredHeight: 44
sourceComponent: root.roundedCancelButton
? roundedCancelButtonComponent : defaultCancelButtonComponent
}
Button {
@@ -144,8 +182,8 @@ Popup {
objectName: "transactionConfirmButton"
Layout.fillWidth: true
implicitHeight: 44
text: root.busy ? qsTr("Submitting...") : root.confirmText
enabled: !root.busy
text: root.busy ? root.busyText : root.confirmText
enabled: !root.actionPending && root.confirmEnabled
Accessible.name: text
onClicked: root.confirm()
@@ -166,4 +204,48 @@ Popup {
}
}
}
Component {
id: defaultCancelButtonComponent
Button {
objectName: "transactionCancelButton"
anchors.fill: parent
text: root.cancelText
enabled: !root.busy
Accessible.name: text
onClicked: root.cancel()
}
}
Component {
id: roundedCancelButtonComponent
Button {
id: cancelButton
objectName: "transactionCancelButton"
anchors.fill: parent
text: root.cancelText
enabled: !root.busy
Accessible.name: text
onClicked: root.cancel()
background: Rectangle {
color: cancelButton.pressed ? "#3f3f46"
: cancelButton.hovered ? "#27272a" : "#18181b"
border.color: "#52525b"
border.width: 1
radius: 6
}
contentItem: Label {
text: cancelButton.text
color: "#f4f4f5"
font.bold: true
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -5,53 +5,127 @@ import QtQuick.Layouts
ItemDelegate {
id: root
required property int index
required property string name
required property string alias
required property string address
required property string balance
required property bool isPublic
required property string displayAddress
required property string kind
required property string section
required property string programName
required property string accountType
required property string decodedData
required property string visibility
required property bool canBePrimary
required property bool isPrimary
signal copyRequested(string text)
signal makePrimaryRequested(string address)
signal renameRequested(string address, string alias)
leftPadding: 12
rightPadding: 8
topPadding: 10
bottomPadding: 10
enabled: root.section !== "hidden"
Accessible.name: root.isPrimary
? qsTr("%1, primary account").arg(root.name)
: root.name
Accessible.name: qsTr("%1, balance %2").arg(root.name).arg(root.balance || "0")
function kindLabel() {
if (root.kind === "user")
return qsTr("User")
if (root.kind === "private")
return qsTr("Account")
if (root.accountType.length > 0)
return root.accountType
return root.kind === "unknown" ? qsTr("Unknown") : qsTr("Program")
}
background: Rectangle {
color: root.highlighted || root.hovered ? "#27272a" : "#18181b"
radius: 6
border.width: root.activeFocus ? 1 : 0
border.color: "#f26a21"
color: root.isPrimary || root.hovered ? "#27272a" : "#18181b"
radius: 8
border.width: root.activeFocus || root.isPrimary ? 1 : 0
border.color: root.isPrimary ? "#f59e0b" : "#52525b"
}
contentItem: ColumnLayout {
spacing: 6
spacing: 7
RowLayout {
Layout.fillWidth: true
spacing: 8
spacing: 7
Label {
Layout.fillWidth: true
text: root.name
color: "#f4f4f5"
color: "#fafafa"
font.bold: true
elide: Text.ElideRight
}
Label {
visible: root.isPrimary
text: qsTr("Primary")
color: "#fbbf24"
font.pixelSize: 11
font.bold: true
}
Label {
text: root.isPublic ? qsTr("Public") : qsTr("Private")
text: root.kindLabel()
color: "#a1a1aa"
font.pixelSize: 11
}
Item { Layout.fillWidth: true }
Label {
text: root.visibility === "private" ? qsTr("Private") : qsTr("Public")
color: root.visibility === "private" ? "#c4b5fd" : "#93c5fd"
font.pixelSize: 11
}
}
Label {
objectName: "walletProgramName"
visible: root.section === "advanced" && root.programName.length > 0
Layout.fillWidth: true
text: qsTr("Program: %1").arg(root.programName)
color: "#a1a1aa"
font.pixelSize: 11
elide: Text.ElideRight
}
ColumnLayout {
visible: root.section === "advanced" && root.decodedData.length > 0
Layout.fillWidth: true
spacing: 4
Label {
text: root.balance.length > 0 ? root.balance : "-"
color: "#f4f4f5"
font.bold: true
objectName: "walletDecodedDataLabel"
text: qsTr("Decoded data")
color: "#a1a1aa"
font.pixelSize: 11
}
Rectangle {
objectName: "walletDecodedDataBox"
Layout.fillWidth: true
implicitHeight: decodedDataText.implicitHeight + 16
color: "#18181b"
radius: 6
border.width: 1
border.color: "#3f3f46"
Text {
id: decodedDataText
objectName: "walletDecodedData"
anchors.fill: parent
anchors.margins: 8
text: root.decodedData
color: "#d4d4d8"
font.family: "monospace"
font.pixelSize: 10
textFormat: Text.PlainText
wrapMode: Text.WrapAnywhere
}
}
}
@@ -61,17 +135,45 @@ ItemDelegate {
Label {
Layout.fillWidth: true
text: root.address
color: "#a1a1aa"
text: root.displayAddress
color: "#71717a"
font.family: "monospace"
font.pixelSize: 11
elide: Text.ElideMiddle
}
CopyButton {
visible: root.address.length > 0
onCopyRequested: root.copyRequested(root.address)
visible: root.displayAddress.length > 0
copyText: root.displayAddress
copyLabel: qsTr("Copy address")
}
}
RowLayout {
Layout.fillWidth: true
spacing: 6
Button {
objectName: "walletRenameButton"
text: qsTr("Rename")
flat: true
onClicked: root.renameRequested(root.address, root.alias)
}
Item { Layout.fillWidth: true }
Button {
objectName: "walletMakePrimaryButton"
visible: root.canBePrimary && !root.isPrimary
text: qsTr("Make primary")
flat: true
onClicked: root.makePrimaryRequested(root.address)
}
}
}
onClicked: {
if (root.canBePrimary && !root.isPrimary)
root.makePrimaryRequested(root.address)
}
}
+20 -1
View File
@@ -5,9 +5,11 @@ WalletIconButton {
signal copyRequested
property string copyText: ""
property string copyLabel: qsTr("Copy")
property bool copied: false
accessibleName: root.copied ? qsTr("Copied") : qsTr("Copy")
accessibleName: root.copied ? qsTr("Copied") : root.copyLabel
iconSource: root.copied
? Qt.resolvedUrl("icons/checkmark.svg")
: Qt.resolvedUrl("icons/copy.svg")
@@ -18,7 +20,24 @@ WalletIconButton {
onTriggered: root.copied = false
}
TextEdit {
id: clipboardProxy
visible: false
}
function copyToClipboard() {
if (root.copyText.length === 0)
return
clipboardProxy.text = root.copyText
clipboardProxy.selectAll()
clipboardProxy.copy()
clipboardProxy.deselect()
clipboardProxy.text = ""
}
onClicked: {
root.copyToClipboard()
root.copyRequested()
root.copied = true
resetTimer.restart()
@@ -16,9 +16,13 @@ Popup {
x: parent ? Math.max(0, Math.round((parent.width - width) / 2)) : 0
y: parent ? Math.max(0, Math.round((parent.height - height) / 2)) : 0
padding: 20
focus: true
closePolicy: root.busy ? Popup.NoAutoClose : Popup.CloseOnEscape | Popup.CloseOnPressOutside
onOpened: privateSwitch.checked = false
onOpened: {
privateSwitch.checked = false
Qt.callLater(function() { privateSwitch.forceActiveFocus() })
}
background: Rectangle {
color: "#18181b"
@@ -20,6 +20,7 @@ Popup {
x: parent ? Math.max(0, Math.round((parent.width - width) / 2)) : 0
y: parent ? Math.max(0, Math.round((parent.height - height) / 2)) : 0
padding: 20
focus: true
closePolicy: root.busy || root.mnemonic.length > 0
? Popup.NoAutoClose
: Popup.CloseOnEscape | Popup.CloseOnPressOutside
@@ -15,8 +15,11 @@ Popup {
x: parent ? Math.max(0, Math.round((parent.width - width) / 2)) : 0
y: parent ? Math.max(0, Math.round((parent.height - height) / 2)) : 0
padding: 20
focus: true
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
onOpened: Qt.callLater(function() { closeButton.forceActiveFocus() })
background: Rectangle {
color: "#18181b"
border.color: "#3f3f46"
@@ -44,6 +47,8 @@ Popup {
}
Button {
id: closeButton
Layout.alignment: Qt.AlignRight
text: qsTr("Close")
onClicked: root.close()