diff --git a/storybook/pages/BannerCardPage.qml b/storybook/pages/BannerCardPage.qml new file mode 100644 index 0000000000..45b69b1a84 --- /dev/null +++ b/storybook/pages/BannerCardPage.qml @@ -0,0 +1,59 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 + +import AppLayouts.Wallet.controls 1.0 + +import StatusQ.Core.Theme 0.1 + +SplitView { + id: root + Pane { + id: leftPane + SplitView.fillWidth: true + SplitView.fillHeight: true + BannerCard { + anchors.centerIn: parent + title: titleField.text + subTitle: subTitleField.text + image: Theme.png("wallet/wallet-green") + closeEnabled: closeEnabled.checked + onClicked: { + print("BannerCard clicked") + } + onClose: { + print("BannerCard closed") + } + } + } + Pane { + id: rightPane + SplitView.fillHeight: true + SplitView.preferredWidth: 300 + ColumnLayout { + anchors.fill: parent + CheckBox { + id: closeEnabled + text: "Show close button" + checked: true + } + Label { + text: "Title" + } + TextField { + id: titleField + text: "Ways to buy crypto" + } + Label { + text: "Sub title" + } + TextField { + id: subTitleField + text: "Via card or bank transfer" + } + Item { + Layout.fillHeight: true + } + } + } +} diff --git a/storybook/pages/BuyReceiveBannerPage.qml b/storybook/pages/BuyReceiveBannerPage.qml new file mode 100644 index 0000000000..9b7643cb6e --- /dev/null +++ b/storybook/pages/BuyReceiveBannerPage.qml @@ -0,0 +1,74 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 + +import AppLayouts.Wallet.panels 1.0 + +import StatusQ.Core.Theme 0.1 + +SplitView { + id: root + Pane { + id: leftPane + SplitView.fillWidth: true + SplitView.fillHeight: true + BuyReceiveBanner { + id: banner + anchors.centerIn: parent + width: leftPane.width + closeEnabled: closeEnabled.checked + buyEnabled: buyEnabled.checked + receiveEnabled: receiveEnabled.checked + onBuyClicked: { + print("Buy clicked") + } + onReceiveClicked: { + print("Receive clicked") + } + onCloseBuy: { + buyEnabled.checked = false + } + onCloseReceive: { + receiveEnabled.checked = false + } + onAnyVisibleItemsChanged: { + print("Any visible: ", anyVisibleItems) + } + } + } + Pane { + id: rightPane + SplitView.fillHeight: true + SplitView.preferredWidth: 300 + ColumnLayout { + anchors.fill: parent + Label { + text: "Any visible items" + } + Rectangle { + width: 30 + height: 30 + radius: 15 + color: banner.anyVisibleItems ? "green" : "red" + } + CheckBox { + id: closeEnabled + text: "Show close button" + checked: true + } + CheckBox { + id: buyEnabled + text: "Show buy button" + checked: false + } + CheckBox { + id: receiveEnabled + text: "Show receive button" + checked: false + } + Item { + Layout.fillHeight: true + } + } + } +} diff --git a/storybook/qmlTests/tests/tst_BannerCard.qml b/storybook/qmlTests/tests/tst_BannerCard.qml new file mode 100644 index 0000000000..be43800a89 --- /dev/null +++ b/storybook/qmlTests/tests/tst_BannerCard.qml @@ -0,0 +1,76 @@ +import QtQuick 2.15 +import QtTest 1.15 + +import AppLayouts.Wallet.controls 1.0 + +Item { + id: root + width: 600 + height: 400 + + Component { + id: bannerCardComponent + + BannerCard {} + } + + TestCase { + id: bannerCardTest + + when: windowShown + + property BannerCard componentUnderTest + + function init() { + componentUnderTest = createTemporaryObject(bannerCardComponent, root) + } + + function test_empty() { + compare(componentUnderTest.image, "") + compare(componentUnderTest.title, "") + compare(componentUnderTest.subTitle, "") + compare(componentUnderTest.closeEnabled, true) + } + + function test_geometry() { + verify(componentUnderTest.height > 0) + verify(componentUnderTest.width > 0) + } + + function test_hoverState() { + compare(componentUnderTest.hovered, false) + mouseMove(componentUnderTest) + compare(componentUnderTest.hovered, true) + + const closeButton = findChild(componentUnderTest, "bannerCard_closeButton") + verify(!!closeButton) + verify(closeButton.visible) + verify(closeButton.width > 0) + verify(closeButton.height > 0) + + mouseMove(closeButton, closeButton.width / 2, closeButton.height / 2) + compare(componentUnderTest.hovered, true) + } + + function test_click() { + let clicked = false + componentUnderTest.clicked.connect(() => { + clicked = true + }) + mouseClick(componentUnderTest) + compare(clicked, true) + + clicked = false + + const closeButton = findChild(componentUnderTest, "bannerCard_closeButton") + + let closed = false + componentUnderTest.close.connect(() => { + closed = true + }) + mouseClick(closeButton) + compare(closed, true) + compare(clicked, false) + } + } +} diff --git a/storybook/qmlTests/tests/tst_BuyReceiveBanner.qml b/storybook/qmlTests/tests/tst_BuyReceiveBanner.qml new file mode 100644 index 0000000000..30302c3bef --- /dev/null +++ b/storybook/qmlTests/tests/tst_BuyReceiveBanner.qml @@ -0,0 +1,161 @@ +import QtQuick 2.15 +import QtTest 1.15 + +import AppLayouts.Wallet.panels 1.0 + +Item { + id: root + width: 600 + height: 400 + + Component { + id: buyReceiveComponent + BuyReceiveBanner { + id: banner + anchors.fill: parent + + readonly property SignalSpy buyClickedSpy: SignalSpy { target: banner; signalName: "buyClicked" } + readonly property SignalSpy receiveClickedSpy: SignalSpy { target: banner; signalName: "receiveClicked" } + readonly property SignalSpy closeBuySpy: SignalSpy { target: banner; signalName: "closeBuy" } + readonly property SignalSpy closeReceiveSpy: SignalSpy { target: banner; signalName: "closeReceive" } + } + } + + TestCase { + id: buyReceiveBannerTest + when: windowShown + + property BuyReceiveBanner componentUnderTest + + function init() { + componentUnderTest = createTemporaryObject(buyReceiveComponent, root) + } + + function test_empty() { + verify(componentUnderTest.closeEnabled) + verify(componentUnderTest.buyEnabled) + verify(componentUnderTest.receiveEnabled) + verify(componentUnderTest.anyVisibleItems) + } + + function test_geometry() { + compare(componentUnderTest.width, root.width) + compare(componentUnderTest.height, root.height) + } + + function test_buyGeometry() { + const buyCard = findChild(componentUnderTest, "buyCard") + verify(!!buyCard) + verify(buyCard.visible) + verify(buyCard.opacity > 0) + verify(buyCard.width > 0) + verify(buyCard.height > 0) + verify(buyCard.x >= 0) + verify(buyCard.x <= buyCard.parent.width - buyCard.width) + verify(buyCard.y >= 0) + verify(buyCard.y <= buyCard.parent.height - buyCard.height) + } + + function test_receiveGeometry() { + const receiveCard = findChild(componentUnderTest, "receiveCard") + verify(!!receiveCard) + verify(receiveCard.visible) + verify(receiveCard.opacity > 0) + verify(receiveCard.width > 0) + verify(receiveCard.height > 0) + verify(receiveCard.x >= 0) + verify(receiveCard.x <= receiveCard.parent.width - receiveCard.width) + verify(receiveCard.y >= 0) + verify(receiveCard.y <= receiveCard.parent.height - receiveCard.height) + } + + function test_clickBuy() { + const buyCard = findChild(componentUnderTest, "buyCard") + verify(!!buyCard) + + compare(componentUnderTest.buyClickedSpy.count, 0) + mouseClick(buyCard) + compare(componentUnderTest.buyClickedSpy.count, 1) + } + + function test_receiveClicked() { + const receiveCard = findChild(componentUnderTest, "receiveCard") + verify(!!receiveCard) + + compare(componentUnderTest.receiveClickedSpy.count, 0) + mouseClick(receiveCard) + compare(componentUnderTest.receiveClickedSpy.count, 1) + } + + function test_closeBuy() { + const buyCard = findChild(componentUnderTest, "buyCard") + verify(!!buyCard) + const closeButton = findChild(buyCard, "bannerCard_closeButton") + verify(!!closeButton) + verify(closeButton.visible) + + compare(componentUnderTest.closeBuySpy.count, 0) + mouseClick(closeButton) + compare(componentUnderTest.closeBuySpy.count, 1) + } + + function test_closeReceive() { + const receiveCard = findChild(componentUnderTest, "receiveCard") + verify(!!receiveCard) + const closeButton = findChild(receiveCard, "bannerCard_closeButton") + verify(!!closeButton) + verify(closeButton.visible) + + compare(componentUnderTest.closeReceiveSpy.count, 0) + mouseClick(closeButton) + compare(componentUnderTest.closeReceiveSpy.count, 1) + } + + function test_anyVisibleItemsChanged() { + compare(componentUnderTest.anyVisibleItems, true) + compare(componentUnderTest.buyEnabled, true) + compare(componentUnderTest.receiveEnabled, true) + + componentUnderTest.buyEnabled = false + compare(componentUnderTest.anyVisibleItems, true) + + componentUnderTest.receiveEnabled = false + tryVerify(() => componentUnderTest.anyVisibleItems, false) + } + + function test_closeDisabled() { + const buyCard = findChild(componentUnderTest, "buyCard") + verify(!!buyCard) + const closeButton = findChild(buyCard, "bannerCard_closeButton") + verify(!!closeButton) + verify(closeButton.visible) + + componentUnderTest.closeEnabled = false + verify(!closeButton.visible) + + const receiveCard = findChild(componentUnderTest, "receiveCard") + verify(!!receiveCard) + const receiveCloseButton = findChild(receiveCard, "bannerCard_closeButton") + verify(!!receiveCloseButton) + verify(!receiveCloseButton.visible) + } + + function test_hideBuy() { + const buyCard = findChild(componentUnderTest, "buyCard") + verify(!!buyCard) + verify(buyCard.visible) + + componentUnderTest.buyEnabled = false + tryVerify(() => !buyCard.visible) + } + + function test_hideReceive() { + const receiveCard = findChild(componentUnderTest, "receiveCard") + verify(!!receiveCard) + verify(receiveCard.visible) + + componentUnderTest.receiveEnabled = false + tryVerify(() => !receiveCard.visible) + } + } +} diff --git a/ui/StatusQ/src/assets/png/wallet/flying-coin.png b/ui/StatusQ/src/assets/png/wallet/flying-coin.png new file mode 100644 index 0000000000..6703d2b671 Binary files /dev/null and b/ui/StatusQ/src/assets/png/wallet/flying-coin.png differ diff --git a/ui/StatusQ/src/assets/png/wallet/wallet-green.png b/ui/StatusQ/src/assets/png/wallet/wallet-green.png new file mode 100644 index 0000000000..874c5ceea4 Binary files /dev/null and b/ui/StatusQ/src/assets/png/wallet/wallet-green.png differ diff --git a/ui/app/AppLayouts/Wallet/controls/BannerCard.qml b/ui/app/AppLayouts/Wallet/controls/BannerCard.qml new file mode 100644 index 0000000000..4aa471314a --- /dev/null +++ b/ui/app/AppLayouts/Wallet/controls/BannerCard.qml @@ -0,0 +1,101 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import QtGraphicalEffects 1.15 + +import StatusQ.Components 0.1 +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 + +import utils 1.0 + +Control { + id: root + + property alias image: image.source + property alias title: title.text + property alias subTitle: subTitle.text + property alias closeEnabled: closeButton.visible + + signal clicked() + signal close() + + implicitHeight: 70 + implicitWidth: 400 + + TapHandler { + acceptedButtons: Qt.LeftButton + enabled: !closeHandler.pressed + onTapped: { + root.clicked() + } + } + HoverHandler { + cursorShape: Qt.PointingHandCursor + } + background: Rectangle { + id: background + anchors.fill: parent + color: Theme.palette.background + radius: 12 + border.width: 1 + border.color: Theme.palette.baseColor2 + layer.enabled: true + layer.effect: DropShadow { + source: background + horizontalOffset: 0 + verticalOffset: 7 + radius: 8 + spread: root.hovered ? 0.3 : 0 + color: Theme.palette.baseColor2 + } + } + contentItem: RowLayout { + id: layout + spacing: Theme.halfPadding + StatusImage { + id: image + Layout.preferredWidth: 36 + Layout.preferredHeight: 36 + Layout.leftMargin: 20 + } + ColumnLayout { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.margins: layout.spacing + StatusBaseText { + id: title + Layout.fillWidth: true + color: Theme.palette.directColor1 + font.pixelSize: Theme.additionalTextSize + font.weight: Font.Medium + elide: Text.ElideRight + } + StatusBaseText { + id: subTitle + Layout.fillWidth: true + color: Theme.palette.baseColor1 + font.pixelSize: Theme.additionalTextSize + elide: Text.ElideRight + } + } + StatusIcon { + id: closeButton + objectName: "bannerCard_closeButton" + Layout.topMargin: layout.spacing + Layout.rightMargin: layout.spacing + Layout.alignment: Qt.AlignTop + icon: "close" + width: 16 + height: 16 + color: Theme.palette.baseColor1 + TapHandler { + id: closeHandler + acceptedButtons: Qt.LeftButton + onTapped: { + root.close() + } + } + } + } +} diff --git a/ui/app/AppLayouts/Wallet/controls/qmldir b/ui/app/AppLayouts/Wallet/controls/qmldir index bce441eaaa..e033e8ca1d 100644 --- a/ui/app/AppLayouts/Wallet/controls/qmldir +++ b/ui/app/AppLayouts/Wallet/controls/qmldir @@ -2,6 +2,7 @@ AccountHeaderGradient 1.0 AccountHeaderGradient.qml ActivityFilterTagItem 1.0 ActivityFilterTagItem.qml AssetSelector 1.0 AssetSelector.qml AssetSelectorCompact 1.0 AssetSelectorCompact.qml +BannerCard 1.0 BannerCard.qml BuyCryptoProvidersDelegate 1.0 BuyCryptoProvidersDelegate.qml BuyCryptoProvidersLoadingDelegate 1.0 BuyCryptoProvidersLoadingDelegate.qml CollectibleBalanceTag 1.0 CollectibleBalanceTag.qml diff --git a/ui/app/AppLayouts/Wallet/panels/BuyReceiveBanner.qml b/ui/app/AppLayouts/Wallet/panels/BuyReceiveBanner.qml new file mode 100644 index 0000000000..678447d8af --- /dev/null +++ b/ui/app/AppLayouts/Wallet/panels/BuyReceiveBanner.qml @@ -0,0 +1,80 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 + +import AppLayouts.Wallet.controls 1.0 + +import StatusQ.Core.Theme 0.1 + +Control { + id: root + + property bool closeEnabled: true + property bool buyEnabled: true + property bool receiveEnabled: true + + readonly property bool anyVisibleItems: buyCard.visible || receiveCard.visible + + signal buyClicked() + signal receiveClicked() + signal closeBuy() + signal closeReceive() + + contentItem: RowLayout { + id: layout + spacing: 16 + + BannerCard { + id: buyCard + objectName: "buyCard" + Layout.fillWidth: true + title: qsTr("Ways to buy crypto") + subTitle: qsTr("Via card or bank transfer") + image: Theme.png("wallet/wallet-green") + closeEnabled: root.closeEnabled + visible: implicitWidth > 0 + implicitWidth: opacity > 0 ? layout.width / 2 : 0 + opacity: root.buyEnabled ? 1 : 0 + + onClose: root.closeBuy() + onClicked: root.buyClicked() + + Behavior on opacity { + NumberAnimation { + duration: 250 + } + } + Behavior on implicitWidth { + NumberAnimation { + duration: 250 + } + } + } + BannerCard { + id: receiveCard + objectName: "receiveCard" + Layout.fillWidth: true + title: qsTr("Receive crypto") + subTitle: qsTr("Deposit to your Wallet address") + image: Theme.png("wallet/flying-coin") + closeEnabled: root.closeEnabled + visible: implicitWidth > 0 + implicitWidth: opacity > 0 ? layout.width / 2 : 0 + opacity: root.receiveEnabled ? 1 : 0 + + onClose: root.closeReceive() + onClicked: root.receiveClicked() + + Behavior on opacity { + NumberAnimation { + duration: 250 + } + } + Behavior on implicitWidth { + NumberAnimation { + duration: 250 + } + } + } + } +} diff --git a/ui/app/AppLayouts/Wallet/panels/qmldir b/ui/app/AppLayouts/Wallet/panels/qmldir index 7ca46bedf6..9e0dab41c8 100644 --- a/ui/app/AppLayouts/Wallet/panels/qmldir +++ b/ui/app/AppLayouts/Wallet/panels/qmldir @@ -1,5 +1,6 @@ ActivityFilterPanel 1.0 ActivityFilterPanel.qml BuyCryptoProvidersListPanel 1.0 BuyCryptoProvidersListPanel.qml +BuyReceiveBanner 1.0 BuyReceiveBanner.qml ContractInfoButtonWithMenu 1.0 ContractInfoButtonWithMenu.qml DAppsWorkflow 1.0 DAppsWorkflow.qml ManageAssetsPanel 1.0 ManageAssetsPanel.qml