diff --git a/apps/amm/src/AmmUiBackend.cpp b/apps/amm/src/AmmUiBackend.cpp index 53ed616..1759082 100644 --- a/apps/amm/src/AmmUiBackend.cpp +++ b/apps/amm/src/AmmUiBackend.cpp @@ -95,6 +95,19 @@ QJsonObject enumFields(const QJsonValue& value, const QString& variant) return value.toObject().value(variant).toObject(); } +QString decodedDataText(const QJsonValue& value) +{ + if (value.isObject()) { + return QString::fromUtf8( + QJsonDocument(value.toObject()).toJson(QJsonDocument::Indented)).trimmed(); + } + if (value.isArray()) { + return QString::fromUtf8( + QJsonDocument(value.toArray()).toJson(QJsonDocument::Indented)).trimmed(); + } + return {}; +} + WalletAccountRead accountRead(const WalletAccount& account) { WalletAccountRead read; @@ -444,6 +457,8 @@ void AmmUiBackend::applyWalletPortfolio(quint64 generation) presentation.address = account.id; presentation.programName = program.programName; presentation.accountType = account.typeName; + if (account.status == QStringLiteral("decoded")) + presentation.decodedData = decodedDataText(account.value); if (program.programId == m_tokenProgramId && account.typeName == QStringLiteral("TokenHolding")) { const QJsonObject fungible = enumFields( diff --git a/apps/shared/wallet/qml/WalletControl.qml b/apps/shared/wallet/qml/WalletControl.qml index daec4a6..9167dc1 100644 --- a/apps/shared/wallet/qml/WalletControl.qml +++ b/apps/shared/wallet/qml/WalletControl.qml @@ -757,6 +757,7 @@ Item { 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 @@ -786,6 +787,7 @@ Item { section: accountWrapper.section programName: accountWrapper.programName accountType: accountWrapper.accountType + decodedData: accountWrapper.decodedData visibility: accountWrapper.visibility canBePrimary: accountWrapper.canBePrimary isPrimary: accountWrapper.isPrimary diff --git a/apps/shared/wallet/qml/internal/AccountDelegate.qml b/apps/shared/wallet/qml/internal/AccountDelegate.qml index ea5da2b..43a2f21 100644 --- a/apps/shared/wallet/qml/internal/AccountDelegate.qml +++ b/apps/shared/wallet/qml/internal/AccountDelegate.qml @@ -16,6 +16,7 @@ ItemDelegate { 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 @@ -87,14 +88,51 @@ ItemDelegate { } Label { - visible: root.programName.length > 0 + objectName: "walletProgramName" + visible: root.section === "advanced" && root.programName.length > 0 Layout.fillWidth: true - text: qsTr("%1 program ยท wallet controlled").arg(root.programName) + 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 { + 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 + } + } + } + RowLayout { Layout.fillWidth: true spacing: 4 diff --git a/apps/shared/wallet/src/WalletAccountModel.cpp b/apps/shared/wallet/src/WalletAccountModel.cpp index 8b93327..f7bdfca 100644 --- a/apps/shared/wallet/src/WalletAccountModel.cpp +++ b/apps/shared/wallet/src/WalletAccountModel.cpp @@ -59,6 +59,8 @@ QVariant WalletAccountModel::data(const QModelIndex& index, int role) const return account.definitionId; case AliasRole: return account.alias; + case DecodedDataRole: + return account.decodedData; default: return {}; } @@ -84,6 +86,7 @@ QHash WalletAccountModel::roleNames() const { DefinitionIdRole, "definitionId" }, { AliasRole, "alias" }, { DisplayAddressRole, "displayAddress" }, + { DecodedDataRole, "decodedData" }, }; } @@ -154,6 +157,7 @@ bool WalletAccountModel::applyPresentations( entry.programName = presentation.programName; entry.accountType = presentation.accountType; entry.definitionId = presentation.definitionId; + entry.decodedData = presentation.decodedData; entry.semanticName = presentation.semanticName; entry.section = sectionFor(entry, presentation.hiddenFromAccounts); entry.canBePrimary = entry.kind == QStringLiteral("user") @@ -175,6 +179,7 @@ bool WalletAccountModel::applyPresentations( && entry.programName == current.programName && entry.accountType == current.accountType && entry.definitionId == current.definitionId + && entry.decodedData == current.decodedData && entry.canBePrimary == current.canBePrimary && entry.isPrimary == current.isPrimary) { continue; @@ -193,6 +198,7 @@ bool WalletAccountModel::applyPresentations( SectionRole, ProgramNameRole, AccountTypeRole, + DecodedDataRole, CanBePrimaryRole, IsPrimaryRole, DefinitionIdRole, diff --git a/apps/shared/wallet/src/WalletAccountModel.h b/apps/shared/wallet/src/WalletAccountModel.h index 8e21a58..7eb3f2c 100644 --- a/apps/shared/wallet/src/WalletAccountModel.h +++ b/apps/shared/wallet/src/WalletAccountModel.h @@ -14,6 +14,7 @@ struct WalletAccountPresentation { QString accountType; QString definitionId; bool hiddenFromAccounts = false; + QString decodedData; }; class WalletAccountModel final : public QAbstractListModel { @@ -39,6 +40,7 @@ public: DefinitionIdRole, AliasRole, DisplayAddressRole, + DecodedDataRole, }; Q_ENUM(Role) @@ -79,6 +81,7 @@ private: QString programName; QString accountType; QString definitionId; + QString decodedData; bool canBePrimary = false; bool isPrimary = false; }; diff --git a/apps/shared/wallet/tests/cpp/LogosWalletProviderTest.cpp b/apps/shared/wallet/tests/cpp/LogosWalletProviderTest.cpp index 43d839f..9fdc6aa 100644 --- a/apps/shared/wallet/tests/cpp/LogosWalletProviderTest.cpp +++ b/apps/shared/wallet/tests/cpp/LogosWalletProviderTest.cpp @@ -604,6 +604,8 @@ void LogosWalletProviderTest::exposesStableAccountModelRoles() QCOMPARE(model.data(model.index(1), WalletAccountModel::AddressRole).toString(), ACCOUNT_B); QCOMPARE(model.roleNames().value(WalletAccountModel::DisplayAddressRole), QByteArray("displayAddress")); + QCOMPARE(model.roleNames().value(WalletAccountModel::DecodedDataRole), + QByteArray("decodedData")); QCOMPARE(model.data(model.index(1), WalletAccountModel::DisplayAddressRole).toString(), walletAccountIdToBase58(ACCOUNT_B)); QCOMPARE(model.data(model.index(1), WalletAccountModel::BalanceRole).toString(), @@ -623,6 +625,7 @@ void LogosWalletProviderTest::exposesStableAccountModelRoles() QStringLiteral("UserAccount"), {}, false, + QStringLiteral("{\"public_key\":\"Public/test\"}"), }, { walletAccountIdToBase58(ACCOUNT_C), @@ -640,6 +643,8 @@ void LogosWalletProviderTest::exposesStableAccountModelRoles() QStringLiteral("hidden")); QCOMPARE(model.data(model.index(2), WalletAccountModel::NameRole).toString(), QStringLiteral("TEST holding")); + QCOMPARE(model.data(model.index(0), WalletAccountModel::DecodedDataRole).toString(), + QStringLiteral("{\"public_key\":\"Public/test\"}")); model.setAlias(ACCOUNT_C, QStringLiteral("Reserve")); QCOMPARE(model.data(model.index(2), WalletAccountModel::NameRole).toString(), QStringLiteral("Reserve")); diff --git a/apps/shared/wallet/tests/qml/tst_WalletControl.qml b/apps/shared/wallet/tests/qml/tst_WalletControl.qml index 6a73ebb..88cba84 100644 --- a/apps/shared/wallet/tests/qml/tst_WalletControl.qml +++ b/apps/shared/wallet/tests/qml/tst_WalletControl.qml @@ -180,6 +180,7 @@ Item { section: account.section || "accounts", programName: account.programName || "", accountType: account.accountType || "", + decodedData: account.decodedData || "", visibility: account.visibility || (account.isPublic === false ? "private" : "public"), canBePrimary: account.canBePrimary === undefined ? true : account.canBePrimary, isPrimary: account.isPrimary === true @@ -622,6 +623,37 @@ Item { compare(fixture.control.selectedAddress, userAddress) } + function test_advancedShowsProgramAndDecodedData() { + const decodedData = "{\n \"name\": \"Test token\"\n}" + const fixture = createControl({ isWalletOpen: true }, [ + { + name: "Token definition", + address: "c".repeat(64), + balance: "0", + isPublic: true, + kind: "token_definition", + section: "advanced", + programName: "Token", + accountType: "TokenDefinition", + decodedData: decodedData, + canBePrimary: false + } + ]) + mouseClick(findChild(fixture.control, "walletAccountButton")) + mouseClick(findChild(fixture.control, "walletAccountsButton")) + mouseClick(findChild(fixture.control, "walletAdvancedAccountsButton")) + + const list = findChild(fixture.control, "walletAccountList") + tryVerify(function() { return list.itemAtIndex(0) !== null }) + const program = findChild(list.itemAtIndex(0), "walletProgramName") + const decoded = findChild(list.itemAtIndex(0), "walletDecodedData") + verify(program && decoded, "Advanced details exist") + tryCompare(program, "visible", true) + compare(program.text, "Program: Token") + tryCompare(decoded, "visible", true) + compare(decoded.text, decodedData) + } + function test_onlyProgramRecordsLeavesPrimaryEmpty() { const fixture = createControl({ isWalletOpen: true }, [{ name: "Token definition",