feat(WC-establish-connection): Fine-tune dApps button

1. Transform the dApps button into a combo box and add badge indicator + tooltip
2. Add storybook page
3. Adding tests
4. Minor fixes - dApps popup width, ModelUtils imports
This commit is contained in:
Alex Jbanca 2024-06-19 16:13:32 +03:00 committed by Alex Jbanca
parent c59ac4f3f0
commit 4bcda69d4a
12 changed files with 388 additions and 166 deletions

View File

@ -341,7 +341,7 @@ Item {
function startTestCase() { function startTestCase() {
d.activeTestCase = settings.testCase d.activeTestCase = settings.testCase
if(root.visible) { if(root.visible) {
dappsWorkflow.clicked() dappsWorkflow.popup.open()
} }
} }

View File

@ -0,0 +1,78 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import AppLayouts.Wallet.controls 1.0
SplitView {
id: root
width: 400
height: 400
Item {
SplitView.fillWidth: true
SplitView.fillHeight: true
DappsComboBox {
id: connectedDappComboBox
anchors.centerIn: parent
model: emptyModelCheckbox.checked ? emptyModel : dappsModel
}
ListModel {
id: emptyModel
}
ListModel {
id: dappsModel
ListElement {
name: "Test dApp 1"
url: "https://dapp.test/1"
iconUrl: "https://se-sdk-dapp.vercel.app/assets/eip155:1.png"
}
ListElement {
name: "Test dApp 2"
url: "https://dapp.test/2"
iconUrl: "https://react-app.walletconnect.com/assets/eip155-1.png"
}
ListElement {
name: "Test dApp 3"
url: "https://dapp.test/3"
iconUrl: "https://react-app.walletconnect.com/assets/eip155-1.png"
}
ListElement {
name: "Test dApp 4 - very long name !!!!!!!!!!!!!!!!"
url: "https://dapp.test/4"
iconUrl: "https://react-app.walletconnect.com/assets/eip155-1.png"
}
ListElement {
name: "Test dApp 5 - very long url"
url: "https://dapp.test/very_long/url/unusual"
iconUrl: "https://react-app.walletconnect.com/assets/eip155-1.png"
}
ListElement {
name: "Test dApp 6"
url: "https://dapp.test/6"
iconUrl: "https://react-app.walletconnect.com/assets/eip155-1.png"
}
}
}
Pane {
id: controls
SplitView.preferredWidth: 300
SplitView.fillHeight: true
ColumnLayout {
CheckBox {
id: emptyModelCheckbox
text: "Empty model"
checked: false
}
}
}
}
// category: Controls
// https://www.figma.com/design/HrmZp1y4S77QJezRFRl6ku/dApp-Interactions---Milestone-1?node-id=130-31949&t=hnzB58fTnEnx2z84-0

View File

@ -14,6 +14,8 @@ public slots:
QGuiApplication::setOrganizationName(QStringLiteral("Status")); QGuiApplication::setOrganizationName(QStringLiteral("Status"));
QGuiApplication::setOrganizationDomain(QStringLiteral("status.im")); QGuiApplication::setOrganizationDomain(QStringLiteral("status.im"));
qputenv("QT_QUICK_CONTROLS_HOVER_ENABLED", QByteArrayLiteral("1"));
const QStringList additionalImportPaths { const QStringList additionalImportPaths {
STATUSQ_MODULE_IMPORT_PATH, STATUSQ_MODULE_IMPORT_PATH,
QML_IMPORT_ROOT + QStringLiteral("/../ui/app"), QML_IMPORT_ROOT + QStringLiteral("/../ui/app"),

View File

@ -537,7 +537,7 @@ Item {
// waitForRendering(controlUnderTest) // waitForRendering(controlUnderTest)
// compare(dappsListReadySpy.count, 1, "expected dappsListReady signal to be emitted") // compare(dappsListReadySpy.count, 1, "expected dappsListReady signal to be emitted")
// let popup = findChild(controlUnderTest, "dappsPopup") // let popup = findChild(controlUnderTest, "dappsListPopup")
// verify(!!popup) // verify(!!popup)
// verify(popup.opened) // verify(popup.opened)
@ -553,7 +553,7 @@ Item {
// mouseClick(controlUnderTest) // mouseClick(controlUnderTest)
// waitForRendering(controlUnderTest) // waitForRendering(controlUnderTest)
// let popup = findChild(controlUnderTest, "dappsPopup") // let popup = findChild(controlUnderTest, "dappsListPopup")
// verify(!!popup) // verify(!!popup)
// verify(popup.opened) // verify(popup.opened)

View File

@ -0,0 +1,100 @@
import QtQuick 2.15
import QtQml.Models 2.15
import QtTest 1.15
import AppLayouts.Wallet.controls 1.0
Item {
id: root
width: 600
height: 400
Component {
id: componentUnderTest
DappsComboBox {
anchors.centerIn: parent
model: ListModel {}
}
}
SignalSpy {
id: dappsListReadySpy
target: controlUnderTest
signalName: "dappsListReady"
}
property DappsComboBox controlUnderTest: null
TestCase {
name: "DappsComboBox"
when: windowShown
function init() {
controlUnderTest = createTemporaryObject(componentUnderTest, root)
dappsListReadySpy.clear()
}
function test_basicGeometry() {
verify(controlUnderTest.width > 0)
verify(controlUnderTest.height > 0)
}
function test_showStatusIndicator() {
const indicator = findChild(controlUnderTest, "dappBadge")
compare(indicator.visible, false)
controlUnderTest.model.append({name: "Test dApp 1", url: "https://dapp.test/1", iconUrl: "https://se-sdk-dapp.vercel.app/assets/eip155:1.png"})
compare(indicator.visible, true)
controlUnderTest.model.clear()
compare(indicator.visible, false)
}
function test_dappIcon() {
const icon = findChild(controlUnderTest, "dappIcon")
compare(icon.icon, "dapp")
compare(icon.width, 16)
compare(icon.height, 16)
compare(icon.status, Image.Ready)
}
function test_openingPopup() {
mouseClick(controlUnderTest)
const popup = findChild(controlUnderTest, "dappsListPopup")
compare(popup.visible, true)
compare(popup.x, controlUnderTest.width - popup.width)
compare(popup.y, controlUnderTest.height + 4)
compare(popup.width, 312)
verify(popup.height > 0)
compare(dappsListReadySpy.count, 1)
const background = findChild(controlUnderTest, "dappsBackground")
compare(background.active, true)
mouseClick(controlUnderTest)
compare(popup.visible, false)
compare(background.active, false)
}
function test_hoverState() {
const background = findChild(controlUnderTest, "dappsBackground")
compare(background.active, false)
mouseMove(controlUnderTest, controlUnderTest.width/2, controlUnderTest.height/2)
compare(background.active, true)
compare(controlUnderTest.hovered, true)
const dappTooltip = findChild(controlUnderTest, "dappTooltip")
wait(dappTooltip.delay + 50)
compare(dappTooltip.visible, true)
compare(dappTooltip.text, "dApp connections")
verify(dappTooltip.width > 0)
verify(dappTooltip.height > 0)
verify(dappTooltip.y > controlUnderTest.height)
mouseMove(root)
compare(background.active, false)
compare(dappTooltip.visible, false)
}
}
}

View File

@ -2,6 +2,7 @@ pragma Singleton
import QtQuick 2.14 import QtQuick 2.14
import StatusQ 0.1
import StatusQ.Internal 0.1 as Internal import StatusQ.Internal 0.1 as Internal
QtObject { QtObject {

View File

@ -1,27 +0,0 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import shared.controls 1.0
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
StatusButton {
id: root
implicitHeight: 38
size: StatusBaseButton.Size.Small
borderColor: Theme.palette.directColor7
normalColor: Theme.palette.transparent
hoverColor: Theme.palette.baseColor2
textPosition: StatusBaseButton.TextPosition.Left
textColor: Theme.palette.baseColor1
icon.name: "dapp"
icon.height: 16
icon.width: 16
icon.color: hovered ? Theme.palette.directColor1 : Theme.palette.baseColor1
}

View File

@ -0,0 +1,106 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtGraphicalEffects 1.15
import StatusQ 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
Item {
id: root
implicitHeight: 50
required property string name
required property string url
required property string iconUrl
signal disconnectDapp()
RowLayout {
anchors.fill: parent
anchors.margins: 8
Item {
Layout.preferredWidth: 32
Layout.preferredHeight: 32
StatusImage {
id: iconImage
anchors.fill: parent
source: iconUrl
visible: !fallbackImage.visible
}
StatusIcon {
id: fallbackImage
anchors.fill: parent
icon: "dapp"
color: Theme.palette.baseColor1
visible: iconImage.isLoading || iconImage.isError || !iconUrl
}
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Rectangle {
width: iconImage.width
height: iconImage.height
radius: width / 2
visible: false
}
}
}
ColumnLayout {
Layout.leftMargin: 12
Layout.rightMargin: 12
StatusBaseText {
text: name
Layout.fillWidth: true
font.pixelSize: 13
font.bold: true
elide: Text.ElideRight
clip: true
}
StatusBaseText {
text: url
Layout.fillWidth: true
font.pixelSize: 12
color: Theme.palette.baseColor1
elide: Text.ElideRight
clip: true
}
}
// TODO #14588 - Show tooltip on hover "Disconnect dApp"
StatusRoundButton {
implicitWidth: 32
implicitHeight: 32
radius: width / 2
icon.name: "disconnect"
onClicked: {
console.debug(`TODO #14755 - Disconnect ${name}`)
//root.disconnectDapp()
}
}
}
}

View File

@ -0,0 +1,86 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtGraphicalEffects 1.15
import shared.controls 1.0
import shared.popups.walletconnect 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import StatusQ.Components.private 0.1 as SQP
ComboBox {
id: root
signal dappsListReady
signal pairDapp
implicitHeight: 38
implicitWidth: 38
background: SQP.StatusComboboxBackground {
objectName: "dappsBackground"
active: root.down || root.hovered
}
indicator: null
contentItem: Item {
objectName: "dappsContentItem"
StatusBadge {
objectName: "dappBadge"
anchors.top: parent.top
anchors.right: parent.right
anchors.margins: 5
width: 6
height: 6
visible: root.delegateModel.count > 0
}
StatusIcon {
objectName: "dappIcon"
anchors.centerIn: parent
width: 16
height: 16
icon: "dapp"
color: Theme.palette.baseColor1
}
}
delegate: DAppDelegate {
width: ListView.view.width
}
popup: DAppsListPopup {
objectName: "dappsListPopup"
x: root.width - width
y: root.height + 4
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
delegateModel: root.delegateModel
onPairWCDapp: {
root.pairDapp()
this.close()
}
onOpened: {
root.dappsListReady()
}
}
StatusToolTip {
id: tooltip
objectName: "dappTooltip"
visible: root.hovered && !root.down
text: qsTr("dApp connections")
orientation: StatusToolTip.Orientation.Bottom
y: root.height + 14
}
}

View File

@ -14,8 +14,8 @@ MaxSendButton 1.0 MaxSendButton.qml
InformationTileAssetDetails 1.0 InformationTileAssetDetails.qml InformationTileAssetDetails 1.0 InformationTileAssetDetails.qml
StatusNetworkListItemTag 1.0 StatusNetworkListItemTag.qml StatusNetworkListItemTag 1.0 StatusNetworkListItemTag.qml
CollectibleBalanceTag 1.0 CollectibleBalanceTag.qml CollectibleBalanceTag 1.0 CollectibleBalanceTag.qml
ConnectedDappsButton 1.0 ConnectedDappsButton.qml
CollectibleLinksTags 1.0 CollectibleLinksTags.qml CollectibleLinksTags 1.0 CollectibleLinksTags.qml
DappsComboBox 1.0 DappsComboBox.qml
SwapExchangeButton 1.0 SwapExchangeButton.qml SwapExchangeButton 1.0 SwapExchangeButton.qml
EditSlippagePanel 1.0 EditSlippagePanel.qml EditSlippagePanel 1.0 EditSlippagePanel.qml
TokenSelector 1.0 TokenSelector.qml TokenSelector 1.0 TokenSelector.qml

View File

@ -11,19 +11,18 @@ import AppLayouts.Wallet.services.dapps.types 1.0
import shared.stores 1.0 import shared.stores 1.0
import utils 1.0 import utils 1.0
ConnectedDappsButton { DappsComboBox {
id: root id: root
required property WalletConnectService wcService required property WalletConnectService wcService
signal dappsListReady()
signal pairWCReady() signal pairWCReady()
onClicked: { model: root.wcService.dappsModel
dappsListLoader.active = true
}
highlighted: dappsListLoader.active onPairDapp: {
pairWCLoader.active = true
}
Loader { Loader {
id: pairWCLoader id: pairWCLoader
@ -47,34 +46,6 @@ ConnectedDappsButton {
} }
} }
Loader {
id: dappsListLoader
active: false
onLoaded: {
item.open()
root.dappsListReady()
}
sourceComponent: DAppsListPopup {
visible: true
model: root.wcService.dappsModel
onPairWCDapp: {
pairWCLoader.active = true
this.close()
}
onOpened: {
this.x = root.width - this.contentWidth - 2 * this.padding
this.y = root.height + 4
}
onClosed: dappsListLoader.active = false
}
}
Loader { Loader {
id: connectDappLoader id: connectDappLoader

View File

@ -1,5 +1,6 @@
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtQml.Models 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import QtGraphicalEffects 1.15 import QtGraphicalEffects 1.15
@ -15,10 +16,12 @@ Popup {
objectName: "dappsPopup" objectName: "dappsPopup"
required property var model required property DelegateModel delegateModel
signal pairWCDapp() signal pairWCDapp()
width: 312
modal: false modal: false
padding: 8 padding: 8
closePolicy: Popup.CloseOnEscape | Popup.CloseOnOutsideClick | Popup.CloseOnPressOutside closePolicy: Popup.CloseOnEscape | Popup.CloseOnOutsideClick | Popup.CloseOnPressOutside
@ -41,11 +44,9 @@ Popup {
} }
} }
ColumnLayout { contentItem: ColumnLayout {
id: mainLayout id: mainLayout
implicitWidth: 280
spacing: 8 spacing: 8
ShapeRectangle { ShapeRectangle {
@ -84,13 +85,9 @@ Popup {
Layout.preferredHeight: contentHeight Layout.preferredHeight: contentHeight
Layout.maximumHeight: 280 Layout.maximumHeight: 280
model: root.model model: root.delegateModel
visible: !listPlaceholder.visible visible: !listPlaceholder.visible
delegate: DAppDelegate {
implicitWidth: listView.width
}
ScrollBar.vertical: null ScrollBar.vertical: null
} }
@ -105,96 +102,4 @@ Popup {
} }
} }
} }
component DAppDelegate: Item {
implicitHeight: 50
required property string name
required property string url
required property string iconUrl
RowLayout {
anchors.fill: parent
anchors.margins: 8
Item {
Layout.preferredWidth: 32
Layout.preferredHeight: 32
StatusImage {
id: iconImage
anchors.fill: parent
source: iconUrl
visible: !fallbackImage.visible
}
StatusIcon {
id: fallbackImage
anchors.fill: parent
icon: "dapp"
color: Theme.palette.baseColor1
visible: iconImage.isLoading || iconImage.isError || !iconUrl
}
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Rectangle {
width: iconImage.width
height: iconImage.height
radius: width / 2
visible: false
}
}
}
ColumnLayout {
Layout.leftMargin: 12
Layout.rightMargin: 12
StatusBaseText {
text: name
Layout.fillWidth: true
font.pixelSize: 13
font.bold: true
elide: Text.ElideRight
clip: true
}
StatusBaseText {
text: url
Layout.fillWidth: true
font.pixelSize: 12
color: Theme.palette.baseColor1
elide: Text.ElideRight
clip: true
}
}
// TODO #14588 - Show tooltip on hover "Disconnect dApp"
StatusRoundButton {
implicitWidth: 32
implicitHeight: 32
radius: width / 2
icon.name: "disconnect"
onClicked: {
console.debug(`TODO #14755 - Disconnect ${name}`)
//root.disconnectDapp()
}
}
}
}
} }