diff --git a/src/app/modules/main/module.nim b/src/app/modules/main/module.nim index 593dbed466..e352530435 100644 --- a/src/app/modules/main/module.nim +++ b/src/app/modules/main/module.nim @@ -410,6 +410,10 @@ proc connectForNotificationsOnly[T](self: Module[T]) = let args = SettingsBoolValueArgs(e) self.view.showIncludeWatchOnlyAccountUpdated(args.value) + self.events.on(SIGNAL_KEYPAIR_DELETED) do(e: Args): + let args = KeypairArgs(e) + self.view.showToastKeypairRemoved(args.keyPairName) + method load*[T]( self: Module[T], events: EventEmitter, diff --git a/src/app/modules/main/profile_section/wallet/accounts/controller.nim b/src/app/modules/main/profile_section/wallet/accounts/controller.nim index 946ffee3d9..6cdfb3d051 100644 --- a/src/app/modules/main/profile_section/wallet/accounts/controller.nim +++ b/src/app/modules/main/profile_section/wallet/accounts/controller.nim @@ -75,3 +75,6 @@ proc updateWalletAccountTestPreferredChains*(self: Controller, address, preferre proc areTestNetworksEnabled*(self: Controller): bool = return self.walletAccountService.areTestNetworksEnabled() + +proc getCurrencyBalance*(self: Controller, address: string, chainIds: seq[int], currency: string): float64 = + return self.walletAccountService.getCurrencyBalance(address, chainIds, currency) diff --git a/src/app/modules/main/profile_section/wallet/accounts/module.nim b/src/app/modules/main/profile_section/wallet/accounts/module.nim index d5d193d2e3..42219a2298 100644 --- a/src/app/modules/main/profile_section/wallet/accounts/module.nim +++ b/src/app/modules/main/profile_section/wallet/accounts/module.nim @@ -67,11 +67,15 @@ method convertWalletAccountDtoToKeyPairAccountItem(self: Module, account: Wallet prodPreferredChainIds = account.prodPreferredChainIds, testPreferredChainIds = account.testPreferredChainIds) -method createKeypairItems*(self: Module, walletAccounts: seq[WalletAccountDto], accountsTokens: OrderedTable[string, seq[WalletTokenDto]]): seq[KeyPairItem] = +method setBalance(self: Module, accountsTokens: OrderedTable[string, seq[WalletTokenDto]]) = let enabledChainIds = self.controller.getEnabledChainIds() let currency = self.controller.getCurrentCurrency() let currencyFormat = self.controller.getCurrencyFormat(currency) + for address, tokens in accountsTokens.pairs: + let balance = currencyAmountToItem(tokens.map(t => t.getCurrencyBalance(enabledChainIds, currency)).foldl(a + b, 0.0),currencyFormat) + self.view.setBalanceForKeyPairs(address, balance) +method createKeypairItems*(self: Module, walletAccounts: seq[WalletAccountDto]): seq[KeyPairItem] = var keyPairItems = keypairs.buildKeyPairsList(self.controller.getKeypairs(), excludeAlreadyMigratedPairs = false, excludePrivateKeyKeypairs = false, self.controller.areTestNetworksEnabled()) @@ -83,14 +87,18 @@ method createKeypairItems*(self: Module, walletAccounts: seq[WalletAccountDto], item.setAccounts(watchOnlyAccounts) keyPairItems.add(item) - for address, tokens in accountsTokens.pairs: - let balance = currencyAmountToItem(tokens.map(t => t.getCurrencyBalance(enabledChainIds, currency)).foldl(a + b, 0.0),currencyFormat) - for item in keyPairItems: - item.setBalanceForAddress(address, balance) + let enabledChainIds = self.controller.getEnabledChainIds() + let currency = self.controller.getCurrentCurrency() + let currencyFormat = self.controller.getCurrencyFormat(currency) + for item in keyPairItems: + let accounts = item.getAccountsModel().getItems() + for acc in accounts: + let balance = currencyAmountToItem(self.controller.getCurrencyBalance(acc.getAddress(), enabledChainIds, currency), currencyFormat) + acc.setBalance(balance) return keyPairItems -method refreshWalletAccounts*(self: Module, accountsTokens: OrderedTable[string, seq[WalletTokenDto]] = initOrderedTable[string, seq[WalletTokenDto]]()) = +method refreshWalletAccounts*(self: Module) = let walletAccounts = self.controller.getWalletAccounts() let items = walletAccounts.map(w => (block: @@ -99,7 +107,7 @@ method refreshWalletAccounts*(self: Module, accountsTokens: OrderedTable[string, walletAccountToWalletAccountItem(w, keycardAccount, areTestNetworksEnabled) )) - self.view.setKeyPairModelItems(self.createKeypairItems(walletAccounts, accountsTokens)) + self.view.setKeyPairModelItems(self.createKeypairItems(walletAccounts)) self.view.setItems(items) method load*(self: Module) = @@ -114,7 +122,7 @@ method load*(self: Module) = self.events.on(SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT) do(e:Args): let arg = TokensPerAccountArgs(e) - self.refreshWalletAccounts(arg.accountsTokens) + self.setBalance(arg.accountsTokens) self.events.on(SIGNAL_WALLET_ACCOUNT_UPDATED) do(e:Args): let args = AccountArgs(e) diff --git a/src/app/modules/main/profile_section/wallet/accounts/view.nim b/src/app/modules/main/profile_section/wallet/accounts/view.nim index 45260735d5..89c112ae59 100644 --- a/src/app/modules/main/profile_section/wallet/accounts/view.nim +++ b/src/app/modules/main/profile_section/wallet/accounts/view.nim @@ -4,6 +4,7 @@ import ./io_interface import ./model import app/modules/shared_models/keypair_model import app/modules/shared_models/wallet_account_item +import app/modules/shared_models/currency_amount QtObject: type @@ -104,3 +105,6 @@ QtObject: proc updateWalletAccountTestPreferredChains*(self: View, address: string, preferredChainIds: string) {.slot.} = self.delegate.updateWalletAccountTestPreferredChains(address, preferredChainIds) + + proc setBalanceForKeyPairs*(self: View, address: string, balance: CurrencyAmount) = + self.keyPairModel.setBalanceForAddress(address, balance) diff --git a/src/app/modules/main/view.nim b/src/app/modules/main/view.nim index 0428914d6b..5114ace5e4 100644 --- a/src/app/modules/main/view.nim +++ b/src/app/modules/main/view.nim @@ -276,3 +276,4 @@ QtObject: proc showToastKeypairRenamed*(self: View, oldName: string, newName: string) {.signal.} proc showNetworkEndpointUpdated*(self: View, name: string, isTest: bool) {.signal.} proc showIncludeWatchOnlyAccountUpdated*(self: View, includeWatchOnly: bool) {.signal.} + proc showToastKeypairRemoved*(self: View, keypairName: string) {.signal.} diff --git a/src/app/modules/shared_models/keypair_model.nim b/src/app/modules/shared_models/keypair_model.nim index 50134d3fd0..b29a61ab7b 100644 --- a/src/app/modules/shared_models/keypair_model.nim +++ b/src/app/modules/shared_models/keypair_model.nim @@ -1,5 +1,6 @@ import NimQml, Tables, strformat, sequtils, sugar import keypair_item +import ./currency_amount export keypair_item @@ -95,3 +96,7 @@ QtObject: if item.isNil: return item.setName(name) + + proc setBalanceForAddress*(self: KeyPairModel, address: string, balance: CurrencyAmount) = + for item in self.items: + item.setBalanceForAddress(address, balance) diff --git a/src/app_service/service/wallet_account/service_account.nim b/src/app_service/service/wallet_account/service_account.nim index da238f45b7..f63fb04848 100644 --- a/src/app_service/service/wallet_account/service_account.nim +++ b/src/app_service/service/wallet_account/service_account.nim @@ -362,6 +362,7 @@ proc deleteKeypair*(self: Service, keyUid: string) = self.updateAccountsPositions() for acc in kp.accounts: self.removeAccountFromLocalStoreAndNotify(acc.address) + self.events.emit(SIGNAL_KEYPAIR_DELETED, KeypairArgs(keyPairName: kp.name)) except Exception as e: error "error: ", procName="deleteKeypair", errName = e.name, errDesription = e.msg @@ -589,4 +590,4 @@ proc getCurrencyFormat*(self: Service, symbol: string): CurrencyFormatDto = return self.currencyService.getCurrencyFormat(symbol) proc areTestNetworksEnabled*(self: Service): bool = - return self.settingsService.areTestNetworksEnabled() \ No newline at end of file + return self.settingsService.areTestNetworksEnabled() diff --git a/src/app_service/service/wallet_account/signals_and_payloads.nim b/src/app_service/service/wallet_account/signals_and_payloads.nim index 001229279f..4c027d10d0 100644 --- a/src/app_service/service/wallet_account/signals_and_payloads.nim +++ b/src/app_service/service/wallet_account/signals_and_payloads.nim @@ -18,6 +18,7 @@ const SIGNAL_WALLET_ACCOUNT_PREFERRED_SHARING_CHAINS_UPDATED* = "walletAccount/p const SIGNAL_KEYPAIR_SYNCED* = "keypairSynced" const SIGNAL_KEYPAIR_NAME_CHANGED* = "keypairNameChanged" +const SIGNAL_KEYPAIR_DELETED* = "keypairDeleted" const SIGNAL_NEW_KEYCARD_SET* = "newKeycardSet" const SIGNAL_KEYCARD_REBUILD* = "keycardRebuild" @@ -38,6 +39,7 @@ type AccountArgs* = ref object of Args type KeypairArgs* = ref object of Args keypair*: KeypairDto + keyPairName*: string oldKeypairName*: string type KeycardArgs* = ref object of Args @@ -63,4 +65,4 @@ type KeycardActivityArgs* = ref object of Args type ChainIdForUrlArgs* = ref object of Args chainId*: int success*: bool - url*: string \ No newline at end of file + url*: string diff --git a/storybook/pages/ProfileAccountsPage.qml b/storybook/pages/ProfileAccountsPage.qml index 4a2dc1ecdd..f0af5eaf9d 100644 --- a/storybook/pages/ProfileAccountsPage.qml +++ b/storybook/pages/ProfileAccountsPage.qml @@ -43,7 +43,6 @@ SplitView { delegate: WalletKeyPairDelegate { width: parent.width keyPair: model.keyPair - chainShortNames: d.walletStore.getAllNetworksSupportedString() userProfilePublicKey: d.walletStore.userProfilePublicKey onGoToAccountView: console.warn("onGoToAccountView ::") } diff --git a/ui/app/AppLayouts/Profile/controls/WalletAccountDelegate.qml b/ui/app/AppLayouts/Profile/controls/WalletAccountDelegate.qml index f7da7961b6..1fd77e04b2 100644 --- a/ui/app/AppLayouts/Profile/controls/WalletAccountDelegate.qml +++ b/ui/app/AppLayouts/Profile/controls/WalletAccountDelegate.qml @@ -15,6 +15,7 @@ StatusListItem { property var account property var getNetworkShortNames: function(chainIds){} property int totalCount: 0 + property bool nextIconVisible: true signal goToAccountView() @@ -37,6 +38,7 @@ StatusListItem { components: StatusIcon { icon: "next" color: Theme.palette.baseColor1 + visible: root.nextIconVisible } onClicked: goToAccountView() diff --git a/ui/app/AppLayouts/Profile/popups/RemoveKeypairPopup.qml b/ui/app/AppLayouts/Profile/popups/RemoveKeypairPopup.qml new file mode 100644 index 0000000000..f486606d2a --- /dev/null +++ b/ui/app/AppLayouts/Profile/popups/RemoveKeypairPopup.qml @@ -0,0 +1,109 @@ +import QtQuick 2.13 +import QtQuick.Layouts 1.13 +import QtQml.Models 2.14 + +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 +import StatusQ.Controls 0.1 +import StatusQ.Popups.Dialog 0.1 +import StatusQ.Core.Utils 0.1 as StatusQUtils + +import AppLayouts.Profile.controls 1.0 +import AppLayouts.Wallet 1.0 + +import shared.panels 1.0 +import utils 1.0 + +StatusDialog { + id: root + + property string name + property var relatedAccounts + property var getNetworkShortNames: function(chainIds){} + + signal confirmClicked() + + title: qsTr("Remove %1 keypair").arg(name) + width: 521 + + ColumnLayout { + anchors.fill: parent + spacing: Style.current.xlPadding + + StatusBaseText { + Layout.fillWidth: true + text: qsTr("Are you sure you want to remove %1 keypair? The keypair will be removed from all of your synced devices. Make sure you have a backup of your keys or seed phrase before proceeding.").arg(name) + wrapMode: Text.WordWrap + font.pixelSize: 15 + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 8 + StatusBaseText { + text: qsTr("Accounts related to this keypair will also be removed:") + font.pixelSize: 15 + } + + StatusScrollView { + id: scrolView1 + Layout.fillWidth: true + Layout.fillHeight: true + contentWidth: availableWidth + Rectangle { + implicitWidth: scrolView1.availableWidth + implicitHeight: listview.height + color: Theme.palette.transparent + border.color: Theme.palette.baseColor2 + border.width: 1 + radius: 8 + clip: true + Column { + id: listview + width: scrolView1.availableWidth + Repeater { + id: repeater + model: root.relatedAccounts + delegate: WalletAccountDelegate { + id: delegate + width: parent.width + account : model.account + getNetworkShortNames: root.getNetworkShortNames + color: Theme.palette.transparent + nextIconVisible: false + components: StatusBaseText { + font.pixelSize: 15 + text: LocaleUtils.currencyAmountToLocaleString(!!account ? account.balance: "") + } + Rectangle { + anchors.bottom: parent.bottom + width: parent.width + height: 1 + color: Theme.palette.baseColor2 + visible: index < repeater.count - 1 + } + } + } + } + } + } + } + } + + footer: StatusDialogFooter { + rightButtons: ObjectModel { + StatusButton { + text: qsTr("Cancel") + onClicked : root.close() + } + StatusButton { + type: StatusBaseButton.Type.Danger + text: qsTr("Remove keypair & associated accounts") + onClicked: root.confirmClicked() + Keys.onReturnPressed: function(event) { + root.confirmClicked() + } + } + } + } +} diff --git a/ui/app/AppLayouts/Profile/popups/qmldir b/ui/app/AppLayouts/Profile/popups/qmldir index c498f964c3..42ab4ad1af 100644 --- a/ui/app/AppLayouts/Profile/popups/qmldir +++ b/ui/app/AppLayouts/Profile/popups/qmldir @@ -6,3 +6,4 @@ RenameKeypairPopup 1.0 RenameKeypairPopup.qml WalletKeypairAccountMenu 1.0 WalletKeypairAccountMenu.qml WalletAddressMenu 1.0 WalletAddressMenu.qml RenameAccontModal 1.0 RenameAccontModal.qml +RemoveKeypairPopup 1.0 RemoveKeypairPopup.qml diff --git a/ui/app/AppLayouts/Profile/views/WalletView.qml b/ui/app/AppLayouts/Profile/views/WalletView.qml index 35b2ec7c1d..e8599ae580 100644 --- a/ui/app/AppLayouts/Profile/views/WalletView.qml +++ b/ui/app/AppLayouts/Profile/views/WalletView.qml @@ -116,8 +116,9 @@ SettingsContentBase { renameKeypairPopup.active = true } onRunRemoveKeypairFlow: { - removeKeypairPopup.keyUid = model.keyPair.keyUid removeKeypairPopup.name = model.keyPair.name + removeKeypairPopup.keyUid = model.keyPair.keyUid + removeKeypairPopup.accounts= model.keyPair.accounts removeKeypairPopup.active = true } } @@ -177,8 +178,9 @@ SettingsContentBase { renameKeypairPopup.active = true } onRunRemoveKeypairFlow: { - removeKeypairPopup.keyUid = keyPair.keyUid removeKeypairPopup.name = keyPair.name + removeKeypairPopup.keyUid = keyPair.keyUid + removeKeypairPopup.accounts= keyPair.accounts removeKeypairPopup.active = true } } @@ -234,23 +236,21 @@ SettingsContentBase { id: removeKeypairPopup active: false - property string keyUid property string name + property string keyUid + property var accounts - 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: { + sourceComponent: RemoveKeypairPopup { + name: removeKeypairPopup.name + relatedAccounts: removeKeypairPopup.accounts + getNetworkShortNames: function(chainIds) {return root.walletStore.getNetworkShortNames(chainIds)} + onClosed: removeKeypairPopup.active = false + onConfirmClicked: { root.walletStore.deleteKeypair(removeKeypairPopup.keyUid) removeKeypairPopup.active = false } } - - onLoaded: { - removeKeypairPopup.item.open() - } + onLoaded: removeKeypairPopup.item.open() } } } diff --git a/ui/app/mainui/AppMain.qml b/ui/app/mainui/AppMain.qml index 2d389474d1..cef6d470e4 100644 --- a/ui/app/mainui/AppMain.qml +++ b/ui/app/mainui/AppMain.qml @@ -137,6 +137,17 @@ Item { Constants.ephemeralNotificationType.success, "") } + + function onShowToastKeypairRemoved(keypairName: string) { + Global.displayToastMessage( + qsTr("“%1” keypair and its associated accounts were successfully removed from all devices").arg(keypairName), + "", + "checkmark-circle", + false, + Constants.ephemeralNotificationType.success, + "" + ) + } } QtObject { diff --git a/ui/imports/shared/controls/qmldir b/ui/imports/shared/controls/qmldir index 00d1e71914..046b5ced4a 100644 --- a/ui/imports/shared/controls/qmldir +++ b/ui/imports/shared/controls/qmldir @@ -43,3 +43,4 @@ GetSyncCodeDesktopInstructions 1.0 GetSyncCodeDesktopInstructions.qml ErrorDetails 1.0 ErrorDetails.qml CopyButton 1.0 CopyButton.qml DisabledTooltipButton 1.0 DisabledTooltipButton.qml +WalletAccountListItem 1.0 WalletAccountListItem.qml