diff --git a/src/app/modules/main/profile_section/io_interface.nim b/src/app/modules/main/profile_section/io_interface.nim index 443bb8e975..ce53eab087 100644 --- a/src/app/modules/main/profile_section/io_interface.nim +++ b/src/app/modules/main/profile_section/io_interface.nim @@ -93,4 +93,13 @@ method communitiesModuleDidLoad*(self: AccessInterface) {.base.} = raise newException(ValueError, "No implementation available") method getKeycardModule*(self: AccessInterface): QVariant {.base.} = - raise newException(ValueError, "No implementation available") \ No newline at end of file + raise newException(ValueError, "No implementation available") + +method walletModuleDidLoad*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method getWalletAccountsModule*(self: AccessInterface): QVariant {.base.} = + raise newException(ValueError, "No implementation available") + +method getWalletNetworksModule*(self: AccessInterface): QVariant {.base.} = + raise newException(ValueError, "No implementation available") diff --git a/src/app/modules/main/profile_section/module.nim b/src/app/modules/main/profile_section/module.nim index 0f592a8f44..1c6eb4fa94 100644 --- a/src/app/modules/main/profile_section/module.nim +++ b/src/app/modules/main/profile_section/module.nim @@ -37,6 +37,7 @@ import ./notifications/module as notifications_module import ./ens_usernames/module as ens_usernames_module import ./communities/module as communities_module import ./keycard/module as keycard_module +import ./wallet/module as wallet_module export io_interface @@ -61,6 +62,7 @@ type ensUsernamesModule: ens_usernames_module.AccessInterface communitiesModule: communities_module.AccessInterface keycardModule: keycard_module.AccessInterface + walletModule: wallet_module.AccessInterface proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitter, @@ -109,6 +111,8 @@ proc newModule*(delegate: delegate_interface.AccessInterface, result.keycardModule = keycard_module.newModule(result, events, keycardService, settingsService, networkService, privacyService, accountsService, walletAccountService, keychainService) + result.walletModule = wallet_module.newModule(result, events, walletAccountService, settingsService, networkService) + singletonInstance.engine.setRootContextProperty("profileSectionModule", result.viewVariant) method delete*(self: Module) = @@ -143,6 +147,7 @@ method load*(self: Module) = self.ensUsernamesModule.load() self.communitiesModule.load() self.keycardModule.load() + self.walletModule.load() method isLoaded*(self: Module): bool = return self.moduleLoaded @@ -187,6 +192,9 @@ proc checkIfModuleDidLoad(self: Module) = if(not self.keycardModule.isLoaded()): return + if(not self.walletModule.isLoaded()): + return + self.moduleLoaded = true self.delegate.profileSectionDidLoad() @@ -263,4 +271,13 @@ method communitiesModuleDidLoad*(self: Module) = self.checkIfModuleDidLoad() method getKeycardModule*(self: Module): QVariant = - self.keycardModule.getModuleAsVariant() \ No newline at end of file + self.keycardModule.getModuleAsVariant() + +method walletModuleDidLoad*(self: Module) = + self.checkIfModuleDidLoad() + +method getWalletAccountsModule*(self: Module): QVariant = + return self.walletModule.getAccountsModuleAsVariant() + +method getWalletNetworksModule*(self: Module): QVariant = + return self.walletModule.getNetworksModuleAsVariant() \ No newline at end of file diff --git a/src/app/modules/main/profile_section/view.nim b/src/app/modules/main/profile_section/view.nim index ef68394de8..8adf6f296e 100644 --- a/src/app/modules/main/profile_section/view.nim +++ b/src/app/modules/main/profile_section/view.nim @@ -80,3 +80,13 @@ QtObject: return self.delegate.getKeycardModule() QtProperty[QVariant] keycardModule: read = getKeycardModule + + proc getWalletAccountsModule(self: View): QVariant {.slot.} = + return self.delegate.getWalletAccountsModule() + QtProperty[QVariant] walletAccountsModule: + read = getWalletAccountsModule + + proc getWalletNetworksModule(self: View): QVariant {.slot.} = + return self.delegate.getWalletNetworksModule() + QtProperty[QVariant] walletNetworksModule: + read = getWalletNetworksModule diff --git a/src/app/modules/main/profile_section/wallet/accounts/controller.nim b/src/app/modules/main/profile_section/wallet/accounts/controller.nim new file mode 100644 index 0000000000..4d469ff2ab --- /dev/null +++ b/src/app/modules/main/profile_section/wallet/accounts/controller.nim @@ -0,0 +1,57 @@ +import sugar, sequtils, tables +import io_interface +import ../../../../../../app_service/service/wallet_account/service as wallet_account_service +import ../../../../../../app_service/service/network/service as network_service + +import ../../../../../global/global_singleton +import ../../../../shared_modules/keycard_popup/io_interface as keycard_shared_module + +import ../../../../../core/eventemitter + +const UNIQUE_PROFILE_SECTION_ACCOUNTS_MODULE_AUTH_IDENTIFIER* = "ProfileSection-AccountsModule-Authentication" + +type + Controller* = ref object of RootObj + delegate: io_interface.AccessInterface + events: EventEmitter + walletAccountService: wallet_account_service.Service + +proc newController*( + delegate: io_interface.AccessInterface, + events: EventEmitter, + walletAccountService: wallet_account_service.Service, +): Controller = + result = Controller() + result.delegate = delegate + result.events = events + result.walletAccountService = walletAccountService + +proc delete*(self: Controller) = + discard + +proc init*(self: Controller) = + self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED) do(e: Args): + let args = SharedKeycarModuleArgs(e) + if args.uniqueIdentifier != UNIQUE_PROFILE_SECTION_ACCOUNTS_MODULE_AUTH_IDENTIFIER: + return + self.delegate.onUserAuthenticated(args.pin, args.password, args.keyUid) + +proc getWalletAccounts*(self: Controller): seq[wallet_account_service.WalletAccountDto] = + return self.walletAccountService.getWalletAccounts() + +proc updateAccount*(self: Controller, address: string, accountName: string, color: string, emoji: string) = + discard self.walletAccountService.updateWalletAccount(address, accountName, color, emoji) + +proc deleteAccount*(self: Controller, address: string, password = "", doPasswordHashing = false) = + self.walletAccountService.deleteAccount(address, password, doPasswordHashing) + +proc authenticateKeyPair*(self: Controller, keyUid = "") = + let data = SharedKeycarModuleAuthenticationArgs(uniqueIdentifier: UNIQUE_PROFILE_SECTION_ACCOUNTS_MODULE_AUTH_IDENTIFIER, + keyUid: keyUid) + self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER, data) + +proc getMigratedKeyPairByKeyUid*(self: Controller, keyUid: string): seq[KeyPairDto] = + return self.walletAccountService.getMigratedKeyPairByKeyUid(keyUid) + +proc getWalletAccount*(self: Controller, address: string): WalletAccountDto = + return self.walletAccountService.getAccountByAddress(address) \ No newline at end of file diff --git a/src/app/modules/main/profile_section/wallet/accounts/io_interface.nim b/src/app/modules/main/profile_section/wallet/accounts/io_interface.nim new file mode 100644 index 0000000000..7443dd329f --- /dev/null +++ b/src/app/modules/main/profile_section/wallet/accounts/io_interface.nim @@ -0,0 +1,41 @@ +import NimQml + +type + AccessInterface* {.pure inheritable.} = ref object of RootObj + ## Abstract class for any input/interaction with this module. + +method delete*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method load*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method isLoaded*(self: AccessInterface): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method syncKeycard*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method deleteAccount*(self: AccessInterface, keyUid: string, address: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method refreshWalletAccounts*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method updateAccount*(self: AccessInterface, address: string, accountName: string, color: string, emoji: string) {.base.} = + raise newException(ValueError, "No implementation available") + +# View Delegate Interface +# Delegate for the view must be declared here due to use of QtObject and multi +# inheritance, which is not well supported in Nim. +method viewDidLoad*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method authenticateUser*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method onUserAuthenticated*(self: AccessInterface, pin: string, password: string, keyUid: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method getModuleAsVariant*(self: AccessInterface): QVariant {.base.} = + raise newException(ValueError, "No implementation available") \ No newline at end of file diff --git a/src/app/modules/main/profile_section/wallet/accounts/item.nim b/src/app/modules/main/profile_section/wallet/accounts/item.nim new file mode 100644 index 0000000000..a841df2575 --- /dev/null +++ b/src/app/modules/main/profile_section/wallet/accounts/item.nim @@ -0,0 +1,68 @@ +import strformat +import ./related_accounts_model as related_accounts_model + +type + Item* = object + name*: string + address: string + color*: string + emoji*: string + walletType: string + path: string + relatedAccounts: related_accounts_model.Model + keyUid: string + +proc initItem*( + name: string = "", + address: string = "", + path: string = "", + color: string = "", + walletType: string = "", + emoji: string = "", + relatedAccounts: related_accounts_model.Model = nil, + keyUid: string = "", +): Item = + result.name = name + result.address = address + result.path = path + result.color = color + result.walletType = walletType + result.emoji = emoji + result.relatedAccounts = relatedAccounts + result.keyUid = keyUid + +proc `$`*(self: Item): string = + result = fmt"""WalletAccountItem( + name: {self.name}, + address: {self.address}, + path: {self.path}, + color: {self.color}, + walletType: {self.walletType}, + emoji: {self.emoji}, + relatedAccounts: {self.relatedAccounts} + keyUid: {self.keyUid}, + ]""" + +proc getName*(self: Item): string = + return self.name + +proc getAddress*(self: Item): string = + return self.address + +proc getPath*(self: Item): string = + return self.path + +proc getEmoji*(self: Item): string = + return self.emoji + +proc getColor*(self: Item): string = + return self.color + +proc getWalletType*(self: Item): string = + return self.walletType + +proc getRelatedAccounts*(self: Item): related_accounts_model.Model = + return self.relatedAccounts + +proc getKeyUid*(self: Item): string = + return self.keyUid \ No newline at end of file diff --git a/src/app/modules/main/profile_section/wallet/accounts/model.nim b/src/app/modules/main/profile_section/wallet/accounts/model.nim new file mode 100644 index 0000000000..b53b34c5af --- /dev/null +++ b/src/app/modules/main/profile_section/wallet/accounts/model.nim @@ -0,0 +1,106 @@ +import NimQml, Tables, strutils, strformat +import ./item + +type + ModelRole {.pure.} = enum + Name = UserRole + 1, + Address, + Path, + Color, + WalletType, + Emoji, + RelatedAccounts, + KeyUid, + +QtObject: + type + Model* = ref object of QAbstractListModel + items: seq[Item] + + proc delete(self: Model) = + self.items = @[] + self.QAbstractListModel.delete + + proc setup(self: Model) = + self.QAbstractListModel.setup + + proc newModel*(): Model = + new(result, delete) + result.setup + + proc `$`*(self: Model): string = + for i in 0 ..< self.items.len: + result &= fmt"""[{i}]:({$self.items[i]})""" + + proc countChanged(self: Model) {.signal.} + + proc getCount*(self: Model): int {.slot.} = + self.items.len + + QtProperty[int] count: + read = getCount + notify = countChanged + + method rowCount(self: Model, index: QModelIndex = nil): int = + return self.items.len + + method roleNames(self: Model): Table[int, string] = + { + ModelRole.Name.int:"name", + ModelRole.Address.int:"address", + ModelRole.Path.int:"path", + ModelRole.Color.int:"color", + ModelRole.WalletType.int:"walletType", + ModelRole.Emoji.int: "emoji", + ModelRole.RelatedAccounts.int: "relatedAccounts", + ModelRole.KeyUid.int: "keyUid", + }.toTable + + + proc setItems*(self: Model, items: seq[Item]) = + self.beginResetModel() + self.items = items + self.endResetModel() + self.countChanged() + + proc onUpdatedAccount*(self: Model, account: Item) = + var i = 0 + for item in self.items.mitems: + if account.getAddress() == item.getAddress(): + item.name = account.name + item.color = account.color + item.emoji = account.emoji + let index = self.createIndex(i, 0, nil) + self.dataChanged(index, index, @[ModelRole.Name.int]) + self.dataChanged(index, index, @[ModelRole.Color.int]) + self.dataChanged(index, index, @[ModelRole.Emoji.int]) + break + i.inc + + method data(self: Model, index: QModelIndex, role: int): QVariant = + if (not index.isValid): + return + + if (index.row < 0 or index.row >= self.items.len): + return + + let item = self.items[index.row] + let enumRole = role.ModelRole + + case enumRole: + of ModelRole.Name: + result = newQVariant(item.getName()) + of ModelRole.Address: + result = newQVariant(item.getAddress()) + of ModelRole.Path: + result = newQVariant(item.getPath()) + of ModelRole.Color: + result = newQVariant(item.getColor()) + of ModelRole.WalletType: + result = newQVariant(item.getWalletType()) + of ModelRole.Emoji: + result = newQVariant(item.getEmoji()) + of ModelRole.RelatedAccounts: + result = newQVariant(item.getRelatedAccounts()) + of ModelRole.KeyUid: + result = newQVariant(item.getKeyUid()) \ No newline at end of file diff --git a/src/app/modules/main/profile_section/wallet/accounts/module.nim b/src/app/modules/main/profile_section/wallet/accounts/module.nim new file mode 100644 index 0000000000..cc6394e020 --- /dev/null +++ b/src/app/modules/main/profile_section/wallet/accounts/module.nim @@ -0,0 +1,136 @@ +import tables, NimQml, sequtils, sugar, chronicles + +import ./io_interface, ./view, ./item, ./controller, ./utils +import ../io_interface as delegate_interface +import ../../../../../global/global_singleton +import ../../../../../core/eventemitter +import ../../../../../../app_service/common/account_constants +import ../../../../../../app_service/service/keycard/service as keycard_service +import ../../../../../../app_service/service/wallet_account/service as wallet_account_service +import ../../../../../../app_service/service/network/service as network_service +import ../../../../shared_modules/keycard_popup/io_interface as keycard_shared_module + +export io_interface + +# TODO: remove it completely if after wallet settings part refactore this is not needed. +type + AuthenticationReason {.pure.} = enum + DeleteAccountAuthentication = 0 + +# TODO: remove it completely if after wallet settings part refactore this is not needed. +type WalletAccountDetails = object + address: string + path: string + addressAccountIsDerivedFrom: string + publicKey: string + keyUid: string + +type + Module* = ref object of io_interface.AccessInterface + delegate: delegate_interface.AccessInterface + events: EventEmitter + view: View + viewVariant: QVariant + controller: Controller + moduleLoaded: bool + walletAccountService: wallet_account_service.Service + processingWalletAccount: WalletAccountDetails + authentiactionReason: AuthenticationReason + +proc newModule*( + delegate: delegate_interface.AccessInterface, + events: EventEmitter, + walletAccountService: wallet_account_service.Service, + networkService: network_service.Service, +): Module = + result = Module() + result.delegate = delegate + result.events = events + result.walletAccountService = walletAccountService + result.view = newView(result) + result.viewVariant = newQVariant(result.view) + result.controller = controller.newController(result, events, walletAccountService) + result.moduleLoaded = false + result.authentiactionReason = AuthenticationReason.DeleteAccountAuthentication + +method delete*(self: Module) = + self.view.delete + self.viewVariant.delete + self.controller.delete + +method getModuleAsVariant*(self: Module): QVariant = + return self.viewVariant + +method refreshWalletAccounts*(self: Module) = + let walletAccounts = self.controller.getWalletAccounts() + + let items = walletAccounts.map(w => (block: + walletAccountToItem(w) + )) + + self.view.setItems(items) + +method load*(self: Module) = + self.events.on(SIGNAL_WALLET_ACCOUNT_SAVED) do(e:Args): + self.refreshWalletAccounts() + + self.events.on(SIGNAL_WALLET_ACCOUNT_DELETED) do(e:Args): + self.refreshWalletAccounts() + + self.events.on(SIGNAL_WALLET_ACCOUNT_UPDATED) do(e:Args): + let args = WalletAccountUpdated(e) + self.view.onUpdatedAccount(walletAccountToItem(args.account)) + + self.events.on(SIGNAL_NEW_KEYCARD_SET) do(e: Args): + let args = KeycardActivityArgs(e) + if not args.success: + return + self.refreshWalletAccounts() + + self.events.on(SIGNAL_KEYCARDS_SYNCHRONIZED) do(e: Args): + let args = KeycardActivityArgs(e) + if not args.success: + return + self.refreshWalletAccounts() + + self.controller.init() + self.view.load() + +method isLoaded*(self: Module): bool = + return self.moduleLoaded + +method viewDidLoad*(self: Module) = + self.refreshWalletAccounts() + self.moduleLoaded = true + self.delegate.accountsModuleDidLoad() + +method updateAccount*(self: Module, address: string, accountName: string, color: string, emoji: string) = + self.controller.updateAccount(address, accountName, color, emoji) + +method authenticateActivityForKeyUid(self: Module, keyUid: string, reason: AuthenticationReason) = + self.authentiactionReason = reason + let keyPair = self.controller.getMigratedKeyPairByKeyUid(keyUid) + let keyPairMigratedToKeycard = keyPair.len > 0 + if keyPairMigratedToKeycard: + self.controller.authenticateKeyPair(keyUid) + else: + self.processingWalletAccount.keyUid = singletonInstance.userProfile.getKeyUid() + self.controller.authenticateKeyPair() + +method deleteAccount*(self: Module, keyUid: string, address: string) = + let accountDto = self.controller.getWalletAccount(address) + if accountDto.walletType == WalletTypeWatch: + self.controller.deleteAccount(address) + return + self.processingWalletAccount = WalletAccountDetails(keyUid: keyUid, address: address) + self.authenticateActivityForKeyUid(keyUid, AuthenticationReason.DeleteAccountAuthentication) + +method onUserAuthenticated*(self: Module, pin: string, password: string, keyUid: string) = + if self.authentiactionReason == AuthenticationReason.DeleteAccountAuthentication: + if self.processingWalletAccount.keyUid != keyUid: + error "cannot resolve key uid of an account being deleted", keyUid=keyUid + return + if password.len == 0: + return + let doPasswordHashing = pin.len != PINLengthForStatusApp + self.controller.deleteAccount(self.processingWalletAccount.address, password, doPasswordHashing) \ No newline at end of file diff --git a/src/app/modules/main/profile_section/wallet/accounts/related_account_item.nim b/src/app/modules/main/profile_section/wallet/accounts/related_account_item.nim new file mode 100644 index 0000000000..0b52b6cc86 --- /dev/null +++ b/src/app/modules/main/profile_section/wallet/accounts/related_account_item.nim @@ -0,0 +1,32 @@ +import strformat + +type + Item* = object + name: string + color: string + emoji: string + +proc initItem*( + name: string = "", + color: string = "", + emoji: string = "", +): Item = + result.name = name + result.color = color + result.emoji = emoji + +proc `$`*(self: Item): string = + result = fmt"""WalletAccountItem( + name: {self.name}, + color: {self.color}, + emoji: {self.emoji}, + ]""" + +proc getName*(self: Item): string = + return self.name + +proc getEmoji*(self: Item): string = + return self.emoji + +proc getColor*(self: Item): string = + return self.color \ No newline at end of file diff --git a/src/app/modules/main/profile_section/wallet/accounts/related_accounts_model.nim b/src/app/modules/main/profile_section/wallet/accounts/related_accounts_model.nim new file mode 100644 index 0000000000..79e9c53cc7 --- /dev/null +++ b/src/app/modules/main/profile_section/wallet/accounts/related_accounts_model.nim @@ -0,0 +1,73 @@ +import NimQml, Tables, strutils, strformat + +import ./related_account_item + +type + ModelRole {.pure.} = enum + Name = UserRole + 1, + Color, + Emoji, + +QtObject: + type + Model* = ref object of QAbstractListModel + items: seq[Item] + + proc delete(self: Model) = + self.items = @[] + self.QAbstractListModel.delete + + proc setup(self: Model) = + self.QAbstractListModel.setup + + proc newModel*(): Model = + new(result, delete) + result.setup + + proc `$`*(self: Model): string = + for i in 0 ..< self.items.len: + result &= fmt"""[{i}]:({$self.items[i]})""" + + proc countChanged(self: Model) {.signal.} + + proc getCount*(self: Model): int {.slot.} = + self.items.len + + QtProperty[int] count: + read = getCount + notify = countChanged + + method rowCount(self: Model, index: QModelIndex = nil): int = + return self.items.len + + method roleNames(self: Model): Table[int, string] = + { + ModelRole.Name.int:"name", + ModelRole.Color.int:"color", + ModelRole.Emoji.int: "emoji", + }.toTable + + + proc setItems*(self: Model, items: seq[Item]) = + self.beginResetModel() + self.items = items + self.endResetModel() + self.countChanged() + + method data(self: Model, index: QModelIndex, role: int): QVariant = + if (not index.isValid): + return + + if (index.row < 0 or index.row >= self.items.len): + return + + let item = self.items[index.row] + let enumRole = role.ModelRole + + case enumRole: + of ModelRole.Name: + result = newQVariant(item.getName()) + of ModelRole.Color: + result = newQVariant(item.getColor()) + of ModelRole.Emoji: + result = newQVariant(item.getEmoji()) \ No newline at end of file diff --git a/src/app/modules/main/profile_section/wallet/accounts/utils.nim b/src/app/modules/main/profile_section/wallet/accounts/utils.nim new file mode 100644 index 0000000000..c2f5ae3514 --- /dev/null +++ b/src/app/modules/main/profile_section/wallet/accounts/utils.nim @@ -0,0 +1,34 @@ +import tables, sequtils, sugar +import ../../../../../../app_service/service/wallet_account/service as wallet_account_service +import ./item +import ./related_account_item as related_account_item +import ./related_accounts_model as related_accounts_model + +proc walletAccountToRelatedAccountItem*(w: WalletAccountDto) : related_account_item.Item = + return related_account_item.initItem( + w.name, + w.color, + w.emoji, + ) + +proc walletAccountToItem*( + w: WalletAccountDto, +) : item.Item = + let relatedAccounts = related_accounts_model.newModel() + if w.isNil: + return item.initItem() + + relatedAccounts.setItems( + w.relatedAccounts.map(x => walletAccountToRelatedAccountItem(x)) + ) + + return item.initItem( + w.name, + w.address, + w.path, + w.color, + w.walletType, + w.emoji, + relatedAccounts, + w.keyUid, + ) diff --git a/src/app/modules/main/profile_section/wallet/accounts/view.nim b/src/app/modules/main/profile_section/wallet/accounts/view.nim new file mode 100644 index 0000000000..729eb3400f --- /dev/null +++ b/src/app/modules/main/profile_section/wallet/accounts/view.nim @@ -0,0 +1,48 @@ +import NimQml, sequtils, strutils, sugar + +import ./model +import ./item +import ./io_interface + +QtObject: + type + View* = ref object of QObject + delegate: io_interface.AccessInterface + accounts: Model + accountsVariant: QVariant + + proc delete*(self: View) = + self.accounts.delete + self.accountsVariant.delete + self.QObject.delete + + proc newView*(delegate: io_interface.AccessInterface): View = + new(result, delete) + result.QObject.setup + result.delegate = delegate + result.accounts = newModel() + result.accountsVariant = newQVariant(result.accounts) + + proc load*(self: View) = + self.delegate.viewDidLoad() + + proc accountsChanged*(self: View) {.signal.} + + proc getAccounts(self: View): QVariant {.slot.} = + return self.accountsVariant + + QtProperty[QVariant] accounts: + read = getAccounts + notify = accountsChanged + + proc setItems*(self: View, items: seq[Item]) = + self.accounts.setItems(items) + + proc updateAccount(self: View, address: string, accountName: string, color: string, emoji: string) {.slot.} = + self.delegate.updateAccount(address, accountName, color, emoji) + + proc onUpdatedAccount*(self: View, account: Item) = + self.accounts.onUpdatedAccount(account) + + proc deleteAccount*(self: View, keyUid: string, address: string) {.slot.} = + self.delegate.deleteAccount(keyUid, address) \ No newline at end of file diff --git a/src/app/modules/main/profile_section/wallet/io_interface.nim b/src/app/modules/main/profile_section/wallet/io_interface.nim new file mode 100644 index 0000000000..9b15122f71 --- /dev/null +++ b/src/app/modules/main/profile_section/wallet/io_interface.nim @@ -0,0 +1,33 @@ +import NimQml + +type + AccessInterface* {.pure inheritable.} = ref object of RootObj + ## Abstract class for any input/interaction with this module. + +method delete*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method load*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method isLoaded*(self: AccessInterface): bool {.base.} = + raise newException(ValueError, "No implementation available") + +# View Delegate Interface +# Delegate for the view must be declared here due to use of QtObject and multi +# inheritance, which is not well supported in Nim. +method viewDidLoad*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +# Methods called by submodules of this module +method accountsModuleDidLoad*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method networksModuleDidLoad*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method getAccountsModuleAsVariant*(self: AccessInterface): QVariant {.base.} = + raise newException(ValueError, "No implementation available") + +method getNetworksModuleAsVariant*(self: AccessInterface): QVariant {.base.} = + raise newException(ValueError, "No implementation available") \ No newline at end of file diff --git a/src/app/modules/main/profile_section/wallet/module.nim b/src/app/modules/main/profile_section/wallet/module.nim new file mode 100644 index 0000000000..58b4927a99 --- /dev/null +++ b/src/app/modules/main/profile_section/wallet/module.nim @@ -0,0 +1,79 @@ +import NimQml, chronicles + +import ./io_interface as io_interface +import ../io_interface as delegate_interface + +import ./accounts/module as accounts_module +import ./networks/module as networks_module + +import ../../../../global/global_singleton +import ../../../../core/eventemitter +import ../../../../../app_service/service/wallet_account/service as wallet_account_service +import ../../../../../app_service/service/network/service as network_service +import ../../../../../app_service/service/settings/service as settings_service + +logScope: + topics = "profile-section-wallet-module" + +import io_interface +export io_interface + +type + Module* = ref object of io_interface.AccessInterface + delegate: delegate_interface.AccessInterface + events: EventEmitter + moduleLoaded: bool + + accountsModule: accounts_module.AccessInterface + networksModule: networks_module.AccessInterface + +proc newModule*( + delegate: delegate_interface.AccessInterface, + events: EventEmitter, + walletAccountService: wallet_account_service.Service, + settingsService: settings_service.Service, + networkService: network_service.Service, +): Module = + result = Module() + result.delegate = delegate + result.events = events + result.moduleLoaded = false + + result.accountsModule = accounts_module.newModule(result, events, walletAccountService, networkService) + result.networksModule = networks_module.newModule(result, events, networkService, walletAccountService, settingsService) + +method delete*(self: Module) = + self.accountsModule.delete + self.networksModule.delete + +method load*(self: Module) = + self.accountsModule.load() + self.networksModule.load() + +method isLoaded*(self: Module): bool = + return self.moduleLoaded + +method getAccountsModuleAsVariant*(self: Module): QVariant = + return self.accountsModule.getModuleAsVariant() + +method getNetworksModuleAsVariant*(self: Module): QVariant = + return self.networksModule.getModuleAsVariant() + +proc checkIfModuleDidLoad(self: Module) = + if(not self.accountsModule.isLoaded()): + return + + if(not self.networksModule.isLoaded()): + return + + self.moduleLoaded = true + self.delegate.walletModuleDidLoad() + +method viewDidLoad*(self: Module) = + self.checkIfModuleDidLoad() + +method accountsModuleDidLoad*(self: Module) = + self.checkIfModuleDidLoad() + +method networksModuleDidLoad*(self: Module) = + self.checkIfModuleDidLoad() \ No newline at end of file diff --git a/src/app/modules/main/profile_section/wallet/networks/controller.nim b/src/app/modules/main/profile_section/wallet/networks/controller.nim new file mode 100644 index 0000000000..3eade1e3bc --- /dev/null +++ b/src/app/modules/main/profile_section/wallet/networks/controller.nim @@ -0,0 +1,44 @@ +import ../../../../../core/eventemitter +import ../../../../../../app_service/service/network/service as network_service +import ../../../../../../app_service/service/wallet_account/service as wallet_account_service +import ../../../../../../app_service/service/settings/service as settings_service +import ./io_interface + + +type + Controller* = ref object of RootObj + delegate: io_interface.AccessInterface + events: EventEmitter + networkService: network_service.Service + walletAccountService: wallet_account_service.Service + settingsService: settings_service.Service + +proc newController*( + delegate: io_interface.AccessInterface, + events: EventEmitter, + networkService: network_service.Service, + walletAccountService: wallet_account_service.Service, + settingsService: settings_service.Service, +): Controller = + result = Controller() + result.delegate = delegate + result.events = events + result.networkService = networkService + result.walletAccountService = walletAccountService + result.settingsService = settingsService + +proc delete*(self: Controller) = + discard + +proc init*(self: Controller) = + self.events.on(SIGNAL_WALLET_ACCOUNT_NETWORK_ENABLED_UPDATED) do(e: Args): + self.delegate.refreshNetworks() + +proc getNetworks*(self: Controller): seq[NetworkDto] = + return self.networkService.getNetworks() + +proc areTestNetworksEnabled*(self: Controller): bool = + return self.settingsService.areTestNetworksEnabled() + +proc toggleTestNetworksEnabled*(self: Controller) = + self.walletAccountService.toggleTestNetworksEnabled() \ No newline at end of file diff --git a/src/app/modules/main/profile_section/wallet/networks/io_interface.nim b/src/app/modules/main/profile_section/wallet/networks/io_interface.nim new file mode 100644 index 0000000000..d5e0209d7c --- /dev/null +++ b/src/app/modules/main/profile_section/wallet/networks/io_interface.nim @@ -0,0 +1,29 @@ +import NimQml + +type + AccessInterface* {.pure inheritable.} = ref object of RootObj + ## Abstract class for any input/interaction with this module. + +method delete*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method load*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method isLoaded*(self: AccessInterface): bool {.base.} = + raise newException(ValueError, "No implementation available") + +# View Delegate Interface +# Delegate for the view must be declared here due to use of QtObject and multi +# inheritance, which is not well supported in Nim. +method viewDidLoad*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method refreshNetworks*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method toggleTestNetworksEnabled*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method getModuleAsVariant*(self: AccessInterface): QVariant {.base.} = + raise newException(ValueError, "No implementation available") \ No newline at end of file diff --git a/src/app/modules/main/profile_section/wallet/networks/item.nim b/src/app/modules/main/profile_section/wallet/networks/item.nim new file mode 100644 index 0000000000..f6aaa505be --- /dev/null +++ b/src/app/modules/main/profile_section/wallet/networks/item.nim @@ -0,0 +1,39 @@ +import strformat + +type + Item* = object + chainId: int + layer: int + chainName: string + iconUrl: string + +proc initItem*( + chainId: int, + layer: int, + chainName: string, + iconUrl: string, +): Item = + result.chainId = chainId + result.layer = layer + result.chainName = chainName + result.iconUrl = iconUrl + +proc `$`*(self: Item): string = + result = fmt"""NetworkItem( + chainId: {self.chainId}, + chainName: {self.chainName}, + layer: {self.layer}, + iconUrl:{self.iconUrl}, + ]""" + +proc getChainId*(self: Item): int = + return self.chainId + +proc getLayer*(self: Item): int = + return self.layer + +proc getChainName*(self: Item): string = + return self.chainName + +proc getIconURL*(self: Item): string = + return self.iconUrl \ No newline at end of file diff --git a/src/app/modules/main/profile_section/wallet/networks/model.nim b/src/app/modules/main/profile_section/wallet/networks/model.nim new file mode 100644 index 0000000000..47e353bea5 --- /dev/null +++ b/src/app/modules/main/profile_section/wallet/networks/model.nim @@ -0,0 +1,88 @@ +import NimQml, Tables, strutils, strformat + +import ./item + +const EXPLORER_TX_PREFIX* = "/tx/" + +type + ModelRole* {.pure.} = enum + ChainId = UserRole + 1, + Layer + ChainName + IconUrl + +QtObject: + type + Model* = ref object of QAbstractListModel + items: seq[Item] + + proc delete(self: Model) = + self.items = @[] + self.QAbstractListModel.delete + + proc setup(self: Model) = + self.QAbstractListModel.setup + + proc newModel*(): Model = + new(result, delete) + result.setup + + proc `$`*(self: Model): string = + for i in 0 ..< self.items.len: + result &= fmt"""[{i}]:({$self.items[i]})""" + + proc countChanged(self: Model) {.signal.} + + proc getCount(self: Model): int {.slot.} = + self.items.len + + QtProperty[int] count: + read = getCount + notify = countChanged + + method rowCount*(self: Model, index: QModelIndex = nil): int = + return self.items.len + + method roleNames(self: Model): Table[int, string] = + { + ModelRole.ChainId.int:"chainId", + ModelRole.Layer.int:"layer", + ModelRole.ChainName.int:"chainName", + ModelRole.IconUrl.int:"iconUrl", + }.toTable + + method data(self: Model, index: QModelIndex, role: int): QVariant = + if (not index.isValid): + return + + if (index.row < 0 or index.row >= self.items.len): + return + + let item = self.items[index.row] + let enumRole = role.ModelRole + + case enumRole: + of ModelRole.ChainId: + result = newQVariant(item.getChainId()) + of ModelRole.Layer: + result = newQVariant(item.getLayer()) + of ModelRole.ChainName: + result = newQVariant(item.getChainName()) + of ModelRole.IconUrl: + result = newQVariant(item.getIconURL()) + + proc rowData*(self: Model, index: int, column: string): string {.slot.} = + if (index >= self.items.len): + return + let item = self.items[index] + case column: + of "chainId": result = $item.getChainId() + of "layer": result = $item.getLayer() + of "chainName": result = $item.getChainName() + of "iconUrl": result = $item.getIconURL() + + proc setItems*(self: Model, items: seq[Item]) = + self.beginResetModel() + self.items = items + self.endResetModel() + self.countChanged() \ No newline at end of file diff --git a/src/app/modules/main/profile_section/wallet/networks/module.nim b/src/app/modules/main/profile_section/wallet/networks/module.nim new file mode 100644 index 0000000000..1c01c7ac75 --- /dev/null +++ b/src/app/modules/main/profile_section/wallet/networks/module.nim @@ -0,0 +1,64 @@ +import Tables, NimQml +import ../io_interface as delegate_interface +import io_interface, view, controller +import ../../../../../core/eventemitter +import ../../../../../../app_service/service/network/service as network_service +import ../../../../../../app_service/service/wallet_account/service as wallet_account_service +import ../../../../../../app_service/service/settings/service as settings_service + +export io_interface + +type + Module* = ref object of io_interface.AccessInterface + delegate: delegate_interface.AccessInterface + view: View + viewVariant: QVariant + controller: Controller + moduleLoaded: bool + +proc newModule*( + delegate: delegate_interface.AccessInterface, + events: EventEmitter, + networkService: networkService.Service, + walletAccountService: wallet_account_service.Service, + settingsService: settings_service.Service, +): Module = + result = Module() + result.delegate = delegate + result.view = view.newView(result) + result.viewVariant = newQVariant(result.view) + result.controller = controller.newController(result, events, networkService, walletAccountService, settingsService) + result.moduleLoaded = false + +method delete*(self: Module) = + self.view.delete + self.viewVariant.delete + self.controller.delete + +method getModuleAsVariant*(self: Module): QVariant = + return self.viewVariant + +method refreshNetworks*(self: Module) = + self.view.load(self.controller.getNetworks()) + +method load*(self: Module) = + self.controller.init() + self.view.setAreTestNetworksEnabled(self.controller.areTestNetworksEnabled()) + self.refreshNetworks() + +method isLoaded*(self: Module): bool = + return self.moduleLoaded + +proc checkIfModuleDidLoad(self: Module) = + self.moduleLoaded = true + self.delegate.networksModuleDidLoad() + +method viewDidLoad*(self: Module) = + self.checkIfModuleDidLoad() + +method areTestNetworksEnabled*(self: Module): bool = + return self.controller.areTestNetworksEnabled() + +method toggleTestNetworksEnabled*(self: Module) = + self.controller.toggleTestNetworksEnabled() + self.refreshNetworks() \ No newline at end of file diff --git a/src/app/modules/main/profile_section/wallet/networks/view.nim b/src/app/modules/main/profile_section/wallet/networks/view.nim new file mode 100644 index 0000000000..a1a80eb323 --- /dev/null +++ b/src/app/modules/main/profile_section/wallet/networks/view.nim @@ -0,0 +1,65 @@ +import Tables, NimQml, sequtils, sugar + +import ../../../../../../app_service/service/network/dto +import ./io_interface +import ./model +import ./item + +QtObject: + type + View* = ref object of QObject + delegate: io_interface.AccessInterface + networks: Model + areTestNetworksEnabled: bool + + proc setup(self: View) = + self.QObject.setup + + proc delete*(self: View) = + self.QObject.delete + + proc newView*(delegate: io_interface.AccessInterface): View = + new(result, delete) + result.delegate = delegate + result.networks = newModel() + result.setup() + + proc areTestNetworksEnabledChanged*(self: View) {.signal.} + + proc getAreTestNetworksEnabled(self: View): bool {.slot.} = + return self.areTestNetworksEnabled + + QtProperty[bool] areTestNetworksEnabled: + read = getAreTestNetworksEnabled + notify = areTestNetworksEnabledChanged + + proc setAreTestNetworksEnabled*(self: View, areTestNetworksEnabled: bool) = + self.areTestNetworksEnabled = areTestNetworksEnabled + self.areTestNetworksEnabledChanged() + + proc toggleTestNetworksEnabled*(self: View) {.slot.} = + self.delegate.toggleTestNetworksEnabled() + self.areTestNetworksEnabled = not self.areTestNetworksEnabled + self.areTestNetworksEnabledChanged() + + proc networksChanged*(self: View) {.signal.} + + proc getNetworks(self: View): QVariant {.slot.} = + return newQVariant(self.networks) + + QtProperty[QVariant] networks: + read = getNetworks + notify = networksChanged + + proc load*(self: View, networks: seq[NetworkDto]) = + var items: seq[Item] = @[] + for n in networks: + items.add(initItem( + n.chainId, + n.layer, + n.chainName, + n.iconUrl, + )) + + self.networks.setItems(items) + self.delegate.viewDidLoad() \ No newline at end of file diff --git a/ui/app/AppLayouts/Profile/popups/RenameAccontModal.qml b/ui/app/AppLayouts/Profile/popups/RenameAccontModal.qml index b3e4b74c68..c6e7a5f4a7 100644 --- a/ui/app/AppLayouts/Profile/popups/RenameAccontModal.qml +++ b/ui/app/AppLayouts/Profile/popups/RenameAccontModal.qml @@ -20,10 +20,10 @@ StatusModal { id: popup property WalletStore walletStore - property var currentAccount: walletStore.currentAccount + property var account property var emojiPopup - header.title: qsTr("Rename %1").arg(currentAccount.name) + header.title: qsTr("Rename %1").arg(popup.account.name) property int marginBetweenInputs: 30 @@ -53,10 +53,10 @@ StatusModal { input.edit.objectName: "renameAccountNameInput" input.isIconSelectable: true placeholderText: qsTr("Enter an account name...") - input.text: currentAccount.name - input.asset.emoji: currentAccount.emoji - input.asset.color: currentAccount.color - input.asset.name: !currentAccount.emoji ? "filled-account": "" + input.text: popup.account.name + input.asset.emoji: popup.account.emoji + input.asset.color: popup.account.color + input.asset.name: !popup.account.emoji ? "filled-account": "" validationMode: StatusInput.ValidationMode.Always @@ -85,16 +85,16 @@ StatusModal { anchors.horizontalCenter: parent.horizontalCenter model: Constants.preDefinedWalletAccountColors titleText: qsTr("color").toUpperCase() - selectedColor: currentAccount.color + selectedColor: popup.account.color selectedColorIndex: { for (let i = 0; i < model.length; i++) { - if(model[i] === currentAccount.color) + if(model[i] === popup.account.color) return i } return -1 } onSelectedColorChanged: { - if(selectedColor !== currentAccount.color) { + if(selectedColor !== popup.account.color) { accountNameInput.input.asset.color = selectedColor } } @@ -113,8 +113,8 @@ StatusModal { text: qsTr("Change Name") enabled: accountNameInput.text !== "" && accountNameInput.valid - && (accountNameInput.text !== currentAccount.name - || (accountColorInput.selectedColorIndex >= 0 && accountColorInput.selectedColor !== currentAccount.color)) + && (accountNameInput.text !== popup.account.name + || (accountColorInput.selectedColorIndex >= 0 && accountColorInput.selectedColor !== popup.account.color)) MessageDialog { id: changeError @@ -128,7 +128,7 @@ StatusModal { return } - const error = walletStore.updateCurrentAccount(currentAccount.address, accountNameInput.text, accountColorInput.selectedColor, accountNameInput.input.asset.emoji); + const error = walletStore.updateAccount(popup.account.address, accountNameInput.text, accountColorInput.selectedColor, accountNameInput.input.asset.emoji); if (error) { Global.playErrorSound(); diff --git a/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml b/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml index 5d1628c0a8..c6bb152d20 100644 --- a/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml +++ b/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml @@ -56,6 +56,8 @@ QtObject { } property WalletStore walletStore: WalletStore { + accountsModule: profileSectionModuleInst.walletAccountsModule + networksModule: profileSectionModuleInst.walletNetworksModule } property KeycardStore keycardStore: KeycardStore { diff --git a/ui/app/AppLayouts/Profile/stores/WalletStore.qml b/ui/app/AppLayouts/Profile/stores/WalletStore.qml index 410e4c6169..30306a5be0 100644 --- a/ui/app/AppLayouts/Profile/stores/WalletStore.qml +++ b/ui/app/AppLayouts/Profile/stores/WalletStore.qml @@ -1,41 +1,32 @@ import QtQuick 2.13 -import "../../Wallet/stores" import utils 1.0 QtObject { id: root + property var accountsModule + property var networksModule + property var accountSensitiveSettings: Global.appIsReady? localAccountSensitiveSettings : null property var areTestNetworksEnabled: networksModule.areTestNetworksEnabled - property var layer1Networks: networksModule.layer1 - property var layer2Networks: networksModule.layer2 - property var testNetworks: networksModule.test + property var networks: networksModule.networks function toggleTestNetworksEnabled(){ networksModule.toggleTestNetworksEnabled() } - property var accounts: Global.appIsReady? walletSectionAccounts.model : null - property var importedAccounts: Global.appIsReady? walletSectionAccounts.imported : null - property var generatedAccounts: Global.appIsReady? walletSectionAccounts.generated : null - property var watchOnlyAccounts: Global.appIsReady? walletSectionAccounts.watchOnly : null - + // TODO(alaibe): there should be no access to wallet section, create collectible in profile property var flatCollectibles: Global.appIsReady ? walletSectionCollectibles.model : null + property var accounts: Global.appIsReady? accountsModule.accounts : null - property var currentAccount: Global.appIsReady? walletSectionCurrent : null - - function switchAccountByAddress(address) { - walletSection.switchAccountByAddress(address) - } - function deleteAccount(keyUid, address) { - return walletSectionAccounts.deleteAccount(keyUid, address) + return accountsModule.deleteAccount(keyUid, address) } - function updateCurrentAccount(address, accountName, color, emoji) { - return walletSectionCurrent.update(address, accountName, color, emoji) + function updateAccount(address, accountName, color, emoji) { + return accountsModule.updateAccount(address, accountName, color, emoji) } property var dappList: Global.appIsReady? dappPermissionsModule.dapps : null diff --git a/ui/app/AppLayouts/Profile/views/WalletView.qml b/ui/app/AppLayouts/Profile/views/WalletView.qml index cd6848e7f1..3c409f0efe 100644 --- a/ui/app/AppLayouts/Profile/views/WalletView.qml +++ b/ui/app/AppLayouts/Profile/views/WalletView.qml @@ -71,7 +71,7 @@ SettingsContentBase { } onGoToAccountView: { - root.walletStore.switchAccountByAddress(address) + accountView.account = account stackContainer.currentIndex = accountViewIndex } @@ -89,6 +89,7 @@ SettingsContentBase { } AccountView { + id: accountView walletStore: root.walletStore emojiPopup: root.emojiPopup diff --git a/ui/app/AppLayouts/Profile/views/wallet/AccountView.qml b/ui/app/AppLayouts/Profile/views/wallet/AccountView.qml index 4ce9870ddd..6a4d17c275 100644 --- a/ui/app/AppLayouts/Profile/views/wallet/AccountView.qml +++ b/ui/app/AppLayouts/Profile/views/wallet/AccountView.qml @@ -21,6 +21,7 @@ Item { property WalletStore walletStore property var emojiPopup + property var account Column { id: column @@ -42,11 +43,11 @@ Item { asset: StatusAssetSettings { width: isLetterIdenticon ? 40 : 20 height: isLetterIdenticon ? 40 : 20 - color: walletStore.currentAccount.color - emoji: walletStore.currentAccount.emoji - name: !walletStore.currentAccount.emoji ? "filled-account": "" + color: root.account ? root.account.color : "#ffffff" + emoji: root.account ? root.account.emoji : "" + name: root.account && !root.account.emoji ? "filled-account": "" letterSize: 14 - isLetterIdenticon: !!walletStore.currentAccount.emoji + isLetterIdenticon: !!root.account && !!root.account.emoji bgWidth: 40 bgHeight: 40 bgColor: Theme.palette.primaryColor3 @@ -59,7 +60,7 @@ Item { StatusBaseText { objectName: "walletAccountViewAccountName" id: accountName - text: walletStore.currentAccount.name + text:root.account ? root.account.name : "" font.weight: Font.Bold font.pixelSize: 28 color: Theme.palette.directColor1 @@ -76,7 +77,7 @@ Item { } } StatusAddressPanel { - value: walletStore.currentAccount.address + value: root.account ? root.account.address : "" font.weight: Font.Normal @@ -98,7 +99,10 @@ Item { maxWidth: parent.width primaryText: qsTr("Type") secondaryText: { - const walletType = walletStore.currentAccount.walletType + if (!root.account) { + return "" + } + const walletType = root.account.walletType if (walletType === "watch") { return qsTr("Watch-Only Account") } else if (walletType === "generated" || walletType === "") { @@ -118,15 +122,15 @@ Item { InformationTile { maxWidth: parent.width primaryText: qsTr("Derivation Path") - secondaryText: walletStore.currentAccount.path - visible: walletStore.currentAccount.path + secondaryText: root.account ? root.account.path : "" + visible: !!root.account && root.account.path } InformationTile { maxWidth: parent.width - visible: walletStore.currentAccount.relatedAccounts.count > 0 + visible:root.account ? root.account.relatedAccounts.count > 0 : false primaryText: qsTr("Related Accounts") - tagsModel: walletStore.currentAccount.relatedAccounts + tagsModel: root.account ? root.account.relatedAccounts : [] tagsDelegate: StatusListItemTag { bgColor: model.color bgRadius: 6 @@ -144,20 +148,20 @@ Item { StatusButton { objectName: "deleteAccountButton" - visible: walletStore.currentAccount.walletType !== "" + visible: !!root.account && root.account.walletType !== "" text: qsTr("Remove from your profile") type: StatusBaseButton.Type.Danger ConfirmationDialog { id: confirmationPopup confirmButtonObjectName: "confirmDeleteAccountButton" - header.title: qsTr("Confirm %1 Removal").arg(walletStore.currentAccount.name) + header.title: qsTr("Confirm %1 Removal").arg(root.account ? root.account.name : "") confirmationText: qsTr("You will not be able to restore viewing access to this account in the future unless you enter this account’s address again.") confirmButtonLabel: qsTr("Remove Account") onConfirmButtonClicked: { confirmationPopup.close(); root.goBack(); - root.walletStore.deleteAccount(walletStore.currentAccount.keyUid, walletStore.currentAccount.address); + root.walletStore.deleteAccount(root.account.keyUid, root.account.address); } } @@ -171,6 +175,7 @@ Item { Component { id: renameAccountModalComponent RenameAccontModal { + account: root.account anchors.centerIn: parent onClosed: destroy() walletStore: root.walletStore diff --git a/ui/app/AppLayouts/Profile/views/wallet/MainView.qml b/ui/app/AppLayouts/Profile/views/wallet/MainView.qml index eb1758f327..80f1c6133f 100644 --- a/ui/app/AppLayouts/Profile/views/wallet/MainView.qml +++ b/ui/app/AppLayouts/Profile/views/wallet/MainView.qml @@ -1,4 +1,5 @@ import QtQuick 2.13 +import SortFilterProxyModel 0.2 import utils 1.0 import shared.status 1.0 @@ -17,7 +18,7 @@ Column { property WalletStore walletStore signal goToNetworksView() - signal goToAccountView(address: string) + signal goToAccountView(var account) signal goToDappPermissionsView() Component.onCompleted: { @@ -84,11 +85,28 @@ Column { width: parent.width height: childrenRect.height objectName: "generatedAccounts" - model: walletStore.generatedAccounts + model: SortFilterProxyModel { + sourceModel: walletStore.accounts + filters: ExpressionFilter { + expression: { + return model.walletType === "generated" || model.walletType === "" + } + } + } delegate: WalletAccountDelegate { account: model onGoToAccountView: { - root.goToAccountView(model.address) + root.goToAccountView(model) + } + } + } + + SortFilterProxyModel { + id: importedAccounts + sourceModel: walletStore.accounts + filters: ExpressionFilter { + expression: { + return model.walletType !== "generated" && model.walletType !== "watch" && model.walletType !== "" } } } @@ -98,33 +116,42 @@ Column { leftPadding: Style.current.padding topPadding: Style.current.halfPadding bottomPadding: Style.current.halfPadding/2 - visible: walletStore.importedAccounts.count > 0 + visible: importedAccounts.count > 0 } Repeater { - model: walletStore.importedAccounts + model: importedAccounts delegate: WalletAccountDelegate { account: model onGoToAccountView: { - root.goToAccountView(model.address) + root.goToAccountView(model) } } } + SortFilterProxyModel { + id: watchOnlyAccounts + sourceModel: walletStore.accounts + filters: ValueFilter { + roleName: "walletType" + value: "watch" + } + } + StatusSectionHeadline { text: qsTr("Watch-Only") leftPadding: Style.current.padding topPadding: Style.current.halfPadding bottomPadding: Style.current.halfPadding/2 - visible: walletStore.watchOnlyAccounts.count > 0 + visible: watchOnlyAccounts.count > 0 } Repeater { - model: walletStore.watchOnlyAccounts + model: watchOnlyAccounts delegate: WalletAccountDelegate { account: model onGoToAccountView: { - root.goToAccountView(model.address) + root.goToAccountView(model) } } } diff --git a/ui/app/AppLayouts/Profile/views/wallet/NetworksView.qml b/ui/app/AppLayouts/Profile/views/wallet/NetworksView.qml index c3633bbb53..53e2da6a49 100644 --- a/ui/app/AppLayouts/Profile/views/wallet/NetworksView.qml +++ b/ui/app/AppLayouts/Profile/views/wallet/NetworksView.qml @@ -1,4 +1,5 @@ import QtQuick 2.13 +import SortFilterProxyModel 0.2 import shared.status 1.0 import StatusQ.Controls 0.1 @@ -23,7 +24,13 @@ Item { Repeater { id: layer1List - model: walletStore.layer1Networks + model: SortFilterProxyModel { + sourceModel: walletStore.networks + filters: ValueFilter { + roleName: "layer" + value: 1 + } + } delegate: WalletNetworkDelegate { network: model } @@ -39,28 +46,16 @@ Item { Repeater { id: layer2List - model: walletStore.layer2Networks + model: SortFilterProxyModel { + sourceModel: walletStore.networks + filters: ValueFilter { + roleName: "layer" + value: 2 + } + } delegate: WalletNetworkDelegate { network: model } } - - Item { - height: Style.current.bigPadding - width: parent.width - } - - StatusButton { - // Disable for now - visible: false - anchors.right: parent.right - anchors.rightMargin: Style.current.bigPadding - id: addCustomNetworkButton - type: StatusFlatRoundButton.Type.Primary - text: qsTr("Add Custom Network") - onClicked: { - root.goBack() - } - } } }