import QtQuick 2.15 import QtQuick.Layouts 1.15 import Qt.labs.settings 1.1 import StatusQ.Components 0.1 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 import utils 1.0 import shared.controls 1.0 import shared.views 1.0 import shared.stores 1.0 as SharedStores import shared.panels 1.0 import "./" import "../stores" import "../panels" import "../views/collectibles" RightTabBaseView { id: root enum TabIndex { Assets = 0, Collectibles = 1, Activity = 2 } property SharedStores.RootStore sharedRootStore property alias currentTabIndex: walletTabBar.currentIndex signal launchShareAddressModal() signal launchBuyCryptoModal() signal launchSwapModal(string tokensKey) function resetView() { resetStack() root.currentTabIndex = 0 } function resetStack() { stack.currentIndex = 0; RootStore.backButtonName = d.getBackButtonText(stack.currentIndex); } headerButton.onClicked: { root.launchShareAddressModal() } header.visible: stack.currentIndex === 0 StackLayout { id: stack anchors.fill: parent onCurrentIndexChanged: { RootStore.backButtonName = d.getBackButtonText(currentIndex) } QtObject { id: d function getBackButtonText(index) { switch(index) { case 1: return qsTr("Collectibles") case 2: return qsTr("Assets") case 3: return qsTr("Activity") default: return "" } } 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 ConfirmHideCommunityAssetsPopup { destroyOnClose: true required property string communityId onConfirmButtonClicked: { RootStore.walletAssetsStore.assetsController.showHideGroup(communityId, false /*hide*/) close(); } } } // StackLayout.currentIndex === 0 ColumnLayout { spacing: 0 ImportKeypairInfo { Layout.fillWidth: true Layout.topMargin: Theme.bigPadding Layout.preferredHeight: childrenRect.height visible: root.store.walletSectionInst.hasPairedDevices && root.store.walletSectionInst.keypairOperabilityForObservedAccount === Constants.keypair.operability.nonOperable onRunImport: { root.store.walletSectionInst.runKeypairImportPopup() } } RowLayout { Layout.fillWidth: true StatusTabBar { id: walletTabBar objectName: "rightSideWalletTabBar" Layout.fillWidth: true Layout.topMargin: Theme.padding StatusTabButton { objectName: "assetsTabButton" leftPadding: 0 width: implicitWidth text: qsTr("Assets") } StatusTabButton { objectName: "collectiblesTabButton" width: implicitWidth text: qsTr("Collectibles") } StatusTabButton { objectName: "activityTabButton" rightPadding: 0 width: implicitWidth text: qsTr("Activity") StatusBetaTag { // TODO remove me when Activity is no longer experimental // Keep Activity as the last tab for now as the Experimental tag don't flow anchors.top: parent.top anchors.topMargin: 6 anchors.left: parent.right anchors.leftMargin: 5 cursorShape: Qt.PointingHandCursor } } onCurrentIndexChanged: { RootStore.setCurrentViewedHoldingType(walletTabBar.currentIndex === 1 ? Constants.TokenType.ERC721 : Constants.TokenType.ERC20) } } StatusFlatButton { id: filterButton objectName: "filterButton" icon.name: "filter" checkable: true icon.color: checked ? Theme.palette.primaryColor1 : Theme.palette.baseColor1 Behavior on icon.color { ColorAnimation { duration: 200; easing.type: Easing.InOutQuad } } highlighted: checked visible: walletTabBar.currentIndex !== RightTabView.TabIndex.Activity // TODO #16761: Re-enable filter for activity when implemented } } Loader { id: mainViewLoader Layout.fillWidth: true Layout.fillHeight: true sourceComponent: { switch (walletTabBar.currentIndex) { case RightTabView.TabIndex.Assets: return assetsView case RightTabView.TabIndex.Collectibles: return collectiblesView case RightTabView.TabIndex.Activity: return historyView } } Component { id: assetsView AssetsView { AssetsViewAdaptor { id: assetsViewAdaptor accounts: RootStore.addressFilters chains: RootStore.networkFiltersArray marketValueThreshold: RootStore.tokensStore.displayAssetsBelowBalance ? RootStore.tokensStore.getDisplayAssetsBelowBalanceThresholdDisplayAmount() : 0 Connections { target: RootStore.tokensStore function displayAssetsBelowBalanceThresholdChanged() { assetsViewAdaptor.marketValueThresholdChanged() } } tokensModel: RootStore.walletAssetsStore.groupedAccountAssetsModel formatBalance: (balance, symbol) => { return LocaleUtils.currencyAmountToLocaleString( RootStore.currencyStore.getCurrencyAmount(balance, symbol)) } chainsError: (chains) => { if (!root.networkConnectionStore) return "" return root.networkConnectionStore.getBlockchainNetworkDownText(chains) } } function refreshSortSettings() { settings.category = settingsCategoryName walletSettings.sync() settings.sync() let value = SortOrderComboBox.TokenOrderBalance if (walletSettings.assetsViewCustomOrderApplyTimestamp > settings.sortOrderUpdateTimestamp && customOrderAvailable) { value = SortOrderComboBox.TokenOrderCustom } else { value = settings.currentSortValue } sortByValue(value) setSortOrder(settings.currentSortOrder) } function saveSortSettings() { settings.currentSortValue = getSortValue() settings.currentSortOrder = getSortOrder() settings.sortOrderUpdateTimestamp = new Date().getTime() settings.sync() } readonly property string settingsCategoryName: { const addressFilters = RootStore.addressFilters return "AssetsViewSortSettings-" + (addressFilters.indexOf(':') > -1 ? "all" : addressFilters) } onSettingsCategoryNameChanged: { saveSortSettings() refreshSortSettings() } Component.onCompleted: refreshSortSettings() Component.onDestruction: saveSortSettings() readonly property Settings walletSettings: Settings { id: walletSettings category: "walletSettings-" + root.contactsStore.myPublicKey property var assetsViewCustomOrderApplyTimestamp } readonly property Settings settings: Settings { id: settings property int currentSortValue: SortOrderComboBox.TokenOrderDateAdded property var sortOrderUpdateTimestamp property int currentSortOrder: Qt.DescendingOrder } loading: RootStore.overview.balanceLoading sorterVisible: filterButton.checked customOrderAvailable: RootStore.walletAssetsStore.assetsController.hasSettings model: assetsViewAdaptor.model bannerComponent: buyReceiveBannerComponent marketDataError: !!root.networkConnectionStore ? root.networkConnectionStore.getMarketNetworkDownText() : "" balanceError: { if (!root.networkConnectionStore) return "" return (root.networkConnectionStore.noBlockchainConnectionAndNoCache && !root.networkConnectionStore.noMarketConnectionAndNoCache) ? root.networkConnectionStore.noBlockchainConnectionAndNoCacheText : "" } formatFiat: balance => RootStore.currencyStore.formatCurrencyAmount( balance, RootStore.currencyStore.currentCurrency) sendEnabled: root.networkConnectionStore.sendBuyBridgeEnabled && !RootStore.overview.isWatchOnlyAccount && RootStore.overview.canSend communitySendEnabled: RootStore.tokensStore.showCommunityAssetsInSend swapEnabled: !RootStore.overview.isWatchOnlyAccount swapVisible: root.swapEnabled onSendRequested: { const modal = root.sendModal modal.preSelectedSendType = Constants.SendType.Transfer modal.preSelectedHoldingID = key modal.preSelectedHoldingType = Constants.TokenType.ERC20 modal.onlyAssets = true modal.open() } onSwapRequested: root.launchSwapModal(key) onReceiveRequested: root.launchShareAddressModal() onCommunityClicked: Global.switchToCommunity(communityKey) onHideRequested: (key) => { const token = ModelUtils.getByKey(model, "key", key) Global.openConfirmHideAssetPopup(token.symbol, token.name, token.icon, !!token.communityId) } onHideCommunityAssetsRequested: (communityKey) => { const community = ModelUtils.getByKey(model, "communityId", communityKey) confirmHideCommunityAssetsPopup.createObject(root, { name: community.communityName, icon: community.communityIcon, communityId: communityKey } ).open() } onManageTokensRequested: Global.changeAppSectionBySectionType( Constants.appSection.profile, Constants.settingsSubsection.wallet, Constants.walletSettingsSubsection.manageAssets) onAssetClicked: (key) => { const token = ModelUtils.getByKey(model, "key", key) RootStore.tokensStore.getHistoricalDataForToken( token.symbol, RootStore.currencyStore.currentCurrency) assetDetailView.token = token RootStore.setCurrentViewedHolding( token.symbol, token.key, Constants.TokenType.ERC20, token.communityId ?? "") stack.currentIndex = 2 } } } Component { id: collectiblesView CollectiblesView { id: collView function refreshSortSettings() { settings.category = settingsCategoryName walletSettings.sync() settings.sync() let value = SortOrderComboBox.TokenOrderBalance if (walletSettings.collectiblesViewCustomOrderApplyTimestamp > settings.sortOrderUpdateTimestamp && customOrderAvailable) { value = SortOrderComboBox.TokenOrderCustom } else { value = settings.currentSortValue } sortByValue(value) setSortOrder(settings.currentSortOrder) } function saveSortSettings() { settings.currentSortValue = getSortValue() settings.currentSortOrder = getSortOrder() settings.sortOrderUpdateTimestamp = new Date().getTime() settings.sync() } readonly property string settingsCategoryName: "CollectiblesViewSortSettings-" + (addressFilters.indexOf(':') > -1 ? "all" : addressFilters) onSettingsCategoryNameChanged: { saveSortSettings() refreshSortSettings() } Component.onCompleted: refreshSortSettings() Component.onDestruction: saveSortSettings() readonly property Settings settings: Settings { id: settings property int currentSortValue: SortOrderComboBox.TokenOrderDateAdded property real sortOrderUpdateTimestamp: 0 property alias selectedFilterGroupIds: collView.selectedFilterGroupIds property int currentSortOrder: Qt.DescendingOrder } ownedAccountsModel: RootStore.nonWatchAccounts controller: RootStore.collectiblesStore.collectiblesController networkFilters: RootStore.networkFilters addressFilters: RootStore.addressFilters 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) d.detailedCollectibleActivityController.resetFilter() d.detailedCollectibleActivityController.setFilterAddressesJson(JSON.stringify(RootStore.addressFilters.split(":"))) d.detailedCollectibleActivityController.setFilterChainsJson(JSON.stringify([chainId]), false) d.detailedCollectibleActivityController.setFilterCollectibles(JSON.stringify([uid])) d.detailedCollectibleActivityController.updateFilter() stack.currentIndex = 1 } onSendRequested: (symbol, tokenType, fromAddress) => { const collectible = ModelUtils.getByKey(controller.sourceModel, "symbol", symbol) if (!!collectible && collectible.communityPrivilegesLevel === Constants.TokenPrivilegesLevel.Owner) { Global.openTransferOwnershipPopup(collectible.communityId, collectible.communityName, collectible.communityImage, { key: collectible.tokenId, privilegesLevel: collectible.communityPrivilegesLevel, chainId: collectible.chainId, name: collectible.name, artworkSource: collectible.communityImage, accountAddress: fromAddress, tokenAddress: collectible.contractAddress }, root.sendModal) return } root.sendModal.preSelectedAccountAddress = fromAddress root.sendModal.preSelectedHoldingID = symbol root.sendModal.preSelectedHoldingType = tokenType root.sendModal.preSelectedSendType = tokenType === Constants.TokenType.ERC721 ? Constants.SendType.ERC721Transfer: Constants.SendType.ERC1155Transfer root.sendModal.onlyAssets = false root.sendModal.open() } onReceiveRequested: (symbol) => root.launchShareAddressModal() onSwitchToCommunityRequested: (communityId) => Global.switchToCommunity(communityId) onManageTokensRequested: Global.changeAppSectionBySectionType(Constants.appSection.profile, Constants.settingsSubsection.wallet, Constants.walletSettingsSubsection.manageCollectibles) isFetching: RootStore.collectiblesStore.areCollectiblesFetching isUpdating: RootStore.collectiblesStore.areCollectiblesUpdating isError: RootStore.collectiblesStore.areCollectiblesError } } Component { id: historyView HistoryView { overview: RootStore.overview walletRootStore: RootStore communitiesStore: root.communitiesStore currencyStore: root.sharedRootStore.currencyStore showAllAccounts: RootStore.showAllAccounts filterVisible: false // TODO #16761: Re-enable filter for activity when implemented bannerComponent: buyReceiveBannerComponent } } } } CollectibleDetailView { id: collectibleDetailView visible : (stack.currentIndex === 1) collectible: RootStore.collectiblesStore.detailedCollectible isCollectibleLoading: RootStore.collectiblesStore.isDetailedCollectibleLoading activityModel: d.detailedCollectibleActivityController.model addressFilters: RootStore.addressFilters rootStore: root.sharedRootStore walletRootStore: RootStore communitiesStore: root.communitiesStore onVisibleChanged: { if (!visible) { RootStore.resetCurrentViewedHolding(Constants.TokenType.ERC721) RootStore.collectiblesStore.resetDetailedCollectible() } } } AssetsDetailView { id: assetDetailView visible: (stack.currentIndex === 2) tokensStore: RootStore.tokensStore allNetworksModel: RootStore.filteredFlatModel address: RootStore.overview.mixedcaseAddress currencyStore: RootStore.currencyStore networkFilters: RootStore.networkFilters networkConnectionStore: root.networkConnectionStore onVisibleChanged: { if (!visible) RootStore.resetCurrentViewedHolding(Constants.TokenType.ERC20) } } } }