diff --git a/apps/amm/qml/components/liquidity/TokenSelectorModal.qml b/apps/amm/qml/components/liquidity/TokenSelectorModal.qml index c07b725..73377cc 100644 --- a/apps/amm/qml/components/liquidity/TokenSelectorModal.qml +++ b/apps/amm/qml/components/liquidity/TokenSelectorModal.qml @@ -211,6 +211,7 @@ Popup { required property var modelData readonly property bool selectable: root.isSelectable(modelData) + readonly property string tokenDefinitionId: root.tokenAddress(modelData) readonly property string disabledReason: root.disabledReasonForCode( modelData.code || modelData.status) @@ -293,13 +294,22 @@ Popup { } Text { + id: tokenDefinitionText + + objectName: "tokenDefinitionAddress" Layout.fillWidth: true - text: root.shortAddress(root.tokenAddress(tokenRow.modelData)) + Layout.minimumWidth: 0 + text: tokenRow.tokenDefinitionId color: root.theme.colors.textPlaceholder font.family: "monospace" font.pixelSize: 11 elide: Text.ElideMiddle + wrapMode: Text.NoWrap + ToolTip.visible: tokenDefinitionText.truncated + && tokenDefinitionHover.hovered + ToolTip.text: tokenRow.tokenDefinitionId } + } } @@ -311,6 +321,17 @@ Popup { horizontalAlignment: Text.AlignRight elide: Text.ElideRight } + + AmmCopyButton { + id: copyTokenDefinitionButton + + objectName: "copyTokenDefinitionButton" + Layout.preferredWidth: visible ? implicitWidth : 0 + Layout.preferredHeight: implicitHeight + theme: root.theme + value: tokenRow.tokenDefinitionId + accessibleName: qsTr("Copy TokenDefinition address") + } } } @@ -318,12 +339,20 @@ Popup { id: rowHover anchors.fill: parent + anchors.rightMargin: copyTokenDefinitionButton.visible + ? copyTokenDefinitionButton.width + 8 : 0 hoverEnabled: true cursorShape: tokenRow.selectable ? Qt.PointingHandCursor : Qt.ForbiddenCursor onClicked: tokenRow.activate() } + HoverHandler { + id: tokenDefinitionHover + + target: tokenDefinitionText + } + ToolTip.visible: tokenRow.disabledReasonVisible ToolTip.text: tokenRow.disabledReason @@ -482,8 +511,4 @@ Popup { return label.length > 0 ? label.charAt(0).toUpperCase() : "" } - function shortAddress(value) { - var text = String(value || "") - return text.length > 14 ? text.slice(0, 7) + "..." + text.slice(-5) : text - } } diff --git a/apps/amm/tests/qml/tst_TokenAmountInput.qml b/apps/amm/tests/qml/tst_TokenAmountInput.qml index dcac305..5d708fc 100644 --- a/apps/amm/tests/qml/tst_TokenAmountInput.qml +++ b/apps/amm/tests/qml/tst_TokenAmountInput.qml @@ -154,6 +154,81 @@ TestCase { verify(!picker.popup.visible) } + function test_tokenDefinitionCopiesRawBase58WithoutSelectingRow() { + failOnWarning(/Detected recursive rearrange/) + + var address = "3thX6LZfHDZZKUs92febYZhYRcXddmzfzF2NvTkPNE" + var input = createTemporaryObject(inputComponent, testCase, { + "visible": true, + "width": 320, + "tokens": [{ + "definitionId": address, + "name": "Copyable token", + "selectable": true + }] + }) + var sink = createTemporaryObject(clipboardSinkComponent, testCase) + verify(input) + verify(sink) + selectedSpy.target = input + enteredSpy.target = input + selectedSpy.clear() + enteredSpy.clear() + + input.popup.open() + tryCompare(input.popup, "visible", true) + var tokenList = findChild(input, "tokenList") + verify(tokenList) + tryCompare(tokenList, "count", 1) + tryVerify(function() { return tokenList.itemAtIndex(0) !== null }) + var option = tokenList.itemAtIndex(0) + var addressText = findChild(option, "tokenDefinitionAddress") + var copyButton = findChild(option, "copyTokenDefinitionButton") + verify(addressText) + verify(copyButton) + compare(addressText.text, address) + compare(addressText.elide, Text.ElideMiddle) + compare(addressText.wrapMode, Text.NoWrap) + tryCompare(addressText, "truncated", true) + + mouseClick(copyButton, copyButton.width / 2, copyButton.height / 2) + sink.paste() + tryCompare(sink, "text", address) + compare(selectedSpy.count, 0) + compare(enteredSpy.count, 0) + tryCompare(input.popup, "visible", true) + } + + function test_tokenRowClickStillSelectsAndClosesModal() { + var input = createTemporaryObject(inputComponent, testCase, { + "visible": true, + "width": 320, + "tokens": [{ + "definitionId": enabledId, + "name": "Enabled", + "selectable": true + }] + }) + verify(input) + selectedSpy.target = input + selectedSpy.clear() + + input.popup.open() + tryCompare(input.popup, "visible", true) + var tokenList = findChild(input, "tokenList") + verify(tokenList) + tryVerify(function() { return tokenList.itemAtIndex(0) !== null }) + var option = tokenList.itemAtIndex(0) + + mouseMove(option, option.width / 2, option.height / 2) + tryCompare(option, "pointerHovered", true) + mouseClick(option, option.width / 2, option.height / 2) + + tryCompare(selectedSpy, "count", 1) + compare(selectedSpy.signalArguments[0][0], enabledId) + tryCompare(input.popup, "visible", false) + } + function test_balanceRefreshShowsLoadingIndicatorWithoutClearingBalance() { failOnWarning(/Detected recursive rearrange/) var input = createTemporaryObject(inputComponent, testCase, {