diff --git a/storybook/pages/AssetsViewPage.qml b/storybook/pages/AssetsViewPage.qml
index 703c1002a8..e2bbe75750 100644
--- a/storybook/pages/AssetsViewPage.qml
+++ b/storybook/pages/AssetsViewPage.qml
@@ -8,9 +8,12 @@ import utils 1.0
import Storybook 1.0
import AppLayouts.Wallet.controls 1.0
+import AppLayouts.Wallet.panels 1.0
import Qt.labs.settings 1.1
+import StatusQ.Popups.Dialog 0.1
+
SplitView {
id: root
@@ -196,6 +199,20 @@ SplitView {
onHideRequested: logs.logEvent(`hide requested: ${key}`)
onHideCommunityAssetsRequested: logs.logEvent(`hide community assets requested: ${communityKey}`)
onManageTokensRequested: logs.logEvent(`manage tokens requested`)
+
+ bannerComponent: buyReceiveBannerComponent
+
+ Component {
+ id: buyReceiveBannerComponent
+ BuyReceiveBanner {
+ id: banner
+ topPadding: anyVisibleItems ? 8 : 0
+ bottomPadding: anyVisibleItems ? 20 : 0
+
+ onCloseBuy: buyEnabled = false
+ onCloseReceive: receiveEnabled = false
+ }
+ }
}
}
@@ -226,6 +243,7 @@ SplitView {
id: sorterVisibleCheckBox
text: "sorter visible"
+ checked: false
}
CheckBox {
id: customOrderAvailableCheckBox
diff --git a/storybook/pages/CollectiblesViewPage.qml b/storybook/pages/CollectiblesViewPage.qml
index 7bcf9713c4..8fce8f164e 100644
--- a/storybook/pages/CollectiblesViewPage.qml
+++ b/storybook/pages/CollectiblesViewPage.qml
@@ -14,6 +14,7 @@ import utils 1.0
import AppLayouts.stores 1.0 as AppLayoutStores
import AppLayouts.Communities.stores 1.0 as CommunitiesStore
+import AppLayouts.Wallet.panels 1.0
import AppLayouts.Wallet.views 1.0
import AppLayouts.Wallet.stores 1.0
@@ -128,6 +129,15 @@ SplitView {
isUpdating: ctrlUpdatingCheckbox.checked
isFetching: ctrlFetchingCheckbox.checked
isError: ctrlErrorCheckbox.checked
+ bannerComponent: BuyReceiveBanner {
+ id: buyReceiveBanner
+ buyEnabled: buyBannerCheckbox.checked
+ receiveEnabled: receiveBannerCheckbox.checked
+ onBuyClicked: logs.logEvent("onBuyClicked")
+ onReceiveClicked: logs.logEvent("onReceiveClicked")
+ onCloseBuy: buyBannerCheckbox.checked = false
+ onCloseReceive: receiveBannerCheckbox.checked = false
+ }
Settings {
id: settingsStore
@@ -178,6 +188,16 @@ SplitView {
checked: false
text: "isError"
}
+ CheckBox {
+ id: buyBannerCheckbox
+ checked: true
+ text: "buy banner visible"
+ }
+ CheckBox {
+ id: receiveBannerCheckbox
+ checked: true
+ text: "sell banner visible"
+ }
ColumnLayout {
Layout.fillWidth: true
diff --git a/storybook/qmlTests/tests/tst_BannerCard.qml b/storybook/qmlTests/tests/tst_BannerCard.qml
index be43800a89..199a491413 100644
--- a/storybook/qmlTests/tests/tst_BannerCard.qml
+++ b/storybook/qmlTests/tests/tst_BannerCard.qml
@@ -39,7 +39,7 @@ Item {
function test_hoverState() {
compare(componentUnderTest.hovered, false)
- mouseMove(componentUnderTest)
+ mouseMove(componentUnderTest, componentUnderTest.width / 2, componentUnderTest.height / 2)
compare(componentUnderTest.hovered, true)
const closeButton = findChild(componentUnderTest, "bannerCard_closeButton")
@@ -68,6 +68,7 @@ Item {
componentUnderTest.close.connect(() => {
closed = true
})
+ mouseMove(closeButton, closeButton.width / 2, closeButton.height / 2)
mouseClick(closeButton)
compare(closed, true)
compare(clicked, false)
diff --git a/storybook/qmlTests/tests/tst_BuyReceiveBanner.qml b/storybook/qmlTests/tests/tst_BuyReceiveBanner.qml
index 30302c3bef..99a1867a26 100644
--- a/storybook/qmlTests/tests/tst_BuyReceiveBanner.qml
+++ b/storybook/qmlTests/tests/tst_BuyReceiveBanner.qml
@@ -91,6 +91,7 @@ Item {
const buyCard = findChild(componentUnderTest, "buyCard")
verify(!!buyCard)
const closeButton = findChild(buyCard, "bannerCard_closeButton")
+ mouseMove(buyCard, buyCard.width / 2, buyCard.height / 2)
verify(!!closeButton)
verify(closeButton.visible)
@@ -103,6 +104,7 @@ Item {
const receiveCard = findChild(componentUnderTest, "receiveCard")
verify(!!receiveCard)
const closeButton = findChild(receiveCard, "bannerCard_closeButton")
+ mouseMove(receiveCard, receiveCard.width / 2, receiveCard.height / 2)
verify(!!closeButton)
verify(closeButton.visible)
@@ -127,6 +129,7 @@ Item {
const buyCard = findChild(componentUnderTest, "buyCard")
verify(!!buyCard)
const closeButton = findChild(buyCard, "bannerCard_closeButton")
+ mouseMove(buyCard, buyCard.width / 2, buyCard.height / 2)
verify(!!closeButton)
verify(closeButton.visible)
diff --git a/ui/StatusQ/src/assets.qrc b/ui/StatusQ/src/assets.qrc
index bfbc6ba698..eb1dc51287 100644
--- a/ui/StatusQ/src/assets.qrc
+++ b/ui/StatusQ/src/assets.qrc
@@ -8965,6 +8965,8 @@
assets/png/traffic_lights/maximize_pressed.png
assets/png/traffic_lights/minimise.png
assets/png/traffic_lights/minimise_pressed.png
+ assets/png/wallet/wallet-green.png
+ assets/png/wallet/flying-coin.png
assets/png/appearance-dark.png
assets/png/appearance-light.png
assets/png/appearance-system.png
diff --git a/ui/app/AppLayouts/Wallet/WalletLayout.qml b/ui/app/AppLayouts/Wallet/WalletLayout.qml
index a6b278ee79..2798ad97ac 100644
--- a/ui/app/AppLayouts/Wallet/WalletLayout.qml
+++ b/ui/app/AppLayouts/Wallet/WalletLayout.qml
@@ -179,6 +179,17 @@ Item {
RootStore.selectedAddress :
StatusQUtils.ModelUtils.get(RootStore.nonWatchAccounts, 0, "address")
}
+
+ function launchBuyCryptoModal() {
+ const walletStore = RootStore
+
+ d.buyFormData.selectedWalletAddress = d.getSelectedOrFirstNonWatchedAddress()
+ d.buyFormData.selectedNetworkChainId = StatusQUtils.ModelUtils.getByKey(RootStore.filteredFlatModel, "layer", 1, "chainId")
+ if(!!walletStore.currentViewedHoldingTokensKey && walletStore.currentViewedHoldingType === Constants.TokenType.ERC20) {
+ d.buyFormData.selectedTokenKey = walletStore.currentViewedHoldingTokensKey
+ }
+ Global.openBuyCryptoModalRequested(d.buyFormData)
+ }
}
SignPhraseModal {
@@ -235,6 +246,7 @@ Item {
d.swapFormData.defaultToTokenKey = RootStore.areTestNetworksEnabled ? Constants.swap.testStatusTokenKey : Constants.swap.mainnetStatusTokenKey
Global.openSwapModalRequested(d.swapFormData)
}
+ onLaunchBuyCryptoModal: d.launchBuyCryptoModal()
}
}
@@ -364,14 +376,7 @@ Item {
d.swapFormData.defaultToTokenKey = RootStore.areTestNetworksEnabled ? Constants.swap.testStatusTokenKey : Constants.swap.mainnetStatusTokenKey
Global.openSwapModalRequested(d.swapFormData)
}
- onLaunchBuyCryptoModal: {
- d.buyFormData.selectedWalletAddress = d.getSelectedOrFirstNonWatchedAddress()
- d.buyFormData.selectedNetworkChainId = StatusQUtils.ModelUtils.getByKey(RootStore.filteredFlatModel, "layer", 1, "chainId")
- if(!!walletStore.currentViewedHoldingTokensKey && walletStore.currentViewedHoldingType === Constants.TokenType.ERC20) {
- d.buyFormData.selectedTokenKey = walletStore.currentViewedHoldingTokensKey
- }
- Global.openBuyCryptoModalRequested(d.buyFormData)
- }
+ onLaunchBuyCryptoModal: d.launchBuyCryptoModal()
ModelEntry {
id: selectedCommunityForCollectible
diff --git a/ui/app/AppLayouts/Wallet/controls/BannerCard.qml b/ui/app/AppLayouts/Wallet/controls/BannerCard.qml
index fcb4471811..9fd04536cb 100644
--- a/ui/app/AppLayouts/Wallet/controls/BannerCard.qml
+++ b/ui/app/AppLayouts/Wallet/controls/BannerCard.qml
@@ -15,7 +15,7 @@ Control {
property alias image: image.source
property alias title: title.text
property alias subTitle: subTitle.text
- property alias closeEnabled: closeButton.visible
+ property bool closeEnabled: true
signal clicked()
signal close()
@@ -86,7 +86,8 @@ Control {
Layout.preferredWidth: 16
Layout.preferredHeight: 16
icon: "close"
- color: Theme.palette.baseColor1
+ color: closeHoverHandler.hovered ? Theme.palette.directColor1 : Theme.palette.baseColor1
+ visible: root.closeEnabled && root.hovered
TapHandler {
id: closeHandler
acceptedButtons: Qt.LeftButton
@@ -94,6 +95,9 @@ Control {
root.close()
}
}
+ HoverHandler {
+ id: closeHoverHandler
+ }
}
}
}
diff --git a/ui/app/AppLayouts/Wallet/panels/BuyReceiveBanner.qml b/ui/app/AppLayouts/Wallet/panels/BuyReceiveBanner.qml
index ff4a7b1c92..6cadf0cbe3 100644
--- a/ui/app/AppLayouts/Wallet/panels/BuyReceiveBanner.qml
+++ b/ui/app/AppLayouts/Wallet/panels/BuyReceiveBanner.qml
@@ -29,7 +29,7 @@ Control {
objectName: "buyCard"
Layout.fillWidth: true
Layout.preferredWidth: root.buyEnabled ? layout.width / layout.children.length : 0
- title: qsTr("Ways to buy crypto")
+ title: qsTr("Ways to buy assets")
subTitle: qsTr("Via card or bank transfer")
image: Theme.png("wallet/wallet-green")
closeEnabled: root.closeEnabled
@@ -51,7 +51,7 @@ Control {
objectName: "receiveCard"
Layout.fillWidth: true
Layout.preferredWidth: root.receiveEnabled ? layout.width / layout.children.length : 0
- title: qsTr("Receive crypto")
+ title: qsTr("Receive assets")
subTitle: qsTr("Deposit to your Wallet address")
image: Theme.png("wallet/flying-coin")
closeEnabled: root.closeEnabled
diff --git a/ui/app/AppLayouts/Wallet/views/CollectiblesView.qml b/ui/app/AppLayouts/Wallet/views/CollectiblesView.qml
index 04d3ada243..081e8177de 100644
--- a/ui/app/AppLayouts/Wallet/views/CollectiblesView.qml
+++ b/ui/app/AppLayouts/Wallet/views/CollectiblesView.qml
@@ -36,6 +36,7 @@ ColumnLayout {
property bool isFetching: false // Indicates if a collectibles page is being loaded from the backend
property bool isUpdating: false // Indicates if the collectibles list is being updated
property bool isError: false // Indicates an error occurred while updating/fetching the collectibles list
+ property alias bannerComponent: banner.sourceComponent
// allows/disables choosing custom sort order from a sorter
property bool customOrderAvailable
@@ -436,6 +437,11 @@ ColumnLayout {
}
}
+ Loader {
+ id: banner
+ Layout.fillWidth: true
+ }
+
ShapeRectangle {
Layout.fillWidth: true
Layout.topMargin: Theme.padding
diff --git a/ui/app/AppLayouts/Wallet/views/RightTabView.qml b/ui/app/AppLayouts/Wallet/views/RightTabView.qml
index 2109440cb3..345cc5bba0 100644
--- a/ui/app/AppLayouts/Wallet/views/RightTabView.qml
+++ b/ui/app/AppLayouts/Wallet/views/RightTabView.qml
@@ -7,6 +7,7 @@ import StatusQ.Controls 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1
+import StatusQ.Popups.Dialog 0.1
import AppLayouts.Wallet.controls 1.0
@@ -35,6 +36,7 @@ RightTabBaseView {
property alias currentTabIndex: walletTabBar.currentIndex
signal launchShareAddressModal()
+ signal launchBuyCryptoModal()
signal launchSwapModal(string tokensKey)
function resetView() {
@@ -78,6 +80,30 @@ RightTabBaseView {
readonly property var detailedCollectibleActivityController: RootStore.tmpActivityController0
}
+ Settings {
+ id: walletSettings
+ category: "walletSettings-" + root.contactsStore.myPublicKey
+ property real collectiblesViewCustomOrderApplyTimestamp: 0
+ property bool buyBannerEnabled: true
+ property bool receiveBannerEnabled: true
+ }
+
+ Component {
+ id: buyReceiveBannerComponent
+ BuyReceiveBanner {
+ id: banner
+ topPadding: anyVisibleItems ? 8 : 0
+ bottomPadding: anyVisibleItems ? 20 : 0
+
+ onBuyClicked: root.launchBuyCryptoModal()
+ onReceiveClicked: root.launchShareAddressModal()
+ buyEnabled: walletSettings.buyBannerEnabled
+ receiveEnabled: walletSettings.receiveBannerEnabled
+ onCloseBuy: walletSettings.buyBannerEnabled = false
+ onCloseReceive: walletSettings.receiveBannerEnabled = false
+ }
+ }
+
Component {
id: confirmHideCommunityAssetsPopup
@@ -258,6 +284,7 @@ RightTabBaseView {
sorterVisible: filterButton.checked
customOrderAvailable: RootStore.walletAssetsStore.assetsController.hasSettings
model: assetsViewAdaptor.model
+ bannerComponent: buyReceiveBannerComponent
marketDataError: !!root.networkConnectionStore
? root.networkConnectionStore.getMarketNetworkDownText()
@@ -360,12 +387,6 @@ RightTabBaseView {
Component.onCompleted: refreshSortSettings()
Component.onDestruction: saveSortSettings()
- readonly property Settings walletSettings: Settings {
- id: walletSettings
- category: "walletSettings-" + root.contactsStore.myPublicKey
- property real collectiblesViewCustomOrderApplyTimestamp: 0
- }
-
readonly property Settings settings: Settings {
id: settings
property int currentSortValue: SortOrderComboBox.TokenOrderDateAdded
@@ -381,6 +402,7 @@ RightTabBaseView {
sendEnabled: root.networkConnectionStore.sendBuyBridgeEnabled && !RootStore.overview.isWatchOnlyAccount && RootStore.overview.canSend
filterVisible: filterButton.checked
customOrderAvailable: controller.hasSettings
+ bannerComponent: buyReceiveBannerComponent
onCollectibleClicked: {
RootStore.collectiblesStore.getDetailedCollectible(chainId, contractAddress, tokenId)
RootStore.setCurrentViewedHolding(uid, uid, tokenType, communityId)
@@ -438,6 +460,7 @@ RightTabBaseView {
currencyStore: root.sharedRootStore.currencyStore
showAllAccounts: RootStore.showAllAccounts
filterVisible: false // TODO #16761: Re-enable filter for activity when implemented
+ bannerComponent: buyReceiveBannerComponent
}
}
}
diff --git a/ui/imports/shared/views/AssetsView.qml b/ui/imports/shared/views/AssetsView.qml
index ab652960b6..bd4f1ffb62 100644
--- a/ui/imports/shared/views/AssetsView.qml
+++ b/ui/imports/shared/views/AssetsView.qml
@@ -64,6 +64,8 @@ Control {
property bool communitySwapVisible: false
property string balanceError
+ // banner component to be displayed on top of the list
+ property alias bannerComponent: banner.sourceComponent
// global market data error, presented for all tokens expecting market data
property string marketDataError
@@ -218,6 +220,11 @@ Control {
StatusDialogDivider { Layout.fillWidth: true }
}
+ Loader {
+ id: banner
+ Layout.fillWidth: true
+ }
+
DelegateModel {
id: regularModel
diff --git a/ui/imports/shared/views/HistoryView.qml b/ui/imports/shared/views/HistoryView.qml
index 3d19563e84..b97377da46 100644
--- a/ui/imports/shared/views/HistoryView.qml
+++ b/ui/imports/shared/views/HistoryView.qml
@@ -42,6 +42,9 @@ ColumnLayout {
property bool hideVerticalScrollbar: false
property int firstItemOffset: 0
+ // banner component to be displayed on top of the list
+ property alias bannerComponent: banner.sourceComponent
+
property real yPosition: transactionListRoot.visibleArea.yPosition * transactionListRoot.contentHeight
function resetView() {
@@ -127,16 +130,6 @@ ColumnLayout {
wrapMode: Text.WordWrap
}
- ShapeRectangle {
- id: noTxs
- Layout.fillWidth: true
- Layout.preferredHeight: 42
- Layout.topMargin: !nonArchivalNodeError.visible? root.firstItemOffset : 0
- visible: !d.isInitialLoading && !root.walletRootStore.currentActivityFiltersStore.filtersSet && transactionListRoot.count === 0
- font.pixelSize: Theme.primaryTextFontSize
- text: qsTr("Activity for this account will appear here")
- }
-
Loader {
id: filterPanelLoader
active: root.filterVisible && (d.isInitialLoading || transactionListRoot.count > 0 || root.walletRootStore.currentActivityFiltersStore.filtersSet)
@@ -151,6 +144,21 @@ ColumnLayout {
}
}
+ Loader {
+ id: banner
+ Layout.fillWidth: true
+ }
+
+ ShapeRectangle {
+ id: noTxs
+ Layout.fillWidth: true
+ Layout.preferredHeight: 42
+ Layout.topMargin: !nonArchivalNodeError.visible? root.firstItemOffset : 0
+ visible: !d.isInitialLoading && !root.walletRootStore.currentActivityFiltersStore.filtersSet && transactionListRoot.count === 0
+ font.pixelSize: Theme.primaryTextFontSize
+ text: qsTr("Activity for this account will appear here")
+ }
+
Item {
id: transactionListWrapper
Layout.alignment: Qt.AlignTop