2023-11-22 19:58:02 +00:00
import QtQuick 2.15
import QtQuick . Controls 2.15
import QtQuick . Layouts 1.15
2023-12-06 10:54:36 +00:00
import Qt . labs . settings 1.1
2021-10-05 20:50:22 +00:00
2023-11-24 12:16:13 +00:00
import StatusQ 0.1
2022-07-13 12:29:38 +00:00
import StatusQ . Core 0.1
2022-08-24 15:47:26 +00:00
import StatusQ . Core . Theme 0.1
2023-11-24 12:16:13 +00:00
import StatusQ . Core . Utils 0.1 as SQUtils
2022-08-08 21:12:12 +00:00
import StatusQ . Controls 0.1
import StatusQ . Components 0.1
2023-11-22 19:58:02 +00:00
import StatusQ . Popups 0.1
import StatusQ . Popups . Dialog 0.1
import StatusQ . Models 0.1
import StatusQ . Internal 0.1
2022-08-08 21:12:12 +00:00
import SortFilterProxyModel 0.2
2022-07-13 12:29:38 +00:00
2021-10-05 20:50:22 +00:00
import utils 1.0
2023-11-22 19:58:02 +00:00
import shared . stores 1.0
2022-11-23 17:58:22 +00:00
import shared . controls 1.0
2023-11-22 19:58:02 +00:00
import shared . popups 1.0
2021-10-05 20:50:22 +00:00
2023-12-06 10:54:36 +00:00
import AppLayouts . Wallet . controls 1.0
ColumnLayout {
2022-08-08 21:12:12 +00:00
id: root
2023-11-24 12:16:13 +00:00
// expected roles: name, symbol, balances, currencyPrice, changePct24hour, communityId, communityName, communityImage
2024-01-31 18:09:44 +00:00
required property var controller
2023-11-22 19:58:02 +00:00
2023-11-24 12:16:13 +00:00
property var currencyStore
2023-03-15 09:17:25 +00:00
property var networkConnectionStore
2023-11-22 19:58:02 +00:00
property var overview
2022-08-08 21:12:12 +00:00
property bool assetDetailsLaunched: false
2023-12-06 10:54:36 +00:00
property bool filterVisible
2023-11-24 12:16:13 +00:00
property bool areAssetsLoading: false
property string addressFilters
property string networkFilters
2022-08-08 21:12:12 +00:00
signal assetClicked ( var token )
2023-11-22 19:58:02 +00:00
signal sendRequested ( string symbol )
signal receiveRequested ( string symbol )
signal switchToCommunityRequested ( string communityId )
signal manageTokensRequested ( )
2023-12-06 10:54:36 +00:00
spacing: 0
2022-08-08 21:12:12 +00:00
QtObject {
id: d
property int selectedAssetIndex: - 1
2023-11-24 12:16:13 +00:00
readonly property int loadingItemsCount: 25
2022-03-25 08:46:47 +00:00
2023-12-06 10:54:36 +00:00
readonly property bool isCustomView: cmbTokenOrder . currentValue === SortOrderComboBox . TokenOrderCustom
2021-10-05 20:50:22 +00:00
2024-01-12 11:22:02 +00:00
function tokenIsVisible ( symbol ) {
2024-01-04 12:22:12 +00:00
// NOTE Backend returns ETH, SNT, STT and DAI by default
2024-01-31 18:09:44 +00:00
if ( ! root . controller . filterAcceptsSymbol ( symbol ) ) // explicitely hidden
2023-11-22 19:58:02 +00:00
return false
2024-01-04 12:22:12 +00:00
// Received tokens can have 0 balance, which indicate previosuly owned token
return true // TODO handle UI threshold (#12611)
2023-12-06 10:54:36 +00:00
}
2023-11-24 12:16:13 +00:00
function getTotalBalance ( balances , decimals ) {
let totalBalance = 0
2024-01-12 11:22:02 +00:00
let nwFilters = root . networkFilters . split ( ":" )
let addrFilters = root . addressFilters . split ( ":" )
2023-11-24 12:16:13 +00:00
for ( let i = 0 ; i < balances . count ; i ++ ) {
2024-01-12 11:22:02 +00:00
let balancePerAddressPerChain = ModelUtils . get ( balances , i )
if ( nwFilters . includes ( balancePerAddressPerChain . chainId + "" ) &&
addrFilters . includes ( balancePerAddressPerChain . account ) ) {
totalBalance += SQUtils . AmountsArithmetic . toNumber ( balancePerAddressPerChain . balance , decimals )
}
2023-11-22 19:58:02 +00:00
}
2023-11-24 12:16:13 +00:00
return totalBalance
}
property SortFilterProxyModel customSFPM: SortFilterProxyModel {
2024-01-31 18:09:44 +00:00
sourceModel: root . controller . sourceModel
2023-11-24 12:16:13 +00:00
proxyRoles: [
FastExpressionRole {
name: "currentBalance"
expression: d . getTotalBalance ( model . balances , model . decimals , root . addressFilters , root . networkFilters )
expectedRoles: [ "balances" , "decimals" ]
} ,
FastExpressionRole {
name: "currentCurrencyBalance"
expression: {
if ( ! model . communityId ) {
2024-01-05 15:47:58 +00:00
if ( ! ! model . marketDetails ) {
return model . currentBalance * model . marketDetails . currencyPrice . amount
}
return 0
2023-11-24 12:16:13 +00:00
}
else {
2024-01-05 11:01:56 +00:00
return model . currentBalance
2023-11-24 12:16:13 +00:00
}
}
2024-01-05 11:01:56 +00:00
expectedRoles: [ "marketDetails" , "communityId" , "currentBalance" ]
2023-11-24 12:16:13 +00:00
} ,
FastExpressionRole {
name: "tokenPrice"
expression: model . marketDetails . currencyPrice . amount
expectedRoles: [ "marketDetails" ]
} ,
2023-12-18 10:12:57 +00:00
FastExpressionRole {
name: "changePct24hour"
expression: model . marketDetails . changePct24hour
expectedRoles: [ "marketDetails" ]
} ,
2023-11-24 12:16:13 +00:00
FastExpressionRole {
name: "isCommunityAsset"
expression: ! ! model . communityId
expectedRoles: [ "communityId" ]
}
]
filters: [
2024-01-05 11:01:56 +00:00
FastExpressionFilter {
2023-11-24 12:16:13 +00:00
expression: {
2024-01-31 18:09:44 +00:00
root . controller . revision
2024-01-12 11:22:02 +00:00
return d . tokenIsVisible ( model . symbol )
2023-11-24 12:16:13 +00:00
}
2024-01-12 11:22:02 +00:00
expectedRoles: [ "symbol" ]
2023-11-24 12:16:13 +00:00
}
]
sorters: [
RoleSorter {
roleName: "isCommunityAsset"
} ,
2024-01-05 11:01:56 +00:00
FastExpressionSorter {
2023-11-24 12:16:13 +00:00
expression: {
2024-01-31 18:09:44 +00:00
root . controller . revision
return root . controller . compareTokens ( modelLeft . symbol , modelRight . symbol )
2023-11-24 12:16:13 +00:00
}
enabled: d . isCustomView
2024-01-05 11:01:56 +00:00
expectedRoles: [ "symbol" ]
2023-11-24 12:16:13 +00:00
} ,
RoleSorter {
roleName: cmbTokenOrder . currentSortRoleName
sortOrder: cmbTokenOrder . currentSortOrder
enabled: ! d . isCustomView
}
]
}
2023-12-06 10:54:36 +00:00
}
2023-11-22 19:58:02 +00:00
2023-12-06 10:54:36 +00:00
Settings {
2024-01-24 12:02:03 +00:00
id: settings
2023-12-06 10:54:36 +00:00
category: "AssetsViewSortSettings"
2024-01-24 12:02:03 +00:00
property int currentSortValue: SortOrderComboBox . TokenOrderCurrencyBalance
2023-12-06 10:54:36 +00:00
property alias currentSortOrder: cmbTokenOrder . currentSortOrder
2023-01-10 13:04:23 +00:00
}
2022-08-24 15:47:26 +00:00
2024-01-24 12:02:03 +00:00
Component.onCompleted: {
settings . sync ( )
cmbTokenOrder . currentIndex = cmbTokenOrder . indexOfValue ( settings . currentSortValue )
}
Component.onDestruction: {
settings . currentSortValue = cmbTokenOrder . currentValue
}
2023-11-22 19:58:02 +00:00
ColumnLayout {
2023-12-06 10:54:36 +00:00
Layout.fillWidth: true
Layout.preferredHeight: root . filterVisible ? implicitHeight : 0
spacing: 20
2023-12-18 10:12:57 +00:00
opacity: root . filterVisible ? 1 : 0
2024-01-29 19:49:59 +00:00
visible: opacity > 0
2023-11-22 19:58:02 +00:00
2023-12-06 10:54:36 +00:00
Behavior on Layout . preferredHeight { NumberAnimation { duration: 200 ; easing.type: Easing . InOutQuad } }
Behavior on opacity { NumberAnimation { duration: 200 ; easing.type: Easing . InOutQuad } }
2023-11-22 19:58:02 +00:00
StatusDialogDivider {
Layout.fillWidth: true
}
RowLayout {
Layout.fillWidth: true
2023-12-06 10:54:36 +00:00
spacing: Style . current . halfPadding
2023-11-22 19:58:02 +00:00
StatusBaseText {
color: Theme . palette . baseColor1
2023-12-06 10:54:36 +00:00
font.pixelSize: Style . current . additionalTextSize
text: qsTr ( "Sort by:" )
2023-11-22 19:58:02 +00:00
}
2023-12-06 10:54:36 +00:00
SortOrderComboBox {
id: cmbTokenOrder
2024-01-31 18:09:44 +00:00
hasCustomOrderDefined: root . controller . hasSettings
2023-12-06 10:54:36 +00:00
model: [
{ value: SortOrderComboBox . TokenOrderCurrencyBalance , text: qsTr ( "Asset balance value" ) , icon: "token-sale" , sortRoleName: "currentCurrencyBalance" } , // custom SFPM ExpressionRole on "enabledNetworkCurrencyBalance" amount
{ value: SortOrderComboBox . TokenOrderBalance , text: qsTr ( "Asset balance" ) , icon: "channel" , sortRoleName: "currentBalance" } , // custom SFPM ExpressionRole on "enabledNetworkBalance" amount
{ value: SortOrderComboBox . TokenOrderCurrencyPrice , text: qsTr ( "Asset value" ) , icon: "token" , sortRoleName: "tokenPrice" } , // custom SFPM ExpressionRole on "currencyPrice" amount
{ value: SortOrderComboBox . TokenOrder1WChange , text: qsTr ( "1d change: balance value" ) , icon: "history" , sortRoleName: "changePct24hour" } , // FIXME changePct1week role missing in backend!!!
{ value: SortOrderComboBox . TokenOrderAlpha , text: qsTr ( "Asset name" ) , icon: "bold" , sortRoleName: "name" } ,
{ value: SortOrderComboBox . TokenOrderCustom , text: qsTr ( "Custom order" ) , icon: "exchange" , sortRoleName: "" } ,
{ 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 ( )
}
2023-11-22 19:58:02 +00:00
}
}
2023-12-06 10:54:36 +00:00
StatusDialogDivider {
2023-11-22 19:58:02 +00:00
Layout.fillWidth: true
}
2023-12-06 10:54:36 +00:00
}
2023-11-22 19:58:02 +00:00
2023-12-18 10:12:57 +00:00
StatusListView {
id: assetsListView
2023-12-06 10:54:36 +00:00
Layout.fillWidth: true
Layout.topMargin: Style . current . padding
2023-12-18 10:12:57 +00:00
Layout.preferredHeight: contentHeight
Layout.fillHeight: true
objectName: "assetViewStatusListView"
model: root . areAssetsLoading ? d.loadingItemsCount : d . customSFPM
delegate: delegateLoader
section {
property: "isCommunityAsset"
delegate: Loader {
width: ListView . view . width
required property string section
sourceComponent: section === "true" ? sectionDelegate : null
2023-11-24 12:16:13 +00:00
}
}
}
Component {
id: sectionDelegate
2024-01-30 13:15:58 +00:00
AssetsSectionDelegate {
2023-12-18 10:12:57 +00:00
width: parent . width
2024-01-30 13:15:58 +00:00
onOpenInfoPopup: Global . openPopup ( communityInfoPopupCmp )
2023-12-06 10:54:36 +00:00
}
}
Component {
id: delegateLoader
Loader {
property var modelData: model
property int delegateIndex: index
width: ListView . view . width
2023-12-18 10:12:57 +00:00
sourceComponent: root . areAssetsLoading ? loadingTokenDelegate : tokenDelegate
2023-11-22 19:58:02 +00:00
}
2023-12-06 10:54:36 +00:00
}
2023-11-22 19:58:02 +00:00
2023-12-06 10:54:36 +00:00
Component {
id: loadingTokenDelegate
LoadingTokenDelegate {
objectName: "AssetView_LoadingTokenDelegate_" + delegateIndex
}
}
Component {
id: tokenDelegate
TokenDelegate {
objectName: "AssetView_TokenListItem_" + ( ! ! modelData ? modelData.symbol : "" )
2023-12-18 10:12:57 +00:00
readonly property string balance: ! ! modelData && ! ! modelData . currentBalance ? "%1" . arg ( modelData . currentBalance ) : "" // Needed for the tests
2023-12-06 10:54:36 +00:00
errorTooltipText_1: ! ! modelData && ! ! networkConnectionStore ? networkConnectionStore . getBlockchainNetworkDownTextForToken ( modelData . balances ) : ""
errorTooltipText_2: ! ! networkConnectionStore ? networkConnectionStore . getMarketNetworkDownText ( ) : ""
subTitle: {
2023-11-24 12:16:13 +00:00
if ( ! modelData || ! modelData . symbol ) {
2023-12-06 10:54:36 +00:00
return ""
2023-11-22 19:58:02 +00:00
}
2023-12-06 10:54:36 +00:00
if ( networkConnectionStore && networkConnectionStore . noTokenBalanceAvailable ) {
return ""
2023-11-22 19:58:02 +00:00
}
2023-11-24 12:16:13 +00:00
return LocaleUtils . currencyAmountToLocaleString ( root . currencyStore . getCurrencyAmount ( modelData . currentBalance , modelData . symbol ) )
}
currencyBalance.text: {
let totalCurrencyBalance = modelData && modelData . currentCurrencyBalance ? modelData.currentCurrencyBalance : 0
2024-01-30 13:15:58 +00:00
return currencyStore . formatCurrencyAmount ( totalCurrencyBalance , currencyStore . currentCurrency )
2023-12-06 10:54:36 +00:00
}
errorMode: ! ! networkConnectionStore ? networkConnectionStore . noBlockchainConnectionAndNoCache && ! networkConnectionStore.noMarketConnectionAndNoCache : false
errorIcon.tooltip.text: ! ! networkConnectionStore ? networkConnectionStore.noBlockchainConnectionAndNoCacheText : ""
onClicked: ( itemId , mouse ) = > {
if ( mouse . button === Qt . LeftButton ) {
2023-11-24 12:16:13 +00:00
RootStore . getHistoricalDataForToken ( modelData . symbol , root . currencyStore . currentCurrency )
2023-12-06 10:54:36 +00:00
d . selectedAssetIndex = delegateIndex
2023-11-24 12:16:13 +00:00
assetClicked ( assetsListView . model . get ( delegateIndex ) )
2023-12-06 10:54:36 +00:00
} else if ( mouse . button === Qt . RightButton ) {
Global . openMenu ( tokenContextMenu , this ,
{ symbol: modelData . symbol , assetName: modelData . name , assetImage: symbolUrl ,
communityId: modelData . communityId , communityName: modelData . communityName , communityImage: modelData . communityImage } )
}
}
onSwitchToCommunityRequested: root . switchToCommunityRequested ( communityId )
Component.onCompleted: {
// on Model reset if the detail view is shown, update the data in background.
if ( root . assetDetailsLaunched && delegateIndex === d . selectedAssetIndex ) {
2023-11-24 12:16:13 +00:00
assetClicked ( assetsListView . model . get ( delegateIndex ) )
2023-11-22 19:58:02 +00:00
}
2022-08-08 21:12:12 +00:00
}
2023-11-22 19:58:02 +00:00
}
2023-12-06 10:54:36 +00:00
}
2023-11-22 19:58:02 +00:00
2023-12-06 10:54:36 +00:00
Component {
id: tokenContextMenu
StatusMenu {
onClosed: destroy ( )
property string symbol
property string assetName
property string assetImage
property string communityId
property string communityName
property string communityImage
StatusAction {
enabled: root . networkConnectionStore . sendBuyBridgeEnabled && ! root . overview . isWatchOnlyAccount && root . overview . canSend
2024-02-02 09:55:56 +00:00
visibleOnDisabled: true
2023-12-06 10:54:36 +00:00
icon.name: "send"
text: qsTr ( "Send" )
onTriggered: root . sendRequested ( symbol )
}
StatusAction {
icon.name: "receive"
text: qsTr ( "Receive" )
onTriggered: root . receiveRequested ( symbol )
}
StatusMenuSeparator { }
StatusAction {
icon.name: "settings"
text: qsTr ( "Manage tokens" )
onTriggered: root . manageTokensRequested ( )
}
StatusAction {
enabled: symbol !== "ETH"
type: StatusAction . Type . Danger
icon.name: "hide"
text: qsTr ( "Hide asset" )
2024-01-31 18:09:44 +00:00
onTriggered: Global . openConfirmHideAssetPopup ( symbol , assetName , assetImage , ! ! communityId )
2023-12-06 10:54:36 +00:00
}
StatusAction {
enabled: ! ! communityId
type: StatusAction . Type . Danger
icon.name: "hide"
text: qsTr ( "Hide all assets from this community" )
onTriggered: Global . openPopup ( confirmHideCommunityAssetsPopup , { communityId , communityName , communityImage } )
2023-11-22 19:58:02 +00:00
}
}
2023-12-06 10:54:36 +00:00
}
2023-11-22 19:58:02 +00:00
2023-12-06 10:54:36 +00:00
Component {
id: communityInfoPopupCmp
2024-01-30 13:15:58 +00:00
CommunityAssetsInfoPopup { }
2023-12-06 10:54:36 +00:00
}
2023-11-22 19:58:02 +00:00
2023-12-06 10:54:36 +00:00
Component {
id: confirmHideCommunityAssetsPopup
ConfirmationDialog {
property string communityId
property string communityName
property string communityImage
width: 520
destroyOnClose: true
2024-01-29 15:37:17 +00:00
confirmButtonLabel: qsTr ( "Hide '%1' assets" ) . arg ( communityName )
2023-12-06 10:54:36 +00:00
cancelBtnType: ""
showCancelButton: true
headerSettings.title: qsTr ( "Hide %1 community assets" ) . arg ( communityName )
headerSettings.asset.name: communityImage
confirmationText: qsTr ( "Are you sure you want to hide all community assets minted by %1? You will no longer see or be able to interact with these assets anywhere inside Status." ) . arg ( communityName )
onCancelButtonClicked: close ( )
onConfirmButtonClicked: {
2024-01-31 18:09:44 +00:00
root . controller . showHideGroup ( communityId , false )
2023-12-06 10:54:36 +00:00
close ( )
Global . displayToastMessage (
qsTr ( "%1 community assets were successfully hidden. You can toggle asset visibility via %2." ) . arg ( communityName )
. arg ( ` < a style = "text-decoration:none" href = "#${Constants.appSection.profile}/${Constants.settingsSubsection.wallet}/${Constants.walletSettingsSubsection.manageAssets}" > ` + qsTr ( "Settings" , "Go to Settings" ) + "</a>" ) ,
"" ,
"checkmark-circle" ,
false ,
Constants . ephemeralNotificationType . success ,
""
)
2022-08-08 21:12:12 +00:00
}
2022-07-14 11:03:36 +00:00
}
2021-10-05 20:50:22 +00:00
}
}