From 50b6b59abfcab5ed084c7177126d03a538f7494e Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Fri, 13 Aug 2021 16:04:04 -0400 Subject: [PATCH] feat: add wallet v2 tab --- src/app/wallet/{ => v1}/core.nim | 10 +- src/app/wallet/{ => v1}/view.nim | 2 +- .../wallet/{ => v1}/views/account_item.nim | 2 +- .../wallet/{ => v1}/views/account_list.nim | 2 +- src/app/wallet/{ => v1}/views/accounts.nim | 6 +- src/app/wallet/{ => v1}/views/asset_list.nim | 2 +- src/app/wallet/{ => v1}/views/balance.nim | 6 +- .../wallet/{ => v1}/views/collectibles.nim | 6 +- .../{ => v1}/views/collectibles_list.nim | 2 +- .../wallet/{ => v1}/views/dapp_browser.nim | 2 +- src/app/wallet/{ => v1}/views/ens.nim | 6 +- src/app/wallet/{ => v1}/views/gas.nim | 4 +- src/app/wallet/{ => v1}/views/history.nim | 6 +- src/app/wallet/{ => v1}/views/token_list.nim | 4 +- src/app/wallet/{ => v1}/views/tokens.nim | 2 +- .../{ => v1}/views/transaction_list.nim | 2 +- .../wallet/{ => v1}/views/transactions.nim | 6 +- src/app/wallet/{ => v1}/views/utils.nim | 0 src/app/wallet/v2/core.nim | 44 ++++ src/app/wallet/v2/view.nim | 45 ++++ src/app/wallet/v2/views/account_item.nim | 58 +++++ src/app/wallet/v2/views/account_list.nim | 105 +++++++++ src/app/wallet/v2/views/accounts.nim | 140 +++++++++++ src/nim_status_client.nim | 9 +- .../Profile/Sections/AdvancedContainer.qml | 14 ++ ui/app/AppLayouts/WalletV2/LeftTab.qml | 221 ++++++++++++++++++ .../WalletV2/SeedPhraseBackupWarning.qml | 92 ++++++++ .../AppLayouts/WalletV2/SignPhraseModal.qml | 110 +++++++++ ui/app/AppLayouts/WalletV2/WalletHeader.qml | 149 ++++++++++++ ui/app/AppLayouts/WalletV2/WalletV2Layout.qml | 86 +++++++ .../WalletV2/components/AddAccount.qml | 75 ++++++ .../WalletV2/components/HeaderButton.qml | 65 ++++++ ui/app/AppLayouts/WalletV2/components/qmldir | 9 + ui/app/AppLayouts/WalletV2/qmldir | 4 + ui/app/AppMain.qml | 22 ++ ui/imports/Constants.qml | 1 + ui/imports/Utils.qml | 1 + 37 files changed, 1284 insertions(+), 36 deletions(-) rename src/app/wallet/{ => v1}/core.nim (92%) rename src/app/wallet/{ => v1}/view.nim (99%) rename src/app/wallet/{ => v1}/views/account_item.nim (97%) rename src/app/wallet/{ => v1}/views/account_list.nim (98%) rename src/app/wallet/{ => v1}/views/accounts.nim (97%) rename src/app/wallet/{ => v1}/views/asset_list.nim (98%) rename src/app/wallet/{ => v1}/views/balance.nim (97%) rename src/app/wallet/{ => v1}/views/collectibles.nim (96%) rename src/app/wallet/{ => v1}/views/collectibles_list.nim (98%) rename src/app/wallet/{ => v1}/views/dapp_browser.nim (96%) rename src/app/wallet/{ => v1}/views/ens.nim (92%) rename src/app/wallet/{ => v1}/views/gas.nim (97%) rename src/app/wallet/{ => v1}/views/history.nim (96%) rename src/app/wallet/{ => v1}/views/token_list.nim (96%) rename src/app/wallet/{ => v1}/views/tokens.nim (97%) rename src/app/wallet/{ => v1}/views/transaction_list.nim (98%) rename src/app/wallet/{ => v1}/views/transactions.nim (97%) rename src/app/wallet/{ => v1}/views/utils.nim (100%) create mode 100644 src/app/wallet/v2/core.nim create mode 100644 src/app/wallet/v2/view.nim create mode 100644 src/app/wallet/v2/views/account_item.nim create mode 100644 src/app/wallet/v2/views/account_list.nim create mode 100644 src/app/wallet/v2/views/accounts.nim create mode 100644 ui/app/AppLayouts/WalletV2/LeftTab.qml create mode 100644 ui/app/AppLayouts/WalletV2/SeedPhraseBackupWarning.qml create mode 100644 ui/app/AppLayouts/WalletV2/SignPhraseModal.qml create mode 100644 ui/app/AppLayouts/WalletV2/WalletHeader.qml create mode 100644 ui/app/AppLayouts/WalletV2/WalletV2Layout.qml create mode 100644 ui/app/AppLayouts/WalletV2/components/AddAccount.qml create mode 100644 ui/app/AppLayouts/WalletV2/components/HeaderButton.qml create mode 100644 ui/app/AppLayouts/WalletV2/components/qmldir create mode 100644 ui/app/AppLayouts/WalletV2/qmldir diff --git a/src/app/wallet/core.nim b/src/app/wallet/v1/core.nim similarity index 92% rename from src/app/wallet/core.nim rename to src/app/wallet/v1/core.nim index bca7ecd870..7ca85e6686 100644 --- a/src/app/wallet/core.nim +++ b/src/app/wallet/v1/core.nim @@ -2,11 +2,11 @@ import NimQml, strformat, strutils, chronicles, sugar, sequtils import view import views/[asset_list, account_list, account_item] -import ../../status/types as status_types -import ../../status/signals/types -import ../../status/[status, wallet, settings] -import ../../status/wallet/account as WalletTypes -import ../../eventemitter +import ../../../status/types as status_types +import ../../../status/signals/types +import ../../../status/[status, wallet, settings] +import ../../../status/wallet/account as WalletTypes +import ../../../eventemitter logScope: topics = "wallet-core" diff --git a/src/app/wallet/view.nim b/src/app/wallet/v1/view.nim similarity index 99% rename from src/app/wallet/view.nim rename to src/app/wallet/v1/view.nim index 69fb020221..1a9bc11ef6 100644 --- a/src/app/wallet/view.nim +++ b/src/app/wallet/v1/view.nim @@ -2,7 +2,7 @@ import atomics, strformat, strutils, sequtils, json, std/wrapnils, parseUtils, t import NimQml, chronicles, stint import - ../../status/[status, wallet], + ../../../status/[status, wallet], views/[accounts, collectibles, transactions, tokens, gas, ens, dapp_browser, history, balance, utils, asset_list, account_list] QtObject: diff --git a/src/app/wallet/views/account_item.nim b/src/app/wallet/v1/views/account_item.nim similarity index 97% rename from src/app/wallet/views/account_item.nim rename to src/app/wallet/v1/views/account_item.nim index 6d7a5b73d6..df8ec3df73 100644 --- a/src/app/wallet/views/account_item.nim +++ b/src/app/wallet/v1/views/account_item.nim @@ -1,5 +1,5 @@ import NimQml, std/wrapnils, strformat, options -from ../../../status/wallet import WalletAccount +from ../../../../status/wallet import WalletAccount import ./asset_list QtObject: diff --git a/src/app/wallet/views/account_list.nim b/src/app/wallet/v1/views/account_list.nim similarity index 98% rename from src/app/wallet/views/account_list.nim rename to src/app/wallet/v1/views/account_list.nim index ffaf0503e8..c77a38d493 100644 --- a/src/app/wallet/views/account_list.nim +++ b/src/app/wallet/v1/views/account_list.nim @@ -1,7 +1,7 @@ import NimQml, Tables, random, strformat, strutils, json_serialization import sequtils as sequtils import account_item, asset_list -from ../../../status/wallet import WalletAccount, Asset, CollectibleList +from ../../../../status/wallet import WalletAccount, Asset, CollectibleList const accountColors* = ["#9B832F", "#D37EF4", "#1D806F", "#FA6565", "#7CDA00", "#887af9", "#8B3131"] type diff --git a/src/app/wallet/views/accounts.nim b/src/app/wallet/v1/views/accounts.nim similarity index 97% rename from src/app/wallet/views/accounts.nim rename to src/app/wallet/v1/views/accounts.nim index 3b99f3772a..7b346a96d6 100644 --- a/src/app/wallet/views/accounts.nim +++ b/src/app/wallet/v1/views/accounts.nim @@ -1,9 +1,9 @@ import NimQml, json, sequtils, chronicles, strutils, strformat, json import - ../../../status/[status, settings, types], - ../../../status/signals/types as signal_types, - ../../../status/wallet as status_wallet + ../../../../status/[status, settings, types], + ../../../../status/signals/types as signal_types, + ../../../../status/wallet as status_wallet import account_list, account_item diff --git a/src/app/wallet/views/asset_list.nim b/src/app/wallet/v1/views/asset_list.nim similarity index 98% rename from src/app/wallet/views/asset_list.nim rename to src/app/wallet/v1/views/asset_list.nim index 00fd1560b5..bf37aeb6e8 100644 --- a/src/app/wallet/views/asset_list.nim +++ b/src/app/wallet/v1/views/asset_list.nim @@ -1,5 +1,5 @@ import NimQml, tables -from ../../../status/wallet import Asset +from ../../../../status/wallet import Asset type AssetRoles {.pure.} = enum diff --git a/src/app/wallet/views/balance.nim b/src/app/wallet/v1/views/balance.nim similarity index 97% rename from src/app/wallet/views/balance.nim rename to src/app/wallet/v1/views/balance.nim index d9d774d3ab..c9ab408c3b 100644 --- a/src/app/wallet/views/balance.nim +++ b/src/app/wallet/v1/views/balance.nim @@ -2,9 +2,9 @@ import atomics, strutils, sequtils, json, tables, chronicles, web3/[ethtypes, co import NimQml, json, sequtils, chronicles, strutils, strformat, json import - ../../../status/[status, wallet, tokens], - ../../../status/tokens as status_tokens, - ../../../status/tasks/[qt, task_runner_impl] + ../../../../status/[status, wallet, tokens], + ../../../../status/tokens as status_tokens, + ../../../../status/tasks/[qt, task_runner_impl] import account_item, accounts, transactions, history diff --git a/src/app/wallet/views/collectibles.nim b/src/app/wallet/v1/views/collectibles.nim similarity index 96% rename from src/app/wallet/views/collectibles.nim rename to src/app/wallet/v1/views/collectibles.nim index d952c9f755..89b6e3658c 100644 --- a/src/app/wallet/views/collectibles.nim +++ b/src/app/wallet/v1/views/collectibles.nim @@ -2,9 +2,9 @@ import atomics, strformat, strutils, sequtils, json, std/wrapnils, parseUtils, t import NimQml, json, sequtils, chronicles, strutils, strformat, json import - ../../../status/[status, settings, wallet, tokens, utils, types], - ../../../status/wallet/collectibles as status_collectibles, - ../../../status/tasks/[qt, task_runner_impl] + ../../../../status/[status, settings, wallet, tokens, utils, types], + ../../../../status/wallet/collectibles as status_collectibles, + ../../../../status/tasks/[qt, task_runner_impl] import collectibles_list, accounts, account_list, account_item diff --git a/src/app/wallet/views/collectibles_list.nim b/src/app/wallet/v1/views/collectibles_list.nim similarity index 98% rename from src/app/wallet/views/collectibles_list.nim rename to src/app/wallet/v1/views/collectibles_list.nim index 334e07268c..ce00972e95 100644 --- a/src/app/wallet/views/collectibles_list.nim +++ b/src/app/wallet/v1/views/collectibles_list.nim @@ -1,5 +1,5 @@ import NimQml, tables -from ../../../status/wallet import CollectibleList +from ../../../../status/wallet import CollectibleList type CollectiblesRoles {.pure.} = enum diff --git a/src/app/wallet/views/dapp_browser.nim b/src/app/wallet/v1/views/dapp_browser.nim similarity index 96% rename from src/app/wallet/views/dapp_browser.nim rename to src/app/wallet/v1/views/dapp_browser.nim index 836abdd323..82139fe99f 100644 --- a/src/app/wallet/views/dapp_browser.nim +++ b/src/app/wallet/v1/views/dapp_browser.nim @@ -1,7 +1,7 @@ import sequtils, json, chronicles, web3/[ethtypes, conversions], stint import NimQml, json, sequtils, chronicles, strutils, json -import ../../../status/[status, settings, wallet, types] +import ../../../../status/[status, settings, wallet, types] import account_list, account_item, accounts diff --git a/src/app/wallet/views/ens.nim b/src/app/wallet/v1/views/ens.nim similarity index 92% rename from src/app/wallet/views/ens.nim rename to src/app/wallet/v1/views/ens.nim index 4ccd6b2aa5..d8270d6042 100644 --- a/src/app/wallet/views/ens.nim +++ b/src/app/wallet/v1/views/ens.nim @@ -2,9 +2,9 @@ import atomics, strformat, strutils, sequtils, json, std/wrapnils, parseUtils, t import NimQml, json, sequtils, chronicles, strutils, strformat, json import - ../../../status/[status, settings, wallet, tokens], - ../../../status/ens as status_ens, - ../../../status/tasks/[qt, task_runner_impl] + ../../../../status/[status, settings, wallet, tokens], + ../../../../status/ens as status_ens, + ../../../../status/tasks/[qt, task_runner_impl] import account_list, account_item, transaction_list, accounts, asset_list, token_list diff --git a/src/app/wallet/views/gas.nim b/src/app/wallet/v1/views/gas.nim similarity index 97% rename from src/app/wallet/views/gas.nim rename to src/app/wallet/v1/views/gas.nim index dfb4f99264..bf98b430cc 100644 --- a/src/app/wallet/views/gas.nim +++ b/src/app/wallet/v1/views/gas.nim @@ -2,8 +2,8 @@ import atomics, strformat, strutils, sequtils, json, std/wrapnils, parseUtils, c import NimQml, json, sequtils, chronicles, strutils, strformat, json import - ../../../status/[status, wallet, utils, types], - ../../../status/tasks/[qt, task_runner_impl] + ../../../../status/[status, wallet, utils, types], + ../../../../status/tasks/[qt, task_runner_impl] import account_item diff --git a/src/app/wallet/views/history.nim b/src/app/wallet/v1/views/history.nim similarity index 96% rename from src/app/wallet/views/history.nim rename to src/app/wallet/v1/views/history.nim index c5b82607a9..b0e9c37844 100644 --- a/src/app/wallet/views/history.nim +++ b/src/app/wallet/v1/views/history.nim @@ -3,9 +3,9 @@ from sugar import `=>`, `->` import NimQml, json, sequtils, chronicles, strutils, json import - ../../../status/[status, wallet, types, utils], - ../../../status/wallet as status_wallet, - ../../../status/tasks/[qt, task_runner_impl] + ../../../../status/[status, wallet, types, utils], + ../../../../status/wallet as status_wallet, + ../../../../status/tasks/[qt, task_runner_impl] import account_list, account_item, transaction_list, accounts, transactions diff --git a/src/app/wallet/views/token_list.nim b/src/app/wallet/v1/views/token_list.nim similarity index 96% rename from src/app/wallet/views/token_list.nim rename to src/app/wallet/v1/views/token_list.nim index f625633bc0..6477b52815 100644 --- a/src/app/wallet/views/token_list.nim +++ b/src/app/wallet/v1/views/token_list.nim @@ -5,8 +5,8 @@ import # vendor libs NimQml import # status-desktop libs - ../../../status/[utils, tokens, settings], - ../../../status/tasks/[qt, task_runner_impl], ../../../status/status + ../../../../status/[utils, tokens, settings], + ../../../../status/tasks/[qt, task_runner_impl], ../../../../status/status from web3/conversions import `$` type diff --git a/src/app/wallet/views/tokens.nim b/src/app/wallet/v1/views/tokens.nim similarity index 97% rename from src/app/wallet/views/tokens.nim rename to src/app/wallet/v1/views/tokens.nim index de38478ed9..7f6bfafa73 100644 --- a/src/app/wallet/views/tokens.nim +++ b/src/app/wallet/v1/views/tokens.nim @@ -1,7 +1,7 @@ import atomics, strformat, strutils, sequtils, json, std/wrapnils, parseUtils, tables, chronicles, web3/[ethtypes, conversions], stint import NimQml, json, sequtils, chronicles, strutils, strformat, json -import ../../../status/[status, settings, wallet, tokens, utils, types] +import ../../../../status/[status, settings, wallet, tokens, utils, types] import account_list, account_item, transaction_list, accounts, asset_list, token_list diff --git a/src/app/wallet/views/transaction_list.nim b/src/app/wallet/v1/views/transaction_list.nim similarity index 98% rename from src/app/wallet/views/transaction_list.nim rename to src/app/wallet/v1/views/transaction_list.nim index 24904b1c54..9d90bcbdee 100644 --- a/src/app/wallet/views/transaction_list.nim +++ b/src/app/wallet/v1/views/transaction_list.nim @@ -1,5 +1,5 @@ import NimQml, tables -from ../../../status/wallet import Transaction +from ../../../../status/wallet import Transaction type TransactionRoles {.pure.} = enum diff --git a/src/app/wallet/views/transactions.nim b/src/app/wallet/v1/views/transactions.nim similarity index 97% rename from src/app/wallet/views/transactions.nim rename to src/app/wallet/v1/views/transactions.nim index 6c012d46ad..1a1a9017b0 100644 --- a/src/app/wallet/views/transactions.nim +++ b/src/app/wallet/v1/views/transactions.nim @@ -2,9 +2,9 @@ import algorithm, atomics, sequtils, strformat, strutils, sugar, sequtils, json, import NimQml, json, sequtils, chronicles, strutils, strformat, json, stint import - ../../../status/[status, settings, wallet, tokens, utils], - ../../../status/wallet as status_wallet, - ../../../status/tasks/[qt, task_runner_impl] + ../../../../status/[status, settings, wallet, tokens, utils], + ../../../../status/wallet as status_wallet, + ../../../../status/tasks/[qt, task_runner_impl] import account_list, account_item, transaction_list, accounts diff --git a/src/app/wallet/views/utils.nim b/src/app/wallet/v1/views/utils.nim similarity index 100% rename from src/app/wallet/views/utils.nim rename to src/app/wallet/v1/views/utils.nim diff --git a/src/app/wallet/v2/core.nim b/src/app/wallet/v2/core.nim new file mode 100644 index 0000000000..2000e5019a --- /dev/null +++ b/src/app/wallet/v2/core.nim @@ -0,0 +1,44 @@ +import NimQml, strformat, strutils, chronicles, sugar, sequtils + +import view +import views/[account_list, account_item] +import ../../../status/types as status_types +import ../../../status/signals/types +import ../../../status/[status, wallet, settings] +import ../../../status/wallet/account as WalletTypes +import ../../../eventemitter + +logScope: + topics = "wallet-core" + +type WalletController* = ref object + status: Status + view*: WalletView + variant*: QVariant + +proc newController*(status: Status): WalletController = + result = WalletController() + result.status = status + result.view = newWalletView(status) + result.variant = newQVariant(result.view) + +proc delete*(self: WalletController) = + delete self.variant + delete self.view + +proc init*(self: WalletController) = + var accounts = self.status.wallet.accounts + for account in accounts: + self.view.addAccountToList(account) + + self.status.events.on("accountsUpdated") do(e: Args): + self.view.updateView() + + self.status.events.on("newAccountAdded") do(e: Args): + var account = WalletTypes.AccountArgs(e) + self.view.addAccountToList(account.account) + self.view.updateView() + + self.status.events.on(SignalType.Wallet.event) do(e:Args): + var data = WalletSignal(e) + debug "TODO: handle wallet signal", signalType=data.eventType diff --git a/src/app/wallet/v2/view.nim b/src/app/wallet/v2/view.nim new file mode 100644 index 0000000000..bc8f16d51e --- /dev/null +++ b/src/app/wallet/v2/view.nim @@ -0,0 +1,45 @@ +import atomics, strformat, strutils, sequtils, json, std/wrapnils, parseUtils, tables +import NimQml, chronicles, stint + +import + ../../../status/[status, wallet], + views/[accounts, account_list] + +QtObject: + type + WalletView* = ref object of QAbstractListModel + status: Status + accountsView: AccountsView + + proc delete(self: WalletView) = + self.accountsView.delete + self.QAbstractListModel.delete + + proc setup(self: WalletView) = + self.QAbstractListModel.setup + + proc newWalletView*(status: Status): WalletView = + new(result, delete) + result.status = status + result.accountsView = newAccountsView(status) + result.setup + + proc getAccounts(self: WalletView): QVariant {.slot.} = newQVariant(self.accountsView) + QtProperty[QVariant] accountsView: + read = getAccounts + + proc updateView*(self: WalletView) = + # TODO: + self.accountsView.triggerUpdateAccounts() + + + proc setCurrentAccountByIndex*(self: WalletView, index: int) {.slot.} = + if self.accountsView.setCurrentAccountByIndex(index): + let selectedAccount = self.accountsView.accounts.getAccount(index) + # TODO: load account details/transactions/collectibles/etc + + proc addAccountToList*(self: WalletView, account: WalletAccount) = + self.accountsView.addAccountToList(account) + # If it's the first account we ever get, use its list as our first lists + if (self.accountsView.accounts.rowCount == 1): + self.setCurrentAccountByIndex(0) diff --git a/src/app/wallet/v2/views/account_item.nim b/src/app/wallet/v2/views/account_item.nim new file mode 100644 index 0000000000..5eaf3f2ef6 --- /dev/null +++ b/src/app/wallet/v2/views/account_item.nim @@ -0,0 +1,58 @@ +import NimQml, std/wrapnils, strformat, options +from ../../../../status/wallet import WalletAccount + +QtObject: + type AccountItemView* = ref object of QObject + account*: WalletAccount + + proc setup(self: AccountItemView) = + self.QObject.setup + + proc delete*(self: AccountItemView) = + self.QObject.delete + + proc newAccountItemView*(): AccountItemView = + new(result, delete) + result = AccountItemView() + result.setup + + proc setAccountItem*(self: AccountItemView, account: WalletAccount) = + self.account = account + + proc name*(self: AccountItemView): string {.slot.} = result = ?.self.account.name + QtProperty[string] name: + read = name + + proc address*(self: AccountItemView): string {.slot.} = result = ?.self.account.address + QtProperty[string] address: + read = address + + proc iconColor*(self: AccountItemView): string {.slot.} = result = ?.self.account.iconColor + QtProperty[string] iconColor: + read = iconColor + + proc balance*(self: AccountItemView): string {.slot.} = + if ?.self.account.balance.isSome: + result = ?.self.account.balance.get() + else: + result = "" + + QtProperty[string] balance: + read = balance + + proc fiatBalance*(self: AccountItemView): string {.slot.} = + if ?.self.account.realFiatBalance.isSome: + result = fmt"{?.self.account.realFiatBalance.get():>.2f}" + else: + result = "" + + QtProperty[string] fiatBalance: + read = fiatBalance + + proc path*(self: AccountItemView): string {.slot.} = result = ?.self.account.path + QtProperty[string] path: + read = path + + proc walletType*(self: AccountItemView): string {.slot.} = result = ?.self.account.walletType + QtProperty[string] walletType: + read = walletType diff --git a/src/app/wallet/v2/views/account_list.nim b/src/app/wallet/v2/views/account_list.nim new file mode 100644 index 0000000000..241135b72f --- /dev/null +++ b/src/app/wallet/v2/views/account_list.nim @@ -0,0 +1,105 @@ +import NimQml, Tables, random, strformat, strutils, json_serialization +import sequtils as sequtils +import account_item +from ../../../../status/wallet import WalletAccount, Asset, CollectibleList + +const accountColors* = ["#9B832F", "#D37EF4", "#1D806F", "#FA6565", "#7CDA00", "#887af9", "#8B3131"] + +type + AccountRoles {.pure.} = enum + Name = UserRole + 1, + Address = UserRole + 2, + Color = UserRole + 3, + Balance = UserRole + 4 + FiatBalance = UserRole + 5 + WalletType = UserRole + 7 + Wallet = UserRole + 8 + Loading = UserRole + 9 + +QtObject: + type AccountList* = ref object of QAbstractListModel + accounts*: seq[WalletAccount] + + proc setup(self: AccountList) = self.QAbstractListModel.setup + + proc delete(self: AccountList) = + self.accounts = @[] + self.QAbstractListModel.delete + + proc newAccountList*(): AccountList = + new(result, delete) + result.accounts = @[] + result.setup + + proc getAccount*(self: AccountList, index: int): WalletAccount = self.accounts[index] + + proc rowData(self: AccountList, index: int, column: string): string {.slot.} = + if (index >= self.accounts.len): + return + + let account = self.accounts[index] + case column: + of "name": result = account.name + of "address": result = account.address + of "iconColor": result = account.iconColor + of "balance": result = if account.balance.isSome(): account.balance.get() else: "..." + of "path": result = account.path + of "walletType": result = account.walletType + of "fiatBalance": result = if account.realFiatBalance.isSome(): fmt"{account.realFiatBalance.get():>.2f}" else: "..." + + proc getAccountindexByAddress*(self: AccountList, address: string): int = + var i = 0 + for accountView in self.accounts: + if (accountView.address.toLowerAscii == address.toLowerAscii): + return i + i = i + 1 + return -1 + + proc deleteAccountAtIndex*(self: AccountList, index: int) = + sequtils.delete(self.accounts, index, index) + + method rowCount*(self: AccountList, index: QModelIndex = nil): int = + return self.accounts.len + + method data(self: AccountList, index: QModelIndex, role: int): QVariant = + if not index.isValid: + return + if index.row < 0 or index.row >= self.accounts.len: + return + let account = self.accounts[index.row] + let accountRole = role.AccountRoles + case accountRole: + of AccountRoles.Name: result = newQVariant(account.name) + of AccountRoles.Address: result = newQVariant(account.address) + of AccountRoles.Color: result = newQVariant(account.iconColor) + of AccountRoles.Balance: result = newQVariant(if account.balance.isSome(): account.balance.get() else: "...") + of AccountRoles.FiatBalance: result = newQVariant(if account.realFiatBalance.isSome(): fmt"{account.realFiatBalance.get():>.2f}" else: "...") + of AccountRoles.WalletType: result = newQVariant(account.walletType) + of AccountRoles.Wallet: result = newQVariant(account.wallet) + of AccountRoles.Loading: result = newQVariant(if account.balance.isSome() and account.realFiatBalance.isSome(): false else: true) + + method roleNames(self: AccountList): Table[int, string] = + { AccountRoles.Name.int:"name", + AccountRoles.Address.int:"address", + AccountRoles.Color.int:"iconColor", + AccountRoles.Balance.int:"balance", + AccountRoles.FiatBalance.int:"fiatBalance", + AccountRoles.Wallet.int:"isWallet", + AccountRoles.WalletType.int:"walletType", + AccountRoles.Loading.int:"isLoading" }.toTable + + proc addAccountToList*(self: AccountList, account: WalletAccount) = + if account.iconColor == "": + randomize() + account.iconColor = accountColors[rand(accountColors.len - 1)] + + self.beginInsertRows(newQModelIndex(), self.accounts.len, self.accounts.len) + self.accounts.add(account) + self.endInsertRows() + + proc forceUpdate*(self: AccountList) = + self.beginResetModel() + self.endResetModel() + + proc hasAccount*(self: AccountList, address: string): bool = + result = self.accounts.anyIt(it.address == address) diff --git a/src/app/wallet/v2/views/accounts.nim b/src/app/wallet/v2/views/accounts.nim new file mode 100644 index 0000000000..b0fb4e0a6a --- /dev/null +++ b/src/app/wallet/v2/views/accounts.nim @@ -0,0 +1,140 @@ +import NimQml, json, sequtils, chronicles, strutils, strformat, json + +import + ../../../../status/[status, settings, types], + ../../../../status/signals/types as signal_types, + ../../../../status/wallet as status_wallet + +import account_list, account_item + +logScope: + topics = "accounts-view" + +QtObject: + type AccountsView* = ref object of QObject + status: Status + accounts*: AccountList + currentAccount*: AccountItemView + focusedAccount*: AccountItemView + + proc setup(self: AccountsView) = self.QObject.setup + proc delete(self: AccountsView) = + self.accounts.delete + self.currentAccount.delete + self.focusedAccount.delete + self.QObject.delete + + proc newAccountsView*(status: Status): AccountsView = + new(result, delete) + result.status = status + result.accounts = newAccountList() + result.currentAccount = newAccountItemView() + result.focusedAccount = newAccountItemView() + result.setup + + proc generateNewAccount*(self: AccountsView, password: string, accountName: string, color: string): string {.slot.} = + try: + self.status.wallet.generateNewAccount(password, accountName, color) + except StatusGoException as e: + result = StatusGoError(error: e.msg).toJson + + proc addAccountsFromSeed*(self: AccountsView, seed: string, password: string, accountName: string, color: string): string {.slot.} = + try: + self.status.wallet.addAccountsFromSeed(seed.strip(), password, accountName, color) + except StatusGoException as e: + result = StatusGoError(error: e.msg).toJson + + proc addAccountsFromPrivateKey*(self: AccountsView, privateKey: string, password: string, accountName: string, color: string): string {.slot.} = + try: + self.status.wallet.addAccountsFromPrivateKey(privateKey, password, accountName, color) + except StatusGoException as e: + result = StatusGoError(error: e.msg).toJson + + proc addWatchOnlyAccount*(self: AccountsView, address: string, accountName: string, color: string): string {.slot.} = + self.status.wallet.addWatchOnlyAccount(address, accountName, color) + + proc currentAccountChanged*(self: AccountsView) {.signal.} + + proc accountListChanged*(self: AccountsView) {.signal.} + + proc addAccountToList*(self: AccountsView, account: WalletAccount) = + self.accounts.addAccountToList(account) + self.accountListChanged() + + proc changeAccountSettings*(self: AccountsView, address: string, accountName: string, color: string): string {.slot.} = + result = self.status.wallet.changeAccountSettings(address, accountName, color) + if (result == ""): + self.currentAccountChanged() + self.accountListChanged() + self.accounts.forceUpdate() + + proc deleteAccount*(self: AccountsView, address: string): string {.slot.} = + result = self.status.wallet.deleteAccount(address) + if (result == ""): + let index = self.accounts.getAccountindexByAddress(address) + if (index == -1): + return fmt"Unable to find the account with the address {address}" + self.accounts.deleteAccountAtIndex(index) + self.accountListChanged() + self.accounts.forceUpdate() + + proc getCurrentAccount*(self: AccountsView): QVariant {.slot.} = + result = newQVariant(self.currentAccount) + + proc focusedAccountChanged*(self: AccountsView) {.signal.} + + proc setFocusedAccountByAddress*(self: AccountsView, address: string) {.slot.} = + if (self.accounts.rowCount() == 0): return + + var index = self.accounts.getAccountindexByAddress(address) + if index == -1: index = 0 + let selectedAccount = self.accounts.getAccount(index) + if self.focusedAccount.address == selectedAccount.address: return + self.focusedAccount.setAccountItem(selectedAccount) + self.focusedAccountChanged() + + proc getFocusedAccount*(self: AccountsView): QVariant {.slot.} = + result = newQVariant(self.focusedAccount) + + QtProperty[QVariant] focusedAccount: + read = getFocusedAccount + write = setFocusedAccountByAddress + notify = focusedAccountChanged + + #TODO: use an Option here + proc setCurrentAccountByIndex*(self: AccountsView, index: int): bool = + if(self.accounts.rowCount() == 0): return false + + let selectedAccount = self.accounts.getAccount(index) + if self.currentAccount.address == selectedAccount.address: return false + self.currentAccount.setAccountItem(selectedAccount) + self.currentAccountChanged() + return true + + QtProperty[QVariant] currentAccount: + read = getCurrentAccount + write = setCurrentAccountByIndex + notify = currentAccountChanged + + proc getAccountList(self: AccountsView): QVariant {.slot.} = + return newQVariant(self.accounts) + + QtProperty[QVariant] accounts: + read = getAccountList + notify = accountListChanged + + proc getDefaultAccount*(self: AccountsView): string {.slot.} = + self.currentAccount.address + + proc setAccountItems*(self: AccountsView) = + for account in self.status.wallet.accounts: + if account.address == self.currentAccount.address: + self.currentAccount.setAccountItem(account) + + self.accountListChanged() + self.currentAccountChanged() + + proc triggerUpdateAccounts*(self: AccountsView) = + self.currentAccountChanged() + self.accountListChanged() + self.accounts.forceUpdate() diff --git a/src/nim_status_client.nim b/src/nim_status_client.nim index 29430e273e..82a7628bf8 100644 --- a/src/nim_status_client.nim +++ b/src/nim_status_client.nim @@ -1,7 +1,8 @@ import NimQml, chronicles, os, strformat import app/chat/core as chat -import app/wallet/core as wallet +import app/wallet/v1/core as wallet +import app/wallet/v2/core as walletV2 import app/node/core as node import app/utilsView/core as utilsView import app/browser/core as browserView @@ -119,6 +120,10 @@ proc mainProc() = defer: wallet.delete() engine.setRootContextProperty("walletModel", wallet.variant) + var wallet2 = walletV2.newController(status) + defer: wallet2.delete() + engine.setRootContextProperty("walletV2Model", wallet2.variant) + var chat = chat.newController(status) defer: chat.delete() engine.setRootContextProperty("chatsModel", chat.variant) @@ -174,6 +179,7 @@ proc mainProc() = status.startMessenger() profile.init(args.account) wallet.init() + wallet2.init() provider.init() chat.init() utilsController.init() @@ -213,6 +219,7 @@ proc mainProc() = # chat.reset() # node.reset() # wallet.reset() + # wallet2.reset() # profile.reset() # 2. Re-init controllers that don't require a running node diff --git a/ui/app/AppLayouts/Profile/Sections/AdvancedContainer.qml b/ui/app/AppLayouts/Profile/Sections/AdvancedContainer.qml index c67b571327..981e92a2d6 100644 --- a/ui/app/AppLayouts/Profile/Sections/AdvancedContainer.qml +++ b/ui/app/AppLayouts/Profile/Sections/AdvancedContainer.qml @@ -85,6 +85,20 @@ ScrollView { } } + StatusSettingsLineButton { + text: qsTr("Wallet v2") + isSwitch: true + switchChecked: appSettings.isWalletV2Enabled + onClicked: { + if (!appSettings.isWalletV2Enabled) { + confirmationPopup.settingsProp = "isWalletV2Enabled" + confirmationPopup.open() + } else { + appSettings.isWalletV2Enabled = false + } + } + } + StatusSettingsLineButton { //% "Dapp Browser" text: qsTrId("dapp-browser") diff --git a/ui/app/AppLayouts/WalletV2/LeftTab.qml b/ui/app/AppLayouts/WalletV2/LeftTab.qml new file mode 100644 index 0000000000..692e54c476 --- /dev/null +++ b/ui/app/AppLayouts/WalletV2/LeftTab.qml @@ -0,0 +1,221 @@ +import QtQuick 2.13 +import QtQuick.Controls 2.13 +import QtQuick.Layouts 1.13 +import QtGraphicalEffects 1.13 +import "../../../imports" +import "../../../shared" +import "./components" + +Rectangle { + property int selectedAccount: 0 + property var changeSelectedAccount: function(newIndex) { + if (newIndex > walletV2Model.accountsView.accounts) { + return + } + selectedAccount = newIndex + walletV2Model.setCurrentAccountByIndex(newIndex) + } + id: walletInfoContainer + color: Style.current.secondaryMenuBackground + + StyledText { + id: title + //% "Wallet" + text: qsTrId("wallet") + anchors.top: parent.top + anchors.topMargin: Style.current.padding + anchors.horizontalCenter: parent.horizontalCenter + font.weight: Font.Bold + font.pixelSize: 17 + } + + Item { + id: walletValueTextContainer + anchors.left: parent.left + anchors.leftMargin: Style.current.padding + anchors.right: parent.right + anchors.rightMargin: Style.current.padding + anchors.top: title.bottom + anchors.topMargin: Style.current.padding + height: childrenRect.height + + StyledTextEdit { + id: walletAmountValue + color: Style.current.textColor + text: Utils.toLocaleString("0.00", globalSettings.locale, {"currency": true}) + " " + "USD" + selectByMouse: true + cursorVisible: true + readOnly: true + anchors.left: parent.left + font.weight: Font.Medium + font.pixelSize: 30 + } + + StyledText { + id: totalValue + color: Style.current.secondaryText + //% "Total value" + text: qsTrId("wallet-total-value") + anchors.left: walletAmountValue.left + anchors.top: walletAmountValue.bottom + font.weight: Font.Medium + font.pixelSize: 13 + } + + AddAccount { + anchors.top: parent.top + anchors.right: parent.right + } + } + + + Component { + id: walletDelegate + + Rectangle { + property bool selected: index === selectedAccount + property bool hovered + + id: rectangle + height: 64 + color: { + if (selected) { + return Style.current.menuBackgroundActive + } + if (hovered) { + return Style.current.backgroundHoverLight + } + return Style.current.transparent + } + radius: Style.current.radius + anchors.right: parent.right + anchors.rightMargin: Style.current.padding + anchors.left: parent.left + anchors.leftMargin: Style.current.padding + + SVGImage { + id: walletIcon + width: 12 + height: 12 + anchors.top: parent.top + anchors.topMargin: Style.current.smallPadding + anchors.left: parent.left + anchors.leftMargin: Style.current.padding + source: "../../img/walletIcon.svg" + } + ColorOverlay { + anchors.fill: walletIcon + source: walletIcon + color: Utils.getCurrentThemeAccountColor(iconColor) || Style.current.accountColors[0] + } + StyledText { + id: walletName + text: name + elide: Text.ElideRight + anchors.right: walletBalance.left + anchors.rightMargin: Style.current.smallPadding + anchors.top: parent.top + anchors.topMargin: Style.current.smallPadding + anchors.left: walletIcon.right + anchors.leftMargin: Style.current.smallPadding + + font.pixelSize: 15 + font.weight: Font.Medium + color: Style.current.textColor + } + StyledText { + id: walletAddress + font.family: Style.current.fontHexRegular.name + text: address + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + elide: Text.ElideMiddle + anchors.bottom: parent.bottom + anchors.bottomMargin: Style.current.smallPadding + anchors.left: walletIcon.left + font.pixelSize: 15 + font.weight: Font.Medium + color: Style.current.secondaryText + opacity: selected ? 0.7 : 1 + } + StyledText { + id: walletBalance + text: isLoading ? "..." : Utils.toLocaleString(fiatBalance, globalSettings.locale, {"currency": true}) + " " + "USD" + anchors.top: parent.top + anchors.topMargin: Style.current.smallPadding + anchors.right: parent.right + anchors.rightMargin: Style.current.padding + font.pixelSize: 15 + font.weight: Font.Medium + color: Style.current.textColor + } + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onEntered: { + rectangle.hovered = true + } + onExited: { + rectangle.hovered = false + } + onClicked: { + changeSelectedAccount(index) + } + } + } + } + + ScrollView { + anchors.bottom: parent.bottom + anchors.top: walletValueTextContainer.bottom + anchors.topMargin: Style.current.padding + anchors.right: parent.right + anchors.left: parent.left + Layout.fillWidth: true + Layout.fillHeight: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: listView.contentHeight > listView.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff + + ListView { + id: listView + + spacing: 5 + anchors.fill: parent + boundsBehavior: Flickable.StopAtBounds + + delegate: walletDelegate + + ListModel { + id: exampleWalletModel + ListElement { + name: "Status account" + address: "0xcfc9f08bbcbcb80760e8cb9a3c1232d19662fc6f" + balance: "12.00 USD" + iconColor: "#7CDA00" + } + + ListElement { + name: "Test account 1" + address: "0x2Ef1...E0Ba" + balance: "12.00 USD" + iconColor: "#FA6565" + } + ListElement { + name: "Status account" + address: "0x2Ef1...E0Ba" + balance: "12.00 USD" + iconColor: "#7CDA00" + } + } + + model: walletV2Model.accountsView.accounts + } + } +} + +/*##^## +Designer { + D{i:0;formeditorColor:"#ffffff";formeditorZoom:0.75;height:770;width:340} +} +##^##*/ diff --git a/ui/app/AppLayouts/WalletV2/SeedPhraseBackupWarning.qml b/ui/app/AppLayouts/WalletV2/SeedPhraseBackupWarning.qml new file mode 100644 index 0000000000..19689469d4 --- /dev/null +++ b/ui/app/AppLayouts/WalletV2/SeedPhraseBackupWarning.qml @@ -0,0 +1,92 @@ +import QtQuick 2.13 +import QtQuick.Controls 2.13 +import QtQuick.Layouts 1.13 +import QtGraphicalEffects 1.13 +import "../../../imports" +import "../../../shared" +import "../Profile/Sections" +import "." + +Rectangle { + id: root + visible: !profileModel.mnemonic.isBackedUp + height: visible ? 32 : 0 + color: Style.current.red + + Row { + spacing: Style.current.halfPadding + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + + StyledText { + //% "Back up your seed phrase" + text: qsTrId("back-up-your-seed-phrase") + font.pixelSize: 13 + anchors.verticalCenter: parent.verticalCenter + color: Style.current.white + } + + Button { + width: 58 + height: 24 + contentItem: Item { + anchors.fill: parent + Text { + text: "Back up" + font.pixelSize: 13 + font.weight: Font.Medium + font.family: Style.current.fontRegular.name + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + color: Style.current.white + } + } + background: Rectangle { + radius: 4 + anchors.fill: parent + border.color: Style.current.white + color: "#19FFFFFF" + } + MouseArea { + cursorShape: Qt.PointingHandCursor + anchors.fill: parent + onClicked: backupSeedModal.open() + } + } + } + + SVGImage { + id: closeImg + anchors.top: parent.top + anchors.topMargin: 6 + anchors.right: parent.right + anchors.rightMargin: 18 + source: "../../img/close-white.svg" + height: 20 + width: 20 + } + ColorOverlay { + anchors.fill: closeImg + source: closeImg + color: Style.current.white + opacity: 0.7 + } + MouseArea { + anchors.fill: closeImg + cursorShape: Qt.PointingHandCursor + onClicked: ParallelAnimation { + PropertyAnimation { target: root; property: "visible"; to: false; } + PropertyAnimation { target: root; property: "y"; to: -1 * root.height } + } + } + + + BackupSeedModal { + id: backupSeedModal + } + +} diff --git a/ui/app/AppLayouts/WalletV2/SignPhraseModal.qml b/ui/app/AppLayouts/WalletV2/SignPhraseModal.qml new file mode 100644 index 0000000000..f3b6350e79 --- /dev/null +++ b/ui/app/AppLayouts/WalletV2/SignPhraseModal.qml @@ -0,0 +1,110 @@ +import QtQuick 2.13 +import QtQuick.Controls 2.13 +import QtQuick.Layouts 1.13 +import "../../../imports" +import "../../../shared" +import "../../../shared/status" +import "." + +ModalPopup { + id: signPhrasePopup + //% "Signing phrase" + title: qsTrId("signing-phrase") + height: 390 + closePolicy: Popup.NoAutoClose + + Column { + anchors.left: parent.left + anchors.right: parent.right + + StyledText { + anchors.horizontalCenter: parent.horizontalCenter + //% "This is your signing phrase" + text: qsTrId("this-is-you-signing") + font.pixelSize: 17 + font.weight: Font.Bold + horizontalAlignment: Text.AlignHCenter + height: Style.current.padding * 3 + } + + StyledText { + anchors.horizontalCenter: parent.horizontalCenter + //% "You should see these 3 words before signing each transaction" + text: qsTrId("three-words-description") + font.pixelSize: 15 + width: 330 + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + height: Style.current.padding * 4 + } + + Rectangle { + color: Style.current.inputBackground + height: 44 + width: parent.width + StyledText { + id: signingPhrase + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + font.pixelSize: 15 + text: walletModel.utilsView.signingPhrase + } + } + + Item { + height: 30 + width: parent.width + SVGImage { + width: 13.33 + height: 13.33 + sourceSize.height: height * 2 + sourceSize.width: width * 2 + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + fillMode: Image.PreserveAspectFit + source: "../../img/exclamation_outline.svg" + } + } + + StyledText { + //% "If you see a different combination, cancel the transaction and sign out" + text: qsTrId("three-words-description-2") + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + width: parent.width + font.pixelSize: 13 + height: 18 + color: Style.current.danger + anchors.horizontalCenter: parent.horizontalCenter + } + } + + footer: Item { + width: parent.width + height: btnRemindLater.height + + StatusButton { + anchors.right: btnRemindLater.left + anchors.rightMargin: Style.current.padding + //% "Ok, got it" + text: qsTrId("ens-got-it") + type: "secondary" + onClicked: { + appSettings.hideSignPhraseModal = true; + close(); + } + } + + + StatusButton { + id: btnRemindLater + anchors.right: parent.right + //% "Remind me later" + text: qsTrId("remind-me-later") + onClicked: { + hideSignPhraseModal = true; + close(); + } + } + } +} diff --git a/ui/app/AppLayouts/WalletV2/WalletHeader.qml b/ui/app/AppLayouts/WalletV2/WalletHeader.qml new file mode 100644 index 0000000000..6ddb5e24b5 --- /dev/null +++ b/ui/app/AppLayouts/WalletV2/WalletHeader.qml @@ -0,0 +1,149 @@ +import QtQuick 2.13 +import QtQuick.Controls 2.13 +import QtQuick.Layouts 1.13 +import "../../../imports" +import "../../../shared" +import "../../../shared/status" +import "./components" + +Item { + property var currentAccount: walletV2Model.accountsView.currentAccount + property var changeSelectedAccount + + id: walletHeader + height: walletAddress.y + walletAddress.height + anchors.right: parent.right + anchors.rightMargin: 0 + anchors.left: parent.left + anchors.leftMargin: 0 + anchors.top: parent.top + anchors.topMargin: 0 + Layout.fillHeight: true + Layout.fillWidth: true + + StyledText { + id: title + text: currentAccount.name + anchors.top: parent.top + anchors.topMargin: 56 + anchors.left: parent.left + anchors.leftMargin: 24 + font.weight: Font.Medium + font.pixelSize: 28 + } + + Rectangle { + id: separatorDot + width: 8 + height: 8 + color: Style.current.primary + anchors.top: title.verticalCenter + anchors.topMargin: -3 + anchors.left: title.right + anchors.leftMargin: 8 + radius: 50 + } + + StyledText { + id: walletBalance + text: currentAccount.balance.toUpperCase() + anchors.left: separatorDot.right + anchors.leftMargin: 8 + anchors.verticalCenter: title.verticalCenter + font.pixelSize: 22 + } + + StatusExpandableAddress { + id: walletAddress + address: currentAccount.address + anchors.top: title.bottom + anchors.left: title.left + addressWidth: 180 + anchors.leftMargin: 0 + anchors.topMargin: 0 + } + + Item { + property int btnMargin: 8 + property int btnOuterMargin: Style.current.bigPadding + id: walletMenu + width: sendBtn.width + receiveBtn.width + settingsBtn.width + + walletMenu.btnOuterMargin * 2 + anchors.top: parent.top + anchors.topMargin: 16 + anchors.right: parent.right + anchors.rightMargin: 16 + + HeaderButton { + id: sendBtn + imageSource: "../../img/send.svg" + //% "Send" + text: qsTrId("command-button-send") + onClicked: () => console.log("TODO"); + } + + HeaderButton { + id: receiveBtn + imageSource: "../../img/send.svg" + flipImage: true + //% "Receive" + text: qsTrId("receive") + onClicked: () => console.log("TODO") + anchors.left: sendBtn.right + anchors.leftMargin: walletMenu.btnOuterMargin + } + + HeaderButton { + id: settingsBtn + imageSource: "../../img/settings.svg" + flipImage: true + text: "" + onClicked: function () { + if (newSettingsMenu.opened) { + newSettingsMenu.close() + } else { + let x = settingsBtn.x + settingsBtn.width / 2 - newSettingsMenu.width / 2 + newSettingsMenu.popup(x, settingsBtn.height) + } + } + anchors.left: receiveBtn.right + anchors.leftMargin: walletMenu.btnOuterMargin + + PopupMenu { + id: newSettingsMenu + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + width: 176 + Action { + //% "Account Settings" + text: qsTrId("account-settings") + icon.source: "../../img/manage-wallet.svg" + icon.width: 16 + icon.height: 16 + onTriggered: console.log("TODO") + } + Action { + //% "Manage Assets" + text: qsTrId("manage-assets") + icon.source: "../../img/add_remove_token.svg" + icon.width: 16 + icon.height: 16 + onTriggered: console.log("TODO") + } + Action { + //% "Set Currency" + text: qsTrId("set-currency") + icon.source: "../../img/currency.svg" + icon.width: 16 + icon.height: 16 + onTriggered: console.log("TODO") + } + } + } + } +} + +/*##^## +Designer { + D{i:0;formeditorColor:"#ffffff"} +} +##^##*/ diff --git a/ui/app/AppLayouts/WalletV2/WalletV2Layout.qml b/ui/app/AppLayouts/WalletV2/WalletV2Layout.qml new file mode 100644 index 0000000000..648bc6b461 --- /dev/null +++ b/ui/app/AppLayouts/WalletV2/WalletV2Layout.qml @@ -0,0 +1,86 @@ +import QtQuick 2.13 +import QtQuick.Controls 2.13 +import QtQuick.Layouts 1.13 +import "../../../imports" +import "../../../shared" +import "." + +import StatusQ.Layout 0.1 + +Item { + id: walletView + + property bool hideSignPhraseModal: false + + function showSigningPhrasePopup(){ + if(!hideSignPhraseModal && !appSettings.hideSignPhraseModal){ + signPhrasePopup.open(); + } + } + + SignPhraseModal { + id: signPhrasePopup + } + + SeedPhraseBackupWarning { + id: seedPhraseWarning + width: parent.width + anchors.top: parent.top + } + + StatusAppTwoPanelLayout { + anchors.top: seedPhraseWarning.bottom + height: walletView.height - seedPhraseWarning.height + width: walletView.width + + Component.onCompleted: { + if(onboardingModel.firstTimeLogin){ + onboardingModel.firstTimeLogin = false + walletModel.setInitialRange() + } + } + + leftPanel: LeftTab { + id: leftTab + anchors.fill: parent + } + + rightPanel: Item { + anchors.fill: parent + + WalletHeader { + id: walletHeader + changeSelectedAccount: leftTab.changeSelectedAccount + } + + RowLayout { + id: walletInfoContainer + anchors.bottom: parent.bottom + anchors.bottomMargin: 0 + anchors.left: parent.left + anchors.leftMargin: 0 + anchors.right: parent.right + anchors.rightMargin: 0 + anchors.top: walletHeader.bottom + anchors.topMargin: 23 + + Item { + id: walletInfoContent + Layout.fillHeight: true + Layout.fillWidth: true + + StyledText { + text: "TODO" + } + } + } + } + } +} + + +/*##^## +Designer { + D{i:0;autoSize:true;formeditorColor:"#ffffff";height:770;width:1152} +} +##^##*/ diff --git a/ui/app/AppLayouts/WalletV2/components/AddAccount.qml b/ui/app/AppLayouts/WalletV2/components/AddAccount.qml new file mode 100644 index 0000000000..13b32901d3 --- /dev/null +++ b/ui/app/AppLayouts/WalletV2/components/AddAccount.qml @@ -0,0 +1,75 @@ +import QtQuick 2.13 +import QtQuick.Controls 2.13 +import "../../../../shared" +import "../../../../shared/status" +import "../../../../imports" + +StatusRoundButton { + id: btnAdd + icon.name: "plusSign" + pressedIconRotation: 45 + size: "medium" + type: "secondary" + width: 36 + height: 36 + + onClicked: { + if (newAccountMenu.opened) { + newAccountMenu.close() + } else { + let x = btnAdd.iconX + btnAdd.icon.width / 2 - newAccountMenu.width / 2 + newAccountMenu.popup(x, btnAdd.height + 4) + } + } + + PopupMenu { + id: newAccountMenu + width: 260 + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + Action { + //% "Generate an account" + text: qsTrId("generate-a-new-account") + icon.source: "../../../img/generate_account.svg" + icon.width: 19 + icon.height: 19 + onTriggered: console.log("TODO") + } + Action { + //% "Add a watch-only address" + text: qsTrId("add-a-watch-account") + icon.source: "../../../img/eye.svg" + icon.width: 19 + icon.height: 19 + onTriggered: console.log("TODO") + } + Action { + //% "Enter a seed phrase" + text: qsTrId("enter-a-seed-phrase") + icon.source: "../../../img/enter_seed_phrase.svg" + icon.width: 19 + icon.height: 19 + onTriggered: console.log("TODO") + } + Action { + //% "Enter a private key" + text: qsTrId("enter-a-private-key") + icon.source: "../../../img/enter_private_key.svg" + icon.width: 19 + icon.height: 19 + onTriggered: console.log("TODO") + } + onAboutToShow: { + btnAdd.state = "pressed" + } + + onAboutToHide: { + btnAdd.state = "default" + } + } +} + +/*##^## +Designer { + D{i:0;height:36;width:36} +} +##^##*/ diff --git a/ui/app/AppLayouts/WalletV2/components/HeaderButton.qml b/ui/app/AppLayouts/WalletV2/components/HeaderButton.qml new file mode 100644 index 0000000000..e27860c94b --- /dev/null +++ b/ui/app/AppLayouts/WalletV2/components/HeaderButton.qml @@ -0,0 +1,65 @@ +import QtQuick 2.13 +import QtGraphicalEffects 1.13 +import "../../../../imports" +import "../../../../shared" + + +Rectangle { + property string text: "" + property url imageSource + property bool flipImage: false + property var onClicked: function () {} + + id: headerButton + width: buttonImage.width + buttonText.width + Style.current.smallPadding * 2 + + (text === "" ? 0 : walletMenu.btnMargin) + height: buttonText.height + Style.current.smallPadding * 2 + border.width: 0 + color: Style.current.transparent + radius: Style.current.radius + + SVGImage { + id: buttonImage + height: 18 + anchors.left: parent.left + anchors.leftMargin: Style.current.smallPadding + anchors.verticalCenter: parent.verticalCenter + fillMode: Image.PreserveAspectFit + source: imageSource + rotation: flipImage ? 180 : 0 + + ColorOverlay { + anchors.fill: parent + source: parent + color: Style.current.primary + } + } + + StyledText { + id: buttonText + visible: !!headerButton.text + text: headerButton.text + anchors.left: buttonImage.right + anchors.leftMargin: walletMenu.btnMargin + anchors.verticalCenter: parent.verticalCenter + font.pixelSize: 13 + font.family: Style.current.fontMedium.name + font.weight: Font.Medium + color: Style.current.blue + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onEntered: { + parent.color = Style.current.secondaryBackground + } + onExited: { + parent.color = Style.current.transparent + } + onClicked: { + headerButton.onClicked() + } + cursorShape: Qt.PointingHandCursor + } +} diff --git a/ui/app/AppLayouts/WalletV2/components/qmldir b/ui/app/AppLayouts/WalletV2/components/qmldir new file mode 100644 index 0000000000..2ea90a3634 --- /dev/null +++ b/ui/app/AppLayouts/WalletV2/components/qmldir @@ -0,0 +1,9 @@ +SendModalContent 1.0 SendModalContent.qml +SetCurrencyModalContent 1.0 SetCurrencyModalContent.qml +TokenSettingsModalContent 1.0 TokenSettingsModalContent.qml +AddAccount 1.0 AddAccount.qml +GenerateAccountModal 1.0 GenerateAccountModal.qml +AddAccountWithSeed 1.0 AddAccountWithSeed.qml +AddAccountWithPrivateKey 1.0 AddAccountWithPrivateKey.qml +AddWatchOnlyAccount 1.0 AddWatchOnlyAccount.qml +TransactionModal 1.0 TransactionModal.qml diff --git a/ui/app/AppLayouts/WalletV2/qmldir b/ui/app/AppLayouts/WalletV2/qmldir new file mode 100644 index 0000000000..2d4d873368 --- /dev/null +++ b/ui/app/AppLayouts/WalletV2/qmldir @@ -0,0 +1,4 @@ +LeftTab 1.0 LeftTab.qml +WalletHeader 1.0 WalletHeader.qml +AssetsTab 1.0 AssetsTab.qml +CollectiblesTab 1.0 CollectiblesTab.qml diff --git a/ui/app/AppMain.qml b/ui/app/AppMain.qml index abf9eb39c1..2d252a0983 100644 --- a/ui/app/AppMain.qml +++ b/ui/app/AppMain.qml @@ -9,6 +9,7 @@ import "../shared/status" import "./AppLayouts" import "./AppLayouts/Timeline" import "./AppLayouts/Wallet" +import "./AppLayouts/WalletV2" import "./AppLayouts/Chat/components" import "./AppLayouts/Chat/CommunityComponents" import Qt.labs.platform 1.1 @@ -235,6 +236,15 @@ StatusAppLayout { onClicked: appMain.changeAppSection(Constants.wallet) }, + StatusNavBarTabButton { + icon.name: "wallet" + tooltip.text: qsTr("Wallet v2") + visible: enabled + enabled: isExperimental === "1" || appSettings.isWalletV2Enabled + checked: appView.currentIndex == Utils.getAppSectionIndex(Constants.walletv2) + onClicked: appMain.changeAppSection(Constants.walletv2) + }, + StatusNavBarTabButton { enabled: isExperimental === "1" || appSettings.isBrowserEnabled visible: enabled @@ -330,6 +340,10 @@ StatusAppLayout { if(this.children[currentIndex] === walletLayoutContainer){ walletLayoutContainer.showSigningPhrasePopup(); } + + if(this.children[currentIndex] === walletV2LayoutContainer){ + walletV2LayoutContainer.showSigningPhrasePopup(); + } } ChatLayout { @@ -400,6 +414,13 @@ StatusAppLayout { Layout.alignment: Qt.AlignLeft | Qt.AlignTop Layout.fillHeight: true } + + WalletV2Layout { + id: walletV2LayoutContainer + Layout.fillWidth: true + Layout.alignment: Qt.AlignLeft | Qt.AlignTop + Layout.fillHeight: true + } } Settings { @@ -410,6 +431,7 @@ StatusAppLayout { property var profileSplitView property bool communitiesEnabled: false property bool isWalletEnabled: false + property bool isWalletV2Enabled: false property bool nodeManagementEnabled: false property bool isBrowserEnabled: false property bool isActivityCenterEnabled: false diff --git a/ui/imports/Constants.qml b/ui/imports/Constants.qml index be8b76eba9..68cfec966c 100644 --- a/ui/imports/Constants.qml +++ b/ui/imports/Constants.qml @@ -41,6 +41,7 @@ QtObject { readonly property string chat: "chat" readonly property string wallet: "wallet" + readonly property string walletv2: "walletV2" readonly property string timeline: "timeline" readonly property string browser: "browser" readonly property string profile: "profile" diff --git a/ui/imports/Utils.qml b/ui/imports/Utils.qml index e98dc12c2c..7b3ee5a813 100644 --- a/ui/imports/Utils.qml +++ b/ui/imports/Utils.qml @@ -120,6 +120,7 @@ QtObject { case Constants.profile: sectionId = 4; break; case Constants.node: sectionId = 5; break; case Constants.ui: sectionId = 6; break; + case Constants.walletv2: sectionId = 7; break; case Constants.community: sectionId = 99; break; } if (sectionId === -1) {