feat: add wallet v2 tab

This commit is contained in:
Richard Ramos 2021-08-13 16:04:04 -04:00 committed by Iuri Matias
parent c47a16298b
commit 50b6b59abf
37 changed files with 1284 additions and 36 deletions

View File

@ -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"

View File

@ -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:

View File

@ -1,5 +1,5 @@
import NimQml, std/wrapnils, strformat, options
from ../../../status/wallet import WalletAccount
from ../../../../status/wallet import WalletAccount
import ./asset_list
QtObject:

View File

@ -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

View File

@ -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

View File

@ -1,5 +1,5 @@
import NimQml, tables
from ../../../status/wallet import Asset
from ../../../../status/wallet import Asset
type
AssetRoles {.pure.} = enum

View File

@ -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

View File

@ -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

View File

@ -1,5 +1,5 @@
import NimQml, tables
from ../../../status/wallet import CollectibleList
from ../../../../status/wallet import CollectibleList
type
CollectiblesRoles {.pure.} = enum

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,5 +1,5 @@
import NimQml, tables
from ../../../status/wallet import Transaction
from ../../../../status/wallet import Transaction
type
TransactionRoles {.pure.} = enum

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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()

View File

@ -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

View File

@ -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")

View File

@ -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}
}
##^##*/

View File

@ -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
}
}

View File

@ -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();
}
}
}
}

View File

@ -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"}
}
##^##*/

View File

@ -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}
}
##^##*/

View File

@ -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}
}
##^##*/

View File

@ -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
}
}

View File

@ -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

View File

@ -0,0 +1,4 @@
LeftTab 1.0 LeftTab.qml
WalletHeader 1.0 WalletHeader.qml
AssetsTab 1.0 AssetsTab.qml
CollectiblesTab 1.0 CollectiblesTab.qml

View File

@ -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

View File

@ -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"

View File

@ -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) {