mirror of
https://github.com/status-im/status-app.git
synced 2026-08-27 07:01:14 +00:00
542 lines
21 KiB
QML
542 lines
21 KiB
QML
import QtQuick
|
|
import QtQuick.Controls
|
|
import QtQuick.Layouts
|
|
import QtCore
|
|
|
|
import StatusQ
|
|
import StatusQ.Layout
|
|
import StatusQ.Core.Utils as StatusQUtils
|
|
import StatusQ.Core.Theme
|
|
|
|
import utils
|
|
import shared.controls
|
|
import shared.popups.addaccount
|
|
import shared.popups.keypairimport
|
|
|
|
import shared.stores as SharedStores
|
|
import shared.stores.send
|
|
|
|
import AppLayouts.Communities.stores
|
|
import AppLayouts.Profile.stores as ProfileStores
|
|
import AppLayouts.Wallet.stores as WalletStores
|
|
import AppLayouts.stores as AppLayoutsStores
|
|
|
|
import QtModelsToolkit
|
|
|
|
import "popups"
|
|
import "panels"
|
|
import "views"
|
|
import "stores"
|
|
import "controls"
|
|
import "popups/swap"
|
|
import "popups/buy"
|
|
|
|
Item {
|
|
id: root
|
|
|
|
// --- Back-navigation contract, forwarded to the inner StatusSectionLayout
|
|
// (walletSectionLayout). This Item wrapper would otherwise be a dead-end
|
|
// for AppMain's Link 2 on desktop. See AppMain.tryGoBack().
|
|
function tryGoBack() {
|
|
return walletSectionLayout.tryGoBack()
|
|
}
|
|
readonly property bool canGoBack: walletSectionLayout.canGoBack
|
|
|
|
property WalletStores.RootStore walletRootStore
|
|
property SharedStores.RootStore sharedRootStore
|
|
property AppLayoutsStores.RootStore store
|
|
property AppLayoutsStores.ContactsStore contactsStore
|
|
property CommunitiesStore communitiesStore
|
|
required property TransactionStore transactionStore
|
|
required property SharedStores.NetworksStore networksStore
|
|
|
|
property var emojiPopup: null
|
|
property SharedStores.NetworkConnectionStore networkConnectionStore
|
|
property bool appMainVisible
|
|
|
|
property bool swapEnabled
|
|
property bool dAppsEnabled
|
|
property bool dAppsVisible
|
|
property bool buyEnabled
|
|
|
|
property var dAppsModel
|
|
|
|
property bool isKeycardEnabled: true
|
|
|
|
signal dappListRequested()
|
|
signal dappConnectRequested()
|
|
signal dappDisconnectRequested(string dappUrl)
|
|
|
|
signal manageNetworksRequested()
|
|
|
|
// TODO: remove tokenType parameter from signals below
|
|
signal sendTokenRequested(string senderAddress, string groupKey, int tokenType)
|
|
|
|
signal openSwapModalRequested(var swapFormData)
|
|
|
|
// Layout interaction:
|
|
/*!
|
|
\qmlproperty int StatusSectionLayoutLandscape::leftPanelWidthOverride
|
|
This property provides an external override for the left panel width.
|
|
|
|
When greater than 0, the layout uses this value to temporarily expand the
|
|
left panel area. When set to 0, the override is cleared and the layout
|
|
collapses the left panel back to its default width.
|
|
*/
|
|
property int leftPanelWidthOverride: 0
|
|
|
|
onAppMainVisibleChanged: {
|
|
resetView()
|
|
}
|
|
|
|
onVisibleChanged: {
|
|
resetView()
|
|
}
|
|
|
|
Connections {
|
|
target: walletSection
|
|
|
|
function onFilterChanged(address) {
|
|
RootStore.selectedAddress = address === "" ? "" : address
|
|
}
|
|
|
|
function onDisplayKeypairImportPopup() {
|
|
keypairImport.active = true
|
|
}
|
|
|
|
function onDestroyKeypairImportPopup() {
|
|
keypairImport.active = false
|
|
}
|
|
|
|
function onDisplayAddAccountPopup() {
|
|
addAccount.active = true
|
|
}
|
|
|
|
function onDestroyAddAccountPopup() {
|
|
addAccount.active = false
|
|
}
|
|
}
|
|
|
|
enum LeftPanelSelection {
|
|
AllAddresses,
|
|
Address,
|
|
SavedAddresses,
|
|
FollowingAddresses
|
|
}
|
|
|
|
enum RightPanelSelection {
|
|
Assets,
|
|
Collectibles,
|
|
Activity
|
|
}
|
|
|
|
function resetView() {
|
|
if (!visible || !appMainVisible) {
|
|
return
|
|
}
|
|
|
|
d.displayAllAddresses()
|
|
|
|
d.resetRightPanelStackView()
|
|
}
|
|
|
|
function openDesiredView(leftPanelSelection, rightPanelSelection, data) {
|
|
if (leftPanelSelection !== WalletLayout.LeftPanelSelection.AllAddresses &&
|
|
leftPanelSelection !== WalletLayout.LeftPanelSelection.SavedAddresses &&
|
|
leftPanelSelection !== WalletLayout.LeftPanelSelection.FollowingAddresses &&
|
|
leftPanelSelection !== WalletLayout.LeftPanelSelection.Address) {
|
|
console.warn("not supported left selection", leftPanelSelection)
|
|
return
|
|
}
|
|
|
|
if (leftPanelSelection === WalletLayout.LeftPanelSelection.SavedAddresses) {
|
|
d.displaySavedAddresses()
|
|
} else if (leftPanelSelection === WalletLayout.LeftPanelSelection.FollowingAddresses) {
|
|
d.displayFollowingAddresses()
|
|
} else {
|
|
let address = data.address ?? ""
|
|
if (leftPanelSelection === WalletLayout.LeftPanelSelection.AllAddresses) {
|
|
d.displayAllAddresses()
|
|
} else if (leftPanelSelection === WalletLayout.LeftPanelSelection.Address) {
|
|
if (!!address) {
|
|
d.displayAddress(address)
|
|
} else {
|
|
d.displayAllAddresses()
|
|
}
|
|
}
|
|
|
|
if (rightPanelSelection !== WalletLayout.RightPanelSelection.Collectibles &&
|
|
rightPanelSelection !== WalletLayout.RightPanelSelection.Assets &&
|
|
rightPanelSelection !== WalletLayout.RightPanelSelection.Activity) {
|
|
console.warn("not supported right selection", rightPanelSelection)
|
|
return
|
|
}
|
|
|
|
d.resetRightPanelStackView()
|
|
rightPanelStackView.currentItem.currentTabIndex = rightPanelSelection
|
|
|
|
let savedAddress = data.savedAddress?? ""
|
|
if (!!savedAddress) {
|
|
RootStore.currentActivityFiltersStore.resetAllFilters()
|
|
RootStore.currentActivityFiltersStore.toggleSavedAddress(savedAddress)
|
|
}
|
|
}
|
|
|
|
// In portrait mode, it automatically swipes to the detailed content
|
|
walletSectionLayout.goToNextPanel()
|
|
}
|
|
|
|
QtObject {
|
|
id: d
|
|
|
|
readonly property bool showSavedAddresses: RootStore.showSavedAddresses
|
|
onShowSavedAddressesChanged: {
|
|
if(showSavedAddresses) {
|
|
rightPanelStackView.replace(cmpSavedAddresses)
|
|
RootStore.backButtonName = ""
|
|
} else if (!showFollowingAddresses) {
|
|
// Only replace with walletContainer if we're not showing following addresses
|
|
rightPanelStackView.replace(walletContainer)
|
|
RootStore.backButtonName = ""
|
|
}
|
|
}
|
|
|
|
readonly property bool showFollowingAddresses: RootStore.showFollowingAddresses
|
|
onShowFollowingAddressesChanged: {
|
|
if(showFollowingAddresses) {
|
|
rightPanelStackView.replace(cmpFollowingAddresses)
|
|
RootStore.backButtonName = ""
|
|
} else if (!showSavedAddresses) {
|
|
// Only replace with walletContainer if we're not showing saved addresses
|
|
rightPanelStackView.replace(walletContainer)
|
|
RootStore.backButtonName = ""
|
|
}
|
|
}
|
|
|
|
property BuyCryptoParamsForm buyFormData: BuyCryptoParamsForm {
|
|
selectedWalletAddress: RootStore.selectedAddress
|
|
}
|
|
|
|
function displayAllAddresses() {
|
|
RootStore.showSavedAddresses = false
|
|
RootStore.showFollowingAddresses = false
|
|
RootStore.selectedAddress = ""
|
|
RootStore.setFilterAllAddresses()
|
|
}
|
|
|
|
function displayAddress(address) {
|
|
RootStore.showSavedAddresses = false
|
|
RootStore.showFollowingAddresses = false
|
|
RootStore.selectedAddress = address
|
|
d.resetRightPanelStackView() // Avoids crashing on asset items being destroyed while in signal handler
|
|
RootStore.setFilterAddress(address)
|
|
}
|
|
|
|
function displaySavedAddresses() {
|
|
RootStore.showSavedAddresses = true
|
|
RootStore.showFollowingAddresses = false
|
|
RootStore.selectedAddress = ""
|
|
}
|
|
|
|
function displayFollowingAddresses() {
|
|
RootStore.showFollowingAddresses = true
|
|
RootStore.showSavedAddresses = false
|
|
RootStore.selectedAddress = ""
|
|
}
|
|
|
|
function resetRightPanelStackView() {
|
|
if (rightPanelStackView.currentItem && !!rightPanelStackView.currentItem.resetView) {
|
|
rightPanelStackView.currentItem.resetView()
|
|
}
|
|
}
|
|
|
|
function getSelectedOrFirstNonWatchedAddress() {
|
|
return !!RootStore.selectedAddress ?
|
|
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(root.networksStore.activeNetworks, "layer", 1, "chainId")
|
|
if(!!walletStore.currentViewedHoldingTokenGroupKey && walletStore.currentViewedHoldingType === Constants.TokenType.ERC20) {
|
|
d.buyFormData.selectedTokenKey = walletStore.currentViewedHoldingTokenGroupKey
|
|
}
|
|
Global.openBuyCryptoModalRequested(d.buyFormData)
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: cmpSavedAddresses
|
|
|
|
SavedAddressesView {
|
|
rootStore: RootStore
|
|
networkConnectionStore: root.networkConnectionStore
|
|
networksStore: root.networksStore
|
|
|
|
onSendToAddressRequested: {
|
|
Global.sendToRecipientRequested(address)
|
|
}
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: cmpFollowingAddresses
|
|
FollowingAddressesView {
|
|
rootStore: RootStore
|
|
contactsStore: root.contactsStore
|
|
networkConnectionStore: root.networkConnectionStore
|
|
networksStore: root.networksStore
|
|
|
|
onSendToAddressRequested: {
|
|
Global.sendToRecipientRequested(address)
|
|
}
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: walletContainer
|
|
|
|
RightTabView {
|
|
walletRootStore: root.walletRootStore
|
|
sharedRootStore: root.sharedRootStore
|
|
store: root.store
|
|
contactsStore: root.contactsStore
|
|
communitiesStore: root.communitiesStore
|
|
networkConnectionStore: root.networkConnectionStore
|
|
networksStore: root.networksStore
|
|
|
|
swapEnabled: root.swapEnabled
|
|
buyEnabled: root.buyEnabled
|
|
dAppsEnabled: root.dAppsEnabled
|
|
dAppsVisible: root.dAppsVisible
|
|
|
|
dAppsModel: root.dAppsModel
|
|
|
|
onLaunchShareAddressModal: Global.openShowQRPopup({
|
|
switchingAccounsEnabled: true,
|
|
hasFloatingButtons: true
|
|
})
|
|
onLaunchSwapModal: (groupKey) => {
|
|
const params = {
|
|
selectedAccountAddress: d.getSelectedOrFirstNonWatchedAddress(),
|
|
selectedNetworkChainId: StatusQUtils.ModelUtils.getByKey(root.networksStore.activeNetworks, "layer", 1, "chainId"),
|
|
defaultFromGroupKey: groupKey
|
|
}
|
|
root.openSwapModalRequested(params)
|
|
}
|
|
onDappListRequested: root.dappListRequested()
|
|
onDappConnectRequested: root.dappConnectRequested()
|
|
onDappDisconnectRequested: (dappUrl) =>root.dappDisconnectRequested(dappUrl)
|
|
onLaunchBuyCryptoModal: d.launchBuyCryptoModal()
|
|
|
|
onSendTokenRequested: (senderAddress, groupKey, tokenType) => root.sendTokenRequested(senderAddress, groupKey, tokenType)
|
|
|
|
onManageNetworksRequested: root.manageNetworksRequested()
|
|
}
|
|
}
|
|
|
|
LeftTabViewState {
|
|
id: leftPanelState
|
|
|
|
accountsModel: root.walletRootStore.accounts
|
|
selectedAddress: root.walletRootStore.selectedAddress
|
|
showSavedAddresses: root.walletRootStore.showSavedAddresses
|
|
showFollowingAddresses: root.walletRootStore.showFollowingAddresses
|
|
totalCurrencyBalance: root.walletRootStore.totalCurrencyBalance
|
|
balanceLoading: root.walletRootStore.balanceLoading
|
|
accountBalanceNotAvailable: root.networkConnectionStore.accountBalanceNotAvailable
|
|
accountBalanceNotAvailableText: root.networkConnectionStore.accountBalanceNotAvailableText
|
|
}
|
|
|
|
StatusSectionLayout {
|
|
id: walletSectionLayout
|
|
currentIndex: 1
|
|
anchors.fill: parent
|
|
backButtonName: RootStore.backButtonName
|
|
onBackButtonClicked: {
|
|
if (rightPanelStackView.currentItem && !!rightPanelStackView.currentItem.resetStack) {
|
|
rightPanelStackView.currentItem.resetStack()
|
|
}
|
|
}
|
|
|
|
// Subsection back history keyed by the selected account. Navigates via
|
|
// d.displayAddress (not changeSelectedAccount, which advances the panel).
|
|
subsectionHistory: StatusQUtils.SubsectionNavigationHistory {
|
|
currentKey: RootStore.selectedAddress
|
|
validateFn: (address) => StatusQUtils.ModelUtils.indexOf(RootStore.accounts, "address", address) >= 0
|
|
onNavigateRequested: (address) => d.displayAddress(address)
|
|
}
|
|
|
|
leftPanel: LeftTabView {
|
|
id: leftTab
|
|
anchors.fill: parent
|
|
viewState: leftPanelState
|
|
|
|
onAddAccountPopupRequested: root.walletRootStore.runAddAccountPopup()
|
|
onAddWatchOnlyAccountPopupRequested: root.walletRootStore.runAddWatchOnlyAccountPopup()
|
|
onEditAccountPopupRequested: address => root.walletRootStore.runEditAccountPopup(address)
|
|
onWatchAccountHiddenFromTotalBalanceUpdated: (address, hideFromTotalBalance) =>
|
|
root.walletRootStore.updateWatchAccountHiddenFromTotalBalance(address, hideFromTotalBalance)
|
|
onAccountDeletionRequested: (address, password) =>
|
|
root.walletRootStore.deleteAccount(address, password)
|
|
onUserAuthenticationRequested: requestedBy =>
|
|
root.walletRootStore.authenticateLoggedInUser(requestedBy)
|
|
|
|
onAccountSelected: address => {
|
|
walletSectionLayout.goToNextPanel()
|
|
d.displayAddress(address)
|
|
}
|
|
onAllAccountsSelected: {
|
|
walletSectionLayout.goToNextPanel()
|
|
d.displayAllAddresses()
|
|
}
|
|
onSavedAddressesSelected: {
|
|
walletSectionLayout.goToNextPanel()
|
|
d.displaySavedAddresses()
|
|
}
|
|
onFollowingAddressesSelected: {
|
|
walletSectionLayout.goToNextPanel()
|
|
d.displayFollowingAddresses()
|
|
}
|
|
}
|
|
|
|
centerPanel: StackView {
|
|
id: rightPanelStackView
|
|
anchors.fill: parent
|
|
anchors.leftMargin: Theme.xlPadding * 2
|
|
anchors.rightMargin: Theme.xlPadding * 2
|
|
initialItem: walletContainer
|
|
replaceEnter: Transition {
|
|
NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 400; easing.type: Easing.OutCubic }
|
|
}
|
|
replaceExit: Transition {
|
|
NumberAnimation { property: "opacity"; from: 1; to: 0; duration: 400; easing.type: Easing.OutCubic }
|
|
}
|
|
}
|
|
headerBackground: AccountHeaderGradient {
|
|
width: parent.width
|
|
overview: RootStore.overview
|
|
}
|
|
|
|
footer: WalletFooter {
|
|
id: footer
|
|
|
|
readonly property bool isHoldingSelected: {
|
|
if (!rightPanelStackView.currentItem || rightPanelStackView.currentItem.currentTabIndex !== WalletLayout.RightPanelSelection.Collectibles) {
|
|
return false
|
|
}
|
|
return !!walletStore.currentViewedCollectible && walletStore.currentViewedHoldingTokenGroupKey !== ""
|
|
}
|
|
readonly property bool isCommunityCollectible: !!walletStore.currentViewedCollectible ? walletStore.currentViewedCollectible.communityId !== "" : false
|
|
readonly property bool isOwnerCommunityCollectible: isCommunityCollectible ? (walletStore.currentViewedCollectible.communityPrivilegesLevel === Constants.TokenPrivilegesLevel.Owner) : false
|
|
|
|
visible: anyActionAvailable
|
|
width: parent.width
|
|
height: visible ? implicitHeight: 0
|
|
walletStore: RootStore
|
|
transactionStore: root.transactionStore
|
|
swapEnabled: root.swapEnabled
|
|
buyEnabled: root.buyEnabled
|
|
networkConnectionStore: root.networkConnectionStore
|
|
isCommunityOwnershipTransfer: footer.isHoldingSelected && footer.isOwnerCommunityCollectible
|
|
communityName: {
|
|
if (selectedCommunityForCollectible.available)
|
|
return selectedCommunityForCollectible.item.name
|
|
if (isCommunityCollectible)
|
|
return Utils.compactAddress(walletStore.currentViewedCollectible.communityId, 4)
|
|
return ""
|
|
}
|
|
|
|
onLaunchShareAddressModal: Global.openShowQRPopup({
|
|
switchingAccounsEnabled: true,
|
|
hasFloatingButtons: true
|
|
})
|
|
onLaunchSendModal: (fromAddress) => {
|
|
if(isCommunityOwnershipTransfer) {
|
|
const tokenItem = walletStore.currentViewedCollectible
|
|
const ownership = StatusQUtils.ModelUtils.get(tokenItem.ownership, 0)
|
|
|
|
Global.openTransferOwnershipPopup(tokenItem.communityId,
|
|
footer.communityName,
|
|
tokenItem.communityImage,
|
|
{
|
|
key: tokenItem.tokenId,
|
|
privilegesLevel: tokenItem.communityPrivilegesLevel,
|
|
chainId: tokenItem.chainId,
|
|
name: tokenItem.name,
|
|
artworkSource: tokenItem.artworkSource,
|
|
accountAddress: fromAddress,
|
|
tokenAddress: tokenItem.contractAddress
|
|
})
|
|
return
|
|
}
|
|
|
|
// Common send modal popup:
|
|
root.sendTokenRequested(fromAddress,
|
|
walletStore.currentViewedHoldingTokenGroupKey,
|
|
walletStore.currentViewedHoldingType)
|
|
}
|
|
|
|
onLaunchSwapModal: {
|
|
let params = {
|
|
selectedAccountAddress: d.getSelectedOrFirstNonWatchedAddress(),
|
|
selectedNetworkChainId: StatusQUtils.ModelUtils.getByKey(root.networksStore.activeNetworks, "layer", 1, "chainId"),
|
|
}
|
|
|
|
if(!!walletStore.currentViewedHoldingTokenGroupKey && walletStore.currentViewedHoldingType === Constants.TokenType.ERC20) {
|
|
params.defaultFromGroupKey = walletStore.currentViewedHoldingTokenGroupKey
|
|
}
|
|
root.openSwapModalRequested(params)
|
|
}
|
|
onLaunchBuyCryptoModal: d.launchBuyCryptoModal()
|
|
|
|
ModelEntry {
|
|
id: selectedCommunityForCollectible
|
|
sourceModel: !!footer.walletStore.currentViewedCollectible && footer.isCommunityCollectible ? root.communitiesStore.communitiesList : null
|
|
key: "id"
|
|
value: footer.walletStore.currentViewedCollectible.communityId
|
|
}
|
|
}
|
|
|
|
// Layout
|
|
leftPanelWidthOverride: root.leftPanelWidthOverride
|
|
}
|
|
|
|
Loader {
|
|
id: addAccount
|
|
active: false
|
|
|
|
sourceComponent: AddAccountPopup {
|
|
isKeycardEnabled: root.isKeycardEnabled
|
|
store.emojiPopup: root.emojiPopup
|
|
store.addAccountModule: walletSection.addAccountModule
|
|
}
|
|
|
|
onLoaded: {
|
|
addAccount.item.open()
|
|
}
|
|
}
|
|
|
|
Loader {
|
|
id: keypairImport
|
|
active: false
|
|
|
|
sourceComponent: KeypairImportPopup {
|
|
store.keypairImportModule: RootStore.keypairImportModule
|
|
}
|
|
|
|
onLoaded: {
|
|
keypairImport.item.open()
|
|
}
|
|
}
|
|
|
|
Settings {
|
|
id: walletLocalSettings
|
|
category: "WalletLocalSettings_%1".arg(root.contactsStore.myPublicKey)
|
|
property alias selectedPanelIndex: walletSectionLayout.currentIndex
|
|
}
|
|
}
|