mirror of
https://github.com/status-im/status-desktop.git
synced 2025-01-11 06:47:01 +00:00
fa4755ce9e
+ store the card state in user settings + amend the BannerCard close button state. It needs to change color on hover and to become visible only when the card is hovered.
341 lines
12 KiB
QML
341 lines
12 KiB
QML
import QtQuick 2.15
|
|
import QtQuick.Controls 2.15
|
|
import QtQuick.Layouts 1.15
|
|
|
|
import QtQml.Models 2.15
|
|
|
|
import StatusQ 0.1
|
|
import StatusQ.Core 0.1
|
|
import StatusQ.Core.Theme 0.1
|
|
import StatusQ.Popups.Dialog 0.1
|
|
|
|
import AppLayouts.Wallet.controls 1.0
|
|
import shared.controls 1.0
|
|
import shared.popups 1.0
|
|
import utils 1.0
|
|
|
|
import SortFilterProxyModel 0.2
|
|
|
|
|
|
Control {
|
|
id: root
|
|
|
|
/**
|
|
Expected model structure:
|
|
|
|
key [string] - unique identifier of a token, e.g "0x3234235"
|
|
symbol [string] - token's symbol e.g. "ETH" or "SNT"
|
|
name [string] - token's name e.g. "Ether" or "Dai"
|
|
icon [url] - token's icon url
|
|
balance [double] - tokens balance is the commonly used unit, e.g. 1.2 for 1.2 ETH, used
|
|
for sorting and computing market value
|
|
balanceText [string] - formatted and localized balance. This is not done internally because
|
|
it may depend on many external factors
|
|
error [string] - error message related to balance
|
|
|
|
marketDetailsAvailable [bool] - specifies if market datails are available for given token
|
|
marketDetailsLoading [bool] - specifies if market datails are available for given token
|
|
marketPrice [double] - specifies market price in currently used currency
|
|
marketChangePct24hour [double] - percentage price change in last 24 hours, e.g. 0.5 for 0.5% of price change
|
|
|
|
communityId [string] - for community assets, unique identifier of a community, e.g. "0x6734235"
|
|
communityName [string] - for community assets, name of a community e.g. "Crypto Kitties"
|
|
communityIcon [url] - for community assets, community's icon url
|
|
|
|
position [int] - if custom order available, display position defined by the user via token management
|
|
canBeHidden [bool] - specifies if given token can be hidden (e.g. ETH should be always visible)
|
|
**/
|
|
property var model
|
|
|
|
// enables global loading state useful when real data are not yet available
|
|
property bool loading
|
|
|
|
// shows/hides list sorter
|
|
property bool sorterVisible
|
|
|
|
// allows/disables choosing custom sort order from a sorter
|
|
property bool customOrderAvailable
|
|
|
|
// switches configuring right click menu
|
|
property bool sendEnabled: true
|
|
property bool communitySendEnabled: false
|
|
property bool swapEnabled: true
|
|
property bool swapVisible: true
|
|
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
|
|
|
|
// formatting function for fiat currency values
|
|
property var formatFiat: balance => `${balance.toLocaleString(Qt.locale())} XYZ`
|
|
|
|
signal sendRequested(string key)
|
|
signal receiveRequested(string key)
|
|
signal swapRequested(string key)
|
|
signal assetClicked(string key)
|
|
signal communityClicked(string communityKey)
|
|
signal hideRequested(string key)
|
|
signal hideCommunityAssetsRequested(string communityKey)
|
|
signal manageTokensRequested
|
|
|
|
function setSortOrder(order) {
|
|
d.sortOrder = order
|
|
}
|
|
|
|
function getSortOrder() {
|
|
return d.sortOrder
|
|
}
|
|
|
|
function getSortValue() {
|
|
return d.sortValue
|
|
}
|
|
|
|
function sortByValue(value) {
|
|
d.sortValue = value
|
|
}
|
|
|
|
QtObject {
|
|
id: d
|
|
|
|
readonly property int loadingItemsCount: 25
|
|
property int sortOrder: Qt.DescendingOrder
|
|
property int sortValue: -1
|
|
}
|
|
|
|
SortFilterProxyModel {
|
|
id: sfpm
|
|
|
|
sourceModel: root.model ?? null
|
|
|
|
proxyRoles: [
|
|
// helper role for rendering section delegate
|
|
FastExpressionRole {
|
|
name: "isCommunity"
|
|
expression: !!communityId ? "community" : ""
|
|
expectedRoles: ["communityId"]
|
|
},
|
|
FastExpressionRole {
|
|
name: "marketBalance"
|
|
expression: balance * marketPrice
|
|
expectedRoles: ["balance", "marketPrice"]
|
|
},
|
|
FastExpressionRole {
|
|
name: "change1DayFiat"
|
|
expression: marketBalance * (1 - (1 / (marketChangePct24hour / 100 + 1)))
|
|
expectedRoles: ["marketBalance", "marketChangePct24hour"]
|
|
}
|
|
]
|
|
|
|
sorters: [
|
|
RoleSorter {
|
|
roleName: "isCommunity"
|
|
},
|
|
RoleSorter {
|
|
roleName: sortOrderComboBox.currentSortRoleName
|
|
sortOrder: sortOrderComboBox.currentSortOrder
|
|
}
|
|
]
|
|
}
|
|
|
|
contentItem: ColumnLayout {
|
|
ColumnLayout {
|
|
Layout.fillHeight: false
|
|
Layout.preferredHeight: root.sorterVisible ? implicitHeight : 0
|
|
|
|
opacity: root.sorterVisible ? 1 : 0
|
|
spacing: 20
|
|
visible: opacity > 0
|
|
|
|
Behavior on Layout.preferredHeight {
|
|
NumberAnimation { duration: 200; easing.type: Easing.InOutQuad }
|
|
}
|
|
|
|
Behavior on opacity {
|
|
NumberAnimation { duration: 200; easing.type: Easing.InOutQuad }
|
|
}
|
|
|
|
StatusDialogDivider { Layout.fillWidth: true }
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: false
|
|
|
|
spacing: Theme.halfPadding
|
|
|
|
StatusBaseText {
|
|
color: Theme.palette.baseColor1
|
|
font.pixelSize: Theme.additionalTextSize
|
|
text: qsTr("Sort by:")
|
|
}
|
|
|
|
SortOrderComboBox {
|
|
id: sortOrderComboBox
|
|
|
|
objectName: "cmbTokenOrder"
|
|
hasCustomOrderDefined: root.customOrderAvailable
|
|
Binding on currentIndex {
|
|
value: {
|
|
sortOrderComboBox.count
|
|
let id = sortOrderComboBox.indexOfValue(d.sortValue)
|
|
if (id === -1)
|
|
id = sortOrderComboBox.indexOfValue(SortOrderComboBox.TokenOrderAlpha)
|
|
return id
|
|
}
|
|
when: sortOrderComboBox.count > 0
|
|
}
|
|
onCurrentValueChanged: d.sortValue = sortOrderComboBox.currentValue
|
|
Binding on currentSortOrder {
|
|
value: d.sortOrder
|
|
}
|
|
onCurrentSortOrderChanged: d.sortOrder = sortOrderComboBox.currentSortOrder
|
|
model: [
|
|
{ value: SortOrderComboBox.TokenOrderCurrencyBalance,
|
|
text: qsTr("Asset balance value"), icon: "", sortRoleName: "marketBalance" },
|
|
{ value: SortOrderComboBox.TokenOrderBalance,
|
|
text: qsTr("Asset balance"), icon: "", sortRoleName: "balance" },
|
|
{ value: SortOrderComboBox.TokenOrderCurrencyPrice,
|
|
text: qsTr("Asset value"), icon: "", sortRoleName: "marketPrice" },
|
|
{ value: SortOrderComboBox.TokenOrder1DChange,
|
|
text: qsTr("1d change: balance value"), icon: "", sortRoleName: "change1DayFiat" },
|
|
{ value: SortOrderComboBox.TokenOrderAlpha,
|
|
text: qsTr("Asset name"), icon: "", sortRoleName: "name" },
|
|
{ value: SortOrderComboBox.TokenOrderCustom,
|
|
text: qsTr("Custom order"), icon: "", sortRoleName: "position" },
|
|
{ value: SortOrderComboBox.TokenOrderNone,
|
|
text: "---", icon: "", sortRoleName: "" }, // separator
|
|
{ value: SortOrderComboBox.TokenOrderCreateCustom,
|
|
text: hasCustomOrderDefined ? qsTr("Edit custom order →") : qsTr("Create custom order →"),
|
|
icon: "", sortRoleName: "" }
|
|
]
|
|
onCreateOrEditRequested: {
|
|
root.manageTokensRequested()
|
|
}
|
|
}
|
|
}
|
|
|
|
StatusDialogDivider { Layout.fillWidth: true }
|
|
}
|
|
|
|
Loader {
|
|
id: banner
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
DelegateModel {
|
|
id: regularModel
|
|
|
|
model: sfpm
|
|
|
|
delegate: TokenDelegate {
|
|
objectName: `AssetView_TokenListItem_${model.symbol}`
|
|
|
|
width: ListView.view.width
|
|
|
|
name: model.name
|
|
icon: model.icon
|
|
balance: model.balanceText
|
|
marketBalance: root.formatFiat(model.marketBalance)
|
|
|
|
marketDetailsAvailable: model.marketDetailsAvailable
|
|
marketDetailsLoading: model.marketDetailsLoading
|
|
marketCurrencyPrice: root.formatFiat(model.change1DayFiat)
|
|
marketChangePct24hour: model.marketChangePct24hour
|
|
|
|
communityId: model.communityId
|
|
communityName: model.communityName ?? ""
|
|
communityIcon: model.communityIcon ?? ""
|
|
|
|
errorTooltipText_1: model.error
|
|
errorTooltipText_2: root.marketDataError
|
|
|
|
errorMode: !!root.balanceError
|
|
errorIcon.tooltip.text: root.balanceError
|
|
|
|
onClicked: {
|
|
if (mouse.button === Qt.LeftButton)
|
|
root.assetClicked(model.key)
|
|
else if (mouse.button === Qt.RightButton)
|
|
tokenContextMenu.createObject(this, { model }).popup(mouse)
|
|
}
|
|
|
|
onCommunityClicked: root.communityClicked(model.communityId)
|
|
}
|
|
}
|
|
|
|
DelegateModel {
|
|
id: loadingModel
|
|
|
|
model: d.loadingItemsCount
|
|
|
|
delegate: LoadingTokenDelegate {
|
|
objectName: `AssetView_LoadingTokenDelegate_${model.index}`
|
|
|
|
width: ListView.view.width
|
|
}
|
|
}
|
|
|
|
StatusListView {
|
|
id: listView
|
|
|
|
objectName: "assetViewStatusListView"
|
|
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
|
|
model: root.loading ? loadingModel : regularModel
|
|
|
|
section {
|
|
property: "isCommunity"
|
|
delegate: AssetsSectionDelegate {
|
|
width: parent.width
|
|
text: qsTr("Community minted")
|
|
onInfoButtonClicked: communityInfoPopup.createObject(this).open()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: tokenContextMenu
|
|
|
|
AssetContextMenu {
|
|
required property var model
|
|
|
|
readonly property string key: model.key
|
|
readonly property string communityKey: model.communityId
|
|
|
|
readonly property bool isCommunity: !!model.isCommunity
|
|
|
|
onClosed: destroy()
|
|
|
|
sendEnabled: root.sendEnabled
|
|
&& (!isCommunity || root.communitySendEnabled)
|
|
swapEnabled: root.swapEnabled
|
|
swapVisible: root.swapVisible && (!isCommunity || root.communitySwapVisible)
|
|
hideVisible: model.canBeHidden
|
|
communityHideVisible: isCommunity
|
|
|
|
onSendRequested: root.sendRequested(key)
|
|
onReceiveRequested: root.receiveRequested(key)
|
|
onSwapRequested: root.swapRequested(key)
|
|
|
|
onHideRequested: root.hideRequested(key)
|
|
onCommunityHideRequested: root.hideCommunityAssetsRequested(communityKey)
|
|
|
|
onManageTokensRequested: root.manageTokensRequested()
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: communityInfoPopup
|
|
|
|
CommunityAssetsInfoPopup {
|
|
destroyOnClose: true
|
|
}
|
|
}
|
|
}
|