From 7c4022e4ac9ac11c1a32a34126aa66bfda552810 Mon Sep 17 00:00:00 2001 From: Dario Gabriel Lipicar Date: Thu, 29 Dec 2022 13:44:51 -0300 Subject: [PATCH] feat(@desktop/wallet): implement unified currency formatting Fixes #8640 --- src/app/boot/app_controller.nim | 6 ++ .../current_account/controller.nim | 21 +++- .../current_account/module.nim | 72 +++++++------ .../browser_section/current_account/view.nim | 29 ++--- .../modules/main/browser_section/module.nim | 9 +- src/app/modules/main/module.nim | 11 +- .../ens_usernames/controller.nim | 7 +- .../profile_section/ens_usernames/module.nim | 4 +- .../modules/main/profile_section/module.nim | 6 +- src/app/modules/main/stickers/controller.nim | 6 +- src/app/modules/main/stickers/module.nim | 4 +- .../wallet_section/accounts/compact_item.nim | 8 +- .../wallet_section/accounts/controller.nim | 17 ++- .../main/wallet_section/accounts/item.nim | 25 ++++- .../main/wallet_section/accounts/module.nim | 88 ++++----------- .../main/wallet_section/accounts/utils.nim | 68 ++++++++++++ .../main/wallet_section/controller.nim | 11 +- .../current_account/controller.nim | 22 +++- .../wallet_section/current_account/module.nim | 100 +++++++++++------- .../wallet_section/current_account/view.nim | 88 +++++++-------- .../modules/main/wallet_section/module.nim | 8 +- src/app/modules/main/wallet_section/view.nim | 5 +- .../modules/shared_models/balance_item.nim | 42 ++++++++ .../modules/shared_models/balance_model.nim | 9 +- .../modules/shared_models/balance_utils.nim | 11 ++ .../modules/shared_models/currency_amount.nim | 67 ++++++++++++ .../shared_models/currency_amount_utils.nim | 10 ++ src/app/modules/shared_models/token_item.nim | 83 ++++++++------- src/app/modules/shared_models/token_model.nim | 20 +++- src/app/modules/shared_models/token_utils.nim | 42 ++++++++ src/app_service/service/currency/dto.nim | 6 ++ src/app_service/service/currency/service.nim | 55 ++++++++++ src/app_service/service/currency/utils.nim | 33 ++++++ src/app_service/service/message/service.nim | 2 +- src/app_service/service/token/dto.nim | 26 +---- src/app_service/service/token/service.nim | 80 +++++++++++--- .../service/wallet_account/dto.nim | 76 +++++++++---- .../service/wallet_account/service.nim | 51 +++------ src/backend/backend.nim | 12 ++- .../pages/CommunitiesPortalLayoutPage.qml | 1 - .../Components/StatusCommunityCard.qml | 10 +- .../Controls/StatusAccountSelector.qml | 4 +- .../Validators/StatusFloatValidator.qml | 6 +- .../Validators/StatusIntValidator.qml | 8 +- .../controls/community/CollectiblesPanel.qml | 3 + .../controls/community/HoldingsDropdown.qml | 2 + .../Chat/controls/community/TokensPanel.qml | 3 + ui/app/AppLayouts/Chat/stores/RootStore.qml | 4 +- .../stores/CommunitiesStore.qml | 2 +- .../AppLayouts/Wallet/panels/WalletHeader.qml | 4 +- .../AppLayouts/Wallet/popups/ReceiveModal.qml | 1 + ui/app/AppLayouts/Wallet/stores/RootStore.qml | 4 +- .../AppLayouts/Wallet/views/LeftTabView.qml | 4 +- .../AppLayouts/Wallet/views/RightTabView.qml | 1 + ui/app/AppLayouts/stores/RootStore.qml | 2 +- ui/imports/shared/controls/AmountInput.qml | 4 +- .../shared/controls/AssetAndAmountInput.qml | 2 +- ui/imports/shared/controls/AssetDelegate.qml | 6 +- .../shared/controls/AssetsDetailsHeader.qml | 3 +- .../controls/TokenBalancePerChainDelegate.qml | 10 +- ui/imports/shared/controls/TokenDelegate.qml | 12 +-- .../shared/panels/StatusAssetSelector.qml | 3 +- .../shared/popups/AccountsModalHeader.qml | 3 +- ui/imports/shared/popups/SendModal.qml | 14 ++- .../keycard/helpers/KeyPairUnknownItem.qml | 4 +- ui/imports/shared/stores/CurrenciesStore.qml | 92 +++++++++++++++- ui/imports/shared/stores/RootStore.qml | 4 +- ui/imports/shared/stores/TransactionStore.qml | 25 ++++- ui/imports/shared/views/AmountToReceive.qml | 3 +- ui/imports/shared/views/AmountToSend.qml | 9 +- ui/imports/shared/views/AssetsDetailView.qml | 23 ++-- ui/imports/shared/views/AssetsView.qml | 5 +- .../shared/views/NetworkCardsComponent.qml | 11 +- .../views/NetworksSimpleRoutingView.qml | 5 +- .../shared/views/TabAddressSelectorView.qml | 3 +- ui/imports/shared/views/TokenListView.qml | 4 +- ui/imports/utils/LocaleUtils.qml | 24 +++++ ui/imports/utils/Utils.qml | 6 +- vendor/status-go | 2 +- 79 files changed, 1121 insertions(+), 455 deletions(-) create mode 100644 src/app/modules/main/wallet_section/accounts/utils.nim create mode 100644 src/app/modules/shared_models/balance_item.nim create mode 100644 src/app/modules/shared_models/balance_utils.nim create mode 100644 src/app/modules/shared_models/currency_amount.nim create mode 100644 src/app/modules/shared_models/currency_amount_utils.nim create mode 100644 src/app/modules/shared_models/token_utils.nim create mode 100644 src/app_service/service/currency/dto.nim create mode 100644 src/app_service/service/currency/service.nim create mode 100644 src/app_service/service/currency/utils.nim diff --git a/src/app/boot/app_controller.nim b/src/app/boot/app_controller.nim index ce1eb34768..006e39e018 100644 --- a/src/app/boot/app_controller.nim +++ b/src/app/boot/app_controller.nim @@ -10,6 +10,7 @@ import ../../app_service/service/chat/service as chat_service import ../../app_service/service/community/service as community_service import ../../app_service/service/message/service as message_service import ../../app_service/service/token/service as token_service +import ../../app_service/service/currency/service as currency_service import ../../app_service/service/transaction/service as transaction_service import ../../app_service/service/collectible/service as collectible_service import ../../app_service/service/wallet_account/service as wallet_account_service @@ -67,6 +68,7 @@ type communityService: community_service.Service messageService: message_service.Service tokenService: token_service.Service + currencyService: currency_service.Service transactionService: transaction_service.Service collectibleService: collectible_service.Service walletAccountService: wallet_account_service.Service @@ -160,6 +162,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController = result.tokenService = token_service.newService( statusFoundation.events, statusFoundation.threadpool, result.networkService ) + result.currencyService = currency_service.newService(result.tokenService, result.settingsService) 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, @@ -221,6 +224,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController = result.communityService, result.messageService, result.tokenService, + result.currencyService, result.transactionService, result.collectibleService, result.walletAccountService, @@ -273,6 +277,7 @@ proc delete*(self: AppController) = self.accountsService.delete self.chatService.delete self.communityService.delete + self.currencyService.delete self.tokenService.delete self.transactionService.delete self.collectibleService.delete @@ -352,6 +357,7 @@ proc load(self: AppController) = self.networkService.init() self.tokenService.init() + self.currencyService.init() self.walletAccountService.init() # Apply runtime log level settings diff --git a/src/app/modules/main/browser_section/current_account/controller.nim b/src/app/modules/main/browser_section/current_account/controller.nim index 55ed3f57c6..f49ee2beb5 100644 --- a/src/app/modules/main/browser_section/current_account/controller.nim +++ b/src/app/modules/main/browser_section/current_account/controller.nim @@ -1,23 +1,31 @@ -import sugar, sequtils +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 ../../../../../app_service/service/token/service as token_service +import ../../../../../app_service/service/currency/service as currency_service type Controller* = ref object of RootObj delegate: io_interface.AccessInterface walletAccountService: wallet_account_service.Service networkService: network_service.Service + tokenService: token_service.Service + currencyService: currency_service.Service proc newController*( delegate: io_interface.AccessInterface, walletAccountService: wallet_account_service.Service, networkService: network_service.Service, + tokenService: token_service.Service, + currencyService: currency_service.Service, ): Controller = result = Controller() result.delegate = delegate result.walletAccountService = walletAccountService result.networkService = networkService + result.tokenService = tokenService + result.currencyService = currencyService proc delete*(self: Controller) = discard @@ -38,4 +46,13 @@ proc getChainIds*(self: Controller): seq[int] = return self.networkService.getNetworks().map(n => n.chainId) proc getEnabledChainIds*(self: Controller): seq[int] = - return self.networkService.getNetworks().filter(n => n.enabled).map(n => n.chainId) \ No newline at end of file + return self.networkService.getNetworks().filter(n => n.enabled).map(n => n.chainId) + +proc getCurrentCurrency*(self: Controller): string = + return self.walletAccountService.getCurrency() + +proc getCurrencyFormat*(self: Controller, symbol: string): CurrencyFormatDto = + return self.currencyService.getCurrencyFormat(symbol) + +proc getAllMigratedKeyPairs*(self: Controller): seq[KeyPairDto] = + return self.walletAccountService.getAllMigratedKeyPairs() \ No newline at end of file diff --git a/src/app/modules/main/browser_section/current_account/module.nim b/src/app/modules/main/browser_section/current_account/module.nim index dbdf303559..f1dc5e101a 100644 --- a/src/app/modules/main/browser_section/current_account/module.nim +++ b/src/app/modules/main/browser_section/current_account/module.nim @@ -1,11 +1,16 @@ -import NimQml, Tables, sequtils +import NimQml, Tables, sequtils, sugar 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/token/service as token_service +import ../../../../../app_service/service/currency/service as currency_service import ../../../shared_models/token_model as token_model import ../../../shared_models/token_item as token_item +import ../../../shared_models/token_utils + +import ../../wallet_section/accounts/utils import ./io_interface, ./view, ./controller import ../io_interface as delegate_interface @@ -28,57 +33,64 @@ proc newModule*( events: EventEmitter, walletAccountService: wallet_account_service.Service, networkService: network_service.Service, + tokenService: token_service.Service, + currencyService: currency_service.Service, ): Module = result = Module() result.delegate = delegate result.events = events result.currentAccountIndex = 0 result.view = newView(result) - result.controller = newController(result, walletAccountService, networkService) + result.controller = newController(result, walletAccountService, networkService, tokenService, currencyService) result.moduleLoaded = false method delete*(self: Module) = self.view.delete proc setAssets(self: Module, tokens: seq[WalletTokenDto]) = - var items: seq[Item] let chainIds = self.controller.getChainIds() let enabledChainIds = self.controller.getEnabledChainIds() - for t in tokens: - let item = token_item.initItem( - t.name, - t.symbol, - t.getBalance(chainIds), - t.getCurrencyBalance(chainIds), - t.getBalance(enabledChainIds), - t.getCurrencyBalance(enabledChainIds), - t.getVisibleForNetwork(enabledChainIds), - t.getVisibleForNetworkWithPositiveBalance(enabledChainIds), - t.getBalances(enabledChainIds), - t.description, - t.assetWebsiteUrl, - t.builtOn, - t.getAddress(), - t.marketCap, - t.highDay, - t.lowDay, - t.changePctHour, - t.changePctDay, - t.changePct24hour, - t.change24hour, - t.currencyPrice, - t.decimals, - ) - items.add(item) + let currency = self.controller.getCurrentCurrency() + + let currencyFormat = self.controller.getCurrencyFormat(currency) + + let items = tokens.map(t => walletTokenToItem(t, chainIds, enabledChainIds, currency, currencyFormat, self.controller.getCurrencyFormat(t.symbol))) self.view.getAssetsModel().setItems(items) method switchAccount*(self: Module, accountIndex: int) = self.currentAccountIndex = accountIndex + + let keyPairMigrated = proc(migratedKeyPairs: seq[KeyPairDto], keyUid: string): bool = + for kp in migratedKeyPairs: + if kp.keyUid == keyUid: + return true + return false + let walletAccount = self.controller.getWalletAccount(accountIndex) + let migratedKeyPairs = self.controller.getAllMigratedKeyPairs() + let currency = self.controller.getCurrentCurrency() + + let chainIds = self.controller.getChainIds() let enabledChainIds = self.controller.getEnabledChainIds() - self.view.setData(walletAccount, enabledChainIds) + + let tokenFormats = collect(initTable()): + for t in walletAccount.tokens: {t.symbol: self.controller.getCurrencyFormat(t.symbol)} + + let currencyFormat = self.controller.getCurrencyFormat(currency) + + let accountItem = walletAccountToItem( + walletAccount, + chainIds, + enabledChainIds, + currency, + keyPairMigrated(migratedKeyPairs, walletAccount.keyUid), + currencyFormat, + tokenFormats + ) + + self.view.setData(accountItem) self.setAssets(walletAccount.tokens) method load*(self: Module) = diff --git a/src/app/modules/main/browser_section/current_account/view.nim b/src/app/modules/main/browser_section/current_account/view.nim index 1db64e2e4a..85dd1800bc 100644 --- a/src/app/modules/main/browser_section/current_account/view.nim +++ b/src/app/modules/main/browser_section/current_account/view.nim @@ -1,10 +1,11 @@ import NimQml, sequtils, sugar -import ../../../../../app_service/service/wallet_account/service as wallet_account_service import ./io_interface import ../../../shared_models/token_model as token_model import ../../../shared_models/token_item as token_item +import ../../../shared_models/currency_amount +import ../../wallet_section/accounts/item as account_item QtObject: type @@ -17,7 +18,7 @@ QtObject: publicKey: string walletType: string isChat: bool - currencyBalance: float64 + currencyBalance: CurrencyAmount assets: token_model.Model emoji: string @@ -140,27 +141,27 @@ QtObject: proc hasGas*(self: View, chainId: int, nativeGasSymbol: string, requiredGas: float): bool {.slot.} = return self.assets.hasGas(chainId, nativeGasSymbol, requiredGas) - proc getTokenBalanceOnChain*(self: View, chainId: int, tokenSymbol: string): string {.slot.} = - return self.assets.getTokenBalanceOnChain(chainId, tokenSymbol) +# proc getTokenBalanceOnChain*(self: View, chainId: int, tokenSymbol: string): QVariant {.slot.} = +# return newQVariant(self.assets.getTokenBalanceOnChain(chainId, tokenSymbol)) -proc setData*(self: View, dto: wallet_account_service.WalletAccountDto, chainIds: seq[int]) = - self.name = dto.name +proc setData*(self: View, item: account_item.Item) = + self.name = item.getName() self.nameChanged() - self.address = dto.address + self.address = item.getAddress() self.addressChanged() - self.path = dto.path + self.path = item.getPath() self.pathChanged() - self.color = dto.color + self.color = item.getColor() self.colorChanged() - self.publicKey = dto.publicKey + self.publicKey = item.getPublicKey() self.publicKeyChanged() - self.walletType = dto.walletType + self.walletType = item.getWalletType() self.walletTypeChanged() - self.isChat = dto.isChat + self.isChat = item.getIsChat() self.isChatChanged() - self.currencyBalance = dto.getCurrencyBalance(chainIds) + self.currencyBalance = item.getCurrencyBalance() self.currencyBalanceChanged() - self.emoji = dto.emoji + self.emoji = item.getEmoji() self.emojiChanged() proc isAddressCurrentAccount*(self: View, address: string): bool = diff --git a/src/app/modules/main/browser_section/module.nim b/src/app/modules/main/browser_section/module.nim index 7b7e4597fe..f9a46f1b9d 100644 --- a/src/app/modules/main/browser_section/module.nim +++ b/src/app/modules/main/browser_section/module.nim @@ -14,6 +14,8 @@ import ../../../../app_service/service/network/service as network_service import ../../../../app_service/service/dapp_permissions/service as dapp_permissions_service import ../../../../app_service/service/provider/service as provider_service import ../../../../app_service/service/wallet_account/service as wallet_account_service +import ../../../../app_service/service/token/service as token_service +import ../../../../app_service/service/currency/service as currency_service export io_interface @@ -36,7 +38,10 @@ proc newModule*(delegate: delegate_interface.AccessInterface, networkService: network_service.Service, dappPermissionsService: dapp_permissions_service.Service, providerService: provider_service.Service, - walletAccountService: wallet_account_service.Service): Module = + walletAccountService: wallet_account_service.Service, + tokenService: token_service.Service, + currencyService: currency_service.Service +): Module = result = Module() result.delegate = delegate result.events = events @@ -46,7 +51,7 @@ proc newModule*(delegate: delegate_interface.AccessInterface, result.providerModule = provider_module.newModule(result, events, settingsService, networkService, providerService) result.bookmarkModule = bookmark_module.newModule(result, events, bookmarkService) result.dappsModule = dapps_module.newModule(result, dappPermissionsService, walletAccountService) - result.currentAccountModule = current_account_module.newModule(result, events, walletAccountService, networkService) + result.currentAccountModule = current_account_module.newModule(result, events, walletAccountService, networkService, tokenService, currencyService) method delete*(self: Module) = self.view.delete diff --git a/src/app/modules/main/module.nim b/src/app/modules/main/module.nim index 9af9d2f140..096709f51f 100644 --- a/src/app/modules/main/module.nim +++ b/src/app/modules/main/module.nim @@ -29,6 +29,7 @@ import ../../../app_service/service/chat/service as chat_service import ../../../app_service/service/community/service as community_service import ../../../app_service/service/message/service as message_service import ../../../app_service/service/token/service as token_service +import ../../../app_service/service/currency/service as currency_service import ../../../app_service/service/transaction/service as transaction_service import ../../../app_service/service/collectible/service as collectible_service import ../../../app_service/service/wallet_account/service as wallet_account_service @@ -109,6 +110,7 @@ proc newModule*[T]( communityService: community_service.Service, messageService: message_service.Service, tokenService: token_service.Service, + currencyService: currency_service.Service, transactionService: transaction_service.Service, collectibleService: collectible_service.Service, walletAccountService: wallet_account_service.Service, @@ -168,21 +170,22 @@ proc newModule*[T]( # Submodules result.channelGroupModules = initOrderedTable[string, chat_section_module.AccessInterface]() result.walletSectionModule = wallet_section_module.newModule( - result, events, tokenService, + result, events, tokenService, currencyService, transactionService, collectible_service, walletAccountService, settingsService, savedAddressService, networkService, accountsService, keycardService ) result.browserSectionModule = browser_section_module.newModule( result, events, bookmarkService, settingsService, networkService, - dappPermissionsService, providerService, walletAccountService + dappPermissionsService, providerService, walletAccountService, + tokenService, currencyService ) result.profileSectionModule = profile_section_module.newModule( result, events, accountsService, settingsService, stickersService, profileService, contactsService, aboutService, languageService, privacyService, nodeConfigurationService, devicesService, mailserversService, chatService, ensService, walletAccountService, generalService, communityService, - networkService, keycardService, keychainService + networkService, keycardService, keychainService, tokenService ) - result.stickersModule = stickers_module.newModule(result, events, stickersService, settingsService, walletAccountService, networkService) + result.stickersModule = stickers_module.newModule(result, events, stickersService, settingsService, walletAccountService, networkService, tokenService) result.activityCenterModule = activity_center_module.newModule(result, events, activityCenterService, contactsService, messageService, chatService) result.communitiesModule = communities_module.newModule(result, events, communityService, contactsService) diff --git a/src/app/modules/main/profile_section/ens_usernames/controller.nim b/src/app/modules/main/profile_section/ens_usernames/controller.nim index 3217a4bce2..1849d8cc2d 100644 --- a/src/app/modules/main/profile_section/ens_usernames/controller.nim +++ b/src/app/modules/main/profile_section/ens_usernames/controller.nim @@ -7,7 +7,7 @@ import ../../../../../app_service/service/settings/service as settings_service import ../../../../../app_service/service/ens/service as ens_service import ../../../../../app_service/service/network/service as network_service import ../../../../../app_service/service/wallet_account/service as wallet_account_service -import ../../../../../app_service/service/token/dto +import ../../../../../app_service/service/token/service as token_service import ../../../shared_modules/keycard_popup/io_interface as keycard_shared_module logScope: @@ -23,11 +23,13 @@ type ensService: ens_service.Service networkService: network_service.Service walletAccountService: wallet_account_service.Service + tokenService: token_service.Service proc newController*( delegate: io_interface.AccessInterface, events: EventEmitter, settingsService: settings_service.Service, ensService: ens_service.Service, walletAccountService: wallet_account_service.Service, networkService: network_service.Service, + tokenService: token_service.Service ): Controller = result = Controller() result.delegate = delegate @@ -36,6 +38,7 @@ proc newController*( result.ensService = ensService result.walletAccountService = walletAccountService result.networkService = networkService + result.tokenService = tokenService proc delete*(self: Controller) = discard @@ -135,7 +138,7 @@ proc getCurrentCurrency*(self: Controller): string = return self.settingsService.getCurrency() proc getPrice*(self: Controller, crypto: string, fiat: string): float64 = - return self.walletAccountService.getPrice(crypto, fiat) + return self.tokenService.getTokenPrice(crypto, fiat) proc getStatusToken*(self: Controller): string = let token = self.ensService.getStatusToken() diff --git a/src/app/modules/main/profile_section/ens_usernames/module.nim b/src/app/modules/main/profile_section/ens_usernames/module.nim index a9366a051e..5fcc20d3bb 100644 --- a/src/app/modules/main/profile_section/ens_usernames/module.nim +++ b/src/app/modules/main/profile_section/ens_usernames/module.nim @@ -12,6 +12,7 @@ import ../../../../../app_service/service/ens/service as ens_service import ../../../../../app_service/service/network/service as network_service import ../../../../../app_service/service/ens/utils as ens_utils import ../../../../../app_service/service/wallet_account/service as wallet_account_service +import ../../../../../app_service/service/token/service as token_service export io_interface @@ -49,12 +50,13 @@ proc newModule*( settingsService: settings_service.Service, ensService: ens_service.Service, walletAccountService: wallet_account_service.Service, networkService: network_service.Service, + tokenService: token_service.Service, ): Module = result = Module() result.delegate = delegate result.view = view.newView(result) result.viewVariant = newQVariant(result.view) - result.controller = controller.newController(result, events, settingsService, ensService, walletAccountService, networkService) + result.controller = controller.newController(result, events, settingsService, ensService, walletAccountService, networkService, tokenService) result.moduleLoaded = false method delete*(self: Module) = diff --git a/src/app/modules/main/profile_section/module.nim b/src/app/modules/main/profile_section/module.nim index bf5a34d46e..2cce5ddf36 100644 --- a/src/app/modules/main/profile_section/module.nim +++ b/src/app/modules/main/profile_section/module.nim @@ -22,6 +22,7 @@ import ../../../../app_service/service/general/service as general_service import ../../../../app_service/service/community/service as community_service import ../../../../app_service/service/keycard/service as keycard_service import ../../../../app_service/service/keychain/service as keychain_service +import ../../../../app_service/service/token/service as token_service import ./profile/module as profile_module import ./contacts/module as contacts_module @@ -79,7 +80,8 @@ proc newModule*(delegate: delegate_interface.AccessInterface, communityService: community_service.Service, networkService: network_service.Service, keycardService: keycard_service.Service, - keychainService: keychain_service.Service + keychainService: keychain_service.Service, + tokenService: token_service.Service ): Module = result = Module() result.delegate = delegate @@ -98,7 +100,7 @@ proc newModule*(delegate: delegate_interface.AccessInterface, result.syncModule = sync_module.newModule(result, events, settingsService, nodeConfigurationService, mailserversService) result.notificationsModule = notifications_module.newModule(result, events, settingsService, chatService, contactsService) result.ensUsernamesModule = ens_usernames_module.newModule( - result, events, settingsService, ensService, walletAccountService, networkService + result, events, settingsService, ensService, walletAccountService, networkService, tokenService ) result.communitiesModule = communities_module.newModule(result, communityService) result.keycardModule = keycard_module.newModule(result, events, keycardService, settingsService, privacyService, diff --git a/src/app/modules/main/stickers/controller.nim b/src/app/modules/main/stickers/controller.nim index 9c1f933586..07e662df2f 100644 --- a/src/app/modules/main/stickers/controller.nim +++ b/src/app/modules/main/stickers/controller.nim @@ -10,6 +10,7 @@ import ../../../../app_service/service/settings/service as settings_service import ../../../../app_service/service/network/service as network_service import ../../../../app_service/service/eth/utils as eth_utils import ../../../../app_service/service/wallet_account/service as wallet_account_service +import ../../../../app_service/service/token/service as token_service import ../../shared_modules/keycard_popup/io_interface as keycard_shared_module const UNIQUE_BUY_STICKER_TRANSACTION_MODULE_IDENTIFIER* = "StickersSection-TransactionModule" @@ -22,6 +23,7 @@ type settingsService: settings_service.Service networkService: network_service.Service walletAccountService: wallet_account_service.Service + tokenService: token_service.Service disconnected: bool # Forward declaration @@ -35,6 +37,7 @@ proc newController*( settingsService: settings_service.Service, walletAccountService: wallet_account_service.Service, networkService: network_service.Service, + tokenService: token_service.Service, ): Controller = result = Controller() result.delegate = delegate @@ -43,6 +46,7 @@ proc newController*( result.settingsService = settingsService result.networkService = networkService result.walletAccountService = walletAccountService + result.tokenService = tokenService result.disconnected = false proc delete*(self: Controller) = @@ -154,7 +158,7 @@ proc getCurrentCurrency*(self: Controller): string = return self.settingsService.getCurrency() proc getPrice*(self: Controller, crypto: string, fiat: string): float64 = - return self.walletAccountService.getPrice(crypto, fiat) + return self.tokenService.getTokenPrice(crypto, fiat) proc getChainIdForStickers*(self: Controller): int = return self.networkService.getNetworkForStickers().chainId diff --git a/src/app/modules/main/stickers/module.nim b/src/app/modules/main/stickers/module.nim index b80b040d2f..d338ab1a43 100644 --- a/src/app/modules/main/stickers/module.nim +++ b/src/app/modules/main/stickers/module.nim @@ -8,6 +8,7 @@ import ../../../../app_service/service/settings/service as settings_service import ../../../../app_service/service/network/service as network_service import ../../../../app_service/common/conversion as service_conversion import ../../../../app_service/service/wallet_account/service as wallet_account_service +import ../../../../app_service/service/token/service as token_service export io_interface @@ -40,12 +41,13 @@ proc newModule*( settingsService: settings_Service.Service, walletAccountService: wallet_account_service.Service, networkService: network_service.Service, + tokenService: token_service.Service, ): Module = result = Module() result.delegate = delegate result.view = newView(result) result.viewVariant = newQVariant(result.view) - result.controller = controller.newController(result, events, stickersService, settingsService, walletAccountService, networkService) + result.controller = controller.newController(result, events, stickersService, settingsService, walletAccountService, networkService, tokenService) result.moduleLoaded = false singletonInstance.engine.setRootContextProperty("stickersModule", result.viewVariant) diff --git a/src/app/modules/main/wallet_section/accounts/compact_item.nim b/src/app/modules/main/wallet_section/accounts/compact_item.nim index 9d60785653..ff84c82ba2 100644 --- a/src/app/modules/main/wallet_section/accounts/compact_item.nim +++ b/src/app/modules/main/wallet_section/accounts/compact_item.nim @@ -1,5 +1,7 @@ import strformat +import ../../../shared_models/currency_amount + type Item* = object name: string @@ -10,7 +12,7 @@ type walletType: string isWallet: bool isChat: bool - currencyBalance: float64 + currencyBalance: CurrencyAmount emoji: string derivedfrom: string @@ -23,7 +25,7 @@ proc initItem*( walletType: string, isWallet: bool, isChat: bool, - currencyBalance: float64, + currencyBalance: CurrencyAmount, emoji: string, derivedfrom: string ): Item = @@ -81,7 +83,7 @@ proc getIsWallet*(self: Item): bool = proc getIsChat*(self: Item): bool = return self.isChat -proc getCurrencyBalance*(self: Item): float64 = +proc getCurrencyBalance*(self: Item): CurrencyAmount = return self.currencyBalance proc getDerivedFrom*(self: Item): string = diff --git a/src/app/modules/main/wallet_section/accounts/controller.nim b/src/app/modules/main/wallet_section/accounts/controller.nim index b2e2781997..7547860056 100644 --- a/src/app/modules/main/wallet_section/accounts/controller.nim +++ b/src/app/modules/main/wallet_section/accounts/controller.nim @@ -1,8 +1,11 @@ -import sugar, sequtils +import sugar, sequtils, tables import io_interface import ../../../../../app_service/service/wallet_account/service as wallet_account_service import ../../../../../app_service/service/accounts/service as accounts_service import ../../../../../app_service/service/network/service as network_service +import ../../../../../app_service/service/token/service as token_service +import ../../../../../app_service/service/currency/service as currency_service +import ../../../../../app_service/service/currency/dto as currency_dto import ../../../../global/global_singleton import ../../../shared_modules/keycard_popup/io_interface as keycard_shared_module @@ -19,6 +22,8 @@ type walletAccountService: wallet_account_service.Service accountsService: accounts_service.Service networkService: network_service.Service + tokenService: token_service.Service + currencyService: currency_service.Service proc newController*( delegate: io_interface.AccessInterface, @@ -26,6 +31,8 @@ proc newController*( walletAccountService: wallet_account_service.Service, accountsService: accounts_service.Service, networkService: network_service.Service, + tokenService: token_service.Service, + currencyService: currency_service.Service, ): Controller = result = Controller() result.delegate = delegate @@ -33,6 +40,8 @@ proc newController*( result.walletAccountService = walletAccountService result.accountsService = accountsService result.networkService = networkService + result.tokenService = tokenService + result.currencyService = currencyService proc delete*(self: Controller) = discard @@ -112,3 +121,9 @@ proc getChainIds*(self: Controller): seq[int] = proc getEnabledChainIds*(self: Controller): seq[int] = return self.networkService.getNetworks().filter(n => n.enabled).map(n => n.chainId) + +proc getCurrentCurrency*(self: Controller): string = + return self.walletAccountService.getCurrency() + +proc getCurrencyFormat*(self: Controller, symbol: string): CurrencyFormatDto = + return self.currencyService.getCurrencyFormat(symbol) diff --git a/src/app/modules/main/wallet_section/accounts/item.nim b/src/app/modules/main/wallet_section/accounts/item.nim index 1e0e7176c9..7e33c98f12 100644 --- a/src/app/modules/main/wallet_section/accounts/item.nim +++ b/src/app/modules/main/wallet_section/accounts/item.nim @@ -1,44 +1,50 @@ import strformat import ../../../shared_models/token_model as token_model +import ../../../shared_models/currency_amount import ./compact_model as compact_model type Item* = object name: string address: string + mixedCaseAddress: string path: string color: string publicKey: string walletType: string isWallet: bool isChat: bool - currencyBalance: float64 + currencyBalance: CurrencyAmount assets: token_model.Model emoji: string derivedfrom: string relatedAccounts: compact_model.Model keyUid: string migratedToKeycard: bool + ens: string proc initItem*( name: string, address: string, + mixedCaseAddress: string, path: string, color: string, publicKey: string, walletType: string, isWallet: bool, isChat: bool, - currencyBalance: float64, + currencyBalance: CurrencyAmount, assets: token_model.Model, emoji: string, derivedfrom: string, relatedAccounts: compact_model.Model, keyUid: string, - migratedToKeycard: bool + migratedToKeycard: bool, + ens: string ): Item = result.name = name result.address = address + result.mixedCaseAddress = mixedCaseAddress result.path = path result.color = color result.publicKey = publicKey @@ -52,11 +58,13 @@ proc initItem*( result.relatedAccounts = relatedAccounts result.keyUid = keyUid result.migratedToKeycard = migratedToKeycard + result.ens = ens proc `$`*(self: Item): string = result = fmt"""WalletAccountItem( name: {self.name}, address: {self.address}, + mixedCaseAddress: {self.mixedCaseAddress}, path: {self.path}, color: {self.color}, publicKey: {self.publicKey}, @@ -69,7 +77,8 @@ proc `$`*(self: Item): string = derivedfrom: {self.derivedfrom}, relatedAccounts: {self.relatedAccounts} keyUid: {self.keyUid}, - migratedToKeycard: {self.migratedToKeycard} + migratedToKeycard: {self.migratedToKeycard}, + ens: {self.ens} ]""" proc getName*(self: Item): string = @@ -78,6 +87,9 @@ proc getName*(self: Item): string = proc getAddress*(self: Item): string = return self.address +proc getMixedCaseAddress*(self: Item): string = + return self.mixedCaseAddress + proc getPath*(self: Item): string = return self.path @@ -99,7 +111,7 @@ proc getIsWallet*(self: Item): bool = proc getIsChat*(self: Item): bool = return self.isChat -proc getCurrencyBalance*(self: Item): float64 = +proc getCurrencyBalance*(self: Item): CurrencyAmount = return self.currencyBalance proc getAssets*(self: Item): token_model.Model = @@ -116,3 +128,6 @@ proc getKeyUid*(self: Item): string = proc getMigratedToKeycard*(self: Item): bool = return self.migratedToKeycard + +proc getEns*(self: Item): string = + return self.ens diff --git a/src/app/modules/main/wallet_section/accounts/module.nim b/src/app/modules/main/wallet_section/accounts/module.nim index a7e3036bac..3ab917d0a4 100644 --- a/src/app/modules/main/wallet_section/accounts/module.nim +++ b/src/app/modules/main/wallet_section/accounts/module.nim @@ -1,6 +1,6 @@ import tables, NimQml, sequtils, sugar, chronicles -import ./io_interface, ./view, ./item, ./controller +import ./io_interface, ./view, ./item, ./controller, ./utils import ../io_interface as delegate_interface import ../../../../global/global_singleton import ../../../../core/eventemitter @@ -9,6 +9,8 @@ import ../../../../../app_service/service/keycard/service as keycard_service import ../../../../../app_service/service/wallet_account/service as wallet_account_service import ../../../../../app_service/service/accounts/service as accounts_service import ../../../../../app_service/service/network/service as network_service +import ../../../../../app_service/service/token/service as token_service +import ../../../../../app_service/service/currency/service as currency_service import ../../../shared_models/token_model as token_model import ../../../shared_models/token_item as token_item import ../../../shared_modules/keycard_popup/module as keycard_shared_module @@ -43,7 +45,9 @@ proc newModule*( keycardService: keycard_service.Service, walletAccountService: wallet_account_service.Service, accountsService: accounts_service.Service, - networkService: network_service.Service + networkService: network_service.Service, + tokenService: token_service.Service, + currencyService: currency_service.Service ): Module = result = Module() result.delegate = delegate @@ -52,7 +56,7 @@ proc newModule*( result.accountsService = accountsService result.walletAccountService = walletAccountService result.view = newView(result) - result.controller = controller.newController(result, events, walletAccountService, accountsService, networkService) + result.controller = controller.newController(result, events, walletAccountService, accountsService, networkService, tokenService, currencyService) result.moduleLoaded = false method delete*(self: Module) = @@ -70,73 +74,27 @@ method refreshWalletAccounts*(self: Module) = let walletAccounts = self.controller.getWalletAccounts() let migratedKeyPairs = self.controller.getAllMigratedKeyPairs() + let currency = self.controller.getCurrentCurrency() let chainIds = self.controller.getChainIds() let enabledChainIds = self.controller.getEnabledChainIds() + + let currencyFormat = self.controller.getCurrencyFormat(currency) - let items = walletAccounts.map(proc (w: WalletAccountDto): item.Item = - let assets = token_model.newModel() - assets.setItems( - w.tokens.map(t => token_item.initItem( - t.name, - t.symbol, - t.getBalance(chainIds), - t.getCurrencyBalance(chainIds), - t.getBalance(enabledChainIds), - t.getCurrencyBalance(enabledChainIds), - t.getVisibleForNetwork(enabledChainIds), - t.getVisibleForNetworkWithPositiveBalance(enabledChainIds), - t.getBalances(enabledChainIds), - t.description, - t.assetWebsiteUrl, - t.builtOn, - t.getAddress(), - t.marketCap, - t.highDay, - t.lowDay, - t.changePctHour, - t.changePctDay, - t.changePct24hour, - t.change24hour, - t.currencyPrice, - t.decimals, - )) + let items = walletAccounts.map(w => (block: + let tokenFormats = collect(initTable()): + for t in w.tokens: {t.symbol: self.controller.getCurrencyFormat(t.symbol)} + + walletAccountToItem( + w, + chainIds, + enabledChainIds, + currency, + keyPairMigrated(migratedKeyPairs, w.keyUid), + currencyFormat, + tokenFormats ) - - let relatedAccounts = compact_model.newModel() - relatedAccounts.setItems( - w.relatedAccounts.map(x => compact_item.initItem( - x.name, - x.address, - x.path, - x.color, - x.publicKey, - x.walletType, - x.isWallet, - x.isChat, - x.getCurrencyBalance(enabledChainIds), - x.emoji, - x.derivedfrom, - )) - ) - - result = initItem( - w.name, - w.address, - w.path, - w.color, - w.publicKey, - w.walletType, - w.isWallet, - w.isChat, - w.getCurrencyBalance(enabledChainIds), - assets, - w.emoji, - w.derivedfrom, - relatedAccounts, - w.keyUid, - keyPairMigrated(migratedKeyPairs, w.keyUid) - )) + )) self.view.setItems(items) diff --git a/src/app/modules/main/wallet_section/accounts/utils.nim b/src/app/modules/main/wallet_section/accounts/utils.nim new file mode 100644 index 0000000000..eb32a92bc9 --- /dev/null +++ b/src/app/modules/main/wallet_section/accounts/utils.nim @@ -0,0 +1,68 @@ +import tables, sequtils, sugar + +import ./item + +import ../../../../../app_service/service/wallet_account/dto +import ../../../../../app_service/service/token/dto as token_dto +import ../../../../../app_service/service/currency/dto as currency_dto +import ../../../shared_models/token_model as token_model +import ../../../shared_models/token_item as token_item +import ../../../shared_models/token_utils +import ../../../shared_models/currency_amount +import ../../../shared_models/currency_amount_utils + +import ./compact_item as compact_item +import ./compact_model as compact_model + +proc walletAccountToCompactItem*(w: WalletAccountDto, enabledChainIds: seq[int], currency: string, currencyFormat: CurrencyFormatDto) : compact_item.Item = + return compact_item.initItem( + w.name, + w.address, + w.path, + w.color, + w.publicKey, + w.walletType, + w.isWallet, + w.isChat, + currencyAmountToItem(w.getCurrencyBalance(enabledChainIds, currency), currencyFormat), + w.emoji, + w.derivedfrom) + +proc walletAccountToItem*( + w: WalletAccountDto, + chainIds: seq[int], + enabledChainIds: seq[int], + currency: string, + keyPairMigrated: bool, + currencyFormat: CurrencyFormatDto, + tokenFormats: Table[string, CurrencyFormatDto], + ) : item.Item = + let assets = token_model.newModel() + assets.setItems( + w.tokens.map(t => walletTokenToItem(t, chainIds, enabledChainIds, currency, currencyFormat, tokenFormats[t.symbol])) + ) + + let relatedAccounts = compact_model.newModel() + relatedAccounts.setItems( + w.relatedAccounts.map(x => walletAccountToCompactItem(x, enabledChainIds, currency, currencyFormat)) + ) + + result = initItem( + w.name, + w.address, + w.mixedCaseAddress, + w.path, + w.color, + w.publicKey, + w.walletType, + w.isWallet, + w.isChat, + currencyAmountToItem(w.getCurrencyBalance(enabledChainIds, currency), currencyFormat), + assets, + w.emoji, + w.derivedfrom, + relatedAccounts, + w.keyUid, + keyPairMigrated, + w.ens + ) diff --git a/src/app/modules/main/wallet_section/controller.nim b/src/app/modules/main/wallet_section/controller.nim index f0fe98eda3..59e11a1b91 100644 --- a/src/app/modules/main/wallet_section/controller.nim +++ b/src/app/modules/main/wallet_section/controller.nim @@ -1,22 +1,29 @@ import io_interface import ../../../../app_service/service/settings/service as settings_service import ../../../../app_service/service/wallet_account/service as wallet_account_service +import ../../../../app_service/service/currency/service as currency_service + +import ../../shared_models/currency_amount +import ../../shared_models/currency_amount_utils type Controller* = ref object of RootObj delegate: io_interface.AccessInterface settingsService: settings_service.Service walletAccountService: wallet_account_service.Service + currencyService: currency_service.Service proc newController*( delegate: io_interface.AccessInterface, settingsService: settings_service.Service, walletAccountService: wallet_account_service.Service, + currencyService: currency_service.Service, ): Controller = result = Controller() result.delegate = delegate result.settingsService = settingsService result.walletAccountService = walletAccountService + result.currencyService = currencyService proc delete*(self: Controller) = discard @@ -33,8 +40,8 @@ proc getSigningPhrase*(self: Controller): string = proc isMnemonicBackedUp*(self: Controller): bool = return self.settingsService.getMnemonic().len > 0 -proc getCurrencyBalance*(self: Controller): float64 = - return self.walletAccountService.getTotalCurrencyBalance() +proc getCurrencyBalance*(self: Controller): CurrencyAmount = + return currencyAmountToItem(self.walletAccountService.getTotalCurrencyBalance(), self.currencyService.getCurrencyFormat(self.getCurrency())) proc updateCurrency*(self: Controller, currency: string) = self.walletAccountService.updateCurrency(currency) diff --git a/src/app/modules/main/wallet_section/current_account/controller.nim b/src/app/modules/main/wallet_section/current_account/controller.nim index 7c710ff3a7..668fc10fc8 100644 --- a/src/app/modules/main/wallet_section/current_account/controller.nim +++ b/src/app/modules/main/wallet_section/current_account/controller.nim @@ -1,24 +1,31 @@ -import sugar, sequtils +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 ../../../../../app_service/service/token/service as token_service +import ../../../../../app_service/service/currency/service as currency_service type Controller* = ref object of RootObj delegate: io_interface.AccessInterface walletAccountService: wallet_account_service.Service networkService: network_service.Service + tokenService: token_service.Service + currencyService: currency_service.Service proc newController*( delegate: io_interface.AccessInterface, walletAccountService: wallet_account_service.Service, networkService: network_service.Service, + tokenService: token_service.Service, + currencyService: currency_service.Service, ): Controller = result = Controller() result.delegate = delegate result.walletAccountService = walletAccountService result.networkService = networkService + result.tokenService = tokenService + result.currencyService = currencyService proc delete*(self: Controller) = discard @@ -39,4 +46,13 @@ proc getChainIds*(self: Controller): seq[int] = return self.networkService.getNetworks().map(n => n.chainId) proc getEnabledChainIds*(self: Controller): seq[int] = - return self.networkService.getNetworks().filter(n => n.enabled).map(n => n.chainId) \ No newline at end of file + return self.networkService.getNetworks().filter(n => n.enabled).map(n => n.chainId) + +proc getCurrentCurrency*(self: Controller): string = + return self.walletAccountService.getCurrency() + +proc getCurrencyFormat*(self: Controller, symbol: string): CurrencyFormatDto = + return self.currencyService.getCurrencyFormat(symbol) + +proc getAllMigratedKeyPairs*(self: Controller): seq[KeyPairDto] = + return self.walletAccountService.getAllMigratedKeyPairs() diff --git a/src/app/modules/main/wallet_section/current_account/module.nim b/src/app/modules/main/wallet_section/current_account/module.nim index bfd6abfd70..cf218620f5 100644 --- a/src/app/modules/main/wallet_section/current_account/module.nim +++ b/src/app/modules/main/wallet_section/current_account/module.nim @@ -1,11 +1,18 @@ -import NimQml, Tables, sequtils +import NimQml, Tables, sequtils, sugar import ../../../../global/global_singleton import ../../../../core/eventemitter +import ../../../../../app_service/service/token/service as token_service +import ../../../../../app_service/service/currency/service as currency_service import ../../../../../app_service/service/wallet_account/service as wallet_account_service import ../../../../../app_service/service/network/service as network_service +import ../../../shared_models/currency_amount_utils import ../../../shared_models/token_model as token_model import ../../../shared_models/token_item as token_item +import ../../../shared_models/token_utils +import ../accounts/compact_item as account_compact_item +import ../accounts/item as account_item +import ../accounts/utils import ./io_interface, ./view, ./controller import ../io_interface as delegate_interface @@ -28,13 +35,15 @@ proc newModule*( events: EventEmitter, walletAccountService: wallet_account_service.Service, networkService: network_service.Service, + tokenService: token_service.Service, + currencyService: currency_service.Service, ): Module = result = Module() result.delegate = delegate result.events = events result.currentAccountIndex = 0 result.view = newView(result) - result.controller = newController(result, walletAccountService, networkService) + result.controller = newController(result, walletAccountService, networkService, tokenService, currencyService) result.moduleLoaded = false method delete*(self: Module) = @@ -71,49 +80,68 @@ method viewDidLoad*(self: Module) = self.delegate.currentAccountModuleDidLoad() proc setAssetsAndBalance(self: Module, tokens: seq[WalletTokenDto]) = - var totalCurrencyBalanceForAllAssets = 0.0 let chainIds = self.controller.getChainIds() let enabledChainIds = self.controller.getEnabledChainIds() - var items: seq[Item] - for t in tokens: - let item = token_item.initItem( - t.name, - t.symbol, - t.getBalance(chainIds), - t.getCurrencyBalance(chainIds), - t.getBalance(enabledChainIds), - t.getCurrencyBalance(enabledChainIds), - t.getVisibleForNetwork(enabledChainIds), - t.getVisibleForNetworkWithPositiveBalance(enabledChainIds), - t.getBalances(enabledChainIds), - t.description, - t.assetWebsiteUrl, - t.builtOn, - t.getAddress(), - t.marketCap, - t.highDay, - t.lowDay, - t.changePctHour, - t.changePctDay, - t.changePct24hour, - t.change24hour, - t.currencyPrice, - t.decimals, - ) - items.add(item) - totalCurrencyBalanceForAllAssets += t.getCurrencyBalance(enabledChainIds) + let currency = self.controller.getCurrentCurrency() + + let currencyFormat = self.controller.getCurrencyFormat(currency) + + let items = tokens.map(t => walletTokenToItem(t, chainIds, enabledChainIds, currency, currencyFormat, self.controller.getCurrencyFormat(t.symbol))) + + let totalCurrencyBalanceForAllAssets = tokens.map(t => t.getCurrencyBalance(enabledChainIds, currency)).foldl(a + b, 0.0) self.view.getAssetsModel().setItems(items) - self.view.setCurrencyBalance(totalCurrencyBalanceForAllAssets) + self.view.setCurrencyBalance(currencyAmountToItem(totalCurrencyBalanceForAllAssets, currencyFormat)) method switchAccount*(self: Module, accountIndex: int) = self.currentAccountIndex = accountIndex - let enabledChainIds = self.controller.getEnabledChainIds() + + let keyPairMigrated = proc(migratedKeyPairs: seq[KeyPairDto], keyUid: string): bool = + for kp in migratedKeyPairs: + if kp.keyUid == keyUid: + return true + return false + + let defaultAccount = self.controller.getWalletAccount(0) # can safely do this as the account will always contain atleast one account let walletAccount = self.controller.getWalletAccount(accountIndex) - # can safely do this as the account will always contain atleast one account - self.view.setDefaultWalletAccount(self.controller.getWalletAccount(0)) - self.view.setData(walletAccount, enabledChainIds) + + let migratedKeyPairs = self.controller.getAllMigratedKeyPairs() + let currency = self.controller.getCurrentCurrency() + + let chainIds = self.controller.getChainIds() + let enabledChainIds = self.controller.getEnabledChainIds() + + let defaultAccountTokenFormats = collect(initTable()): + for t in defaultAccount.tokens: {t.symbol: self.controller.getCurrencyFormat(t.symbol)} + + let accountTokenFormats = collect(initTable()): + for t in walletAccount.tokens: {t.symbol: self.controller.getCurrencyFormat(t.symbol)} + + let currencyFormat = self.controller.getCurrencyFormat(currency) + + let defaultAccountItem = walletAccountToItem( + defaultAccount, + chainIds, + enabledChainIds, + currency, + keyPairMigrated(migratedKeyPairs, defaultAccount.keyUid), + currencyFormat, + defaultAccountTokenFormats + ) + + let accountItem = walletAccountToItem( + walletAccount, + chainIds, + enabledChainIds, + currency, + keyPairMigrated(migratedKeyPairs, walletAccount.keyUid), + currencyFormat, + accountTokenFormats + ) + + self.view.setDefaultWalletAccount(defaultAccountItem) + self.view.setData(accountItem) self.setAssetsAndBalance(walletAccount.tokens) method update*(self: Module, address: string, accountName: string, color: string, emoji: string) = diff --git a/src/app/modules/main/wallet_section/current_account/view.nim b/src/app/modules/main/wallet_section/current_account/view.nim index 44695e7a04..dc1a11eaac 100644 --- a/src/app/modules/main/wallet_section/current_account/view.nim +++ b/src/app/modules/main/wallet_section/current_account/view.nim @@ -1,12 +1,14 @@ -import NimQml, sequtils, sugar +import NimQml, sequtils, sugar, json -import ../../../../../app_service/service/wallet_account/service as wallet_account_service import ./io_interface import ../../../shared_models/token_model as token_model import ../../../shared_models/token_item as token_item +import ../../../shared_models/currency_amount import ../accounts/compact_model import ../accounts/compact_item +import ../accounts/item as account_item + const GENERATED = "generated" const GENERATED_FROM_IMPORTED = "generated from imported accounts" @@ -14,7 +16,7 @@ QtObject: type View* = ref object of QObject delegate: io_interface.AccessInterface - defaultAccount: wallet_account_service.WalletAccountDto + defaultAccount: account_item.Item name: string address: string mixedcaseAddress: string @@ -23,7 +25,7 @@ QtObject: publicKey: string walletType: string isChat: bool - currencyBalance: float64 + currencyBalance: CurrencyAmount assets: token_model.Model emoji: string derivedfrom: string @@ -117,7 +119,7 @@ QtObject: proc currencyBalanceChanged(self: View) {.signal.} proc getCurrencyBalance*(self: View): QVariant {.slot.} = return newQVariant(self.currencyBalance) - proc setCurrencyBalance*(self: View, value: float) = + proc setCurrencyBalance*(self: View, value: CurrencyAmount) = self.currencyBalance = value self.currencyBalanceChanged() QtProperty[QVariant] currencyBalance: @@ -173,66 +175,50 @@ QtObject: proc update(self: View, address: string, accountName: string, color: string, emoji: string) {.slot.} = self.delegate.update(address, accountName, color, emoji) - proc setDefaultWalletAccount*(self: View, default: wallet_account_service.WalletAccountDto) = + proc setDefaultWalletAccount*(self: View, default: account_item.Item) = self.defaultAccount = default - proc setData*(self: View, dto: wallet_account_service.WalletAccountDto, chainIds: seq[int]) = - if(self.name != dto.name): - self.name = dto.name + proc setData*(self: View, item: account_item.Item) = + if(self.name != item.getName()): + self.name = item.getName() self.nameChanged() - if(self.address != dto.address): - self.address = dto.address + if(self.address != item.getAddress()): + self.address = item.getAddress() self.addressChanged() - if(self.mixedcaseAddress != dto.mixedcaseAddress): - self.mixedcaseAddress = dto.mixedcaseAddress + if(self.mixedcaseAddress != item.getMixedCaseAddress()): + self.mixedcaseAddress = item.getMixedCaseAddress() self.mixedcaseAddressChanged() - if(self.path != dto.path): - self.path = dto.path + if(self.path != item.getPath()): + self.path = item.getPath() self.pathChanged() - if(self.color != dto.color): - self.color = dto.color + if(self.color != item.getColor()): + self.color = item.getColor() self.colorChanged() - if(self.publicKey != dto.publicKey): - self.publicKey = dto.publicKey + if(self.publicKey != item.getPublicKey()): + self.publicKey = item.getPublicKey() self.publicKeyChanged() # Check if the account is generated from default wallet account else change wallettype - if dto.walletType == GENERATED and dto.derivedfrom != self.defaultAccount.derivedfrom: + if item.getWalletType() == GENERATED and item.getDerivedfrom() != self.defaultAccount.getDerivedfrom(): self.walletType = GENERATED_FROM_IMPORTED self.walletTypeChanged() else: - if(self.walletType != dto.walletType): - self.walletType = dto.walletType + if(self.walletType != item.getWalletType()): + self.walletType = item.getWalletType() self.walletTypeChanged() - if(self.isChat != dto.isChat): - self.isChat = dto.isChat + if(self.isChat != item.getIsChat()): + self.isChat = item.getIsChat() self.isChatChanged() - if(self.emoji != dto.emoji): - self.emoji = dto.emoji + if(self.emoji != item.getEmoji()): + self.emoji = item.getEmoji() self.emojiChanged() - if(self.derivedfrom != dto.derivedfrom): - self.derivedfrom = dto.derivedfrom + if(self.derivedfrom != item.getDerivedFrom()): + self.derivedfrom = item.getDerivedFrom() self.derivedfromChanged() - if(self.ens != dto.ens): - self.ens = dto.ens + if(self.ens != item.getEns()): + self.ens = item.getEns() self.ensChanged() # Set related accounts - let relatedAccounts = compact_model.newModel() - relatedAccounts.setItems( - dto.relatedAccounts.map(x => compact_item.initItem( - x.name, - x.address, - x.path, - x.color, - x.publicKey, - x.walletType, - x.isWallet, - x.isChat, - x.getCurrencyBalance(chainIds), - x.emoji, - x.derivedfrom - )) - ) - self.relatedAccounts = relatedAccounts + self.relatedAccounts = item.getRelatedAccounts() self.relatedAccountsChanged() proc findTokenSymbolByAddress*(self: View, address: string): string {.slot.} = @@ -241,5 +227,9 @@ QtObject: proc hasGas*(self: View, chainId: int, nativeGasSymbol: string, requiredGas: float): bool {.slot.} = return self.assets.hasGas(chainId, nativeGasSymbol, requiredGas) - proc getTokenBalanceOnChain*(self: View, chainId: int, tokenSymbol: string): string {.slot.} = - return self.assets.getTokenBalanceOnChain(chainId, tokenSymbol) + # Returning a QVariant from a slot with parameters other than "self" won't compile + #proc getTokenBalanceOnChain*(self: View, chainId: int, tokenSymbol: string): QVariant {.slot.} = + # return newQVariant(self.assets.getTokenBalanceOnChain(chainId, tokenSymbol)) + + proc getTokenBalanceOnChainAsJson*(self: View, chainId: int, tokenSymbol: string): string {.slot.} = + return $self.assets.getTokenBalanceOnChain(chainId, tokenSymbol).toJsonNode() diff --git a/src/app/modules/main/wallet_section/module.nim b/src/app/modules/main/wallet_section/module.nim index 62d0ec40f3..cbed724b1e 100644 --- a/src/app/modules/main/wallet_section/module.nim +++ b/src/app/modules/main/wallet_section/module.nim @@ -16,6 +16,7 @@ import ../../../global/global_singleton import ../../../core/eventemitter import ../../../../app_service/service/keycard/service as keycard_service import ../../../../app_service/service/token/service as token_service +import ../../../../app_service/service/currency/service as currency_service import ../../../../app_service/service/transaction/service as transaction_service import ../../../../app_service/service/collectible/service as collectible_service import ../../../../app_service/service/wallet_account/service as wallet_account_service @@ -48,6 +49,7 @@ proc newModule*( delegate: delegate_interface.AccessInterface, events: EventEmitter, tokenService: token_service.Service, + currencyService: currency_service.Service, transactionService: transaction_service.Service, collectibleService: collectible_service.Service, walletAccountService: wallet_account_service.Service, @@ -61,13 +63,13 @@ proc newModule*( result.delegate = delegate result.events = events result.moduleLoaded = false - result.controller = newController(result, settingsService, walletAccountService) + result.controller = newController(result, settingsService, walletAccountService, currencyService) result.view = newView(result) - result.accountsModule = accounts_module.newModule(result, events, keycardService, walletAccountService, accountsService, networkService) + result.accountsModule = accounts_module.newModule(result, events, keycardService, walletAccountService, accountsService, networkService, tokenService, currencyService) result.allTokensModule = all_tokens_module.newModule(result, events, tokenService, walletAccountService) result.collectiblesModule = collectibles_module.newModule(result, events, collectibleService, walletAccountService, networkService) - result.currentAccountModule = current_account_module.newModule(result, events, walletAccountService, networkService) + result.currentAccountModule = current_account_module.newModule(result, events, walletAccountService, networkService, tokenService, currencyService) result.transactionsModule = transactions_module.newModule(result, events, transactionService, walletAccountService, networkService) result.savedAddressesModule = saved_addresses_module.newModule(result, events, savedAddressService) result.buySellCryptoModule = buy_sell_crypto_module.newModule(result, events, transactionService) diff --git a/src/app/modules/main/wallet_section/view.nim b/src/app/modules/main/wallet_section/view.nim index 4883c0a486..b9f91dd723 100644 --- a/src/app/modules/main/wallet_section/view.nim +++ b/src/app/modules/main/wallet_section/view.nim @@ -1,13 +1,14 @@ import NimQml import ./io_interface +import ../../shared_models/currency_amount QtObject: type View* = ref object of QObject delegate: io_interface.AccessInterface currentCurrency: string - totalCurrencyBalance: float64 + totalCurrencyBalance: CurrencyAmount signingPhrase: string isMnemonicBackedUp: bool @@ -66,7 +67,7 @@ QtObject: proc switchAccountByAddress(self: View, address: string) {.slot.} = self.delegate.switchAccountByAddress(address) - proc setTotalCurrencyBalance*(self: View, totalCurrencyBalance: float64) = + proc setTotalCurrencyBalance*(self: View, totalCurrencyBalance: CurrencyAmount) = self.totalCurrencyBalance = totalCurrencyBalance self.totalCurrencyBalanceChanged() diff --git a/src/app/modules/shared_models/balance_item.nim b/src/app/modules/shared_models/balance_item.nim new file mode 100644 index 0000000000..11220dbc30 --- /dev/null +++ b/src/app/modules/shared_models/balance_item.nim @@ -0,0 +1,42 @@ +import strformat + +import ./currency_amount + +type + Item* = object + balance*: CurrencyAmount + address*: string + chainId*: int + +proc initItem*( + balance: CurrencyAmount, + address: string, + chainId: int, +): Item = + result.balance = balance + result.address = address + result.chainId = chainId + +proc `$`*(self: Item): string = + result = fmt"""BalanceItem( + name: {self.balance}, + address: {self.address}, + chainId: {self.chainId}, + ]""" + +proc getBalance*(self: Item): CurrencyAmount = + return self.balance + +proc getAddress*(self: Item): string = + return self.address + +proc getChainId*(self: Item): int = + return self.chainId + +proc getCurrencyBalance*(self: Item, currencyPrice: CurrencyAmount): CurrencyAmount = + return newCurrencyAmount( + self.balance.getAmount() * currencyPrice.getAmount(), + currencyPrice.getSymbol(), + currencyPrice.getDisplayDecimals(), + currencyPrice.isStripTrailingZeroesActive() + ) diff --git a/src/app/modules/shared_models/balance_model.nim b/src/app/modules/shared_models/balance_model.nim index fe52e84d12..25021167f9 100644 --- a/src/app/modules/shared_models/balance_model.nim +++ b/src/app/modules/shared_models/balance_model.nim @@ -1,6 +1,7 @@ import NimQml, Tables, strutils, strformat -import ../../../app_service/service/wallet_account/dto +import balance_item +import ./currency_amount type ModelRole {.pure.} = enum @@ -11,7 +12,7 @@ type QtObject: type BalanceModel* = ref object of QAbstractListModel - items*: seq[BalanceDto] + items*: seq[Item] proc delete(self: BalanceModel) = self.items = @[] @@ -63,7 +64,7 @@ QtObject: of ModelRole.Address: result = newQVariant(item.address) of ModelRole.Balance: - result = newQVariant($item.balance) + result = newQVariant(item.balance) proc rowData(self: BalanceModel, index: int, column: string): string {.slot.} = if (index >= self.items.len): @@ -74,7 +75,7 @@ QtObject: of "address": result = $item.address of "balance": result = $item.balance - proc setItems*(self: BalanceModel, items: seq[BalanceDto]) = + proc setItems*(self: BalanceModel, items: seq[Item]) = self.beginResetModel() self.items = items self.endResetModel() diff --git a/src/app/modules/shared_models/balance_utils.nim b/src/app/modules/shared_models/balance_utils.nim new file mode 100644 index 0000000000..a6a5eb813e --- /dev/null +++ b/src/app/modules/shared_models/balance_utils.nim @@ -0,0 +1,11 @@ +import ../../../app_service/service/wallet_account/dto +import ../../../app_service/service/currency/dto as currency_dto +import ./currency_amount_utils +import ./balance_item + +proc balanceToItem*(b: BalanceDto, format: CurrencyFormatDto) : Item = + return initItem( + currencyAmountToItem(b.balance, format), + b.address, + b.chainId + ) diff --git a/src/app/modules/shared_models/currency_amount.nim b/src/app/modules/shared_models/currency_amount.nim new file mode 100644 index 0000000000..e550092272 --- /dev/null +++ b/src/app/modules/shared_models/currency_amount.nim @@ -0,0 +1,67 @@ +import NimQml, strformat, json + +QtObject: + type CurrencyAmount* = ref object of QObject + amount: float64 + symbol: string + displayDecimals: int + stripTrailingZeroes: bool + + proc setup(self: CurrencyAmount) = + self.QObject.setup + + proc delete*(self: CurrencyAmount) = + self.QObject.delete + + proc newCurrencyAmount*( + amount: float64, + symbol: string, + displayDecimals: int, + stripTrailingZeroes: bool, + ): CurrencyAmount = + new(result, delete) + result.setup + result.amount = amount + result.symbol = symbol + result.displayDecimals = displayDecimals + result.stripTrailingZeroes = stripTrailingZeroes + + proc `$`*(self: CurrencyAmount): string = + result = fmt"""CurrencyAmount( + amount: {self.amount}, + symbol: {self.symbol}, + displayDecimals: {self.displayDecimals}, + stripTrailingZeroes: {self.stripTrailingZeroes} + )""" + + proc getAmount*(self: CurrencyAmount): float64 = + return self.amount + + proc getAmountFloat*(self: CurrencyAmount): float {.slot.} = + return self.amount + QtProperty[float] amount: + read = getAmountFloat + + proc getSymbol*(self: CurrencyAmount): string {.slot.} = + return self.symbol + QtProperty[string] symbol: + read = getSymbol + + proc getDisplayDecimals*(self: CurrencyAmount): int {.slot.} = + return self.displayDecimals + QtProperty[int] displayDecimals: + read = getDisplayDecimals + + proc isStripTrailingZeroesActive*(self: CurrencyAmount): bool {.slot.} = + return self.stripTrailingZeroes + QtProperty[bool] stripTrailingZeroes: + read = isStripTrailingZeroesActive + + # Needed to expose object to QML, see issue #8913 + proc toJsonNode*(self: CurrencyAmount): JsonNode = + result = %* { + "amount": self.amount, + "symbol": self.symbol, + "displayDecimals": self.displayDecimals, + "stripTrailingZeroes": self.stripTrailingZeroes + } diff --git a/src/app/modules/shared_models/currency_amount_utils.nim b/src/app/modules/shared_models/currency_amount_utils.nim new file mode 100644 index 0000000000..befa27e45d --- /dev/null +++ b/src/app/modules/shared_models/currency_amount_utils.nim @@ -0,0 +1,10 @@ +import ../../../app_service/service/currency/dto +import ./currency_amount + +proc currencyAmountToItem*(amount: float64, format: CurrencyFormatDto) : CurrencyAmount = + return newCurrencyAmount( + amount, + format.symbol, + format.displayDecimals, + format.stripTrailingZeroes + ) diff --git a/src/app/modules/shared_models/token_item.nim b/src/app/modules/shared_models/token_item.nim index b81cb69dc2..cc046a2f55 100644 --- a/src/app/modules/shared_models/token_item.nim +++ b/src/app/modules/shared_models/token_item.nim @@ -1,16 +1,18 @@ import strformat import ../../../app_service/service/wallet_account/dto +import ./balance_item as balance_item import ./balance_model as balance_model +import ./currency_amount type Item* = object name: string symbol: string - totalBalance: float - totalCurrencyBalance: float - enabledNetworkCurrencyBalance: float - enabledNetworkBalance: float + totalBalance: CurrencyAmount + totalCurrencyBalance: CurrencyAmount + enabledNetworkCurrencyBalance: CurrencyAmount + enabledNetworkBalance: CurrencyAmount visibleForNetwork: bool visibleForNetworkWithPositiveBalance: bool balances: balance_model.BalanceModel @@ -18,38 +20,40 @@ type assetWebsiteUrl: string builtOn: string address: string - marketCap: string - highDay: string - lowDay: string - changePctHour: string - changePctDay: string - changePct24hour: string - change24hour: string - currencyPrice: float + marketCap: CurrencyAmount + highDay: CurrencyAmount + lowDay: CurrencyAmount + changePctHour: float64 + changePctDay: float64 + changePct24hour: float64 + change24hour: float64 + currencyPrice: CurrencyAmount decimals: int + pegSymbol: string proc initItem*( name, symbol: string, - totalBalance: float, - totalCurrencyBalance: float, - enabledNetworkBalance: float, - enabledNetworkCurrencyBalance: float, + totalBalance: CurrencyAmount, + totalCurrencyBalance: CurrencyAmount, + enabledNetworkBalance: CurrencyAmount, + enabledNetworkCurrencyBalance: CurrencyAmount, visibleForNetwork: bool, visibleForNetworkWithPositiveBalance: bool, - balances: seq[BalanceDto], + balances: seq[balance_item.Item], description: string, assetWebsiteUrl: string, builtOn: string, address: string, - marketCap: string, - highDay: string, - lowDay: string, - changePctHour: string, - changePctDay: string, - changePct24hour: string, - change24hour: string, - currencyPrice: float, + marketCap: CurrencyAmount, + highDay: CurrencyAmount, + lowDay: CurrencyAmount, + changePctHour: float64, + changePctDay: float64, + changePct24hour: float64, + change24hour: float64, + currencyPrice: CurrencyAmount, decimals: int, + pegSymbol: string, ): Item = result.name = name result.symbol = symbol @@ -74,6 +78,7 @@ proc initItem*( result.change24hour = change24hour result.currencyPrice = currencyPrice result.decimals = decimals + result.pegSymbol = pegSymbol proc `$`*(self: Item): string = result = fmt"""AllTokensItem( @@ -98,6 +103,7 @@ proc `$`*(self: Item): string = change24hour: {self.change24hour}, currencyPrice: {self.currencyPrice}, decimals: {self.decimals}, + pegSymbol: {self.pegSymbol}, ]""" proc getName*(self: Item): string = @@ -106,16 +112,16 @@ proc getName*(self: Item): string = proc getSymbol*(self: Item): string = return self.symbol -proc getTotalBalance*(self: Item): float = +proc getTotalBalance*(self: Item): CurrencyAmount = return self.totalBalance -proc getTotalCurrencyBalance*(self: Item): float = +proc getTotalCurrencyBalance*(self: Item): CurrencyAmount = return self.totalCurrencyBalance -proc getEnabledNetworkBalance*(self: Item): float = +proc getEnabledNetworkBalance*(self: Item): CurrencyAmount = return self.enabledNetworkBalance -proc getEnabledNetworkCurrencyBalance*(self: Item): float = +proc getEnabledNetworkCurrencyBalance*(self: Item): CurrencyAmount = return self.enabledNetworkCurrencyBalance proc getVisibleForNetwork*(self: Item): bool = @@ -139,29 +145,32 @@ proc getBuiltOn*(self: Item): string = proc getAddress*(self: Item): string = return self.address -proc getMarketCap*(self: Item): string = +proc getMarketCap*(self: Item): CurrencyAmount = return self.marketCap -proc getHighDay*(self: Item): string = +proc getHighDay*(self: Item): CurrencyAmount = return self.highDay -proc getLowDay*(self: Item): string = +proc getLowDay*(self: Item): CurrencyAmount = return self.lowDay -proc getChangePctHour*(self: Item): string = +proc getChangePctHour*(self: Item): float64 = return self.changePctHour -proc getChangePctDay*(self: Item): string = +proc getChangePctDay*(self: Item): float64 = return self.changePctDay -proc getChangePct24hour*(self: Item): string = +proc getChangePct24hour*(self: Item): float64 = return self.changePct24hour -proc getChange24hour*(self: Item): string = +proc getChange24hour*(self: Item): float64 = return self.change24hour -proc getCurrencyPrice*(self: Item): float = +proc getCurrencyPrice*(self: Item): CurrencyAmount = return self.currencyPrice proc getDecimals*(self: Item): int = return self.decimals + +proc getPegSymbol*(self: Item): string = + return self.pegSymbol diff --git a/src/app/modules/shared_models/token_model.nim b/src/app/modules/shared_models/token_model.nim index 94f6caf44a..c15fc6863e 100644 --- a/src/app/modules/shared_models/token_model.nim +++ b/src/app/modules/shared_models/token_model.nim @@ -1,6 +1,7 @@ import NimQml, Tables, strutils, strformat import ./token_item +import ./currency_amount type ModelRole {.pure.} = enum @@ -26,6 +27,7 @@ type Change24hour CurrencyPrice Decimals + PegSymbol QtObject: type @@ -83,6 +85,7 @@ QtObject: ModelRole.Change24hour.int:"change24hour", ModelRole.CurrencyPrice.int:"currencyPrice", ModelRole.Decimals.int:"decimals", + ModelRole.PegSymbol.int:"pegSymbol", }.toTable method data(self: Model, index: QModelIndex, role: int): QVariant = @@ -140,6 +143,8 @@ QtObject: result = newQVariant(item.getCurrencyPrice()) of ModelRole.Decimals: result = newQVariant(item.getDecimals()) + of ModelRole.PegSymbol: + result = newQVariant(item.getPegSymbol()) proc rowData(self: Model, index: int, column: string): string {.slot.} = if (index >= self.items.len): @@ -167,6 +172,7 @@ QtObject: of "change24hour": result = $item.getChange24hour() of "currencyPrice": result = $item.getCurrencyPrice() of "decimals": result = $item.getDecimals() + of "pegSymbol": result = $item.getPegSymbol() proc setItems*(self: Model, items: seq[Item]) = self.beginResetModel() @@ -191,13 +197,12 @@ QtObject: if (balance.chainId != chainId): continue - if(balance.balance >= requiredGas): + if(balance.balance.getAmount() >= requiredGas): return true return false - proc getTokenBalanceOnChain*(self: Model, chainId: int, tokenSymbol: string): string = - var tokenBalance: float64 = 0.0 + proc getTokenBalanceOnChain*(self: Model, chainId: int, tokenSymbol: string): CurrencyAmount = for item in self.items: if(item.getSymbol() != tokenSymbol): continue @@ -206,6 +211,11 @@ QtObject: if (balance.chainId != chainId): continue - tokenBalance = balance.balance + return balance.balance - return $tokenBalance + return newCurrencyAmount( + 0.0, + tokenSymbol, + 0, + false + ) diff --git a/src/app/modules/shared_models/token_utils.nim b/src/app/modules/shared_models/token_utils.nim new file mode 100644 index 0000000000..57b60e11c8 --- /dev/null +++ b/src/app/modules/shared_models/token_utils.nim @@ -0,0 +1,42 @@ +import tables, sequtils, sugar +import ../../../app_service/service/wallet_account/dto +import ../../../app_service/service/currency/dto as currency_dto +import ./currency_amount_utils +import ./balance_utils +import ./token_item + +proc walletTokenToItem*( + t: WalletTokenDto, + chainIds: seq[int], + enabledChainIds: seq[int], + currency: string, + currencyFormat: CurrencyFormatDto, + tokenFormat: CurrencyFormatDto, + ) : token_item.Item = + let marketValues = t.marketValuesPerCurrency.getOrDefault(currency) + + return token_item.initItem( + t.name, + t.symbol, + currencyAmountToItem(t.getBalance(chainIds), tokenFormat), + currencyAmountToItem(t.getCurrencyBalance(chainIds, currency), currencyFormat), + currencyAmountToItem(t.getBalance(enabledChainIds), tokenFormat), + currencyAmountToItem(t.getCurrencyBalance(enabledChainIds, currency), currencyFormat), + t.getVisibleForNetwork(enabledChainIds), + t.getVisibleForNetworkWithPositiveBalance(enabledChainIds), + t.getBalances(enabledChainIds).map(b => balanceToItem(b, tokenFormat)), + t.description, + t.assetWebsiteUrl, + t.builtOn, + t.getAddress(), + currencyAmountToItem(marketValues.marketCap, currencyFormat), + currencyAmountToItem(marketValues.highDay, currencyFormat), + currencyAmountToItem(marketValues.lowDay, currencyFormat), + marketValues.changePctHour, + marketValues.changePctDay, + marketValues.changePct24hour, + marketValues.change24hour, + currencyAmountToItem(marketValues.price, currencyFormat), + t.decimals, + t.pegSymbol + ) diff --git a/src/app_service/service/currency/dto.nim b/src/app_service/service/currency/dto.nim new file mode 100644 index 0000000000..e7daa0ebca --- /dev/null +++ b/src/app_service/service/currency/dto.nim @@ -0,0 +1,6 @@ + +type + CurrencyFormatDto* = ref object + symbol*: string + displayDecimals*: int + stripTrailingZeroes*: bool diff --git a/src/app_service/service/currency/service.nim b/src/app_service/service/currency/service.nim new file mode 100644 index 0000000000..853f108b11 --- /dev/null +++ b/src/app_service/service/currency/service.nim @@ -0,0 +1,55 @@ +import NimQml, strformat, strutils +import ../settings/service as settings_service +import ../token/service as token_service +import ./dto, ./utils + +export dto + +const DECIMALS_CALCULATION_CURRENCY = "USD" + +QtObject: + type Service* = ref object of QObject + tokenService: token_service.Service + settingsService: settings_service.Service + + proc delete*(self: Service) = + self.QObject.delete + + proc newService*( + tokenService: token_service.Service, + settingsService: settings_service.Service, + ): Service = + new(result, delete) + result.QObject.setup + result.tokenService = tokenService + result.settingsService = settingsService + + proc init*(self: Service) = + discard + + proc getFiatCurrencyFormat(self: Service, symbol: string): CurrencyFormatDto = + return CurrencyFormatDto( + symbol: toUpperAscii(symbol), + displayDecimals: getFiatDisplayDecimals(symbol), + stripTrailingZeroes: false + ) + + proc getTokenCurrencyFormat(self: Service, symbol: string): CurrencyFormatDto = + let pegSymbol = self.tokenService.getTokenPegSymbol(symbol) + if pegSymbol != "": + var currencyFormat = self.getFiatCurrencyFormat(pegSymbol) + currencyFormat.symbol = symbol + return currencyFormat + else: + let price = self.tokenService.getTokenPrice(symbol, DECIMALS_CALCULATION_CURRENCY, false) + return CurrencyFormatDto( + symbol: symbol, + displayDecimals: getTokenDisplayDecimals(price), + stripTrailingZeroes: true + ) + + proc getCurrencyFormat*(self: Service, symbol: string): CurrencyFormatDto = + if isCurrencyFiat(symbol): + return self.getFiatCurrencyFormat(symbol) + else: + return self.getTokenCurrencyFormat(symbol) diff --git a/src/app_service/service/currency/utils.nim b/src/app_service/service/currency/utils.nim new file mode 100644 index 0000000000..3a63396cae --- /dev/null +++ b/src/app_service/service/currency/utils.nim @@ -0,0 +1,33 @@ +import math, chronicles, json, strutils +import ../../../backend/backend as backend + +logScope: + topics = "currency-utils" + +proc isCurrencyFiat*(symbol: string): bool = + let response = backend.isCurrencyFiat(symbol) + return response.result.getBool + +proc getFiatDisplayDecimals*(symbol: string): int = + result = 0 + try: + let response = backend.getFiatCurrencyMinorUnit(symbol) + result = response.result.getInt + except Exception as e: + let errDesription = e.msg + error "error getFiatDisplayDecimals: ", errDesription + +proc getTokenDisplayDecimals*(currencyPrice: float): int = + var decimals = 0.0 + if currencyPrice > 0: + const lowerCurrencyResolution = 0.1 + const higherCurrencyResolution = 0.01 + let lowerDecimalsBound = max(0.0, log10(currencyPrice) - log10(lowerCurrencyResolution)) + let upperDecimalsBound = max(0.0, log10(currencyPrice) - log10(higherCurrencyResolution)) + + # Use as few decimals as needed to ensure lower precision + decimals = ceil(lowerDecimalsBound) + if (decimals + 1 < upperDecimalsBound): + # If allowed by upper bound, ensure resolution changes as soon as currency hits multiple of 10 + decimals += 1 + return decimals.int diff --git a/src/app_service/service/message/service.nim b/src/app_service/service/message/service.nim index 45b1a679fe..5588d66589 100644 --- a/src/app_service/service/message/service.nim +++ b/src/app_service/service/message/service.nim @@ -318,7 +318,7 @@ QtObject: proc getTransactionDetails*(self: Service, message: MessageDto): (string, string) = let networksDto = self.networkService.getNetworks() - var token = newTokenDto(networksDto[0].nativeCurrencyName, networksDto[0].chainId, parseAddress(ZERO_ADDRESS), networksDto[0].nativeCurrencySymbol, networksDto[0].nativeCurrencyDecimals, true) + var token = newTokenDto(networksDto[0].nativeCurrencyName, networksDto[0].chainId, parseAddress(ZERO_ADDRESS), networksDto[0].nativeCurrencySymbol, networksDto[0].nativeCurrencyDecimals, true, "") if message.transactionParameters.contract != "": for networkDto in networksDto: diff --git a/src/app_service/service/token/dto.nim b/src/app_service/service/token/dto.nim index 3ab8fee90d..0bc5060a1d 100644 --- a/src/app_service/service/token/dto.nim +++ b/src/app_service/service/token/dto.nim @@ -23,17 +23,7 @@ type color*: string isCustom* {.dontSerialize.}: bool isVisible* {.dontSerialize.}: bool - description* :string - assetWebsiteUrl*: string - builtOn*: string - smartContractAddress*: string - marketCap*: string - highDay*: string - lowDay*: string - changePctHour*: string - changePctDay*: string - changePct24hour*: string - change24hour*: string + pegSymbol*: string proc newTokenDto*( name: string, @@ -42,18 +32,8 @@ proc newTokenDto*( symbol: string, decimals: int, hasIcon: bool, + pegSymbol: string, isCustom: bool = false, - description: string = "", - assetWebsiteUrl: string = "", - builtOn: string = "", - smartContractAddress: string = "", - marketCap: string = "", - highDay: string = "", - lowDay: string = "", - changePctHour: string = "", - changePctDay: string = "", - changePct24hour: string = "", - change24hour: string = "", ): TokenDto = return TokenDto( name: name, @@ -62,6 +42,7 @@ proc newTokenDto*( symbol: symbol, decimals: decimals, hasIcon: hasIcon, + pegSymbol: pegSymbol, isCustom: isCustom ) @@ -76,6 +57,7 @@ proc toTokenDto*(jsonObj: JsonNode, isVisible: bool, hasIcon: bool = false, isCu discard jsonObj.getProp("symbol", result.symbol) discard jsonObj.getProp("decimals", result.decimals) discard jsonObj.getProp("color", result.color) + discard jsonObj.getProp("pegSymbol", result.pegSymbol) result.isVisible = isVisible proc addressAsString*(self: TokenDto): string = diff --git a/src/app_service/service/token/service.nim b/src/app_service/service/token/service.nim index 755d0a6eb6..38aef871a7 100644 --- a/src/app_service/service/token/service.nim +++ b/src/app_service/service/token/service.nim @@ -6,9 +6,12 @@ from web3/conversions import `$` import ../../../backend/backend as backend import ../network/service as network_service +import ../wallet_account/dto as wallet_account_dto +import ../../../app/global/global_singleton import ../../../app/core/eventemitter import ../../../app/core/tasks/[qt, threadpool] +import ../../../backend/cache import ./dto export dto @@ -36,6 +39,9 @@ QtObject: threadpool: ThreadPool networkService: network_service.Service tokens: Table[int, seq[TokenDto]] + priceCache: TimedCache + + proc updateCachedTokenPrice(self: Service, crypto: string, fiat: string, price: float64) proc delete*(self: Service) = self.QObject.delete @@ -51,6 +57,7 @@ QtObject: result.threadpool = threadpool result.networkService = networkService result.tokens = initTable[int, seq[TokenDto]]() + result.priceCache = newTimedCache() proc init*(self: Service) = try: @@ -63,7 +70,6 @@ QtObject: found = true break - if found: continue @@ -82,6 +88,12 @@ QtObject: error "error: ", errDesription return + proc updateTokenPrices*(self: Service, tokens: seq[WalletTokenDto]) = + # Use data fetched by walletAccountService to update local price cache + for token in tokens: + for currency, marketValues in token.marketValuesPerCurrency: + self.updateCachedTokenPrice(token.symbol, currency, marketValues.price) + proc findTokenBySymbol*(self: Service, network: NetworkDto, symbol: string): TokenDto = try: for token in self.tokens[network.chainId]: @@ -111,6 +123,49 @@ QtObject: return token.symbol return "" +# Token + proc renameSymbol(symbol: string) : string = + return toUpperAscii(symbol) + + proc getTokenPriceCacheKey(crypto: string, fiat: string) : string = + return renameSymbol(crypto) & renameSymbol(fiat) + + proc getTokenPrice*(self: Service, crypto: string, fiat: string, fetchIfNotAvailable: bool = true): float64 = + let cacheKey = getTokenPriceCacheKey(crypto, fiat) + if self.priceCache.isCached(cacheKey) or (self.priceCache.hasKey(cacheKey) and not fetchIfNotAvailable): + return parseFloat(self.priceCache.get(cacheKey)) + elif not fetchIfNotAvailable: + return 0.0 + var prices = initTable[string, Table[string, float]]() + + try: + let cryptoKey = renameSymbol(crypto) + let fiatKey = renameSymbol(fiat) + let response = backend.fetchPrices(@[cryptoKey], @[fiatKey]) + for (symbol, pricePerCurrency) in response.result.pairs: + for (currency, price) in pricePerCurrency.pairs: + prices[symbol][currency] = price.getFloat + + self.updateCachedTokenPrice(cryptoKey, fiatKey, prices[cryptoKey][fiatKey]) + return prices[cryptoKey][fiatKey] + except Exception as e: + let errDesription = e.msg + error "error: ", errDesription + return 0.0 + + proc updateCachedTokenPrice(self: Service, crypto: string, fiat: string, price: float64) = + let cacheKey = getTokenPriceCacheKey(crypto, fiat) + self.priceCache.set(cacheKey, $price) + + proc getTokenPegSymbol*(self: Service, symbol: string): string = + for _, tokens in self.tokens: + for token in tokens: + if token.symbol == symbol: + return token.pegSymbol + + return "" + +# History Data proc tokenHistoricalDataResolved*(self: Service, response: string) {.slot.} = let responseObj = response.parseJson if (responseObj.kind != JObject): @@ -121,17 +176,6 @@ QtObject: result: response )) - proc tokenBalanceHistoryDataResolved*(self: Service, response: string) {.slot.} = - # TODO - let responseObj = response.parseJson - if (responseObj.kind != JObject): - info "blance history response is not a json object" - return - - self.events.emit(SIGNAL_BALANCE_HISTORY_DATA_READY, TokenBalanceHistoryDataArgs( - result: response - )) - proc getHistoricalDataForToken*(self: Service, symbol: string, currency: string, range: int) = let arg = GetTokenHistoricalDataTaskArg( tptr: cast[ByteAddress](getTokenHistoricalDataTask), @@ -143,6 +187,18 @@ QtObject: ) self.threadpool.start(arg) +# Historical Balance + proc tokenBalanceHistoryDataResolved*(self: Service, response: string) {.slot.} = + # TODO + let responseObj = response.parseJson + if (responseObj.kind != JObject): + info "blance history response is not a json object" + return + + self.events.emit(SIGNAL_BALANCE_HISTORY_DATA_READY, TokenBalanceHistoryDataArgs( + result: response + )) + proc fetchHistoricalBalanceForTokenAsJson*(self: Service, address: string, symbol: string, timeInterval: BalanceHistoryTimeInterval) = let networks = self.networkService.getNetworks() for network in networks: diff --git a/src/app_service/service/wallet_account/dto.nim b/src/app_service/service/wallet_account/dto.nim index 1ecc094609..41dc93fbcb 100644 --- a/src/app_service/service/wallet_account/dto.nim +++ b/src/app_service/service/wallet_account/dto.nim @@ -22,6 +22,49 @@ type BalanceDto* = object address*: string chainId*: int +type + TokenMarketValuesDto* = object + marketCap*: float64 + highDay*: float64 + lowDay*: float64 + changePctHour*: float64 + changePctDay*: float64 + changePct24hour*: float64 + change24hour*: float64 + price*: float64 + +proc newTokenMarketValuesDto*( + marketCap: float64, + highDay: float64, + lowDay: float64, + changePctHour: float64, + changePctDay: float64, + changePct24hour: float64, + change24hour: float64, + price: float64 +): TokenMarketValuesDto = + return TokenMarketValuesDto( + marketCap: marketCap, + highDay: highDay, + lowDay: lowDay, + changePctHour: changePctHour, + changePctDay: changePctDay, + changePct24hour: changePct24hour, + change24hour: change24hour, + price: price, + ) + +proc toTokenMarketValuesDto*(jsonObj: JsonNode): TokenMarketValuesDto = + result = TokenMarketValuesDto() + discard jsonObj.getProp("marketCap", result.marketCap) + discard jsonObj.getProp("highDay", result.highDay) + discard jsonObj.getProp("lowDay", result.lowDay) + discard jsonObj.getProp("changePctHour", result.changePctHour) + discard jsonObj.getProp("changePctDay", result.changePctDay) + discard jsonObj.getProp("changePct24hour", result.changePct24hour) + discard jsonObj.getProp("change24hour", result.change24hour) + discard jsonObj.getProp("price", result.price) + type WalletTokenDto* = object name*: string @@ -32,14 +75,8 @@ type description*: string assetWebsiteUrl*: string builtOn*: string - marketCap*: string - highDay*: string - lowDay*: string - changePctHour*: string - changePctDay*: string - changePct24hour*: string - change24hour*: string - currencyPrice*: float64 + pegSymbol*: string + marketValuesPerCurrency*: Table[string, TokenMarketValuesDto] type WalletAccountDto* = ref object of RootObj @@ -123,11 +160,12 @@ proc getBalance*(self: WalletTokenDto, chainIds: seq[int]): float64 = return sum -proc getCurrencyBalance*(self: WalletTokenDto, chainIds: seq[int]): float64 = +proc getCurrencyBalance*(self: WalletTokenDto, chainIds: seq[int], currency: string): float64 = var sum = 0.0 + let price = if self.marketValuesPerCurrency.hasKey(currency): self.marketValuesPerCurrency[currency].price else: 0.0 for chainId in chainIds: if self.balancesPerChain.hasKey(chainId): - sum += self.balancesPerChain[chainId].getCurrencyBalance(self.currencyPrice) + sum += self.balancesPerChain[chainId].getCurrencyBalance(price) return sum @@ -148,8 +186,8 @@ proc getVisibleForNetworkWithPositiveBalance*(self: WalletTokenDto, chainIds: se return false -proc getCurrencyBalance*(self: WalletAccountDto, chainIds: seq[int]): float64 = - return self.tokens.map(t => t.getCurrencyBalance(chainIds)).foldl(a + b, 0.0) +proc getCurrencyBalance*(self: WalletAccountDto, chainIds: seq[int], currency: string): float64 = + return self.tokens.map(t => t.getCurrencyBalance(chainIds, currency)).foldl(a + b, 0.0) proc toBalanceDto*(jsonObj: JsonNode): BalanceDto = result = BalanceDto() @@ -166,14 +204,12 @@ proc toWalletTokenDto*(jsonObj: JsonNode): WalletTokenDto = discard jsonObj.getProp("description", result.description) discard jsonObj.getProp("assetWebsiteUrl", result.assetWebsiteUrl) discard jsonObj.getProp("builtOn", result.builtOn) - discard jsonObj.getProp("marketCap", result.marketCap) - discard jsonObj.getProp("highDay", result.highDay) - discard jsonObj.getProp("lowDay", result.lowDay) - discard jsonObj.getProp("changePctHour", result.changePctHour) - discard jsonObj.getProp("changePctDay", result.changePctDay) - discard jsonObj.getProp("changePct24hour", result.changePct24hour) - discard jsonObj.getProp("change24hour", result.change24hour) - discard jsonObj.getProp("currencyPrice", result.currencyPrice) + discard jsonObj.getProp("pegSymbol", result.pegSymbol) + + var marketValuesPerCurrencyObj: JsonNode + if(jsonObj.getProp("marketValuesPerCurrency", marketValuesPerCurrencyObj)): + for currency, marketValuesObj in marketValuesPerCurrencyObj: + result.marketValuesPerCurrency[currency] = marketValuesObj.toTokenMarketValuesDto() var balancesPerChainObj: JsonNode if(jsonObj.getProp("balancesPerChain", balancesPerChainObj)): diff --git a/src/app_service/service/wallet_account/service.nim b/src/app_service/service/wallet_account/service.nim index bf756014b2..cbeb659330 100644 --- a/src/app_service/service/wallet_account/service.nim +++ b/src/app_service/service/wallet_account/service.nim @@ -17,7 +17,6 @@ import ../../../backend/accounts as status_go_accounts import ../../../backend/backend as backend import ../../../backend/eth as status_go_eth import ../../../backend/transactions as status_go_transactions -import ../../../backend/cache export dto, derived_address, key_pair_dto @@ -101,7 +100,6 @@ include ../../common/json_utils QtObject: type Service* = ref object of QObject closingApp: bool - ignoreTimeInitiatedTokensBuild: bool events: EventEmitter threadpool: ThreadPool settingsService: settings_service.Service @@ -109,11 +107,7 @@ QtObject: tokenService: token_service.Service networkService: network_service.Service walletAccounts: OrderedTable[string, WalletAccountDto] - timerStartTimeInSeconds: int64 - priceCache: TimedCache processedKeyPair: KeyPairDto - ignoreTimeInitiatedHistoryFetchBuild: bool - isHistoryFetchTimerAlreadyRunning: bool # Forward declaration proc buildAllTokens(self: Service, accounts: seq[string]) @@ -135,7 +129,6 @@ QtObject: new(result, delete) result.QObject.setup result.closingApp = false - result.ignoreTimeInitiatedTokensBuild = false result.events = events result.threadpool = threadpool result.settingsService = settingsService @@ -143,27 +136,6 @@ QtObject: result.tokenService = tokenService result.networkService = networkService result.walletAccounts = initOrderedTable[string, WalletAccountDto]() - result.priceCache = newTimedCache() - result.ignoreTimeInitiatedHistoryFetchBuild = false - result.isHistoryFetchTimerAlreadyRunning = false - - proc getPrice*(self: Service, crypto: string, fiat: string): float64 = - let cacheKey = crypto & fiat - if self.priceCache.isCached(cacheKey): - return parseFloat(self.priceCache.get(cacheKey)) - var prices = initTable[string, float]() - - try: - let response = backend.fetchPrices(@[crypto], fiat) - for (symbol, value) in response.result.pairs: - prices[symbol] = value.getFloat - self.priceCache.set(cacheKey, $value.getFloat) - - return prices[crypto] - except Exception as e: - let errDesription = e.msg - error "error: ", errDesription - return 0.0 proc fetchAccounts*(self: Service): seq[WalletAccountDto] = let response = status_go_accounts.getAccounts() @@ -360,6 +332,9 @@ QtObject: self.events.emit(SIGNAL_WALLET_ACCOUNT_DELETED, AccountDeleted(account: accountDeleted)) + proc getCurrency*(self: Service): string = + return self.settingsService.getCurrency() + proc updateCurrency*(self: Service, newCurrency: string) = discard self.settingsService.saveCurrency(newCurrency) self.buildAllTokens(self.getAddresses()) @@ -472,7 +447,7 @@ QtObject: tokens.sort(priorityTokenCmp) self.walletAccounts[wAddress].tokens = tokens data.accountsTokens[wAddress] = tokens - + self.tokenService.updateTokenPrices(tokens) # For efficiency. Will be removed when token info fetching gets moved to the tokenService self.events.emit(SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT, data) except Exception as e: error "error: ", procName="onAllTokensBuilt", errName = e.name, errDesription = e.msg @@ -494,22 +469,28 @@ QtObject: self.checkRecentHistory() self.startWallet() - proc getNetworkCurrencyBalance*(self: Service, network: NetworkDto): float64 = + proc getCurrentCurrencyIfEmpty(self: Service, currency: string): string = + if currency != "": + return currency + else: + return self.getCurrency() + + proc getNetworkCurrencyBalance*(self: Service, network: NetworkDto, currency: string = ""): float64 = for walletAccount in toSeq(self.walletAccounts.values): - result += walletAccount.getCurrencyBalance(@[network.chainId]) + result += walletAccount.getCurrencyBalance(@[network.chainId], self.getCurrentCurrencyIfEmpty(currency)) proc findTokenSymbolByAddress*(self: Service, address: string): string = return self.tokenService.findTokenSymbolByAddress(address) - proc getCurrencyBalanceForAddress*(self: Service, address: string): float64 = + proc getCurrencyBalanceForAddress*(self: Service, address: string, currency: string = ""): float64 = let chainIds = self.networkService.getNetworks().map(n => n.chainId) if not self.walletAccounts.hasKey(address): return - return self.walletAccounts[address].getCurrencyBalance(chainIds) + return self.walletAccounts[address].getCurrencyBalance(chainIds, self.getCurrentCurrencyIfEmpty(currency)) - proc getTotalCurrencyBalance*(self: Service): float64 = + proc getTotalCurrencyBalance*(self: Service, currency: string = ""): float64 = let chainIds = self.networkService.getNetworks().filter(a => a.enabled).map(a => a.chainId) - return self.getWalletAccounts().map(a => a.getCurrencyBalance(chainIds)).foldl(a + b, 0.0) + return self.getWalletAccounts().map(a => a.getCurrencyBalance(chainIds, self.getCurrentCurrencyIfEmpty(currency))).foldl(a + b, 0.0) proc responseHasNoErrors(self: Service, procName: string, response: RpcResponse[JsonNode]): bool = var errMsg = "" diff --git a/src/backend/backend.nim b/src/backend/backend.nim index 4cbe30da4b..dbfe9e4348 100644 --- a/src/backend/backend.nim +++ b/src/backend/backend.nim @@ -100,7 +100,7 @@ rpc(getTransactionEstimatedTime, "wallet"): rpc(fetchPrices, "wallet"): symbols: seq[string] - currency: string + currencies: seq[string] rpc(generateAccountWithDerivedPath, "accounts"): password: string @@ -219,7 +219,7 @@ rpc(deleteDappPermissionsByNameAndAddress, "permissions"): rpc(fetchMarketValues, "wallet"): symbols: seq[string] - currency: string + currencies: seq[string] rpc(fetchTokenDetails, "wallet"): symbols: seq[string] @@ -277,4 +277,10 @@ rpc(getName, "ens"): rpc(getBalanceHistory, "wallet"): chainId: int address: string - timeInterval: int \ No newline at end of file + timeInterval: int + +rpc(isCurrencyFiat, "wallet"): + code: string + +rpc(getFiatCurrencyMinorUnit, "wallet"): + code: string diff --git a/storybook/pages/CommunitiesPortalLayoutPage.qml b/storybook/pages/CommunitiesPortalLayoutPage.qml index a23f794cb2..651db8f258 100644 --- a/storybook/pages/CommunitiesPortalLayoutPage.qml +++ b/storybook/pages/CommunitiesPortalLayoutPage.qml @@ -23,7 +23,6 @@ SplitView { SplitView.fillHeight: true communitiesStore: CommunitiesStore { - readonly property string locale: "" readonly property int unreadNotificationsCount: 42 readonly property string communityTags: ModelsData.communityTags readonly property var curatedCommunitiesModel: SortFilterProxyModel { diff --git a/ui/StatusQ/src/StatusQ/Components/StatusCommunityCard.qml b/ui/StatusQ/src/StatusQ/Components/StatusCommunityCard.qml index 169e602713..967e5392eb 100644 --- a/ui/StatusQ/src/StatusQ/Components/StatusCommunityCard.qml +++ b/ui/StatusQ/src/StatusQ/Components/StatusCommunityCard.qml @@ -94,11 +94,11 @@ Rectangle { */ property ListModel categories: ListModel {} /*! - \qmlproperty string StatusCommunityCard::locale + \qmlproperty var StatusCommunityCard::locale This property holds the application locale used to give format to members number representation. If not provided, default value is "en". */ - property string locale: "en" + property var locale: Qt.locale("en") /*! \qmlproperty url StatusCommunityCard::banner This property holds the community banner image url. @@ -148,14 +148,14 @@ Rectangle { const ks = 1000 if(number > million) { res = number / million - res = Number(number / million).toLocaleString(Qt.locale(root.locale), 'f', 1) + 'M' + res = Number(number / million).toLocaleString(root.locale, 'f', 1) + 'M' } else if(number > ks) { res = number / ks - res = Number(number / ks).toLocaleString(Qt.locale(root.locale), 'f', 1) + 'K' + res = Number(number / ks).toLocaleString(root.locale, 'f', 1) + 'K' } else - res = Number(number).toLocaleString(Qt.locale(root.locale), 'f', 0) + res = Number(number).toLocaleString(root.locale, 'f', 0) return res } } diff --git a/ui/StatusQ/src/StatusQ/Controls/StatusAccountSelector.qml b/ui/StatusQ/src/StatusQ/Controls/StatusAccountSelector.qml index 875980516b..ed012d2a5a 100644 --- a/ui/StatusQ/src/StatusQ/Controls/StatusAccountSelector.qml +++ b/ui/StatusQ/src/StatusQ/Controls/StatusAccountSelector.qml @@ -47,7 +47,7 @@ Item { if (showBalanceForAssetSymbol == "" || minRequiredAssetBalance == 0 || !assetFound) { return root.isValid } - root.isValid = assetFound.totalBalance >= minRequiredAssetBalance + root.isValid = assetFound.totalBalance.amount >= minRequiredAssetBalance return root.isValid } @@ -88,7 +88,7 @@ Item { if (!assetFound) { return } - txtAssetBalance.text = root.assetBalanceTextFn(assetFound.totalBalance) + txtAssetBalance.text = root.assetBalanceTextFn(assetFound.totalBalance.amount) txtAssetSymbol.text = " " + assetFound.symbol } diff --git a/ui/StatusQ/src/StatusQ/Controls/Validators/StatusFloatValidator.qml b/ui/StatusQ/src/StatusQ/Controls/Validators/StatusFloatValidator.qml index 505ba8184c..ac6889b9aa 100644 --- a/ui/StatusQ/src/StatusQ/Controls/Validators/StatusFloatValidator.qml +++ b/ui/StatusQ/src/StatusQ/Controls/Validators/StatusFloatValidator.qml @@ -33,10 +33,10 @@ StatusValidator { property real bottom: qmlDoubleValidator.bottom /*! - \qmlproperty string StatusFloatValidator::locale - This property holds the name of the locale used to interpret the number. + \qmlproperty var StatusFloatValidator::locale + This property holds the locale used to interpret the number. */ - property string locale + property var locale: Qt.locale() /*! \qmlproperty real StatusFloatValidator::top diff --git a/ui/StatusQ/src/StatusQ/Controls/Validators/StatusIntValidator.qml b/ui/StatusQ/src/StatusQ/Controls/Validators/StatusIntValidator.qml index 4186ffa95e..aca4fd57e6 100644 --- a/ui/StatusQ/src/StatusQ/Controls/Validators/StatusIntValidator.qml +++ b/ui/StatusQ/src/StatusQ/Controls/Validators/StatusIntValidator.qml @@ -35,10 +35,10 @@ StatusValidator { property int bottom /*! - \qmlproperty string StatusIntValidator::locale - This property holds the name of the locale used to interpret the number. + \qmlproperty var StatusIntValidator::locale + This property holds the locale used to interpret the number. */ - property string locale + property var locale: Qt.locale() /*! \qmlproperty string StatusIntValidator::top @@ -48,7 +48,7 @@ StatusValidator { name: "intValidator" errorMessage: qsTr("Please enter a valid numeric value.") - validatorObj: IntValidator { bottom: root.bottom; locale: root.locale; top: root.top } + validatorObj: IntValidator { bottom: root.bottom; locale: root.locale.name; top: root.top } validate: function (t) { // Basic validation management diff --git a/ui/app/AppLayouts/Chat/controls/community/CollectiblesPanel.qml b/ui/app/AppLayouts/Chat/controls/community/CollectiblesPanel.qml index f2bfa59372..13278a7f34 100644 --- a/ui/app/AppLayouts/Chat/controls/community/CollectiblesPanel.qml +++ b/ui/app/AppLayouts/Chat/controls/community/CollectiblesPanel.qml @@ -17,6 +17,7 @@ ColumnLayout { property alias amountText: amountInput.text property alias amount: amountInput.amount readonly property bool amountValid: amountInput.valid && amountInput.text.length > 0 + property var locale signal pickerClicked @@ -66,5 +67,7 @@ ColumnLayout { Layout.topMargin: 8 allowDecimals: false + + locale: root.locale } } diff --git a/ui/app/AppLayouts/Chat/controls/community/HoldingsDropdown.qml b/ui/app/AppLayouts/Chat/controls/community/HoldingsDropdown.qml index 7278c97611..3f75e993be 100644 --- a/ui/app/AppLayouts/Chat/controls/community/HoldingsDropdown.qml +++ b/ui/app/AppLayouts/Chat/controls/community/HoldingsDropdown.qml @@ -291,6 +291,7 @@ StatusDropdown { tokenName: d.defaultTokenNameText amountText: d.tokenAmountText onAmountTextChanged: d.tokenAmountText = amountText + locale: root.store.locale readonly property real effectiveAmount: amountValid ? amount : 0 onEffectiveAmountChanged: root.tokenAmount = effectiveAmount @@ -343,6 +344,7 @@ StatusDropdown { collectibleName: d.defaultCollectibleNameText amountText: d.collectibleAmountText onAmountTextChanged: d.collectibleAmountText = amountText + locale: root.store.locale readonly property real effectiveAmount: amountValid ? amount : 0 onEffectiveAmountChanged: root.collectibleAmount = effectiveAmount diff --git a/ui/app/AppLayouts/Chat/controls/community/TokensPanel.qml b/ui/app/AppLayouts/Chat/controls/community/TokensPanel.qml index 17cf026f80..d41a409071 100644 --- a/ui/app/AppLayouts/Chat/controls/community/TokensPanel.qml +++ b/ui/app/AppLayouts/Chat/controls/community/TokensPanel.qml @@ -15,6 +15,7 @@ ColumnLayout { property alias amountText: amountInput.text property alias amount: amountInput.amount readonly property bool amountValid: amountInput.valid && amountInput.text.length > 0 + property var locale signal pickerClicked @@ -43,5 +44,7 @@ ColumnLayout { Layout.fillWidth: true Layout.topMargin: 8 + + locale: root.locale } } diff --git a/ui/app/AppLayouts/Chat/stores/RootStore.qml b/ui/app/AppLayouts/Chat/stores/RootStore.qml index 33b5e89a5b..c840b608c0 100644 --- a/ui/app/AppLayouts/Chat/stores/RootStore.qml +++ b/ui/app/AppLayouts/Chat/stores/RootStore.qml @@ -7,7 +7,7 @@ import shared.stores 1.0 QtObject { id: root - property string locale: localAppSettings.language + property var locale: Qt.locale(localAppSettings.language) property var contactsStore @@ -464,7 +464,7 @@ QtObject { property var accounts: walletSectionAccounts.model property var currentAccount: walletSectionCurrent property string currentCurrency: walletSection.currentCurrency - property CurrenciesStore currencyStore: CurrenciesStore { } + property CurrenciesStore currencyStore: CurrenciesStore {} property var allNetworks: networksModule.all property var savedAddressesModel: walletSectionSavedAddresses.model diff --git a/ui/app/AppLayouts/CommunitiesPortal/stores/CommunitiesStore.qml b/ui/app/AppLayouts/CommunitiesPortal/stores/CommunitiesStore.qml index 6f86e023a6..f0402953ac 100644 --- a/ui/app/AppLayouts/CommunitiesPortal/stores/CommunitiesStore.qml +++ b/ui/app/AppLayouts/CommunitiesPortal/stores/CommunitiesStore.qml @@ -28,7 +28,7 @@ QtObject { property bool discordImportHasCommunityImage: root.communitiesModuleInst.discordImportHasCommunityImage property var discordImportTasks: root.communitiesModuleInst.discordImportTasks property bool downloadingCommunityHistoryArchives: root.communitiesModuleInst.downloadingCommunityHistoryArchives - property string locale: localAppSettings.language + property var locale: Qt.locale(localAppSettings.language) property var advancedModule: profileSectionModule.advancedModule // TODO: Could the backend provide directly 2 filtered models?? diff --git a/ui/app/AppLayouts/Wallet/panels/WalletHeader.qml b/ui/app/AppLayouts/Wallet/panels/WalletHeader.qml index ae338868b6..80dbd75bb6 100644 --- a/ui/app/AppLayouts/Wallet/panels/WalletHeader.qml +++ b/ui/app/AppLayouts/Wallet/panels/WalletHeader.qml @@ -18,7 +18,7 @@ import "../stores" Item { id: root - property string locale: "" + property var locale property string currency: "" property var currentAccount property var store @@ -46,7 +46,7 @@ Item { font.pixelSize: 28 font.bold: true color: Theme.palette.baseColor1 - text: "%1 %2".arg(Utils.toLocaleString(root.currentAccount.currencyBalance.toFixed(2), root.locale, {"currency": true})).arg(root.currency.toUpperCase()) + text: LocaleUtils.currencyAmountToLocaleString(root.currentAccount.currencyBalance, root.locale) } } diff --git a/ui/app/AppLayouts/Wallet/popups/ReceiveModal.qml b/ui/app/AppLayouts/Wallet/popups/ReceiveModal.qml index d1a96a4198..e1121e4790 100644 --- a/ui/app/AppLayouts/Wallet/popups/ReceiveModal.qml +++ b/ui/app/AppLayouts/Wallet/popups/ReceiveModal.qml @@ -59,6 +59,7 @@ StatusModal { root.selectedAccount = newAccount } showAllWalletTypes: true + locale: RootStore.locale } contentItem: Column { diff --git a/ui/app/AppLayouts/Wallet/stores/RootStore.qml b/ui/app/AppLayouts/Wallet/stores/RootStore.qml index a629d36b32..77fd8992e0 100644 --- a/ui/app/AppLayouts/Wallet/stores/RootStore.qml +++ b/ui/app/AppLayouts/Wallet/stores/RootStore.qml @@ -22,12 +22,12 @@ QtObject { property var generatedAccounts: walletSectionAccounts.generated property var appSettings: localAppSettings property var accountSensitiveSettings: localAccountSensitiveSettings - property string locale: Qt.locale().name + property var locale: Qt.locale(appSettings.language) property bool hideSignPhraseModal: accountSensitiveSettings.hideSignPhraseModal property var currencyStore: SharedStore.RootStore.currencyStore property string currentCurrency: currencyStore.currentCurrency - property string totalCurrencyBalance: walletSection.totalCurrencyBalance + property var totalCurrencyBalance: walletSection.totalCurrencyBalance property string signingPhrase: walletSection.signingPhrase property string mnemonicBackedUp: walletSection.isMnemonicBackedUp diff --git a/ui/app/AppLayouts/Wallet/views/LeftTabView.qml b/ui/app/AppLayouts/Wallet/views/LeftTabView.qml index 53b70df908..400fcfef75 100644 --- a/ui/app/AppLayouts/Wallet/views/LeftTabView.qml +++ b/ui/app/AppLayouts/Wallet/views/LeftTabView.qml @@ -82,7 +82,7 @@ Rectangle { objectName: "walletLeftListAmountValue" color: Style.current.textColor text: { - Utils.toLocaleString(parseFloat(RootStore.totalCurrencyBalance).toFixed(2), localAppSettings.language, {"currency": true}) + " " + RootStore.currentCurrency.toUpperCase() + LocaleUtils.currencyAmountToLocaleString(RootStore.totalCurrencyBalance, RootStore.currencyStore.locale) } selectByMouse: true cursorVisible: true @@ -117,7 +117,7 @@ Rectangle { width: ListView.view.width highlighted: RootStore.currentAccount.name === model.name title: model.name - subTitle: Utils.toLocaleString(model.currencyBalance.toFixed(2), RootStore.locale, {"model.currency": true}) + " " + RootStore.currentCurrency.toUpperCase() + subTitle: LocaleUtils.currencyAmountToLocaleString(model.currencyBalance, RootStore.currencyStore.locale) asset.emoji: !!model.emoji ? model.emoji: "" asset.color: model.color asset.name: !model.emoji ? "filled-account": "" diff --git a/ui/app/AppLayouts/Wallet/views/RightTabView.qml b/ui/app/AppLayouts/Wallet/views/RightTabView.qml index 49600b7263..340170d2bd 100644 --- a/ui/app/AppLayouts/Wallet/views/RightTabView.qml +++ b/ui/app/AppLayouts/Wallet/views/RightTabView.qml @@ -92,6 +92,7 @@ Item { AssetsView { account: RootStore.currentAccount assetDetailsLaunched: stack.currentIndex === 2 + locale: RootStore.locale onAssetClicked: { assetDetailView.token = token stack.currentIndex = 2 diff --git a/ui/app/AppLayouts/stores/RootStore.qml b/ui/app/AppLayouts/stores/RootStore.qml index 4e41f08af7..01c7aa5d5c 100644 --- a/ui/app/AppLayouts/stores/RootStore.qml +++ b/ui/app/AppLayouts/stores/RootStore.qml @@ -7,7 +7,7 @@ import "../Profile/stores" QtObject { id: root - property string locale: localAppSettings.language + property var locale: Qt.locale(localAppSettings.language) property var mainModuleInst: mainModule property var aboutModuleInst: aboutModule diff --git a/ui/imports/shared/controls/AmountInput.qml b/ui/imports/shared/controls/AmountInput.qml index a371fd5a8e..4be074954b 100644 --- a/ui/imports/shared/controls/AmountInput.qml +++ b/ui/imports/shared/controls/AmountInput.qml @@ -10,7 +10,7 @@ Input { id: root property int maximumLength: 10 - property var locale: Qt.locale() + property var locale readonly property alias amount: d.amount readonly property bool valid: validationError.length === 0 @@ -40,7 +40,7 @@ Input { decimals: root.allowDecimals ? 100 : 0 bottom: 0 notation: DoubleValidator.StandardNotation - locale: root.locale.name + locale: root.locale } onTextChanged: { diff --git a/ui/imports/shared/controls/AssetAndAmountInput.qml b/ui/imports/shared/controls/AssetAndAmountInput.qml index 8e7201ff45..d90ac976b5 100644 --- a/ui/imports/shared/controls/AssetAndAmountInput.qml +++ b/ui/imports/shared/controls/AssetAndAmountInput.qml @@ -181,7 +181,7 @@ Item { if (!selectAsset.selectedAsset) { return } - txtBalance.text = Utils.stripTrailingZeros(parseFloat(selectAsset.selectedAsset.balance).toFixed(4)) + txtBalance.text = Utils.stripTrailingZeros(selectAsset.selectedAsset.balance.amount.toFixed(4)) if (inputAmount.text === "" || isNaN(inputAmount.text)) { return } diff --git a/ui/imports/shared/controls/AssetDelegate.qml b/ui/imports/shared/controls/AssetDelegate.qml index 0288ad56e1..38d3d4ba23 100644 --- a/ui/imports/shared/controls/AssetDelegate.qml +++ b/ui/imports/shared/controls/AssetDelegate.qml @@ -10,7 +10,7 @@ Item { id: assetDelegate objectName: symbol - property string locale: "" + property var locale property string currency: "" property string currencySymbol: "" @@ -52,7 +52,7 @@ Item { anchors.leftMargin: Style.current.smallPadding font.pixelSize: 15 color: Style.current.secondaryText - text: qsTr("%1 %2").arg(enabledNetworkBalance.toString()).arg(symbol) + text: LocaleUtils.currencyAmountToLocaleString(enabledNetworkBalance, root.locale) } StyledText { @@ -63,6 +63,6 @@ Item { anchors.rightMargin: 0 font.pixelSize: 15 font.strikeout: false - text: enabledNetworkCurrencyBalance.toLocaleCurrencyString(Qt.locale(), assetDelegate.currencySymbol) + text: LocaleUtils.currencyAmountToLocaleString(enabledNetworkCurrencyBalance, root.locale) } } diff --git a/ui/imports/shared/controls/AssetsDetailsHeader.qml b/ui/imports/shared/controls/AssetsDetailsHeader.qml index e6ccaf1c1f..cc85e7245e 100644 --- a/ui/imports/shared/controls/AssetsDetailsHeader.qml +++ b/ui/imports/shared/controls/AssetsDetailsHeader.qml @@ -22,6 +22,7 @@ Control { } property var getNetworkColor: function(chainId){} property var getNetworkIcon: function(chainId){} + property var formatBalance: function(balance){} topPadding: Style.current.padding @@ -77,7 +78,7 @@ Control { id: chainRepeater model: balances ? balances : null delegate: InformationTag { - tagPrimaryLabel.text: model.balance + tagPrimaryLabel.text: root.formatBalance(model.balance) tagPrimaryLabel.color: root.getNetworkColor(model.chainId) image.source: Style.svg("tiny/%1".arg(root.getNetworkIcon(model.chainId))) } diff --git a/ui/imports/shared/controls/TokenBalancePerChainDelegate.qml b/ui/imports/shared/controls/TokenBalancePerChainDelegate.qml index 90c8ef11a0..d6a14eaa03 100644 --- a/ui/imports/shared/controls/TokenBalancePerChainDelegate.qml +++ b/ui/imports/shared/controls/TokenBalancePerChainDelegate.qml @@ -8,14 +8,14 @@ import utils 1.0 StatusListItem { id: root - property string currentCurrencySymbol + property var locale property var getNetworkIcon: function(chainId){ return "" } signal tokenSelected(var selectedToken) title: name - label: enabledNetworkCurrencyBalance.toLocaleCurrencyString(Qt.locale(), root.currentCurrencySymbol) + label: LocaleUtils.currencyAmountToLocaleString(enabledNetworkCurrencyBalance, root.locale) asset.name: symbol ? Style.png("tokens/" + symbol) : "" asset.isImage: true asset.width: 32 @@ -34,14 +34,14 @@ StatusListItem { width: 16 height: 16 image.source: Style.svg("tiny/%1".arg(root.getNetworkIcon(chainId))) - visible: balance > 0 + visible: balance.amount > 0 } } Component { id: expandedItem StatusListItemTag { height: 16 - title: balance + title: LocaleUtils.currencyAmountToLocaleString(balance, root.locale) titleText.font.pixelSize: 12 closeButtonVisible: false bgColor: "transparent" @@ -49,7 +49,7 @@ StatusListItem { asset.height: 16 asset.isImage: true asset.name: Style.svg("tiny/%1".arg(root.getNetworkIcon(chainId))) - visible: balance > 0 + visible: balance.amount > 0 } } } diff --git a/ui/imports/shared/controls/TokenDelegate.qml b/ui/imports/shared/controls/TokenDelegate.qml index b0ddbb1775..79e447e778 100644 --- a/ui/imports/shared/controls/TokenDelegate.qml +++ b/ui/imports/shared/controls/TokenDelegate.qml @@ -9,10 +9,10 @@ import StatusQ.Core 0.1 import utils 1.0 StatusListItem { - property string currentCurrencySymbol - + id: root + property var locale title: name - subTitle: `${enabledNetworkBalance} ${symbol}` + subTitle: LocaleUtils.currencyAmountToLocaleString(enabledNetworkBalance, root.locale) asset.name: symbol ? Style.png("tokens/" + symbol) : "" asset.isImage: true components: [ @@ -25,7 +25,7 @@ StatusListItem { anchors.right: parent.right font.pixelSize: 15 font.strikeout: false - text: enabledNetworkCurrencyBalance.toLocaleCurrencyString(Qt.locale(), currentCurrencySymbol) + text: LocaleUtils.currencyAmountToLocaleString(enabledNetworkCurrencyBalance, root.locale) } Row { anchors.horizontalCenter: parent.horizontalCenter @@ -35,7 +35,7 @@ StatusListItem { font.pixelSize: 15 font.strikeout: false color: valueColumn.textColor - text: currencyPrice.toLocaleCurrencyString(Qt.locale(), currentCurrencySymbol) + text: LocaleUtils.currencyAmountToLocaleString(currencyPrice, root.locale) } Rectangle { width: 1 @@ -46,7 +46,7 @@ StatusListItem { font.pixelSize: 15 font.strikeout: false color: valueColumn.textColor - text: changePct24hour !== "" ? "%1%".arg(changePct24hour) : "---" + text: changePct24hour !== "" ? changePct24hour.toFixed(2) + "%" : "---" } } } diff --git a/ui/imports/shared/panels/StatusAssetSelector.qml b/ui/imports/shared/panels/StatusAssetSelector.qml index 7589a77350..25f22cac12 100644 --- a/ui/imports/shared/panels/StatusAssetSelector.qml +++ b/ui/imports/shared/panels/StatusAssetSelector.qml @@ -27,6 +27,7 @@ Item { property string userSelectedToken property string currentCurrencySymbol property string placeholderText + property var locale property var tokenAssetSourceFn: function (symbol) { return "" @@ -149,7 +150,7 @@ Item { delegate: TokenBalancePerChainDelegate { objectName: "AssetSelector_ItemDelegate_" + symbol width: comboBox.control.popup.width - currentCurrencySymbol: root.currentCurrencySymbol + locale: root.locale getNetworkIcon: root.getNetworkIcon onTokenSelected: { userSelectedToken = selectedToken.symbol diff --git a/ui/imports/shared/popups/AccountsModalHeader.qml b/ui/imports/shared/popups/AccountsModalHeader.qml index a5fd246b07..b4de66cb2c 100644 --- a/ui/imports/shared/popups/AccountsModalHeader.qml +++ b/ui/imports/shared/popups/AccountsModalHeader.qml @@ -18,6 +18,7 @@ import "../views" StatusFloatingButtonsSelector { id: root + property var locale property var selectedAccount // Expected signature: function(newAccount, newIndex) property var changeSelectedAccount: function(){} @@ -88,7 +89,7 @@ StatusFloatingButtonsSelector { popupMenuDelegate: StatusListItem { implicitWidth: 272 title: name - subTitle: currencyBalance + subTitle: LocaleUtils.currencyAmountToLocaleString(currencyBalance, locale) asset.emoji: !!emoji ? emoji: "" asset.color: model.color asset.name: !emoji ? "filled-account": "" diff --git a/ui/imports/shared/popups/SendModal.qml b/ui/imports/shared/popups/SendModal.qml index 4794c8da15..0e49f78c00 100644 --- a/ui/imports/shared/popups/SendModal.qml +++ b/ui/imports/shared/popups/SendModal.qml @@ -81,9 +81,9 @@ StatusDialog { readonly property int errorType: !amountToSendInput.input.valid ? Constants.SendAmountExceedsBalance : (networkSelector.bestRoutes && networkSelector.bestRoutes.length <= 0 && !!amountToSendInput.input.text && recipientReady && !popup.isLoading) ? Constants.NoRoute : Constants.NoError - readonly property double maxFiatBalance: !!assetSelector.selectedAsset ? (amountToSendInput.cryptoFiatFlipped ? + readonly property var maxFiatBalance: !!assetSelector.selectedAsset ? (amountToSendInput.cryptoFiatFlipped ? assetSelector.selectedAsset.totalCurrencyBalance : - assetSelector.selectedAsset.totalBalance): 0 + assetSelector.selectedAsset.totalBalance): undefined readonly property bool errorMode: popup.isLoading || !recipientReady ? false : errorType !== Constants.NoError || networkSelector.errorMode || isNaN(amountToSendInput.input.text) readonly property bool recipientReady: (isAddressValid || isENSValid) && !recipientSelector.isPending property bool isAddressValid: Utils.isValidAddress(popup.addressText) @@ -239,11 +239,12 @@ StatusDialog { } popup.recalculateRoutesAndFees() } + locale: popup.store.locale } StatusListItemTag { Layout.alignment: Qt.AlignVCenter | Qt.AlignRight Layout.preferredHeight: 22 - title: d.maxFiatBalance > 0 ? qsTr("Max: %1").arg(LocaleUtils.numberToLocaleString(d.maxFiatBalance)) : qsTr("No balances active") + title: d.maxFiatBalance && d.maxFiatBalance.amount > 0 ? qsTr("Max: %1").arg(LocaleUtils.currencyAmountToLocaleString(d.maxFiatBalance, popup.store.locale)) : qsTr("No balances active") closeButtonVisible: false titleText.font.pixelSize: 12 bgColor: amountToSendInput.input.valid ? Theme.palette.primaryColor3 : Theme.palette.dangerColor2 @@ -255,6 +256,7 @@ StatusDialog { AmountToSend { id: amountToSendInput Layout.fillWidth:true + locale: popup.store.locale isBridgeTx: popup.isBridgeTx interactive: popup.interactive selectedAsset: assetSelector.selectedAsset @@ -272,6 +274,7 @@ StatusDialog { id: amountToReceive Layout.alignment: Qt.AlignRight Layout.fillWidth:true + locale: popup.store.locale visible: popup.bestRoutes !== undefined && popup.bestRoutes.length > 0 store: popup.store isLoading: popup.isLoading @@ -287,7 +290,7 @@ StatusDialog { anchors.right: parent.right visible: !assetSelector.selectedAsset assets: popup.selectedAccount && popup.selectedAccount.assets ? popup.selectedAccount.assets : [] - currentCurrencySymbol: RootStore.currencyStore.currentCurrencySymbol + locale: popup.store.locale searchTokenSymbolByAddressFn: function (address) { if(popup.selectedAccount) { return popup.selectedAccount.findTokenSymbolByAddress(address) @@ -400,6 +403,7 @@ StatusDialog { d.waitTimer.restart() } visible: !d.recipientReady && !isBridgeTx && !!assetSelector.selectedAsset + locale: popup.store.locale } NetworkSelector { @@ -445,7 +449,7 @@ StatusDialog { footer: SendModalFooter { nextButtonText: popup.isBridgeTx ? qsTr("Bridge") : qsTr("Send") - maxFiatFees: popup.isLoading ? "..." : "%1 %2".arg(LocaleUtils.numberToLocaleString(d.totalFeesInFiat)).arg(popup.store.currentCurrency.toUpperCase()) + maxFiatFees: popup.isLoading ? "..." : "%1 %2".arg(LocaleUtils.numberToLocaleString(d.totalFeesInFiat, -1, popup.store.locale)).arg(popup.store.currentCurrency.toUpperCase()) totalTimeEstimate: popup.isLoading? "..." : d.totalTimeEstimate pending: d.isPendingTx || popup.isLoading visible: d.recipientReady && !isNaN(amountToSendInput.cryptoValueToSend) && !d.errorMode diff --git a/ui/imports/shared/popups/keycard/helpers/KeyPairUnknownItem.qml b/ui/imports/shared/popups/keycard/helpers/KeyPairUnknownItem.qml index 446bc7ed65..7c9568ba64 100644 --- a/ui/imports/shared/popups/keycard/helpers/KeyPairUnknownItem.qml +++ b/ui/imports/shared/popups/keycard/helpers/KeyPairUnknownItem.qml @@ -131,10 +131,10 @@ Rectangle { text: { if (Global.appMain) { return "%1%2".arg(SharedStore.RootStore.currencyStore.currentCurrencySymbol) - .arg(Utils.toLocaleString(model.account.balance.toFixed(2), appSettings.locale, {"model.account.currency": true})) + .arg(Utils.toLocaleString(parseFloat(model.account.balance.amount).toFixed(2), appSettings.locale, {"model.account.currency": true})) } // without language/model refactor no way to read currency symbol or `appSettings.locale` before user logs in - return "$%1".arg(Utils.toLocaleString(model.account.balance.toFixed(2), localAppSettings.language, {"model.account.currency": true})) + return "$%1".arg(Utils.toLocaleString(parseFloat(model.account.balance.amount).toFixed(2), localAppSettings.language, {"model.account.currency": true})) } wrapMode: Text.WordWrap font.pixelSize: Constants.keycard.general.fontSize2 diff --git a/ui/imports/shared/stores/CurrenciesStore.qml b/ui/imports/shared/stores/CurrenciesStore.qml index 8fc8be0958..a182a046ed 100644 --- a/ui/imports/shared/stores/CurrenciesStore.qml +++ b/ui/imports/shared/stores/CurrenciesStore.qml @@ -4,15 +4,20 @@ import utils 1.0 QtObject { id: root + property var locale: Qt.locale(localAppSettings.language) + property string currentCurrency: walletSection.currentCurrency - property string currentCurrencySymbol: { + property int currentCurrencyModelIndex: { for (var i=0; i lockedItem !== undefined && lockedItem.chainID === model.chainId) !== -1 preCalculatedAdvancedText: { let index = store.lockedInAmounts.findIndex(lockedItem => lockedItem!== undefined && lockedItem.chainID === model.chainId) @@ -106,8 +107,8 @@ Item { } else return LocaleUtils.numberToLocaleString(fromNetwork.amountToSend) } - maxAdvancedValue: parseFloat(tokenBalanceOnChain) - state: tokenBalanceOnChain === 0 || !hasGas ? + maxAdvancedValue: tokenBalanceOnChain.amount + state: tokenBalanceOnChain.amount === 0 || !hasGas ? "unavailable" : (root.errorMode || !advancedInput.valid) && (parseFloat(advancedInputText) !== 0) ? "error" : "default" cardIcon.source: Style.svg(model.iconUrl) diff --git a/ui/imports/shared/views/NetworksSimpleRoutingView.qml b/ui/imports/shared/views/NetworksSimpleRoutingView.qml index e064e786dc..5bdd9df209 100644 --- a/ui/imports/shared/views/NetworksSimpleRoutingView.qml +++ b/ui/imports/shared/views/NetworksSimpleRoutingView.qml @@ -126,8 +126,9 @@ RowLayout { rightPadding: 5 implicitWidth: 150 title: chainName - subTitle: selectedAccount && selectedAccount!== undefined && selectedAsset!== undefined ? - selectedAccount.getTokenBalanceOnChain(chainId, selectedAsset.symbol) : "" + property bool tokenBalanceOnChainValid: selectedAccount && selectedAccount !== undefined && selectedAsset !== undefined + property var tokenBalanceOnChain: tokenBalanceOnChainValid ? root.store.getTokenBalanceOnChain(selectedAccount, chainId, selectedAsset.symbol) : undefined + subTitle: tokenBalanceOnChain ? LocaleUtils.currencyAmountToLocaleString(tokenBalanceOnChain, root.store.locale) : "N/A" statusListItemSubTitle.color: Theme.palette.primaryColor1 asset.width: 32 asset.height: 32 diff --git a/ui/imports/shared/views/TabAddressSelectorView.qml b/ui/imports/shared/views/TabAddressSelectorView.qml index cd461eb72c..0fb62c3225 100644 --- a/ui/imports/shared/views/TabAddressSelectorView.qml +++ b/ui/imports/shared/views/TabAddressSelectorView.qml @@ -22,6 +22,7 @@ Item { implicitHeight: visible ? accountSelectionTabBar.height + stackLayout.height + Style.current.bigPadding: 0 property var store + property var locale signal contactSelected(string address, int type) @@ -152,7 +153,7 @@ Item { objectName: model.name height: visible ? 64 : 0 title: !!model.name ? model.name : "" - subTitle: "%1 %2".arg(LocaleUtils.numberToLocaleString(model.currencyBalance)).arg(store.currentCurrency.toUpperCase()) + subTitle: LocaleUtils.currencyAmountToLocaleString(model.currencyBalance, root.locale) asset.emoji: !!model.emoji ? model.emoji: "" asset.color: model.color asset.name: !model.emoji ? "filled-account": "" diff --git a/ui/imports/shared/views/TokenListView.qml b/ui/imports/shared/views/TokenListView.qml index 5734a1d7a8..f80dec4fa0 100644 --- a/ui/imports/shared/views/TokenListView.qml +++ b/ui/imports/shared/views/TokenListView.qml @@ -14,8 +14,8 @@ import "../controls" Rectangle { id: root + property var locale property var assets: [] - property string currentCurrencySymbol signal tokenSelected(var selectedToken) property var searchTokenSymbolByAddressFn: function (address) { return "" @@ -56,7 +56,7 @@ Rectangle { } delegate: TokenBalancePerChainDelegate { width: ListView.view.width - currentCurrencySymbol: root.currentCurrencySymbol + locale: root.locale getNetworkIcon: root.getNetworkIcon onTokenSelected: root.tokenSelected(selectedToken) } diff --git a/ui/imports/utils/LocaleUtils.qml b/ui/imports/utils/LocaleUtils.qml index 5703b144ab..1b503ab434 100644 --- a/ui/imports/utils/LocaleUtils.qml +++ b/ui/imports/utils/LocaleUtils.qml @@ -11,6 +11,11 @@ QtObject { return num.toString().split('.')[1].length } + function stripTrailingZeroes(numStr, locale) { + let regEx = locale.decimalPoint == "." ? /(\.[0-9]*[1-9])0+$|\.0*$/ : /(\,[0-9]*[1-9])0+$|\,0*$/ + return numStr.replace(regEx, '$1') + } + function numberToLocaleString(num, precision = -1, locale = null) { locale = locale || Qt.locale() @@ -19,4 +24,23 @@ QtObject { return num.toLocaleString(locale, 'f', precision) } + + function currencyAmountToLocaleString(currencyAmount, locale) { + if (!locale) { + console.log("Unspecified locale for: " + JSON.stringify(currencyAmount)) + locale = Qt.locale() + } + if (typeof(currencyAmount) !== "object") { + console.log("Wrong type for currencyAmount: " + JSON.stringify(currencyAmount)) + return NaN + } + var amountStr = numberToLocaleString(currencyAmount.amount, currencyAmount.displayDecimals, locale) + if (currencyAmount.stripTrailingZeroes) { + amountStr = stripTrailingZeroes(amountStr, locale) + } + if (currencyAmount.symbol) { + amountStr = "%1 %2".arg(amountStr).arg(currencyAmount.symbol) + } + return amountStr + } } diff --git a/ui/imports/utils/Utils.qml b/ui/imports/utils/Utils.qml index 11185483cb..cfa5f06a1a 100644 --- a/ui/imports/utils/Utils.qml +++ b/ui/imports/utils/Utils.qml @@ -88,7 +88,11 @@ QtObject { } function toLocaleString(val, locale, options) { - return NumberPolyFill.toLocaleString(val, locale, options) + if (typeof(val) === "object") { + console.log("Wrong type for val: " + JSON.stringify(val)) + return NaN + } + return NumberPolyFill.toLocaleString(val, locale, options) } function isOnlyEmoji(inputText) { diff --git a/vendor/status-go b/vendor/status-go index 883f9677c5..77a56e2b93 160000 --- a/vendor/status-go +++ b/vendor/status-go @@ -1 +1 @@ -Subproject commit 883f9677c593494eceadef004932c0434575a5be +Subproject commit 77a56e2b930c52709d462aa5add764014cc16a09