feat(@desktop/wallet): Account view - New Account view UI

fixes #11537
This commit is contained in:
Khushboo Mehta 2023-07-18 11:10:48 +02:00 committed by Khushboo-dev-cpp
parent ef2d1b75ad
commit 9800dad3ba
28 changed files with 465 additions and 195 deletions

View File

@ -183,7 +183,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
result.collectibleService = collectible_service.newService(statusFoundation.events, statusFoundation.threadpool, result.networkService)
result.walletAccountService = wallet_account_service.newService(
statusFoundation.events, statusFoundation.threadpool, result.settingsService, result.accountsService,
result.tokenService, result.networkService
result.tokenService, result.networkService, result.currencyService
)
result.messageService = message_service.newService(
statusFoundation.events,

View File

@ -213,7 +213,7 @@ proc addAccountsToKeycardItem(self: Module, item: KeycardItem, accounts: seq[Wal
if acc.emoji.len == 0:
icon = "wallet"
item.addAccount(newKeyPairAccountItem(acc.name, acc.path, acc.address, acc.publicKey, acc.emoji, acc.colorId,
icon = icon, balance = 0.0))
icon = icon))
proc buildMainViewKeycardItem(self: Module, keypair: KeypairDto): KeycardItem =
if keypair.isNil or keypair.keycards.len == 0:
@ -271,7 +271,7 @@ proc buildDetailsViewKeycardItem(self: Module, keycard: KeycardDto): KeycardItem
for ua in unknownAccountsAddresses:
i.inc
let name = atc.KEYCARD_ACCOUNT_NAME_OF_UNKNOWN_WALLET_ACCOUNT & $i
item.addAccount(newKeyPairAccountItem(name, path = "", ua, pubKey = "", emoji = "", colorId = "undefined", icon = "wallet", balance = 0.0))
item.addAccount(newKeyPairAccountItem(name, path = "", ua, pubKey = "", emoji = "", colorId = "undefined", icon = "wallet"))
return item
proc buildKeycardList(self: Module) =

View File

@ -1,5 +1,6 @@
import io_interface
import app_service/service/wallet_account/service as wallet_account_service
import app_service/service/currency/dto
import app/modules/shared_modules/keycard_popup/io_interface as keycard_shared_module
type
@ -53,3 +54,12 @@ proc toggleIncludeWatchOnlyAccount*(self: Controller) =
proc isIncludeWatchOnlyAccount*(self: Controller): bool =
return self.walletAccountService.isIncludeWatchOnlyAccount()
proc getEnabledChainIds*(self: Controller): seq[int] =
return self.walletAccountService.getEnabledChainIds()
proc getCurrentCurrency*(self: Controller): string =
return self.walletAccountService.getCurrency()
proc getCurrencyFormat*(self: Controller, symbol: string): CurrencyFormatDto =
return self.walletAccountService.getCurrencyFormat(symbol)

View File

@ -1,10 +1,10 @@
import NimQml, sequtils, sugar, chronicles
import NimQml, sequtils, sugar, chronicles, tables
import ./io_interface, ./view, ./item, ./controller
import ../io_interface as delegate_interface
import app/modules/shared/wallet_utils
import app/modules/shared/keypairs
import app/modules/shared_models/keypair_model
import app/modules/shared_models/[keypair_model, currency_amount]
import app/global/global_singleton
import app/core/eventemitter
import app_service/service/keycard/service as keycard_service
@ -59,11 +59,16 @@ method convertWalletAccountDtoToKeyPairAccountItem(self: Module, account: Wallet
emoji = account.emoji,
colorId = account.colorId,
icon = "",
balance = 0,
balance = newCurrencyAmount(),
balanceFetched = false,
operability = account.operable)
operability = account.operable,
isDefaultAccount = account.isWallet)
method createKeypairItems*(self: Module, walletAccounts: seq[WalletAccountDto], accountsTokens: OrderedTable[string, seq[WalletTokenDto]]): seq[KeyPairItem] =
let enabledChainIds = self.controller.getEnabledChainIds()
let currency = self.controller.getCurrentCurrency()
let currencyFormat = self.controller.getCurrencyFormat(currency)
method createKeypairItems*(self: Module, walletAccounts: seq[WalletAccountDto]): seq[KeyPairItem] =
var keyPairItems = keypairs.buildKeyPairsList(self.controller.getKeypairs(), excludeAlreadyMigratedPairs = false,
excludePrivateKeyKeypairs = false)
@ -72,9 +77,15 @@ method createKeypairItems*(self: Module, walletAccounts: seq[WalletAccountDto]):
item.setPairType(KeyPairType.WatchOnly.int)
item.setAccounts(walletAccounts.filter(a => a.walletType == WalletTypeWatch).map(x => self.convertWalletAccountDtoToKeyPairAccountItem(x)))
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)
return keyPairItems
method refreshWalletAccounts*(self: Module) =
method refreshWalletAccounts*(self: Module, accountsTokens: OrderedTable[string, seq[WalletTokenDto]] = initOrderedTable[string, seq[WalletTokenDto]]()) =
let walletAccounts = self.controller.getWalletAccounts()
let items = walletAccounts.map(w => (block:
@ -82,7 +93,7 @@ method refreshWalletAccounts*(self: Module) =
walletAccountToWalletSettingsAccountsItem(w, keycardAccount)
))
self.view.setKeyPairModelItems(self.createKeypairItems(walletAccounts))
self.view.setKeyPairModelItems(self.createKeypairItems(walletAccounts, accountsTokens))
self.view.setItems(items)
method load*(self: Module) =
@ -95,6 +106,10 @@ method load*(self: Module) =
self.events.on(SIGNAL_WALLET_ACCOUNT_DELETED) do(e:Args):
self.refreshWalletAccounts()
self.events.on(SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT) do(e:Args):
let arg = TokensPerAccountArgs(e)
self.refreshWalletAccounts(arg.accountsTokens)
self.events.on(SIGNAL_WALLET_ACCOUNT_UPDATED) do(e:Args):
let args = AccountArgs(e)
let keycardAccount = self.controller.isKeycardAccount(args.account)

View File

@ -1,6 +1,6 @@
import strutils, sequtils, sugar, chronicles
import ../shared_models/[keypair_item]
import ../shared_models/[keypair_item, currency_amount]
import ../../global/global_singleton
import ../../../app_service/service/wallet_account/[keypair_dto, keycard_dto]
@ -40,7 +40,7 @@ proc buildKeyPairsList*(keypairs: seq[KeypairDto], excludeAlreadyMigratedPairs:
if acc.emoji.len == 0:
icon = "wallet"
item.addAccount(newKeyPairAccountItem(acc.name, acc.path, acc.address, acc.publicKey, acc.emoji, acc.colorId,
icon, balance = 0.0, balanceFetched = true, operability = acc.operable))
icon, newCurrencyAmount(), balanceFetched = true, operability = acc.operable, acc.isWallet))
items.insert(item, 0) # Status Account must be at first place
continue
if kp.keypairType == KeypairTypeSeed:
@ -59,7 +59,7 @@ proc buildKeyPairsList*(keypairs: seq[KeypairDto], excludeAlreadyMigratedPairs:
if acc.emoji.len == 0:
icon = "wallet"
item.addAccount(newKeyPairAccountItem(acc.name, acc.path, acc.address, acc.publicKey, acc.emoji, acc.colorId,
icon, balance = 0.0, balanceFetched = true, operability = acc.operable))
icon, newCurrencyAmount(), balanceFetched = true, operability = acc.operable, acc.isWallet))
items.add(item)
continue
if kp.keypairType == KeypairTypeKey:
@ -80,7 +80,7 @@ proc buildKeyPairsList*(keypairs: seq[KeypairDto], excludeAlreadyMigratedPairs:
if acc.emoji.len == 0:
icon = "wallet"
item.addAccount(newKeyPairAccountItem(acc.name, acc.path, acc.address, acc.publicKey, acc.emoji, acc.colorId,
icon, balance = 0.0, balanceFetched = true, operability = acc.operable))
icon, newCurrencyAmount(), balanceFetched = true, operability = acc.operable, acc.isWallet))
items.add(item)
continue
if items.len == 0:

View File

@ -1,5 +1,6 @@
import NimQml, strformat
import app_service/service/wallet_account/dto as wa_dto
import ./currency_amount
export wa_dto
@ -13,14 +14,15 @@ QtObject:
emoji: string
colorId: string
icon: string
balance: float
balance: CurrencyAmount
balanceFetched: bool
isDefaultAccount: bool
proc delete*(self: KeyPairAccountItem) =
self.QObject.delete
proc newKeyPairAccountItem*(name = "", path = "", address = "", pubKey = "", emoji = "", colorId = "", icon = "",
balance = 0.0, balanceFetched = true, operability = wa_dto.AccountFullyOperable): KeyPairAccountItem =
balance = newCurrencyAmount(), balanceFetched = true, operability = wa_dto.AccountFullyOperable, isDefaultAccount = false): KeyPairAccountItem =
new(result, delete)
result.QObject.setup
result.name = name
@ -33,6 +35,7 @@ QtObject:
result.balance = balance
result.balanceFetched = balanceFetched
result.operability = operability
result.isDefaultAccount = isDefaultAccount
proc `$`*(self: KeyPairAccountItem): string =
result = fmt"""KeyPairAccountItem[
@ -45,6 +48,7 @@ QtObject:
icon: {self.icon},
balance: {self.balance},
balanceFetched: {self.balanceFetched}
isDefaultAccount = {self.isDefaultAccount}
]"""
proc nameChanged*(self: KeyPairAccountItem) {.signal.}
@ -136,19 +140,27 @@ QtObject:
notify = iconChanged
proc balanceChanged*(self: KeyPairAccountItem) {.signal.}
proc getBalance*(self: KeyPairAccountItem): float {.slot.} =
return self.balance
proc setBalance*(self: KeyPairAccountItem, value: float) {.slot.} =
proc getBalance*(self: KeyPairAccountItem): QVariant {.slot.} =
return newQVariant(self.balance)
proc setBalance*(self: KeyPairAccountItem, value: CurrencyAmount) =
self.balance = value
self.balanceFetched = true
self.balanceChanged()
QtProperty[float] balance:
QtProperty[QVariant] balance:
read = getBalance
write = setBalance
notify = balanceChanged
proc balanceFetchedChanged*(self: KeyPairAccountItem) {.signal.}
proc getBalanceFetched*(self: KeyPairAccountItem): bool {.slot.} =
return self.balanceFetched
QtProperty[bool] balanceFetched:
read = getBalanceFetched
notify = balanceChanged
notify = balanceFetchedChanged
proc isDefaultAccountChanged*(self: KeyPairAccountItem) {.signal.}
proc getIsDefaultAccount*(self: KeyPairAccountItem): bool {.slot.} =
return self.isDefaultAccount
QtProperty[bool] isDefaultAccount:
read = getIsDefaultAccount
notify = isDefaultAccountChanged

View File

@ -1,5 +1,6 @@
import NimQml, Tables, strformat, strutils
import keypair_account_item
import ./currency_amount
import ../../../app_service/common/utils
@ -129,7 +130,7 @@ QtObject:
if cmpIgnoreCase(self.items[i].getAddress(), address) == 0:
self.items[i].setOperability(operability)
proc setBalanceForAddress*(self: KeyPairAccountModel, address: string, balance: float) =
proc setBalanceForAddress*(self: KeyPairAccountModel, address: string, balance: CurrencyAmount) =
for i in 0 ..< self.items.len:
if cmpIgnoreCase(self.items[i].getAddress(), address) == 0:
self.items[i].setBalance(balance)

View File

@ -1,5 +1,6 @@
import NimQml, strformat, sequtils, sugar
import keypair_account_model
import ./currency_amount
export keypair_account_model
@ -249,7 +250,7 @@ QtObject:
return self.accounts.containsPathOutOfTheDefaultStatusDerivationTree()
proc updateDetailsForAccountWithAddressIfTheyAreSet*(self: KeyPairItem, address, name, colorId, emoji: string) =
self.accounts.updateDetailsForAddressIfTheyAreSet(address, name, colorId, emoji)
proc setBalanceForAddress*(self: KeyPairItem, address: string, balance: float) =
proc setBalanceForAddress*(self: KeyPairItem, address: string, balance: CurrencyAmount) =
self.accounts.setBalanceForAddress(address, balance)
proc updateOperabilityForAccountWithAddress*(self: KeyPairItem, address: string, operability: string) =
self.accounts.updateOperabilityForAddress(address, operability)

View File

@ -17,6 +17,7 @@ import ../../../../app_service/service/privacy/service as privacy_service
import ../../../../app_service/service/accounts/service as accounts_service
import ../../../../app_service/service/wallet_account/service as wallet_account_service
import ../../../../app_service/service/keychain/service as keychain_service
import ../../../../app_service/service/currency/dto
import ../../shared_models/[keypair_item]
@ -798,3 +799,6 @@ proc tryToStoreDataToKeychain*(self: Controller, password: string) =
return
let loggedInAccount = self.getLoggedInAccount()
self.keychainService.storeData(loggedInAccount.keyUid, password)
proc getCurrencyFormat*(self: Controller, symbol: string): CurrencyFormatDto =
return self.walletAccountService.getCurrencyFormat(symbol)

View File

@ -3,8 +3,8 @@ import NimQml, tables, random, strutils, sequtils, sugar, chronicles
import io_interface
import view, controller
import internal/[state, state_factory]
import ../../shared/keypairs
import ../../shared_models/[keypair_model, keypair_item]
import ../../shared/[keypairs, wallet_utils]
import ../../shared_models/[keypair_model, keypair_item, currency_amount]
import ../../../global/app_translatable_constants as atc
import ../../../global/global_singleton
import ../../../core/eventemitter
@ -540,8 +540,9 @@ method onTokensRebuilt*[T](self: Module[T], accountsTokens: OrderedTable[string,
return
let chainIds = self.controller.getChainIdsOfAllKnownNetworks()
let currency = self.controller.getCurrency()
let currencyFormat = self.controller.getCurrencyFormat(currency)
for address, tokens in accountsTokens.pairs:
let balance = tokens.map(t => t.getCurrencyBalance(chainIds, currency)).foldl(a + b, 0.0)
let balance = currencyAmountToItem(tokens.map(t => t.getCurrencyBalance(chainIds, currency)).foldl(a + b, 0.0), currencyFormat)
self.getKeyPairForProcessing().setBalanceForAddress(address, balance)
proc buildKeyPairItemBasedOnCardMetadata[T](self: Module[T], cardMetadata: CardMetadata):
@ -567,7 +568,7 @@ proc buildKeyPairItemBasedOnCardMetadata[T](self: Module[T], cardMetadata: CardM
if acc.emoji.len == 0:
icon = "wallet"
result.item.addAccount(newKeyPairAccountItem(acc.name, acc.path, acc.address, acc.publicKey, acc.emoji, acc.colorId, icon,
balance = 0.0, balanceFetched = true, operability = acc.operable))
balance = newCurrencyAmount(), balanceFetched = true, operability = acc.operable))
# handle unknown accounts
var unknownAccountNumber = 0
for cardAcc in cardMetadata.walletAccounts:
@ -580,9 +581,10 @@ proc buildKeyPairItemBasedOnCardMetadata[T](self: Module[T], cardMetadata: CardM
let (balance, balanceFetched) = self.controller.getOrFetchBalanceForAddressInPreferredCurrency(cardAcc.address)
result.knownKeyPair = false
unknownAccountNumber.inc
let currencyFormat = self.controller.getCurrencyFormat(self.controller.getCurrency())
let name = atc.KEYCARD_ACCOUNT_NAME_OF_UNKNOWN_WALLET_ACCOUNT & $unknownAccountNumber
result.item.addAccount(newKeyPairAccountItem(name, cardAcc.path, cardAcc.address, pubKey = cardAcc.publicKey,
emoji = "", colorId = "undefined", icon = "wallet", balance, balanceFetched))
emoji = "", colorId = "undefined", icon = "wallet", currencyAmountToItem(balance, currencyFormat), balanceFetched))
method updateKeyPairForProcessing*[T](self: Module[T], cardMetadata: CardMetadata) =
let(item, knownKeyPair) = self.buildKeyPairItemBasedOnCardMetadata(cardMetadata)

View File

@ -6,6 +6,7 @@ import ../settings/service as settings_service
import ../accounts/service as accounts_service
import ../token/service as token_service
import ../network/service as network_service
import ../currency/service as currency_service
import ../../common/[utils]
import ../../../app/global/global_singleton
@ -134,6 +135,7 @@ QtObject:
accountsService: accounts_service.Service
tokenService: token_service.Service
networkService: network_service.Service
currencyService: currency_service.Service
walletAccounts: OrderedTable[string, WalletAccountDto]
# Forward declaration
@ -157,6 +159,7 @@ QtObject:
accountsService: accounts_service.Service,
tokenService: token_service.Service,
networkService: network_service.Service,
currencyService: currency_service.Service,
): Service =
new(result, delete)
result.QObject.setup
@ -167,6 +170,7 @@ QtObject:
result.accountsService = accountsService
result.tokenService = tokenService
result.networkService = networkService
result.currencyService = currencyService
result.walletAccounts = initOrderedTable[string, WalletAccountDto]()
proc getAccounts*(self: Service): seq[WalletAccountDto] =
@ -1050,3 +1054,9 @@ QtObject:
url: url
)
self.threadpool.start(arg)
proc getEnabledChainIds*(self: Service): seq[int] =
return self.networkService.getNetworks().filter(n => n.enabled).map(n => n.chainId)
proc getCurrencyFormat*(self: Service, symbol: string): CurrencyFormatDto =
return self.currencyService.getCurrencyFormat(symbol)

View File

@ -89,6 +89,10 @@ ListModel {
title: "EditOwnerTokenView"
section: "Views"
}
ListElement {
title: "AccountView"
section: "Views"
}
ListElement {
title: "StatusCommunityCard"
section: "Panels"

View File

@ -0,0 +1,85 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import AppLayouts.Profile.views.wallet 1.0
import StatusQ.Core.Theme 0.1
import utils 1.0
import Storybook 1.0
import Models 1.0
SplitView {
id: root
Logs { id: logs }
orientation: Qt.Vertical
QtObject {
id: d
readonly property string emptyString: ""
property var dummyOverview: updateDummyView(StatusColors.colors['black'])
function updateDummyView(color) {
const clr = Utils.getIdForColor(color)
dummyOverview = ({
name: "helloworld",
address: "0xcdc2ea3b6ba8fed3a3402f8db8b2fab53e7b7421",
ens: emptyString,
colorId: clr,
emoji: "⚽",
balanceLoading: false,
hasBalanceCache: true,
balance: ({amount: 1.25,
symbol: "USD",
displayDecimals: 4,
stripTrailingZeroes: false}),
isAllAccounts: false,
includeWatchOnly: false,
path: "m/44/60/0/0/34"
})
}
readonly property QtObject connectionStore: QtObject {
property bool accountBalanceNotAvailable: false
}
readonly property QtObject walletStore: QtObject {
property var allNetworks: enabledNetworks
property var layer1Networks: NetworksModel.layer1Networks
property var layer2Networks: NetworksModel.layer2Networks
property var testNetworks: NetworksModel.testNetworks
property var enabledNetworks: NetworksModel.enabledNetworks
function toggleNetwork(chainId) {
}
function getAllNetworksSupportedPrefix(hovered) {
return hovered ? "<font color=\"" + "#627EEA" + "\">" + "eth:" + "</font>" +
"<font color=\"" + "#E90101" + "\">" + "opt:" + "</font>" +
"<font color=\"" + "#27A0EF" + "\">" + "arb:" + "</font>" : "eth:opt:arb:"
}
}
property var keyPairModel: WalletKeyPairModel {}
}
Item {
SplitView.fillWidth: true
SplitView.fillHeight: true
AccountView {
anchors.top: parent.top
anchors.topMargin: 100
anchors.horizontalCenter: parent.horizontalCenter
width: 560
account: d.dummyOverview
walletStore: d.walletStore
keyPair: d.keyPairModel.data[0].keyPair
}
}
}

View File

@ -45,6 +45,7 @@ SplitView {
model: WalletKeyPairModel {}
delegate: WalletKeyPairDelegate {
width: parent.width
keyPair: model.keyPair
chainShortNames: d.walletStore.getAllNetworksSupportedString()
userProfilePublicKey: d.walletStore.userProfilePublicKey
onGoToAccountView: console.warn("onGoToAccountView ::")

View File

@ -269,9 +269,8 @@ class SettingsScreen:
def verify_editedAccount(self, new_name: str, new_color: str):
accountName = get_obj(WalletSettingsScreen.ACCOUNT_VIEW_ACCOUNT_NAME.value)
iconSettings = get_obj(WalletSettingsScreen.ACCOUNT_VIEW_ICON_SETTINGS.value)
verify_values_equal(str(accountName.text), str(new_name), "Edited account name not updated")
verify_values_equal(str(iconSettings.asset.color.name), str(new_color.lower()),
verify_values_equal(str(accountName.color.name), str(new_color.lower()),
"Edited account color not updated")
def open_communities_section(self):

View File

@ -97,12 +97,12 @@ settings_Wallet_AccountView_DeleteAccount_Confirm = {"container": statusDesktop_
mainWindow_ScrollView_2 = {"container": statusDesktop_mainWindow, "occurrence": 2, "type": "StatusScrollView", "unnamed": 1, "visible": True}
settings_Wallet_MainView_Networks = {"container": statusDesktop_mainWindow, "objectName": "networksItem", "type": "StatusListItem"}
settings_Wallet_NetworksView_TestNet_Toggle = {"container": statusDesktop_mainWindow, "objectName": "testnetModeSwitch", "type": "StatusSwitch"}
settings_Wallet_AccountView_EditAccountButton = {"container": statusDesktop_mainWindow, "type": "StatusFlatRoundButton", "objectName": "walletAccountViewEditAccountButton"}
settings_Wallet_AccountView_EditAccountButton = {"container": statusDesktop_mainWindow, "type": "StatusButton", "objectName": "walletAccountViewEditAccountButton"}
settings_Wallet_AccountView_EditAccountNameInput = {"container": statusDesktop_mainWindow_overlay, "type": "TextEdit", "objectName": "renameAccountNameInput", "visible": True}
settings_Wallet_AccountView_EditAccountSaveButton = {"container": statusDesktop_mainWindow_overlay, "type": "StatusButton", "objectName": "renameAccountModalSaveBtn"}
settings_Wallet_AccountView_EditAccountColorRepeater = {"container": statusDesktop_mainWindow, "type": "Repeater", "objectName": "statusColorRepeater", "visible": True}
settings_Wallet_AccountView_AccountName = {"container": statusDesktop_mainWindow, "type": "StatusBaseText", "objectName": "walletAccountViewAccountName"}
settings_Wallet_AccountView_IconSettings = {"container": statusDesktop_mainWindow, "type": "StatusSmartIdenticon", "objectName": "walletAccountViewAccountImage" , "visible": True}
settings_Wallet_AccountView_IconSettings = {"container": statusDesktop_mainWindow, "type": "StatusEmoji", "objectName": "walletAccountViewAccountImage" , "visible": True}
settings_Wallet_MainView_BackupSeedPhrase = {"container": mainWindow_ScrollView, "objectName": SettingsSubsection.BACKUP_SEED.value, "type": "StatusNavigationListItem", "visible": True}
generatedAccounts_ListView = {"container": statusDesktop_mainWindow, "objectName": "generatedAccounts", "type": "ListView"}

View File

@ -7,6 +7,8 @@ import shared 1.0
import shared.panels 1.0
import shared.stores 1.0 as SharedStores
import AppLayouts.Wallet.controls 1.0
import "stores"
import "popups"
import "views"
@ -56,6 +58,12 @@ StatusSectionLayout {
readonly property int contentWidth: 560
}
headerBackground: AccountHeaderGradient {
width: parent.width
overview: root.store.walletStore.selectedAccount
visible: profileContainer.currentIndex === Constants.settingsSubsection.wallet && !!root.store.walletStore.selectedAccount
}
leftPanel: LeftTabView {
store: root.store
anchors.fill: parent

View File

@ -0,0 +1,45 @@
import QtQuick 2.13
import StatusQ.Components 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1
import utils 1.0
StatusListItem {
property var keyPair
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 {
width: keyPair && keyPair.icon ? Style.current.bigPadding : 40
height: keyPair && keyPair.icon ? Style.current.bigPadding : 40
name: keyPair ? keyPair.image ? keyPair.image : keyPair.icon: ""
isImage: !!keyPair && !!keyPair.image
color: keyPair && keyPair.pairType === Constants.keycard.keyPairType.profile ? Utils.colorForPubkey(root.userProfilePublicKey) : Theme.palette.primaryColor1
letterSize: Math.max(4, asset.width / 2.4)
charactersLen: 2
isLetterIdenticon: !!keyPair && !keyPair.icon && !asset.name.toString()
}
color: Theme.palette.transparent
ringSettings {
ringSpecModel: keyPair && keyPair.pairType === Constants.keycard.keyPairType.profile ? Utils.getColorHashAsJson(root.userProfilePublicKey) : []
ringPxSize: Math.max(asset.width / 24.0)
}
tagsModel: keyPair ? keyPair.accounts: []
tagsDelegate: StatusListItemTag {
bgColor: !!model.account.colorId ? Utils.getColorForId(model.account.colorId): ""
bgRadius: 6
height: Style.current.bigPadding
closeButtonVisible: false
asset.width: Style.current.bigPadding
asset.height: Style.current.bigPadding
asset.emoji: model.account.emoji
asset.emojiSize: Emoji.size.verySmall
asset.color: Theme.palette.transparent
asset.isLetterIdenticon: true
title: model.account.name
titleText.font.pixelSize: 12
titleText.color: Theme.palette.indirectColor1
}
}

View File

@ -0,0 +1,17 @@
import QtQuick 2.13
import StatusQ.Components 0.1
import StatusQ.Core.Theme 0.1
StatusListItem {
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
}

View File

@ -12,6 +12,7 @@ import utils 1.0
Rectangle {
id: root
property var keyPair
property string chainShortNames
property string userProfilePublicKey
property bool includeWatchOnlyAccount
@ -22,11 +23,11 @@ Rectangle {
QtObject {
id: d
readonly property var relatedAccounts: model.keyPair.accounts
readonly property bool isWatchOnly: model.keyPair.pairType === Constants.keycard.keyPairType.watchOnly
readonly property bool isPrivateKeyImport: model.keyPair.pairType === Constants.keycard.keyPairType.privateKeyImport
readonly property bool isProfileKeypair: model.keyPair.pairType === Constants.keycard.keyPairType.profile
readonly property string locationInfo: model.keyPair.migratedToKeycard ? qsTr("On Keycard"): qsTr("On device")
readonly property var relatedAccounts: keyPair.accounts
readonly property bool isWatchOnly: keyPair.pairType === Constants.keycard.keyPairType.watchOnly
readonly property bool isPrivateKeyImport: keyPair.pairType === Constants.keycard.keyPairType.privateKeyImport
readonly property bool isProfileKeypair: keyPair.pairType === Constants.keycard.keyPairType.profile
readonly property string locationInfo: keyPair.migratedToKeycard ? qsTr("On Keycard"): qsTr("On device")
}
implicitHeight: layout.height
@ -39,25 +40,25 @@ Rectangle {
width: parent.width
StatusListItem {
Layout.fillWidth: true
title: d.isWatchOnly ? qsTr("Watched addresses") : model.keyPair.name
title: d.isWatchOnly ? qsTr("Watched addresses") : keyPair.name
statusListItemSubTitle.textFormat: Qt.RichText
titleTextIcon: model.keyPair.migratedToKeycard ? "keycard": ""
titleTextIcon: keyPair.migratedToKeycard ? "keycard": ""
subTitle: d.isWatchOnly ? "" : d.isProfileKeypair ?
Utils.getElidedCompressedPk(model.keyPair.pubKey) + Constants.settingsSection.dotSepString + d.locationInfo : d.locationInfo
Utils.getElidedCompressedPk(keyPair.pubKey) + Constants.settingsSection.dotSepString + d.locationInfo : d.locationInfo
color: Theme.palette.transparent
ringSettings {
ringSpecModel: d.isProfileKeypair ? Utils.getColorHashAsJson(root.userProfilePublicKey) : []
ringPxSize: Math.max(asset.width / 24.0)
}
asset {
width: model.keyPair.icon ? 24 : 40
height: model.keyPair.icon ? 24 : 40
name: model.keyPair.image ? model.keyPair.image : model.keyPair.icon
isImage: !!model.keyPair.image
width: keyPair.icon ? Style.current.bigPadding : 40
height: keyPair.icon ? Style.current.bigPadding : 40
name: keyPair.image ? keyPair.image : keyPair.icon
isImage: !!keyPair.image
color: d.isProfileKeypair ? Utils.colorForPubkey(root.userProfilePublicKey) : Theme.palette.primaryColor1
letterSize: Math.max(4, asset.width / 2.4)
charactersLen: 2
isLetterIdenticon: !model.keyPair.icon && !asset.name.toString()
isLetterIdenticon: !keyPair.icon && !asset.name.toString()
}
components: [
StatusFlatRoundButton {

View File

@ -12,6 +12,7 @@ QtObject {
readonly property bool areTestNetworksEnabled: networksModule.areTestNetworksEnabled
readonly property var combinedNetworks: networksModule.combinedNetworks
property var selectedAccount
function toggleTestNetworksEnabled(){
networksModule.toggleTestNetworksEnabled()

View File

@ -95,7 +95,7 @@ Item {
objectName: "settingsContentBaseScrollView"
anchors.top: titleRow.bottom
anchors.bottom: parent.bottom
anchors.topMargin: Style.current.padding
anchors.topMargin: titleLayout.visible ? Style.current.padding: 0
padding: 0
width: root.width
contentWidth: root.contentWidth

View File

@ -5,6 +5,7 @@ import QtGraphicalEffects 1.13
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import StatusQ.Core.Utils 0.1
import utils 1.0
import shared 1.0
@ -100,7 +101,8 @@ SettingsContentBase {
}
onGoToAccountView: {
accountView.account = account
root.walletStore.selectedAccount = account
accountView.keyPair = keypair
stackContainer.currentIndex = accountViewIndex
}
@ -148,13 +150,12 @@ SettingsContentBase {
AccountView {
id: accountView
Layout.fillHeight: false
account: root.walletStore.selectedAccount
walletStore: root.walletStore
emojiPopup: root.emojiPopup
onGoBack: {
stackContainer.currentIndex = mainViewIndex
}
userProfilePublicKey: walletStore.userProfilePublicKey
onGoBack: stackContainer.currentIndex = mainViewIndex
onVisibleChanged: if(!visible) root.walletStore.selectedAccount = null
}
DappPermissionsView {

View File

@ -1,158 +1,210 @@
import QtQuick 2.13
import QtQuick.Layouts 1.13
import StatusQ.Controls 0.1
import StatusQ.Core 0.1
import StatusQ.Components 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1
import StatusQ.Core.Utils 0.1 as StatusQUtils
import AppLayouts.Wallet 1.0
import utils 1.0
import shared.status 1.0
import shared.popups 1.0
import shared.controls 1.0
import shared.panels 1.0
import utils 1.0
import "../../stores"
import "../../popups"
import "../../controls"
Item {
ColumnLayout {
id: root
implicitHeight: childrenRect.height
signal goBack
property WalletStore walletStore
property var emojiPopup
property var account
property var keyPair
property var walletStore
property var emojiPopup
property string userProfilePublicKey
Column {
id: column
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: Style.current.padding
anchors.rightMargin: Style.current.padding
QtObject {
id: d
property bool watchOnlyAccount: keyPair && keyPair.pairType === Constants.keycard.keyPairType.watchOnly
property bool privateKeyAccount: keyPair && keyPair.pairType === Constants.keycard.keyPairType.privateKeyImport
}
spacing: Style.current.bigPadding
spacing: 0
Row {
id: header
RowLayout {
Layout.preferredWidth: parent.width
RowLayout {
Layout.fillWidth: true
spacing: Style.current.smallPadding
StatusSmartIdenticon {
StatusBaseText {
id: accountName
objectName: "walletAccountViewAccountName"
Layout.alignment: Qt.AlignLeft
text: root.account ? root.account.name : ""
font.weight: Font.Bold
font.pixelSize: 28
color: root.account ? Utils.getColorForId(root.account.colorId) : Theme.palette.directColor1
}
StatusEmoji {
id: accountImage
objectName: "walletAccountViewAccountImage"
anchors.verticalCenter: parent.verticalCenter
asset: StatusAssetSettings {
width: isLetterIdenticon ? 40 : 20
height: isLetterIdenticon ? 40 : 20
color: root.account ? Utils.getColorForId(root.account.colorId) : "#ffffff"
emoji: root.account ? root.account.emoji : ""
name: root.account && !root.account.emoji ? "filled-account": ""
letterSize: 14
isLetterIdenticon: !!root.account && !!root.account.emoji
bgWidth: 40
bgHeight: 40
bgColor: Theme.palette.primaryColor3
}
}
Column {
spacing: Style.current.halfPadding
Row {
spacing: Style.current.halfPadding
StatusBaseText {
objectName: "walletAccountViewAccountName"
id: accountName
text:root.account ? root.account.name : ""
font.weight: Font.Bold
font.pixelSize: 28
color: Theme.palette.directColor1
}
StatusFlatRoundButton {
objectName: "walletAccountViewEditAccountButton"
width: 28
height: 28
anchors.verticalCenter: accountName.verticalCenter
type: StatusFlatRoundButton.Type.Tertiary
color: "transparent"
icon.name: "pencil"
onClicked: Global.openPopup(renameAccountModalComponent)
}
}
StatusAddressPanel {
value: root.account ? root.account.address : ""
font.weight: Font.Normal
showFrame: false
onDoCopy: (address) => globalUtils.copyToClipboard(address)
}
Layout.preferredWidth: 28
Layout.preferredHeight: 28
emojiId: StatusQUtils.Emoji.iconId(root.account && root.account.emoji ? root.account.emoji : "", StatusQUtils.Emoji.size.big) || ""
}
}
Flow {
width: parent.width
spacing: Style.current.halfPadding
InformationTile {
id: typeRectangle
maxWidth: parent.width
primaryText: qsTr("Type")
secondaryText: {
if (!root.account) {
return ""
}
const walletType = root.account.walletType
if (walletType === "watch") {
return qsTr("Watch-Only Account")
} else if (walletType === "generated" || walletType === "") {
return qsTr("Generated by your Status seed phrase profile")
} else {
return qsTr("Imported Account")
}
}
}
InformationTile {
maxWidth: parent.width
primaryText: qsTr("Storage")
secondaryText: qsTr("On Device")
}
InformationTile {
maxWidth: parent.width
primaryText: qsTr("Derivation Path")
secondaryText: root.account ? root.account.path : ""
visible: !!root.account && root.account.path
}
InformationTile {
maxWidth: parent.width
visible:root.account ? root.account.relatedAccounts.count > 0 : false
primaryText: qsTr("Related Accounts")
tagsModel: root.account ? root.account.relatedAccounts : []
tagsDelegate: StatusListItemTag {
bgColor: Utils.getColorForId(model.colorId)
bgRadius: 6
height: 22
closeButtonVisible: false
asset.emoji: model.emoji
asset.emojiSize: Emoji.size.verySmall
asset.isLetterIdenticon: true
title: model.name
titleText.font.pixelSize: 12
titleText.color: Theme.palette.indirectColor1
}
}
}
StatusButton {
Layout.alignment: Qt.AlignRight
objectName: "walletAccountViewEditAccountButton"
text: qsTr("Edit account")
icon.name: "edit_pencil"
onClicked: Global.openPopup(renameAccountModalComponent)
}
}
StatusBaseText {
Layout.topMargin: Style.current.bigPadding
text: qsTr("Account details")
font.pixelSize: 15
color: Theme.palette.baseColor1
}
Rectangle {
Layout.topMargin: Style.current.halfPadding
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height
radius: Style.current.radius
border.width: 1
border.color: Theme.palette.directColor8
color: Theme.palette.transparent
ColumnLayout {
width: parent.width
spacing: 0
WalletAccountDetailsListItem {
Layout.fillWidth: true
title: qsTr("Balance")
subTitle: root.account && root.account.balance ? LocaleUtils.currencyAmountToLocaleString(root.account.balance): ""
}
Separator {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Theme.palette.baseColor2
}
WalletAccountDetailsListItem {
Layout.fillWidth: true
title: qsTr("Address")
subTitle: {
let address = root.account && root.account.address ? root.account.address: ""
d.watchOnlyAccount ? address : WalletUtils.colorizedChainPrefix(walletStore.getAllNetworksSupportedPrefix()) + address
}
}
Separator {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Theme.palette.baseColor2
}
StatusBaseText {
text: qsTr("Keypair")
Layout.leftMargin: 16
Layout.topMargin: 12
font.pixelSize: 13
color: Theme.palette.baseColor1
visible: !d.watchOnlyAccount
}
WalletAccountDetailsKeypairItem {
Layout.fillWidth: true
keyPair: root.keyPair
visible: !d.watchOnlyAccount
}
Separator {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Theme.palette.baseColor2
visible: !d.watchOnlyAccount
}
WalletAccountDetailsListItem {
Layout.fillWidth: true
title: qsTr("Origin")
subTitle: {
if(keyPair) {
switch(keyPair.pairType) {
case Constants.keycard.keyPairType.profile:
return qsTr("Derived from your default Status keypair")
case Constants.keycard.keyPairType.seedImport:
return qsTr("Imported from seed phrase")
case Constants.keycard.keyPairType.privateKeyImport:
return qsTr("Imported from private key")
case Constants.keycard.keyPairType.watchOnly:
return qsTr("Watched address")
default:
return ""
}
}
return ""
}
}
Separator {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Theme.palette.baseColor2
}
WalletAccountDetailsListItem {
id: derivationPath
Layout.fillWidth: true
title: qsTr("Derivation Path")
subTitle: root.account ? Utils.getPathForDisplay(root.account.path) : ""
visible: !!subTitle && !d.privateKeyAccount
}
Separator {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Theme.palette.baseColor2
visible: derivationPath.visible
}
WalletAccountDetailsListItem {
Layout.fillWidth: true
title: qsTr("Stored")
subTitle: keyPair && keyPair.migratedToKeycard ? qsTr("On Keycard"): qsTr("On device")
}
}
}
Separator {
Layout.topMargin: 40
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Theme.palette.baseColor2
}
RowLayout {
Layout.topMargin: 20
Layout.fillWidth: true
spacing: 8
StatusButton {
Layout.fillWidth: true
visible: !d.watchOnlyAccount && !d.privateKeyAccount
text: keyPair && keyPair.migratedToKeycard? qsTr("Stop using Keycard") : qsTr("Migrate to Keycard")
icon.name: "keycard"
onClicked: {
if (keyPair && keyPair.migratedToKeycard)
console.warn("TODO: stop using Keycard")
else
console.warn("TODO: move keys to a Keycard")
}
}
StatusButton {
Layout.fillWidth: true
objectName: "deleteAccountButton"
visible: !!root.account && root.account.walletType !== ""
text: qsTr("Remove from your profile")
visible: !!root.account && !root.account.isDefaultAccount
text: qsTr("Remove account")
icon.name: "delete"
type: StatusBaseButton.Type.Danger
onClicked: confirmationPopup.open()
ConfirmationDialog {
id: confirmationPopup
@ -161,19 +213,13 @@ Item {
confirmationText: qsTr("You will not be able to restore viewing access to this account in the future unless you enter this accounts address again.")
confirmButtonLabel: qsTr("Remove Account")
onConfirmButtonClicked: {
confirmationPopup.close();
root.goBack();
root.walletStore.deleteAccount(root.account.address);
confirmationPopup.close()
root.goBack()
}
}
onClicked : {
confirmationPopup.open()
}
}
}
Component {
id: renameAccountModalComponent
RenameAccontModal {

View File

@ -21,7 +21,7 @@ Column {
signal goToNetworksView()
signal goToAccountOrderView()
signal goToAccountView(var account)
signal goToAccountView(var account, var keypair)
signal goToDappPermissionsView()
spacing: 8
@ -100,10 +100,11 @@ Column {
model: walletStore.originModel
delegate: WalletKeyPairDelegate {
width: parent.width
keyPair: model.keyPair
chainShortNames: walletStore.getAllNetworksSupportedPrefix()
userProfilePublicKey: walletStore.userProfilePublicKey
includeWatchOnlyAccount: walletStore.includeWatchOnlyAccount
onGoToAccountView: root.goToAccountView(account)
onGoToAccountView: root.goToAccountView(account, keyPair)
onToggleIncludeWatchOnlyAccount: walletStore.toggleIncludeWatchOnlyAccount()
onRunRenameKeypairFlow: {
renameKeypairPopup.keyUid = model.keyPair.keyUid

View File

@ -1,2 +1,3 @@
EditNetworkView 1.0 EditNetworkView.qml
EditNetworkForm 1.0 EditNetworkForm.qml
AccountView 1.0 AccountView.qml

View File

@ -10,14 +10,14 @@ Loader {
property var overview
sourceComponent: root.overview.isAllAccounts ? multipleAccountsGradient : singleAccountGradient
sourceComponent: root.overview && root.overview.isAllAccounts ? multipleAccountsGradient : singleAccountGradient
Component {
id: singleAccountGradient
Rectangle {
implicitHeight: 115
gradient: Gradient {
GradientStop { position: 0.0; color: Theme.palette.alphaColor(Utils.getColorForId(overview.colorId), 0.1) }
GradientStop { position: 0.0; color: overview && overview.colorId ? Theme.palette.alphaColor(Utils.getColorForId(overview.colorId), 0.1) : "" }
GradientStop { position: 1.0; color: "transparent" }
}
}

View File

@ -786,6 +786,11 @@ QtObject {
return Theme.palette.customisationColors.blue
}
function getPathForDisplay(path) {
let letters = path.split("/").join(" / ")
return letters
}
// Leave this function at the bottom of the file as QT Creator messes up the code color after this
function isPunct(c) {
return /(!|\@|#|\$|%|\^|&|\*|\(|\)|\+|\||-|=|\\|{|}|[|]|"|;|'|<|>|\?|,|\.|\/)/.test(c)