feat(@desktop/wallet): Account View Setting Actions

fixes #11689
This commit is contained in:
Khushboo Mehta 2023-07-28 09:57:58 +02:00 committed by Khushboo-dev-cpp
parent 5af56be60f
commit ea91cd605f
11 changed files with 351 additions and 117 deletions

View File

@ -3,12 +3,17 @@ import QtQuick 2.13
import StatusQ.Components 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1
import StatusQ.Controls 0.1
import utils 1.0
StatusListItem {
id: root
property var keyPair
signal buttonClicked()
title: keyPair ? keyPair.pairType === Constants.keycard.keyPairType.watchOnly ? qsTr("Watch only") : keyPair.name: ""
titleAsideText: keyPair && keyPair.pairType === Constants.keycard.keyPairType.profile ? Utils.getElidedCompressedPk(keyPair.pubKey): ""
asset {
@ -21,7 +26,12 @@ StatusListItem {
charactersLen: 2
isLetterIdenticon: !!keyPair && !keyPair.icon && !asset.name.toString()
}
color: Theme.palette.transparent
color: {
if (sensor.containsMouse || root.highlighted) {
return Theme.palette.baseColor2
}
return Theme.palette.transparent
}
ringSettings {
ringSpecModel: keyPair && keyPair.pairType === Constants.keycard.keyPairType.profile ? Utils.getColorHashAsJson(root.userProfilePublicKey) : []
ringPxSize: Math.max(asset.width / 24.0)
@ -42,4 +52,17 @@ StatusListItem {
titleText.font.pixelSize: 12
titleText.color: Theme.palette.indirectColor1
}
components: [
StatusRoundButton {
width: 32
height: 32
radius: 8
visible: root.sensor.containsMouse
type: StatusRoundButton.Type.Quinary
icon.name: "more"
icon.color: hovered ? Theme.palette.directColor1 : Theme.palette.baseColor1
icon.hoverColor: Theme.palette.primaryColor3
onClicked: root.buttonClicked()
}
]
}

View File

@ -2,16 +2,71 @@ import QtQuick 2.13
import StatusQ.Components 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import shared.controls 1.0
StatusListItem {
id: root
property bool isInteractive: false
property bool copyButtonEnabled: false
property bool moreButtonEnabled: false
signal buttonClicked()
signal copyClicked()
QtObject {
id: d
readonly property var timer: Timer {}
}
statusListItemTitle.customColor: Theme.palette.baseColor1
statusListItemTitle.font.pixelSize: 13
statusListItemTitle.lineHeightMode: Text.FixedHeight
statusListItemTitle.lineHeight: 18
statusListItemSubTitle.customColor: Theme.palette.directColor1
statusListItemSubTitle.textFormat: Qt.RichText
statusListItemSubTitle.wrapMode: Qt.TextWrapAnywhere
statusListItemSubTitle.lineHeightMode: Text.FixedHeight
statusListItemSubTitle.lineHeight: 22
color: Theme.palette.transparent
statusListItemSubTitle.elide: Text.ElideNone
statusListItemSubTitle.wrapMode: Text.WrapAnywhere
color: {
if (isInteractive && (sensor.containsMouse || root.highlighted)) {
return Theme.palette.baseColor2
}
return Theme.palette.transparent
}
components: [
StatusRoundButton {
width: 32
height: 32
radius: 8
visible: moreButtonEnabled && isInteractive && root.sensor.containsMouse
type: StatusRoundButton.Type.Quinary
icon.name: "more"
icon.color: hovered ? Theme.palette.directColor1 : Theme.palette.baseColor1
icon.hoverColor: Theme.palette.primaryColor3
onClicked: root.buttonClicked()
},
StatusRoundButton {
id: copyButton
property bool checked: false
width: 32
height: 32
radius: 8
visible: copyButtonEnabled && isInteractive && root.sensor.containsMouse
type: StatusRoundButton.Type.Quinary
icon.name: copyButton.checked ? "tiny/checkmark": "copy"
icon.color: copyButton.checked ? Theme.palette.successColor1 : hovered ? Theme.palette.directColor1 : Theme.palette.baseColor1
icon.hoverColor: copyButton.checked ? Theme.palette.successColor1 : Theme.palette.primaryColor3
onClicked: {
copyButton.checked = true
root.copyClicked()
d.timer.setTimeout(function() {
copyButton.checked = false
}, 1500)
}
}
]
}

View File

@ -9,6 +9,8 @@ import StatusQ.Popups 0.1
import utils 1.0
import AppLayouts.Profile.popups 1.0
Rectangle {
id: root
@ -75,60 +77,13 @@ Rectangle {
Loader {
id: menuLoader
active: false
sourceComponent: StatusMenu {
sourceComponent: WalletAccountKeycardMenu {
onClosed: {
menuLoader.active = false
}
StatusAction {
text: enabled? qsTr("Show encrypted QR of keypairs on device") : ""
enabled: !d.isProfileKeypair &&
!model.keyPair.migratedToKeycard &&
!model.keyPair.operability === Constants.keypair.operability.nonOperable
icon.name: "qr"
icon.color: Theme.palette.primaryColor1
onTriggered: {
console.warn("TODO: show encrypted QR")
}
}
StatusAction {
text: model.keyPair.migratedToKeycard? qsTr("Stop using Keycard") : qsTr("Move keys to a Keycard")
icon.name: model.keyPair.migratedToKeycard? "keycard-crossed" : "keycard"
icon.color: Theme.palette.primaryColor1
onTriggered: {
if (model.keyPair.migratedToKeycard)
console.warn("TODO: stop using Keycard")
else
console.warn("TODO: move keys to a Keycard")
}
}
StatusAction {
text: enabled? qsTr("Rename keypair") : ""
enabled: !d.isProfileKeypair
icon.name: "edit"
icon.color: Theme.palette.primaryColor1
onTriggered: {
root.runRenameKeypairFlow()
}
}
StatusMenuSeparator {
visible: !d.isProfileKeypair
}
StatusAction {
text: enabled? qsTr("Remove keypair and associated accounts") : ""
enabled: !d.isProfileKeypair
type: StatusAction.Type.Danger
icon.name: "delete"
icon.color: Theme.palette.dangerColor1
onTriggered: {
root.runRemoveKeypairFlow()
}
}
keyPair: root.keyPair
onRunRenameKeypairFlow: root.runRenameKeypairFlow()
onRunRemoveKeypairFlow: root.runRemoveKeypairFlow()
}
}
},

View File

@ -0,0 +1,69 @@
import QtQuick 2.15
import StatusQ.Core.Theme 0.1
import StatusQ.Popups 0.1
import utils 1.0
StatusMenu {
id: root
property var keyPair
signal runRenameKeypairFlow()
signal runRemoveKeypairFlow()
QtObject {
id: d
readonly property bool isProfileKeypair: keyPair.pairType === Constants.keycard.keyPairType.profile
}
StatusAction {
text: enabled? qsTr("Show encrypted QR of keypairs on device") : ""
enabled: !d.isProfileKeypair &&
!keyPair.migratedToKeycard &&
!keyPair.operability === Constants.keypair.operability.nonOperable
icon.name: "qr"
icon.color: Theme.palette.primaryColor1
onTriggered: {
console.warn("TODO: show encrypted QR")
}
}
StatusAction {
text: keyPair.migratedToKeycard? qsTr("Stop using Keycard") : qsTr("Move keys to a Keycard")
icon.name: keyPair.migratedToKeycard? "keycard-crossed" : "keycard"
icon.color: Theme.palette.primaryColor1
onTriggered: {
if (keyPair.migratedToKeycard)
console.warn("TODO: stop using Keycard")
else
console.warn("TODO: move keys to a Keycard")
}
}
StatusAction {
text: enabled? qsTr("Rename keypair") : ""
enabled: !d.isProfileKeypair
icon.name: "edit"
icon.color: Theme.palette.primaryColor1
onTriggered: {
root.runRenameKeypairFlow()
}
}
StatusMenuSeparator {
visible: !d.isProfileKeypair
}
StatusAction {
text: enabled? qsTr("Remove keypair and associated accounts") : ""
enabled: !d.isProfileKeypair
type: StatusAction.Type.Danger
icon.name: "delete"
icon.color: Theme.palette.dangerColor1
onTriggered: {
root.runRemoveKeypairFlow()
}
}
}

View File

@ -0,0 +1,80 @@
import QtQuick 2.15
import StatusQ.Popups 0.1
import utils 1.0
import AppLayouts.Wallet.popups 1.0
StatusMenu {
id: root
property string selectedAddress
property bool areTestNetworksEnabled: false
property string preferredSharingNetworks
property var preferredSharingNetworksArray
signal copyToClipboard(string address)
function openMenu(delegate) {
const x = delegate.width - 40
const y = delegate.height / 2 + 20
root.popup(delegate, x, y)
}
StatusAction {
id: showOnEtherscanAction
text: qsTr("View address on Etherscan")
icon.name: "link"
onTriggered: {
const link = areTestNetworksEnabled ? Constants.networkExplorerLinks.goerliEtherscan : Constants.networkExplorerLinks.etherscan
Global.openLink("%1/%2/%3".arg(link).arg(Constants.networkExplorerLinks.addressPath).arg(root.selectedAddress))
}
}
StatusAction {
id: showOnArbiscanAction
text: qsTr("View address on Arbiscan")
icon.name: "link"
onTriggered: {
const link = areTestNetworksEnabled ? Constants.networkExplorerLinks.goerliArbiscan : Constants.networkExplorerLinks.arbiscan
Global.openLink("%1/%2/%3".arg(link).arg(Constants.networkExplorerLinks.addressPath).arg(root.selectedAddress))
}
}
StatusAction {
id: showOnOptimismAction
text: qsTr("View address on Optimism Explorer")
icon.name: "link"
onTriggered: {
const link = areTestNetworksEnabled ? Constants.networkExplorerLinks.goerliOptimistic : Constants.networkExplorerLinks.optimistic
Global.openLink("%1/%2/%3".arg(link).arg(Constants.networkExplorerLinks.addressPath).arg(root.selectedAddress))
}
}
StatusSuccessAction {
id: copyAddressAction
successText: qsTr("Address copied")
text: qsTr("Copy address")
icon.name: "copy"
onTriggered: root.copyToClipboard(root.selectedAddress)
}
StatusAction {
id: showQrAction
text: qsTr("Show address QR")
icon.name: "qr"
onTriggered: Global.openPopup(addressQr)
}
Component {
id: addressQr
ReceiveModal {
anchors.centerIn: parent
address: root.selectedAddress
chainShortNames: root.preferredSharingNetworks
preferredSharingNetworksArray: root.preferredSharingNetworksArray
readOnly: true
hasFloatingButtons: false
advancedHeaderComponent: null
description: qsTr("Address")
onClosed: destroy()
}
}
}

View File

@ -2,4 +2,7 @@ BackupSeedModal 1.0 BackupSeedModal.qml
SetupSyncingPopup 1.0 SetupSyncingPopup.qml
AddSocialLinkModal 1.0 AddSocialLinkModal.qml
ModifySocialLinkModal 1.0 ModifySocialLinkModal.qml
RenameKeypairPopup 1.0 RenameKeypairPopup.qml
RenameKeypairPopup 1.0 RenameKeypairPopup.qml
WalletAccountKeycardMenu 1.0 WalletAccountKeycardMenu.qml
WalletAddressMenu 1.0 WalletAddressMenu.qml
RenameAccontModal 1.0 RenameAccontModal.qml

View File

@ -104,4 +104,8 @@ QtObject {
}
return prefChains
}
function copyToClipboard(textToCopy) {
globalUtils.copyToClipboard(textToCopy)
}
}

View File

@ -109,6 +109,17 @@ SettingsContentBase {
onGoToAccountOrderView: {
stackContainer.currentIndex = accountOrderViewIndex
}
onRunRenameKeypairFlow: {
renameKeypairPopup.keyUid = model.keyPair.keyUid
renameKeypairPopup.name = model.keyPair.name
renameKeypairPopup.accounts = model.keyPair.accounts
renameKeypairPopup.active = true
}
onRunRemoveKeypairFlow: {
removeKeypairPopup.keyUid = model.keyPair.keyUid
removeKeypairPopup.name = model.keyPair.name
removeKeypairPopup.active = true
}
}
NetworksView {
@ -156,6 +167,17 @@ SettingsContentBase {
userProfilePublicKey: walletStore.userProfilePublicKey
onGoBack: stackContainer.currentIndex = mainViewIndex
onVisibleChanged: if(!visible) root.walletStore.selectedAccount = null
onRunRenameKeypairFlow: {
renameKeypairPopup.keyUid = keyPair.keyUid
renameKeypairPopup.name = keyPair.name
renameKeypairPopup.accounts = keyPair.accounts
renameKeypairPopup.active = true
}
onRunRemoveKeypairFlow: {
removeKeypairPopup.keyUid = keyPair.keyUid
removeKeypairPopup.name = keyPair.name
removeKeypairPopup.active = true
}
}
DappPermissionsView {
@ -180,5 +202,52 @@ SettingsContentBase {
image.fillMode: Image.PreserveAspectCrop
}
}
Loader {
id: renameKeypairPopup
active: false
property string keyUid
property string name
property var accounts
sourceComponent: RenameKeypairPopup {
accountsModule: root.walletStore.accountsModule
keyUid: renameKeypairPopup.keyUid
name: renameKeypairPopup.name
accounts: renameKeypairPopup.accounts
onClosed: {
renameKeypairPopup.active = false
}
}
onLoaded: {
renameKeypairPopup.item.open()
}
}
Loader {
id: removeKeypairPopup
active: false
property string keyUid
property string name
sourceComponent: ConfirmationDialog {
headerSettings.title: qsTr("Confirm %1 Removal").arg(removeKeypairPopup.name)
confirmationText: qsTr("You will not be able to restore viewing access to any account of this keypair in the future unless you import this keypair again.")
confirmButtonLabel: qsTr("Remove keypair")
onConfirmButtonClicked: {
root.walletStore.deleteKeypair(removeKeypairPopup.keyUid)
removeKeypairPopup.active = false
}
}
onLoaded: {
removeKeypairPopup.item.open()
}
}
}
}

View File

@ -9,6 +9,7 @@ import StatusQ.Core.Utils 0.1 as StatusQUtils
import AppLayouts.Wallet 1.0
import AppLayouts.Wallet.controls 1.0
import AppLayouts.Profile.popups 1.0
import shared.popups 1.0
import shared.panels 1.0
@ -16,13 +17,14 @@ import utils 1.0
import SortFilterProxyModel 0.2
import "../../popups"
import "../../controls"
ColumnLayout {
id: root
signal goBack
signal runRenameKeypairFlow()
signal runRemoveKeypairFlow()
property var account
property var keyPair
@ -36,7 +38,11 @@ ColumnLayout {
readonly property bool privateKeyAccount: keyPair && keyPair.pairType ? keyPair.pairType === Constants.keycard.keyPairType.privateKeyImport: false
readonly property string preferredSharingNetworks: !!account ? account.preferredSharingChainIds: ""
property var preferredSharingNetworksArray: preferredSharingNetworks.split(":").filter(Boolean)
onPreferredSharingNetworksChanged: preferredSharingNetworksArray = preferredSharingNetworks.split(":").filter(Boolean)
property string preferredSharingNetworkShortNames: walletStore.getNetworkShortNames(preferredSharingNetworks)
onPreferredSharingNetworksChanged: {
preferredSharingNetworksArray = preferredSharingNetworks.split(":")
preferredSharingNetworkShortNames = walletStore.getNetworkShortNames(preferredSharingNetworks)
}
}
spacing: 0
@ -103,11 +109,14 @@ ColumnLayout {
}
WalletAccountDetailsListItem {
Layout.fillWidth: true
isInteractive: true
moreButtonEnabled: true
title: qsTr("Address")
subTitle: {
let address = root.account && root.account.address ? root.account.address: ""
return WalletUtils.colorizedChainPrefix(walletStore.getNetworkShortNames(d.preferredSharingNetworks)) + address
return WalletUtils.colorizedChainPrefix(d.preferredSharingNetworkShortNames) + address
}
onButtonClicked: addressMenu.openMenu(this)
}
Separator {
Layout.fillWidth: true
@ -126,6 +135,7 @@ ColumnLayout {
Layout.fillWidth: true
keyPair: root.keyPair
visible: !d.watchOnlyAccount
onButtonClicked: keycardMenu.popup(this, this.width - 40, this.height / 2 + 20)
}
Separator {
Layout.fillWidth: true
@ -162,8 +172,11 @@ ColumnLayout {
WalletAccountDetailsListItem {
id: derivationPath
Layout.fillWidth: true
isInteractive: true
copyButtonEnabled: true
title: qsTr("Derivation Path")
subTitle: root.account ? Utils.getPathForDisplay(root.account.path) : ""
onCopyClicked: root.walletStore.copyToClipboard(root.account ? root.account.path : "")
visible: !!subTitle && !d.privateKeyAccount && !d.watchOnlyAccount
}
Separator {
@ -258,4 +271,20 @@ ColumnLayout {
emojiPopup: root.emojiPopup
}
}
WalletAddressMenu {
id: addressMenu
selectedAddress: !!root.account ? root.account.address: ""
areTestNetworksEnabled: root.walletStore.areTestNetworksEnabled
preferredSharingNetworks: d.preferredSharingNetworkShortNames
preferredSharingNetworksArray: d.preferredSharingNetworksArray
onCopyToClipboard: root.walletStore.copyToClipboard(address)
}
WalletAccountKeycardMenu {
id: keycardMenu
keyPair: root.keyPair
onRunRenameKeypairFlow: root.runRenameKeypairFlow()
onRunRemoveKeypairFlow: root.runRemoveKeypairFlow()
}
}

View File

@ -25,6 +25,8 @@ Column {
signal goToAccountOrderView()
signal goToAccountView(var account, var keypair)
signal goToDappPermissionsView()
signal runRenameKeypairFlow(var model)
signal runRemoveKeypairFlow(var model)
spacing: 8
@ -108,65 +110,9 @@ Column {
includeWatchOnlyAccount: walletStore.includeWatchOnlyAccount
onGoToAccountView: root.goToAccountView(account, keyPair)
onToggleIncludeWatchOnlyAccount: walletStore.toggleIncludeWatchOnlyAccount()
onRunRenameKeypairFlow: {
renameKeypairPopup.keyUid = model.keyPair.keyUid
renameKeypairPopup.name = model.keyPair.name
renameKeypairPopup.accounts = model.keyPair.accounts
renameKeypairPopup.active = true
}
onRunRemoveKeypairFlow: {
removeKeypairPopup.keyUid = model.keyPair.keyUid
removeKeypairPopup.name = model.keyPair.name
removeKeypairPopup.active = true
}
onRunRenameKeypairFlow: root.runRenameKeypairFlow(model)
onRunRemoveKeypairFlow: root.runRemoveKeypairFlow(model)
}
}
}
Loader {
id: renameKeypairPopup
active: false
property string keyUid
property string name
property var accounts
sourceComponent: RenameKeypairPopup {
accountsModule: root.walletStore.accountsModule
keyUid: renameKeypairPopup.keyUid
name: renameKeypairPopup.name
accounts: renameKeypairPopup.accounts
onClosed: {
renameKeypairPopup.active = false
}
}
onLoaded: {
renameKeypairPopup.item.open()
}
}
Loader {
id: removeKeypairPopup
active: false
property string keyUid
property string name
sourceComponent: ConfirmationDialog {
headerSettings.title: qsTr("Confirm %1 Removal").arg(removeKeypairPopup.name)
confirmationText: qsTr("You will not be able to restore viewing access to any account of this keypair in the future unless you import this keypair again.")
confirmButtonLabel: qsTr("Remove keypair")
onConfirmButtonClicked: {
root.walletStore.deleteKeypair(removeKeypairPopup.keyUid)
removeKeypairPopup.active = false
}
}
onLoaded: {
removeKeypairPopup.item.open()
}
}
}

View File

@ -1,4 +1,5 @@
NetworkSelectPopup 1.0 NetworkSelectPopup.qml
ActivityFilterMenu 1.0 ActivityFilterMenu.qml
ActivityPeriodFilterSubMenu 1.0 filterSubMenus/ActivityPeriodFilterSubMenu.qml
ActivityTypeFilterSubMenu 1.0 filterSubMenus/ActivityTypeFilterSubMenu.qml
ActivityTypeFilterSubMenu 1.0 filterSubMenus/ActivityTypeFilterSubMenu.qml
ReceiveModal 1.0 ReceiveModal.qml