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 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 property alias currentTabIndex: walletTabBar.currentIndex signal launchShareAddressModal() 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 } 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: Style.current.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: Style.current.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 } } Loader { id: mainViewLoader Layout.fillWidth: true Layout.fillHeight: true sourceComponent: { switch (walletTabBar.currentIndex) { case 0: return assetsView case 1: return collectiblesView case 2: 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.TokenOrderAlpha 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 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) SharedStores.RootStore.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.TokenOrderAlpha 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 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 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 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 communitiesStore: root.communitiesStore showAllAccounts: RootStore.showAllAccounts sendModal: root.sendModal filterVisible: filterButton.checked onLaunchTransactionDetail: function (txID) { RootStore.activityController.fetchTxDetails(txID) stack.currentIndex = 3 } } } } } CollectibleDetailView { id: collectibleDetailView visible : (stack.currentIndex === 1) collectible: RootStore.collectiblesStore.detailedCollectible isCollectibleLoading: RootStore.collectiblesStore.isDetailedCollectibleLoading activityModel: d.detailedCollectibleActivityController.model addressFilters: RootStore.addressFilters rootStore: SharedStores.RootStore walletRootStore: RootStore communitiesStore: root.communitiesStore onVisibleChanged: { if (!visible) { RootStore.resetCurrentViewedHolding(Constants.TokenType.ERC721) RootStore.collectiblesStore.resetDetailedCollectible() } } onLaunchTransactionDetail: function (txID) { d.detailedCollectibleActivityController.fetchTxDetails(txID) stack.currentIndex = 3 // Take user to the activity view when they press the "Back" button walletTabBar.currentIndex = 2 } } AssetsDetailView { id: assetDetailView visible: (stack.currentIndex === 2) allNetworksModel: RootStore.filteredFlatModel address: RootStore.overview.mixedcaseAddress currencyStore: RootStore.currencyStore networkFilters: RootStore.networkFilters networkConnectionStore: root.networkConnectionStore onVisibleChanged: { if (!visible) RootStore.resetCurrentViewedHolding(Constants.TokenType.ERC20) } } Loader { active: stack.currentIndex === 3 sourceComponent: TransactionDetailView { controller: RootStore.activityDetailsController onVisibleChanged: { if (visible) { if (!!transaction) { RootStore.addressWasShown(transaction.sender) if (transaction.sender !== transaction.recipient) { RootStore.addressWasShown(transaction.recipient) } } } else { controller.resetActivityEntry() } } showAllAccounts: RootStore.showAllAccounts communitiesStore: root.communitiesStore sendModal: root.sendModal contactsStore: root.contactsStore networkConnectionStore: root.networkConnectionStore visible: (stack.currentIndex === 3) } } } }